ESP32 | FLUTTER | BLE - Temperature & Humidity Check App
72
Esp32_temperature_humidity_ble_FLUTTER/flutter_ble_app/.gitignore
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
**/android/.gradle
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!**/ios/**/default.mode1v3
|
||||
!**/ios/**/default.mode2v3
|
||||
!**/ios/**/default.pbxuser
|
||||
!**/ios/**/default.perspectivev3
|
||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: b712a172f9694745f50505c93340883493b505e5
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
@@ -0,0 +1,16 @@
|
||||
# flutter_ble_app
|
||||
|
||||
A new Flutter application.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
@@ -0,0 +1,61 @@
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.eric.flutter_ble_app"
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 28
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.eric.flutter_ble_app">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,33 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.eric.flutter_ble_app">
|
||||
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
In most cases you can leave this as-is, but you if you want to provide
|
||||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:label="flutter_ble_app"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- This keeps the window background of the activity showing
|
||||
until Flutter renders its first frame. It can be removed if
|
||||
there is no splash screen (such as the default splash screen
|
||||
defined in @style/LaunchTheme). -->
|
||||
<meta-data
|
||||
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
|
||||
android:value="true" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.eric.flutter_ble_app;
|
||||
|
||||
import android.os.Bundle;
|
||||
import io.flutter.app.FlutterActivity;
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||
|
||||
public class MainActivity extends FlutterActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
GeneratedPluginRegistrant.registerWith(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
|
After Width: | Height: | Size: 544 B |
|
After Width: | Height: | Size: 442 B |
|
After Width: | Height: | Size: 721 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.eric.flutter_ble_app">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,29 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.2.1'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = '../build'
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
}
|
||||
subprojects {
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#Fri Jun 23 08:50:38 CEST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
|
||||
@@ -0,0 +1,15 @@
|
||||
include ':app'
|
||||
|
||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
||||
|
||||
def plugins = new Properties()
|
||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
||||
if (pluginsFile.exists()) {
|
||||
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
||||
}
|
||||
|
||||
plugins.each { name, path ->
|
||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
||||
include ":$name"
|
||||
project(":$name").projectDir = pluginDirectory
|
||||
}
|
||||
7
Esp32_temperature_humidity_ble_FLUTTER/flutter_ble_app/flutter_ble-master/.gitignore
vendored
Executable file
@@ -0,0 +1,7 @@
|
||||
.DS_Store
|
||||
.dart_tool/
|
||||
|
||||
.packages
|
||||
.pub/
|
||||
|
||||
build/
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
|
||||
channel: stable
|
||||
|
||||
project_type: plugin
|
||||
@@ -0,0 +1,97 @@
|
||||
## 0.5.1
|
||||
* Forked repo to internal use
|
||||
* Upgrade Android protobuf dependencies
|
||||
* Refresh iOS build files
|
||||
|
||||
## 0.5.0
|
||||
* **Breaking change**. Migrate from the deprecated original Android Support
|
||||
Library to AndroidX. This shouldn't result in any functional changes, but it
|
||||
requires any Android apps using this plugin to [also
|
||||
migrate](https://developer.android.com/jetpack/androidx/migrate) if they're
|
||||
using the original support library.
|
||||
|
||||
## 0.4.2+1
|
||||
* Upgrade Android Gradle plugin to 3.3.0
|
||||
* Refresh iOS build files
|
||||
|
||||
## 0.4.2
|
||||
* Set the verbosity of log messages with `setLogLevel`
|
||||
* Updated iOS and Android project files
|
||||
* `autoConnect` now configurable for Android
|
||||
* Various bug fixes
|
||||
|
||||
## 0.4.1
|
||||
* Fixed bug where setNotifyValue wasn't properly awaitable.
|
||||
* Various UI bug fixes to example app.
|
||||
* Removed unnecessary intl dependencies in example app.
|
||||
|
||||
## 0.4.0
|
||||
* **Breaking change**. Manufacturer Data is now a `Map` of manufacturer ID's.
|
||||
* Service UUID's, service data, tx power level packets fixed in advertising data.
|
||||
* Example app updated to show advertising data.
|
||||
* Various other bug fixes.
|
||||
|
||||
## 0.3.4
|
||||
* Updated to use the latest protobuf (^0.9.0+1)
|
||||
* Updated other dependencies
|
||||
|
||||
## 0.3.3
|
||||
* `scan` `withServices` to filter by service UUID's (iOS)
|
||||
* Error handled when trying to scan with adapter off (Android)
|
||||
|
||||
## 0.3.2
|
||||
* Runtime permissions for Android
|
||||
* `scan` `withServices` to filter by service UUID's (Android)
|
||||
* Scan mode can be specified (Android)
|
||||
* Now targets the latest android SDK
|
||||
* Dart 2 compatibility
|
||||
|
||||
## 0.3.1
|
||||
* Now allows simultaneous notifications of characteristics
|
||||
* Fixed bug on iOS that was returning `discoverServices` too early
|
||||
|
||||
## 0.3.0
|
||||
* iOS support added
|
||||
* Bug fixed in example causing discoverServices to be called multiple times
|
||||
* Various other bug fixes
|
||||
|
||||
## 0.2.4
|
||||
* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin
|
||||
3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in
|
||||
order to use this version of the plugin. Instructions can be found
|
||||
[here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1).
|
||||
|
||||
## 0.2.3
|
||||
* Bug fixes
|
||||
|
||||
## 0.2.2
|
||||
* **Breaking changes**:
|
||||
* `startScan` renamed to `scan`
|
||||
* `ScanResult` now returns a `BluetoothDevice`
|
||||
* `connect` now takes a `BluetoothDevice` and returns Stream<BluetoothDeviceState>
|
||||
* Added parameter `timeout` to `connect`
|
||||
* Automatic disconnect on deviceConnection.cancel()
|
||||
|
||||
## 0.2.1
|
||||
* **Breaking change**. Removed `stopScan` from API, use `scanSubscription.cancel()` instead
|
||||
* Automatically stops scan when `startScan` subscription is canceled (thanks to @brianegan)
|
||||
* Added `timeout` parameter to `startScan`
|
||||
* Updated example app to show new scan functionality
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* Added state and onStateChanged for BluetoothDevice
|
||||
* Updated example to show new functionality
|
||||
|
||||
## 0.1.1
|
||||
|
||||
* Fixed image for pub.dartlang.org
|
||||
|
||||
## 0.1.0
|
||||
|
||||
* Characteristic notifications/indications.
|
||||
* Merged in Guid library, removed from pubspec.yaml.
|
||||
|
||||
## 0.0.1 - September 1st, 2017
|
||||
|
||||
* Initial Release.
|
||||
@@ -0,0 +1 @@
|
||||
TODO: Add your license here.
|
||||
@@ -0,0 +1,119 @@
|
||||
|
||||
<br>
|
||||
<p align="center">
|
||||
<img alt="FlutterBle" src="https://github.com/pauldemarco/flutter_blue/blob/master/site/flutterblue.png?raw=true" />
|
||||
</p>
|
||||
<br><br>
|
||||
|
||||
## Introduction
|
||||
|
||||
FlutterBle is a bluetooth plugin for [Flutter](http://www.flutter.io), a new mobile SDK to help developers build modern apps for iOS and Android.
|
||||
fork of [FlutterBlue](https://github.com/pauldemarco/flutter_blue)
|
||||
|
||||
## Cross-Platform Bluetooth LE
|
||||
FlutterBle aims to offer the most from both platforms (iOS and Android).
|
||||
|
||||
Using the FlutterBle instance, you can scan for and connect to nearby devices ([BluetoothDevice](#bluetoothdevice-api)).
|
||||
Once connected to a device, the BluetoothDevice object can discover services ([BluetoothService](lib/src/bluetooth_service.dart)), characteristics ([BluetoothCharacteristic](lib/src/bluetooth_characteristic.dart)), and descriptors ([BluetoothDescriptor](lib/src/bluetooth_descriptor.dart)).
|
||||
The BluetoothDevice object is then used to directly interact with characteristics and descriptors.
|
||||
|
||||
## Usage
|
||||
### Obtain an instance
|
||||
```dart
|
||||
FlutterBle flutterBlue = FlutterBle.instance;
|
||||
```
|
||||
|
||||
### Scan for devices
|
||||
```dart
|
||||
/// Start scanning
|
||||
var scanSubscription = flutterBlue.scan().listen((scanResult) {
|
||||
// do something with scan result
|
||||
});
|
||||
|
||||
/// Stop scanning
|
||||
scanSubscription.cancel();
|
||||
```
|
||||
|
||||
### Connect to a device
|
||||
```dart
|
||||
/// Create a connection to the device
|
||||
var deviceConnection = flutterBlue.connect(device).listen((s) {
|
||||
if(s == BluetoothDeviceState.connected) {
|
||||
// device is connected, do something
|
||||
}
|
||||
});
|
||||
|
||||
/// Disconnect from device
|
||||
deviceConnection.cancel();
|
||||
```
|
||||
|
||||
### Discover services
|
||||
```dart
|
||||
List<BluetoothService> services = await device.discoverServices();
|
||||
services.forEach((service) {
|
||||
// do something with service
|
||||
});
|
||||
```
|
||||
|
||||
### Read and write characteristics
|
||||
```dart
|
||||
// Reads all characteristics
|
||||
var characteristics = service.characteristics;
|
||||
for(BluetoothCharacteristic c in characteristics) {
|
||||
List<int> value = await device.readCharacteristic(c);
|
||||
print(value);
|
||||
}
|
||||
|
||||
// Writes to a characteristic
|
||||
await device.writeCharacteristic(c, [0x12, 0x34])
|
||||
```
|
||||
|
||||
### Read and write descriptors
|
||||
```dart
|
||||
// Reads all descriptors
|
||||
var descriptors = characteristic.descriptors;
|
||||
for(BluetoothDescriptor d in descriptors) {
|
||||
List<int> value = await device.readDescriptor(d);
|
||||
print(value);
|
||||
}
|
||||
|
||||
// Writes to a descriptor
|
||||
await device.writeDescriptor(d, [0x12, 0x34])
|
||||
```
|
||||
|
||||
### Set notifications
|
||||
```dart
|
||||
await device.setNotifyValue(characteristic, true);
|
||||
device.onValueChanged(characteristic).listen((value) {
|
||||
// do something with new value
|
||||
});
|
||||
```
|
||||
|
||||
## Reference
|
||||
### FlutterBle API
|
||||
| | Android | iOS | Description |
|
||||
| :--------------- | :----------------: | :------------------: | :-------------------------------- |
|
||||
| scan | :white_check_mark: | :white_check_mark: | Starts a scan for Bluetooth Low Energy devices. |
|
||||
| connect | :white_check_mark: | :white_check_mark: | Establishes a connection to the Bluetooth Device. |
|
||||
| state | :white_check_mark: | :white_check_mark: | Gets the current state of the Bluetooth Adapter. |
|
||||
| onStateChanged | :white_check_mark: | :white_check_mark: | Stream of state changes for the Bluetooth Adapter. |
|
||||
|
||||
### BluetoothDevice API
|
||||
| | Android | iOS | Description |
|
||||
| :-------------------------- | :------------------: | :------------------: | :-------------------------------- |
|
||||
| discoverServices | :white_check_mark: | :white_check_mark: | Discovers services offered by the remote device as well as their characteristics and descriptors. |
|
||||
| services | :white_check_mark: | :white_check_mark: | Gets a list of services. Requires that discoverServices() has completed. |
|
||||
| readCharacteristic | :white_check_mark: | :white_check_mark: | Retrieves the value of a specified characteristic. |
|
||||
| readDescriptor | :white_check_mark: | :white_check_mark: | Retrieves the value of a specified descriptor. |
|
||||
| writeCharacteristic | :white_check_mark: | :white_check_mark: | Writes the value of a characteristic. |
|
||||
| writeDescriptor | :white_check_mark: | :white_check_mark: | Writes the value of a descriptor. |
|
||||
| setNotifyValue | :white_check_mark: | :white_check_mark: | Sets notifications or indications on the specified characteristic. |
|
||||
| onValueChanged | :white_check_mark: | :white_check_mark: | Notifies when the characteristic's value has changed. |
|
||||
| state | :white_check_mark: | :white_check_mark: | Gets the current state of the Bluetooth Device. |
|
||||
| onStateChanged | :white_check_mark: | :white_check_mark: | Notifies of state changes for the Bluetooth Device. |
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
### Scanning for service UUID's doesn't return any results
|
||||
Make sure the device is advertising which service UUID's it supports. This is found in the advertisement
|
||||
packet as **UUID 16 bit complete list** or **UUID 128 bit complete list**.
|
||||
8
Esp32_temperature_humidity_ble_FLUTTER/flutter_ble_app/flutter_ble-master/android/.gitignore
vendored
Executable file
@@ -0,0 +1,8 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
@@ -0,0 +1,77 @@
|
||||
group 'ai.longev.flutter.flutter_ble'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.4.1'
|
||||
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.google.protobuf'
|
||||
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 19
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
}
|
||||
sourceSets {
|
||||
main {
|
||||
proto {
|
||||
srcDir '../protos'
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
// Required for local unit tests (JUnit 4 framework)
|
||||
implementation 'androidx.core:core:1.0.2'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protobuf {
|
||||
// Configure the protoc executable
|
||||
protoc {
|
||||
// Download from repositories
|
||||
artifact = 'com.google.protobuf:protoc:3.7.0'
|
||||
}
|
||||
plugins {
|
||||
javalite { artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'}
|
||||
grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.19.0' }
|
||||
}
|
||||
generateProtoTasks {
|
||||
all().each { task ->
|
||||
task.plugins {
|
||||
javalite {}
|
||||
grpc {
|
||||
// Options added to --grpc_out
|
||||
option 'lite'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.protobuf:protobuf-lite:3.0.1'
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
@@ -0,0 +1 @@
|
||||
rootProject.name = 'flutter_ble'
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="ai.longev.flutter.flutter_ble">
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,106 @@
|
||||
package ai.longev.flutter.flutter_ble;
|
||||
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import ai.longev.flutter.flutter_ble.Protos;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Parser of Bluetooth Advertisement packets.
|
||||
*/
|
||||
class AdvertisementParser {
|
||||
|
||||
/**
|
||||
* Parses packet data into {@link Protos.AdvertisementData} structure.
|
||||
*
|
||||
* @param rawData The scan record data.
|
||||
* @return An AdvertisementData proto object.
|
||||
* @throws ArrayIndexOutOfBoundsException if the input is truncated.
|
||||
*/
|
||||
static Protos.AdvertisementData parse(byte[] rawData) {
|
||||
ByteBuffer data = ByteBuffer.wrap(rawData).asReadOnlyBuffer().order(ByteOrder.LITTLE_ENDIAN);
|
||||
Protos.AdvertisementData.Builder ret = Protos.AdvertisementData.newBuilder();
|
||||
boolean seenLongLocalName = false;
|
||||
do {
|
||||
int length = data.get() & 0xFF;
|
||||
if (length == 0) {
|
||||
break;
|
||||
}
|
||||
if (length > data.remaining()) {
|
||||
throw new ArrayIndexOutOfBoundsException("Not enough data.");
|
||||
}
|
||||
|
||||
int type = data.get() & 0xFF;
|
||||
length--;
|
||||
|
||||
switch (type) {
|
||||
case 0x08: // Short local name.
|
||||
case 0x09: { // Long local name.
|
||||
if (seenLongLocalName) {
|
||||
// Prefer the long name over the short.
|
||||
data.position(data.position() + length);
|
||||
break;
|
||||
}
|
||||
byte[] name = new byte[length];
|
||||
data.get(name);
|
||||
ret.setLocalName(new String(name, StandardCharsets.UTF_8));
|
||||
if (type == 0x09) {
|
||||
seenLongLocalName = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x0A: { // Power level.
|
||||
ret.setTxPowerLevel(Protos.Int32Value.newBuilder().setValue(data.get()));
|
||||
break;
|
||||
}
|
||||
case 0x16: // Service Data with 16 bit UUID.
|
||||
case 0x20: // Service Data with 32 bit UUID.
|
||||
case 0x21: { // Service Data with 128 bit UUID.
|
||||
UUID uuid;
|
||||
int remainingDataLength = 0;
|
||||
if (type == 0x16 || type == 0x20) {
|
||||
long uuidValue;
|
||||
if (type == 0x16) {
|
||||
uuidValue = data.getShort() & 0xFFFF;
|
||||
remainingDataLength = length - 2;
|
||||
} else {
|
||||
uuidValue = data.getInt() & 0xFFFFFFFF;
|
||||
remainingDataLength = length - 4;
|
||||
}
|
||||
uuid = UUID.fromString(String.format("%08x-0000-1000-8000-00805f9b34fb", uuidValue));
|
||||
} else {
|
||||
long msb = data.getLong();
|
||||
long lsb = data.getLong();
|
||||
uuid = new UUID(msb, lsb);
|
||||
remainingDataLength = length - 16;
|
||||
}
|
||||
byte[] remainingData = new byte[remainingDataLength];
|
||||
data.get(remainingData);
|
||||
ret.putServiceData(uuid.toString(), ByteString.copyFrom(remainingData));
|
||||
break;
|
||||
}
|
||||
case 0xFF: {// Manufacturer specific data.
|
||||
if (length < 2) {
|
||||
throw new ArrayIndexOutOfBoundsException("Not enough data for Manufacturer specific data.");
|
||||
}
|
||||
int manufacturerId = data.getShort();
|
||||
if ((length - 2) > 0) {
|
||||
byte[] msd = new byte[length - 2];
|
||||
data.get(msd);
|
||||
ret.putManufacturerData(manufacturerId, ByteString.copyFrom(msd));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
data.position(data.position() + length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
return ret.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,884 @@
|
||||
package ai.longev.flutter.flutter_ble;
|
||||
|
||||
// Copyright 2017, Paul DeMarco.
|
||||
// All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.TargetApi;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.bluetooth.le.BluetoothLeScanner;
|
||||
import android.bluetooth.le.ScanCallback;
|
||||
import android.bluetooth.le.ScanFilter;
|
||||
import android.bluetooth.le.ScanResult;
|
||||
import android.bluetooth.le.ScanSettings;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelUuid;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.flutter.plugin.common.EventChannel;
|
||||
import io.flutter.plugin.common.EventChannel.EventSink;
|
||||
import io.flutter.plugin.common.EventChannel.StreamHandler;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugin.common.MethodChannel.Result;
|
||||
import io.flutter.plugin.common.PluginRegistry.Registrar;
|
||||
import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener;
|
||||
|
||||
|
||||
/**
|
||||
* FlutterBlePlugin
|
||||
*/
|
||||
public class FlutterBlePlugin implements MethodCallHandler, RequestPermissionsResultListener {
|
||||
private static final String TAG = "FlutterBlePlugin";
|
||||
private static final String NAMESPACE = "ai.longev.flutter/flutter_ble";
|
||||
private static final int REQUEST_COARSE_LOCATION_PERMISSIONS = 1452;
|
||||
static final private UUID CCCD_ID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
|
||||
private final Registrar registrar;
|
||||
private final MethodChannel channel;
|
||||
private final EventChannel stateChannel;
|
||||
private final EventChannel scanResultChannel;
|
||||
private final EventChannel servicesDiscoveredChannel;
|
||||
private final EventChannel characteristicReadChannel;
|
||||
private final EventChannel descriptorReadChannel;
|
||||
private final BluetoothManager mBluetoothManager;
|
||||
private final Map<String, BluetoothGatt> mGattServers = new HashMap<>();
|
||||
private final StreamHandler stateHandler = new StreamHandler() {
|
||||
private EventSink sink;
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
|
||||
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
|
||||
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
|
||||
BluetoothAdapter.ERROR);
|
||||
switch (state) {
|
||||
case BluetoothAdapter.STATE_OFF:
|
||||
sink.success(Protos.BluetoothState.newBuilder().setState(Protos.BluetoothState.State.OFF).build().toByteArray());
|
||||
break;
|
||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
||||
sink.success(Protos.BluetoothState.newBuilder().setState(Protos.BluetoothState.State.TURNING_OFF).build().toByteArray());
|
||||
break;
|
||||
case BluetoothAdapter.STATE_ON:
|
||||
sink.success(Protos.BluetoothState.newBuilder().setState(Protos.BluetoothState.State.ON).build().toByteArray());
|
||||
break;
|
||||
case BluetoothAdapter.STATE_TURNING_ON:
|
||||
sink.success(Protos.BluetoothState.newBuilder().setState(Protos.BluetoothState.State.TURNING_ON).build().toByteArray());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onListen(Object o, EventChannel.EventSink eventSink) {
|
||||
sink = eventSink;
|
||||
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
registrar.activity().registerReceiver(mReceiver, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(Object o) {
|
||||
sink = null;
|
||||
registrar.activity().unregisterReceiver(mReceiver);
|
||||
}
|
||||
};
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
private LogLevel logLevel = LogLevel.EMERGENCY;
|
||||
// Pending call and result for startScan, in the case where permissions are needed
|
||||
private MethodCall pendingCall;
|
||||
private Result pendingResult;
|
||||
private ScanCallback scanCallback21;
|
||||
private BluetoothAdapter.LeScanCallback scanCallback18;
|
||||
private EventSink scanResultsSink;
|
||||
private final StreamHandler scanResultsHandler = new StreamHandler() {
|
||||
@Override
|
||||
public void onListen(Object o, EventChannel.EventSink eventSink) {
|
||||
scanResultsSink = eventSink;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(Object o) {
|
||||
scanResultsSink = null;
|
||||
}
|
||||
};
|
||||
private EventSink servicesDiscoveredSink;
|
||||
private final StreamHandler servicesDiscoveredHandler = new StreamHandler() {
|
||||
@Override
|
||||
public void onListen(Object o, EventChannel.EventSink eventSink) {
|
||||
servicesDiscoveredSink = eventSink;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(Object o) {
|
||||
servicesDiscoveredSink = null;
|
||||
}
|
||||
};
|
||||
private EventSink characteristicReadSink;
|
||||
private final StreamHandler characteristicReadHandler = new StreamHandler() {
|
||||
@Override
|
||||
public void onListen(Object o, EventChannel.EventSink eventSink) {
|
||||
characteristicReadSink = eventSink;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(Object o) {
|
||||
characteristicReadSink = null;
|
||||
}
|
||||
};
|
||||
private EventSink descriptorReadSink;
|
||||
private final StreamHandler descriptorReadHandler = new StreamHandler() {
|
||||
@Override
|
||||
public void onListen(Object o, EventChannel.EventSink eventSink) {
|
||||
descriptorReadSink = eventSink;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(Object o) {
|
||||
descriptorReadSink = null;
|
||||
}
|
||||
};
|
||||
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
|
||||
@Override
|
||||
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
||||
log(LogLevel.DEBUG, "[onConnectionStateChange] status: " + status + " newState: " + newState);
|
||||
channel.invokeMethod("DeviceState", ProtoMaker.from(gatt.getDevice(), newState).toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
log(LogLevel.DEBUG, "[onServicesDiscovered] count: " + gatt.getServices().size() + " status: " + status);
|
||||
if (servicesDiscoveredSink != null) {
|
||||
Protos.DiscoverServicesResult.Builder p = Protos.DiscoverServicesResult.newBuilder();
|
||||
p.setRemoteId(gatt.getDevice().getAddress());
|
||||
for (BluetoothGattService s : gatt.getServices()) {
|
||||
p.addServices(ProtoMaker.from(gatt.getDevice(), s, gatt));
|
||||
}
|
||||
servicesDiscoveredSink.success(p.build().toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
log(LogLevel.DEBUG, "[onCharacteristicRead] uuid: " + characteristic.getUuid().toString() + " status: " + status);
|
||||
if (characteristicReadSink != null) {
|
||||
Protos.ReadCharacteristicResponse.Builder p = Protos.ReadCharacteristicResponse.newBuilder();
|
||||
p.setRemoteId(gatt.getDevice().getAddress());
|
||||
p.setCharacteristic(ProtoMaker.from(characteristic, gatt));
|
||||
characteristicReadSink.success(p.build().toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
log(LogLevel.DEBUG, "[onCharacteristicWrite] uuid: " + characteristic.getUuid().toString() + " status: " + status);
|
||||
Protos.WriteCharacteristicRequest.Builder request = Protos.WriteCharacteristicRequest.newBuilder();
|
||||
request.setRemoteId(gatt.getDevice().getAddress());
|
||||
request.setCharacteristicUuid(characteristic.getUuid().toString());
|
||||
request.setServiceUuid(characteristic.getService().getUuid().toString());
|
||||
Protos.WriteCharacteristicResponse.Builder p = Protos.WriteCharacteristicResponse.newBuilder();
|
||||
p.setRequest(request);
|
||||
p.setSuccess(status == BluetoothGatt.GATT_SUCCESS);
|
||||
channel.invokeMethod("WriteCharacteristicResponse", p.build().toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
log(LogLevel.DEBUG, "[onCharacteristicChanged] uuid: " + characteristic.getUuid().toString());
|
||||
Protos.OnNotificationResponse.Builder p = Protos.OnNotificationResponse.newBuilder();
|
||||
p.setRemoteId(gatt.getDevice().getAddress());
|
||||
p.setCharacteristic(ProtoMaker.from(characteristic, gatt));
|
||||
channel.invokeMethod("OnValueChanged", p.build().toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
log(LogLevel.DEBUG, "[onDescriptorRead] uuid: " + descriptor.getUuid().toString() + " status: " + status);
|
||||
if (descriptorReadSink != null) {
|
||||
// Rebuild the ReadAttributeRequest and send back along with response
|
||||
Protos.ReadDescriptorRequest.Builder q = Protos.ReadDescriptorRequest.newBuilder();
|
||||
q.setRemoteId(gatt.getDevice().getAddress());
|
||||
q.setCharacteristicUuid(descriptor.getCharacteristic().getUuid().toString());
|
||||
q.setDescriptorUuid(descriptor.getUuid().toString());
|
||||
if (descriptor.getCharacteristic().getService().getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY) {
|
||||
q.setServiceUuid(descriptor.getCharacteristic().getService().getUuid().toString());
|
||||
} else {
|
||||
// Reverse search to find service
|
||||
for (BluetoothGattService s : gatt.getServices()) {
|
||||
for (BluetoothGattService ss : s.getIncludedServices()) {
|
||||
if (ss.getUuid().equals(descriptor.getCharacteristic().getService().getUuid())) {
|
||||
q.setServiceUuid(s.getUuid().toString());
|
||||
q.setSecondaryServiceUuid(ss.getUuid().toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Protos.ReadDescriptorResponse.Builder p = Protos.ReadDescriptorResponse.newBuilder();
|
||||
p.setRequest(q);
|
||||
p.setValue(ByteString.copyFrom(descriptor.getValue()));
|
||||
descriptorReadSink.success(p.build().toByteArray());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
log(LogLevel.DEBUG, "[onDescriptorWrite] uuid: " + descriptor.getUuid().toString() + " status: " + status);
|
||||
Protos.WriteDescriptorRequest.Builder request = Protos.WriteDescriptorRequest.newBuilder();
|
||||
request.setRemoteId(gatt.getDevice().getAddress());
|
||||
request.setDescriptorUuid(descriptor.getUuid().toString());
|
||||
request.setCharacteristicUuid(descriptor.getCharacteristic().getUuid().toString());
|
||||
request.setServiceUuid(descriptor.getCharacteristic().getService().getUuid().toString());
|
||||
Protos.WriteDescriptorResponse.Builder p = Protos.WriteDescriptorResponse.newBuilder();
|
||||
p.setRequest(request);
|
||||
p.setSuccess(status == BluetoothGatt.GATT_SUCCESS);
|
||||
channel.invokeMethod("WriteDescriptorResponse", p.build().toByteArray());
|
||||
|
||||
if (descriptor.getUuid().compareTo(CCCD_ID) == 0) {
|
||||
// SetNotificationResponse
|
||||
Protos.SetNotificationResponse.Builder q = Protos.SetNotificationResponse.newBuilder();
|
||||
q.setRemoteId(gatt.getDevice().getAddress());
|
||||
q.setCharacteristic(ProtoMaker.from(descriptor.getCharacteristic(), gatt));
|
||||
channel.invokeMethod("SetNotificationResponse", q.build().toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||
log(LogLevel.DEBUG, "[onReliableWriteCompleted] status: " + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
|
||||
log(LogLevel.DEBUG, "[onReadRemoteRssi] rssi: " + rssi + " status: " + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||
log(LogLevel.DEBUG, "[onMtuChanged] mtu: " + mtu + " status: " + status);
|
||||
}
|
||||
};
|
||||
|
||||
FlutterBlePlugin(Registrar r) {
|
||||
this.registrar = r;
|
||||
this.channel = new MethodChannel(registrar.messenger(), NAMESPACE + "/methods");
|
||||
this.stateChannel = new EventChannel(registrar.messenger(), NAMESPACE + "/state");
|
||||
this.scanResultChannel = new EventChannel(registrar.messenger(), NAMESPACE + "/scanResult");
|
||||
this.servicesDiscoveredChannel = new EventChannel(registrar.messenger(), NAMESPACE + "/servicesDiscovered");
|
||||
this.characteristicReadChannel = new EventChannel(registrar.messenger(), NAMESPACE + "/characteristicRead");
|
||||
this.descriptorReadChannel = new EventChannel(registrar.messenger(), NAMESPACE + "/descriptorRead");
|
||||
this.mBluetoothManager = (BluetoothManager) r.activity().getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
this.mBluetoothAdapter = mBluetoothManager.getAdapter();
|
||||
channel.setMethodCallHandler(this);
|
||||
stateChannel.setStreamHandler(stateHandler);
|
||||
scanResultChannel.setStreamHandler(scanResultsHandler);
|
||||
servicesDiscoveredChannel.setStreamHandler(servicesDiscoveredHandler);
|
||||
characteristicReadChannel.setStreamHandler(characteristicReadHandler);
|
||||
descriptorReadChannel.setStreamHandler(descriptorReadHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin registration.
|
||||
*/
|
||||
public static void registerWith(Registrar registrar) {
|
||||
final FlutterBlePlugin instance = new FlutterBlePlugin(registrar);
|
||||
registrar.addRequestPermissionsResultListener(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMethodCall(MethodCall call, Result result) {
|
||||
if (mBluetoothAdapter == null && !"isAvailable".equals(call.method)) {
|
||||
result.error("bluetooth_unavailable", "the device does not have bluetooth", null);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (call.method) {
|
||||
case "setLogLevel": {
|
||||
int logLevelIndex = (int) call.arguments;
|
||||
logLevel = LogLevel.values()[logLevelIndex];
|
||||
result.success(null);
|
||||
break;
|
||||
}
|
||||
|
||||
case "state": {
|
||||
Protos.BluetoothState.Builder p = Protos.BluetoothState.newBuilder();
|
||||
try {
|
||||
switch (mBluetoothAdapter.getState()) {
|
||||
case BluetoothAdapter.STATE_OFF:
|
||||
p.setState(Protos.BluetoothState.State.OFF);
|
||||
break;
|
||||
case BluetoothAdapter.STATE_ON:
|
||||
p.setState(Protos.BluetoothState.State.ON);
|
||||
break;
|
||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
||||
p.setState(Protos.BluetoothState.State.TURNING_OFF);
|
||||
break;
|
||||
case BluetoothAdapter.STATE_TURNING_ON:
|
||||
p.setState(Protos.BluetoothState.State.TURNING_ON);
|
||||
break;
|
||||
default:
|
||||
p.setState(Protos.BluetoothState.State.UNKNOWN);
|
||||
break;
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
p.setState(Protos.BluetoothState.State.UNAUTHORIZED);
|
||||
}
|
||||
result.success(p.build().toByteArray());
|
||||
break;
|
||||
}
|
||||
|
||||
case "isAvailable": {
|
||||
result.success(mBluetoothAdapter != null);
|
||||
break;
|
||||
}
|
||||
|
||||
case "isOn": {
|
||||
result.success(mBluetoothAdapter.isEnabled());
|
||||
break;
|
||||
}
|
||||
|
||||
case "startScan": {
|
||||
if (ContextCompat.checkSelfPermission(registrar.activity(), Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(
|
||||
registrar.activity(),
|
||||
new String[]{
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
},
|
||||
REQUEST_COARSE_LOCATION_PERMISSIONS);
|
||||
pendingCall = call;
|
||||
pendingResult = result;
|
||||
break;
|
||||
}
|
||||
startScan(call, result);
|
||||
break;
|
||||
}
|
||||
|
||||
case "stopScan": {
|
||||
stopScan();
|
||||
result.success(null);
|
||||
break;
|
||||
}
|
||||
|
||||
case "connect": {
|
||||
byte[] data = call.arguments();
|
||||
Protos.ConnectRequest options;
|
||||
try {
|
||||
options = Protos.ConnectRequest.newBuilder().mergeFrom(data).build();
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
result.error("RuntimeException", e.getMessage(), e);
|
||||
break;
|
||||
}
|
||||
String deviceId = options.getRemoteId();
|
||||
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(deviceId);
|
||||
boolean isConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT).contains(device);
|
||||
|
||||
// If device is already connected, return error
|
||||
if (mGattServers.containsKey(deviceId) && isConnected) {
|
||||
result.error("already_connected", "connection with device already exists", null);
|
||||
return;
|
||||
}
|
||||
|
||||
// If device was connected to previously but is now disconnected, attempt a reconnect
|
||||
if (mGattServers.containsKey(deviceId) && !isConnected) {
|
||||
if (!mGattServers.get(deviceId).connect()) {
|
||||
result.error("reconnect_error", "error when reconnecting to device", null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// New request, connect and add gattServer to Map
|
||||
BluetoothGatt gattServer = device.connectGatt(registrar.activity(), options.getAndroidAutoConnect(), mGattCallback);
|
||||
mGattServers.put(deviceId, gattServer);
|
||||
result.success(null);
|
||||
break;
|
||||
}
|
||||
|
||||
case "createBond": {
|
||||
byte[] data = call.arguments();
|
||||
Protos.ConnectRequest options;
|
||||
try {
|
||||
options = Protos.ConnectRequest.newBuilder().mergeFrom(data).build();
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
result.error("RuntimeException", e.getMessage(), e);
|
||||
break;
|
||||
}
|
||||
String deviceId = options.getRemoteId();
|
||||
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(deviceId);
|
||||
boolean isConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT).contains(device);
|
||||
if (mGattServers.containsKey(deviceId) && !isConnected) {
|
||||
if (device.createBond()) {
|
||||
result.success(null);
|
||||
} else {
|
||||
result.error(" bond error", "create bond error", null);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case "disconnect": {
|
||||
String deviceId = (String) call.arguments;
|
||||
BluetoothGatt gattServer = mGattServers.remove(deviceId);
|
||||
if (gattServer != null) {
|
||||
gattServer.close();
|
||||
}
|
||||
result.success(null);
|
||||
break;
|
||||
}
|
||||
|
||||
case "deviceState": {
|
||||
String deviceId = (String) call.arguments;
|
||||
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(deviceId);
|
||||
int state = mBluetoothManager.getConnectionState(device, BluetoothProfile.GATT);
|
||||
try {
|
||||
result.success(ProtoMaker.from(device, state).toByteArray());
|
||||
} catch (Exception e) {
|
||||
result.error("device_state_error", e.getMessage(), null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "discoverServices": {
|
||||
String deviceId = (String) call.arguments;
|
||||
BluetoothGatt gattServer = mGattServers.get(deviceId);
|
||||
if (gattServer == null) {
|
||||
result.error("discover_services_error", "no instance of BluetoothGatt, have you connected first?", null);
|
||||
return;
|
||||
}
|
||||
if (gattServer.discoverServices()) {
|
||||
result.success(null);
|
||||
} else {
|
||||
result.error("discover_services_error", "unknown reason", null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "services": {
|
||||
String deviceId = (String) call.arguments;
|
||||
BluetoothGatt gattServer = mGattServers.get(deviceId);
|
||||
if (gattServer == null) {
|
||||
result.error("get_services_error", "no instance of BluetoothGatt, have you connected first?", null);
|
||||
return;
|
||||
}
|
||||
if (gattServer.getServices().isEmpty()) {
|
||||
result.error("get_services_error", "services are empty, have you called discoverServices() yet?", null);
|
||||
return;
|
||||
}
|
||||
Protos.DiscoverServicesResult.Builder p = Protos.DiscoverServicesResult.newBuilder();
|
||||
p.setRemoteId(deviceId);
|
||||
for (BluetoothGattService s : gattServer.getServices()) {
|
||||
p.addServices(ProtoMaker.from(gattServer.getDevice(), s, gattServer));
|
||||
}
|
||||
result.success(p.build().toByteArray());
|
||||
break;
|
||||
}
|
||||
|
||||
case "readCharacteristic": {
|
||||
byte[] data = call.arguments();
|
||||
Protos.ReadCharacteristicRequest request;
|
||||
try {
|
||||
request = Protos.ReadCharacteristicRequest.newBuilder().mergeFrom(data).build();
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
result.error("RuntimeException", e.getMessage(), e);
|
||||
break;
|
||||
}
|
||||
|
||||
BluetoothGatt gattServer;
|
||||
BluetoothGattCharacteristic characteristic;
|
||||
try {
|
||||
gattServer = locateGatt(request.getRemoteId());
|
||||
characteristic = locateCharacteristic(gattServer, request.getServiceUuid(), request.getSecondaryServiceUuid(), request.getCharacteristicUuid());
|
||||
} catch (Exception e) {
|
||||
result.error("read_characteristic_error", e.getMessage(), null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gattServer.readCharacteristic(characteristic)) {
|
||||
result.success(null);
|
||||
} else {
|
||||
result.error("read_characteristic_error", "unknown reason, may occur if readCharacteristic was called before last read finished.", null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "readDescriptor": {
|
||||
byte[] data = call.arguments();
|
||||
Protos.ReadDescriptorRequest request;
|
||||
try {
|
||||
request = Protos.ReadDescriptorRequest.newBuilder().mergeFrom(data).build();
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
result.error("RuntimeException", e.getMessage(), e);
|
||||
break;
|
||||
}
|
||||
|
||||
BluetoothGatt gattServer;
|
||||
BluetoothGattCharacteristic characteristic;
|
||||
BluetoothGattDescriptor descriptor;
|
||||
try {
|
||||
gattServer = locateGatt(request.getRemoteId());
|
||||
characteristic = locateCharacteristic(gattServer, request.getServiceUuid(), request.getSecondaryServiceUuid(), request.getCharacteristicUuid());
|
||||
descriptor = locateDescriptor(characteristic, request.getDescriptorUuid());
|
||||
} catch (Exception e) {
|
||||
result.error("read_descriptor_error", e.getMessage(), null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gattServer.readDescriptor(descriptor)) {
|
||||
result.success(null);
|
||||
} else {
|
||||
result.error("read_descriptor_error", "unknown reason, may occur if readDescriptor was called before last read finished.", null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "writeCharacteristic": {
|
||||
byte[] data = call.arguments();
|
||||
Protos.WriteCharacteristicRequest request;
|
||||
try {
|
||||
request = Protos.WriteCharacteristicRequest.newBuilder().mergeFrom(data).build();
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
result.error("RuntimeException", e.getMessage(), e);
|
||||
break;
|
||||
}
|
||||
|
||||
BluetoothGatt gattServer;
|
||||
BluetoothGattCharacteristic characteristic;
|
||||
try {
|
||||
gattServer = locateGatt(request.getRemoteId());
|
||||
characteristic = locateCharacteristic(gattServer, request.getServiceUuid(), request.getSecondaryServiceUuid(), request.getCharacteristicUuid());
|
||||
} catch (Exception e) {
|
||||
result.error("write_characteristic_error", e.getMessage(), null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set characteristic to new value
|
||||
if (!characteristic.setValue(request.getValue().toByteArray())) {
|
||||
result.error("write_characteristic_error", "could not set the local value of characteristic", null);
|
||||
}
|
||||
|
||||
// Apply the correct write type
|
||||
if (request.getWriteType() == Protos.WriteCharacteristicRequest.WriteType.WITHOUT_RESPONSE) {
|
||||
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
|
||||
} else {
|
||||
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
|
||||
}
|
||||
|
||||
if (!gattServer.writeCharacteristic(characteristic)) {
|
||||
result.error("write_characteristic_error", "writeCharacteristic failed", null);
|
||||
return;
|
||||
}
|
||||
|
||||
result.success(null);
|
||||
break;
|
||||
}
|
||||
|
||||
case "writeDescriptor": {
|
||||
byte[] data = call.arguments();
|
||||
Protos.WriteDescriptorRequest request;
|
||||
try {
|
||||
request = Protos.WriteDescriptorRequest.newBuilder().mergeFrom(data).build();
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
result.error("RuntimeException", e.getMessage(), e);
|
||||
break;
|
||||
}
|
||||
|
||||
BluetoothGatt gattServer;
|
||||
BluetoothGattCharacteristic characteristic;
|
||||
BluetoothGattDescriptor descriptor;
|
||||
try {
|
||||
gattServer = locateGatt(request.getRemoteId());
|
||||
characteristic = locateCharacteristic(gattServer, request.getServiceUuid(), request.getSecondaryServiceUuid(), request.getCharacteristicUuid());
|
||||
descriptor = locateDescriptor(characteristic, request.getDescriptorUuid());
|
||||
} catch (Exception e) {
|
||||
result.error("write_descriptor_error", e.getMessage(), null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set descriptor to new value
|
||||
if (!descriptor.setValue(request.getValue().toByteArray())) {
|
||||
result.error("write_descriptor_error", "could not set the local value for descriptor", null);
|
||||
}
|
||||
|
||||
if (!gattServer.writeDescriptor(descriptor)) {
|
||||
result.error("write_descriptor_error", "writeCharacteristic failed", null);
|
||||
return;
|
||||
}
|
||||
|
||||
result.success(null);
|
||||
break;
|
||||
}
|
||||
|
||||
case "setNotification": {
|
||||
byte[] data = call.arguments();
|
||||
Protos.SetNotificationRequest request;
|
||||
try {
|
||||
request = Protos.SetNotificationRequest.newBuilder().mergeFrom(data).build();
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
result.error("RuntimeException", e.getMessage(), e);
|
||||
break;
|
||||
}
|
||||
|
||||
BluetoothGatt gattServer;
|
||||
BluetoothGattCharacteristic characteristic;
|
||||
BluetoothGattDescriptor cccDescriptor;
|
||||
try {
|
||||
gattServer = locateGatt(request.getRemoteId());
|
||||
characteristic = locateCharacteristic(gattServer, request.getServiceUuid(), request.getSecondaryServiceUuid(), request.getCharacteristicUuid());
|
||||
cccDescriptor = characteristic.getDescriptor(CCCD_ID);
|
||||
if (cccDescriptor == null) {
|
||||
throw new Exception("could not locate CCCD descriptor for characteristic: " + characteristic.getUuid().toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
result.error("set_notification_error", e.getMessage(), null);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] value = null;
|
||||
|
||||
if (request.getEnable()) {
|
||||
boolean canNotify = (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0;
|
||||
boolean canIndicate = (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0;
|
||||
if (!canIndicate && !canNotify) {
|
||||
result.error("set_notification_error", "the characteristic cannot notify or indicate", null);
|
||||
return;
|
||||
}
|
||||
if (canIndicate) {
|
||||
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
|
||||
}
|
||||
if (canNotify) {
|
||||
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
|
||||
}
|
||||
} else {
|
||||
value = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
|
||||
}
|
||||
|
||||
if (!gattServer.setCharacteristicNotification(characteristic, request.getEnable())) {
|
||||
result.error("set_notification_error", "could not set characteristic notifications to :" + request.getEnable(), null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cccDescriptor.setValue(value)) {
|
||||
result.error("set_notification_error", "error when setting the descriptor value to: " + value, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gattServer.writeDescriptor(cccDescriptor)) {
|
||||
result.error("set_notification_error", "error when writing the descriptor", null);
|
||||
return;
|
||||
}
|
||||
|
||||
result.success(null);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
result.notImplemented();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRequestPermissionsResult(
|
||||
int requestCode, String[] permissions, int[] grantResults) {
|
||||
if (requestCode == REQUEST_COARSE_LOCATION_PERMISSIONS) {
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
startScan(pendingCall, pendingResult);
|
||||
} else {
|
||||
pendingResult.error(
|
||||
"no_permissions", "flutter_blue plugin requires location permissions for scanning", null);
|
||||
pendingResult = null;
|
||||
pendingCall = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private BluetoothGatt locateGatt(String remoteId) throws Exception {
|
||||
BluetoothGatt gattServer = mGattServers.get(remoteId);
|
||||
if (gattServer == null) {
|
||||
throw new Exception("no instance of BluetoothGatt, have you connected first?");
|
||||
}
|
||||
return gattServer;
|
||||
}
|
||||
|
||||
private BluetoothGattCharacteristic locateCharacteristic(BluetoothGatt gattServer, String serviceId, String secondaryServiceId, String characteristicId) throws Exception {
|
||||
BluetoothGattService primaryService = gattServer.getService(UUID.fromString(serviceId));
|
||||
if (primaryService == null) {
|
||||
throw new Exception("service (" + serviceId + ") could not be located on the device");
|
||||
}
|
||||
BluetoothGattService secondaryService = null;
|
||||
if (secondaryServiceId.length() > 0) {
|
||||
for (BluetoothGattService s : primaryService.getIncludedServices()) {
|
||||
if (s.getUuid().equals(UUID.fromString(secondaryServiceId))) {
|
||||
secondaryService = s;
|
||||
}
|
||||
}
|
||||
if (secondaryService == null) {
|
||||
throw new Exception("secondary service (" + secondaryServiceId + ") could not be located on the device");
|
||||
}
|
||||
}
|
||||
BluetoothGattService service = (secondaryService != null) ? secondaryService : primaryService;
|
||||
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characteristicId));
|
||||
if (characteristic == null) {
|
||||
throw new Exception("characteristic (" + characteristicId + ") could not be located in the service (" + service.getUuid().toString() + ")");
|
||||
}
|
||||
return characteristic;
|
||||
}
|
||||
|
||||
private BluetoothGattDescriptor locateDescriptor(BluetoothGattCharacteristic characteristic, String descriptorId) throws Exception {
|
||||
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(descriptorId));
|
||||
if (descriptor == null) {
|
||||
throw new Exception("descriptor (" + descriptorId + ") could not be located in the characteristic (" + characteristic.getUuid().toString() + ")");
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private void startScan(MethodCall call, Result result) {
|
||||
byte[] data = call.arguments();
|
||||
Protos.ScanSettings settings;
|
||||
try {
|
||||
settings = Protos.ScanSettings.newBuilder().mergeFrom(data).build();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
startScan21(settings);
|
||||
} else {
|
||||
startScan18(settings);
|
||||
}
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
result.error("startScan", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopScan() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
stopScan21();
|
||||
} else {
|
||||
stopScan18();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private ScanCallback getScanCallback21() {
|
||||
if (scanCallback21 == null) {
|
||||
scanCallback21 = new ScanCallback() {
|
||||
|
||||
@Override
|
||||
public void onScanResult(int callbackType, ScanResult result) {
|
||||
super.onScanResult(callbackType, result);
|
||||
if (scanResultsSink != null) {
|
||||
Protos.ScanResult scanResult = ProtoMaker.from(result.getDevice(), result);
|
||||
scanResultsSink.success(scanResult.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatchScanResults(List<ScanResult> results) {
|
||||
super.onBatchScanResults(results);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScanFailed(int errorCode) {
|
||||
super.onScanFailed(errorCode);
|
||||
}
|
||||
};
|
||||
}
|
||||
return scanCallback21;
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private void startScan21(Protos.ScanSettings proto) throws IllegalStateException {
|
||||
BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
|
||||
if (scanner == null)
|
||||
throw new IllegalStateException("getBluetoothLeScanner() is null. Is the Adapter on?");
|
||||
int scanMode = proto.getAndroidScanMode();
|
||||
int count = proto.getServiceUuidsCount();
|
||||
List<ScanFilter> filters = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
String uuid = proto.getServiceUuids(i);
|
||||
ScanFilter f = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(uuid)).build();
|
||||
filters.add(f);
|
||||
}
|
||||
ScanSettings settings = new ScanSettings.Builder().setScanMode(scanMode).build();
|
||||
scanner.startScan(filters, settings, getScanCallback21());
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private void stopScan21() {
|
||||
BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
|
||||
if (scanner != null) scanner.stopScan(getScanCallback21());
|
||||
}
|
||||
|
||||
private BluetoothAdapter.LeScanCallback getScanCallback18() {
|
||||
if (scanCallback18 == null) {
|
||||
scanCallback18 = new BluetoothAdapter.LeScanCallback() {
|
||||
@Override
|
||||
public void onLeScan(final BluetoothDevice bluetoothDevice, int rssi,
|
||||
byte[] scanRecord) {
|
||||
if (scanResultsSink != null) {
|
||||
Protos.ScanResult scanResult = ProtoMaker.from(bluetoothDevice, scanRecord, rssi);
|
||||
scanResultsSink.success(scanResult.toByteArray());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return scanCallback18;
|
||||
}
|
||||
|
||||
private void startScan18(Protos.ScanSettings proto) throws IllegalStateException {
|
||||
List<String> serviceUuids = proto.getServiceUuidsList();
|
||||
UUID[] uuids = new UUID[serviceUuids.size()];
|
||||
for (int i = 0; i < serviceUuids.size(); i++) {
|
||||
uuids[i] = UUID.fromString(serviceUuids.get(i));
|
||||
}
|
||||
boolean success = mBluetoothAdapter.startLeScan(uuids, getScanCallback18());
|
||||
if (!success)
|
||||
throw new IllegalStateException("getBluetoothLeScanner() is null. Is the Adapter on?");
|
||||
}
|
||||
|
||||
private void stopScan18() {
|
||||
mBluetoothAdapter.stopLeScan(getScanCallback18());
|
||||
}
|
||||
|
||||
private void log(LogLevel level, String message) {
|
||||
if (level.ordinal() <= logLevel.ordinal()) {
|
||||
Log.d(TAG, message);
|
||||
}
|
||||
}
|
||||
|
||||
enum LogLevel {
|
||||
EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package ai.longev.flutter.flutter_ble;
|
||||
|
||||
// Copyright 2017, Paul DeMarco.
|
||||
// All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.bluetooth.le.ScanRecord;
|
||||
import android.bluetooth.le.ScanResult;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelUuid;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by paul on 8/31/17.
|
||||
*/
|
||||
|
||||
public class ProtoMaker {
|
||||
|
||||
private static final UUID CCCD_UUID = UUID.fromString("000002902-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
static Protos.ScanResult from(BluetoothDevice device, byte[] advertisementData, int rssi) {
|
||||
Protos.ScanResult.Builder p = Protos.ScanResult.newBuilder();
|
||||
p.setDevice(from(device));
|
||||
if (advertisementData != null && advertisementData.length > 0)
|
||||
p.setAdvertisementData(AdvertisementParser.parse(advertisementData));
|
||||
p.setRssi(rssi);
|
||||
return p.build();
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
static Protos.ScanResult from(BluetoothDevice device, ScanResult scanResult) {
|
||||
Protos.ScanResult.Builder p = Protos.ScanResult.newBuilder();
|
||||
p.setDevice(from(device));
|
||||
Protos.AdvertisementData.Builder a = Protos.AdvertisementData.newBuilder();
|
||||
ScanRecord scanRecord = scanResult.getScanRecord();
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
a.setConnectable(scanResult.isConnectable());
|
||||
} else {
|
||||
if (scanRecord != null) {
|
||||
int flags = scanRecord.getAdvertiseFlags();
|
||||
a.setConnectable((flags & 0x2) > 0);
|
||||
}
|
||||
}
|
||||
if (scanRecord != null) {
|
||||
String deviceName = scanRecord.getDeviceName();
|
||||
if (deviceName != null) {
|
||||
a.setLocalName(deviceName);
|
||||
}
|
||||
int txPower = scanRecord.getTxPowerLevel();
|
||||
if (txPower != Integer.MIN_VALUE) {
|
||||
a.setTxPowerLevel(Protos.Int32Value.newBuilder().setValue(txPower));
|
||||
}
|
||||
// Manufacturer Specific Data
|
||||
SparseArray<byte[]> msd = scanRecord.getManufacturerSpecificData();
|
||||
for (int i = 0; i < msd.size(); i++) {
|
||||
int key = msd.keyAt(i);
|
||||
byte[] value = msd.valueAt(i);
|
||||
a.putManufacturerData(key, ByteString.copyFrom(value));
|
||||
}
|
||||
// Service Data
|
||||
Map<ParcelUuid, byte[]> serviceData = scanRecord.getServiceData();
|
||||
for (Map.Entry<ParcelUuid, byte[]> entry : serviceData.entrySet()) {
|
||||
ParcelUuid key = entry.getKey();
|
||||
byte[] value = entry.getValue();
|
||||
a.putServiceData(key.getUuid().toString(), ByteString.copyFrom(value));
|
||||
}
|
||||
// Service UUIDs
|
||||
List<ParcelUuid> serviceUuids = scanRecord.getServiceUuids();
|
||||
if (serviceUuids != null) {
|
||||
for (ParcelUuid s : serviceUuids) {
|
||||
a.addServiceUuids(s.getUuid().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
p.setRssi(scanResult.getRssi());
|
||||
p.setAdvertisementData(a.build());
|
||||
return p.build();
|
||||
}
|
||||
|
||||
static Protos.BluetoothDevice from(BluetoothDevice device) {
|
||||
Protos.BluetoothDevice.Builder p = Protos.BluetoothDevice.newBuilder();
|
||||
p.setRemoteId(device.getAddress());
|
||||
String name = device.getName();
|
||||
if (name != null) {
|
||||
p.setName(name);
|
||||
}
|
||||
switch (device.getType()) {
|
||||
case BluetoothDevice.DEVICE_TYPE_LE:
|
||||
p.setType(Protos.BluetoothDevice.Type.LE);
|
||||
break;
|
||||
case BluetoothDevice.DEVICE_TYPE_CLASSIC:
|
||||
p.setType(Protos.BluetoothDevice.Type.CLASSIC);
|
||||
break;
|
||||
case BluetoothDevice.DEVICE_TYPE_DUAL:
|
||||
p.setType(Protos.BluetoothDevice.Type.DUAL);
|
||||
break;
|
||||
default:
|
||||
p.setType(Protos.BluetoothDevice.Type.UNKNOWN);
|
||||
break;
|
||||
}
|
||||
return p.build();
|
||||
}
|
||||
|
||||
static Protos.BluetoothService from(BluetoothDevice device, BluetoothGattService service, BluetoothGatt gatt) {
|
||||
Protos.BluetoothService.Builder p = Protos.BluetoothService.newBuilder();
|
||||
p.setRemoteId(device.getAddress());
|
||||
p.setUuid(service.getUuid().toString());
|
||||
p.setIsPrimary(service.getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY);
|
||||
for (BluetoothGattCharacteristic c : service.getCharacteristics()) {
|
||||
p.addCharacteristics(from(c, gatt));
|
||||
}
|
||||
for (BluetoothGattService s : service.getIncludedServices()) {
|
||||
p.addIncludedServices(from(device, s, gatt));
|
||||
}
|
||||
return p.build();
|
||||
}
|
||||
|
||||
static Protos.BluetoothCharacteristic from(BluetoothGattCharacteristic characteristic, BluetoothGatt gatt) {
|
||||
Protos.BluetoothCharacteristic.Builder p = Protos.BluetoothCharacteristic.newBuilder();
|
||||
p.setUuid(characteristic.getUuid().toString());
|
||||
p.setProperties(from(characteristic.getProperties()));
|
||||
if (characteristic.getValue() != null)
|
||||
p.setValue(ByteString.copyFrom(characteristic.getValue()));
|
||||
for (BluetoothGattDescriptor d : characteristic.getDescriptors()) {
|
||||
p.addDescriptors(from(d));
|
||||
}
|
||||
if (characteristic.getService().getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY) {
|
||||
p.setServiceUuid(characteristic.getService().getUuid().toString());
|
||||
} else {
|
||||
// Reverse search to find service
|
||||
for (BluetoothGattService s : gatt.getServices()) {
|
||||
for (BluetoothGattService ss : s.getIncludedServices()) {
|
||||
if (ss.getUuid().equals(characteristic.getService().getUuid())) {
|
||||
p.setServiceUuid(s.getUuid().toString());
|
||||
p.setSecondaryServiceUuid(ss.getUuid().toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return p.build();
|
||||
}
|
||||
|
||||
static Protos.BluetoothDescriptor from(BluetoothGattDescriptor descriptor) {
|
||||
Protos.BluetoothDescriptor.Builder p = Protos.BluetoothDescriptor.newBuilder();
|
||||
p.setUuid(descriptor.getUuid().toString());
|
||||
p.setCharacteristicUuid(descriptor.getCharacteristic().getUuid().toString());
|
||||
p.setServiceUuid(descriptor.getCharacteristic().getService().getUuid().toString());
|
||||
if (descriptor.getValue() != null)
|
||||
p.setValue(ByteString.copyFrom(descriptor.getValue()));
|
||||
return p.build();
|
||||
}
|
||||
|
||||
static Protos.CharacteristicProperties from(int properties) {
|
||||
return Protos.CharacteristicProperties.newBuilder()
|
||||
.setBroadcast((properties & 1) != 0)
|
||||
.setRead((properties & 2) != 0)
|
||||
.setWriteWithoutResponse((properties & 4) != 0)
|
||||
.setWrite((properties & 8) != 0)
|
||||
.setNotify((properties & 16) != 0)
|
||||
.setIndicate((properties & 32) != 0)
|
||||
.setAuthenticatedSignedWrites((properties & 64) != 0)
|
||||
.setExtendedProperties((properties & 128) != 0)
|
||||
.setNotifyEncryptionRequired((properties & 256) != 0)
|
||||
.setIndicateEncryptionRequired((properties & 512) != 0)
|
||||
.build();
|
||||
}
|
||||
|
||||
static Protos.DeviceStateResponse from(BluetoothDevice device, int state) {
|
||||
Protos.DeviceStateResponse.Builder p = Protos.DeviceStateResponse.newBuilder();
|
||||
switch (state) {
|
||||
case BluetoothProfile.STATE_DISCONNECTING:
|
||||
p.setState(Protos.DeviceStateResponse.BluetoothDeviceState.DISCONNECTING);
|
||||
break;
|
||||
case BluetoothProfile.STATE_CONNECTED:
|
||||
p.setState(Protos.DeviceStateResponse.BluetoothDeviceState.CONNECTED);
|
||||
break;
|
||||
case BluetoothProfile.STATE_CONNECTING:
|
||||
p.setState(Protos.DeviceStateResponse.BluetoothDeviceState.CONNECTING);
|
||||
break;
|
||||
case BluetoothProfile.STATE_DISCONNECTED:
|
||||
p.setState(Protos.DeviceStateResponse.BluetoothDeviceState.DISCONNECTED);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
p.setRemoteId(device.getAddress());
|
||||
return p.build();
|
||||
}
|
||||
}
|
||||
70
Esp32_temperature_humidity_ble_FLUTTER/flutter_ble_app/flutter_ble-master/example/.gitignore
vendored
Executable file
@@ -0,0 +1,70 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# Visual Studio Code related
|
||||
.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
**/android/.gradle
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!**/ios/**/default.mode1v3
|
||||
!**/ios/**/default.mode2v3
|
||||
!**/ios/**/default.pbxuser
|
||||
!**/ios/**/default.perspectivev3
|
||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
@@ -0,0 +1,16 @@
|
||||
# flutter_ble_example
|
||||
|
||||
Demonstrates how to use the flutter_ble plugin.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
@@ -0,0 +1,61 @@
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "ai.longev.flutter.flutter_ble_example"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 28
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="ai.longev.flutter.flutter_ble_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,37 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="ai.longev.flutter.flutter_ble_example">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
In most cases you can leave this as-is, but you if you want to provide
|
||||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:label="flutter_ble_example"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
tools:ignore="GoogleAppIndexingWarning"
|
||||
android:allowBackup="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- This keeps the window background of the activity showing
|
||||
until Flutter renders its first frame. It can be removed if
|
||||
there is no splash screen (such as the default splash screen
|
||||
defined in @style/LaunchTheme). -->
|
||||
<meta-data
|
||||
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
|
||||
android:value="true" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,13 @@
|
||||
package ai.longev.flutter.flutter_ble_example;
|
||||
|
||||
import android.os.Bundle;
|
||||
import io.flutter.app.FlutterActivity;
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||
|
||||
public class MainActivity extends FlutterActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
GeneratedPluginRegistrant.registerWith(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
|
After Width: | Height: | Size: 544 B |
|
After Width: | Height: | Size: 442 B |
|
After Width: | Height: | Size: 721 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="ai.longev.flutter.flutter_ble_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,29 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.4.1'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = '../build'
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
}
|
||||
subprojects {
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
@@ -0,0 +1,6 @@
|
||||
#Fri May 31 15:39:48 CDT 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
|
||||
@@ -0,0 +1,15 @@
|
||||
include ':app'
|
||||
|
||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
||||
|
||||
def plugins = new Properties()
|
||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
||||
if (pluginsFile.exists()) {
|
||||
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
||||
}
|
||||
|
||||
plugins.each { name, path ->
|
||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
||||
include ":$name"
|
||||
project(":$name").projectDir = pluginDirectory
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>App</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.flutter.flutter.app</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>App</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
@@ -0,0 +1,69 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '9.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
project 'Runner', {
|
||||
'Debug' => :debug,
|
||||
'Profile' => :release,
|
||||
'Release' => :release,
|
||||
}
|
||||
|
||||
def parse_KV_file(file, separator='=')
|
||||
file_abs_path = File.expand_path(file)
|
||||
if !File.exists? file_abs_path
|
||||
return [];
|
||||
end
|
||||
pods_ary = []
|
||||
skip_line_start_symbols = ["#", "/"]
|
||||
File.foreach(file_abs_path) { |line|
|
||||
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
|
||||
plugin = line.split(pattern=separator)
|
||||
if plugin.length == 2
|
||||
podname = plugin[0].strip()
|
||||
path = plugin[1].strip()
|
||||
podpath = File.expand_path("#{path}", file_abs_path)
|
||||
pods_ary.push({:name => podname, :path => podpath});
|
||||
else
|
||||
puts "Invalid plugin specification: #{line}"
|
||||
end
|
||||
}
|
||||
return pods_ary
|
||||
end
|
||||
|
||||
target 'Runner' do
|
||||
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
|
||||
# referring to absolute paths on developers' machines.
|
||||
system('rm -rf .symlinks')
|
||||
system('mkdir -p .symlinks/plugins')
|
||||
|
||||
# Flutter Pods
|
||||
generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
|
||||
if generated_xcode_build_settings.empty?
|
||||
puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
|
||||
end
|
||||
generated_xcode_build_settings.map { |p|
|
||||
if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
|
||||
symlink = File.join('.symlinks', 'flutter')
|
||||
File.symlink(File.dirname(p[:path]), symlink)
|
||||
pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
|
||||
end
|
||||
}
|
||||
|
||||
# Plugin Pods
|
||||
plugin_pods = parse_KV_file('../.flutter-plugins')
|
||||
plugin_pods.map { |p|
|
||||
symlink = File.join('.symlinks', 'plugins', p[:name])
|
||||
File.symlink(p[:path], symlink)
|
||||
pod p[:name], :path => File.join(symlink, 'ios')
|
||||
}
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['ENABLE_BITCODE'] = 'NO'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
PODS:
|
||||
- "!ProtoCompiler (3.7.0)":
|
||||
- Protobuf (~> 3.0)
|
||||
- Flutter (1.0.0)
|
||||
- flutter_ble (0.5.1):
|
||||
- "!ProtoCompiler"
|
||||
- Flutter
|
||||
- flutter_ble/Protos (= 0.5.1)
|
||||
- flutter_ble/Protos (0.5.1):
|
||||
- "!ProtoCompiler"
|
||||
- Flutter
|
||||
- Protobuf
|
||||
- Protobuf (3.8.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Flutter (from `.symlinks/flutter/ios`)
|
||||
- flutter_ble (from `.symlinks/plugins/flutter_ble/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- "!ProtoCompiler"
|
||||
- Protobuf
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Flutter:
|
||||
:path: ".symlinks/flutter/ios"
|
||||
flutter_ble:
|
||||
:path: ".symlinks/plugins/flutter_ble/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
"!ProtoCompiler": efc8ba4dceb16b48f6b366a08500871c8d3ff8d2
|
||||
Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a
|
||||
flutter_ble: c8480760bed31cb8337311c3faedfa9fcae5f9c1
|
||||
Protobuf: 3f617b9a6e73605565086864c9bc26b2bf2dd5a3
|
||||
|
||||
PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09
|
||||
|
||||
COCOAPODS: 1.7.0
|
||||
@@ -0,0 +1,577 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
|
||||
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
|
||||
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
|
||||
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
|
||||
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
A2A537F352EE5164B05911AB /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CD001701BDC4728022DB42C /* libPods-Runner.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
|
||||
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
|
||||
6CD001701BDC4728022DB42C /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
7DD8CFB7F0364D55F5354FE1 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
|
||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
CF6E4140832AD784A39F6B08 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
DE00DC1DC429064AF0B2580C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
|
||||
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
|
||||
A2A537F352EE5164B05911AB /* libPods-Runner.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
3A903686E102B85427835225 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6CD001701BDC4728022DB42C /* libPods-Runner.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B80C3931E831B6300D905FE /* App.framework */,
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||
9740EEBA1CF902C7004384FC /* Flutter.framework */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||
);
|
||||
name = Flutter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
D6EA32F9A89D2F62F8ABD1E7 /* Pods */,
|
||||
3A903686E102B85427835225 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146EF1CF9000F007C117D /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146F01CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
|
||||
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
97C147021CF9000F007C117D /* Info.plist */,
|
||||
97C146F11CF9000F007C117D /* Supporting Files */,
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146F11CF9000F007C117D /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146F21CF9000F007C117D /* main.m */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D6EA32F9A89D2F62F8ABD1E7 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DE00DC1DC429064AF0B2580C /* Pods-Runner.debug.xcconfig */,
|
||||
7DD8CFB7F0364D55F5354FE1 /* Pods-Runner.release.xcconfig */,
|
||||
CF6E4140832AD784A39F6B08 /* Pods-Runner.profile.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
F4B005EC65297E62A1063D35 /* [CP] Check Pods Manifest.lock */,
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
743D5EE8D0F9D613E1A63883 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0910;
|
||||
ORGANIZATIONNAME = "The Chromium Authors";
|
||||
TargetAttributes = {
|
||||
97C146ED1CF9000F007C117D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
DevelopmentTeam = 3D56TJUQRV;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
97C146ED1CF9000F007C117D /* Runner */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Thin Binary";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
|
||||
};
|
||||
743D5EE8D0F9D613E1A63883 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
|
||||
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
F4B005EC65297E62A1063D35 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
|
||||
97C146F31CF9000F007C117D /* main.m in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C146FB1CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C147001CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 3D56TJUQRV;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = ai.longev.flutter.flutterBleExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
97C147031CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147041CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
97C147061CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 3D56TJUQRV;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = ai.longev.flutter.flutterBleExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147071CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 3D56TJUQRV;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = ai.longev.flutter.flutterBleExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147031CF9000F007C117D /* Debug */,
|
||||
97C147041CF9000F007C117D /* Release */,
|
||||
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147061CF9000F007C117D /* Debug */,
|
||||
97C147071CF9000F007C117D /* Release */,
|
||||
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0910"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildSystemType</key>
|
||||
<string>Original</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : FlutterAppDelegate
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "AppDelegate.h"
|
||||
#include "GeneratedPluginRegistrant.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
[GeneratedPluginRegistrant registerWithRegistry:self];
|
||||
// Override point for customization after application launch.
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "Icon-App-1024x1024@1x.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 564 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 68 B |
|
After Width: | Height: | Size: 68 B |
|
After Width: | Height: | Size: 68 B |
@@ -0,0 +1,5 @@
|
||||
# Launch Screen Assets
|
||||
|
||||
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="LaunchImage" width="168" height="185"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Flutter View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>flutter_ble_example</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,9 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
// Copyright 2017, Paul DeMarco.
|
||||
// All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_ble/flutter_ble.dart';
|
||||
import 'package:flutter_ble_example/widgets.dart';
|
||||
|
||||
void main() {
|
||||
runApp(new FlutterBleApp());
|
||||
}
|
||||
|
||||
class FlutterBleApp extends StatefulWidget {
|
||||
FlutterBleApp({Key key, this.title}) : super(key: key);
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
_FlutterBleAppState createState() => new _FlutterBleAppState();
|
||||
}
|
||||
|
||||
class _FlutterBleAppState extends State<FlutterBleApp> {
|
||||
FlutterBle _flutterBlue = FlutterBle.instance;
|
||||
|
||||
/// Scanning
|
||||
StreamSubscription _scanSubscription;
|
||||
Map<DeviceIdentifier, ScanResult> scanResults = new Map();
|
||||
bool isScanning = false;
|
||||
|
||||
/// State
|
||||
StreamSubscription _stateSubscription;
|
||||
BluetoothState state = BluetoothState.unknown;
|
||||
|
||||
/// Device
|
||||
BluetoothDevice device;
|
||||
bool get isConnected => (device != null);
|
||||
StreamSubscription deviceConnection;
|
||||
StreamSubscription deviceStateSubscription;
|
||||
List<BluetoothService> services = new List();
|
||||
Map<Guid, StreamSubscription> valueChangedSubscriptions = {};
|
||||
BluetoothDeviceState deviceState = BluetoothDeviceState.disconnected;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Immediately get the state of FlutterBle
|
||||
_flutterBlue.state.then((s) {
|
||||
setState(() {
|
||||
state = s;
|
||||
});
|
||||
});
|
||||
// Subscribe to state changes
|
||||
_stateSubscription = _flutterBlue.onStateChanged().listen((s) {
|
||||
setState(() {
|
||||
state = s;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_stateSubscription?.cancel();
|
||||
_stateSubscription = null;
|
||||
_scanSubscription?.cancel();
|
||||
_scanSubscription = null;
|
||||
deviceConnection?.cancel();
|
||||
deviceConnection = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_startScan() {
|
||||
_scanSubscription = _flutterBlue
|
||||
.scan(
|
||||
timeout: const Duration(seconds: 5),
|
||||
/*withServices: [
|
||||
new Guid('0000180F-0000-1000-8000-00805F9B34FB')
|
||||
]*/
|
||||
)
|
||||
.listen((scanResult) {
|
||||
print('localName: ${scanResult.advertisementData.localName}');
|
||||
print(
|
||||
'manufacturerData: ${scanResult.advertisementData.manufacturerData}');
|
||||
print('serviceData: ${scanResult.advertisementData.serviceData}');
|
||||
setState(() {
|
||||
scanResults[scanResult.device.id] = scanResult;
|
||||
});
|
||||
}, onDone: _stopScan);
|
||||
|
||||
setState(() {
|
||||
isScanning = true;
|
||||
});
|
||||
}
|
||||
|
||||
_stopScan() {
|
||||
_scanSubscription?.cancel();
|
||||
_scanSubscription = null;
|
||||
setState(() {
|
||||
isScanning = false;
|
||||
});
|
||||
}
|
||||
|
||||
_connect(BluetoothDevice d) async {
|
||||
device = d;
|
||||
// Connect to device
|
||||
deviceConnection = _flutterBlue
|
||||
.connect(device, timeout: const Duration(seconds: 4))
|
||||
.listen(
|
||||
null,
|
||||
onDone: _disconnect,
|
||||
);
|
||||
|
||||
// Update the connection state immediately
|
||||
device.state.then((s) {
|
||||
setState(() {
|
||||
deviceState = s;
|
||||
});
|
||||
});
|
||||
|
||||
// Subscribe to connection changes
|
||||
deviceStateSubscription = device.onStateChanged().listen((s) {
|
||||
setState(() {
|
||||
deviceState = s;
|
||||
});
|
||||
if (s == BluetoothDeviceState.connected) {
|
||||
device.discoverServices().then((s) {
|
||||
setState(() {
|
||||
services = s;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_disconnect() {
|
||||
// Remove all value changed listeners
|
||||
valueChangedSubscriptions.forEach((uuid, sub) => sub.cancel());
|
||||
valueChangedSubscriptions.clear();
|
||||
deviceStateSubscription?.cancel();
|
||||
deviceStateSubscription = null;
|
||||
deviceConnection?.cancel();
|
||||
deviceConnection = null;
|
||||
setState(() {
|
||||
device = null;
|
||||
});
|
||||
}
|
||||
|
||||
_readCharacteristic(BluetoothCharacteristic c) async {
|
||||
await device.readCharacteristic(c);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
_writeCharacteristic(BluetoothCharacteristic c) async {
|
||||
await device.writeCharacteristic(c, [0x12, 0x34],
|
||||
type: CharacteristicWriteType.withResponse);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
_readDescriptor(BluetoothDescriptor d) async {
|
||||
await device.readDescriptor(d);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
_writeDescriptor(BluetoothDescriptor d) async {
|
||||
await device.writeDescriptor(d, [0x12, 0x34]);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
_setNotification(BluetoothCharacteristic c) async {
|
||||
if (c.isNotifying) {
|
||||
await device.setNotifyValue(c, false);
|
||||
// Cancel subscription
|
||||
valueChangedSubscriptions[c.uuid]?.cancel();
|
||||
valueChangedSubscriptions.remove(c.uuid);
|
||||
} else {
|
||||
await device.setNotifyValue(c, true);
|
||||
// ignore: cancel_subscriptions
|
||||
final sub = device.onValueChanged(c).listen((d) {
|
||||
setState(() {
|
||||
print('onValueChanged $d');
|
||||
});
|
||||
});
|
||||
// Add to map
|
||||
valueChangedSubscriptions[c.uuid] = sub;
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
_refreshDeviceState(BluetoothDevice d) async {
|
||||
var state = await d.state;
|
||||
setState(() {
|
||||
deviceState = state;
|
||||
print('State refreshed: $deviceState');
|
||||
});
|
||||
}
|
||||
|
||||
_buildScanningButton() {
|
||||
if (isConnected || state != BluetoothState.on) {
|
||||
return null;
|
||||
}
|
||||
if (isScanning) {
|
||||
return new FloatingActionButton(
|
||||
child: new Icon(Icons.stop),
|
||||
onPressed: _stopScan,
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
} else {
|
||||
return new FloatingActionButton(
|
||||
child: new Icon(Icons.search), onPressed: _startScan);
|
||||
}
|
||||
}
|
||||
|
||||
_buildScanResultTiles() {
|
||||
return scanResults.values
|
||||
.map((r) => ScanResultTile(
|
||||
result: r,
|
||||
onTap: () => _connect(r.device),
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<Widget> _buildServiceTiles() {
|
||||
return services
|
||||
.map(
|
||||
(s) => new ServiceTile(
|
||||
service: s,
|
||||
characteristicTiles: s.characteristics
|
||||
.map(
|
||||
(c) => new CharacteristicTile(
|
||||
characteristic: c,
|
||||
onReadPressed: () => _readCharacteristic(c),
|
||||
onWritePressed: () => _writeCharacteristic(c),
|
||||
onNotificationPressed: () => _setNotification(c),
|
||||
descriptorTiles: c.descriptors
|
||||
.map(
|
||||
(d) => new DescriptorTile(
|
||||
descriptor: d,
|
||||
onReadPressed: () => _readDescriptor(d),
|
||||
onWritePressed: () =>
|
||||
_writeDescriptor(d),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
_buildActionButtons() {
|
||||
if (isConnected) {
|
||||
return <Widget>[
|
||||
new IconButton(
|
||||
icon: const Icon(Icons.cancel),
|
||||
onPressed: () => _disconnect(),
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
_buildAlertTile() {
|
||||
return new Container(
|
||||
color: Colors.redAccent,
|
||||
child: new ListTile(
|
||||
title: new Text(
|
||||
'Bluetooth adapter is ${state.toString().substring(15)}',
|
||||
style: Theme.of(context).primaryTextTheme.subhead,
|
||||
),
|
||||
trailing: new Icon(
|
||||
Icons.error,
|
||||
color: Theme.of(context).primaryTextTheme.subhead.color,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_buildDeviceStateTile() {
|
||||
return new ListTile(
|
||||
leading: (deviceState == BluetoothDeviceState.connected)
|
||||
? const Icon(Icons.bluetooth_connected)
|
||||
: const Icon(Icons.bluetooth_disabled),
|
||||
title: new Text('Device is ${deviceState.toString().split('.')[1]}.'),
|
||||
subtitle: new Text('${device.id}'),
|
||||
trailing: new IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () => _refreshDeviceState(device),
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
|
||||
));
|
||||
}
|
||||
|
||||
_buildProgressBarTile() {
|
||||
return new LinearProgressIndicator();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var tiles = new List<Widget>();
|
||||
if (state != BluetoothState.on) {
|
||||
tiles.add(_buildAlertTile());
|
||||
}
|
||||
if (isConnected) {
|
||||
tiles.add(_buildDeviceStateTile());
|
||||
tiles.addAll(_buildServiceTiles());
|
||||
} else {
|
||||
tiles.addAll(_buildScanResultTiles());
|
||||
}
|
||||
return new MaterialApp(
|
||||
home: new Scaffold(
|
||||
appBar: new AppBar(
|
||||
title: const Text('Flutter Longev BLE'),
|
||||
actions: _buildActionButtons(),
|
||||
),
|
||||
floatingActionButton: _buildScanningButton(),
|
||||
body: new Stack(
|
||||
children: <Widget>[
|
||||
(isScanning) ? _buildProgressBarTile() : new Container(),
|
||||
new ListView(
|
||||
children: tiles,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
// Copyright 2017, Paul DeMarco.
|
||||
// All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_ble/flutter_ble.dart';
|
||||
|
||||
class ScanResultTile extends StatelessWidget {
|
||||
const ScanResultTile({Key key, this.result, this.onTap}) : super(key: key);
|
||||
|
||||
final ScanResult result;
|
||||
final VoidCallback onTap;
|
||||
|
||||
Widget _buildTitle(BuildContext context) {
|
||||
if (result.device.name.length > 0) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(result.device.name),
|
||||
Text(
|
||||
result.device.id.toString(),
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
)
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Text(result.device.id.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildAdvRow(BuildContext context, String title, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(title, style: Theme.of(context).textTheme.caption),
|
||||
SizedBox(
|
||||
width: 12.0,
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.caption
|
||||
.apply(color: Colors.black),
|
||||
softWrap: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String getNiceHexArray(List<int> bytes) {
|
||||
return '[${bytes.map((i) => i.toRadixString(16).padLeft(2, '0')).join(', ')}]'
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
String getNiceManufacturerData(Map<int, List<int>> data) {
|
||||
if (data.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
List<String> res = [];
|
||||
data.forEach((id, bytes) {
|
||||
res.add(
|
||||
'${id.toRadixString(16).toUpperCase()}: ${getNiceHexArray(bytes)}');
|
||||
});
|
||||
return res.join(', ');
|
||||
}
|
||||
|
||||
String getNiceServiceData(Map<String, List<int>> data) {
|
||||
if (data.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
List<String> res = [];
|
||||
data.forEach((id, bytes) {
|
||||
res.add('${id.toUpperCase()}: ${getNiceHexArray(bytes)}');
|
||||
});
|
||||
return res.join(', ');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ExpansionTile(
|
||||
title: _buildTitle(context),
|
||||
leading: Text(result.rssi.toString()),
|
||||
trailing: RaisedButton(
|
||||
child: Text('CONNECT'),
|
||||
color: Colors.black,
|
||||
textColor: Colors.white,
|
||||
onPressed: (result.advertisementData.connectable) ? onTap : null,
|
||||
),
|
||||
children: <Widget>[
|
||||
_buildAdvRow(
|
||||
context, 'Complete Local Name', result.advertisementData.localName),
|
||||
_buildAdvRow(context, 'Tx Power Level',
|
||||
'${result.advertisementData.txPowerLevel ?? 'N/A'}'),
|
||||
_buildAdvRow(
|
||||
context,
|
||||
'Manufacturer Data',
|
||||
getNiceManufacturerData(
|
||||
result.advertisementData.manufacturerData) ??
|
||||
'N/A'),
|
||||
_buildAdvRow(
|
||||
context,
|
||||
'Service UUIDs',
|
||||
(result.advertisementData.serviceUuids.isNotEmpty)
|
||||
? result.advertisementData.serviceUuids.join(', ').toUpperCase()
|
||||
: 'N/A'),
|
||||
_buildAdvRow(context, 'Service Data',
|
||||
getNiceServiceData(result.advertisementData.serviceData) ?? 'N/A'),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceTile extends StatelessWidget {
|
||||
final BluetoothService service;
|
||||
final List<CharacteristicTile> characteristicTiles;
|
||||
|
||||
const ServiceTile({Key key, this.service, this.characteristicTiles})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (characteristicTiles.length > 0) {
|
||||
return new ExpansionTile(
|
||||
title: new Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Text('Service'),
|
||||
new Text(
|
||||
'0x${service.uuid.toString().toUpperCase().substring(4, 8)}',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.body1
|
||||
.copyWith(color: Theme.of(context).textTheme.caption.color))
|
||||
],
|
||||
),
|
||||
children: characteristicTiles,
|
||||
);
|
||||
} else {
|
||||
return new ListTile(
|
||||
title: const Text('Service'),
|
||||
subtitle: new Text(
|
||||
'0x${service.uuid.toString().toUpperCase().substring(4, 8)}'),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CharacteristicTile extends StatelessWidget {
|
||||
final BluetoothCharacteristic characteristic;
|
||||
final List<DescriptorTile> descriptorTiles;
|
||||
final VoidCallback onReadPressed;
|
||||
final VoidCallback onWritePressed;
|
||||
final VoidCallback onNotificationPressed;
|
||||
|
||||
const CharacteristicTile(
|
||||
{Key key,
|
||||
this.characteristic,
|
||||
this.descriptorTiles,
|
||||
this.onReadPressed,
|
||||
this.onWritePressed,
|
||||
this.onNotificationPressed})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var actions = new Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
new IconButton(
|
||||
icon: new Icon(
|
||||
Icons.file_download,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
|
||||
),
|
||||
onPressed: onReadPressed,
|
||||
),
|
||||
new IconButton(
|
||||
icon: new Icon(Icons.file_upload,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.5)),
|
||||
onPressed: onWritePressed,
|
||||
),
|
||||
new IconButton(
|
||||
icon: new Icon(
|
||||
characteristic.isNotifying ? Icons.sync_disabled : Icons.sync,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.5)),
|
||||
onPressed: onNotificationPressed,
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
var title = new Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Text('Characteristic'),
|
||||
new Text(
|
||||
'0x${characteristic.uuid.toString().toUpperCase().substring(4, 8)}',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.body1
|
||||
.copyWith(color: Theme.of(context).textTheme.caption.color))
|
||||
],
|
||||
);
|
||||
|
||||
if (descriptorTiles.length > 0) {
|
||||
return new ExpansionTile(
|
||||
title: new ListTile(
|
||||
title: title,
|
||||
subtitle: new Text(characteristic.value.toString()),
|
||||
contentPadding: EdgeInsets.all(0.0),
|
||||
),
|
||||
trailing: actions,
|
||||
children: descriptorTiles,
|
||||
);
|
||||
} else {
|
||||
return new ListTile(
|
||||
title: title,
|
||||
subtitle: new Text(characteristic.value.toString()),
|
||||
trailing: actions,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DescriptorTile extends StatelessWidget {
|
||||
final BluetoothDescriptor descriptor;
|
||||
final VoidCallback onReadPressed;
|
||||
final VoidCallback onWritePressed;
|
||||
|
||||
const DescriptorTile(
|
||||
{Key key, this.descriptor, this.onReadPressed, this.onWritePressed})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var title = new Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Text('Descriptor'),
|
||||
new Text(
|
||||
'0x${descriptor.uuid.toString().toUpperCase().substring(4, 8)}',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.body1
|
||||
.copyWith(color: Theme.of(context).textTheme.caption.color))
|
||||
],
|
||||
);
|
||||
return new ListTile(
|
||||
title: title,
|
||||
subtitle: new Text(descriptor.value.toString()),
|
||||
trailing: new Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
new IconButton(
|
||||
icon: new Icon(
|
||||
Icons.file_download,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
|
||||
),
|
||||
onPressed: onReadPressed,
|
||||
),
|
||||
new IconButton(
|
||||
icon: new Icon(
|
||||
Icons.file_upload,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
|
||||
),
|
||||
onPressed: onWritePressed,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
# Generated by pub
|
||||
# See https://www.dartlang.org/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.14.11"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.10.9"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_ble:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.5.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.5"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.6"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.2"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
protobuf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protobuf
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.13.11"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.5"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.9.3"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.4"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.6"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
sdks:
|
||||
dart: ">=2.2.0 <3.0.0"
|
||||
@@ -0,0 +1,63 @@
|
||||
name: flutter_ble_example
|
||||
description: Demonstrates how to use the flutter_ble plugin.
|
||||
publish_to: 'none'
|
||||
|
||||
environment:
|
||||
sdk: ">=2.1.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^0.1.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
flutter_ble:
|
||||
path: ../
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://www.dartlang.org/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
@@ -0,0 +1,27 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:flutter_ble_example/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Verify Platform version', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(FlutterBleApp());
|
||||
|
||||
// Verify that platform version is retrieved.
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
(Widget widget) => widget is Text &&
|
||||
widget.data.startsWith('Running on:'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
}
|
||||
36
Esp32_temperature_humidity_ble_FLUTTER/flutter_ble_app/flutter_ble-master/ios/.gitignore
vendored
Executable file
@@ -0,0 +1,36 @@
|
||||
.idea/
|
||||
.vagrant/
|
||||
.sconsign.dblite
|
||||
.svn/
|
||||
|
||||
.DS_Store
|
||||
*.swp
|
||||
profile
|
||||
|
||||
DerivedData/
|
||||
build/
|
||||
GeneratedPluginRegistrant.h
|
||||
GeneratedPluginRegistrant.m
|
||||
|
||||
.generated/
|
||||
|
||||
*.pbxuser
|
||||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.perspectivev3
|
||||
|
||||
!default.pbxuser
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
!default.perspectivev3
|
||||
|
||||
xcuserdata
|
||||
|
||||
*.moved-aside
|
||||
|
||||
*.pyc
|
||||
*sync/
|
||||
Icon?
|
||||
.tags*
|
||||
|
||||
/Flutter/Generated.xcconfig
|
||||
@@ -0,0 +1,11 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
|
||||
#define NAMESPACE @"ai.longev.flutter/flutter_ble"
|
||||
|
||||
@interface FlutterBlePlugin : NSObject<FlutterPlugin, CBCentralManagerDelegate, CBPeripheralDelegate>
|
||||
@end
|
||||
|
||||
@interface FlutterBleStreamHandler : NSObject<FlutterStreamHandler>
|
||||
@property FlutterEventSink sink;
|
||||
@end
|
||||
@@ -0,0 +1,752 @@
|
||||
// Copyright 2017, Paul DeMarco.
|
||||
// All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#import "FlutterBlePlugin.h"
|
||||
#import "Flutterblue.pbobjc.h"
|
||||
|
||||
@interface CBUUID (CBUUIDAdditionsFlutterBle)
|
||||
- (NSString *)fullUUIDString;
|
||||
@end
|
||||
|
||||
@implementation CBUUID (CBUUIDAdditionsFlutterBle)
|
||||
- (NSString *)fullUUIDString {
|
||||
if(self.UUIDString.length == 4) {
|
||||
return [[NSString stringWithFormat:@"0000%@-0000-1000-8000-00805F9B34FB", self.UUIDString] lowercaseString];
|
||||
}
|
||||
return [self.UUIDString lowercaseString];
|
||||
}
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(NSUInteger, LogLevel) {
|
||||
emergency = 0,
|
||||
alert = 1,
|
||||
critical = 2,
|
||||
error = 3,
|
||||
warning = 4,
|
||||
notice = 5,
|
||||
info = 6,
|
||||
debug = 7
|
||||
};
|
||||
|
||||
@interface FlutterBlePlugin ()
|
||||
@property(nonatomic, retain) NSObject<FlutterPluginRegistrar> *registrar;
|
||||
@property(nonatomic, retain) FlutterMethodChannel *channel;
|
||||
@property(nonatomic, retain) FlutterBleStreamHandler *stateStreamHandler;
|
||||
@property(nonatomic, retain) FlutterBleStreamHandler *scanResultStreamHandler;
|
||||
@property(nonatomic, retain) FlutterBleStreamHandler *servicesDiscoveredStreamHandler;
|
||||
@property(nonatomic, retain) FlutterBleStreamHandler *characteristicReadStreamHandler;
|
||||
@property(nonatomic, retain) FlutterBleStreamHandler *descriptorReadStreamHandler;
|
||||
@property(nonatomic, retain) CBCentralManager *centralManager;
|
||||
@property(nonatomic) NSMutableDictionary *scannedPeripherals;
|
||||
@property(nonatomic) NSMutableArray *servicesThatNeedDiscovered;
|
||||
@property(nonatomic) NSMutableArray *characteristicsThatNeedDiscovered;
|
||||
@property(nonatomic) LogLevel logLevel;
|
||||
@end
|
||||
|
||||
@implementation FlutterBlePlugin
|
||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
||||
FlutterMethodChannel* channel = [FlutterMethodChannel
|
||||
methodChannelWithName:NAMESPACE @"/methods"
|
||||
binaryMessenger:[registrar messenger]];
|
||||
FlutterEventChannel* stateChannel = [FlutterEventChannel eventChannelWithName:NAMESPACE @"/state" binaryMessenger:[registrar messenger]];
|
||||
FlutterEventChannel* scanResultChannel = [FlutterEventChannel eventChannelWithName:NAMESPACE @"/scanResult" binaryMessenger:[registrar messenger]];
|
||||
FlutterEventChannel* servicesDiscoveredChannel = [FlutterEventChannel eventChannelWithName:NAMESPACE @"/servicesDiscovered" binaryMessenger:[registrar messenger]];
|
||||
FlutterEventChannel* characteristicReadChannel = [FlutterEventChannel eventChannelWithName:NAMESPACE @"/characteristicRead" binaryMessenger:[registrar messenger]];
|
||||
FlutterEventChannel* descriptorReadChannel = [FlutterEventChannel eventChannelWithName:NAMESPACE @"/descriptorRead" binaryMessenger:[registrar messenger]];
|
||||
FlutterBlePlugin* instance = [[FlutterBlePlugin alloc] init];
|
||||
instance.channel = channel;
|
||||
instance.centralManager = [[CBCentralManager alloc] initWithDelegate:instance queue:nil];
|
||||
instance.scannedPeripherals = [NSMutableDictionary new];
|
||||
instance.servicesThatNeedDiscovered = [NSMutableArray new];
|
||||
instance.characteristicsThatNeedDiscovered = [NSMutableArray new];
|
||||
instance.logLevel = emergency;
|
||||
|
||||
// STATE
|
||||
FlutterBleStreamHandler* stateStreamHandler = [[FlutterBleStreamHandler alloc] init];
|
||||
[stateChannel setStreamHandler:stateStreamHandler];
|
||||
instance.stateStreamHandler = stateStreamHandler;
|
||||
|
||||
// SCAN RESULTS
|
||||
FlutterBleStreamHandler* scanResultStreamHandler = [[FlutterBleStreamHandler alloc] init];
|
||||
[scanResultChannel setStreamHandler:scanResultStreamHandler];
|
||||
instance.scanResultStreamHandler = scanResultStreamHandler;
|
||||
|
||||
// SERVICES DISCOVERED
|
||||
FlutterBleStreamHandler* servicesDiscoveredStreamHandler = [[FlutterBleStreamHandler alloc] init];
|
||||
[servicesDiscoveredChannel setStreamHandler:servicesDiscoveredStreamHandler];
|
||||
instance.servicesDiscoveredStreamHandler = servicesDiscoveredStreamHandler;
|
||||
|
||||
// CHARACTERISTIC READ
|
||||
FlutterBleStreamHandler* characteristicReadStreamHandler = [[FlutterBleStreamHandler alloc] init];
|
||||
[characteristicReadChannel setStreamHandler:characteristicReadStreamHandler];
|
||||
instance.characteristicReadStreamHandler = characteristicReadStreamHandler;
|
||||
|
||||
// DESCRIPTOR READ
|
||||
FlutterBleStreamHandler* descriptorReadStreamHandler = [[FlutterBleStreamHandler alloc] init];
|
||||
[descriptorReadChannel setStreamHandler:descriptorReadStreamHandler];
|
||||
instance.descriptorReadStreamHandler = descriptorReadStreamHandler;
|
||||
|
||||
[registrar addMethodCallDelegate:instance channel:channel];
|
||||
}
|
||||
|
||||
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
|
||||
if ([@"setLogLevel" isEqualToString:call.method]) {
|
||||
NSNumber *logLevelIndex = [call arguments];
|
||||
_logLevel = (LogLevel)[logLevelIndex integerValue];
|
||||
result(nil);
|
||||
} else if ([@"state" isEqualToString:call.method]) {
|
||||
FlutterStandardTypedData *data = [self toFlutterData:[self toBluetoothStateProto:self->_centralManager.state]];
|
||||
result(data);
|
||||
} else if([@"isAvailable" isEqualToString:call.method]) {
|
||||
if(self.centralManager.state != CBManagerStateUnsupported && self.centralManager.state != CBManagerStateUnknown) {
|
||||
result(@(YES));
|
||||
} else {
|
||||
result(@(NO));
|
||||
}
|
||||
} else if([@"isOn" isEqualToString:call.method]) {
|
||||
if(self.centralManager.state == CBManagerStatePoweredOn) {
|
||||
result(@(YES));
|
||||
} else {
|
||||
result(@(NO));
|
||||
}
|
||||
} else if([@"startScan" isEqualToString:call.method]) {
|
||||
// Clear any existing scan results
|
||||
[self.scannedPeripherals removeAllObjects];
|
||||
// TODO: Request Permission?
|
||||
FlutterStandardTypedData *data = [call arguments];
|
||||
ProtosScanSettings *request = [[ProtosScanSettings alloc] initWithData:[data data] error:nil];
|
||||
// UUID Service filter
|
||||
NSArray *uuids = [NSArray array];
|
||||
for (int i = 0; i < [request serviceUuidsArray_Count]; i++) {
|
||||
NSString *u = [request.serviceUuidsArray objectAtIndex:i];
|
||||
uuids = [uuids arrayByAddingObject:[CBUUID UUIDWithString:u]];
|
||||
}
|
||||
// TODO: iOS Scan Options (#35)
|
||||
[self->_centralManager scanForPeripheralsWithServices:uuids options:nil];
|
||||
result(nil);
|
||||
} else if([@"stopScan" isEqualToString:call.method]) {
|
||||
[self->_centralManager stopScan];
|
||||
result(nil);
|
||||
} else if([@"connect" isEqualToString:call.method]) {
|
||||
FlutterStandardTypedData *data = [call arguments];
|
||||
ProtosConnectRequest *request = [[ProtosConnectRequest alloc] initWithData:[data data] error:nil];
|
||||
NSString *remoteId = [request remoteId];
|
||||
@try {
|
||||
CBPeripheral *peripheral = [_scannedPeripherals objectForKey:remoteId];
|
||||
if(peripheral == nil) {
|
||||
@throw [FlutterError errorWithCode:@"connect"
|
||||
message:@"Peripheral not found"
|
||||
details:nil];
|
||||
}
|
||||
// TODO: Implement Connect options (#36)
|
||||
[_centralManager connectPeripheral:peripheral options:nil];
|
||||
result(nil);
|
||||
} @catch(FlutterError *e) {
|
||||
result(e);
|
||||
}
|
||||
} else if([@"disconnect" isEqualToString:call.method]) {
|
||||
NSString *remoteId = [call arguments];
|
||||
@try {
|
||||
CBPeripheral *peripheral = [self findPeripheral:remoteId];
|
||||
[_centralManager cancelPeripheralConnection:peripheral];
|
||||
result(nil);
|
||||
} @catch(FlutterError *e) {
|
||||
result(e);
|
||||
}
|
||||
} else if([@"deviceState" isEqualToString:call.method]) {
|
||||
NSString *remoteId = [call arguments];
|
||||
@try {
|
||||
CBPeripheral *peripheral = [self findPeripheral:remoteId];
|
||||
result([self toFlutterData:[self toDeviceStateProto:peripheral state:peripheral.state]]);
|
||||
} @catch(FlutterError *e) {
|
||||
result(e);
|
||||
}
|
||||
} else if([@"discoverServices" isEqualToString:call.method]) {
|
||||
NSString *remoteId = [call arguments];
|
||||
@try {
|
||||
CBPeripheral *peripheral = [self findPeripheral:remoteId];
|
||||
// Clear helper arrays
|
||||
[_servicesThatNeedDiscovered removeAllObjects];
|
||||
[_characteristicsThatNeedDiscovered removeAllObjects ];
|
||||
[peripheral discoverServices:nil];
|
||||
result(nil);
|
||||
} @catch(FlutterError *e) {
|
||||
result(e);
|
||||
}
|
||||
} else if([@"services" isEqualToString:call.method]) {
|
||||
NSString *remoteId = [call arguments];
|
||||
@try {
|
||||
CBPeripheral *peripheral = [self findPeripheral:remoteId];
|
||||
result([self toFlutterData:[self toServicesResultProto:peripheral]]);
|
||||
} @catch(FlutterError *e) {
|
||||
result(e);
|
||||
}
|
||||
} else if([@"readCharacteristic" isEqualToString:call.method]) {
|
||||
FlutterStandardTypedData *data = [call arguments];
|
||||
ProtosReadCharacteristicRequest *request = [[ProtosReadCharacteristicRequest alloc] initWithData:[data data] error:nil];
|
||||
NSString *remoteId = [request remoteId];
|
||||
@try {
|
||||
// Find peripheral
|
||||
CBPeripheral *peripheral = [self findPeripheral:remoteId];
|
||||
// Find characteristic
|
||||
CBCharacteristic *characteristic = [self locateCharacteristic:[request characteristicUuid] peripheral:peripheral serviceId:[request serviceUuid] secondaryServiceId:[request secondaryServiceUuid]];
|
||||
// Trigger a read
|
||||
[peripheral readValueForCharacteristic:characteristic];
|
||||
result(nil);
|
||||
} @catch(FlutterError *e) {
|
||||
result(e);
|
||||
}
|
||||
} else if([@"readDescriptor" isEqualToString:call.method]) {
|
||||
FlutterStandardTypedData *data = [call arguments];
|
||||
ProtosReadDescriptorRequest *request = [[ProtosReadDescriptorRequest alloc] initWithData:[data data] error:nil];
|
||||
NSString *remoteId = [request remoteId];
|
||||
@try {
|
||||
// Find peripheral
|
||||
CBPeripheral *peripheral = [self findPeripheral:remoteId];
|
||||
// Find characteristic
|
||||
CBCharacteristic *characteristic = [self locateCharacteristic:[request characteristicUuid] peripheral:peripheral serviceId:[request serviceUuid] secondaryServiceId:[request secondaryServiceUuid]];
|
||||
// Find descriptor
|
||||
CBDescriptor *descriptor = [self locateDescriptor:[request descriptorUuid] characteristic:characteristic];
|
||||
[peripheral readValueForDescriptor:descriptor];
|
||||
result(nil);
|
||||
} @catch(FlutterError *e) {
|
||||
result(e);
|
||||
}
|
||||
} else if([@"writeCharacteristic" isEqualToString:call.method]) {
|
||||
FlutterStandardTypedData *data = [call arguments];
|
||||
ProtosWriteCharacteristicRequest *request = [[ProtosWriteCharacteristicRequest alloc] initWithData:[data data] error:nil];
|
||||
NSString *remoteId = [request remoteId];
|
||||
@try {
|
||||
// Find peripheral
|
||||
CBPeripheral *peripheral = [self findPeripheral:remoteId];
|
||||
// Find characteristic
|
||||
CBCharacteristic *characteristic = [self locateCharacteristic:[request characteristicUuid] peripheral:peripheral serviceId:[request serviceUuid] secondaryServiceId:[request secondaryServiceUuid]];
|
||||
// Get correct write type
|
||||
CBCharacteristicWriteType type = ([request writeType] == ProtosWriteCharacteristicRequest_WriteType_WithoutResponse) ? CBCharacteristicWriteWithoutResponse : CBCharacteristicWriteWithResponse;
|
||||
// Write to characteristic
|
||||
[peripheral writeValue:[request value] forCharacteristic:characteristic type:type];
|
||||
result(nil);
|
||||
} @catch(FlutterError *e) {
|
||||
result(e);
|
||||
}
|
||||
} else if([@"writeDescriptor" isEqualToString:call.method]) {
|
||||
FlutterStandardTypedData *data = [call arguments];
|
||||
ProtosWriteDescriptorRequest *request = [[ProtosWriteDescriptorRequest alloc] initWithData:[data data] error:nil];
|
||||
NSString *remoteId = [request remoteId];
|
||||
@try {
|
||||
// Find peripheral
|
||||
CBPeripheral *peripheral = [self findPeripheral:remoteId];
|
||||
// Find characteristic
|
||||
CBCharacteristic *characteristic = [self locateCharacteristic:[request characteristicUuid] peripheral:peripheral serviceId:[request serviceUuid] secondaryServiceId:[request secondaryServiceUuid]];
|
||||
// Find descriptor
|
||||
CBDescriptor *descriptor = [self locateDescriptor:[request descriptorUuid] characteristic:characteristic];
|
||||
// Write descriptor
|
||||
[peripheral writeValue:[request value] forDescriptor:descriptor];
|
||||
result(nil);
|
||||
} @catch(FlutterError *e) {
|
||||
result(e);
|
||||
}
|
||||
} else if([@"setNotification" isEqualToString:call.method]) {
|
||||
FlutterStandardTypedData *data = [call arguments];
|
||||
ProtosSetNotificationRequest *request = [[ProtosSetNotificationRequest alloc] initWithData:[data data] error:nil];
|
||||
NSString *remoteId = [request remoteId];
|
||||
@try {
|
||||
// Find peripheral
|
||||
CBPeripheral *peripheral = [self findPeripheral:remoteId];
|
||||
// Find characteristic
|
||||
CBCharacteristic *characteristic = [self locateCharacteristic:[request characteristicUuid] peripheral:peripheral serviceId:[request serviceUuid] secondaryServiceId:[request secondaryServiceUuid]];
|
||||
// Set notification value
|
||||
[peripheral setNotifyValue:[request enable] forCharacteristic:characteristic];
|
||||
result(nil);
|
||||
} @catch(FlutterError *e) {
|
||||
result(e);
|
||||
}
|
||||
} else {
|
||||
result(FlutterMethodNotImplemented);
|
||||
}
|
||||
}
|
||||
|
||||
- (CBPeripheral*)findPeripheral:(NSString*)remoteId {
|
||||
NSArray<CBPeripheral*> *peripherals = [_centralManager retrievePeripheralsWithIdentifiers:@[[[NSUUID alloc] initWithUUIDString:remoteId]]];
|
||||
CBPeripheral *peripheral;
|
||||
for(CBPeripheral *p in peripherals) {
|
||||
if([[p.identifier UUIDString] isEqualToString:remoteId]) {
|
||||
peripheral = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(peripheral == nil) {
|
||||
@throw [FlutterError errorWithCode:@"findPeripheral"
|
||||
message:@"Peripheral not found"
|
||||
details:nil];
|
||||
}
|
||||
return peripheral;
|
||||
}
|
||||
|
||||
- (CBCharacteristic*)locateCharacteristic:(NSString*)characteristicId peripheral:(CBPeripheral*)peripheral serviceId:(NSString*)serviceId secondaryServiceId:(NSString*)secondaryServiceId {
|
||||
CBService *primaryService = [self getServiceFromArray:serviceId array:[peripheral services]];
|
||||
if(primaryService == nil || [primaryService isPrimary] == false) {
|
||||
@throw [FlutterError errorWithCode:@"locateCharacteristic"
|
||||
message:@"service could not be located on the device"
|
||||
details:nil];
|
||||
}
|
||||
CBService *secondaryService;
|
||||
if(secondaryServiceId.length) {
|
||||
secondaryService = [self getServiceFromArray:secondaryServiceId array:[primaryService includedServices]];
|
||||
@throw [FlutterError errorWithCode:@"locateCharacteristic"
|
||||
message:@"secondary service could not be located on the device"
|
||||
details:secondaryServiceId];
|
||||
}
|
||||
CBService *service = (secondaryService != nil) ? secondaryService : primaryService;
|
||||
CBCharacteristic *characteristic = [self getCharacteristicFromArray:characteristicId array:[service characteristics]];
|
||||
if(characteristic == nil) {
|
||||
@throw [FlutterError errorWithCode:@"locateCharacteristic"
|
||||
message:@"characteristic could not be located on the device"
|
||||
details:nil];
|
||||
}
|
||||
return characteristic;
|
||||
}
|
||||
|
||||
- (CBDescriptor*)locateDescriptor:(NSString*)descriptorId characteristic:(CBCharacteristic*)characteristic {
|
||||
CBDescriptor *descriptor = [self getDescriptorFromArray:descriptorId array:[characteristic descriptors]];
|
||||
if(descriptor == nil) {
|
||||
@throw [FlutterError errorWithCode:@"locateDescriptor"
|
||||
message:@"descriptor could not be located on the device"
|
||||
details:nil];
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
// Reverse search to find primary service
|
||||
- (CBService*)findPrimaryService:(CBService*)secondaryService peripheral:(CBPeripheral*)peripheral {
|
||||
for(CBService *s in [peripheral services]) {
|
||||
for(CBService *ss in [s includedServices]) {
|
||||
if([[ss.UUID UUIDString] isEqualToString:[secondaryService.UUID UUIDString]]) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CBDescriptor*)findCCCDescriptor:(CBCharacteristic*)characteristic {
|
||||
for(CBDescriptor *d in characteristic.descriptors) {
|
||||
if([d.UUID.UUIDString isEqualToString:@"2902"]) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CBService*)getServiceFromArray:(NSString*)uuidString array:(NSArray<CBService*>*)array {
|
||||
for(CBService *s in array) {
|
||||
if([[s UUID] isEqual:[CBUUID UUIDWithString:uuidString]]) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CBCharacteristic*)getCharacteristicFromArray:(NSString*)uuidString array:(NSArray<CBCharacteristic*>*)array {
|
||||
for(CBCharacteristic *c in array) {
|
||||
if([[c UUID] isEqual:[CBUUID UUIDWithString:uuidString]]) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CBDescriptor*)getDescriptorFromArray:(NSString*)uuidString array:(NSArray<CBDescriptor*>*)array {
|
||||
for(CBDescriptor *d in array) {
|
||||
if([[d UUID] isEqual:[CBUUID UUIDWithString:uuidString]]) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// CBCentralManagerDelegate methods
|
||||
//
|
||||
- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
|
||||
if(_stateStreamHandler.sink != nil) {
|
||||
FlutterStandardTypedData *data = [self toFlutterData:[self toBluetoothStateProto:self->_centralManager.state]];
|
||||
self.stateStreamHandler.sink(data);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
|
||||
[self.scannedPeripherals setObject:peripheral
|
||||
forKey:[[peripheral identifier] UUIDString]];
|
||||
if(_scanResultStreamHandler.sink != nil) {
|
||||
FlutterStandardTypedData *data = [self toFlutterData:[self toScanResultProto:peripheral advertisementData:advertisementData RSSI:RSSI]];
|
||||
_scanResultStreamHandler.sink(data);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
|
||||
NSLog(@"didConnectPeripheral");
|
||||
// Register self as delegate for peripheral
|
||||
peripheral.delegate = self;
|
||||
|
||||
// Send connection state
|
||||
[_channel invokeMethod:@"DeviceState" arguments:[self toFlutterData:[self toDeviceStateProto:peripheral state:peripheral.state]]];
|
||||
}
|
||||
|
||||
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
|
||||
NSLog(@"didDisconnectPeripheral");
|
||||
// Unregister self as delegate for peripheral, not working #42
|
||||
peripheral.delegate = nil;
|
||||
|
||||
// Send connection state
|
||||
[_channel invokeMethod:@"DeviceState" arguments:[self toFlutterData:[self toDeviceStateProto:peripheral state:peripheral.state]]];
|
||||
}
|
||||
|
||||
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
|
||||
// TODO:?
|
||||
}
|
||||
|
||||
//
|
||||
// CBPeripheralDelegate methods
|
||||
//
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
|
||||
NSLog(@"didDiscoverServices");
|
||||
// Loop through and discover characteristics and secondary services
|
||||
[_servicesThatNeedDiscovered addObjectsFromArray:peripheral.services];
|
||||
for(CBService *s in [peripheral services]) {
|
||||
NSLog(@"Found service: %@", [s.UUID UUIDString]);
|
||||
[peripheral discoverCharacteristics:nil forService:s];
|
||||
// [peripheral discoverIncludedServices:nil forService:s]; // Secondary services in the future (#8)
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
|
||||
NSLog(@"didDiscoverCharacteristicsForService");
|
||||
// Loop through and discover descriptors for characteristics
|
||||
[_servicesThatNeedDiscovered removeObject:service];
|
||||
[_characteristicsThatNeedDiscovered addObjectsFromArray:service.characteristics];
|
||||
for(CBCharacteristic *c in [service characteristics]) {
|
||||
[peripheral discoverDescriptorsForCharacteristic:c];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
|
||||
NSLog(@"didDiscoverDescriptorsForCharacteristic");
|
||||
[_characteristicsThatNeedDiscovered removeObject:characteristic];
|
||||
if(_servicesThatNeedDiscovered.count > 0 || _characteristicsThatNeedDiscovered.count > 0) {
|
||||
// Still discovering
|
||||
return;
|
||||
}
|
||||
// Send updated tree
|
||||
if(_servicesDiscoveredStreamHandler.sink != nil) {
|
||||
ProtosDiscoverServicesResult *result = [self toServicesResultProto:peripheral];
|
||||
_servicesDiscoveredStreamHandler.sink([self toFlutterData:result]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverIncludedServicesForService:(CBService *)service error:(NSError *)error {
|
||||
NSLog(@"didDiscoverIncludedServicesForService");
|
||||
// Loop through and discover characteristics for secondary services
|
||||
for(CBService *ss in [service includedServices]) {
|
||||
[peripheral discoverCharacteristics:nil forService:ss];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
|
||||
NSLog(@"didUpdateValueForCharacteristic %@", [peripheral.identifier UUIDString]);
|
||||
if(_characteristicReadStreamHandler.sink != nil) {
|
||||
ProtosReadCharacteristicResponse *result = [[ProtosReadCharacteristicResponse alloc] init];
|
||||
[result setRemoteId:[peripheral.identifier UUIDString]];
|
||||
[result setCharacteristic:[self toCharacteristicProto:characteristic]];
|
||||
_characteristicReadStreamHandler.sink([self toFlutterData:result]);
|
||||
}
|
||||
// on iOS, this method also handle notification values
|
||||
ProtosOnNotificationResponse *result = [[ProtosOnNotificationResponse alloc] init];
|
||||
[result setRemoteId:[peripheral.identifier UUIDString]];
|
||||
[result setCharacteristic:[self toCharacteristicProto:characteristic]];
|
||||
[_channel invokeMethod:@"OnValueChanged" arguments:[self toFlutterData:result]];
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
|
||||
NSLog(@"didWriteValueForCharacteristic");
|
||||
ProtosWriteCharacteristicRequest *request = [[ProtosWriteCharacteristicRequest alloc] init];
|
||||
[request setRemoteId:[peripheral.identifier UUIDString]];
|
||||
[request setCharacteristicUuid:[characteristic.UUID fullUUIDString]];
|
||||
[request setServiceUuid:[characteristic.service.UUID fullUUIDString]];
|
||||
ProtosWriteCharacteristicResponse *result = [[ProtosWriteCharacteristicResponse alloc] init];
|
||||
[result setRequest:request];
|
||||
[result setSuccess:(error == nil)];
|
||||
[_channel invokeMethod:@"WriteCharacteristicResponse" arguments:[self toFlutterData:result]];
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
|
||||
NSLog(@"didUpdateNotificationStateForCharacteristic");
|
||||
// Read CCC descriptor of characteristic
|
||||
CBDescriptor *cccd = [self findCCCDescriptor:characteristic];
|
||||
if(cccd == nil || error != nil) {
|
||||
// Send error
|
||||
ProtosSetNotificationResponse *response = [[ProtosSetNotificationResponse alloc] init];
|
||||
[response setRemoteId:[peripheral.identifier UUIDString]];
|
||||
[response setCharacteristic:[self toCharacteristicProto:characteristic]];
|
||||
[response setSuccess:false];
|
||||
[_channel invokeMethod:@"SetNotificationResponse" arguments:[self toFlutterData:response]];
|
||||
return;
|
||||
}
|
||||
|
||||
// Request a read
|
||||
[peripheral readValueForDescriptor:cccd];
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error {
|
||||
if(_descriptorReadStreamHandler.sink != nil) {
|
||||
ProtosReadDescriptorRequest *q = [[ProtosReadDescriptorRequest alloc] init];
|
||||
[q setRemoteId:[peripheral.identifier UUIDString]];
|
||||
[q setCharacteristicUuid:[descriptor.characteristic.UUID fullUUIDString]];
|
||||
[q setDescriptorUuid:[descriptor.UUID fullUUIDString]];
|
||||
if([descriptor.characteristic.service isPrimary]) {
|
||||
[q setServiceUuid:[descriptor.characteristic.service.UUID fullUUIDString]];
|
||||
} else {
|
||||
[q setSecondaryServiceUuid:[descriptor.characteristic.service.UUID fullUUIDString]];
|
||||
CBService *primaryService = [self findPrimaryService:[descriptor.characteristic service] peripheral:[descriptor.characteristic.service peripheral]];
|
||||
[q setServiceUuid:[primaryService.UUID fullUUIDString]];
|
||||
}
|
||||
ProtosReadDescriptorResponse *result = [[ProtosReadDescriptorResponse alloc] init];
|
||||
[result setRequest:q];
|
||||
int value = [descriptor.value intValue];
|
||||
[result setValue:[NSData dataWithBytes:&value length:sizeof(value)]];
|
||||
_descriptorReadStreamHandler.sink([self toFlutterData:result]);
|
||||
}
|
||||
// If descriptor is CCCD, send a SetNotificationResponse in case anything is awaiting
|
||||
if([descriptor.UUID.UUIDString isEqualToString:@"2902"]){
|
||||
ProtosSetNotificationResponse *response = [[ProtosSetNotificationResponse alloc] init];
|
||||
[response setRemoteId:[peripheral.identifier UUIDString]];
|
||||
[response setCharacteristic:[self toCharacteristicProto:descriptor.characteristic]];
|
||||
[response setSuccess:true];
|
||||
[_channel invokeMethod:@"SetNotificationResponse" arguments:[self toFlutterData:response]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error {
|
||||
ProtosWriteDescriptorRequest *request = [[ProtosWriteDescriptorRequest alloc] init];
|
||||
[request setRemoteId:[peripheral.identifier UUIDString]];
|
||||
[request setCharacteristicUuid:[descriptor.characteristic.UUID fullUUIDString]];
|
||||
[request setDescriptorUuid:[descriptor.UUID fullUUIDString]];
|
||||
if([descriptor.characteristic.service isPrimary]) {
|
||||
[request setServiceUuid:[descriptor.characteristic.service.UUID fullUUIDString]];
|
||||
} else {
|
||||
[request setSecondaryServiceUuid:[descriptor.characteristic.service.UUID fullUUIDString]];
|
||||
CBService *primaryService = [self findPrimaryService:[descriptor.characteristic service] peripheral:[descriptor.characteristic.service peripheral]];
|
||||
[request setServiceUuid:[primaryService.UUID fullUUIDString]];
|
||||
}
|
||||
ProtosWriteDescriptorResponse *result = [[ProtosWriteDescriptorResponse alloc] init];
|
||||
[result setRequest:request];
|
||||
[result setSuccess:(error == nil)];
|
||||
[_channel invokeMethod:@"WriteDescriptorResponse" arguments:[self toFlutterData:result]];
|
||||
}
|
||||
|
||||
//
|
||||
// Proto Helper methods
|
||||
//
|
||||
- (FlutterStandardTypedData*)toFlutterData:(GPBMessage*)proto {
|
||||
FlutterStandardTypedData *data = [FlutterStandardTypedData typedDataWithBytes:[[proto data] copy]];
|
||||
return data;
|
||||
}
|
||||
|
||||
- (ProtosBluetoothState*)toBluetoothStateProto:(CBManagerState)state {
|
||||
ProtosBluetoothState *result = [[ProtosBluetoothState alloc] init];
|
||||
switch(state) {
|
||||
case CBManagerStateResetting:
|
||||
[result setState:ProtosBluetoothState_State_TurningOn];
|
||||
break;
|
||||
case CBManagerStateUnsupported:
|
||||
[result setState:ProtosBluetoothState_State_Unavailable];
|
||||
break;
|
||||
case CBManagerStateUnauthorized:
|
||||
[result setState:ProtosBluetoothState_State_Unauthorized];
|
||||
break;
|
||||
case CBManagerStatePoweredOff:
|
||||
[result setState:ProtosBluetoothState_State_Off];
|
||||
break;
|
||||
case CBManagerStatePoweredOn:
|
||||
[result setState:ProtosBluetoothState_State_On];
|
||||
break;
|
||||
default:
|
||||
[result setState:ProtosBluetoothState_State_Unknown];
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (ProtosScanResult*)toScanResultProto:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
|
||||
ProtosScanResult *result = [[ProtosScanResult alloc] init];
|
||||
[result setDevice:[self toDeviceProto:peripheral]];
|
||||
[result setRssi:[RSSI intValue]];
|
||||
ProtosAdvertisementData *ads = [[ProtosAdvertisementData alloc] init];
|
||||
[ads setConnectable:[advertisementData[CBAdvertisementDataIsConnectable] boolValue]];
|
||||
[ads setLocalName:advertisementData[CBAdvertisementDataLocalNameKey]];
|
||||
// Tx Power Level
|
||||
NSNumber *txPower = advertisementData[CBAdvertisementDataTxPowerLevelKey];
|
||||
if(txPower != nil) {
|
||||
ProtosInt32Value *txPowerWrapper = [[ProtosInt32Value alloc] init];
|
||||
[txPowerWrapper setValue:[txPower intValue]];
|
||||
[ads setTxPowerLevel:txPowerWrapper];
|
||||
}
|
||||
// Manufacturer Specific Data
|
||||
NSData *manufData = advertisementData[CBAdvertisementDataManufacturerDataKey];
|
||||
if(manufData.length > 2) {
|
||||
unsigned short manufacturerId;
|
||||
[manufData getBytes:&manufacturerId length:2];
|
||||
[[ads manufacturerData] setObject:[manufData subdataWithRange:NSMakeRange(2, manufData.length - 2)] forKey:manufacturerId];
|
||||
}
|
||||
// Service Data
|
||||
NSDictionary *serviceData = advertisementData[CBAdvertisementDataServiceDataKey];
|
||||
for (CBUUID *uuid in serviceData) {
|
||||
[[ads serviceData] setObject:serviceData[uuid] forKey:uuid.UUIDString];
|
||||
}
|
||||
// Service Uuids
|
||||
NSArray *serviceUuids = advertisementData[CBAdvertisementDataServiceUUIDsKey];
|
||||
for (CBUUID *uuid in serviceUuids) {
|
||||
[[ads serviceUuidsArray] addObject:uuid.UUIDString];
|
||||
}
|
||||
[result setAdvertisementData:ads];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (ProtosBluetoothDevice*)toDeviceProto:(CBPeripheral *)peripheral {
|
||||
ProtosBluetoothDevice *result = [[ProtosBluetoothDevice alloc] init];
|
||||
[result setName:[peripheral name]];
|
||||
[result setRemoteId:[[peripheral identifier] UUIDString]];
|
||||
[result setType:ProtosBluetoothDevice_Type_Le]; // TODO: Does iOS differentiate?
|
||||
return result;
|
||||
}
|
||||
|
||||
- (ProtosDeviceStateResponse*)toDeviceStateProto:(CBPeripheral *)peripheral state:(CBPeripheralState)state {
|
||||
ProtosDeviceStateResponse *result = [[ProtosDeviceStateResponse alloc] init];
|
||||
switch(state) {
|
||||
case CBPeripheralStateDisconnected:
|
||||
[result setState:ProtosDeviceStateResponse_BluetoothDeviceState_Disconnected];
|
||||
break;
|
||||
case CBPeripheralStateConnecting:
|
||||
[result setState:ProtosDeviceStateResponse_BluetoothDeviceState_Connecting];
|
||||
break;
|
||||
case CBPeripheralStateConnected:
|
||||
[result setState:ProtosDeviceStateResponse_BluetoothDeviceState_Connected];
|
||||
break;
|
||||
case CBPeripheralStateDisconnecting:
|
||||
[result setState:ProtosDeviceStateResponse_BluetoothDeviceState_Disconnecting];
|
||||
break;
|
||||
}
|
||||
[result setRemoteId:[[peripheral identifier] UUIDString]];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (ProtosDiscoverServicesResult*)toServicesResultProto:(CBPeripheral *)peripheral {
|
||||
ProtosDiscoverServicesResult *result = [[ProtosDiscoverServicesResult alloc] init];
|
||||
[result setRemoteId:[peripheral.identifier UUIDString]];
|
||||
NSMutableArray *servicesProtos = [NSMutableArray new];
|
||||
for(CBService *s in [peripheral services]) {
|
||||
[servicesProtos addObject:[self toServiceProto:peripheral service:s]];
|
||||
}
|
||||
[result setServicesArray:servicesProtos];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (ProtosBluetoothService*)toServiceProto:(CBPeripheral *)peripheral service:(CBService *)service {
|
||||
ProtosBluetoothService *result = [[ProtosBluetoothService alloc] init];
|
||||
NSLog(@"peripheral uuid:%@", [peripheral.identifier UUIDString]);
|
||||
NSLog(@"service uuid:%@", [service.UUID fullUUIDString]);
|
||||
[result setRemoteId:[peripheral.identifier UUIDString]];
|
||||
[result setUuid:[service.UUID fullUUIDString]];
|
||||
[result setIsPrimary:[service isPrimary]];
|
||||
|
||||
// Characteristic Array
|
||||
NSMutableArray *characteristicProtos = [NSMutableArray new];
|
||||
for(CBCharacteristic *c in [service characteristics]) {
|
||||
[characteristicProtos addObject:[self toCharacteristicProto:c]];
|
||||
}
|
||||
[result setCharacteristicsArray:characteristicProtos];
|
||||
|
||||
// Included Services Array
|
||||
NSMutableArray *includedServicesProtos = [NSMutableArray new];
|
||||
for(CBService *s in [service includedServices]) {
|
||||
[includedServicesProtos addObject:[self toServiceProto:peripheral service:s]];
|
||||
}
|
||||
[result setIncludedServicesArray:includedServicesProtos];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (ProtosBluetoothCharacteristic*)toCharacteristicProto:(CBCharacteristic *)characteristic {
|
||||
ProtosBluetoothCharacteristic *result = [[ProtosBluetoothCharacteristic alloc] init];
|
||||
[result setUuid:[characteristic.UUID fullUUIDString]];
|
||||
[result setProperties:[self toCharacteristicPropsProto:characteristic.properties]];
|
||||
[result setValue:[characteristic value]];
|
||||
NSLog(@"uuid: %@ value: %@", [characteristic.UUID fullUUIDString], [characteristic value]);
|
||||
NSMutableArray *descriptorProtos = [NSMutableArray new];
|
||||
for(CBDescriptor *d in [characteristic descriptors]) {
|
||||
[descriptorProtos addObject:[self toDescriptorProto:d]];
|
||||
}
|
||||
[result setDescriptorsArray:descriptorProtos];
|
||||
if([characteristic.service isPrimary]) {
|
||||
[result setServiceUuid:[characteristic.service.UUID fullUUIDString]];
|
||||
} else {
|
||||
// Reverse search to find service and secondary service UUID
|
||||
[result setSecondaryServiceUuid:[characteristic.service.UUID fullUUIDString]];
|
||||
CBService *primaryService = [self findPrimaryService:[characteristic service] peripheral:[characteristic.service peripheral]];
|
||||
[result setServiceUuid:[primaryService.UUID fullUUIDString]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (ProtosBluetoothDescriptor*)toDescriptorProto:(CBDescriptor *)descriptor {
|
||||
ProtosBluetoothDescriptor *result = [[ProtosBluetoothDescriptor alloc] init];
|
||||
[result setUuid:[descriptor.UUID fullUUIDString]];
|
||||
[result setCharacteristicUuid:[descriptor.characteristic.UUID fullUUIDString]];
|
||||
[result setServiceUuid:[descriptor.characteristic.service.UUID fullUUIDString]];
|
||||
int value = [descriptor.value intValue];
|
||||
[result setValue:[NSData dataWithBytes:&value length:sizeof(value)]];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (ProtosCharacteristicProperties*)toCharacteristicPropsProto:(CBCharacteristicProperties)props {
|
||||
ProtosCharacteristicProperties *result = [[ProtosCharacteristicProperties alloc] init];
|
||||
[result setBroadcast:(props & CBCharacteristicPropertyBroadcast) != 0];
|
||||
[result setRead:(props & CBCharacteristicPropertyRead) != 0];
|
||||
[result setWriteWithoutResponse:(props & CBCharacteristicPropertyWriteWithoutResponse) != 0];
|
||||
[result setWrite:(props & CBCharacteristicPropertyWrite) != 0];
|
||||
[result setNotify:(props & CBCharacteristicPropertyNotify) != 0];
|
||||
[result setIndicate:(props & CBCharacteristicPropertyIndicate) != 0];
|
||||
[result setAuthenticatedSignedWrites:(props & CBCharacteristicPropertyAuthenticatedSignedWrites) != 0];
|
||||
[result setExtendedProperties:(props & CBCharacteristicPropertyExtendedProperties) != 0];
|
||||
[result setNotifyEncryptionRequired:(props & CBCharacteristicPropertyNotifyEncryptionRequired) != 0];
|
||||
[result setIndicateEncryptionRequired:(props & CBCharacteristicPropertyIndicateEncryptionRequired) != 0];
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
- (void)log:(LogLevel)level format:(NSString *)format, ... {
|
||||
if(level <= _logLevel) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
// NSString* formattedMessage = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
NSLog(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation FlutterBleStreamHandler
|
||||
|
||||
- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink {
|
||||
self.sink = eventSink;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (FlutterError*)onCancelWithArguments:(id)arguments {
|
||||
self.sink = nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'flutter_ble'
|
||||
s.version = '0.5.1'
|
||||
s.summary = 'A new Flutter plugin.'
|
||||
s.description = <<-DESC
|
||||
A new Flutter plugin.
|
||||
DESC
|
||||
s.homepage = 'http://example.com'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.public_header_files = 'Classes/**/*.h'
|
||||
s.dependency 'Flutter'
|
||||
s.framework = 'CoreBluetooth'
|
||||
s.dependency '!ProtoCompiler'
|
||||
|
||||
protoc = ENV['PWD'] + '/Pods/!ProtoCompiler/protoc'
|
||||
objc_out = 'gen'
|
||||
proto_in = '../protos'
|
||||
s.prepare_command = <<-CMD
|
||||
mkdir -p #{objc_out}
|
||||
#{protoc} \
|
||||
--objc_out=#{objc_out} \
|
||||
--proto_path=#{proto_in} \
|
||||
#{proto_in}/*.proto
|
||||
CMD
|
||||
|
||||
s.subspec 'Protos' do |ss|
|
||||
ss.source_files = 'gen/**/*.pbobjc.{h,m}'
|
||||
ss.header_mappings_dir = 'gen'
|
||||
ss.requires_arc = false
|
||||
ss.dependency 'Protobuf'
|
||||
end
|
||||
|
||||
s.pod_target_xcconfig = {
|
||||
# This is needed by all pods that depend on Protobuf:
|
||||
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
|
||||
}
|
||||
|
||||
|
||||
s.ios.deployment_target = '8.0'
|
||||
end
|
||||
|
||||
@@ -0,0 +1,733 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: flutterblue.proto
|
||||
|
||||
// This CPP symbol can be defined to use imports that match up to the framework
|
||||
// imports needed when using CocoaPods.
|
||||
#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
|
||||
#define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
|
||||
#endif
|
||||
|
||||
#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
|
||||
#import <Protobuf/GPBProtocolBuffers.h>
|
||||
#else
|
||||
#import "GPBProtocolBuffers.h"
|
||||
#endif
|
||||
|
||||
#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
|
||||
#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
|
||||
#endif
|
||||
#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
|
||||
#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
|
||||
#endif
|
||||
|
||||
// @@protoc_insertion_point(imports)
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
CF_EXTERN_C_BEGIN
|
||||
|
||||
@class ProtosAdvertisementData;
|
||||
@class ProtosBluetoothCharacteristic;
|
||||
@class ProtosBluetoothDescriptor;
|
||||
@class ProtosBluetoothDevice;
|
||||
@class ProtosBluetoothService;
|
||||
@class ProtosCharacteristicProperties;
|
||||
@class ProtosInt32Value;
|
||||
@class ProtosReadDescriptorRequest;
|
||||
@class ProtosWriteCharacteristicRequest;
|
||||
@class ProtosWriteDescriptorRequest;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - Enum ProtosBluetoothState_State
|
||||
|
||||
typedef GPB_ENUM(ProtosBluetoothState_State) {
|
||||
/**
|
||||
* Value used if any message's field encounters a value that is not defined
|
||||
* by this enum. The message will also have C functions to get/set the rawValue
|
||||
* of the field.
|
||||
**/
|
||||
ProtosBluetoothState_State_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
|
||||
ProtosBluetoothState_State_Unknown = 0,
|
||||
ProtosBluetoothState_State_Unavailable = 1,
|
||||
ProtosBluetoothState_State_Unauthorized = 2,
|
||||
ProtosBluetoothState_State_TurningOn = 3,
|
||||
ProtosBluetoothState_State_On = 4,
|
||||
ProtosBluetoothState_State_TurningOff = 5,
|
||||
ProtosBluetoothState_State_Off = 6,
|
||||
};
|
||||
|
||||
GPBEnumDescriptor *ProtosBluetoothState_State_EnumDescriptor(void);
|
||||
|
||||
/**
|
||||
* Checks to see if the given value is defined by the enum or was not known at
|
||||
* the time this source was generated.
|
||||
**/
|
||||
BOOL ProtosBluetoothState_State_IsValidValue(int32_t value);
|
||||
|
||||
#pragma mark - Enum ProtosBluetoothDevice_Type
|
||||
|
||||
typedef GPB_ENUM(ProtosBluetoothDevice_Type) {
|
||||
/**
|
||||
* Value used if any message's field encounters a value that is not defined
|
||||
* by this enum. The message will also have C functions to get/set the rawValue
|
||||
* of the field.
|
||||
**/
|
||||
ProtosBluetoothDevice_Type_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
|
||||
ProtosBluetoothDevice_Type_Unknown = 0,
|
||||
ProtosBluetoothDevice_Type_Classic = 1,
|
||||
ProtosBluetoothDevice_Type_Le = 2,
|
||||
ProtosBluetoothDevice_Type_Dual = 3,
|
||||
};
|
||||
|
||||
GPBEnumDescriptor *ProtosBluetoothDevice_Type_EnumDescriptor(void);
|
||||
|
||||
/**
|
||||
* Checks to see if the given value is defined by the enum or was not known at
|
||||
* the time this source was generated.
|
||||
**/
|
||||
BOOL ProtosBluetoothDevice_Type_IsValidValue(int32_t value);
|
||||
|
||||
#pragma mark - Enum ProtosWriteCharacteristicRequest_WriteType
|
||||
|
||||
typedef GPB_ENUM(ProtosWriteCharacteristicRequest_WriteType) {
|
||||
/**
|
||||
* Value used if any message's field encounters a value that is not defined
|
||||
* by this enum. The message will also have C functions to get/set the rawValue
|
||||
* of the field.
|
||||
**/
|
||||
ProtosWriteCharacteristicRequest_WriteType_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
|
||||
ProtosWriteCharacteristicRequest_WriteType_WithResponse = 0,
|
||||
ProtosWriteCharacteristicRequest_WriteType_WithoutResponse = 1,
|
||||
};
|
||||
|
||||
GPBEnumDescriptor *ProtosWriteCharacteristicRequest_WriteType_EnumDescriptor(void);
|
||||
|
||||
/**
|
||||
* Checks to see if the given value is defined by the enum or was not known at
|
||||
* the time this source was generated.
|
||||
**/
|
||||
BOOL ProtosWriteCharacteristicRequest_WriteType_IsValidValue(int32_t value);
|
||||
|
||||
#pragma mark - Enum ProtosDeviceStateResponse_BluetoothDeviceState
|
||||
|
||||
typedef GPB_ENUM(ProtosDeviceStateResponse_BluetoothDeviceState) {
|
||||
/**
|
||||
* Value used if any message's field encounters a value that is not defined
|
||||
* by this enum. The message will also have C functions to get/set the rawValue
|
||||
* of the field.
|
||||
**/
|
||||
ProtosDeviceStateResponse_BluetoothDeviceState_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
|
||||
ProtosDeviceStateResponse_BluetoothDeviceState_Disconnected = 0,
|
||||
ProtosDeviceStateResponse_BluetoothDeviceState_Connecting = 1,
|
||||
ProtosDeviceStateResponse_BluetoothDeviceState_Connected = 2,
|
||||
ProtosDeviceStateResponse_BluetoothDeviceState_Disconnecting = 3,
|
||||
};
|
||||
|
||||
GPBEnumDescriptor *ProtosDeviceStateResponse_BluetoothDeviceState_EnumDescriptor(void);
|
||||
|
||||
/**
|
||||
* Checks to see if the given value is defined by the enum or was not known at
|
||||
* the time this source was generated.
|
||||
**/
|
||||
BOOL ProtosDeviceStateResponse_BluetoothDeviceState_IsValidValue(int32_t value);
|
||||
|
||||
#pragma mark - ProtosFlutterblueRoot
|
||||
|
||||
/**
|
||||
* Exposes the extension registry for this file.
|
||||
*
|
||||
* The base class provides:
|
||||
* @code
|
||||
* + (GPBExtensionRegistry *)extensionRegistry;
|
||||
* @endcode
|
||||
* which is a @c GPBExtensionRegistry that includes all the extensions defined by
|
||||
* this file and all files that it depends on.
|
||||
**/
|
||||
@interface ProtosFlutterblueRoot : GPBRootObject
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosInt32Value
|
||||
|
||||
typedef GPB_ENUM(ProtosInt32Value_FieldNumber) {
|
||||
ProtosInt32Value_FieldNumber_Value = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper message for `int32`.
|
||||
*
|
||||
* Allows for nullability of fields in messages
|
||||
**/
|
||||
@interface ProtosInt32Value : GPBMessage
|
||||
|
||||
/** The int32 value. */
|
||||
@property(nonatomic, readwrite) int32_t value;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosBluetoothState
|
||||
|
||||
typedef GPB_ENUM(ProtosBluetoothState_FieldNumber) {
|
||||
ProtosBluetoothState_FieldNumber_State = 1,
|
||||
};
|
||||
|
||||
@interface ProtosBluetoothState : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite) ProtosBluetoothState_State state;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Fetches the raw value of a @c ProtosBluetoothState's @c state property, even
|
||||
* if the value was not defined by the enum at the time the code was generated.
|
||||
**/
|
||||
int32_t ProtosBluetoothState_State_RawValue(ProtosBluetoothState *message);
|
||||
/**
|
||||
* Sets the raw value of an @c ProtosBluetoothState's @c state property, allowing
|
||||
* it to be set to a value that was not defined by the enum at the time the code
|
||||
* was generated.
|
||||
**/
|
||||
void SetProtosBluetoothState_State_RawValue(ProtosBluetoothState *message, int32_t value);
|
||||
|
||||
#pragma mark - ProtosAdvertisementData
|
||||
|
||||
typedef GPB_ENUM(ProtosAdvertisementData_FieldNumber) {
|
||||
ProtosAdvertisementData_FieldNumber_LocalName = 1,
|
||||
ProtosAdvertisementData_FieldNumber_TxPowerLevel = 2,
|
||||
ProtosAdvertisementData_FieldNumber_Connectable = 3,
|
||||
ProtosAdvertisementData_FieldNumber_ManufacturerData = 4,
|
||||
ProtosAdvertisementData_FieldNumber_ServiceData = 5,
|
||||
ProtosAdvertisementData_FieldNumber_ServiceUuidsArray = 6,
|
||||
};
|
||||
|
||||
@interface ProtosAdvertisementData : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *localName;
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) ProtosInt32Value *txPowerLevel;
|
||||
/** Test to see if @c txPowerLevel has been set. */
|
||||
@property(nonatomic, readwrite) BOOL hasTxPowerLevel;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL connectable;
|
||||
|
||||
/** Map of manufacturers to their data */
|
||||
@property(nonatomic, readwrite, strong, null_resettable) GPBInt32ObjectDictionary<NSData*> *manufacturerData;
|
||||
/** The number of items in @c manufacturerData without causing the array to be created. */
|
||||
@property(nonatomic, readonly) NSUInteger manufacturerData_Count;
|
||||
|
||||
/** Map of service UUIDs to their data. */
|
||||
@property(nonatomic, readwrite, strong, null_resettable) NSMutableDictionary<NSString*, NSData*> *serviceData;
|
||||
/** The number of items in @c serviceData without causing the array to be created. */
|
||||
@property(nonatomic, readonly) NSUInteger serviceData_Count;
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *serviceUuidsArray;
|
||||
/** The number of items in @c serviceUuidsArray without causing the array to be created. */
|
||||
@property(nonatomic, readonly) NSUInteger serviceUuidsArray_Count;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosScanSettings
|
||||
|
||||
typedef GPB_ENUM(ProtosScanSettings_FieldNumber) {
|
||||
ProtosScanSettings_FieldNumber_AndroidScanMode = 1,
|
||||
ProtosScanSettings_FieldNumber_ServiceUuidsArray = 2,
|
||||
};
|
||||
|
||||
@interface ProtosScanSettings : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite) int32_t androidScanMode;
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *serviceUuidsArray;
|
||||
/** The number of items in @c serviceUuidsArray without causing the array to be created. */
|
||||
@property(nonatomic, readonly) NSUInteger serviceUuidsArray_Count;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosScanResult
|
||||
|
||||
typedef GPB_ENUM(ProtosScanResult_FieldNumber) {
|
||||
ProtosScanResult_FieldNumber_Device = 1,
|
||||
ProtosScanResult_FieldNumber_AdvertisementData = 2,
|
||||
ProtosScanResult_FieldNumber_Rssi = 3,
|
||||
};
|
||||
|
||||
@interface ProtosScanResult : GPBMessage
|
||||
|
||||
/** The received peer's ID. */
|
||||
@property(nonatomic, readwrite, strong, null_resettable) ProtosBluetoothDevice *device;
|
||||
/** Test to see if @c device has been set. */
|
||||
@property(nonatomic, readwrite) BOOL hasDevice;
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) ProtosAdvertisementData *advertisementData;
|
||||
/** Test to see if @c advertisementData has been set. */
|
||||
@property(nonatomic, readwrite) BOOL hasAdvertisementData;
|
||||
|
||||
@property(nonatomic, readwrite) int32_t rssi;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosConnectRequest
|
||||
|
||||
typedef GPB_ENUM(ProtosConnectRequest_FieldNumber) {
|
||||
ProtosConnectRequest_FieldNumber_RemoteId = 1,
|
||||
ProtosConnectRequest_FieldNumber_AndroidAutoConnect = 2,
|
||||
};
|
||||
|
||||
@interface ProtosConnectRequest : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL androidAutoConnect;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosBluetoothDevice
|
||||
|
||||
typedef GPB_ENUM(ProtosBluetoothDevice_FieldNumber) {
|
||||
ProtosBluetoothDevice_FieldNumber_RemoteId = 1,
|
||||
ProtosBluetoothDevice_FieldNumber_Name = 2,
|
||||
ProtosBluetoothDevice_FieldNumber_Type = 3,
|
||||
};
|
||||
|
||||
@interface ProtosBluetoothDevice : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
|
||||
|
||||
@property(nonatomic, readwrite) ProtosBluetoothDevice_Type type;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Fetches the raw value of a @c ProtosBluetoothDevice's @c type property, even
|
||||
* if the value was not defined by the enum at the time the code was generated.
|
||||
**/
|
||||
int32_t ProtosBluetoothDevice_Type_RawValue(ProtosBluetoothDevice *message);
|
||||
/**
|
||||
* Sets the raw value of an @c ProtosBluetoothDevice's @c type property, allowing
|
||||
* it to be set to a value that was not defined by the enum at the time the code
|
||||
* was generated.
|
||||
**/
|
||||
void SetProtosBluetoothDevice_Type_RawValue(ProtosBluetoothDevice *message, int32_t value);
|
||||
|
||||
#pragma mark - ProtosBluetoothService
|
||||
|
||||
typedef GPB_ENUM(ProtosBluetoothService_FieldNumber) {
|
||||
ProtosBluetoothService_FieldNumber_Uuid = 1,
|
||||
ProtosBluetoothService_FieldNumber_RemoteId = 2,
|
||||
ProtosBluetoothService_FieldNumber_IsPrimary = 3,
|
||||
ProtosBluetoothService_FieldNumber_CharacteristicsArray = 4,
|
||||
ProtosBluetoothService_FieldNumber_IncludedServicesArray = 5,
|
||||
};
|
||||
|
||||
@interface ProtosBluetoothService : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *uuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
/** Indicates whether the type of service is primary or secondary. */
|
||||
@property(nonatomic, readwrite) BOOL isPrimary;
|
||||
|
||||
/** A list of characteristics that have been discovered in this service. */
|
||||
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<ProtosBluetoothCharacteristic*> *characteristicsArray;
|
||||
/** The number of items in @c characteristicsArray without causing the array to be created. */
|
||||
@property(nonatomic, readonly) NSUInteger characteristicsArray_Count;
|
||||
|
||||
/** A list of included services that have been discovered in this service. */
|
||||
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<ProtosBluetoothService*> *includedServicesArray;
|
||||
/** The number of items in @c includedServicesArray without causing the array to be created. */
|
||||
@property(nonatomic, readonly) NSUInteger includedServicesArray_Count;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosBluetoothCharacteristic
|
||||
|
||||
typedef GPB_ENUM(ProtosBluetoothCharacteristic_FieldNumber) {
|
||||
ProtosBluetoothCharacteristic_FieldNumber_Uuid = 1,
|
||||
ProtosBluetoothCharacteristic_FieldNumber_ServiceUuid = 2,
|
||||
ProtosBluetoothCharacteristic_FieldNumber_SecondaryServiceUuid = 3,
|
||||
ProtosBluetoothCharacteristic_FieldNumber_DescriptorsArray = 4,
|
||||
ProtosBluetoothCharacteristic_FieldNumber_Properties = 5,
|
||||
ProtosBluetoothCharacteristic_FieldNumber_Value = 6,
|
||||
};
|
||||
|
||||
@interface ProtosBluetoothCharacteristic : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *uuid;
|
||||
|
||||
/** The service that this characteristic belongs to. */
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *serviceUuid;
|
||||
|
||||
/** The secondary service if nested */
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *secondaryServiceUuid;
|
||||
|
||||
/** A list of descriptors that have been discovered in this characteristic. */
|
||||
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<ProtosBluetoothDescriptor*> *descriptorsArray;
|
||||
/** The number of items in @c descriptorsArray without causing the array to be created. */
|
||||
@property(nonatomic, readonly) NSUInteger descriptorsArray_Count;
|
||||
|
||||
/** The properties of the characteristic. */
|
||||
@property(nonatomic, readwrite, strong, null_resettable) ProtosCharacteristicProperties *properties;
|
||||
/** Test to see if @c properties has been set. */
|
||||
@property(nonatomic, readwrite) BOOL hasProperties;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSData *value;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosBluetoothDescriptor
|
||||
|
||||
typedef GPB_ENUM(ProtosBluetoothDescriptor_FieldNumber) {
|
||||
ProtosBluetoothDescriptor_FieldNumber_Uuid = 1,
|
||||
ProtosBluetoothDescriptor_FieldNumber_ServiceUuid = 2,
|
||||
ProtosBluetoothDescriptor_FieldNumber_CharacteristicUuid = 3,
|
||||
ProtosBluetoothDescriptor_FieldNumber_Value = 4,
|
||||
};
|
||||
|
||||
@interface ProtosBluetoothDescriptor : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *uuid;
|
||||
|
||||
/** The service that this descriptor belongs to. */
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *serviceUuid;
|
||||
|
||||
/** The characteristic that this descriptor belongs to. */
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *characteristicUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSData *value;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosCharacteristicProperties
|
||||
|
||||
typedef GPB_ENUM(ProtosCharacteristicProperties_FieldNumber) {
|
||||
ProtosCharacteristicProperties_FieldNumber_Broadcast = 1,
|
||||
ProtosCharacteristicProperties_FieldNumber_Read = 2,
|
||||
ProtosCharacteristicProperties_FieldNumber_WriteWithoutResponse = 3,
|
||||
ProtosCharacteristicProperties_FieldNumber_Write = 4,
|
||||
ProtosCharacteristicProperties_FieldNumber_Notify = 5,
|
||||
ProtosCharacteristicProperties_FieldNumber_Indicate = 6,
|
||||
ProtosCharacteristicProperties_FieldNumber_AuthenticatedSignedWrites = 7,
|
||||
ProtosCharacteristicProperties_FieldNumber_ExtendedProperties = 8,
|
||||
ProtosCharacteristicProperties_FieldNumber_NotifyEncryptionRequired = 9,
|
||||
ProtosCharacteristicProperties_FieldNumber_IndicateEncryptionRequired = 10,
|
||||
};
|
||||
|
||||
@interface ProtosCharacteristicProperties : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite) BOOL broadcast;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL read;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL writeWithoutResponse;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL write;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL notify;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL indicate;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL authenticatedSignedWrites;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL extendedProperties;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL notifyEncryptionRequired;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL indicateEncryptionRequired;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosDiscoverServicesResult
|
||||
|
||||
typedef GPB_ENUM(ProtosDiscoverServicesResult_FieldNumber) {
|
||||
ProtosDiscoverServicesResult_FieldNumber_RemoteId = 1,
|
||||
ProtosDiscoverServicesResult_FieldNumber_ServicesArray = 2,
|
||||
};
|
||||
|
||||
@interface ProtosDiscoverServicesResult : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<ProtosBluetoothService*> *servicesArray;
|
||||
/** The number of items in @c servicesArray without causing the array to be created. */
|
||||
@property(nonatomic, readonly) NSUInteger servicesArray_Count;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosReadCharacteristicRequest
|
||||
|
||||
typedef GPB_ENUM(ProtosReadCharacteristicRequest_FieldNumber) {
|
||||
ProtosReadCharacteristicRequest_FieldNumber_RemoteId = 1,
|
||||
ProtosReadCharacteristicRequest_FieldNumber_CharacteristicUuid = 2,
|
||||
ProtosReadCharacteristicRequest_FieldNumber_ServiceUuid = 3,
|
||||
ProtosReadCharacteristicRequest_FieldNumber_SecondaryServiceUuid = 4,
|
||||
};
|
||||
|
||||
@interface ProtosReadCharacteristicRequest : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *characteristicUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *serviceUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *secondaryServiceUuid;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosReadCharacteristicResponse
|
||||
|
||||
typedef GPB_ENUM(ProtosReadCharacteristicResponse_FieldNumber) {
|
||||
ProtosReadCharacteristicResponse_FieldNumber_RemoteId = 1,
|
||||
ProtosReadCharacteristicResponse_FieldNumber_Characteristic = 2,
|
||||
};
|
||||
|
||||
@interface ProtosReadCharacteristicResponse : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) ProtosBluetoothCharacteristic *characteristic;
|
||||
/** Test to see if @c characteristic has been set. */
|
||||
@property(nonatomic, readwrite) BOOL hasCharacteristic;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosReadDescriptorRequest
|
||||
|
||||
typedef GPB_ENUM(ProtosReadDescriptorRequest_FieldNumber) {
|
||||
ProtosReadDescriptorRequest_FieldNumber_RemoteId = 1,
|
||||
ProtosReadDescriptorRequest_FieldNumber_DescriptorUuid = 2,
|
||||
ProtosReadDescriptorRequest_FieldNumber_ServiceUuid = 3,
|
||||
ProtosReadDescriptorRequest_FieldNumber_SecondaryServiceUuid = 4,
|
||||
ProtosReadDescriptorRequest_FieldNumber_CharacteristicUuid = 5,
|
||||
};
|
||||
|
||||
@interface ProtosReadDescriptorRequest : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *descriptorUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *serviceUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *secondaryServiceUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *characteristicUuid;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosReadDescriptorResponse
|
||||
|
||||
typedef GPB_ENUM(ProtosReadDescriptorResponse_FieldNumber) {
|
||||
ProtosReadDescriptorResponse_FieldNumber_Request = 1,
|
||||
ProtosReadDescriptorResponse_FieldNumber_Value = 2,
|
||||
};
|
||||
|
||||
@interface ProtosReadDescriptorResponse : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) ProtosReadDescriptorRequest *request;
|
||||
/** Test to see if @c request has been set. */
|
||||
@property(nonatomic, readwrite) BOOL hasRequest;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSData *value;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosWriteCharacteristicRequest
|
||||
|
||||
typedef GPB_ENUM(ProtosWriteCharacteristicRequest_FieldNumber) {
|
||||
ProtosWriteCharacteristicRequest_FieldNumber_RemoteId = 1,
|
||||
ProtosWriteCharacteristicRequest_FieldNumber_CharacteristicUuid = 2,
|
||||
ProtosWriteCharacteristicRequest_FieldNumber_ServiceUuid = 3,
|
||||
ProtosWriteCharacteristicRequest_FieldNumber_SecondaryServiceUuid = 4,
|
||||
ProtosWriteCharacteristicRequest_FieldNumber_WriteType = 5,
|
||||
ProtosWriteCharacteristicRequest_FieldNumber_Value = 6,
|
||||
};
|
||||
|
||||
@interface ProtosWriteCharacteristicRequest : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *characteristicUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *serviceUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *secondaryServiceUuid;
|
||||
|
||||
@property(nonatomic, readwrite) ProtosWriteCharacteristicRequest_WriteType writeType;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSData *value;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Fetches the raw value of a @c ProtosWriteCharacteristicRequest's @c writeType property, even
|
||||
* if the value was not defined by the enum at the time the code was generated.
|
||||
**/
|
||||
int32_t ProtosWriteCharacteristicRequest_WriteType_RawValue(ProtosWriteCharacteristicRequest *message);
|
||||
/**
|
||||
* Sets the raw value of an @c ProtosWriteCharacteristicRequest's @c writeType property, allowing
|
||||
* it to be set to a value that was not defined by the enum at the time the code
|
||||
* was generated.
|
||||
**/
|
||||
void SetProtosWriteCharacteristicRequest_WriteType_RawValue(ProtosWriteCharacteristicRequest *message, int32_t value);
|
||||
|
||||
#pragma mark - ProtosWriteCharacteristicResponse
|
||||
|
||||
typedef GPB_ENUM(ProtosWriteCharacteristicResponse_FieldNumber) {
|
||||
ProtosWriteCharacteristicResponse_FieldNumber_Request = 1,
|
||||
ProtosWriteCharacteristicResponse_FieldNumber_Success = 2,
|
||||
};
|
||||
|
||||
@interface ProtosWriteCharacteristicResponse : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) ProtosWriteCharacteristicRequest *request;
|
||||
/** Test to see if @c request has been set. */
|
||||
@property(nonatomic, readwrite) BOOL hasRequest;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL success;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosWriteDescriptorRequest
|
||||
|
||||
typedef GPB_ENUM(ProtosWriteDescriptorRequest_FieldNumber) {
|
||||
ProtosWriteDescriptorRequest_FieldNumber_RemoteId = 1,
|
||||
ProtosWriteDescriptorRequest_FieldNumber_DescriptorUuid = 2,
|
||||
ProtosWriteDescriptorRequest_FieldNumber_ServiceUuid = 3,
|
||||
ProtosWriteDescriptorRequest_FieldNumber_SecondaryServiceUuid = 4,
|
||||
ProtosWriteDescriptorRequest_FieldNumber_CharacteristicUuid = 5,
|
||||
ProtosWriteDescriptorRequest_FieldNumber_Value = 6,
|
||||
};
|
||||
|
||||
@interface ProtosWriteDescriptorRequest : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *descriptorUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *serviceUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *secondaryServiceUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *characteristicUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSData *value;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosWriteDescriptorResponse
|
||||
|
||||
typedef GPB_ENUM(ProtosWriteDescriptorResponse_FieldNumber) {
|
||||
ProtosWriteDescriptorResponse_FieldNumber_Request = 1,
|
||||
ProtosWriteDescriptorResponse_FieldNumber_Success = 2,
|
||||
};
|
||||
|
||||
@interface ProtosWriteDescriptorResponse : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) ProtosWriteDescriptorRequest *request;
|
||||
/** Test to see if @c request has been set. */
|
||||
@property(nonatomic, readwrite) BOOL hasRequest;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL success;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosSetNotificationRequest
|
||||
|
||||
typedef GPB_ENUM(ProtosSetNotificationRequest_FieldNumber) {
|
||||
ProtosSetNotificationRequest_FieldNumber_RemoteId = 1,
|
||||
ProtosSetNotificationRequest_FieldNumber_ServiceUuid = 2,
|
||||
ProtosSetNotificationRequest_FieldNumber_SecondaryServiceUuid = 3,
|
||||
ProtosSetNotificationRequest_FieldNumber_CharacteristicUuid = 4,
|
||||
ProtosSetNotificationRequest_FieldNumber_Enable = 5,
|
||||
};
|
||||
|
||||
@interface ProtosSetNotificationRequest : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *serviceUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *secondaryServiceUuid;
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *characteristicUuid;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL enable;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosSetNotificationResponse
|
||||
|
||||
typedef GPB_ENUM(ProtosSetNotificationResponse_FieldNumber) {
|
||||
ProtosSetNotificationResponse_FieldNumber_RemoteId = 1,
|
||||
ProtosSetNotificationResponse_FieldNumber_Characteristic = 2,
|
||||
ProtosSetNotificationResponse_FieldNumber_Success = 3,
|
||||
};
|
||||
|
||||
@interface ProtosSetNotificationResponse : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) ProtosBluetoothCharacteristic *characteristic;
|
||||
/** Test to see if @c characteristic has been set. */
|
||||
@property(nonatomic, readwrite) BOOL hasCharacteristic;
|
||||
|
||||
@property(nonatomic, readwrite) BOOL success;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosOnNotificationResponse
|
||||
|
||||
typedef GPB_ENUM(ProtosOnNotificationResponse_FieldNumber) {
|
||||
ProtosOnNotificationResponse_FieldNumber_RemoteId = 1,
|
||||
ProtosOnNotificationResponse_FieldNumber_Characteristic = 2,
|
||||
};
|
||||
|
||||
@interface ProtosOnNotificationResponse : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite, strong, null_resettable) ProtosBluetoothCharacteristic *characteristic;
|
||||
/** Test to see if @c characteristic has been set. */
|
||||
@property(nonatomic, readwrite) BOOL hasCharacteristic;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - ProtosDeviceStateResponse
|
||||
|
||||
typedef GPB_ENUM(ProtosDeviceStateResponse_FieldNumber) {
|
||||
ProtosDeviceStateResponse_FieldNumber_RemoteId = 1,
|
||||
ProtosDeviceStateResponse_FieldNumber_State = 2,
|
||||
};
|
||||
|
||||
@interface ProtosDeviceStateResponse : GPBMessage
|
||||
|
||||
@property(nonatomic, readwrite, copy, null_resettable) NSString *remoteId;
|
||||
|
||||
@property(nonatomic, readwrite) ProtosDeviceStateResponse_BluetoothDeviceState state;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Fetches the raw value of a @c ProtosDeviceStateResponse's @c state property, even
|
||||
* if the value was not defined by the enum at the time the code was generated.
|
||||
**/
|
||||
int32_t ProtosDeviceStateResponse_State_RawValue(ProtosDeviceStateResponse *message);
|
||||
/**
|
||||
* Sets the raw value of an @c ProtosDeviceStateResponse's @c state property, allowing
|
||||
* it to be set to a value that was not defined by the enum at the time the code
|
||||
* was generated.
|
||||
**/
|
||||
void SetProtosDeviceStateResponse_State_RawValue(ProtosDeviceStateResponse *message, int32_t value);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
CF_EXTERN_C_END
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// @@protoc_insertion_point(global_scope)
|
||||