├── .github └── workflows │ └── dart-flutter-package-analyzer.yml ├── .gitignore ├── .metadata ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── polidea │ └── blemulator │ ├── BlemulatorPlugin.java │ ├── DeviceContainer.java │ ├── SimulatedAdapter.java │ └── bridging │ ├── CharacteristicContainer.java │ ├── DartMethodCaller.java │ ├── DartValueHandler.java │ ├── JSONToBleErrorConverter.java │ ├── constants │ ├── ArgumentName.java │ ├── ChannelName.java │ ├── DartMethodName.java │ ├── PlatformMethodName.java │ └── SimulationArgumentName.java │ └── decoder │ ├── BleErrorDartValueDecoder.java │ ├── CharacteristicDartValueDecoder.java │ ├── DescriptorDartValueDecoder.java │ └── ServiceDartValueDecoder.java ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── polidea │ │ │ │ │ └── blemulator_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── 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.swift │ │ ├── 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 ├── lib │ ├── adapter │ │ └── ble_adapter.dart │ ├── common │ │ └── components │ │ │ ├── property_row.dart │ │ │ ├── title_icon.dart │ │ │ └── title_image_icon.dart │ ├── 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 │ ├── di │ │ └── ble_adapter_injector.dart │ ├── example_peripherals │ │ ├── generic_peripheral.dart │ │ └── sensor_tag.dart │ ├── main.dart │ ├── model │ │ ├── ble_device.dart │ │ ├── ble_peripheral.dart │ │ ├── ble_service.dart │ │ └── signal_level.dart │ ├── navigation │ │ ├── bloc.dart │ │ ├── navigation_bloc.dart │ │ ├── navigation_event.dart │ │ ├── route_factory.dart │ │ ├── route_name.dart │ │ └── router.dart │ ├── peripheral_details │ │ ├── bloc.dart │ │ ├── components │ │ │ ├── characteristics_view.dart │ │ │ ├── peripheral_details_view.dart │ │ │ └── services_sliver.dart │ │ ├── peripheral_details_bloc.dart │ │ ├── peripheral_details_event.dart │ │ ├── peripheral_details_screen.dart │ │ └── peripheral_details_state.dart │ ├── peripheral_list │ │ ├── bloc.dart │ │ ├── components │ │ │ ├── peripheral_category_icon.dart │ │ │ ├── peripheral_item.dart │ │ │ └── rssi_view.dart │ │ ├── peripheral_list_bloc.dart │ │ ├── peripheral_list_event.dart │ │ ├── peripheral_list_screen.dart │ │ └── peripheral_list_state.dart │ ├── repository │ │ └── device_repository.dart │ ├── sensor_tag_config.dart │ ├── styles │ │ ├── custom_colors.dart │ │ ├── custom_sizes.dart │ │ ├── custom_text_style.dart │ │ └── custom_theme.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 │ ├── adapter │ └── ble_adapter_test.dart │ ├── mock │ ├── mocks.dart │ ├── sample_ble_peripheral.dart │ └── sample_ble_service.dart │ ├── model │ └── signal_level_test.dart │ ├── peripheral_details │ └── peripheral_details_bloc_test.dart │ └── peripheral_list │ └── peripheral_list_bloc_test.dart ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── BlemulatorPlugin.h │ ├── BlemulatorPlugin.m │ ├── Bridging │ │ ├── CharacteristicContainer.h │ │ ├── CharacteristicContainer.m │ │ ├── Converter │ │ │ ├── DartCallArgumentsConverter.h │ │ │ ├── DartCallArgumentsConverter.m │ │ │ ├── DartResultConverter.h │ │ │ ├── DartResultConverter.m │ │ │ ├── DomainTypesConverter.h │ │ │ └── DomainTypesConverter.m │ │ ├── DartMethodCaller.h │ │ ├── DartMethodCaller.m │ │ ├── DartValueHandler.h │ │ └── DartValueHandler.m │ ├── Constants │ │ ├── DartCallArgumentKeys.h │ │ ├── DartCallArgumentKeys.m │ │ ├── DartMethodName.h │ │ ├── DartMethodName.m │ │ ├── DartResultKeys.h │ │ ├── DartResultKeys.m │ │ ├── PlatformMethodName.h │ │ ├── PlatformMethodName.m │ │ ├── SimulationChannelName.h │ │ └── SimulationChannelName.m │ ├── DeviceContainer.h │ ├── DeviceContainer.m │ ├── Error │ │ ├── BleError.h │ │ ├── BleError.m │ │ └── BleErrorCode.h │ ├── Model │ │ ├── AdvertisementData.h │ │ ├── AdvertisementData.m │ │ ├── Characteristic.h │ │ ├── Characteristic.m │ │ ├── ConnectionStateEvent.h │ │ ├── ConnectionStateEvent.m │ │ ├── Descriptor.h │ │ ├── Descriptor.m │ │ ├── Peripheral.h │ │ ├── Peripheral.m │ │ ├── ScannedPeripheral.h │ │ ├── ScannedPeripheral.m │ │ ├── Service.h │ │ └── Service.m │ ├── Response │ │ ├── BlemulatorCharacteristicResponse.h │ │ ├── BlemulatorCharacteristicResponse.m │ │ ├── BlemulatorDescriptorResponse.h │ │ ├── BlemulatorDescriptorResponse.m │ │ ├── BlemulatorPeripheralResponse.h │ │ └── BlemulatorPeripheralResponse.m │ ├── SimulatedAdapter.h │ ├── SimulatedAdapter.m │ └── Util │ │ ├── ArrayUtilities.h │ │ ├── ArrayUtilities.m │ │ ├── Base64Coder.h │ │ ├── Base64Coder.m │ │ ├── BlemulatorCommonTypes.h │ │ ├── BlemulatorJSONStringifier.h │ │ ├── BlemulatorJSONStringifier.m │ │ ├── FlutterMethodCallHandler.h │ │ ├── StringUtilities.h │ │ └── StringUtilities.m └── blemulator.podspec ├── lib ├── blemulator.dart ├── blemulator_core.dart ├── simulated_ble_error.dart ├── simulation │ ├── simulated_characteristic.dart │ ├── simulated_descriptor.dart │ ├── simulated_peripheral.dart │ └── simulated_service.dart └── src │ ├── bridge │ ├── constants.dart │ ├── dart_to_platform_bridge.dart │ └── platform_to_dart_bridge.dart │ ├── defaults.dart │ ├── id_generator.dart │ ├── internal.dart │ ├── scan_result.dart │ ├── simulation_manager.dart │ ├── simulation_manager_mixins │ ├── characteristics_mixin.dart │ ├── client_managing_mixin.dart │ ├── descriptors_mixin.dart │ ├── discovery_mixin.dart │ ├── error_checks_mixin.dart │ ├── mtu_mixin.dart │ ├── peripheral_connection_mixin.dart │ ├── peripheral_scanning_mixin.dart │ ├── response_models.dart │ ├── rssi_mixin.dart │ └── simulation_manager_base.dart │ └── util │ └── mappers.dart ├── pubspec.yaml ├── site └── logo_Blemulator.png └── test ├── factory ├── simulated_peripheral_factory.dart └── simulation_manager_factory.dart └── internal ├── bridge └── platform_to_dart_bridge_test.dart └── simulation_manager_mixin ├── discovery_mixin_test.dart └── mtu_mixin_test.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 | .dart_tool/package_config.json 4 | .idea 5 | .packages 6 | .pub/ 7 | build/ 8 | ios/.generated/ 9 | packages 10 | pubspec.lock 11 | *.iml 12 | example/android/res/values/strings_en.arb 13 | res/values/strings_en.arb 14 | lib/generated/ 15 | example/lib/generated/ 16 | example/ios/Podfile.lock -------------------------------------------------------------------------------- /.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: cc949a8e8b9cf394b9290a8e80f87af3e207dce5 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: flutter_example 9 | if: branch = master OR type = pull_request 10 | - name: native 11 | if: branch = master OR type = pull_request 12 | 13 | _flutter_job_template: &_flutter_job_template 14 | before_script: 15 | - git clone https://github.com/flutter/flutter.git -b stable 16 | - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH 17 | script: 18 | - flutter packages get 19 | - flutter test 20 | 21 | _flutter_example_job_template: &_flutter_example_job_template 22 | before_script: 23 | - git clone https://github.com/flutter/flutter.git -b stable 24 | - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH 25 | script: 26 | - cd example 27 | - flutter packages get 28 | - flutter test 29 | 30 | _android_job_template: &android_job_template 31 | language: android 32 | android: 33 | components: 34 | - tools 35 | - build-tools-28.0.3 36 | - android-28 37 | - build-tools-29.0.5 38 | - android-29 39 | - extra-android-m2repository 40 | licenses: 41 | - 'android-sdk-license-.+' 42 | env: 43 | global: 44 | - GRADLE_OPTS="-Xms128m" 45 | - ABI="default;armeabi-v7a" 46 | before_cache: 47 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 48 | cache: 49 | directories: 50 | - $HOME/.m2 51 | - $HOME/.gradle/caches/ 52 | - $HOME/.gradle/wrapper/ 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 apk 59 | 60 | _ios_job_template: &ios_job_template 61 | language: objective-c 62 | os: osx 63 | osx_image: xcode11.6 64 | xcode_project: example/ios/Runner.xcworkspace 65 | xcode_scheme: Runner 66 | before_script: 67 | - git clone https://github.com/flutter/flutter.git -b stable 68 | - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH 69 | script: 70 | - cd example 71 | - flutter build ios --no-codesign --verbose 72 | 73 | matrix: 74 | include: 75 | - <<: *android_job_template 76 | name: Build Android 77 | stage: native 78 | - <<: *ios_job_template 79 | name: Build iOS 80 | stage: native 81 | - <<: *_flutter_job_template 82 | name: Build Flutter 83 | stage: flutter 84 | - <<: *_flutter_example_job_template 85 | name: Test Flutter Example 86 | stage: flutter_example 87 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.2.1 2 | 3 | * Fix crash on iOS when manufacturer's data was present in the advertising packet 4 | 5 | ## 1.2.0 6 | 7 | * Add support for connecting to peripheral without launching scan first 8 | 9 | ## 1.1.3 10 | 11 | * Fix incorrect iOS plugin class name causing `Duplicate plugin key: FlutterBleLib` 12 | 13 | ## 1.1.2 14 | 15 | * Unify internal representation of UUIDs to lower case, making simulated GATTs case insensitive 16 | 17 | ## 1.1.1 18 | 19 | * hide private API 20 | * remove unnecessary log 21 | * update dependencies 22 | 23 | ## 1.1.0 24 | 25 | * **NEW** characteristics may now contain descriptors 26 | 27 | ## 1.0.1 28 | 29 | * Fix an issue causing characteristics' properties to be null on iOS 30 | 31 | ## 1.0.0 32 | First release of BLEmulator for Flutter! 33 | 34 | BLEmulator supports: 35 | * switching simulation on 36 | * peripherals (manipulation of advertisement data and interval, connection, discovery) 37 | * services (fetching characteristics) 38 | * characteristics (reading, writing, notifications/indications) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Polidea Sp. z o.o 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.9.0.yaml -------------------------------------------------------------------------------- /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.blemulator' 2 | version '1.2.1' 3 | 4 | buildscript { 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.2.1' 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 'com.github.Polidea:MultiPlatformBleAdapter:0.1.7' 39 | } 40 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=true 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-Xmx1536M 4 | 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-4.10.2-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'blemulator' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/BlemulatorPlugin.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator; 2 | 3 | import android.content.Context; 4 | import androidx.annotation.NonNull; 5 | 6 | import com.polidea.blemulator.bridging.DartMethodCaller; 7 | import com.polidea.blemulator.bridging.DartValueHandler; 8 | import com.polidea.blemulator.bridging.constants.ChannelName; 9 | import com.polidea.blemulator.bridging.constants.PlatformMethodName; 10 | import com.polidea.multiplatformbleadapter.BleAdapter; 11 | import com.polidea.multiplatformbleadapter.BleAdapterCreator; 12 | import com.polidea.multiplatformbleadapter.BleAdapterFactory; 13 | 14 | import io.flutter.plugin.common.MethodCall; 15 | import io.flutter.plugin.common.MethodChannel; 16 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler; 17 | import io.flutter.plugin.common.PluginRegistry; 18 | 19 | public class BlemulatorPlugin implements MethodCallHandler { 20 | 21 | private DartMethodCaller dartMethodCaller; 22 | private DartValueHandler dartValueHandler; 23 | 24 | public static void registerWith(PluginRegistry.Registrar registrar) { 25 | MethodChannel dartToPlatformChannel = new MethodChannel(registrar.messenger(), ChannelName.TO_PLATFORM); 26 | MethodChannel platformToDartChannel = new MethodChannel(registrar.messenger(), ChannelName.TO_DART); 27 | 28 | dartToPlatformChannel.setMethodCallHandler(new BlemulatorPlugin(platformToDartChannel)); 29 | } 30 | 31 | private BlemulatorPlugin(MethodChannel platformToDartChannel) { 32 | dartMethodCaller = new DartMethodCaller(platformToDartChannel); 33 | dartValueHandler = new DartValueHandler(); 34 | } 35 | 36 | @Override 37 | public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { 38 | switch (call.method) { 39 | case PlatformMethodName.SIMULATE: 40 | switchToSimulation(result); 41 | return; 42 | default: 43 | dartValueHandler.onMethodCall(call, result); 44 | return; 45 | } 46 | } 47 | 48 | private void switchToSimulation(MethodChannel.Result result) { 49 | BleAdapterFactory.setBleAdapterCreator(new BleAdapterCreator() { 50 | @Override 51 | public BleAdapter createAdapter(Context context) { 52 | SimulatedAdapter adapter = new SimulatedAdapter(dartMethodCaller, dartValueHandler); 53 | return adapter; 54 | } 55 | }); 56 | result.success(null); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/DeviceContainer.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator; 2 | 3 | import com.polidea.blemulator.bridging.CharacteristicContainer; 4 | import com.polidea.multiplatformbleadapter.Service; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class DeviceContainer { 10 | private String identifier; 11 | private String name; 12 | private List services; 13 | private Map> characteristicContainersIndexedByServiceUuids; 14 | private boolean isConnected = false; 15 | 16 | public DeviceContainer(String identifier, String name, List services, Map> characteristicContainersIndexedByServiceUuids) { 17 | this.identifier = identifier; 18 | this.name = name; 19 | this.services = services; 20 | this.characteristicContainersIndexedByServiceUuids = characteristicContainersIndexedByServiceUuids; 21 | } 22 | 23 | public boolean isConnected() { 24 | return isConnected; 25 | } 26 | 27 | public void setConnected(boolean connected) { 28 | isConnected = connected; 29 | } 30 | 31 | public String getIdentifier() { 32 | return identifier; 33 | } 34 | 35 | public String getName() { 36 | return name; 37 | } 38 | 39 | public List getServices() { 40 | return services; 41 | } 42 | 43 | public Map> getCharacteristicContainersIndexedByServiceUuids() { 44 | return characteristicContainersIndexedByServiceUuids; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/bridging/CharacteristicContainer.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator.bridging; 2 | 3 | import com.polidea.multiplatformbleadapter.Characteristic; 4 | import com.polidea.multiplatformbleadapter.Descriptor; 5 | 6 | import java.util.List; 7 | 8 | public class CharacteristicContainer { 9 | private final Characteristic characteristic; 10 | private final List descriptors; 11 | 12 | public CharacteristicContainer(Characteristic characteristic, List descriptors) { 13 | this.characteristic = characteristic; 14 | this.descriptors = descriptors; 15 | } 16 | 17 | public Characteristic getCharacteristic() { 18 | return characteristic; 19 | } 20 | 21 | public List getDescriptors() { 22 | return descriptors; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/bridging/JSONToBleErrorConverter.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator.bridging; 2 | 3 | import com.polidea.multiplatformbleadapter.errors.BleError; 4 | import com.polidea.multiplatformbleadapter.errors.BleErrorCode; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | public class JSONToBleErrorConverter { 10 | private static final String ERROR_CODE = "errorCode"; 11 | private static final String REASON = "reason"; 12 | 13 | public BleError bleErrorFromJSON(String jsonString) { 14 | try { 15 | JSONObject jsonObject = new JSONObject(jsonString); 16 | return new BleError(findValue(jsonObject.getInt(ERROR_CODE)), jsonObject.getString(REASON), -1); 17 | } catch (JSONException e) { 18 | return new BleError(BleErrorCode.UnknownError, "Failed to convert: " + jsonString, -1); 19 | } 20 | } 21 | 22 | private BleErrorCode findValue(int errorCode) throws JSONException { 23 | for (BleErrorCode value : BleErrorCode.values()) { 24 | if (value.code == errorCode) { 25 | return value; 26 | } 27 | } 28 | 29 | throw new JSONException("Failed to find value for " + errorCode); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/bridging/constants/ArgumentName.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator.bridging.constants; 2 | 3 | public interface ArgumentName { 4 | String IDENTIFIER = "id"; 5 | String SERVICE_UUID = "serviceUuid"; 6 | String TRANSACTION_ID = "transactionId"; 7 | String DEVICE_IDENTIFIER = "deviceIdentifier"; 8 | String CHARACTERISTIC_UUID = "characteristicUuid"; 9 | String SERVICE_IDENTIFIER = "serviceId"; 10 | String CHARACTERISTIC_IDENTIFIER = "characteristicIdentifier"; 11 | String DESCRIPTOR_IDENTIFIER = "descriptorIdentifier"; 12 | String DESCRIPTOR_UUID = "descriptorUuid"; 13 | String VALUE = "value"; 14 | } 15 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/bridging/constants/ChannelName.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator.bridging.constants; 2 | 3 | public interface ChannelName { 4 | String BASE = "com.polidea.blemulator"; 5 | 6 | String TO_DART = BASE + "/toDart"; 7 | String TO_PLATFORM = BASE + "/toJava"; 8 | } 9 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/bridging/constants/DartMethodName.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator.bridging.constants; 2 | 3 | public interface DartMethodName { 4 | String CREATE_CLIENT = "createClient"; 5 | String DESTROY_CLIENT = "destroyClient"; 6 | 7 | String START_DEVICE_SCAN = "startDeviceScan"; 8 | String STOP_DEVICE_SCAN = "stopDeviceScan"; 9 | 10 | String CONNECT_TO_DEVICE = "connectToDevice"; 11 | String IS_DEVICE_CONNECTED = "isDeviceConnected"; 12 | String DISCONNECT_OR_CANCEL_CONNECTION = "disconnectOrCancelConnection"; 13 | 14 | String DISCOVER_ALL_SERVICES_AND_CHARACTERISTICS = "discoverAllServicesAndCharacteristics"; 15 | String GET_SERVICES = "services"; 16 | String GET_CHARACTERISTICS = "characteristics"; 17 | 18 | String READ_CHARACTERISTIC_FOR_IDENTIFIER = "readCharacteristicForIdentifier"; 19 | String READ_CHARACTERISTIC_FOR_DEVICE = "readCharacteristicForDevice"; 20 | String READ_CHARACTERISTIC_FOR_SERVICE = "readCharacteristicForService"; 21 | String WRITE_CHARACTERISTIC_FOR_IDENTIFIER = "writeCharacteristicForIdentifier"; 22 | String WRITE_CHARACTERISTIC_FOR_DEVICE = "writeCharacteristicForDevice"; 23 | String WRITE_CHARACTERISTIC_FOR_SERVICE = "writeCharacteristicForService"; 24 | String MONITOR_CHARACTERISTIC_FOR_IDENTIFIER = "monitorCharacteristicForIdentifier"; 25 | String MONITOR_CHARACTERISTIC_FOR_DEVICE = "monitorCharacteristicForDevice"; 26 | String MONITOR_CHARACTERISTIC_FOR_SERVICE = "monitorCharacteristicForService"; 27 | 28 | String READ_DESCRIPTOR_FOR_IDENTIFIER = "readDescriptorForIdentifier"; 29 | String READ_DESCRIPTOR_FOR_CHARACTERISTIC = "readDescriptorForCharacteristic"; 30 | String READ_DESCRIPTOR_FOR_SERVICE = "readDescriptorForService"; 31 | String READ_DESCRIPTOR_FOR_DEVICE = "readDescriptorForDevice"; 32 | 33 | String WRITE_DESCRIPTOR_FOR_IDENTIFIER = "writeDescriptorForIdentifier"; 34 | String WRITE_DESCRIPTOR_FOR_CHARACTERISTIC = "writeDescriptorForCharacteristic"; 35 | String WRITE_DESCRIPTOR_FOR_SERVICE = "writeDescriptorForService"; 36 | String WRITE_DESCRIPTOR_FOR_DEVICE = "writeDescriptorForDevice"; 37 | 38 | String RSSI = "rssi"; 39 | String REQUEST_MTU = "requestMtu"; 40 | String CANCEL_TRANSACTION = "cancelTransaction"; 41 | } 42 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/bridging/constants/PlatformMethodName.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator.bridging.constants; 2 | 3 | public interface PlatformMethodName { 4 | String SIMULATE = "simulate"; 5 | 6 | String PUBLISH_SCAN_RESULT = "publishScanResult"; 7 | String PUBLISH_CONNECTION_STATE = "publishConnectionState"; 8 | String PUBLISH_CHARACTERISTIC_UPDATE = "publishCharacteristicUpdate"; 9 | String PUBLISH_CHARACTERISTIC_MONITORING_ERROR = "publishCharacteristicMonitoringError"; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/bridging/constants/SimulationArgumentName.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator.bridging.constants; 2 | 3 | public interface SimulationArgumentName { 4 | String DEVICE_IDENTIFIER = "deviceIdentifier"; 5 | String DEVICE_NAME = "name"; 6 | String DEVICE_ID = "id"; 7 | String RSSI = "rssi"; 8 | String MTU = "mtu"; 9 | String IS_CONNECTABLE = "isConnectable"; 10 | String TX_POWER_LEVEL = "txPowerLevel"; 11 | String MANUFACTURER_DATA = "manufacturerData"; 12 | String SERVICE_DATA = "serviceData"; 13 | String SERVICE_UUIDS = "serviceUuids"; 14 | String LOCAL_NAME = "localName"; 15 | String SOLICITED_SERVICE_UUIDS = "solicitedServiceUuids"; 16 | String OVERFLOW_SERVICE_UUIDS = "overflowUuids"; 17 | 18 | String CONNECTION_STATE = "connectionState"; 19 | 20 | String CHARACTERISTICS = "characteristics"; 21 | String UUID = "uuid"; 22 | String SERVICE_ID = "serviceId"; 23 | String SERVICE_UUID = "serviceUuid"; 24 | String CHARACTERISTIC_UUID = "characteristicUuid"; 25 | String CHARACTERISTIC_ID = "characteristicId"; 26 | String IS_READABLE = "isReadable"; 27 | String IS_WRITABLE_WITH_RESPONSE = "isWritableWithResponse"; 28 | String IS_WRITABLE_WITHOUT_RESPONSE = "isWritableWithoutResponse"; 29 | String IS_NOTIFIABLE = "isNotifiable"; 30 | String IS_NOTIFYING = "isNotifying"; 31 | String IS_INDICATABLE = "isIndicatable"; 32 | String VALUE = "value"; 33 | 34 | String DESCRIPTORS = "descriptors"; 35 | 36 | String TRANSACTION_ID = "transactionId"; 37 | } 38 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/bridging/decoder/BleErrorDartValueDecoder.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator.bridging.decoder; 2 | 3 | import com.polidea.multiplatformbleadapter.errors.BleError; 4 | import com.polidea.multiplatformbleadapter.errors.BleErrorCode; 5 | 6 | import java.util.Map; 7 | 8 | public class BleErrorDartValueDecoder { 9 | 10 | private interface Metadata { 11 | String ERROR_CODE = "errorCode"; 12 | String REASON = "reason"; 13 | } 14 | 15 | public BleError decode(Map values) { 16 | return new BleError( 17 | mapToBleErrorCode((Integer) values.get(Metadata.ERROR_CODE)), 18 | (String) values.get(Metadata.REASON), 19 | null); 20 | } 21 | 22 | private BleErrorCode mapToBleErrorCode(int code) { 23 | for (BleErrorCode errorCode : BleErrorCode.values()) { 24 | if (errorCode.code == code) return errorCode; 25 | } 26 | return BleErrorCode.UnknownError; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/bridging/decoder/DescriptorDartValueDecoder.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator.bridging.decoder; 2 | 3 | import android.bluetooth.BluetoothGattDescriptor; 4 | 5 | import com.polidea.multiplatformbleadapter.Descriptor; 6 | 7 | import java.util.Map; 8 | import java.util.UUID; 9 | 10 | public class DescriptorDartValueDecoder { 11 | 12 | private interface Metadata { 13 | String DESCRIPTOR_UUID = "descriptorUuid"; 14 | String DESCRIPTOR_ID = "descriptorId"; 15 | String CHARACTERISTIC_ID = "characteristicId"; 16 | String CHARACTERISTIC_UUID = "characteristicUuid"; 17 | String SERVICE_ID = "serviceId"; 18 | String SERVICE_UUID = "serviceUuid"; 19 | String DEVICE_ID = "deviceIdentifier"; 20 | String VALUE = "value"; 21 | } 22 | 23 | public Descriptor decode(Map values) { 24 | BluetoothGattDescriptor gattDescriptor = new BluetoothGattDescriptor( 25 | UUID.fromString( 26 | (String) values.get(Metadata.DESCRIPTOR_UUID)), 27 | 0); 28 | 29 | Descriptor descriptor = new Descriptor( 30 | (Integer) values.get(Metadata.CHARACTERISTIC_ID), 31 | (Integer) values.get(Metadata.SERVICE_ID), 32 | UUID.fromString((String) values.get(Metadata.CHARACTERISTIC_UUID)), 33 | UUID.fromString((String) values.get(Metadata.SERVICE_UUID)), 34 | (String) values.get(Metadata.DEVICE_ID), 35 | gattDescriptor, 36 | (Integer) values.get(Metadata.DESCRIPTOR_ID), 37 | UUID.fromString( 38 | (String) values.get(Metadata.DESCRIPTOR_UUID)) 39 | ); 40 | 41 | byte[] value = (byte[]) values.get(Metadata.VALUE); 42 | descriptor.setValue(value); 43 | 44 | return descriptor; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /android/src/main/java/com/polidea/blemulator/bridging/decoder/ServiceDartValueDecoder.java: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator.bridging.decoder; 2 | 3 | import android.bluetooth.BluetoothGattService; 4 | 5 | import com.polidea.blemulator.bridging.constants.SimulationArgumentName; 6 | import com.polidea.multiplatformbleadapter.Service; 7 | import com.polidea.multiplatformbleadapter.utils.UUIDConverter; 8 | 9 | import java.util.Map; 10 | import java.util.UUID; 11 | 12 | public class ServiceDartValueDecoder { 13 | 14 | public Service decode(String deviceId, Map values) { 15 | int serviceId = (int) values.get(SimulationArgumentName.SERVICE_ID); 16 | UUID serviceUuid = UUIDConverter.convert((String) values.get(SimulationArgumentName.SERVICE_UUID)); 17 | BluetoothGattService bluetoothGattService = new BluetoothGattService(serviceUuid, BluetoothGattService.SERVICE_TYPE_PRIMARY); 18 | return new Service(serviceId, deviceId, bluetoothGattService); 19 | } 20 | } -------------------------------------------------------------------------------- /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 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 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 | 77 | # Code coverage related 78 | /coverage/ -------------------------------------------------------------------------------- /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: cc949a8e8b9cf394b9290a8e80f87af3e207dce5 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # blemulator_example 2 | 3 | Demonstrates how to use the blemulator 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 plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.polidea.blemulator_example" 42 | minSdkVersion 18 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | 57 | packagingOptions { 58 | exclude 'META-INF/proguard/androidx-annotations.pro' 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | testImplementation 'junit:junit:4.12' 69 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 70 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 71 | } 72 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 15 | 22 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/polidea/blemulator_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.polidea.blemulator_example 2 | 3 | import android.os.Bundle 4 | 5 | import io.flutter.app.FlutterActivity 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun onCreate(savedInstanceState: Bundle?) { 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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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 | ext.kotlin_version = '1.3.61' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.3' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | 3 | android.enableR8=true 4 | android.enableJetifier=true 5 | android.useAndroidX=true 6 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Feb 11 14:37:45 CET 2020 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.4.1-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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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.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.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/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 | blemulator_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /example/lib/common/components/title_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/styles/custom_sizes.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | class TitleIcon extends Icon { 5 | TitleIcon(IconData icon, {Color color}) 6 | : super(icon, size: CustomSizes.titleIcon, color: color); 7 | } 8 | -------------------------------------------------------------------------------- /example/lib/common/components/title_image_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/styles/custom_sizes.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | class TitleImageIcon extends ImageIcon { 5 | TitleImageIcon(ImageProvider image, {Color color}) 6 | : super(image, size: CustomSizes.titleIcon, color: color); 7 | } 8 | -------------------------------------------------------------------------------- /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:blemulator_example/device_details/device_details_bloc.dart'; 6 | import 'package:blemulator_example/device_details/devices_details_bloc_provider.dart'; 7 | import 'package:blemulator_example/device_details/view/auto_test_view.dart'; 8 | import 'package:blemulator_example/device_details/view/manual_test_view.dart'; 9 | 10 | class DeviceDetailsView extends StatefulWidget { 11 | @override 12 | State createState() => DeviceDetailsViewState(); 13 | 14 | } 15 | 16 | class DeviceDetailsViewState extends State { 17 | DeviceDetailsBloc _deviceDetailsBloc; 18 | StreamSubscription _appStateSubscription; 19 | 20 | bool _shouldRunOnResume = true; 21 | 22 | @override 23 | void didChangeDependencies() { 24 | super.didChangeDependencies(); 25 | Fimber.d('didChangeDependencies'); 26 | if (_deviceDetailsBloc == null) { 27 | _deviceDetailsBloc = DeviceDetailsBlocProvider.of(context); 28 | if (_shouldRunOnResume) { 29 | _shouldRunOnResume = false; 30 | _onResume(); 31 | } 32 | } 33 | } 34 | 35 | void _onResume() { 36 | Fimber.d('onResume'); 37 | _deviceDetailsBloc.init(); 38 | _appStateSubscription = 39 | _deviceDetailsBloc.disconnectedDevice.listen((bleDevice) async { 40 | Fimber.d('navigate to details'); 41 | _onPause(); 42 | Navigator.pop(context); 43 | _shouldRunOnResume = true; 44 | Fimber.d('back from details'); 45 | }); 46 | } 47 | 48 | void _onPause() { 49 | Fimber.d('onPause'); 50 | _appStateSubscription.cancel(); 51 | _deviceDetailsBloc.dispose(); 52 | } 53 | 54 | @override 55 | void dispose() { 56 | Fimber.d('Dispose DeviceListScreenState'); 57 | _onPause(); 58 | super.dispose(); 59 | } 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | return WillPopScope( 64 | onWillPop: () { 65 | return _deviceDetailsBloc.disconnect().then((_) { 66 | return false; 67 | }); 68 | }, 69 | child: DefaultTabController( 70 | length: 2, 71 | child: Scaffold( 72 | backgroundColor: Colors.grey[300], 73 | appBar: AppBar( 74 | title: Text('Device Details'), 75 | bottom: TabBar( 76 | tabs: [ 77 | Tab(icon: Icon(Icons.autorenew), text: 'Automatic',), 78 | Tab(icon: Icon(Icons.settings), text: 'Manual',), 79 | ], 80 | ), 81 | ), 82 | body: TabBarView( 83 | children: [ 84 | AutoTestView(_deviceDetailsBloc), 85 | ManualTestView(_deviceDetailsBloc), 86 | ], 87 | )), 88 | ), 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /example/lib/device_details/devices_details_bloc_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:blemulator_example/repository/device_repository.dart'; 3 | import 'package:flutter_ble_lib/flutter_ble_lib.dart'; 4 | 5 | import 'device_details_bloc.dart'; 6 | 7 | 8 | class DeviceDetailsBlocProvider extends InheritedWidget { 9 | final DeviceDetailsBloc deviceDetailsBloc; 10 | 11 | DeviceDetailsBlocProvider({ 12 | Key key, 13 | DeviceDetailsBloc deviceDetailsBloc, 14 | Widget child, 15 | }) : deviceDetailsBloc = deviceDetailsBloc ?? DeviceDetailsBloc(DeviceRepository(), BleManager()), 16 | super(key: key, child: child); 17 | 18 | @override 19 | bool updateShouldNotify(InheritedWidget oldWidget) => true; 20 | 21 | static DeviceDetailsBloc of(BuildContext context) => 22 | (context.inheritFromWidgetOfExactType(DeviceDetailsBlocProvider) 23 | as DeviceDetailsBlocProvider) 24 | .deviceDetailsBloc; 25 | } 26 | -------------------------------------------------------------------------------- /example/lib/device_details/view/auto_test_view.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:blemulator_example/device_details/device_details_bloc.dart'; 4 | import 'package:blemulator_example/device_details/view/button_view.dart'; 5 | import 'package:blemulator_example/device_details/view/logs_container_view.dart'; 6 | 7 | 8 | class AutoTestView extends StatelessWidget { 9 | 10 | final DeviceDetailsBloc _deviceDetailsBloc; 11 | 12 | AutoTestView(this._deviceDetailsBloc); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Padding( 17 | padding: const EdgeInsets.all(16.0), 18 | child: Column(children: [ 19 | Expanded( 20 | flex: 1, 21 | child: SingleChildScrollView( 22 | child: _createAutoTestControlPanel(), 23 | ), 24 | ), 25 | Expanded( 26 | flex: 9, 27 | child: LogsContainerView(_deviceDetailsBloc.logs), 28 | ) 29 | ]), 30 | ); 31 | } 32 | 33 | Widget _createAutoTestControlPanel() { 34 | return Row( 35 | children: [ 36 | ButtonView('Start Auto Test', action: _startAutoTest), 37 | ], 38 | ); 39 | } 40 | 41 | void _startAutoTest() { 42 | _deviceDetailsBloc.startAutoTest(); 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /example/lib/device_details/view/button_view.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | 4 | class ButtonView extends StatelessWidget { 5 | 6 | final String _text; 7 | final Function action; 8 | 9 | ButtonView(this._text, { this.action }); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Expanded( 14 | child: Padding( 15 | padding: const EdgeInsets.all(2.0), 16 | child: RaisedButton( 17 | color: Colors.blue, 18 | textColor: Colors.white, 19 | child: Text(_text), 20 | onPressed: action, 21 | ), 22 | ), 23 | ); 24 | } 25 | } -------------------------------------------------------------------------------- /example/lib/device_details/view/logs_container_view.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:blemulator_example/device_details/device_details_bloc.dart'; 4 | import 'package:rxdart/rxdart.dart'; 5 | 6 | class LogsContainerView extends StatelessWidget { 7 | 8 | final Observable> _logs; 9 | 10 | LogsContainerView(this._logs); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Container( 15 | margin: EdgeInsets.symmetric(vertical: 8.0), 16 | decoration: BoxDecoration( 17 | color: Colors.white, 18 | borderRadius: BorderRadius.all(Radius.circular(4.0))), 19 | child: SizedBox.expand( 20 | child: Column( 21 | children: [ 22 | Flexible( 23 | child: StreamBuilder>( 24 | initialData: [], 25 | stream: _logs, 26 | builder: (context, snapshot) => _buildLogs(context, snapshot), 27 | ), 28 | ), 29 | ], 30 | ), 31 | ), 32 | ); 33 | } 34 | 35 | Widget _buildLogs(BuildContext context, AsyncSnapshot> logs) { 36 | return ListView.builder( 37 | itemCount: logs.data.length, 38 | shrinkWrap: true, 39 | itemBuilder: (buildContext, index) => Container( 40 | decoration: BoxDecoration( 41 | border: Border( 42 | top: BorderSide( 43 | color: Colors.grey, 44 | width: 0.5, 45 | ), 46 | bottom: BorderSide( 47 | color: Colors.grey, 48 | width: 0.5, 49 | ), 50 | ), 51 | ), 52 | child: Padding( 53 | padding: const EdgeInsets.only(top: 2.0), 54 | child: Row( 55 | crossAxisAlignment: CrossAxisAlignment.start, 56 | children: [ 57 | Padding( 58 | padding: const EdgeInsets.only(right: 8.0), 59 | child: Text( 60 | logs.data[index].time, 61 | style: TextStyle(fontSize: 9), 62 | ), 63 | ), 64 | Flexible( 65 | child: Text(logs.data[index].content, 66 | overflow: TextOverflow.ellipsis, 67 | softWrap: true, 68 | style: TextStyle(fontSize: 13)), 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:blemulator_example/devices_list/devices_bloc.dart'; 3 | import 'package:blemulator_example/repository/device_repository.dart'; 4 | import 'package:flutter_ble_lib/flutter_ble_lib.dart'; 5 | 6 | class DevicesBlocProvider extends InheritedWidget { 7 | final DevicesBloc devicesBloc; 8 | 9 | DevicesBlocProvider({ 10 | Key key, 11 | DevicesBloc devicesBloc, 12 | Widget child, 13 | }) : devicesBloc = devicesBloc ?? 14 | DevicesBloc(DeviceRepository(), BleManager()), 15 | super(key: key, child: child); 16 | 17 | @override 18 | bool updateShouldNotify(InheritedWidget oldWidget) => true; 19 | 20 | static DevicesBloc of(BuildContext context) => 21 | (context.inheritFromWidgetOfExactType(DevicesBlocProvider) 22 | as DevicesBlocProvider) 23 | .devicesBloc; 24 | } 25 | -------------------------------------------------------------------------------- /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/di/ble_adapter_injector.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/adapter/ble_adapter.dart'; 2 | import 'package:flutter_ble_lib/flutter_ble_lib.dart'; 3 | import 'package:blemulator/blemulator.dart'; 4 | 5 | class BleAdapterInjector { 6 | static BleAdapter _instance; 7 | 8 | static BleAdapter get inject { 9 | _instance ??= BleAdapter(BleManager(), Blemulator()); 10 | return _instance; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/example_peripherals/generic_peripheral.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:blemulator/blemulator.dart'; 4 | 5 | class GenericPeripheral extends SimulatedPeripheral { 6 | GenericPeripheral( 7 | {String name = 'Generic Peripheral', 8 | String id = 'generic-peripheral-id', 9 | int milliseconds = 1000, 10 | List services = const []}) 11 | : super( 12 | name: name, 13 | id: id, 14 | advertisementInterval: Duration(milliseconds: milliseconds), 15 | services: [ 16 | SimulatedService( 17 | uuid: 'F000AA00-0001-4000-B000-000000000000', 18 | isAdvertised: true, 19 | characteristics: [ 20 | SimulatedCharacteristic( 21 | uuid: 'F000AA10-0001-4000-B000-000000000000', 22 | value: Uint8List.fromList([0]), 23 | convenienceName: 'Generic characteristic 1'), 24 | ], 25 | convenienceName: 'Generic service 1', 26 | ), 27 | SimulatedService( 28 | uuid: 'F000AA01-0001-4000-B000-000000000000', 29 | isAdvertised: true, 30 | characteristics: [ 31 | SimulatedCharacteristic( 32 | uuid: 'F000AA11-0001-4000-B000-000000000000', 33 | value: Uint8List.fromList([0]), 34 | convenienceName: 'Generic characteristic 2'), 35 | ], 36 | convenienceName: 'Generic service 2', 37 | ), 38 | SimulatedService( 39 | uuid: 'F000AA02-0001-4000-B000-000000000000', 40 | isAdvertised: true, 41 | characteristics: [ 42 | SimulatedCharacteristic( 43 | uuid: 'F000AA12-0001-4000-B000-000000000000', 44 | value: Uint8List.fromList([0]), 45 | convenienceName: 'Generic characteristic 3'), 46 | ], 47 | convenienceName: 'Generic service 3', 48 | ), 49 | SimulatedService( 50 | uuid: 'F000AA03-0001-4000-B000-000000000000', 51 | isAdvertised: true, 52 | characteristics: [ 53 | SimulatedCharacteristic( 54 | uuid: 'F000AA13-0001-4000-B000-000000000000', 55 | value: Uint8List.fromList([0]), 56 | convenienceName: 'Generic characteristic 4'), 57 | ], 58 | convenienceName: 'Generic service 4', 59 | ), 60 | ], 61 | ); 62 | 63 | @override 64 | Future onConnectRequest() async { 65 | await Future.delayed(Duration(milliseconds: 500)); 66 | return super.onConnectRequest(); 67 | } 68 | 69 | @override 70 | Future onDiscoveryRequest() async { 71 | await Future.delayed(Duration(milliseconds: 1000)); 72 | return super.onDiscoveryRequest(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/device_details/device_detail_view.dart'; 2 | import 'package:blemulator_example/device_details/devices_details_bloc_provider.dart'; 3 | import 'package:blemulator_example/devices_list/devices_bloc_provider.dart'; 4 | import 'package:blemulator_example/devices_list/devices_list_view.dart'; 5 | import 'package:blemulator_example/navigation/bloc.dart'; 6 | import 'package:blemulator_example/navigation/route_name.dart'; 7 | import 'package:blemulator_example/navigation/router.dart'; 8 | import 'package:blemulator_example/styles/custom_colors.dart'; 9 | import 'package:blemulator_example/styles/custom_theme.dart'; 10 | import 'package:fimber/fimber.dart'; 11 | import 'package:flutter/material.dart' hide Router; 12 | import 'package:flutter_bloc/flutter_bloc.dart'; 13 | 14 | void main() { 15 | Fimber.plantTree(DebugTree()); 16 | runApp(MyApp()); 17 | } 18 | 19 | final RouteObserver routeObserver = RouteObserver(); 20 | 21 | class MyApp extends StatelessWidget { 22 | final GlobalKey _navigatorKey = GlobalKey(); 23 | final bool useNewExample = false; 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return useNewExample ? _buildNewExample() : _buildExample(); 28 | } 29 | 30 | Widget _buildExample() { 31 | return MaterialApp(title: 'Blemulator example', 32 | theme: ThemeData( 33 | primaryColor: Color(0xFF0A3D91), 34 | accentColor: Color(0xFFCC0000), 35 | ), 36 | initialRoute: '/', 37 | routes: { 38 | '/': (context) => DevicesBlocProvider(child: DevicesListScreen()), 39 | '/details': (context) => 40 | DeviceDetailsBlocProvider(child: DeviceDetailsView()), 41 | }, 42 | navigatorObservers: [routeObserver], 43 | ); 44 | } 45 | 46 | Widget _buildNewExample() { 47 | return BlocProvider( 48 | create: (context) => NavigationBloc(navigatorKey: _navigatorKey), 49 | child: MaterialApp( 50 | navigatorKey: _navigatorKey, 51 | title: 'Blemulator example', 52 | theme: ThemeData( 53 | primaryColor: CustomColors.primary, 54 | accentColor: CustomColors.accent, 55 | scaffoldBackgroundColor: CustomColors.scaffoldBackground, 56 | cardTheme: CustomTheme.card, 57 | ), 58 | initialRoute: RouteName.home, 59 | onGenerateRoute: (settings) => Router.generateRoute(settings), 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /example/lib/model/ble_device.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:collection/collection.dart'; 4 | import 'package:flutter_ble_lib/flutter_ble_lib.dart'; 5 | 6 | abstract class BleDevice { 7 | String id; 8 | int counter = 0; 9 | final String name; 10 | DeviceCategory _category; 11 | bool _isConnected = false; 12 | Peripheral peripheral; 13 | 14 | bool get isConnected => _isConnected; 15 | 16 | DeviceCategory get category => _category; 17 | 18 | BleDevice(this.name, this.id, this.peripheral) { 19 | _category = _nameToCategory(name); 20 | if (name == null) {} 21 | } 22 | 23 | factory BleDevice.connected(BleDevice bleDevice) { 24 | return ConnectedBleDevice( 25 | bleDevice.name, bleDevice.id, bleDevice.peripheral); 26 | } 27 | 28 | factory BleDevice.notConnected( 29 | String name, String id, Peripheral peripheral) { 30 | return DisconnectedBleDevice(name, id, peripheral); 31 | } 32 | 33 | DeviceCategory _nameToCategory(String name) { 34 | if (name == 'SensorTag') { 35 | return DeviceCategory.sensorTag; 36 | } else if (name != null && name.startsWith('Hex')) { 37 | return DeviceCategory.hex; 38 | } else { 39 | return DeviceCategory.other; 40 | } 41 | } 42 | 43 | @override 44 | int get hashCode => 123; 45 | 46 | @override 47 | bool operator ==(other) => 48 | other is BleDevice && 49 | name != null && 50 | other.name != null && 51 | compareAsciiLowerCase(name, other.name) == 0 && 52 | id == other.id; 53 | 54 | @override 55 | String toString() { 56 | return 'BleDevice{counter: $counter, name: $name}'; 57 | } 58 | 59 | void abandon(); 60 | } 61 | 62 | class DisconnectedBleDevice extends BleDevice { 63 | StreamController _devicesInConnectingProcess; 64 | 65 | DisconnectedBleDevice(String name, String id, Peripheral peripheral) 66 | : super(name ?? '', id, peripheral); 67 | 68 | @override 69 | String toString() { 70 | return 'DisconnectedBleDevice{} ${super.toString()}'; 71 | } 72 | 73 | @override 74 | void abandon() { 75 | _devicesInConnectingProcess?.close(); 76 | } 77 | } 78 | 79 | class ConnectedBleDevice extends BleDevice { 80 | ConnectedBleDevice(String name, String id, Peripheral peripheral) 81 | : super(name ?? '', id, peripheral); 82 | 83 | @override 84 | void abandon() {} 85 | } 86 | 87 | enum DeviceCategory { sensorTag, hex, other } 88 | -------------------------------------------------------------------------------- /example/lib/model/ble_peripheral.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:flutter_ble_lib/flutter_ble_lib.dart'; 3 | 4 | class BlePeripheral extends Equatable { 5 | final String name; 6 | final String id; 7 | final int rssi; 8 | final bool isConnected; 9 | final BlePeripheralCategory category; 10 | 11 | BlePeripheral(this.name, this.id, this.rssi, this.isConnected, this.category); 12 | 13 | @override 14 | List get props => [name, id]; 15 | } 16 | 17 | enum BlePeripheralCategory { sensorTag, other } 18 | 19 | class BlePeripheralCategoryResolver { 20 | static const String sensorTag = 'SensorTag'; 21 | 22 | static BlePeripheralCategory categoryForScanResult(ScanResult scanResult) { 23 | return _isSensorTag(scanResult.peripheral.name) 24 | ? BlePeripheralCategory.sensorTag 25 | : BlePeripheralCategory.other; 26 | } 27 | 28 | static bool _isSensorTag(String blePeripheralName) { 29 | return blePeripheralName == sensorTag; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/lib/model/ble_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:flutter_ble_lib/flutter_ble_lib.dart'; 5 | 6 | class BleService extends Equatable { 7 | final String uuid; 8 | final List characteristics; 9 | 10 | BleService(this.uuid, this.characteristics); 11 | 12 | @override 13 | List get props => [uuid, characteristics]; 14 | } 15 | 16 | class BleCharacteristic extends Equatable { 17 | final String uuid; 18 | final Uint8List value; 19 | final bool isReadable; 20 | final bool isWritableWithResponse; 21 | final bool isWritableWithoutResponse; 22 | final bool isNotifiable; 23 | final bool isIndicatable; 24 | 25 | BleCharacteristic( 26 | this.uuid, 27 | this.value, 28 | this.isReadable, 29 | this.isWritableWithResponse, 30 | this.isWritableWithoutResponse, 31 | this.isNotifiable, 32 | this.isIndicatable, 33 | ); 34 | 35 | @override 36 | List get props => [ 37 | uuid, 38 | value, 39 | isReadable, 40 | isWritableWithResponse, 41 | isWritableWithoutResponse, 42 | isNotifiable, 43 | isIndicatable 44 | ]; 45 | 46 | BleCharacteristic.fromCharacteristic(Characteristic characteristic) 47 | : uuid = characteristic.uuid, 48 | value = null, 49 | isReadable = characteristic.isReadable, 50 | isWritableWithResponse = characteristic.isWritableWithResponse, 51 | isWritableWithoutResponse = characteristic.isWritableWithoutResponse, 52 | isNotifiable = characteristic.isNotifiable, 53 | isIndicatable = characteristic.isIndicatable; 54 | } 55 | -------------------------------------------------------------------------------- /example/lib/model/signal_level.dart: -------------------------------------------------------------------------------- 1 | enum SignalLevel { high, medium, low, unknown } 2 | 3 | SignalLevel signalLevelForRssi(int rssi) { 4 | if (rssi == null) return SignalLevel.unknown; 5 | if (rssi > -60) { 6 | return SignalLevel.high; 7 | } else if (rssi > -90) { 8 | return SignalLevel.medium; 9 | } else { 10 | return SignalLevel.low; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/navigation/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'navigation_bloc.dart'; 2 | export 'navigation_event.dart'; -------------------------------------------------------------------------------- /example/lib/navigation/navigation_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:blemulator_example/navigation/route_name.dart'; 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:flutter/material.dart'; 5 | import './bloc.dart'; 6 | 7 | class NavigationBloc extends Bloc { 8 | final GlobalKey navigatorKey; 9 | 10 | NavigationBloc({@required this.navigatorKey}); 11 | 12 | @override 13 | void get initialState { 14 | return; 15 | } 16 | 17 | @override 18 | Stream mapEventToState( 19 | NavigationEvent event, 20 | ) async* { 21 | if (event is Pop) { 22 | navigatorKey.currentState.pop(); 23 | } else if (event is NavigateToPeripheralDetails) { 24 | await navigatorKey.currentState 25 | .pushNamed(RouteName.peripheralDetails, arguments: event.peripheral); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/lib/navigation/navigation_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/model/ble_peripheral.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | abstract class NavigationEvent extends Equatable { 5 | const NavigationEvent(); 6 | 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class Pop extends NavigationEvent {} 12 | 13 | class NavigateToPeripheralDetails extends NavigationEvent { 14 | final BlePeripheral peripheral; 15 | 16 | const NavigateToPeripheralDetails({this.peripheral}); 17 | 18 | @override 19 | List get props => [peripheral]; 20 | } -------------------------------------------------------------------------------- /example/lib/navigation/route_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:bloc/bloc.dart'; 4 | 5 | class RouteFactory { 6 | static MaterialPageRoute build>( 7 | Bloc bloc, 8 | Widget view, 9 | ) { 10 | return MaterialPageRoute( 11 | builder: (_) => BlocProvider( 12 | create: (_) => bloc, 13 | child: view, 14 | ), 15 | ); 16 | } 17 | } -------------------------------------------------------------------------------- /example/lib/navigation/route_name.dart: -------------------------------------------------------------------------------- 1 | class RouteName { 2 | static const String home = '/'; 3 | static const String peripheralDetails = '/peripheralDetails'; 4 | } -------------------------------------------------------------------------------- /example/lib/navigation/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/di/ble_adapter_injector.dart'; 2 | import 'package:blemulator_example/navigation/route_factory.dart' as navigation; 3 | import 'package:blemulator_example/navigation/route_name.dart'; 4 | import 'package:blemulator_example/peripheral_details/bloc.dart'; 5 | import 'package:blemulator_example/peripheral_details/peripheral_details_screen.dart'; 6 | import 'package:blemulator_example/peripheral_list/bloc.dart'; 7 | import 'package:blemulator_example/peripheral_list/peripheral_list_screen.dart'; 8 | import 'package:flutter/material.dart'; 9 | 10 | class Router { 11 | static Route generateRoute(RouteSettings settings) { 12 | switch (settings.name) { 13 | case RouteName.home: 14 | return navigation.RouteFactory.build( 15 | PeripheralListBloc(BleAdapterInjector.inject), 16 | PeripheralListScreen(), 17 | ); 18 | case RouteName.peripheralDetails: 19 | return navigation.RouteFactory.build( 20 | PeripheralDetailsBloc( 21 | BleAdapterInjector.inject, settings.arguments), 22 | PeripheralDetailsScreen(), 23 | ); 24 | default: 25 | return navigation.RouteFactory.build( 26 | PeripheralListBloc(BleAdapterInjector.inject), 27 | PeripheralListScreen(), 28 | ); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /example/lib/peripheral_details/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'peripheral_details_bloc.dart'; 2 | export 'peripheral_details_event.dart'; 3 | export 'peripheral_details_state.dart'; -------------------------------------------------------------------------------- /example/lib/peripheral_details/components/characteristics_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/model/ble_service.dart'; 2 | import 'package:blemulator_example/styles/custom_text_style.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | class CharacteristicsView extends StatelessWidget { 7 | final BleCharacteristic _characteristic; 8 | 9 | CharacteristicsView(this._characteristic); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Card( 14 | child: Padding( 15 | padding: const EdgeInsets.all(8.0), 16 | child: Column( 17 | crossAxisAlignment: CrossAxisAlignment.start, 18 | children: [ 19 | Text( 20 | 'UUID: ${_characteristic.uuid}', 21 | style: CustomTextStyle.characteristicsStyle, 22 | ), 23 | Text( 24 | 'Properties: ${_getCharacteristicProperties(_characteristic).toString()}', 25 | style: CustomTextStyle.characteristicsStyle, 26 | ), 27 | ], 28 | ), 29 | ), 30 | ); 31 | } 32 | 33 | static List _getCharacteristicProperties( 34 | BleCharacteristic characteristic) { 35 | var properties = []; 36 | 37 | if (characteristic.isWritableWithResponse || 38 | characteristic.isWritableWithoutResponse) { 39 | properties.add('write'); 40 | } 41 | if (characteristic.isReadable) { 42 | properties.add('read'); 43 | } 44 | if (characteristic.isIndicatable || characteristic.isNotifiable) { 45 | properties.add('notify'); 46 | } 47 | 48 | return properties; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /example/lib/peripheral_details/components/peripheral_details_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/common/components/property_row.dart'; 2 | import 'package:blemulator_example/peripheral_details/bloc.dart'; 3 | import 'package:blemulator_example/peripheral_details/components/services_sliver.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | import 'package:flutter_bloc/flutter_bloc.dart'; 7 | 8 | class PeripheralDetailsView extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | return CustomScrollView( 12 | slivers: [ 13 | SliverPadding( 14 | padding: const EdgeInsets.all(8.0), 15 | sliver: SliverToBoxAdapter( 16 | child: BlocBuilder( 17 | builder: (context, state) { 18 | return Column( 19 | mainAxisSize: MainAxisSize.min, 20 | children: [ 21 | PropertyRow( 22 | title: 'Identifier', 23 | titleIcon: Icon(Icons.perm_device_information), 24 | titleColor: Theme.of(context).primaryColor, 25 | value: state.peripheral.id, 26 | ), 27 | ], 28 | ); 29 | }, 30 | ), 31 | ), 32 | ), 33 | BlocBuilder( 34 | builder: (context, state) { 35 | return ServicesSliver(state.bleServiceStates); 36 | }, 37 | ) 38 | ], 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /example/lib/peripheral_details/components/services_sliver.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/common/components/property_row.dart'; 2 | import 'package:blemulator_example/peripheral_details/components/characteristics_view.dart'; 3 | import 'package:blemulator_example/peripheral_details/peripheral_details_bloc.dart'; 4 | import 'package:blemulator_example/peripheral_details/peripheral_details_event.dart'; 5 | import 'package:blemulator_example/peripheral_details/peripheral_details_state.dart'; 6 | import 'package:blemulator_example/styles/custom_text_style.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter/widgets.dart'; 9 | import 'package:flutter_bloc/flutter_bloc.dart'; 10 | 11 | class ServicesSliver extends StatelessWidget { 12 | final List _bleServiceStates; 13 | 14 | ServicesSliver(this._bleServiceStates); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return SliverList( 19 | delegate: SliverChildBuilderDelegate( 20 | (context, index) => 21 | _createServiceTileView(context, _bleServiceStates[index], index), 22 | childCount: _bleServiceStates.length, 23 | ), 24 | ); 25 | } 26 | 27 | Widget _createServiceTileView( 28 | BuildContext context, 29 | BleServiceState serviceState, 30 | int index, 31 | ) { 32 | // ignore: close_sinks 33 | final bloc = 34 | BlocProvider.of(context); 35 | 36 | return Column( 37 | children: [ 38 | PropertyRow( 39 | title: 'Service UUID', 40 | titleColor: Theme.of(context).primaryColor, 41 | value: serviceState.service.uuid, 42 | valueTextStyle: CustomTextStyle.serviceUuidStyle, 43 | rowAccessory: IconButton( 44 | icon: Icon( 45 | serviceState.expanded ? Icons.unfold_less : Icons.unfold_more), 46 | onPressed: () => bloc.add(ServiceViewExpandedEvent( 47 | index, 48 | )), 49 | ), 50 | ), 51 | if (serviceState.expanded) 52 | Padding( 53 | padding: EdgeInsets.only(left: 16.0), 54 | child: ListView.builder( 55 | itemCount: serviceState.service.characteristics.length, 56 | itemBuilder: (context, index) => CharacteristicsView( 57 | serviceState.service.characteristics[index]), 58 | shrinkWrap: true, 59 | physics: NeverScrollableScrollPhysics(), 60 | ), 61 | ), 62 | ], 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /example/lib/peripheral_details/peripheral_details_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:blemulator_example/adapter/ble_adapter.dart'; 3 | import 'package:blemulator_example/model/ble_peripheral.dart'; 4 | import 'package:bloc/bloc.dart'; 5 | import './bloc.dart'; 6 | import 'package:flutter_ble_lib/flutter_ble_lib.dart'; 7 | 8 | class PeripheralDetailsBloc 9 | extends Bloc { 10 | final BleAdapter _bleAdapter; 11 | final BlePeripheral _chosenPeripheral; 12 | 13 | PeripheralDetailsBloc(this._bleAdapter, this._chosenPeripheral) { 14 | try { 15 | //TODO check if device is connected 16 | _bleAdapter 17 | .discoverAndGetServicesCharacteristics(_chosenPeripheral.id) 18 | .then( 19 | (bleServices) { 20 | add(ServicesFetchedEvent(bleServices)); 21 | }, 22 | ); 23 | } on BleError catch (_) { 24 | // TODO handle the error. 25 | // To my knowledge only possible cause is either peripheral got 26 | // disconnected or Bluetooth has been turned off, 27 | // so it should be handled the same way as disconnection. 28 | } 29 | } 30 | 31 | @override 32 | PeripheralDetailsState get initialState => 33 | PeripheralDetailsState(peripheral: _chosenPeripheral); 34 | 35 | @override 36 | Stream mapEventToState( 37 | PeripheralDetailsEvent event, 38 | ) async* { 39 | if (event is ServicesFetchedEvent) { 40 | yield _mapServicesFetchedEventToState(event); 41 | } else if (event is ServiceViewExpandedEvent) { 42 | yield _mapServiceViewExpandedEventToState(event); 43 | } 44 | } 45 | 46 | PeripheralDetailsState _mapServicesFetchedEventToState( 47 | ServicesFetchedEvent event, 48 | ) { 49 | return PeripheralDetailsState( 50 | peripheral: state.peripheral, 51 | bleServiceStates: event.services 52 | .map((service) => BleServiceState(service: service, expanded: false)) 53 | .toList(), 54 | ); 55 | } 56 | 57 | PeripheralDetailsState _mapServiceViewExpandedEventToState( 58 | ServiceViewExpandedEvent event, 59 | ) { 60 | var newBleServiceStates = 61 | List.from(state.bleServiceStates); 62 | 63 | newBleServiceStates[event.expandedViewIndex] = BleServiceState( 64 | service: state.bleServiceStates[event.expandedViewIndex].service, 65 | expanded: !state.bleServiceStates[event.expandedViewIndex].expanded); 66 | 67 | return PeripheralDetailsState( 68 | peripheral: state.peripheral, 69 | bleServiceStates: newBleServiceStates, 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /example/lib/peripheral_details/peripheral_details_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/model/ble_service.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | abstract class PeripheralDetailsEvent extends Equatable { 5 | const PeripheralDetailsEvent(); 6 | } 7 | 8 | class ServicesFetchedEvent extends PeripheralDetailsEvent { 9 | @override 10 | List get props => [services]; 11 | 12 | final List services; 13 | 14 | ServicesFetchedEvent(this.services); 15 | } 16 | 17 | class ServiceViewExpandedEvent extends PeripheralDetailsEvent { 18 | @override 19 | List get props => [expandedViewIndex]; 20 | 21 | final int expandedViewIndex; 22 | 23 | ServiceViewExpandedEvent(this.expandedViewIndex); 24 | } 25 | -------------------------------------------------------------------------------- /example/lib/peripheral_details/peripheral_details_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/model/ble_peripheral.dart'; 2 | import 'package:blemulator_example/peripheral_details/bloc.dart'; 3 | import 'package:blemulator_example/peripheral_details/components/peripheral_details_view.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | 7 | class PeripheralDetailsScreen extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return BlocBuilder( 11 | builder: (context, state) { 12 | if (state.peripheral.category == BlePeripheralCategory.sensorTag) { 13 | return DefaultTabController( 14 | length: 3, 15 | child: Scaffold( 16 | appBar: AppBar( 17 | title: Text(state.peripheral.name), 18 | bottom: TabBar(tabs: [ 19 | Tab( 20 | icon: Icon(Icons.table_chart), 21 | text: 'Details', 22 | ), 23 | Tab( 24 | icon: Icon(Icons.format_list_numbered), 25 | text: 'Auto test', 26 | ), 27 | Tab( 28 | icon: Icon(Icons.settings), 29 | text: 'Manual test', 30 | ), 31 | ]), 32 | ), 33 | body: TabBarView( 34 | children: [ 35 | _buildDetailsView(), 36 | Text('Auto test'), 37 | Text('Manual test'), 38 | ], 39 | ), 40 | ), 41 | ); 42 | } else { 43 | return Scaffold( 44 | appBar: AppBar( 45 | title: Text(state.peripheral.name), 46 | ), 47 | body: _buildDetailsView(), 48 | ); 49 | } 50 | }, 51 | ); 52 | } 53 | 54 | Widget _buildDetailsView() { 55 | return PeripheralDetailsView(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /example/lib/peripheral_details/peripheral_details_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/model/ble_peripheral.dart'; 2 | import 'package:blemulator_example/model/ble_service.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | 6 | class PeripheralDetailsState extends Equatable { 7 | final BlePeripheral peripheral; 8 | final List bleServiceStates; 9 | 10 | const PeripheralDetailsState( 11 | {@required this.peripheral, this.bleServiceStates = const []}); 12 | 13 | @override 14 | List get props => [peripheral, bleServiceStates]; 15 | } 16 | 17 | class BleServiceState extends Equatable { 18 | final BleService service; 19 | final bool expanded; 20 | 21 | @override 22 | List get props => [service, expanded]; 23 | 24 | BleServiceState({@required this.service, @required this.expanded}); 25 | } -------------------------------------------------------------------------------- /example/lib/peripheral_list/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'peripheral_list_bloc.dart'; 2 | export 'peripheral_list_event.dart'; 3 | export 'peripheral_list_state.dart'; -------------------------------------------------------------------------------- /example/lib/peripheral_list/components/peripheral_category_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/common/components/title_icon.dart'; 2 | import 'package:blemulator_example/common/components/title_image_icon.dart'; 3 | import 'package:blemulator_example/model/ble_peripheral.dart'; 4 | import 'package:blemulator_example/styles/custom_colors.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class PeripheralCategoryIcon extends StatelessWidget { 8 | final BlePeripheralCategory _peripheralCategory; 9 | 10 | PeripheralCategoryIcon(this._peripheralCategory); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | if (_peripheralCategory == BlePeripheralCategory.sensorTag) { 15 | return TitleImageIcon( 16 | AssetImage('assets/ti_logo.png'), 17 | color: CustomColors.sensorTagRed, 18 | ); 19 | } else { 20 | return TitleIcon( 21 | Icons.bluetooth, 22 | color: Theme.of(context).primaryColor, 23 | ); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/lib/peripheral_list/components/peripheral_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/model/ble_peripheral.dart'; 2 | import 'package:blemulator_example/navigation/bloc.dart'; 3 | import 'package:blemulator_example/common/components/property_row.dart'; 4 | import 'package:blemulator_example/peripheral_list/components/peripheral_category_icon.dart'; 5 | import 'package:blemulator_example/peripheral_list/components/rssi_view.dart'; 6 | import 'package:blemulator_example/styles/custom_colors.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_bloc/flutter_bloc.dart'; 9 | 10 | class PeripheralItem extends StatelessWidget { 11 | final BlePeripheral _peripheral; 12 | 13 | PeripheralItem(this._peripheral); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | final navigationBloc = BlocProvider.of(context); 18 | 19 | return PropertyRow( 20 | title: _peripheral.id, 21 | titleIcon: PeripheralCategoryIcon(_peripheral.category), 22 | titleColor: _peripheral.category.color(context), 23 | value: _peripheral.name, 24 | titleAccessory: Icon( 25 | Icons.chevron_right, 26 | color: Colors.grey, 27 | ), 28 | valueAccessory: RssiView(_peripheral.rssi), 29 | onTap: () => _onRowTap(navigationBloc), 30 | ); 31 | } 32 | 33 | void _onRowTap(NavigationBloc navigationBloc) { 34 | navigationBloc.add(NavigateToPeripheralDetails(peripheral: _peripheral)); 35 | } 36 | } 37 | 38 | extension on BlePeripheralCategory { 39 | Color color(BuildContext context) { 40 | if (this == BlePeripheralCategory.sensorTag) { 41 | return CustomColors.sensorTagRed; 42 | } else { 43 | return Theme.of(context).primaryColor; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /example/lib/peripheral_list/components/rssi_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/styles/custom_text_style.dart'; 2 | import 'package:blemulator_example/model/signal_level.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class RssiView extends StatelessWidget { 6 | final int _rssi; 7 | 8 | RssiView(this._rssi); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Row( 13 | crossAxisAlignment: CrossAxisAlignment.end, 14 | children: [ 15 | Text( 16 | _formatRssi(), 17 | style: CustomTextStyle.cardValueAccessory 18 | .copyWith(color: signalLevelForRssi(_rssi).color()), 19 | ), 20 | Padding( 21 | padding: const EdgeInsets.only(left: 4.0), 22 | child: Icon( 23 | Icons.settings_input_antenna, 24 | color: signalLevelForRssi(_rssi).color(), 25 | ), 26 | ), 27 | ], 28 | mainAxisSize: MainAxisSize.min, 29 | ); 30 | } 31 | 32 | String _formatRssi() { 33 | return '${_rssi ?? '-'} dBm'; 34 | } 35 | } 36 | 37 | extension on SignalLevel { 38 | Color color() { 39 | switch (this) { 40 | case SignalLevel.high: 41 | return Colors.green; 42 | case SignalLevel.medium: 43 | return Colors.orange; 44 | case SignalLevel.low: 45 | return Colors.red; 46 | case SignalLevel.unknown: 47 | default: 48 | return Colors.grey; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /example/lib/peripheral_list/peripheral_list_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:blemulator_example/model/ble_peripheral.dart'; 3 | import 'package:blemulator_example/adapter/ble_adapter.dart'; 4 | import 'package:bloc/bloc.dart'; 5 | import './bloc.dart'; 6 | 7 | class PeripheralListBloc 8 | extends Bloc { 9 | final BleAdapter _bleAdapter; 10 | StreamSubscription _blePeripheralsSubscription; 11 | 12 | PeripheralListBloc(this._bleAdapter); 13 | 14 | @override 15 | PeripheralListState get initialState => PeripheralListState.initial(); 16 | 17 | @override 18 | Stream mapEventToState( 19 | PeripheralListEvent event, 20 | ) async* { 21 | if (event is StartPeripheralScan) { 22 | yield* _mapStartPeripheralScanToState(event); 23 | } else if (event is StopPeripheralScan) { 24 | yield* _mapStopPeripheralScanToState(event); 25 | } else if (event is NewPeripheralScan) { 26 | yield* _mapNewPeripheralScanToState(event); 27 | } 28 | } 29 | 30 | Stream _mapStartPeripheralScanToState( 31 | StartPeripheralScan event) async* { 32 | _cancelBlePeripheralSubscription(); 33 | _blePeripheralsSubscription = 34 | _bleAdapter.blePeripherals.listen((BlePeripheral peripheral) { 35 | add(NewPeripheralScan(peripheral)); 36 | }); 37 | yield PeripheralListState(peripherals: state.peripherals, scanningEnabled: true); 38 | } 39 | 40 | Stream _mapStopPeripheralScanToState( 41 | StopPeripheralScan event) async* { 42 | _cancelBlePeripheralSubscription(); 43 | yield PeripheralListState( 44 | peripherals: state.peripherals, scanningEnabled: false); 45 | } 46 | 47 | Stream _mapNewPeripheralScanToState( 48 | NewPeripheralScan event) async* { 49 | var updatedPeripherals = state.peripherals; 50 | if (!updatedPeripherals.contains(event.peripheral)) { 51 | updatedPeripherals = List.from(state.peripherals); 52 | updatedPeripherals.add(event.peripheral); 53 | } 54 | yield PeripheralListState( 55 | peripherals: updatedPeripherals, 56 | scanningEnabled: state.scanningEnabled); 57 | } 58 | 59 | void _cancelBlePeripheralSubscription() async { 60 | if (_blePeripheralsSubscription != null) { 61 | await _blePeripheralsSubscription.cancel(); 62 | } 63 | } 64 | 65 | @override 66 | Future close() { 67 | _cancelBlePeripheralSubscription(); 68 | return super.close(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /example/lib/peripheral_list/peripheral_list_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/model/ble_peripheral.dart'; 2 | 3 | abstract class PeripheralListEvent { 4 | const PeripheralListEvent(); 5 | } 6 | 7 | class StartPeripheralScan extends PeripheralListEvent {} 8 | 9 | class StopPeripheralScan extends PeripheralListEvent {} 10 | 11 | class NewPeripheralScan extends PeripheralListEvent { 12 | final BlePeripheral peripheral; 13 | 14 | const NewPeripheralScan(this.peripheral); 15 | } 16 | 17 | class PickPeripheral extends PeripheralListEvent { 18 | final BlePeripheral peripheral; 19 | 20 | const PickPeripheral(this.peripheral); 21 | } -------------------------------------------------------------------------------- /example/lib/peripheral_list/peripheral_list_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/peripheral_list/bloc.dart'; 2 | import 'package:blemulator_example/peripheral_list/components/peripheral_item.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | 7 | class PeripheralListScreen extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | final peripheralListBloc = BlocProvider.of(context); 11 | 12 | return Scaffold( 13 | appBar: AppBar( 14 | title: Text('Bluetooth peripherals'), 15 | actions: [ 16 | BlocBuilder( 17 | condition: (previousState, state) { 18 | return previousState.scanningEnabled != state.scanningEnabled; 19 | }, 20 | builder: (context, state) { 21 | return IconButton( 22 | icon: Icon(state.scanningEnabled 23 | ? Icons.bluetooth_searching 24 | : Icons.bluetooth_disabled), 25 | tooltip: state.scanningEnabled 26 | ? 'Disable Bluetooth scanning' 27 | : 'Enable Bluetooth scanning', 28 | onPressed: () => state.scanningEnabled 29 | ? _stopScanning(peripheralListBloc) 30 | : _startScanning(peripheralListBloc), 31 | ); 32 | }, 33 | ) 34 | ], 35 | ), 36 | body: BlocBuilder( 37 | condition: (previousState, state) { 38 | return previousState.peripherals != state.peripherals; 39 | }, 40 | builder: (context, state) { 41 | return ListView.builder( 42 | itemCount: state.peripherals.length, 43 | itemBuilder: (context, index) { 44 | return PeripheralItem(state.peripherals[index]); 45 | }, 46 | padding: EdgeInsets.all(8.0), 47 | ); 48 | }, 49 | ), 50 | ); 51 | } 52 | 53 | void _startScanning(PeripheralListBloc peripheralListBloc) { 54 | peripheralListBloc.add(StartPeripheralScan()); 55 | } 56 | 57 | void _stopScanning(PeripheralListBloc peripheralListBloc) { 58 | peripheralListBloc.add(StopPeripheralScan()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /example/lib/peripheral_list/peripheral_list_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/model/ble_peripheral.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import 'package:flutter/foundation.dart'; 4 | 5 | class PeripheralListState extends Equatable { 6 | final List peripherals; 7 | final bool scanningEnabled; 8 | 9 | const PeripheralListState({@required this.peripherals, @required this.scanningEnabled}); 10 | 11 | const PeripheralListState.initial( 12 | {this.peripherals = const [], this.scanningEnabled = false}); 13 | 14 | @override 15 | List get props => [peripherals, scanningEnabled]; 16 | } 17 | -------------------------------------------------------------------------------- /example/lib/repository/device_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_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 | ValueObservable 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/styles/custom_colors.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | abstract class CustomColors { 6 | static const primary = Color(0xFF0A3D91); 7 | static const accent = Color(0xFFCC0000); 8 | 9 | static const rowCardBackground = Colors.white; 10 | static const scaffoldBackground = Color.fromRGBO(242, 242, 247, 1.0); 11 | 12 | static const sensorTagRed = Color(0xFFCC0000); 13 | } 14 | -------------------------------------------------------------------------------- /example/lib/styles/custom_sizes.dart: -------------------------------------------------------------------------------- 1 | abstract class CustomSizes { 2 | static const double titleIcon = 20.0; 3 | } 4 | -------------------------------------------------------------------------------- /example/lib/styles/custom_text_style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | abstract class CustomTextStyle { 5 | static const cardTitle = TextStyle( 6 | fontSize: 16.0, 7 | fontWeight: FontWeight.w600, 8 | ); 9 | 10 | static const cardValue = TextStyle( 11 | fontSize: 26.0, 12 | fontWeight: FontWeight.w600, 13 | ); 14 | 15 | static const cardValueCompanion = TextStyle( 16 | fontSize: 14.0, 17 | fontWeight: FontWeight.w500, 18 | ); 19 | 20 | static const cardValueAccessory = TextStyle( 21 | fontSize: 14.0, 22 | fontWeight: FontWeight.w500, 23 | color: Colors.grey, 24 | ); 25 | 26 | static const serviceUuidStyle = TextStyle( 27 | fontSize: 14.0, 28 | fontWeight: FontWeight.w500, 29 | color: Colors.grey, 30 | ); 31 | 32 | static const characteristicsStyle = TextStyle( 33 | fontSize: 12.0, 34 | fontWeight: FontWeight.w500, 35 | color: Colors.black26, 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /example/lib/styles/custom_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | abstract class CustomTheme { 4 | static const card = CardTheme( 5 | elevation: 0, 6 | shape: RoundedRectangleBorder( 7 | borderRadius: BorderRadius.all( 8 | Radius.circular(8.0), 9 | ), 10 | ), 11 | ); 12 | } -------------------------------------------------------------------------------- /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 | var 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); 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 | 12 | Future runTestScenario() async { 13 | await _peripheralTestOperations 14 | .connect() 15 | .then((_) => _peripheralTestOperations.cancelTransaction()) 16 | .then((_) => _peripheralTestOperations.discovery()) 17 | .then((_) => _peripheralTestOperations.testRequestingMtu()) 18 | .then((_) => _peripheralTestOperations.testReadingRssi()) 19 | .then((_) => _peripheralTestOperations 20 | .readWriteMonitorCharacteristicForPeripheral()) 21 | .then((_) => _peripheralTestOperations 22 | .readWriteMonitorCharacteristicForService()) 23 | .then((_) => _peripheralTestOperations.readWriteMonitorCharacteristic()) 24 | .then((_) => Future.delayed(Duration(milliseconds: 100))) 25 | .then( 26 | (_) => _peripheralTestOperations.readWriteDescriptorForPeripheral()) 27 | .then((_) => _peripheralTestOperations.readWriteDescriptorForService()) 28 | .then((_) => 29 | _peripheralTestOperations.readWriteDescriptorForCharacteristic()) 30 | .then((_) => _peripheralTestOperations.readWriteDescriptor()) 31 | .then((_) => _peripheralTestOperations.fetchConnectedDevice()) 32 | .then((_) => _peripheralTestOperations.fetchKnownDevice()) 33 | .then((_) => _peripheralTestOperations.disconnect()) 34 | .catchError((error) => _peripheralTestOperations.logError(error)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /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/foundation.dart'; 7 | import 'package:flutter_ble_lib/flutter_ble_lib.dart'; 8 | 9 | import 'package:blemulator_example/sensor_tag_config.dart'; 10 | 11 | part 'base.dart'; 12 | 13 | part 'sensor_tag_test_with_scan_and_connection_scenario.dart'; 14 | 15 | part 'bluetooth_state_toggle_scenario.dart'; 16 | 17 | part 'sensor_tag_test_scenario.dart'; 18 | 19 | part 'peripheral_test_operations.dart'; 20 | -------------------------------------------------------------------------------- /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: blemulator_example 2 | description: Demonstrates how to use the blemulator plugin. 3 | version: 0.0.1+1 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.6.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | rxdart: ^0.22.0 13 | fimber: ^0.1.10 14 | flutter_ble_lib: ^2.2.0 15 | flutter_bloc: ^2.0.0 16 | equatable: ^0.6.1 17 | 18 | # The following adds the Cupertino Icons font to your application. 19 | # Use with the CupertinoIcons class for iOS style icons. 20 | cupertino_icons: ^0.1.2 21 | 22 | dev_dependencies: 23 | flutter_test: 24 | sdk: flutter 25 | 26 | blemulator: 27 | path: ../ 28 | 29 | mockito: ^4.1.1 30 | 31 | # For information on the generic Dart part of this file, see the 32 | # following page: https://dart.dev/tools/pub/pubspec 33 | 34 | # The following section is specific to Flutter. 35 | flutter: 36 | 37 | # The following line ensures that the Material Icons font is 38 | # included with your application, so that you can use the icons in 39 | # the material Icons class. 40 | uses-material-design: true 41 | 42 | # To add assets to your application, add an assets section, like this: 43 | assets: 44 | - assets/ti_logo.png 45 | # - images/a_dot_ham.jpeg 46 | 47 | # An image asset can refer to one or more resolution-specific "variants", see 48 | # https://flutter.dev/assets-and-images/#resolution-aware. 49 | 50 | # For details regarding adding assets from package dependencies, see 51 | # https://flutter.dev/assets-and-images/#from-packages 52 | 53 | # To add custom fonts to your application, add a fonts section here, 54 | # in this "flutter" section. Each entry in this list should have a 55 | # "family" key with the font family name, and a "fonts" key with a 56 | # list giving the asset and other descriptors for the font. For 57 | # example: 58 | # fonts: 59 | # - family: Schyler 60 | # fonts: 61 | # - asset: fonts/Schyler-Regular.ttf 62 | # - asset: fonts/Schyler-Italic.ttf 63 | # style: italic 64 | # - family: Trajan Pro 65 | # fonts: 66 | # - asset: fonts/TrajanPro.ttf 67 | # - asset: fonts/TrajanPro_Bold.ttf 68 | # weight: 700 69 | # 70 | # For details regarding fonts from package dependencies, 71 | # see https://flutter.dev/custom-fonts/#from-packages 72 | -------------------------------------------------------------------------------- /example/test/mock/mocks.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator/blemulator.dart'; 2 | import 'package:blemulator_example/adapter/ble_adapter.dart'; 3 | import 'package:flutter_ble_lib/flutter_ble_lib.dart'; 4 | import 'package:mockito/mockito.dart'; 5 | 6 | class MockBleAdapter extends Mock implements BleAdapter {} 7 | 8 | class MockBleManager extends Mock implements BleManager {} 9 | 10 | class MockBlemulator extends Mock implements Blemulator {} 11 | 12 | class MockScanResult extends Mock implements ScanResult {} 13 | 14 | class MockPeripheral extends Mock implements Peripheral {} 15 | 16 | class MockAdvertisementData extends Mock implements AdvertisementData {} 17 | -------------------------------------------------------------------------------- /example/test/mock/sample_ble_peripheral.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/model/ble_peripheral.dart'; 2 | 3 | class SampleBlePeripheral extends BlePeripheral { 4 | SampleBlePeripheral({ 5 | String name = 'Sample peripheral', 6 | String id = 'peripheral id', 7 | int rssi = -30, 8 | bool isConnected = false, 9 | }) : super(name, id, rssi, isConnected, 10 | BlePeripheralCategory.other); 11 | 12 | SampleBlePeripheral.different({ 13 | String name = 'Different sample peripheral', 14 | String id = 'different peripheral id', 15 | int rssi = -30, 16 | bool isConnected = false, 17 | }) : super(name, id, rssi, isConnected, 18 | BlePeripheralCategory.other); 19 | } 20 | -------------------------------------------------------------------------------- /example/test/mock/sample_ble_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:blemulator_example/model/ble_service.dart'; 4 | 5 | class SampleBleService extends BleService { 6 | SampleBleService({ 7 | String uuid = 'F000AA00-0001-4000-B000-000000000000', 8 | List characteristics 9 | }) : super(uuid, characteristics) { 10 | characteristics ??= [SampleBleCharacteristic()]; 11 | } 12 | } 13 | 14 | class SampleBleCharacteristic extends BleCharacteristic { 15 | SampleBleCharacteristic({ 16 | String uuid = 'F000AA00-0001-4000-B000-000000000000', 17 | Uint8List value, 18 | bool isReadable = true, 19 | bool isWritableWithResponse = false, 20 | bool isWritableWithoutResponse = false, 21 | bool isNotifiable = false, 22 | bool isIndicatable = false 23 | }) : super( 24 | uuid, 25 | value, 26 | isReadable, 27 | isWritableWithResponse, 28 | isWritableWithoutResponse, 29 | isNotifiable, 30 | isIndicatable) { 31 | value ??= Uint8List(1); 32 | } 33 | } -------------------------------------------------------------------------------- /example/test/model/signal_level_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator_example/model/signal_level.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | List rssiTestValues; 6 | List expectedSignalLevels; 7 | 8 | tearDown(() { 9 | rssiTestValues = null; 10 | }); 11 | 12 | void testRssiToSignalLevelParsing() { 13 | rssiTestValues.asMap().forEach((index, rssi) => 14 | expect(signalLevelForRssi(rssi), expectedSignalLevels[index])); 15 | } 16 | 17 | group('SignalLevel for rssi', () { 18 | test('= null is .unknown', () { 19 | // when 20 | rssiTestValues = [null]; 21 | 22 | // then 23 | expectedSignalLevels = [SignalLevel.unknown]; 24 | testRssiToSignalLevelParsing(); 25 | }); 26 | 27 | test('<= -90 is .low', () { 28 | // when 29 | rssiTestValues = [-91, -90]; 30 | 31 | // then 32 | expectedSignalLevels = [SignalLevel.low, SignalLevel.low]; 33 | testRssiToSignalLevelParsing(); 34 | }); 35 | 36 | test('> -90 and <= -60 is .medium', () { 37 | // when 38 | rssiTestValues = [-89, -60]; 39 | 40 | // then 41 | expectedSignalLevels = [ 42 | SignalLevel.medium, 43 | SignalLevel.medium 44 | ]; 45 | testRssiToSignalLevelParsing(); 46 | }); 47 | 48 | test('> -60 is .high', () { 49 | // when 50 | rssiTestValues = [-59, -58]; 51 | 52 | // then 53 | expectedSignalLevels = [SignalLevel.high, SignalLevel.high]; 54 | testRssiToSignalLevelParsing(); 55 | }); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /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 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/BlemulatorPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface BlemulatorPlugin : NSObject 4 | 5 | - (instancetype)initWithPlatformToDartChannel:(FlutterMethodChannel *)platformToDartChannel; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /ios/Classes/BlemulatorPlugin.m: -------------------------------------------------------------------------------- 1 | #import "BlemulatorPlugin.h" 2 | #import "SimulatedAdapter.h" 3 | #import "DartMethodCaller.h" 4 | #import "DartValueHandler.h" 5 | #import "PlatformMethodName.h" 6 | #import "SimulationChannelName.h" 7 | 8 | @import MultiplatformBleAdapter; 9 | 10 | typedef id _Nonnull (^BleAdapterCreator)(dispatch_queue_t _Nonnull queue, NSString * _Nullable restoreIdentifierKey); 11 | 12 | @interface BlemulatorPlugin () 13 | 14 | @property (nonatomic) DartMethodCaller *dartMethodCaller; 15 | @property (nonatomic) DartValueHandler *dartValueHandler; 16 | 17 | @end 18 | 19 | @implementation BlemulatorPlugin 20 | 21 | // MARK: - Public methods 22 | 23 | + (void)registerWithRegistrar:(NSObject *)registrar { 24 | FlutterMethodChannel *dartToPlatformChannel = [FlutterMethodChannel methodChannelWithName:SIMULATION_CHANNEL_NAME_TO_PLATFORM 25 | binaryMessenger:[registrar messenger]]; 26 | FlutterMethodChannel *platformToDartChannel = [FlutterMethodChannel methodChannelWithName:SIMULATION_CHANNEL_NAME_TO_DART 27 | binaryMessenger:[registrar messenger]]; 28 | 29 | BlemulatorPlugin *instance = [[BlemulatorPlugin alloc] initWithPlatformToDartChannel:platformToDartChannel]; 30 | 31 | [registrar addMethodCallDelegate:instance channel:dartToPlatformChannel]; 32 | 33 | } 34 | 35 | // MARK: - Initializers 36 | 37 | - (instancetype)initWithPlatformToDartChannel:(FlutterMethodChannel *)platformToDartChannel { 38 | self = [super init]; 39 | if (self) { 40 | self.dartMethodCaller = [[DartMethodCaller alloc] initWithDartMethodChannel:platformToDartChannel]; 41 | self.dartValueHandler = [DartValueHandler new]; 42 | } 43 | return self; 44 | } 45 | 46 | // MARK: - FlutterMethodCallHandler implementation 47 | 48 | - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { 49 | if ([PLATFORM_METHOD_NAME_SIMULATE isEqualToString:call.method]) { 50 | [self switchToEmulation:result]; 51 | return; 52 | } else { 53 | [self.dartValueHandler handleMethodCall:call result:result]; 54 | return; 55 | } 56 | } 57 | 58 | // MARK: - Private methods 59 | 60 | - (void)switchToEmulation:(FlutterResult)result { 61 | BleAdapterCreator bleAdapterCreator = ^(dispatch_queue_t _Nonnull queue, NSString * _Nullable restoreIdentifierKey) { 62 | return [[SimulatedAdapter alloc] initWithDartMethodCaller:self.dartMethodCaller 63 | dartValueHandler:self.dartValueHandler]; 64 | }; 65 | [BleAdapterFactory setBleAdapterCreator:bleAdapterCreator]; 66 | result(nil); 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /ios/Classes/Bridging/CharacteristicContainer.h: -------------------------------------------------------------------------------- 1 | #import "Characteristic.h" 2 | #import "Descriptor.h" 3 | 4 | @interface CharacteristicContainer : NSObject 5 | 6 | @property (readonly) Characteristic *characteristic; 7 | @property (readonly) NSArray *descriptors; 8 | 9 | - (instancetype)initWithCharacteristic:(Characteristic *)characteristic 10 | descriptors:(NSArray *)descriptors; 11 | 12 | - (NSArray *)descriptorsJsonRepresentation; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ios/Classes/Bridging/CharacteristicContainer.m: -------------------------------------------------------------------------------- 1 | #import "CharacteristicContainer.h" 2 | 3 | @interface CharacteristicContainer () 4 | 5 | @property (readwrite) Characteristic *characteristic; 6 | @property (readwrite) NSArray *descriptors; 7 | 8 | @end 9 | 10 | @implementation CharacteristicContainer 11 | 12 | - (instancetype)initWithCharacteristic:(Characteristic *)characteristic descriptors:(NSArray *)descriptors { 13 | self = [super init]; 14 | if (self) { 15 | self.characteristic = characteristic; 16 | self.descriptors = descriptors; 17 | } 18 | return self; 19 | } 20 | 21 | - (NSArray *)descriptorsJsonRepresentation { 22 | NSMutableArray *result = [[NSMutableArray alloc] init]; 23 | for (Descriptor *descriptor in self.descriptors) { 24 | [result addObject:[descriptor jsonObjectRepresentation]]; 25 | } 26 | 27 | return result; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /ios/Classes/Bridging/Converter/DartCallArgumentsConverter.h: -------------------------------------------------------------------------------- 1 | #import "ScannedPeripheral.h" 2 | #import "ConnectionStateEvent.h" 3 | #import "Characteristic.h" 4 | #import "BleError.h" 5 | 6 | @interface DartCallArgumentsConverter : NSObject 7 | 8 | + (ScannedPeripheral * _Nonnull)scannedPeripheralFromCallArguments:(NSDictionary * _Nonnull)callArguments; 9 | 10 | + (ConnectionStateEvent * _Nonnull)connectionStateEventFromCallArguments:(NSDictionary * _Nonnull)callArguments; 11 | 12 | + (Characteristic * _Nonnull)characteristicFromCallArguments:(NSDictionary * _Nonnull)callArguments; 13 | 14 | + (BleError * _Nonnull)bleErrorFromCallArguments:(NSDictionary * _Nonnull)callArguments; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/Classes/Bridging/Converter/DartResultConverter.h: -------------------------------------------------------------------------------- 1 | #import "DeviceContainer.h" 2 | #import "CharacteristicContainer.h" 3 | 4 | @interface DartResultConverter : NSObject 5 | 6 | + (DeviceContainer *)deviceContainerFromDartResult:(id)result 7 | peripheral:(Peripheral *)peripheral; 8 | 9 | + (Characteristic *)characteristicFromDartResult:(id)result; 10 | 11 | + (Descriptor *)descriptorFromDartResult:(id)result; 12 | @end 13 | -------------------------------------------------------------------------------- /ios/Classes/Bridging/Converter/DomainTypesConverter.h: -------------------------------------------------------------------------------- 1 | #import "Service.h" 2 | #import "Characteristic.h" 3 | #import "Descriptor.h" 4 | 5 | @interface DomainTypesConverter: NSObject 6 | 7 | + (Service *)serviceFromDictionary:(NSDictionary *)dictionary 8 | withPeripheralIdentifier:(NSString *)peripheralIdentifier; 9 | 10 | + (Characteristic *)characteristicFromDictionary:(NSDictionary *)dictionary service:(Service *)service; 11 | 12 | + (Descriptor *)descriptorFromDictionary:(NSDictionary *)dictionary 13 | characteristic:(Characteristic *)characteristic; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ios/Classes/Bridging/DartValueHandler.h: -------------------------------------------------------------------------------- 1 | #import "FlutterMethodCallHandler.h" 2 | #import "ScannedPeripheral.h" 3 | #import "ConnectionStateEvent.h" 4 | #import "Characteristic.h" 5 | #import "BleError.h" 6 | 7 | @protocol DartValueHandlerScanEventDelegate 8 | 9 | - (void)dispatchDartValueHandlerScanEvent:(ScannedPeripheral *)scannedPeripheral; 10 | 11 | @end 12 | 13 | @protocol DartValueHandlerConnectionEventDelegate 14 | 15 | - (void)dispatchDartValueHandlerConnectionStateEvent:(ConnectionStateEvent *)connectionStateEvent; 16 | 17 | @end 18 | 19 | @protocol DartValueHandlerReadEventDelegate 20 | 21 | - (void)dispatchDartValueHandlerReadEvent:(Characteristic *)characteristic 22 | transactionId:(NSString *)transactionId; 23 | 24 | - (void)dispatchDartValueHandlerReadError:(BleError *)bleError 25 | transactionId:(NSString *)transactionId; 26 | 27 | @end 28 | 29 | @interface DartValueHandler : NSObject 30 | 31 | @property NSMutableArray *observedDeviceIdentifiers; 32 | @property NSMutableArray *monitoredCharacteristics; 33 | 34 | @property id scanEventDelegate; 35 | @property id connectionEventDelegate; 36 | @property id readEventDelegate; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /ios/Classes/Bridging/DartValueHandler.m: -------------------------------------------------------------------------------- 1 | #import "DartValueHandler.h" 2 | #import "PlatformMethodName.h" 3 | #import "ScannedPeripheral.h" 4 | #import "DartCallArgumentsConverter.h" 5 | #import "DartCallArgumentKeys.h" 6 | 7 | @implementation DartValueHandler 8 | 9 | - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { 10 | if ([PLATFORM_METHOD_NAME_PUBLISH_SCAN_RESULT isEqualToString:call.method]) { 11 | [self.scanEventDelegate dispatchDartValueHandlerScanEvent:[DartCallArgumentsConverter scannedPeripheralFromCallArguments:call.arguments]]; 12 | result(nil); 13 | } else if ([PLATFORM_METHOD_NAME_PUBLISH_CONNECTION_STATE isEqualToString:call.method]) { 14 | [self.connectionEventDelegate dispatchDartValueHandlerConnectionStateEvent:[DartCallArgumentsConverter connectionStateEventFromCallArguments:call.arguments]]; 15 | result(nil); 16 | } else if ([PLATFORM_METHOD_NAME_PUBLISH_CHARACTERISTIC_UPADTE isEqualToString:call.method]) { 17 | [self.readEventDelegate dispatchDartValueHandlerReadEvent:[DartCallArgumentsConverter characteristicFromCallArguments:call.arguments] 18 | transactionId:[call.arguments objectForKey:DART_CALL_ARGUMENT_TRANSACTION_ID]]; 19 | result(nil); 20 | } else if ([PLATFORM_METHOD_NAME_PUBLISH_CHARACTERISTIC_MONITORING_ERROR isEqualToString:call.method]) { 21 | [self.readEventDelegate dispatchDartValueHandlerReadError:[DartCallArgumentsConverter bleErrorFromCallArguments:call.arguments] 22 | transactionId:[call.arguments objectForKey:DART_CALL_ARGUMENT_TRANSACTION_ID]]; 23 | } else { 24 | result(FlutterMethodNotImplemented); 25 | } 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ios/Classes/Constants/DartCallArgumentKeys.h: -------------------------------------------------------------------------------- 1 | extern NSString * const DART_CALL_ARGUMENT_DEVICE_ID; 2 | 3 | extern NSString * const DART_CALL_ARGUMENT_PERIPHERAL_ID; 4 | extern NSString * const DART_CALL_ARGUMENT_IS_CONNECTABLE; 5 | extern NSString * const DART_CALL_ARGUMENT_LOCAL_NAME; 6 | extern NSString * const DART_CALL_ARGUMENT_MANUFACTURER_DATA; 7 | extern NSString * const DART_CALL_ARGUMENT_MTU; 8 | extern NSString * const DART_CALL_ARGUMENT_NAME; 9 | extern NSString * const DART_CALL_ARGUMENT_OVERFLOW_UUIDS; 10 | extern NSString * const DART_CALL_ARGUMENT_RSSI; 11 | extern NSString * const DART_CALL_ARGUMENT_SERVICE_DATA; 12 | extern NSString * const DART_CALL_ARGUMENT_SERVICE_UUIDS; 13 | extern NSString * const DART_CALL_ARGUMENT_SOLICITED_SERVICE_UUIDS; 14 | extern NSString * const DART_CALL_ARGUMENT_TX_POWER_LEVEL; 15 | 16 | extern NSString * const DART_CALL_CONNECTION_STATE; 17 | 18 | extern NSString * const DART_CALL_ARGUMENT_DEVICE_IDENTIFIER; 19 | 20 | extern NSString * const DART_CALL_ARGUMENT_SERVICE_ID; 21 | extern NSString * const DART_CALL_ARGUMENT_CHARACTERISTIC_IDENTIFIER; 22 | extern NSString * const DART_CALL_ARGUMENT_DESCRIPTOR_IDENTIFIER; 23 | 24 | extern NSString * const DART_CALL_ARGUMENT_SERVICE_UUID; 25 | extern NSString * const DART_CALL_ARGUMENT_CHARACTERISTIC_UUID; 26 | extern NSString * const DART_CALL_ARGUMENT_DESCRIPTOR_UUID; 27 | 28 | extern NSString * const DART_CALL_ARGUMENT_VALUE; 29 | 30 | extern NSString * const DART_CALL_ARGUMENT_TRANSACTION_ID; 31 | 32 | extern NSString * const DART_CALL_ARGUMENT_ERROR_CODE; 33 | extern NSString * const DART_CALL_ARGUMENT_REASON; 34 | -------------------------------------------------------------------------------- /ios/Classes/Constants/DartCallArgumentKeys.m: -------------------------------------------------------------------------------- 1 | NSString * const DART_CALL_ARGUMENT_DEVICE_ID = @"id"; 2 | 3 | NSString * const DART_CALL_ARGUMENT_PERIPHERAL_ID = @"id"; 4 | NSString * const DART_CALL_ARGUMENT_IS_CONNECTABLE = @"isConnectable"; 5 | NSString * const DART_CALL_ARGUMENT_LOCAL_NAME = @"localName"; 6 | NSString * const DART_CALL_ARGUMENT_MANUFACTURER_DATA = @"manufacturerData"; 7 | NSString * const DART_CALL_ARGUMENT_MTU = @"mtu"; 8 | NSString * const DART_CALL_ARGUMENT_NAME = @"name"; 9 | NSString * const DART_CALL_ARGUMENT_OVERFLOW_UUIDS = @"overflowUuids"; 10 | NSString * const DART_CALL_ARGUMENT_RSSI = @"rssi"; 11 | NSString * const DART_CALL_ARGUMENT_SERVICE_DATA = @"serviceData"; 12 | NSString * const DART_CALL_ARGUMENT_SERVICE_UUIDS = @"serviceUuids"; 13 | NSString * const DART_CALL_ARGUMENT_SOLICITED_SERVICE_UUIDS = @"solicitedServiceUuids"; 14 | NSString * const DART_CALL_ARGUMENT_TX_POWER_LEVEL = @"txPowerLevel"; 15 | 16 | NSString * const DART_CALL_CONNECTION_STATE = @"connectionState"; 17 | 18 | NSString * const DART_CALL_ARGUMENT_DEVICE_IDENTIFIER = @"deviceIdentifier"; 19 | 20 | NSString * const DART_CALL_ARGUMENT_SERVICE_ID = @"serviceId"; 21 | NSString * const DART_CALL_ARGUMENT_CHARACTERISTIC_IDENTIFIER = @"characteristicIdentifier"; 22 | NSString * const DART_CALL_ARGUMENT_DESCRIPTOR_IDENTIFIER = @"descriptorIdentifier"; 23 | 24 | NSString * const DART_CALL_ARGUMENT_SERVICE_UUID = @"serviceUuid"; 25 | NSString * const DART_CALL_ARGUMENT_CHARACTERISTIC_UUID = @"characteristicUuid"; 26 | NSString * const DART_CALL_ARGUMENT_DESCRIPTOR_UUID = @"descriptorUuid"; 27 | 28 | NSString * const DART_CALL_ARGUMENT_VALUE = @"value"; 29 | 30 | NSString * const DART_CALL_ARGUMENT_TRANSACTION_ID = @"transactionId"; 31 | 32 | NSString * const DART_CALL_ARGUMENT_ERROR_CODE = @"errorCode"; 33 | NSString * const DART_CALL_ARGUMENT_REASON = @"reason"; 34 | -------------------------------------------------------------------------------- /ios/Classes/Constants/DartMethodName.h: -------------------------------------------------------------------------------- 1 | extern NSString * const DART_METHOD_NAME_CREATE_CLIENT; 2 | 3 | extern NSString * const DART_METHOD_NAME_START_DEVICE_SCAN; 4 | extern NSString * const DART_METHOD_NAME_STOP_DEVICE_SCAN; 5 | 6 | extern NSString * const DART_METHOD_NAME_CONNECT_TO_DEVICE; 7 | extern NSString * const DART_METHOD_NAME_DISCONNECT_OR_CANCEL_CONNECTION; 8 | extern NSString * const DART_METHOD_NAME_IS_DEVICE_CONNECTED; 9 | 10 | extern NSString * const DART_METHOD_NAME_DISCOVER_ALL_SERVICES_AND_CHARACTERISTICS; 11 | 12 | extern NSString * const DART_METHOD_NAME_READ_CHARACTERISTIC_FOR_DEVICE; 13 | extern NSString * const DART_METHOD_NAME_READ_CHARACTERISTIC_FOR_SERVICE; 14 | extern NSString * const DART_METHOD_NAME_READ_CHARACTERISTIC_FOR_IDENTIFIER; 15 | 16 | extern NSString * const DART_METHOD_NAME_WRITE_CHARACTERISTIC_FOR_DEVICE; 17 | extern NSString * const DART_METHOD_NAME_WRITE_CHARACTERISTIC_FOR_SERVICE; 18 | extern NSString * const DART_METHOD_NAME_WRITE_CHARACTERISTIC_FOR_IDENTIFIER; 19 | 20 | extern NSString * const DART_METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_DEVICE; 21 | extern NSString * const DART_METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_SERVICE; 22 | extern NSString * const DART_METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_IDENTIFIER; 23 | 24 | extern NSString * const DART_METHOD_NAME_READ_DESCRIPTOR_FOR_IDENTIFIER; 25 | extern NSString * const DART_METHOD_NAME_READ_DESCRIPTOR_FOR_CHARACTERISTIC; 26 | extern NSString * const DART_METHOD_NAME_READ_DESCRIPTOR_FOR_SERVICE; 27 | extern NSString * const DART_METHOD_NAME_READ_DESCRIPTOR_FOR_DEVICE; 28 | 29 | extern NSString * const DART_METHOD_NAME_WRITE_DESCRIPTOR_FOR_IDENTIFIER; 30 | extern NSString * const DART_METHOD_NAME_WRITE_DESCRIPTOR_FOR_CHARACTERISTIC; 31 | extern NSString * const DART_METHOD_NAME_WRITE_DESCRIPTOR_FOR_SERVICE; 32 | extern NSString * const DART_METHOD_NAME_WRITE_DESCRIPTOR_FOR_DEVICE; 33 | 34 | extern NSString * const DART_METHOD_NAME_RSSI; 35 | extern NSString * const DART_METHOD_NAME_REQUEST_MTU; 36 | 37 | extern NSString * const DART_METHOD_NAME_CANCEL_TRANSACTION; 38 | -------------------------------------------------------------------------------- /ios/Classes/Constants/DartMethodName.m: -------------------------------------------------------------------------------- 1 | NSString * const DART_METHOD_NAME_CREATE_CLIENT = @"createClient"; 2 | 3 | NSString * const DART_METHOD_NAME_START_DEVICE_SCAN = @"startDeviceScan"; 4 | NSString * const DART_METHOD_NAME_STOP_DEVICE_SCAN = @"stopDeviceScan"; 5 | 6 | NSString * const DART_METHOD_NAME_CONNECT_TO_DEVICE = @"connectToDevice"; 7 | NSString * const DART_METHOD_NAME_DISCONNECT_OR_CANCEL_CONNECTION = @"disconnectOrCancelConnection"; 8 | NSString * const DART_METHOD_NAME_IS_DEVICE_CONNECTED = @"isDeviceConnected"; 9 | 10 | NSString * const DART_METHOD_NAME_DISCOVER_ALL_SERVICES_AND_CHARACTERISTICS = @"discoverAllServicesAndCharacteristics"; 11 | 12 | NSString * const DART_METHOD_NAME_READ_CHARACTERISTIC_FOR_DEVICE = @"readCharacteristicForDevice"; 13 | NSString * const DART_METHOD_NAME_READ_CHARACTERISTIC_FOR_SERVICE = @"readCharacteristicForService"; 14 | NSString * const DART_METHOD_NAME_READ_CHARACTERISTIC_FOR_IDENTIFIER = @"readCharacteristicForIdentifier"; 15 | 16 | NSString * const DART_METHOD_NAME_WRITE_CHARACTERISTIC_FOR_DEVICE = @"writeCharacteristicForDevice"; 17 | NSString * const DART_METHOD_NAME_WRITE_CHARACTERISTIC_FOR_SERVICE = @"writeCharacteristicForService"; 18 | NSString * const DART_METHOD_NAME_WRITE_CHARACTERISTIC_FOR_IDENTIFIER = @"writeCharacteristicForIdentifier"; 19 | 20 | NSString * const DART_METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_DEVICE = @"monitorCharacteristicForDevice"; 21 | NSString * const DART_METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_SERVICE = @"monitorCharacteristicForService"; 22 | NSString * const DART_METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_IDENTIFIER = @"monitorCharacteristicForIdentifier"; 23 | 24 | NSString * const DART_METHOD_NAME_READ_DESCRIPTOR_FOR_IDENTIFIER = @"readDescriptorForIdentifier"; 25 | NSString * const DART_METHOD_NAME_READ_DESCRIPTOR_FOR_CHARACTERISTIC = @"readDescriptorForCharacteristic"; 26 | NSString * const DART_METHOD_NAME_READ_DESCRIPTOR_FOR_SERVICE = @"readDescriptorForService"; 27 | NSString * const DART_METHOD_NAME_READ_DESCRIPTOR_FOR_DEVICE = @"readDescriptorForDevice"; 28 | 29 | NSString * const DART_METHOD_NAME_WRITE_DESCRIPTOR_FOR_IDENTIFIER = @"writeDescriptorForIdentifier"; 30 | NSString * const DART_METHOD_NAME_WRITE_DESCRIPTOR_FOR_CHARACTERISTIC = @"writeDescriptorForCharacteristic"; 31 | NSString * const DART_METHOD_NAME_WRITE_DESCRIPTOR_FOR_SERVICE = @"writeDescriptorForService"; 32 | NSString * const DART_METHOD_NAME_WRITE_DESCRIPTOR_FOR_DEVICE = @"writeDescriptorForDevice"; 33 | 34 | NSString * const DART_METHOD_NAME_RSSI = @"rssi"; 35 | NSString * const DART_METHOD_NAME_REQUEST_MTU = @"requestMtu"; 36 | 37 | NSString * const DART_METHOD_NAME_CANCEL_TRANSACTION = @"cancelTransaction"; 38 | -------------------------------------------------------------------------------- /ios/Classes/Constants/DartResultKeys.h: -------------------------------------------------------------------------------- 1 | extern NSString * const DART_RESULT_CHARACTERISTICS; 2 | extern NSString * const DART_RESULT_DESCRIPTORS; 3 | extern NSString * const DART_RESULT_SERVICE_ID; 4 | extern NSString * const DART_RESULT_SERVICE_UUID; 5 | 6 | extern NSString * const DART_RESULT_CHARACTERISTIC_ID; 7 | extern NSString * const DART_RESULT_CHARACTERISTIC_UUID; 8 | extern NSString * const DART_RESULT_DEVICE_IDENTIFIER; 9 | extern NSString * const DART_RESULT_IS_INDICATABLE; 10 | extern NSString * const DART_RESULT_IS_NOTIFIABLE; 11 | extern NSString * const DART_RESULT_IS_NOTIFYING; 12 | extern NSString * const DART_RESULT_IS_READABLE; 13 | extern NSString * const DART_RESULT_IS_WRITABLE_WITH_RESPONSE; 14 | extern NSString * const DART_RESULT_IS_WRITABLE_WITHOUT_RESPONSE; 15 | extern NSString * const DART_RESULT_VALUE; 16 | 17 | extern NSString * const DART_RESULT_DESCRIPTOR_ID; 18 | extern NSString * const DART_RESULT_DESCRIPTOR_UUID; 19 | -------------------------------------------------------------------------------- /ios/Classes/Constants/DartResultKeys.m: -------------------------------------------------------------------------------- 1 | NSString * const DART_RESULT_CHARACTERISTICS = @"characteristics"; 2 | NSString * const DART_RESULT_DESCRIPTORS = @"descriptors"; 3 | NSString * const DART_RESULT_SERVICE_ID = @"serviceId"; 4 | NSString * const DART_RESULT_SERVICE_UUID = @"serviceUuid"; 5 | 6 | NSString * const DART_RESULT_CHARACTERISTIC_ID = @"characteristicId"; 7 | NSString * const DART_RESULT_CHARACTERISTIC_UUID = @"characteristicUuid"; 8 | NSString * const DART_RESULT_DEVICE_IDENTIFIER = @"deviceIdentifier"; 9 | NSString * const DART_RESULT_IS_INDICATABLE = @"isIndicatable"; 10 | NSString * const DART_RESULT_IS_NOTIFIABLE = @"isNotifiable"; 11 | NSString * const DART_RESULT_IS_NOTIFYING = @"isNotifying"; 12 | NSString * const DART_RESULT_IS_READABLE = @"isReadable"; 13 | NSString * const DART_RESULT_IS_WRITABLE_WITH_RESPONSE = @"isWritableWithResponse"; 14 | NSString * const DART_RESULT_IS_WRITABLE_WITHOUT_RESPONSE = @"isWritableWithoutResponse"; 15 | NSString * const DART_RESULT_VALUE = @"value"; 16 | 17 | NSString * const DART_RESULT_DESCRIPTOR_ID = @"descriptorId"; 18 | NSString * const DART_RESULT_DESCRIPTOR_UUID = @"descriptorUuid"; 19 | -------------------------------------------------------------------------------- /ios/Classes/Constants/PlatformMethodName.h: -------------------------------------------------------------------------------- 1 | extern NSString * const PLATFORM_METHOD_NAME_SIMULATE; 2 | 3 | extern NSString * const PLATFORM_METHOD_NAME_PUBLISH_SCAN_RESULT; 4 | extern NSString * const PLATFORM_METHOD_NAME_PUBLISH_CONNECTION_STATE; 5 | extern NSString * const PLATFORM_METHOD_NAME_PUBLISH_CHARACTERISTIC_UPADTE; 6 | extern NSString * const PLATFORM_METHOD_NAME_PUBLISH_CHARACTERISTIC_MONITORING_ERROR; 7 | -------------------------------------------------------------------------------- /ios/Classes/Constants/PlatformMethodName.m: -------------------------------------------------------------------------------- 1 | NSString * const PLATFORM_METHOD_NAME_SIMULATE = @"simulate"; 2 | 3 | NSString * const PLATFORM_METHOD_NAME_PUBLISH_SCAN_RESULT = @"publishScanResult"; 4 | NSString * const PLATFORM_METHOD_NAME_PUBLISH_CONNECTION_STATE = @"publishConnectionState"; 5 | NSString * const PLATFORM_METHOD_NAME_PUBLISH_CHARACTERISTIC_UPADTE = @"publishCharacteristicUpdate"; 6 | NSString * const PLATFORM_METHOD_NAME_PUBLISH_CHARACTERISTIC_MONITORING_ERROR = @"publishCharacteristicMonitoringError"; 7 | -------------------------------------------------------------------------------- /ios/Classes/Constants/SimulationChannelName.h: -------------------------------------------------------------------------------- 1 | extern NSString * const SIMULATION_CHANNEL_NAME_TO_PLATFORM; 2 | extern NSString * const SIMULATION_CHANNEL_NAME_TO_DART; 3 | -------------------------------------------------------------------------------- /ios/Classes/Constants/SimulationChannelName.m: -------------------------------------------------------------------------------- 1 | #define BASE @"com.polidea.blemulator" 2 | 3 | NSString * const SIMULATION_CHANNEL_NAME_TO_PLATFORM = (BASE @"/toJava"); 4 | NSString * const SIMULATION_CHANNEL_NAME_TO_DART = (BASE @"/toDart"); 5 | -------------------------------------------------------------------------------- /ios/Classes/DeviceContainer.h: -------------------------------------------------------------------------------- 1 | #import "Service.h" 2 | #import "CharacteristicContainer.h" 3 | 4 | @interface DeviceContainer : NSObject 5 | 6 | @property (readonly) NSString *identifier; 7 | @property (readonly) NSString *name; 8 | @property (readonly) NSArray *services; 9 | @property (readonly) NSDictionary *> *characteristicContainers; 10 | @property BOOL isConnected; 11 | 12 | - (instancetype)initWithIdentifier:(NSString *)identifier 13 | name:(NSString *)name; 14 | 15 | - (instancetype)initWithIdentifier:(NSString *)identifier 16 | name:(NSString *)name 17 | services:(NSArray *)services 18 | characteristicContainers:(NSDictionary *> *)characteristicContainers; 19 | 20 | - (NSArray *)servicesJsonRepresentation; 21 | 22 | - (NSArray *)characteristicsJsonRepresentationForService:(NSString *)serviceUuidString; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ios/Classes/DeviceContainer.m: -------------------------------------------------------------------------------- 1 | #import "DeviceContainer.h" 2 | 3 | @interface DeviceContainer () 4 | 5 | @property (readwrite) NSString *identifier; 6 | @property (readwrite) NSString *name; 7 | @property (readwrite) NSArray *services; 8 | @property (readwrite) NSDictionary *> *characteristicContainers; 9 | 10 | @end 11 | 12 | @implementation DeviceContainer 13 | 14 | - (instancetype)initWithIdentifier:(NSString *)identifier name:(NSString *)name { 15 | self = [super init]; 16 | if (self) { 17 | self.identifier = identifier; 18 | self.name = name; 19 | self.services = nil; 20 | self.characteristicContainers = nil; 21 | self.isConnected = false; 22 | } 23 | return self; 24 | } 25 | 26 | - (instancetype)initWithIdentifier:(NSString *)identifier 27 | name:(NSString *)name 28 | services:(NSArray *)services 29 | characteristicContainers:(NSDictionary *> *)characteristicContainers { 30 | self = [super init]; 31 | if (self) { 32 | self.identifier = identifier; 33 | self.name = name; 34 | self.services = services; 35 | self.characteristicContainers = characteristicContainers; 36 | self.isConnected = false; 37 | } 38 | return self; 39 | } 40 | 41 | - (NSArray *)servicesJsonRepresentation { 42 | NSMutableArray *result = [[NSMutableArray alloc] init]; 43 | for (Service *service in _services) { 44 | [result addObject:[service jsonObjectRepresentation]]; 45 | } 46 | return result; 47 | } 48 | 49 | - (NSArray *)characteristicsJsonRepresentationForService:(NSString *)serviceUuidString { 50 | NSMutableArray *result = [[NSMutableArray alloc] init]; 51 | for (CharacteristicContainer *characteristicContainer in [_characteristicContainers objectForKey:serviceUuidString.lowercaseString]) { 52 | [result addObject:[characteristicContainer.characteristic jsonObjectRepresentation]]; 53 | } 54 | return result; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /ios/Classes/Error/BleError.h: -------------------------------------------------------------------------------- 1 | #import "BleErrorCode.h" 2 | #import "BlemulatorCommonTypes.h" 3 | 4 | @interface BleError : NSObject 5 | 6 | @property BleErrorCode errorCode; 7 | @property NSNumber *attErrorCode; 8 | @property NSNumber *iosErrorCode; 9 | @property NSString *reason; 10 | @property NSString *deviceID; 11 | @property NSString *serviceUUID; 12 | @property NSString *characteristicUUID; 13 | @property NSString *descriptorUUID; 14 | @property NSString *internalMessage; 15 | 16 | - (instancetype)initWithErrorCode:(BleErrorCode)errorCode reason:(NSString *)reason; 17 | 18 | - (NSString *)jsonStringRepresentation; 19 | 20 | - (NSDictionary *)jsonObjectRepresentation; 21 | 22 | - (void)callReject:(Reject)reject; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ios/Classes/Error/BleError.m: -------------------------------------------------------------------------------- 1 | #import "BleError.h" 2 | #import "BlemulatorJSONStringifier.h" 3 | 4 | @implementation BleError 5 | 6 | - (instancetype)initWithErrorCode:(BleErrorCode)errorCode reason:(NSString *)reason { 7 | self = [super init]; 8 | if (self) { 9 | self.errorCode = errorCode; 10 | self.reason = reason; 11 | 12 | self.attErrorCode = nil; 13 | self.iosErrorCode = nil; 14 | self.deviceID = nil; 15 | self.serviceUUID = nil; 16 | self.characteristicUUID = nil; 17 | self.descriptorUUID = nil; 18 | self.internalMessage = nil; 19 | } 20 | return self; 21 | } 22 | 23 | - (NSString *)jsonStringRepresentation { 24 | return [BlemulatorJSONStringifier jsonStringFromJSONObject:[self jsonObjectRepresentation]]; 25 | } 26 | 27 | - (NSDictionary *)jsonObjectRepresentation { 28 | return [NSDictionary dictionaryWithObjectsAndKeys: 29 | [NSNumber numberWithInt:_errorCode], @"errorCode", 30 | [self valueOrNull:_attErrorCode], @"attErrorCode", 31 | [self valueOrNull:_iosErrorCode], @"iosErrorCode", 32 | [NSNull null], @"androidErrorCode", 33 | [self valueOrNull:_reason], @"reason", 34 | [self valueOrNull:_deviceID], @"deviceID", 35 | [self valueOrNull:_serviceUUID], @"serviceUUID", 36 | [self valueOrNull:_characteristicUUID], @"characteristicUUID", 37 | [self valueOrNull:_descriptorUUID], @"descriptorUUID", 38 | [self valueOrNull:_internalMessage], @"internalMessage", 39 | nil]; 40 | } 41 | 42 | - (void)callReject:(Reject)reject { 43 | reject(nil, [BlemulatorJSONStringifier jsonStringFromJSONObject:[self jsonObjectRepresentation]], nil); 44 | } 45 | 46 | - (id)valueOrNull:(id)argument { 47 | return argument ? argument : [NSNull null]; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /ios/Classes/Error/BleErrorCode.h: -------------------------------------------------------------------------------- 1 | typedef enum { 2 | BleErrorCodeUnknownError = 0, 3 | BleErrorCodeBluetoothManagerDestroyed = 1, 4 | BleErrorCodeOperationCancelled = 2, 5 | BleErrorCodeOperationTimedOut = 3, 6 | BleErrorCodeOperationStartFailed = 4, 7 | BleErrorCodeInvalidIdentifiers = 5, 8 | 9 | BleErrorCodeBluetoothUnsupported = 100, 10 | BleErrorCodeBluetoothUnauthorized = 101, 11 | BleErrorCodeBluetoothPoweredOff = 102, 12 | BleErrorCodeBluetoothInUnknownState = 103, 13 | BleErrorCodeBluetoothResetting = 104, 14 | BleErrorCodeBluetoothStateChangeFailed = 105, 15 | 16 | BleErrorCodeDeviceConnectionFailed = 200, 17 | BleErrorCodeDeviceDisconnected = 201, 18 | BleErrorCodeDeviceRSSIReadFailed = 202, 19 | BleErrorCodeDeviceAlreadyConnected = 203, 20 | BleErrorCodeDeviceNotFound = 204, 21 | BleErrorCodeDeviceNotConnected = 205, 22 | BleErrorCodeDeviceMTUChangeFailed = 206, 23 | 24 | BleErrorCodeServicesDiscoveryFailed = 300, 25 | BleErrorCodeIncludedServicesDiscoveryFailed = 301, 26 | BleErrorCodeServiceNotFound = 302, 27 | BleErrorCodeServicesNotDiscovered = 303, 28 | 29 | BleErrorCodeCharacteristicsDiscoveryFailed = 400, 30 | BleErrorCodeCharacteristicWriteFailed = 401, 31 | BleErrorCodeCharacteristicReadFailed = 402, 32 | BleErrorCodeCharacteristicNotifyChangeFailed = 403, 33 | BleErrorCodeCharacteristicNotFound = 404, 34 | BleErrorCodeCharacteristicsNotDiscovered = 405, 35 | BleErrorCodeCharacteristicInvalidDataFormat = 406, 36 | 37 | BleErrorCodeDescriptorsDiscoveryFailed = 500, 38 | BleErrorCodeDescriptorWriteFailed = 501, 39 | BleErrorCodeDescriptorReadFailed = 502, 40 | BleErrorCodeDescriptorNotFound = 503, 41 | BleErrorCodeDescriptorsNotDiscovered = 504, 42 | BleErrorCodeDescriptorInvalidDataFormat = 505, 43 | 44 | BleErrorCodeScanStartFailed = 600, 45 | BleErrorCodeLocationServicesDisabled = 601, 46 | } BleErrorCode; 47 | -------------------------------------------------------------------------------- /ios/Classes/Model/AdvertisementData.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AdvertisementData : NSObject 4 | 5 | @property NSData * _Nullable manufacturerData; 6 | @property NSDictionary * _Nullable serviceData; 7 | @property NSArray * _Nullable serviceUUIDs; 8 | @property NSString * _Nullable localName; 9 | @property NSNumber * _Nullable txPowerLevel; 10 | @property NSArray * _Nullable solicitedServiceUUIDs; 11 | @property BOOL * _Nullable isConnectable; 12 | @property NSArray * _Nullable overflowServiceUUIDs; 13 | 14 | - (instancetype _Nonnull)initWithManufacturerData:(NSData * _Nullable)manufacturerData 15 | serviceData:(NSDictionary * _Nullable)serviceData 16 | serviceUUIDs:(NSArray * _Nullable)serviceUUIDs 17 | localName:(NSString * _Nullable)localName 18 | txPowerLevel:(NSNumber * _Nullable)txPowerLevel 19 | solicitedServiceUUIDs:(NSArray * _Nullable)solicitedServiceUUIDs 20 | isConnectable:(BOOL * _Nullable)isConnectable 21 | overflowServiceUUIDs:(NSArray * _Nullable)overflowServiceUUIDs; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ios/Classes/Model/AdvertisementData.m: -------------------------------------------------------------------------------- 1 | #import "AdvertisementData.h" 2 | 3 | @implementation AdvertisementData 4 | 5 | - (instancetype)initWithManufacturerData:(NSData *)manufacturerData 6 | serviceData:(NSDictionary *)serviceData 7 | serviceUUIDs:(NSArray *)serviceUUIDs 8 | localName:(NSString *)localName 9 | txPowerLevel:(NSNumber *)txPowerLevel 10 | solicitedServiceUUIDs:(NSArray *)solicitedServiceUUIDs 11 | isConnectable:(BOOL *)isConnectable 12 | overflowServiceUUIDs:(NSArray *)overflowServiceUUIDs { 13 | self = [super init]; 14 | if (self) { 15 | self.manufacturerData = manufacturerData; 16 | self.serviceData = serviceData; 17 | self.serviceUUIDs = serviceUUIDs; 18 | self.localName = localName; 19 | self.txPowerLevel = txPowerLevel; 20 | self.solicitedServiceUUIDs = solicitedServiceUUIDs; 21 | self.isConnectable = isConnectable; 22 | self.overflowServiceUUIDs = overflowServiceUUIDs; 23 | } 24 | return self; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /ios/Classes/Model/Characteristic.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Service.h" 3 | 4 | @interface Characteristic : NSObject 5 | 6 | @property int objectId; 7 | @property CBUUID * _Nonnull uuid; 8 | @property NSData * _Nullable value; 9 | @property Service * _Nonnull service; 10 | @property BOOL isNotifying; 11 | @property CBCharacteristicProperties properties; 12 | 13 | - (instancetype _Nonnull)initWithObjectId:(int)objectId 14 | uuid:(CBUUID * _Nonnull)uuid 15 | value:(NSData * _Nullable)value 16 | service:(Service * _Nonnull)service 17 | isNotifying:(BOOL)isNotifying 18 | properties:(CBCharacteristicProperties)properties; 19 | 20 | - (NSDictionary * _Nonnull)jsonObjectRepresentation; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ios/Classes/Model/Characteristic.m: -------------------------------------------------------------------------------- 1 | #import "Characteristic.h" 2 | #import "BlemulatorCharacteristicResponse.h" 3 | #import 4 | 5 | @implementation Characteristic 6 | 7 | - (instancetype)initWithObjectId:(int)objectId 8 | uuid:(CBUUID *)uuid 9 | value:(NSData *)value 10 | service:(Service *)service 11 | isNotifying:(BOOL)isNotifying 12 | properties:(CBCharacteristicProperties)properties { 13 | self = [super init]; 14 | if (self) { 15 | self.objectId = objectId; 16 | self.uuid = uuid; 17 | self.value = value; 18 | self.service = service; 19 | self.isNotifying = isNotifying; 20 | self.properties = properties; 21 | } 22 | return self; 23 | } 24 | 25 | - (NSDictionary *)jsonObjectRepresentation { 26 | return [NSDictionary dictionaryWithObjectsAndKeys: 27 | [NSNumber numberWithInt:_objectId], CHARACTERISTIC_RESPONSE_ID, 28 | [_uuid UUIDString].lowercaseString, CHARACTERISTIC_RESPONSE_UUID, 29 | [NSNumber numberWithInt:_service.objectId], CHARACTERISTIC_RESPONSE_SERVICE_ID, 30 | [_service.uuid UUIDString].lowercaseString, CHARACTERISTIC_RESPONSE_SERVICE_UUID, 31 | _service.peripheralIdentifier, CHARACTERISTIC_RESPONSE_DEVICE_ID, 32 | [NSNumber numberWithBool:_properties & CBCharacteristicPropertyRead], CHARACTERISTIC_RESPONSE_IS_READABLE, 33 | [NSNumber numberWithBool:_properties & CBCharacteristicPropertyWrite], CHARACTERISTIC_RESPONSE_IS_WRITABLE_WITH_RESPONSE, 34 | [NSNumber numberWithBool:_properties & CBCharacteristicPropertyWriteWithoutResponse], CHARACTERISTIC_RESPONSE_IS_WRITABLE_WITHOUT_RESPONSE, 35 | [NSNumber numberWithBool:_properties & CBCharacteristicPropertyNotify], CHARACTERISTIC_RESPONSE_IS_NOTIFIABLE, 36 | [NSNumber numberWithBool:_isNotifying], CHARACTERISTIC_RESPONSE_IS_NOTIFYING, 37 | [NSNumber numberWithBool:_properties & CBCharacteristicPropertyIndicate], CHARACTERISTIC_RESPONSE_IS_INDICATABLE, 38 | [self base64encodedStringFromBytes:_value], CHARACTERISTIC_RESPONSE_VALUE, 39 | nil]; 40 | } 41 | 42 | - (NSString *)base64encodedStringFromBytes:(FlutterStandardTypedData *)bytes { 43 | return [bytes.data base64EncodedStringWithOptions:0]; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /ios/Classes/Model/ConnectionStateEvent.h: -------------------------------------------------------------------------------- 1 | typedef enum { connecting, connected, disconnected } ConnectionState; 2 | 3 | @interface ConnectionStateEvent : NSObject 4 | 5 | @property NSString * _Nonnull deviceId; 6 | @property NSString * _Nonnull connectionState; 7 | 8 | - (instancetype _Nonnull)initWith:(NSString * _Nonnull)deviceId 9 | connectionState:(NSString * _Nonnull)connectionState; 10 | 11 | - (NSDictionary * _Nonnull)jsonObjectRepresentation; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Classes/Model/ConnectionStateEvent.m: -------------------------------------------------------------------------------- 1 | #import "ConnectionStateEvent.h" 2 | 3 | NSString * const JSON_KEY_ID = @"id"; 4 | NSString * const JSON_KEY_CONNECTION_STATE = @"connectionState"; 5 | 6 | @implementation ConnectionStateEvent 7 | 8 | - (instancetype)initWith:(NSString *)deviceId connectionState:(NSString *)connectionState { 9 | self = [super init]; 10 | if (self) { 11 | self.deviceId = deviceId; 12 | self.connectionState = connectionState; 13 | } 14 | return self; 15 | } 16 | 17 | - (NSDictionary *)jsonObjectRepresentation { 18 | return [NSDictionary dictionaryWithObjectsAndKeys: 19 | _deviceId, JSON_KEY_ID, 20 | _connectionState, JSON_KEY_CONNECTION_STATE, 21 | nil]; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ios/Classes/Model/Descriptor.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Characteristic.h" 3 | 4 | @interface Descriptor : NSObject 5 | 6 | @property int objectId; 7 | @property CBUUID * _Nonnull uuid; 8 | @property Characteristic * _Nonnull characteristic; 9 | @property NSData * _Nullable value; 10 | 11 | - (_Nonnull instancetype)initWithObjectId:(int)objectId 12 | uuid:(CBUUID * _Nonnull)uuid 13 | value:(NSData * _Nullable)value 14 | characteristic:(Characteristic * _Nonnull)characteristic; 15 | 16 | - (NSDictionary * _Nonnull)jsonObjectRepresentation; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios/Classes/Model/Descriptor.m: -------------------------------------------------------------------------------- 1 | #import "Descriptor.h" 2 | #import 3 | #import "BlemulatorDescriptorResponse.h" 4 | 5 | @implementation Descriptor 6 | 7 | - (instancetype)initWithObjectId:(int)objectId 8 | uuid:(CBUUID *)uuid 9 | value:(NSData *)value 10 | characteristic:(Characteristic *)characteristic { 11 | self = [super init]; 12 | if (self) { 13 | self.objectId = objectId; 14 | self.uuid = uuid; 15 | self.characteristic = characteristic; 16 | self.value = value; 17 | } 18 | 19 | return self; 20 | } 21 | 22 | - (NSDictionary *)jsonObjectRepresentation { 23 | return [NSDictionary dictionaryWithObjectsAndKeys: 24 | [NSNumber numberWithInt:self.objectId], BLEMULATOR_DESCRIPTOR_RESPONSE_DESCRIPTOR_ID, 25 | [self.uuid UUIDString].lowercaseString, BLEMULATOR_DESCRIPTOR_RESPONSE_DESCRIPTOR_UUID, 26 | [NSNumber numberWithInt:self.characteristic.objectId], BLEMULATOR_DESCRIPTOR_RESPONSE_CHARACTERISTIC_ID, 27 | [self.characteristic.uuid UUIDString].lowercaseString, BLEMULATOR_DESCRIPTOR_RESPONSE_CHARACTERISTIC_UUID, 28 | [self.characteristic.service.uuid UUIDString].lowercaseString, BLEMULATOR_DESCRIPTOR_RESPONSE_SERVICE_UUID, 29 | [NSNumber numberWithInt:self.characteristic.service.objectId], BLEMULATOR_DESCRIPTOR_RESPONSE_SERVICE_ID, 30 | self.characteristic.service.peripheralIdentifier, BLEMULATOR_DESCRIPTOR_RESPONSE_DEVICE_ID, 31 | [self base64encodedStringFromBytes:self.value], BLEMULATOR_DESCRIPTOR_RESPONSE_VALUE, 32 | nil]; 33 | } 34 | 35 | - (NSString *)base64encodedStringFromBytes:(FlutterStandardTypedData *)bytes { 36 | return [bytes.data base64EncodedStringWithOptions:0]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ios/Classes/Model/Peripheral.h: -------------------------------------------------------------------------------- 1 | @interface Peripheral : NSObject 2 | 3 | @property NSString * _Nonnull identifier; 4 | @property NSString * _Nullable name; 5 | @property NSNumber * _Nullable mtu; 6 | 7 | - (instancetype _Nonnull)initWithIdentifier:(NSString * _Nonnull)identifier 8 | name:(NSString * _Nullable)name; 9 | 10 | - (NSDictionary * _Nonnull)jsonObjectRepresentation; 11 | 12 | - (NSDictionary * _Nonnull)jsonObjectRepresentationWithRssi:(NSNumber * _Nonnull)rssi; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /ios/Classes/Model/Peripheral.m: -------------------------------------------------------------------------------- 1 | #import "Peripheral.h" 2 | #import "BlemulatorPeripheralResponse.h" 3 | 4 | @implementation Peripheral 5 | 6 | - (instancetype)initWithIdentifier:(NSString *)identifier 7 | name:(NSString *)name { 8 | self = [super init]; 9 | if (self) { 10 | self.identifier = identifier; 11 | self.name = name; 12 | self.mtu = nil; 13 | } 14 | return self; 15 | } 16 | 17 | - (NSDictionary *)jsonObjectRepresentation { 18 | return [NSDictionary dictionaryWithObjectsAndKeys: 19 | _identifier, PERIPHERAL_RESPONSE_ID, 20 | _name, PERIPHERAL_RESPONSE_NAME, 21 | [NSNull null], PERIPHERAL_RESPONSE_RSSI, 22 | _mtu != nil ? _mtu : [NSNull null], PERIPHERAL_RESPONSE_MTU, 23 | [NSNull null], PERIPHERAL_RESPONSE_MANUFACTURER_DATA, 24 | [NSNull null], PERIPHERAL_RESPONSE_SERVICE_DATA, 25 | [NSNull null], PERIPHERAL_RESPONSE_SERVICE_UUIDS, 26 | [NSNull null], PERIPHERAL_RESPONSE_LOCAL_NAME, 27 | [NSNull null], PERIPHERAL_RESPONSE_TX_POWER_LEVEL, 28 | [NSNull null], PERIPHERAL_RESPONSE_SOLICITED_SERVICE_UUIDS, 29 | [NSNull null], PERIPHERAL_RESPONSE_IS_CONNECTABLE, 30 | [NSNull null], PERIPHERAL_RESPONSE_OVERFLOW_SERVICE_UUIDS, 31 | nil]; 32 | } 33 | 34 | - (NSDictionary *)jsonObjectRepresentationWithRssi:(NSNumber *)rssi { 35 | return [NSDictionary dictionaryWithObjectsAndKeys: 36 | _identifier, PERIPHERAL_RESPONSE_ID, 37 | _name, PERIPHERAL_RESPONSE_NAME, 38 | rssi, PERIPHERAL_RESPONSE_RSSI, 39 | _mtu != nil ? _mtu : [NSNull null], PERIPHERAL_RESPONSE_MTU, 40 | [NSNull null], PERIPHERAL_RESPONSE_MANUFACTURER_DATA, 41 | [NSNull null], PERIPHERAL_RESPONSE_SERVICE_DATA, 42 | [NSNull null], PERIPHERAL_RESPONSE_SERVICE_UUIDS, 43 | [NSNull null], PERIPHERAL_RESPONSE_LOCAL_NAME, 44 | [NSNull null], PERIPHERAL_RESPONSE_TX_POWER_LEVEL, 45 | [NSNull null], PERIPHERAL_RESPONSE_SOLICITED_SERVICE_UUIDS, 46 | [NSNull null], PERIPHERAL_RESPONSE_IS_CONNECTABLE, 47 | [NSNull null], PERIPHERAL_RESPONSE_OVERFLOW_SERVICE_UUIDS, 48 | nil]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ios/Classes/Model/ScannedPeripheral.h: -------------------------------------------------------------------------------- 1 | #import "Peripheral.h" 2 | #import "AdvertisementData.h" 3 | 4 | @interface ScannedPeripheral : NSObject 5 | 6 | @property Peripheral * _Nonnull peripheral; 7 | @property AdvertisementData * _Nonnull advertisementData; 8 | @property NSNumber * _Nonnull rssi; 9 | 10 | - (instancetype _Nonnull)initWithPeripheral:(Peripheral * _Nonnull)peripheral 11 | advertisementData:(AdvertisementData * _Nonnull)advertisementData 12 | rssi:(NSNumber * _Nonnull)rssi; 13 | 14 | - (NSDictionary * _Nonnull)jsonObjectRepresentation; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/Classes/Model/ScannedPeripheral.m: -------------------------------------------------------------------------------- 1 | #import "ScannedPeripheral.h" 2 | #import "BlemulatorPeripheralResponse.h" 3 | #import "Base64Coder.h" 4 | #import "ArrayUtilities.h" 5 | 6 | @implementation ScannedPeripheral 7 | 8 | - (instancetype)initWithPeripheral:(Peripheral *)peripheral 9 | advertisementData:(AdvertisementData *)advertisementData 10 | rssi:(NSNumber *)rssi { 11 | self = [super init]; 12 | if (self) { 13 | self.peripheral = peripheral; 14 | self.advertisementData = advertisementData; 15 | self.rssi = rssi; 16 | } 17 | return self; 18 | } 19 | 20 | - (NSDictionary *)jsonObjectRepresentation { 21 | id manufacturerData = [NSNull null]; 22 | id serviceUUIDs = [NSNull null]; 23 | id solicitedServiceUUIDs = [NSNull null]; 24 | id overflowServiceUUIDs = [NSNull null]; 25 | 26 | if (![_advertisementData.manufacturerData isKindOfClass: NSNull.class]) { 27 | manufacturerData = [Base64Coder base64StringFromData:_advertisementData.manufacturerData]; 28 | } 29 | if (![_advertisementData.serviceUUIDs isKindOfClass: NSNull.class]) { 30 | serviceUUIDs = [ArrayUtilities stringArrayFromCBUUIDArray:_advertisementData.serviceUUIDs]; 31 | } 32 | if (![_advertisementData.solicitedServiceUUIDs isKindOfClass: NSNull.class]) { 33 | solicitedServiceUUIDs = [ArrayUtilities stringArrayFromCBUUIDArray:_advertisementData.solicitedServiceUUIDs]; 34 | } 35 | if (![_advertisementData.overflowServiceUUIDs isKindOfClass: NSNull.class]) { 36 | overflowServiceUUIDs = [ArrayUtilities stringArrayFromCBUUIDArray:_advertisementData.overflowServiceUUIDs]; 37 | } 38 | 39 | return [NSDictionary dictionaryWithObjectsAndKeys: 40 | _peripheral.identifier, PERIPHERAL_RESPONSE_ID, 41 | _peripheral.name, PERIPHERAL_RESPONSE_NAME, 42 | _rssi, PERIPHERAL_RESPONSE_RSSI, 43 | [NSNumber numberWithInteger:[_peripheral mtu]], PERIPHERAL_RESPONSE_MTU, 44 | manufacturerData, PERIPHERAL_RESPONSE_MANUFACTURER_DATA, 45 | _advertisementData.serviceData, PERIPHERAL_RESPONSE_SERVICE_DATA, 46 | serviceUUIDs, PERIPHERAL_RESPONSE_SERVICE_UUIDS, 47 | _advertisementData.localName, PERIPHERAL_RESPONSE_LOCAL_NAME, 48 | _advertisementData.txPowerLevel, PERIPHERAL_RESPONSE_TX_POWER_LEVEL, 49 | solicitedServiceUUIDs, PERIPHERAL_RESPONSE_SOLICITED_SERVICE_UUIDS, 50 | [NSNumber numberWithBool:_advertisementData.isConnectable], PERIPHERAL_RESPONSE_IS_CONNECTABLE, 51 | overflowServiceUUIDs, PERIPHERAL_RESPONSE_OVERFLOW_SERVICE_UUIDS, 52 | nil]; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /ios/Classes/Model/Service.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Peripheral.h" 3 | 4 | @interface Service : NSObject 5 | 6 | @property int objectId; 7 | @property CBUUID * _Nonnull uuid; 8 | @property NSString * _Nonnull peripheralIdentifier; 9 | @property BOOL isPrimary; 10 | 11 | - (instancetype _Nonnull)initWithObjectId:(int)objectId 12 | uuid:(CBUUID * _Nonnull)uuid 13 | peripheralIdentifier:(NSString * _Nonnull)peripheralIdentifier 14 | isPrimary:(BOOL)isPrimary; 15 | 16 | - (NSDictionary * _Nonnull)jsonObjectRepresentation; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios/Classes/Model/Service.m: -------------------------------------------------------------------------------- 1 | #import "Service.h" 2 | 3 | @implementation Service 4 | 5 | - (instancetype)initWithObjectId:(int)objectId 6 | uuid:(CBUUID *)uuid 7 | peripheralIdentifier:(NSString *)peripheralIdentifier 8 | isPrimary:(BOOL)isPrimary { 9 | self = [super init]; 10 | if (self) { 11 | self.objectId = objectId; 12 | self.uuid = uuid; 13 | self.peripheralIdentifier = peripheralIdentifier; 14 | self.isPrimary = isPrimary; 15 | } 16 | return self; 17 | } 18 | 19 | - (NSDictionary *)jsonObjectRepresentation { 20 | return [NSDictionary dictionaryWithObjectsAndKeys: 21 | [NSNumber numberWithInt:_objectId], @"id", 22 | [_uuid UUIDString].lowercaseString, @"uuid", 23 | nil]; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /ios/Classes/Response/BlemulatorCharacteristicResponse.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/BlemulatorCharacteristicResponse.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/BlemulatorDescriptorResponse.h: -------------------------------------------------------------------------------- 1 | extern NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_DESCRIPTOR_ID; 2 | extern NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_DESCRIPTOR_UUID; 3 | extern NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_CHARACTERISTIC_ID; 4 | extern NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_CHARACTERISTIC_UUID; 5 | extern NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_SERVICE_ID; 6 | extern NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_SERVICE_UUID; 7 | extern NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_DEVICE_ID; 8 | extern NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_VALUE; 9 | -------------------------------------------------------------------------------- /ios/Classes/Response/BlemulatorDescriptorResponse.m: -------------------------------------------------------------------------------- 1 | NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_DESCRIPTOR_ID = @"id"; 2 | NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_DESCRIPTOR_UUID = @"uuid"; 3 | NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_CHARACTERISTIC_ID = @"characteristicID"; 4 | NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_CHARACTERISTIC_UUID = @"characteristicUUID"; 5 | NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_SERVICE_ID = @"serviceID"; 6 | NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_SERVICE_UUID = @"serviceUUID"; 7 | NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_DEVICE_ID = @"deviceID"; 8 | NSString * const BLEMULATOR_DESCRIPTOR_RESPONSE_VALUE = @"value"; 9 | -------------------------------------------------------------------------------- /ios/Classes/Response/BlemulatorPeripheralResponse.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/BlemulatorPeripheralResponse.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/SimulatedAdapter.h: -------------------------------------------------------------------------------- 1 | #import "DartMethodCaller.h" 2 | #import "DartValueHandler.h" 3 | @import MultiplatformBleAdapter; 4 | 5 | @protocol BleAdapter; 6 | 7 | @interface SimulatedAdapter : NSObject 8 | 9 | - (instancetype)initWithDartMethodCaller:(DartMethodCaller *)dartMethodCaller 10 | dartValueHandler:(DartValueHandler *)dartValueHandler; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /ios/Classes/Util/ArrayUtilities.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ArrayUtilities : NSObject 4 | 5 | + (NSArray *)stringArrayFromCBUUIDArray:(NSArray *)cbuuidArray; 6 | 7 | + (NSArray *)cbuuidArrayFromStringArray:(NSArray *)stringArray; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /ios/Classes/Util/ArrayUtilities.m: -------------------------------------------------------------------------------- 1 | #import "ArrayUtilities.h" 2 | 3 | @implementation ArrayUtilities 4 | 5 | + (NSArray *)stringArrayFromCBUUIDArray:(NSArray *)cbuuidArray { 6 | NSMutableArray *stringArray = [[NSMutableArray alloc] init]; 7 | for (CBUUID * cbuuid in cbuuidArray) { 8 | [stringArray addObject:[cbuuid UUIDString].lowercaseString]; 9 | } 10 | return stringArray; 11 | } 12 | 13 | + (NSArray *)cbuuidArrayFromStringArray:(NSArray *)stringArray { 14 | NSMutableArray *cbuuidArray = [[NSMutableArray alloc] init]; 15 | for (NSString * cbuuidString in stringArray) { 16 | [cbuuidArray addObject:[CBUUID UUIDWithString:cbuuidString]]; 17 | } 18 | return cbuuidArray; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /ios/Classes/Util/Base64Coder.h: -------------------------------------------------------------------------------- 1 | @interface Base64Coder : NSObject 2 | 3 | + (NSString *)base64StringFromData:(NSData *)data; 4 | 5 | + (NSData *)dataFromBase64String:(NSString *)string; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /ios/Classes/Util/Base64Coder.m: -------------------------------------------------------------------------------- 1 | #import "Base64Coder.h" 2 | 3 | @implementation Base64Coder 4 | 5 | + (NSString *)base64StringFromData:(NSData *)data { 6 | return [data base64EncodedStringWithOptions:nil]; 7 | } 8 | 9 | + (NSData *)dataFromBase64String:(NSString *)string { 10 | return [[NSData alloc] initWithBase64EncodedString:string options:0]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Classes/Util/BlemulatorCommonTypes.h: -------------------------------------------------------------------------------- 1 | typedef void (^Resolve)(id result); 2 | typedef void (^Reject)(NSString *code, NSString *message, NSError *error); 3 | -------------------------------------------------------------------------------- /ios/Classes/Util/BlemulatorJSONStringifier.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface BlemulatorJSONStringifier : NSObject 4 | 5 | + (NSString *)jsonStringFromJSONObject:(id)jsonObject; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /ios/Classes/Util/BlemulatorJSONStringifier.m: -------------------------------------------------------------------------------- 1 | #import "BlemulatorJSONStringifier.h" 2 | 3 | @implementation BlemulatorJSONStringifier 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/Util/FlutterMethodCallHandler.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @protocol FlutterMethodCallHandler 4 | 5 | - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /ios/Classes/Util/StringUtilities.h: -------------------------------------------------------------------------------- 1 | @interface StringUtilitites : NSObject 2 | 3 | + (NSString *)stringFromNSUUID:(NSUUID *)nsuuid; 4 | 5 | + (NSUUID *)nsuuidFromString:(NSString *)string; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /ios/Classes/Util/StringUtilities.m: -------------------------------------------------------------------------------- 1 | #import "StringUtilities.h" 2 | 3 | @implementation StringUtilitites 4 | 5 | + (NSString *)stringFromNSUUID:(NSUUID *)nsuuid { 6 | return [nsuuid UUIDString]; 7 | } 8 | 9 | + (NSUUID *)nsuuidFromString:(NSString *)string { 10 | return [[NSUUID alloc] initWithUUIDString:string]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/blemulator.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 = 'blemulator' 6 | s.version = '1.2.1' 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.dependency 'MultiplatformBleAdapter', '~> 0.1.7' 19 | 20 | s.ios.deployment_target = '8.0' 21 | end 22 | 23 | -------------------------------------------------------------------------------- /lib/blemulator.dart: -------------------------------------------------------------------------------- 1 | /// Library with all BLEmulator definitions 2 | /// 3 | /// To use create a Blemulator instance, add an instance of 4 | /// SimulatedPeripheral to it and call blemulator.simulate() 5 | library blemulator; 6 | 7 | import 'dart:async'; 8 | import 'dart:convert'; 9 | import 'dart:typed_data'; 10 | import 'dart:math'; 11 | 12 | import 'package:flutter/foundation.dart'; 13 | import 'package:blemulator/src/id_generator.dart'; 14 | import 'package:flutter_ble_lib/flutter_ble_lib.dart' as flutter_ble_lib; 15 | 16 | import 'src/internal.dart'; 17 | 18 | part 'simulated_ble_error.dart'; 19 | 20 | part 'blemulator_core.dart'; 21 | 22 | part 'simulation/simulated_characteristic.dart'; 23 | 24 | part 'simulation/simulated_descriptor.dart'; 25 | 26 | part 'simulation/simulated_peripheral.dart'; 27 | 28 | part 'simulation/simulated_service.dart'; 29 | -------------------------------------------------------------------------------- /lib/blemulator_core.dart: -------------------------------------------------------------------------------- 1 | part of blemulator; 2 | 3 | /// Entry point for using the simulator 4 | class Blemulator { 5 | static final Blemulator _instance = Blemulator._internal(); 6 | 7 | DartToPlatformBridge _toPlatformBridge; 8 | SimulationManager _simulationManager; 9 | // ignore: unused_field 10 | PlatformToDartBridge _toDartBridge; 11 | 12 | factory Blemulator() => _instance; 13 | 14 | Blemulator._internal() { 15 | _toPlatformBridge = DartToPlatformBridge(); 16 | _simulationManager = SimulationManager(_toPlatformBridge); 17 | _toDartBridge = PlatformToDartBridge(_simulationManager); 18 | } 19 | 20 | /// Switches the implementation of the native adapter to simulation 21 | /// 22 | /// Must be called before BleManager.createClient()! 23 | Future simulate() => _toPlatformBridge.simulate(); 24 | 25 | /// Adds peripheral to simulation 26 | void addSimulatedPeripheral(SimulatedPeripheral peripheral) { 27 | _simulationManager.addSimulatedPeripheral(peripheral); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/simulated_ble_error.dart: -------------------------------------------------------------------------------- 1 | part of blemulator; 2 | 3 | abstract class BleErrorCode { 4 | static const int UnknownError = 0; 5 | static const int BluetoothManagerDestroyed = 1; 6 | static const int OperationCancelled = 2; 7 | static const int OperationTimedOut = 3; 8 | static const int OperationStartFailed = 4; 9 | static const int InvalidIdentifiers = 5; 10 | 11 | static const int BluetoothUnsupported = 100; 12 | static const int BluetoothUnauthorized = 101; 13 | static const int BluetoothPoweredOff = 102; 14 | static const int BluetoothInUnknownState = 103; 15 | static const int BluetoothResetting = 104; 16 | static const int BluetoothStateChangeFailed = 105; 17 | 18 | static const int DeviceConnectionFailed = 200; 19 | static const int DeviceDisconnected = 201; 20 | static const int DeviceRSSIReadFailed = 202; 21 | static const int DeviceAlreadyConnected = 203; 22 | static const int DeviceNotFound = 204; 23 | static const int DeviceNotConnected = 205; 24 | static const int DeviceMTUChangeFailed = 206; 25 | 26 | static const int ServicesDiscoveryFailed = 300; 27 | static const int IncludedServicesDiscoveryFailed = 301; 28 | static const int ServiceNotFound = 302; 29 | static const int ServicesNotDiscovered = 303; 30 | 31 | static const int CharacteristicsDiscoveryFailed = 400; 32 | static const int CharacteristicWriteFailed = 401; 33 | static const int CharacteristicReadFailed = 402; 34 | static const int CharacteristicNotifyChangeFailed = 403; 35 | static const int CharacteristicNotFound = 404; 36 | static const int CharacteristicsNotDiscovered = 405; 37 | static const int CharacteristicInvalidDataFormat = 406; 38 | 39 | static const int DescriptorsDiscoveryFailed = 500; 40 | static const int DescriptorWriteFailed = 501; 41 | static const int DescriptorReadFailed = 502; 42 | static const int DescriptorNotFound = 503; 43 | static const int DescriptorsNotDiscovered = 504; 44 | static const int DescriptorInvalidDataFormat = 505; 45 | 46 | static const int ScanStartFailed = 600; 47 | static const int LocationServicesDisabled = 601; 48 | } 49 | 50 | class SimulatedBleError implements Exception { 51 | static final String _errorCode = 'errorCode'; 52 | static final String _reason = 'reason'; 53 | int errorCode; 54 | String reason; 55 | 56 | SimulatedBleError(this.errorCode, this.reason); 57 | 58 | @override 59 | String toString() { 60 | return jsonEncode({ 61 | _errorCode: errorCode, 62 | _reason: reason, 63 | }); 64 | } 65 | 66 | @override 67 | bool operator ==(Object other) => 68 | identical(this, other) || 69 | other is SimulatedBleError && 70 | runtimeType == other.runtimeType && 71 | errorCode == other.errorCode && 72 | reason == other.reason; 73 | 74 | @override 75 | int get hashCode => errorCode.hashCode ^ reason.hashCode; 76 | } 77 | -------------------------------------------------------------------------------- /lib/simulation/simulated_characteristic.dart: -------------------------------------------------------------------------------- 1 | part of blemulator; 2 | 3 | class SimulatedCharacteristic { 4 | final String uuid; 5 | final int id; 6 | SimulatedService service; 7 | Uint8List _value; 8 | final String convenienceName; 9 | final bool isReadable; 10 | final bool isWritableWithResponse; 11 | final bool isWritableWithoutResponse; 12 | final bool isNotifiable; 13 | bool isNotifying; 14 | final bool isIndicatable; 15 | final Map _descriptors; 16 | 17 | StreamController _streamController; 18 | 19 | SimulatedCharacteristic({ 20 | @required String uuid, 21 | @required Uint8List value, 22 | this.convenienceName, 23 | this.isReadable = true, 24 | this.isWritableWithResponse = true, 25 | this.isWritableWithoutResponse = true, 26 | this.isNotifiable = false, 27 | this.isNotifying = false, 28 | this.isIndicatable = false, 29 | List descriptors = const [], 30 | }) : uuid = uuid.toLowerCase(), 31 | id = IdGenerator().nextId(), 32 | _descriptors = { 33 | for (var descriptor in descriptors) descriptor.id: descriptor 34 | } { 35 | _value = value; 36 | _descriptors.values 37 | .forEach((descriptor) => descriptor.attachToCharacteristic(this)); 38 | } 39 | 40 | void attachToService(SimulatedService service) => this.service = service; 41 | 42 | Future read() async => _value; 43 | 44 | Future write(Uint8List value, {bool sendNotification = true}) async { 45 | _value = value; 46 | if (sendNotification && _streamController?.hasListener == true) { 47 | _streamController.sink.add(value); 48 | } 49 | } 50 | 51 | Stream monitor() { 52 | _streamController ??= StreamController.broadcast( 53 | onListen: () { 54 | isNotifying = true; 55 | }, 56 | onCancel: () { 57 | isNotifying = false; 58 | _streamController.close(); 59 | _streamController = null; 60 | }, 61 | ); 62 | return _streamController.stream; 63 | } 64 | 65 | List descriptors() => _descriptors.values.toList(); 66 | 67 | SimulatedDescriptor descriptor(int id) => _descriptors[id]; 68 | 69 | SimulatedDescriptor descriptorByUuid(String uuid) => 70 | _descriptors.values.firstWhere( 71 | (descriptor) => descriptor.uuid.toLowerCase() == uuid.toLowerCase(), 72 | orElse: () => null, 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /lib/simulation/simulated_descriptor.dart: -------------------------------------------------------------------------------- 1 | part of blemulator; 2 | 3 | class SimulatedDescriptor { 4 | final String uuid; 5 | final String convenienceName; 6 | final bool readable; 7 | final bool writable; 8 | Uint8List _value; 9 | final int id; 10 | SimulatedCharacteristic characteristic; 11 | StreamController _streamController; 12 | 13 | SimulatedDescriptor({ 14 | @required String uuid, 15 | @required Uint8List value, 16 | this.convenienceName, 17 | this.readable = true, 18 | this.writable = true, 19 | }) : uuid = uuid.toLowerCase(), 20 | id = IdGenerator().nextId() { 21 | _value = value; 22 | } 23 | 24 | void attachToCharacteristic(SimulatedCharacteristic characteristic) => 25 | this.characteristic = characteristic; 26 | 27 | Future read() async => _value; 28 | 29 | Future write(Uint8List value) async { 30 | _value = value; 31 | if (_streamController?.hasListener == true) { 32 | _streamController.add(_value); 33 | } 34 | } 35 | 36 | Stream monitor() { 37 | _streamController ??= StreamController.broadcast( 38 | onCancel: () { 39 | _streamController.close(); 40 | _streamController = null; 41 | }, 42 | ); 43 | return _streamController.stream; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/simulation/simulated_service.dart: -------------------------------------------------------------------------------- 1 | part of blemulator; 2 | 3 | class SimulatedService { 4 | final String uuid; 5 | final int id; 6 | final bool isAdvertised; 7 | final String convenienceName; 8 | final Map _characteristics; 9 | 10 | SimulatedService( 11 | {@required String uuid, 12 | @required this.isAdvertised, 13 | @required List characteristics, 14 | this.convenienceName}) 15 | : uuid = uuid.toLowerCase(), 16 | _characteristics = Map.fromIterable(characteristics, key: (v) => v.id), 17 | id = IdGenerator().nextId() { 18 | _characteristics.values.forEach((v) => v.attachToService(this)); 19 | } 20 | 21 | List characteristics() => 22 | _characteristics.values.toList(); 23 | 24 | SimulatedCharacteristic characteristic(int id) => _characteristics[id]; 25 | 26 | SimulatedCharacteristic characteristicByUuid(String uuid) => 27 | _characteristics.values.firstWhere( 28 | (characteristic) => 29 | characteristic.uuid.toLowerCase() == uuid.toLowerCase(), 30 | orElse: () => null); 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/defaults.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | const int defaultMtu = 23; 4 | const int defaultRssi = -30; 5 | 6 | const min_mtu = 23; 7 | const max_mtu = 512; 8 | -------------------------------------------------------------------------------- /lib/src/id_generator.dart: -------------------------------------------------------------------------------- 1 | class IdGenerator { 2 | static final IdGenerator _instance = IdGenerator._internal(); 3 | int _id = 0; 4 | 5 | factory IdGenerator() { 6 | return _instance; 7 | } 8 | 9 | IdGenerator._internal(); 10 | 11 | int nextId() { 12 | return _id++; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/internal.dart: -------------------------------------------------------------------------------- 1 | library internal; 2 | 3 | import 'dart:async'; 4 | import 'dart:collection'; 5 | import 'dart:typed_data'; 6 | import 'dart:io'; 7 | 8 | import 'package:async/async.dart'; 9 | import 'package:flutter/services.dart'; 10 | import 'package:blemulator/blemulator.dart'; 11 | import 'package:flutter_ble_lib/flutter_ble_lib.dart' as flutter_ble_lib; 12 | import 'package:meta/meta.dart'; 13 | 14 | part 'bridge/constants.dart'; 15 | 16 | part 'bridge/dart_to_platform_bridge.dart'; 17 | 18 | part 'bridge/platform_to_dart_bridge.dart'; 19 | 20 | part 'defaults.dart'; 21 | 22 | part 'scan_result.dart'; 23 | 24 | part 'simulation_manager.dart'; 25 | 26 | part 'simulation_manager_mixins/characteristics_mixin.dart'; 27 | 28 | part 'simulation_manager_mixins/client_managing_mixin.dart'; 29 | 30 | part 'simulation_manager_mixins/descriptors_mixin.dart'; 31 | 32 | part 'simulation_manager_mixins/discovery_mixin.dart'; 33 | 34 | part 'simulation_manager_mixins/error_checks_mixin.dart'; 35 | 36 | part 'simulation_manager_mixins/peripheral_connection_mixin.dart'; 37 | 38 | part 'simulation_manager_mixins/peripheral_scanning_mixin.dart'; 39 | 40 | part 'simulation_manager_mixins/response_models.dart'; 41 | 42 | part 'simulation_manager_mixins/rssi_mixin.dart'; 43 | 44 | part 'simulation_manager_mixins/mtu_mixin.dart'; 45 | 46 | part 'simulation_manager_mixins/simulation_manager_base.dart'; 47 | 48 | part 'util/mappers.dart'; 49 | -------------------------------------------------------------------------------- /lib/src/scan_result.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | class ScanResult { 4 | final String name; 5 | final String id; 6 | 7 | final int rssi; 8 | final bool isConnectable; 9 | final int txPowerLevel; 10 | 11 | final Uint8List manufacturerData; 12 | final Map serviceData; 13 | final List serviceUuids; 14 | 15 | final String localName; 16 | final List solicitedServiceUuids; 17 | final List overflowUuids; 18 | 19 | ScanResult(ScanInfo scanInfo, SimulatedPeripheral peripheral) 20 | : name = peripheral.name, 21 | id = peripheral.id, 22 | rssi = scanInfo.rssi, 23 | isConnectable = scanInfo.isConnectable, 24 | txPowerLevel = scanInfo.txPowerLevel, 25 | manufacturerData = scanInfo.manufacturerData, 26 | serviceData = scanInfo.serviceData, 27 | serviceUuids = scanInfo.serviceUuids, 28 | localName = scanInfo.localName, 29 | solicitedServiceUuids = scanInfo.solicitedServiceUuids, 30 | overflowUuids = scanInfo.overflowUuids; 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/simulation_manager.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | class SimulationManager extends SimulationManagerBaseWithErrorChecks 4 | with 5 | ClientManagingMixin, 6 | CharacteristicsMixin, 7 | DescriptorsMixin, 8 | ErrorChecksMixin, 9 | PeripheralConnectionMixin, 10 | PeripheralScanningMixing, 11 | DiscoveryMixin, 12 | PeripheralRssiMixin, 13 | PeripheralMtuMixin { 14 | SimulationManager(DartToPlatformBridge bridge) : super(bridge); 15 | 16 | void addSimulatedPeripheral(SimulatedPeripheral peripheral) { 17 | var mapEntry = _peripherals.putIfAbsent(peripheral.id, () => peripheral); 18 | 19 | if (!identical(mapEntry, peripheral)) { 20 | print('Peripheral not added - there already' 21 | ' exists a peripheral with the same id!'); 22 | } 23 | } 24 | 25 | void removeAllSimulatedPeripherals() { 26 | _peripherals.clear(); 27 | //TODO notify bridge? 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/simulation_manager_mixins/client_managing_mixin.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | mixin ClientManagingMixin on SimulationManagerBase { 4 | Future _createClient() async {} 5 | 6 | Future _destroyClient() async {} 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/simulation_manager_mixins/discovery_mixin.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | mixin DiscoveryMixin on SimulationManagerBaseWithErrorChecks { 4 | Future discoverAllServicesAndCharacteristics( 5 | String deviceIdentifier, String transactionId) => 6 | _saveCancelableOperation(transactionId, () async { 7 | await _errorIfUnknown(deviceIdentifier); 8 | await _errorIfNotConnected(deviceIdentifier); 9 | 10 | await _peripherals[deviceIdentifier].onDiscoveryRequest(); 11 | await _peripherals[deviceIdentifier].onDiscovery(); 12 | await _errorIfDisconnected(deviceIdentifier); 13 | return _peripherals[deviceIdentifier].services(); 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/simulation_manager_mixins/mtu_mixin.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | mixin PeripheralMtuMixin on SimulationManagerBaseWithErrorChecks { 4 | Future requestMtuForDevice(String identifier, int requestedMtu) async { 5 | await _errorIfUnknown(identifier); 6 | await _errorIfNotConnected(identifier); 7 | 8 | int mtu; 9 | if (Platform.isIOS) { 10 | mtu = await _peripherals[identifier].requestMtu(max_mtu); 11 | } else { 12 | mtu = await _peripherals[identifier].requestMtu(requestedMtu); 13 | } 14 | 15 | await _errorIfDisconnected(identifier); 16 | return mtu; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/simulation_manager_mixins/peripheral_connection_mixin.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | mixin PeripheralConnectionMixin on SimulationManagerBaseWithErrorChecks { 4 | final Map _connectionStateSubscriptions = {}; 5 | 6 | Future _connectToDevice(String identifier) async { 7 | await _errorIfUnknown(identifier); 8 | 9 | addConnectionStateObserverIfNeeded(identifier); 10 | 11 | await _errorIfConnected(identifier); 12 | 13 | await _errorIfCannotConnect(identifier); 14 | await _peripherals[identifier].onConnect(); 15 | 16 | if (Platform.isIOS) { 17 | await _peripherals[identifier].requestMtu(max_mtu); 18 | } 19 | } 20 | 21 | void addConnectionStateObserverIfNeeded(String identifier) { 22 | _connectionStateSubscriptions.putIfAbsent( 23 | identifier, 24 | () => _peripherals[identifier] 25 | .connectionStateStream 26 | .listen((connectionState) { 27 | _bridge.publishConnectionState( 28 | _peripherals[identifier], connectionState); 29 | 30 | if (connectionState == 31 | flutter_ble_lib.PeripheralConnectionState.disconnected) { 32 | _connectionStateSubscriptions[identifier].cancel(); 33 | _connectionStateSubscriptions.remove(identifier); 34 | } 35 | })); 36 | } 37 | 38 | Future _isDeviceConnected(String identifier) async { 39 | await _errorIfUnknown(identifier); 40 | return _peripherals[identifier].isConnected(); 41 | } 42 | 43 | Future _disconnectOrCancelConnection(String identifier) async { 44 | await _errorIfUnknown(identifier); 45 | await _errorIfNotConnected(identifier); 46 | return _peripherals[identifier].onDisconnect(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/simulation_manager_mixins/peripheral_scanning_mixin.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | mixin PeripheralScanningMixing on SimulationManagerBase { 4 | final List _scanSubscriptions = []; 5 | 6 | Future _startDeviceScan() async { 7 | _peripherals.values.forEach((peripheral) { 8 | _scanSubscriptions 9 | .add(peripheral.onScan(allowDuplicates: true).listen((scanResult) { 10 | return _bridge.publishScanResult(scanResult); 11 | })); 12 | }); 13 | } 14 | 15 | Future _stopDeviceScan() async { 16 | _scanSubscriptions 17 | .forEach((subscription) async => await subscription.cancel()); 18 | _scanSubscriptions.clear(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/simulation_manager_mixins/response_models.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | class CharacteristicResponse { 4 | SimulatedCharacteristic characteristic; 5 | Uint8List value; 6 | 7 | CharacteristicResponse(this.characteristic, this.value); 8 | } 9 | 10 | class DescriptorResponse { 11 | final String peripheralId; 12 | final SimulatedDescriptor descriptor; 13 | final Uint8List value; 14 | 15 | DescriptorResponse(this.peripheralId, this.descriptor, this.value); 16 | } 17 | -------------------------------------------------------------------------------- /lib/src/simulation_manager_mixins/rssi_mixin.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | mixin PeripheralRssiMixin on SimulationManagerBaseWithErrorChecks { 4 | Future _readRssiForDevice(String identifier) async { 5 | await _errorIfUnknown(identifier); 6 | await _errorIfNotConnected(identifier); 7 | 8 | var rssi = await _peripherals[identifier].rssi(); 9 | await _errorIfDisconnected(identifier); 10 | return rssi; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/util/mappers.dart: -------------------------------------------------------------------------------- 1 | part of internal; 2 | 3 | Map mapToCharacteristicJson( 4 | String peripheralId, 5 | SimulatedCharacteristic characteristic, 6 | Uint8List value, { 7 | bool serializeDescriptors = false, 8 | }) { 9 | var characteristicMap = { 10 | Metadata.deviceIdentifier: peripheralId, 11 | Metadata.characteristicId: characteristic.id, 12 | Metadata.characteristicUuid: characteristic.uuid, 13 | Metadata.value: value, 14 | Metadata.serviceUuid: characteristic.service.uuid, 15 | Metadata.serviceId: characteristic.service.id, 16 | Metadata.isReadable: characteristic.isReadable, 17 | Metadata.isWritableWithResponse: characteristic.isWritableWithResponse, 18 | Metadata.isWritableWithoutResponse: 19 | characteristic.isWritableWithoutResponse, 20 | Metadata.isNotifiable: characteristic.isNotifiable, 21 | Metadata.isNotifying: characteristic.isNotifying, 22 | Metadata.isIndicatable: characteristic.isIndicatable, 23 | }; 24 | if (serializeDescriptors) { 25 | characteristicMap.putIfAbsent( 26 | Metadata.descriptors, 27 | () => characteristic 28 | .descriptors() 29 | .map( 30 | (descriptor) => mapToDescriptorJson( 31 | DescriptorResponse(peripheralId, descriptor, null)), 32 | ) 33 | .toList(), 34 | ); 35 | } 36 | 37 | return characteristicMap; 38 | } 39 | 40 | Map mapToDescriptorJson( 41 | DescriptorResponse response, 42 | ) => 43 | { 44 | Metadata.deviceIdentifier: response.peripheralId, 45 | Metadata.serviceUuid: response.descriptor.characteristic.service.uuid, 46 | Metadata.serviceId: response.descriptor.characteristic.service.id, 47 | Metadata.characteristicUuid: response.descriptor.characteristic.uuid, 48 | Metadata.characteristicId: response.descriptor.characteristic.id, 49 | Metadata.descriptorUuid: response.descriptor.uuid, 50 | Metadata.descriptorId: response.descriptor.id, 51 | Metadata.value: response.value, 52 | }; 53 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: blemulator 2 | description: Flutter plugin for simulating BLE peripherals on FlutterBleLib 3 | version: 1.2.1 4 | homepage: https://github.com/Polidea/blemulator_flutter 5 | 6 | environment: 7 | sdk: ">=2.2.2 <3.0.0" 8 | # Flutter versions prior to 1.10 did not support the flutter.plugin.platforms map. 9 | flutter: ">=1.10.0 <2.0.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | flutter_ble_lib: ^2.3.1 15 | async: ^2.4.0 16 | meta: ^1.1.8 17 | pedantic: ^1.9.0 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | test: ^1.5.3 24 | mockito: ^4.0.0 25 | 26 | # For information on the generic Dart part of this file, see the 27 | # following page: https://dart.dev/tools/pub/pubspec 28 | 29 | # The following section is specific to Flutter. 30 | flutter: 31 | # This section identifies this Flutter project as a plugin project. 32 | # The androidPackage and pluginClass identifiers should not ordinarily 33 | # be modified. They are used by the tooling to maintain consistency when 34 | # adding or updating assets for this project. 35 | plugin: 36 | platforms: 37 | android: 38 | package: com.polidea.blemulator 39 | pluginClass: BlemulatorPlugin 40 | ios: 41 | pluginClass: BlemulatorPlugin 42 | 43 | # To add assets to your plugin package, add an assets section, like this: 44 | # assets: 45 | # - images/a_dot_burr.jpeg 46 | # - images/a_dot_ham.jpeg 47 | # 48 | # For details regarding assets in packages, see 49 | # https://flutter.dev/assets-and-images/#from-packages 50 | # 51 | # An image asset can refer to one or more resolution-specific "variants", see 52 | # https://flutter.dev/assets-and-images/#resolution-aware. 53 | 54 | # To add custom fonts to your plugin package, add a fonts section here, 55 | # in this "flutter" section. Each entry in this list should have a 56 | # "family" key with the font family name, and a "fonts" key with a 57 | # list giving the asset and other descriptors for the font. For 58 | # example: 59 | # fonts: 60 | # - family: Schyler 61 | # fonts: 62 | # - asset: fonts/Schyler-Regular.ttf 63 | # - asset: fonts/Schyler-Italic.ttf 64 | # style: italic 65 | # - family: Trajan Pro 66 | # fonts: 67 | # - asset: fonts/TrajanPro.ttf 68 | # - asset: fonts/TrajanPro_Bold.ttf 69 | # weight: 700 70 | # 71 | # For details regarding fonts in packages, see 72 | # https://flutter.dev/custom-fonts/#from-packages 73 | -------------------------------------------------------------------------------- /site/logo_Blemulator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polidea/blemulator_flutter/5046c7d4b3bfd59529914efa0411852db145f52f/site/logo_Blemulator.png -------------------------------------------------------------------------------- /test/factory/simulated_peripheral_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator/blemulator.dart'; 2 | 3 | class SimulatedPeripheralBuilder { 4 | String deviceId = 'defaultDeviceId'; 5 | bool isConnected = false; 6 | int mtu = 11; 7 | 8 | SimulatedPeripheral build() { 9 | var samplePeripheral = SamplePeripheral(id: deviceId)..mtu = mtu; 10 | 11 | if (isConnected) samplePeripheral.onConnect(); 12 | 13 | return samplePeripheral; 14 | } 15 | } 16 | 17 | class SamplePeripheral extends SimulatedPeripheral { 18 | SamplePeripheral( 19 | {String id = '4B:99:4C:34:DE:77', String name = 'SamplePeripheral'}) 20 | : super( 21 | id: id, 22 | name: name, 23 | services: [], 24 | advertisementInterval: Duration(milliseconds: 500)); 25 | } 26 | -------------------------------------------------------------------------------- /test/factory/simulation_manager_factory.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:blemulator/src/internal.dart'; 3 | import 'package:mockito/mockito.dart'; 4 | 5 | 6 | class DartToPlatformBridgeMock extends Mock implements DartToPlatformBridge {} 7 | 8 | class SimulationManagerFactory { 9 | SimulationManager create() => SimulationManager(DartToPlatformBridgeMock()); 10 | } -------------------------------------------------------------------------------- /test/internal/bridge/platform_to_dart_bridge_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:blemulator/src/internal.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_test/flutter_test.dart' as flutter_test; 6 | import 'package:mockito/mockito.dart'; 7 | import 'package:test/test.dart'; 8 | 9 | class SimulationManagerMock extends Mock implements SimulationManager {} 10 | 11 | void main() { 12 | flutter_test.TestWidgetsFlutterBinding.ensureInitialized(); 13 | const DEVICE_ID = 'id123'; 14 | const REQUESTED_MTU = 33; 15 | const NEGOTIATED_MTU = 24; 16 | 17 | SimulationManagerMock simulationManager; 18 | PlatformToDartBridge platformToDartBridge; 19 | 20 | setUp(() { 21 | simulationManager = SimulationManagerMock(); 22 | platformToDartBridge = PlatformToDartBridge(simulationManager); 23 | }); 24 | 25 | group('MTU', () { 26 | test('should call request MTU with proper params from the call', () { 27 | //given 28 | var methodCall = MethodCall(DartMethodName.requestMtu, { 29 | SimulationArgumentName.deviceIdentifier: DEVICE_ID, 30 | SimulationArgumentName.mtu: REQUESTED_MTU 31 | }); 32 | 33 | //when 34 | platformToDartBridge.dispatchPlatformCall(methodCall); 35 | 36 | //then 37 | verify(simulationManager.requestMtuForDevice(DEVICE_ID, REQUESTED_MTU)); 38 | }); 39 | 40 | test('should return returned MTU', () async { 41 | //given 42 | var methodCall = MethodCall(DartMethodName.requestMtu, { 43 | SimulationArgumentName.deviceIdentifier: DEVICE_ID, 44 | SimulationArgumentName.mtu: REQUESTED_MTU 45 | }); 46 | 47 | when(simulationManager.requestMtuForDevice(DEVICE_ID, REQUESTED_MTU)) 48 | .thenAnswer((_) => Future.value(NEGOTIATED_MTU)); 49 | 50 | //when 51 | int negotiatedMtu = await platformToDartBridge.dispatchPlatformCall(methodCall); 52 | 53 | //then 54 | expect(negotiatedMtu, NEGOTIATED_MTU); 55 | }); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /test/internal/simulation_manager_mixin/discovery_mixin_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator/blemulator.dart'; 2 | import 'package:blemulator/src/internal.dart'; 3 | import 'package:mockito/mockito.dart'; 4 | import 'package:pedantic/pedantic.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../factory/simulation_manager_factory.dart'; 8 | 9 | class MockedPeripheral extends Mock implements SimulatedPeripheral {} 10 | 11 | void main() { 12 | const DEVICE_ID = 'qwe123'; 13 | DiscoveryMixin discoveryMixin; 14 | var mockedPeripheral; 15 | 16 | setUp(() { 17 | mockedPeripheral = MockedPeripheral(); 18 | when(mockedPeripheral.id).thenAnswer((_) => DEVICE_ID); 19 | when(mockedPeripheral.isConnected()).thenAnswer((_) => true); 20 | when(mockedPeripheral.onDiscovery()).thenAnswer((_) => Future.sync(() {})); 21 | when(mockedPeripheral.services()).thenAnswer((_) => []); 22 | discoveryMixin = SimulationManagerFactory().create() 23 | ..addSimulatedPeripheral(mockedPeripheral); 24 | }); 25 | 26 | test( 27 | 'discoverAllServicesAndCharacteristics triggers error when transaction with given id has not been finished yet', 28 | () async { 29 | when(mockedPeripheral.onDiscoveryRequest()) 30 | .thenAnswer((_) => Future.delayed(Duration(milliseconds: 200))); 31 | 32 | unawaited(expectLater( 33 | discoveryMixin.discoverAllServicesAndCharacteristics(DEVICE_ID, '1'), 34 | throwsA(equals(SimulatedBleError( 35 | BleErrorCode.OperationCancelled, 'Operation cancelled'))), 36 | )); 37 | await Future.delayed(Duration(milliseconds: 100)); 38 | unawaited( 39 | discoveryMixin.discoverAllServicesAndCharacteristics(DEVICE_ID, '1')); 40 | }); 41 | 42 | test( 43 | 'discoverAllServicesAndCharacteristics does not trigger error when transaction with given id has already finished', 44 | () async { 45 | when(mockedPeripheral.onDiscoveryRequest()) 46 | .thenAnswer((_) => Future.sync(() {})); 47 | 48 | await expectLater( 49 | discoveryMixin.discoverAllServicesAndCharacteristics(DEVICE_ID, '1'), 50 | completion(equals([])), 51 | ); 52 | 53 | await Future.delayed(Duration(milliseconds: 100)); 54 | 55 | await discoveryMixin.discoverAllServicesAndCharacteristics(DEVICE_ID, '1'); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /test/internal/simulation_manager_mixin/mtu_mixin_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:blemulator/src/internal.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | import '../../factory/simulated_peripheral_factory.dart'; 5 | import '../../factory/simulation_manager_factory.dart'; 6 | 7 | void main() { 8 | const DEVICE_ID = 'qwe123'; 9 | const MTU = 33; 10 | PeripheralMtuMixin peripheralMtuMixin; 11 | 12 | setUp(() { 13 | peripheralMtuMixin = SimulationManagerFactory().create() 14 | ..addSimulatedPeripheral( 15 | (SimulatedPeripheralBuilder() 16 | ..isConnected = true 17 | ..mtu = MTU 18 | ..deviceId = DEVICE_ID) 19 | .build() 20 | ); 21 | 22 | }); 23 | 24 | test('should change MTU', () async { 25 | //given 26 | const newMtu = 119; 27 | 28 | //when 29 | var mtu = await peripheralMtuMixin.requestMtuForDevice(DEVICE_ID, newMtu); 30 | 31 | //then 32 | expect(mtu, newMtu); 33 | }); 34 | 35 | test('should not allow to request MTU over 512', () async { 36 | //given 37 | const maxMtu = 512; 38 | 39 | //when 40 | var mtu = await peripheralMtuMixin.requestMtuForDevice(DEVICE_ID, maxMtu + 1); 41 | 42 | //then 43 | expect(mtu, maxMtu); 44 | }); 45 | 46 | test('should not allow to request MTU below 23', () async { 47 | //given 48 | const minMtu = 23; 49 | 50 | //when 51 | var mtu = await peripheralMtuMixin.requestMtuForDevice(DEVICE_ID, minMtu - 23); 52 | 53 | //then 54 | expect(mtu, minMtu); 55 | }); 56 | } --------------------------------------------------------------------------------