From b42e04227231dea81bcde8c8205316caee88ea26 Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Tue, 4 Jul 2023 15:30:52 +0100 Subject: [PATCH 01/17] Adds support for Android companion pairing & device bonding --- README.md | 1 + example/android/app/build.gradle | 2 +- .../android/gradle/wrapper/gradle-wrapper.jar | Bin 53636 -> 59536 bytes .../gradle/wrapper/gradle-wrapper.properties | 3 +- example/android/gradlew | 290 +++++++++------ example/android/gradlew.bat | 53 ++- .../lib/src/device_connector.dart | 3 + .../lib/src/device_scanner.dart | 18 + .../lib/src/reactive_ble.dart | 16 + packages/flutter_reactive_ble/pubspec.yaml | 7 +- .../reactive_ble_mobile/android/build.gradle | 2 +- .../android/src/main/AndroidManifest.xml | 1 + .../flutterreactiveble/PluginController.kt | 333 +++++++++++------- .../flutterreactiveble/ReactiveBlePlugin.kt | 24 +- .../hue/flutterreactiveble/ble/BleClient.kt | 3 +- .../flutterreactiveble/ble/BondingManager.kt | 104 ++++++ .../flutterreactiveble/ble/DeviceConnector.kt | 156 +++++--- .../ble/ReactiveBleClient.kt | 14 +- .../channelhandlers/CompanionHandler.kt | 140 ++++++++ .../DeviceConnectionHandler.kt | 4 +- .../flutterreactiveble/model/BondingMode.kt | 9 + .../converter/args_to_protubuf_converter.dart | 22 ++ .../lib/src/converter/protobuf_converter.dart | 9 + .../lib/src/generated/bledata.pb.dart | 114 ++++++ .../lib/src/generated/bledata.pbjson.dart | 33 +- .../lib/src/reactive_ble_mobile_platform.dart | 26 ++ .../reactive_ble_mobile/protos/bledata.proto | 11 + packages/reactive_ble_mobile/pubspec.yaml | 4 + .../args_to_protobuf_converter_test.dart | 8 +- .../test/reactive_ble_platform_test.dart | 6 +- .../lib/src/model/association_info.dart | 16 + .../lib/src/model/association_info.g.dart | 64 ++++ .../lib/src/model/bonding_mode.dart | 13 + .../lib/src/models.dart | 2 + .../src/reactive_ble_platform_interface.dart | 11 + 35 files changed, 1190 insertions(+), 332 deletions(-) create mode 100644 packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt create mode 100644 packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt create mode 100644 packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/BondingMode.kt create mode 100644 packages/reactive_ble_platform_interface/lib/src/model/association_info.dart create mode 100644 packages/reactive_ble_platform_interface/lib/src/model/association_info.g.dart create mode 100644 packages/reactive_ble_platform_interface/lib/src/model/bonding_mode.dart diff --git a/README.md b/README.md index 5901ae35..e8eb3f15 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ To update the kotlin version open Android studio and go to `Tools > Kotlin > Con The reactive BLE lib supports the following: - BLE device discovery +- Permission-less connection on Android using the `CompanionDeviceManager` framework. - Observe host device BLE status - Establishing a BLE connection - Maintaining connection status of multiple BLE devices diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 498d1b66..0e4d7063 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 31 + compileSdkVersion 33 sourceSets { main.java.srcDirs += 'src/main/kotlin' diff --git a/example/android/gradle/wrapper/gradle-wrapper.jar b/example/android/gradle/wrapper/gradle-wrapper.jar index 13372aef5e24af05341d49695ee84e5f9b594659..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL literal 53636 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ⅅ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 H-+%o-(+5aQ diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index b8793d3c..ffed3a25 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/example/android/gradlew b/example/android/gradlew index 9d82f789..1b6c7873 100755 --- a/example/android/gradlew +++ b/example/android/gradlew @@ -1,74 +1,129 @@ -#!/usr/bin/env bash +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -77,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -85,76 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/example/android/gradlew.bat b/example/android/gradlew.bat index aec99730..ac1b06f9 100644 --- a/example/android/gradlew.bat +++ b/example/android/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -8,20 +24,23 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,34 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/packages/flutter_reactive_ble/lib/src/device_connector.dart b/packages/flutter_reactive_ble/lib/src/device_connector.dart index 88e2a4fd..b0dc6964 100644 --- a/packages/flutter_reactive_ble/lib/src/device_connector.dart +++ b/packages/flutter_reactive_ble/lib/src/device_connector.dart @@ -10,6 +10,7 @@ abstract class DeviceConnector { required String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, + BondingMode? bondingMode, }); Stream connectToAdvertisingDevice({ @@ -53,6 +54,7 @@ class DeviceConnectorImpl implements DeviceConnector { required String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, + BondingMode? bondingMode, }) { final specificConnectedDeviceStream = deviceConnectionStateUpdateStream .where((update) => update.deviceId == id) @@ -69,6 +71,7 @@ class DeviceConnectorImpl implements DeviceConnector { id, servicesWithCharacteristicsToDiscover, connectionTimeout, + bondingMode, ) .asyncExpand((_) => specificConnectedDeviceStream), onCancel: () => _blePlatform.disconnectDevice(id), diff --git a/packages/flutter_reactive_ble/lib/src/device_scanner.dart b/packages/flutter_reactive_ble/lib/src/device_scanner.dart index 441a6265..4baa001b 100644 --- a/packages/flutter_reactive_ble/lib/src/device_scanner.dart +++ b/packages/flutter_reactive_ble/lib/src/device_scanner.dart @@ -5,6 +5,12 @@ import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; abstract class DeviceScanner { ScanSession? get currentScan; + Future launchCompanionWorkflow({ + required String pattern, + required bool singleDeviceScan, + required bool forceConfirmation, + }); + Stream scanForDevices({ required List withServices, ScanMode scanMode = ScanMode.balanced, @@ -38,6 +44,18 @@ class DeviceScannerImpl implements DeviceScanner { @override ScanSession? get currentScan => _currentScanSession; + @override + Future launchCompanionWorkflow({ + required String pattern, + required bool singleDeviceScan, + required bool forceConfirmation, + }) async => + _blePlatform.launchCompanionWorkflow( + pattern: pattern, + singleDeviceScan: singleDeviceScan, + forceConfirmation: forceConfirmation, + ); + @override Stream scanForDevices({ required List withServices, diff --git a/packages/flutter_reactive_ble/lib/src/reactive_ble.dart b/packages/flutter_reactive_ble/lib/src/reactive_ble.dart index 9bf00d14..b4cec8f3 100644 --- a/packages/flutter_reactive_ble/lib/src/reactive_ble.dart +++ b/packages/flutter_reactive_ble/lib/src/reactive_ble.dart @@ -217,6 +217,20 @@ class FlutterReactiveBle { deviceId, priority); } + Future launchCompanionWorkflow({ + required String pattern, + bool singleDeviceScan = true, + bool forceConfirmation = false, + }) async { + await initialize(); + + return _deviceScanner.launchCompanionWorkflow( + pattern: pattern, + singleDeviceScan: singleDeviceScan, + forceConfirmation: forceConfirmation, + ); + } + /// Scan for BLE peripherals advertising the services specified in [withServices] /// or for all BLE peripherals, if no services is specified. It is recommended to always specify some services. /// @@ -256,6 +270,7 @@ class FlutterReactiveBle { required String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, + BondingMode? bondingMode, }) => initialize().asStream().asyncExpand( (_) => _deviceConnector.connect( @@ -263,6 +278,7 @@ class FlutterReactiveBle { servicesWithCharacteristicsToDiscover: servicesWithCharacteristicsToDiscover, connectionTimeout: connectionTimeout, + bondingMode: bondingMode, ), ); diff --git a/packages/flutter_reactive_ble/pubspec.yaml b/packages/flutter_reactive_ble/pubspec.yaml index af7dedf8..226080f3 100644 --- a/packages/flutter_reactive_ble/pubspec.yaml +++ b/packages/flutter_reactive_ble/pubspec.yaml @@ -30,4 +30,9 @@ dev_dependencies: sdk: flutter functional_data_generator: ^1.1.2 mockito: ^5.0.14 - + +dependency_overrides: + reactive_ble_mobile: + path: ../reactive_ble_mobile + reactive_ble_platform_interface: + path: ../reactive_ble_platform_interface diff --git a/packages/reactive_ble_mobile/android/build.gradle b/packages/reactive_ble_mobile/android/build.gradle index ada934a7..cd082abd 100644 --- a/packages/reactive_ble_mobile/android/build.gradle +++ b/packages/reactive_ble_mobile/android/build.gradle @@ -38,7 +38,7 @@ apply plugin: "io.gitlab.arturbosch.detekt" apply plugin: "de.mannodermaus.android-junit5" android { - compileSdkVersion 31 + compileSdkVersion 33 sourceSets { main.java.srcDirs += 'src/main/kotlin' test.java.srcDirs += 'src/test/kotlin' diff --git a/packages/reactive_ble_mobile/android/src/main/AndroidManifest.xml b/packages/reactive_ble_mobile/android/src/main/AndroidManifest.xml index 361b2281..c18bc1ae 100644 --- a/packages/reactive_ble_mobile/android/src/main/AndroidManifest.xml +++ b/packages/reactive_ble_mobile/android/src/main/AndroidManifest.xml @@ -8,4 +8,5 @@ + diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt index d23a4ccb..384cf260 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt @@ -1,9 +1,14 @@ package com.signify.hue.flutterreactiveble +import android.app.Activity import android.content.Context +import android.content.Intent +import android.os.Build +import android.util.Log import com.signify.hue.flutterreactiveble.ble.RequestConnectionPriorityFailed import com.signify.hue.flutterreactiveble.channelhandlers.BleStatusHandler import com.signify.hue.flutterreactiveble.channelhandlers.CharNotificationHandler +import com.signify.hue.flutterreactiveble.channelhandlers.CompanionHandler import com.signify.hue.flutterreactiveble.channelhandlers.DeviceConnectionHandler import com.signify.hue.flutterreactiveble.channelhandlers.ScanDevicesHandler import com.signify.hue.flutterreactiveble.converters.ProtobufMessageConverter @@ -15,28 +20,34 @@ import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel.Result +import io.flutter.plugin.common.PluginRegistry import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import java.util.UUID import com.signify.hue.flutterreactiveble.ProtobufModel as pb @Suppress("TooManyFunctions") -class PluginController { +class PluginController : PluginRegistry.ActivityResultListener { + companion object { + private const val TAG = "PluginController" + } + private val pluginMethods = mapOf Unit>( - "initialize" to this::initializeClient, - "deinitialize" to this::deinitializeClient, - "scanForDevices" to this::scanForDevices, - "connectToDevice" to this::connectToDevice, - "clearGattCache" to this::clearGattCache, - "disconnectFromDevice" to this::disconnectFromDevice, - "readCharacteristic" to this::readCharacteristic, - "writeCharacteristicWithResponse" to this::writeCharacteristicWithResponse, - "writeCharacteristicWithoutResponse" to this::writeCharacteristicWithoutResponse, - "readNotifications" to this::readNotifications, - "stopNotifications" to this::stopNotifications, - "negotiateMtuSize" to this::negotiateMtuSize, - "requestConnectionPriority" to this::requestConnectionPriority, - "discoverServices" to this::discoverServices + "initialize" to this::initializeClient, + "deinitialize" to this::deinitializeClient, + "launchCompanionWorkflow" to this::launchCompanionFlow, + "scanForDevices" to this::scanForDevices, + "connectToDevice" to this::connectToDevice, + "clearGattCache" to this::clearGattCache, + "disconnectFromDevice" to this::disconnectFromDevice, + "readCharacteristic" to this::readCharacteristic, + "writeCharacteristicWithResponse" to this::writeCharacteristicWithResponse, + "writeCharacteristicWithoutResponse" to this::writeCharacteristicWithoutResponse, + "readNotifications" to this::readNotifications, + "stopNotifications" to this::stopNotifications, + "negotiateMtuSize" to this::negotiateMtuSize, + "requestConnectionPriority" to this::requestConnectionPriority, + "discoverServices" to this::discoverServices ) lateinit var bleClient: com.signify.hue.flutterreactiveble.ble.BleClient @@ -45,6 +56,7 @@ class PluginController { lateinit var deviceConnectionChannel: EventChannel lateinit var charNotificationChannel: EventChannel + lateinit var companionHandler: CompanionHandler lateinit var scandevicesHandler: ScanDevicesHandler lateinit var deviceConnectionHandler: DeviceConnectionHandler lateinit var charNotificationHandler: CharNotificationHandler @@ -60,6 +72,7 @@ class PluginController { charNotificationChannel = EventChannel(messenger, "flutter_reactive_ble_char_update") val bleStatusChannel = EventChannel(messenger, "flutter_reactive_ble_status") + companionHandler = CompanionHandler(context, bleClient) scandevicesHandler = ScanDevicesHandler(bleClient) deviceConnectionHandler = DeviceConnectionHandler(bleClient) charNotificationHandler = CharNotificationHandler(bleClient) @@ -90,6 +103,17 @@ class PluginController { result.success(null) } + private fun launchCompanionFlow(call: MethodCall, result: Result) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + companionHandler.launchCompanionFlow( + pb.LaunchCompanionRequest.parseFrom(call.arguments as ByteArray), + result + ) + } else { + result.error("NOT_SUPPORTED", "Companion flow is only supported on Android Oreo and above", null) + } + } + private fun scanForDevices(call: MethodCall, result: Result) { scandevicesHandler.prepareScan(pb.ScanForDevicesRequest.parseFrom(call.arguments as ByteArray)) result.success(null) @@ -104,26 +128,27 @@ class PluginController { private fun clearGattCache(call: MethodCall, result: Result) { val args = pb.ClearGattCacheRequest.parseFrom(call.arguments as ByteArray) bleClient.clearGattCache(args.deviceId) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { - val info = pb.ClearGattCacheInfo.getDefaultInstance() - result.success(info.toByteArray()) - }, - { - val info = protoConverter.convertClearGattCacheError( - ClearGattCacheErrorType.UNKNOWN, - it.message - ) - result.success(info.toByteArray()) - } - ) - .discard() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + val info = pb.ClearGattCacheInfo.getDefaultInstance() + result.success(info.toByteArray()) + }, + { + val info = protoConverter.convertClearGattCacheError( + ClearGattCacheErrorType.UNKNOWN, + it.message + ) + result.success(info.toByteArray()) + } + ) + .discard() } private fun disconnectFromDevice(call: MethodCall, result: Result) { result.success(null) - val connectDeviceMessage = pb.DisconnectFromDeviceRequest.parseFrom(call.arguments as ByteArray) + val connectDeviceMessage = + pb.DisconnectFromDeviceRequest.parseFrom(call.arguments as ByteArray) deviceConnectionHandler.disconnectDevice(connectDeviceMessage.deviceId) } @@ -132,84 +157,113 @@ class PluginController { val readCharMessage = pb.ReadCharacteristicRequest.parseFrom(call.arguments as ByteArray) val deviceId = readCharMessage.characteristic.deviceId - val characteristic = uuidConverter.uuidFromByteArray(readCharMessage.characteristic.characteristicUuid.data.toByteArray()) + val characteristic = + uuidConverter.uuidFromByteArray(readCharMessage.characteristic.characteristicUuid.data.toByteArray()) bleClient.readCharacteristic( - readCharMessage.characteristic.deviceId, characteristic + readCharMessage.characteristic.deviceId, characteristic ) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { charResult -> - when (charResult) { - is com.signify.hue.flutterreactiveble.ble.CharOperationSuccessful -> { - val charInfo = protoConverter.convertCharacteristicInfo( - readCharMessage.characteristic, - charResult.value.toByteArray() - ) - charNotificationHandler.addSingleReadToStream(charInfo) - } - is com.signify.hue.flutterreactiveble.ble.CharOperationFailed -> { - protoConverter.convertCharacteristicError(readCharMessage.characteristic, - "Failed to connect") - charNotificationHandler.addSingleErrorToStream( - readCharMessage.characteristic, - charResult.errorMessage - ) - } - } - }, - { throwable -> + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { charResult -> + when (charResult) { + is com.signify.hue.flutterreactiveble.ble.CharOperationSuccessful -> { + val charInfo = protoConverter.convertCharacteristicInfo( + readCharMessage.characteristic, + charResult.value.toByteArray() + ) + charNotificationHandler.addSingleReadToStream(charInfo) + } + + is com.signify.hue.flutterreactiveble.ble.CharOperationFailed -> { protoConverter.convertCharacteristicError( - readCharMessage.characteristic, - throwable.message) + readCharMessage.characteristic, + "Failed to connect" + ) charNotificationHandler.addSingleErrorToStream( - readCharMessage.characteristic, - throwable?.message ?: "Failure") + readCharMessage.characteristic, + charResult.errorMessage + ) } - ) - .discard() + } + }, + { throwable -> + protoConverter.convertCharacteristicError( + readCharMessage.characteristic, + throwable.message + ) + charNotificationHandler.addSingleErrorToStream( + readCharMessage.characteristic, + throwable?.message ?: "Failure" + ) + } + ) + .discard() } private fun writeCharacteristicWithResponse(call: MethodCall, result: Result) { - executeWriteAndPropagateResultToChannel(call, result, com.signify.hue.flutterreactiveble.ble.BleClient::writeCharacteristicWithResponse) + executeWriteAndPropagateResultToChannel( + call, + result, + com.signify.hue.flutterreactiveble.ble.BleClient::writeCharacteristicWithResponse + ) } private fun writeCharacteristicWithoutResponse(call: MethodCall, result: Result) { - executeWriteAndPropagateResultToChannel(call, result, com.signify.hue.flutterreactiveble.ble.BleClient::writeCharacteristicWithoutResponse) + executeWriteAndPropagateResultToChannel( + call, + result, + com.signify.hue.flutterreactiveble.ble.BleClient::writeCharacteristicWithoutResponse + ) } private fun executeWriteAndPropagateResultToChannel( - call: MethodCall, - result: Result, - writeOperation: com.signify.hue.flutterreactiveble.ble.BleClient.( - deviceId: String, - characteristic: UUID, - value: ByteArray - ) -> Single + call: MethodCall, + result: Result, + writeOperation: com.signify.hue.flutterreactiveble.ble.BleClient.( + deviceId: String, + characteristic: UUID, + value: ByteArray + ) -> Single ) { val writeCharMessage = pb.WriteCharacteristicRequest.parseFrom(call.arguments as ByteArray) - bleClient.writeOperation(writeCharMessage.characteristic.deviceId, - uuidConverter.uuidFromByteArray(writeCharMessage.characteristic.characteristicUuid.data.toByteArray()), - writeCharMessage.value.toByteArray()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ operationResult -> - when (operationResult) { - is com.signify.hue.flutterreactiveble.ble.CharOperationSuccessful -> { - result.success(protoConverter.convertWriteCharacteristicInfo(writeCharMessage, - null).toByteArray()) - } - is com.signify.hue.flutterreactiveble.ble.CharOperationFailed -> { - result.success(protoConverter.convertWriteCharacteristicInfo(writeCharMessage, - operationResult.errorMessage).toByteArray()) - } + bleClient.writeOperation( + writeCharMessage.characteristic.deviceId, + uuidConverter.uuidFromByteArray(writeCharMessage.characteristic.characteristicUuid.data.toByteArray()), + writeCharMessage.value.toByteArray() + ) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ operationResult -> + when (operationResult) { + is com.signify.hue.flutterreactiveble.ble.CharOperationSuccessful -> { + result.success( + protoConverter.convertWriteCharacteristicInfo( + writeCharMessage, + null + ).toByteArray() + ) } - }, - { throwable -> - result.success(protoConverter.convertWriteCharacteristicInfo(writeCharMessage, - throwable.message).toByteArray()) - } - ) - .discard() + + is com.signify.hue.flutterreactiveble.ble.CharOperationFailed -> { + result.success( + protoConverter.convertWriteCharacteristicInfo( + writeCharMessage, + operationResult.errorMessage + ).toByteArray() + ) + } + } + }, + { throwable -> + result.success( + protoConverter.convertWriteCharacteristicInfo( + writeCharMessage, + throwable.message + ).toByteArray() + ) + } + ) + .discard() } private fun readNotifications(call: MethodCall, result: Result) { @@ -227,44 +281,87 @@ class PluginController { private fun negotiateMtuSize(call: MethodCall, result: Result) { val request = pb.NegotiateMtuRequest.parseFrom(call.arguments as ByteArray) bleClient.negotiateMtuSize(request.deviceId, request.mtuSize) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ mtuResult -> - result.success(protoConverter.convertNegotiateMtuInfo(mtuResult).toByteArray()) - }, { throwable -> - result.success(protoConverter.convertNegotiateMtuInfo(com.signify.hue.flutterreactiveble.ble.MtuNegotiateFailed(request.deviceId, - throwable.message ?: "")).toByteArray()) - } + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ mtuResult -> + result.success(protoConverter.convertNegotiateMtuInfo(mtuResult).toByteArray()) + }, { throwable -> + result.success( + protoConverter.convertNegotiateMtuInfo( + com.signify.hue.flutterreactiveble.ble.MtuNegotiateFailed( + request.deviceId, + throwable.message ?: "" + ) + ).toByteArray() ) - .discard() + } + ) + .discard() } private fun requestConnectionPriority(call: MethodCall, result: Result) { val request = pb.ChangeConnectionPriorityRequest.parseFrom(call.arguments as ByteArray) - bleClient.requestConnectionPriority(request.deviceId, request.priority.toConnectionPriority()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ requestResult -> - result.success(protoConverter - .convertRequestConnectionPriorityInfo(requestResult).toByteArray()) - }, - { throwable -> - result.success(protoConverter.convertRequestConnectionPriorityInfo( - RequestConnectionPriorityFailed(request.deviceId, throwable?.message - ?: "Unknown error")).toByteArray()) - }) - .discard() + bleClient.requestConnectionPriority( + request.deviceId, + request.priority.toConnectionPriority() + ) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ requestResult -> + result.success( + protoConverter + .convertRequestConnectionPriorityInfo(requestResult).toByteArray() + ) + }, + { throwable -> + result.success( + protoConverter.convertRequestConnectionPriorityInfo( + RequestConnectionPriorityFailed( + request.deviceId, throwable?.message + ?: "Unknown error" + ) + ).toByteArray() + ) + }) + .discard() } private fun discoverServices(call: MethodCall, result: Result) { val request = pb.DiscoverServicesRequest.parseFrom(call.arguments as ByteArray) bleClient.discoverServices(request.deviceId) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ discoverResult -> - result.success(protoConverter.convertDiscoverServicesInfo(request.deviceId, discoverResult).toByteArray()) - }, { - throwable -> result.error("service_discovery_failure", throwable.message, null) - }) - .discard() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ discoverResult -> + result.success( + protoConverter.convertDiscoverServicesInfo( + request.deviceId, + discoverResult + ).toByteArray() + ) + }, { throwable -> + result.error("service_discovery_failure", throwable.message, null) + }) + .discard() + } + + fun setActivity(activity: Activity?) { + companionHandler.setActivity(activity) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { + if (requestCode == CompanionHandler.SELECT_DEVICE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + Log.d(TAG, "received result from device selection activity, parsing via companion handler") + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + Log.d(TAG, "not running on Android O, ignoring result") + return false; + } + + Log.d(TAG, "running on Android O, parsing result") + + val scanResult = companionHandler.onActivityResult(data) ?: return false +Log.d(TAG, "got the scan result: ${scanResult.device.address}") + } + + return false } } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ReactiveBlePlugin.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ReactiveBlePlugin.kt index 9c55357b..92ae6eaa 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ReactiveBlePlugin.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ReactiveBlePlugin.kt @@ -2,12 +2,14 @@ package com.signify.hue.flutterreactiveble import android.content.Context import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.activity.ActivityAware +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.Result -class ReactiveBlePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { +class ReactiveBlePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware { override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { initializePlugin(binding.binaryMessenger, binding.applicationContext, this) } @@ -40,4 +42,24 @@ class ReactiveBlePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { override fun onMethodCall(call: MethodCall, result: Result) { pluginController.execute(call, result) } + + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + binding.addActivityResultListener(pluginController) + pluginController.setActivity(binding.activity) + } + + override fun onDetachedFromActivityForConfigChanges() { + pluginController.setActivity(null) + } + + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + binding.addActivityResultListener(pluginController) + pluginController.setActivity(binding.activity) + } + + override fun onDetachedFromActivity() { + pluginController.setActivity(null) + } + + } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt index 05b7d720..17b7968f 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt @@ -2,6 +2,7 @@ package com.signify.hue.flutterreactiveble.ble import android.os.ParcelUuid import com.polidea.rxandroidble2.RxBleDeviceServices +import com.signify.hue.flutterreactiveble.model.BondingMode import com.signify.hue.flutterreactiveble.model.ScanMode import com.signify.hue.flutterreactiveble.utils.Duration import io.reactivex.Completable @@ -17,7 +18,7 @@ interface BleClient { fun initializeClient() fun scanForDevices(services: List, scanMode: ScanMode, requireLocationServicesEnabled: Boolean): Observable - fun connectToDevice(deviceId: String, timeout: Duration) + fun connectToDevice(deviceId: String, timeout: Duration, bondingMode: BondingMode) fun disconnectDevice(deviceId: String) fun disconnectAllDevices() fun discoverServices(deviceId: String): Single diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt new file mode 100644 index 00000000..48df26b8 --- /dev/null +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt @@ -0,0 +1,104 @@ +package com.signify.hue.flutterreactiveble.ble + +import android.annotation.SuppressLint +import android.bluetooth.BluetoothDevice +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.BroadcastReceiver +import android.os.Build +import android.util.Log +import com.polidea.rxandroidble2.RxBleDevice +import com.signify.hue.flutterreactiveble.model.BondingMode +import io.reactivex.Completable +import io.reactivex.disposables.Disposables + +/** + * Created by clement on 2017-08-24. + */ + +class BondingFailedException : RuntimeException() + +object BondingManager { + const val TAG = "CompanionHandler" + + /** + * @throws BondingFailedException + */ + // TODO: try to understand why the popup is being displayed in the notification center (sometimes) ???!!! + @SuppressLint("MissingPermission") + fun bondWithDevice( + context: Context, + rxBleDevice: RxBleDevice, + bondingMode: BondingMode + ): Completable { + if (bondingMode == BondingMode.NONE) { + return Completable.complete() + } + + return Completable.create { completion -> + Log.d(TAG, "pairWithDevice: ${rxBleDevice.bluetoothDevice.bondState}") + when (rxBleDevice.bluetoothDevice.bondState) { + BluetoothDevice.BOND_BONDED -> completion.onComplete() + else -> { + + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val deviceBeingPaired: BluetoothDevice? = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java) + } else { + @Suppress("DEPRECATION") + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) + } + + Log.d(TAG, "pairWithDevice: deviceBeingPaired: $deviceBeingPaired") + + if (deviceBeingPaired?.address == rxBleDevice.bluetoothDevice.address) { + val state = intent.getIntExtra( + BluetoothDevice.EXTRA_BOND_STATE, + BluetoothDevice.BOND_NONE + ) + + Log.d(TAG, "pairWithDevice: state: $state") + + when (state) { + BluetoothDevice.BOND_BONDED -> completion.onComplete() + BluetoothDevice.BOND_NONE -> + // When bonding is required, we throw an exception if the bonding fails. + if (bondingMode == BondingMode.REQUIRED) { + completion.tryOnError(BondingFailedException()) + } else { // BondingMode.NOT_REQUIRED + completion.onComplete() + } + } + } + } + } + + completion.setDisposable(Disposables.fromAction { + context.unregisterReceiver( + receiver + ) + }) + + context.registerReceiver( + receiver, + IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED) + ) + + val createBondResult = rxBleDevice.bluetoothDevice.createBond() + + Log.d(TAG, "pairWithDevice: createBondResult: $createBondResult") + + if (!createBondResult) { + completion.tryOnError(BondingFailedException()) + } + + } + } + + } + + } +} diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt index 32da5ea3..79413f85 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt @@ -1,9 +1,11 @@ package com.signify.hue.flutterreactiveble.ble +import android.content.Context import androidx.annotation.VisibleForTesting import com.polidea.rxandroidble2.RxBleConnection import com.polidea.rxandroidble2.RxBleCustomOperation import com.polidea.rxandroidble2.RxBleDevice +import com.signify.hue.flutterreactiveble.model.BondingMode import com.signify.hue.flutterreactiveble.model.ConnectionState import com.signify.hue.flutterreactiveble.model.toConnectionState import com.signify.hue.flutterreactiveble.utils.Duration @@ -16,10 +18,12 @@ import io.reactivex.subjects.BehaviorSubject import java.util.concurrent.TimeUnit internal class DeviceConnector( - private val device: RxBleDevice, - private val connectionTimeout: Duration, - private val updateListeners: (update: ConnectionUpdate) -> Unit, - private val connectionQueue: ConnectionQueue + private val context: Context, + private val device: RxBleDevice, + private val connectionTimeout: Duration, + private val updateListeners: (update: ConnectionUpdate) -> Unit, + private val connectionQueue: ConnectionQueue, + private val bondingMode: BondingMode = BondingMode.NONE, ) { companion object { @@ -46,15 +50,22 @@ internal class DeviceConnector( private val connectionStatusUpdates by lazy { device.observeConnectionStateChanges() - .startWith(device.connectionState) - .map { ConnectionUpdateSuccess(device.macAddress, it.toConnectionState().code) } - .onErrorReturn { - ConnectionUpdateError(device.macAddress, it.message - ?: "Unknown error") - } - .subscribe { - updateListeners.invoke(it) - } + .startWith(device.connectionState) + .map { + ConnectionUpdateSuccess( + device.macAddress, + it.toConnectionState().code + ) + } + .onErrorReturn { + ConnectionUpdateError( + device.macAddress, it.message + ?: "Unknown error" + ) + } + .subscribe { + updateListeners.invoke(it) + } } internal fun disconnectDevice(deviceId: String) { @@ -65,11 +76,14 @@ internal class DeviceConnector( disconnect to quickly after establishing connection. https://issuetracker.google.com/issues/37121223 */ if (diff < DeviceConnector.Companion.minTimeMsBeforeDisconnectingIsAllowed) { - Single.timer(DeviceConnector.Companion.minTimeMsBeforeDisconnectingIsAllowed - diff, TimeUnit.MILLISECONDS) - .doFinally { - sendDisconnectedUpdate(deviceId) - disposeSubscriptions() - }.subscribe() + Single.timer( + DeviceConnector.Companion.minTimeMsBeforeDisconnectingIsAllowed - diff, + TimeUnit.MILLISECONDS + ) + .doFinally { + sendDisconnectedUpdate(deviceId) + disposeSubscriptions() + }.subscribe() } else { sendDisconnectedUpdate(deviceId) disposeSubscriptions() @@ -94,52 +108,75 @@ internal class DeviceConnector( updateListeners(ConnectionUpdateSuccess(deviceId, ConnectionState.CONNECTING.code)) return waitUntilFirstOfQueue(deviceId) - .switchMap { queue -> - if (!queue.contains(deviceId)) { - Observable.just(EstablishConnectionFailure(deviceId, - "Device is not in queue")) - } else { - connectDevice(rxBleDevice, shouldNotTimeout) - .map { EstablishedConnection(rxBleDevice.macAddress, it) } - } - } - .onErrorReturn { error -> - EstablishConnectionFailure(rxBleDevice.macAddress, - error.message ?: "Unknown error") - } - .doOnNext { - // Trigger side effect by calling the lazy initialization of this property so - // listening to changes starts. - connectionStatusUpdates - timestampEstablishConnection = System.currentTimeMillis() - connectionQueue.removeFromQueue(deviceId) - if (it is EstablishConnectionFailure) { - updateListeners.invoke(ConnectionUpdateError(deviceId, it.errorMessage)) - } + .switchMap { queue -> + if (!queue.contains(deviceId)) { + Observable.just( + EstablishConnectionFailure( + deviceId, + "Device is not in queue" + ) + ) + } else { + connectDevice(rxBleDevice, shouldNotTimeout, bondingMode) + .map { + EstablishedConnection( + rxBleDevice.macAddress, + it + ) + } } - .doOnError { - connectionQueue.removeFromQueue(deviceId) - updateListeners.invoke(ConnectionUpdateError(deviceId, it.message - ?: "Unknown error")) + } + .onErrorReturn { error -> + EstablishConnectionFailure( + rxBleDevice.macAddress, + error.message ?: "Unknown error" + ) + } + .doOnNext { + // Trigger side effect by calling the lazy initialization of this property so + // listening to changes starts. + connectionStatusUpdates + timestampEstablishConnection = System.currentTimeMillis() + connectionQueue.removeFromQueue(deviceId) + if (it is EstablishConnectionFailure) { + updateListeners.invoke(ConnectionUpdateError(deviceId, it.errorMessage)) } - .subscribe({ connectDeviceSubject.onNext(it) }, - { throwable -> connectDeviceSubject.onError(throwable) }) + } + .doOnError { + connectionQueue.removeFromQueue(deviceId) + updateListeners.invoke( + ConnectionUpdateError( + deviceId, it.message + ?: "Unknown error" + ) + ) + } + .subscribe({ connectDeviceSubject.onNext(it) }, + { throwable -> connectDeviceSubject.onError(throwable) }) } - private fun connectDevice(rxBleDevice: RxBleDevice, shouldNotTimeout: Boolean): Observable = - rxBleDevice.establishConnection(shouldNotTimeout) + private fun connectDevice( + rxBleDevice: RxBleDevice, + shouldNotTimeout: Boolean, + bondingMode: BondingMode, + ): Observable = + BondingManager.bondWithDevice(this.context, rxBleDevice, bondingMode) + .andThen( + rxBleDevice.establishConnection(shouldNotTimeout) + .compose { if (shouldNotTimeout) { it } else { it.timeout( - Observable.timer(connectionTimeout.value, connectionTimeout.unit), - Function> { - Observable.never() - } + Observable.timer(connectionTimeout.value, connectionTimeout.unit), + Function> { + Observable.never() + } ) } } + ) internal fun clearGattCache(): Completable = currentConnection?.let { connection -> when (connection) { @@ -166,7 +203,10 @@ internal class DeviceConnector( val success = refreshMethod.invoke(bluetoothGatt) as Boolean if (success) { Observable.empty() - .delay(DeviceConnector.Companion.delayMsAfterClearingCache, TimeUnit.MILLISECONDS) + .delay( + DeviceConnector.Companion.delayMsAfterClearingCache, + TimeUnit.MILLISECONDS + ) } else { val reason = "BluetoothGatt.refresh() returned false" Observable.error(RuntimeException(reason)) @@ -179,9 +219,9 @@ internal class DeviceConnector( } private fun waitUntilFirstOfQueue(deviceId: String) = - connectionQueue.observeQueue() - .filter { queue -> - queue.firstOrNull() == deviceId || !queue.contains(deviceId) - } - .takeUntil { it.isEmpty() || it.first() == deviceId } + connectionQueue.observeQueue() + .filter { queue -> + queue.firstOrNull() == deviceId || !queue.contains(deviceId) + } + .takeUntil { it.isEmpty() || it.first() == deviceId } } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt index 4c10e9ef..6e5aa8df 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt @@ -19,6 +19,7 @@ import com.polidea.rxandroidble2.scan.ScanSettings import com.signify.hue.flutterreactiveble.ble.extensions.writeCharWithResponse import com.signify.hue.flutterreactiveble.ble.extensions.writeCharWithoutResponse import com.signify.hue.flutterreactiveble.converters.extractManufacturerData +import com.signify.hue.flutterreactiveble.model.BondingMode import com.signify.hue.flutterreactiveble.model.ScanMode import com.signify.hue.flutterreactiveble.model.toScanSettings import com.signify.hue.flutterreactiveble.utils.Duration @@ -97,8 +98,8 @@ open class ReactiveBleClient(private val context: Context) : BleClient { } } - override fun connectToDevice(deviceId: String, timeout: Duration) { - allConnections.add(getConnection(deviceId, timeout) + override fun connectToDevice(deviceId: String, timeout: Duration, bondingMode: BondingMode) { + allConnections.add(getConnection(deviceId, timeout, bondingMode) .subscribe({ result -> when (result) { is EstablishedConnection -> { @@ -240,16 +241,17 @@ open class ReactiveBleClient(private val context: Context) : BleClient { .map { it.toBleState() } @VisibleForTesting - internal open fun createDeviceConnector(device: RxBleDevice, timeout: Duration) = - DeviceConnector(device, timeout, connectionUpdateBehaviorSubject::onNext, connectionQueue) + internal open fun createDeviceConnector(device: RxBleDevice, timeout: Duration, bondingMode: BondingMode) = + DeviceConnector(context, device, timeout, connectionUpdateBehaviorSubject::onNext, connectionQueue, bondingMode) private fun getConnection( deviceId: String, - timeout: Duration = Duration(0, TimeUnit.MILLISECONDS) + timeout: Duration = Duration(0, TimeUnit.MILLISECONDS), + bondingMode: BondingMode = BondingMode.NONE, ): Observable { val device = rxBleClient.getBleDevice(deviceId) val connector = - activeConnections.getOrPut(deviceId) { createDeviceConnector(device, timeout) } + activeConnections.getOrPut(deviceId) { createDeviceConnector(device, timeout, bondingMode) } return connector.connection } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt new file mode 100644 index 00000000..661e9f4d --- /dev/null +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt @@ -0,0 +1,140 @@ +package com.signify.hue.flutterreactiveble.channelhandlers + +import android.app.Activity +import android.bluetooth.le.ScanResult +import android.companion.AssociationInfo +import android.companion.AssociationRequest +import android.companion.BluetoothLeDeviceFilter +import android.companion.CompanionDeviceManager +import android.content.Context +import android.content.Intent +import android.content.IntentSender +import android.os.Build +import android.util.Log +import androidx.annotation.RequiresApi +import com.signify.hue.flutterreactiveble.ProtobufModel +import com.signify.hue.flutterreactiveble.ble.BleClient +import io.flutter.plugin.common.MethodChannel +import java.lang.ref.WeakReference +import java.util.concurrent.Executor +import java.util.regex.Pattern + +class CompanionHandler(private val context: Context, private val bleClient: BleClient) { + companion object { + const val SELECT_DEVICE_REQUEST_CODE = 426312 + const val TAG = "CompanionHandler" + } + + private var activity = WeakReference(null) + + @RequiresApi(Build.VERSION_CODES.O) + fun launchCompanionFlow( + parseFrom: ProtobufModel.LaunchCompanionRequest, + result: MethodChannel.Result + ) { + val deviceFilter: BluetoothLeDeviceFilter = BluetoothLeDeviceFilter.Builder() + .setNamePattern(Pattern.compile(parseFrom.pattern)) + .build() + + val pairingRequestBuilder = AssociationRequest.Builder() + // Find only devices that match this request filter. + .addDeviceFilter(deviceFilter) + // Stop scanning as soon as one device matching the filter is found. + .setSingleDevice(parseFrom.singleDeviceScan) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + pairingRequestBuilder.setForceConfirmation(parseFrom.forceConfirmation) + } + + val deviceManager = + context.getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager + + val executor = Executor { it.run() } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + deviceManager.associate(pairingRequestBuilder.build(), + executor, + object : CompanionDeviceManager.Callback() { + // // Called when a device is found. Launch the IntentSender so the user + // // can select the device they want to pair with. + override fun onAssociationPending(intentSender: IntentSender) { + Log.d(TAG, "onAssociationPending: $intentSender") + intentSender.let { + activity.get()?.startIntentSenderForResult( + it, + SELECT_DEVICE_REQUEST_CODE, + null, + 0, + 0, + 0 + ) + } + } + + override fun onAssociationCreated(associationInfo: AssociationInfo) { + Log.d(TAG, "onAssociationCreated: $associationInfo") + + result.success( + ProtobufModel.AssociationInfo.newBuilder() + .setDeviceMacAddress( + associationInfo.deviceMacAddress!!.toString().uppercase() + ) + .build() + .toByteArray() + ) + associationInfo.deviceMacAddress + } + + override fun onFailure(errorMessage: CharSequence?) { + Log.e(TAG, "onFailure: $errorMessage") + result.error("CompanionHandler", errorMessage.toString(), null) + } + }) + } else { + deviceManager.associate( + pairingRequestBuilder.build(), + object : CompanionDeviceManager.Callback() { + @Deprecated("Deprecated in Java") + override fun onDeviceFound(chooserLauncher: IntentSender) { + activity.get()?.startIntentSenderForResult( + chooserLauncher, + SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0 + ) + } + + override fun onFailure(error: CharSequence?) { + result.error("CompanionHandler", error.toString(), null) + } + }, null + ) + } + } + + fun setActivity(activity: Activity?) { + this.activity = WeakReference(activity); + } + + @RequiresApi(Build.VERSION_CODES.O) + fun onActivityResult(data: Intent?): ScanResult? { + Log.d(TAG, "onActivityResult: $data") + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val associationInfo = data?.getParcelableExtra( + CompanionDeviceManager.EXTRA_ASSOCIATION, + AssociationInfo::class.java + ) + + Log.d(TAG, "onActivityResult: ${associationInfo?.id}") + Log.d(TAG, "onActivityResult: ${associationInfo?.displayName}") + Log.d(TAG, "onActivityResult: ${associationInfo?.deviceMacAddress}") + Log.d(TAG, "onActivityResult: ${associationInfo?.deviceProfile}") + + null + } else { + @Suppress("DEPRECATION") + val scanResult: ScanResult? = + data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE) + + null + } + } +} \ No newline at end of file diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt index 54ab690c..8aed2ca2 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt @@ -2,6 +2,7 @@ package com.signify.hue.flutterreactiveble.channelhandlers import com.signify.hue.flutterreactiveble.ProtobufModel as pb import com.signify.hue.flutterreactiveble.converters.ProtobufMessageConverter +import com.signify.hue.flutterreactiveble.model.BondingMode import com.signify.hue.flutterreactiveble.utils.Duration import io.flutter.plugin.common.EventChannel import io.reactivex.android.schedulers.AndroidSchedulers @@ -29,7 +30,8 @@ class DeviceConnectionHandler(private val bleClient: com.signify.hue.flutterreac fun connectToDevice(connectToDeviceMessage: pb.ConnectToDeviceRequest) { bleClient.connectToDevice( connectToDeviceMessage.deviceId, - Duration(connectToDeviceMessage.timeoutInMs.toLong(), TimeUnit.MILLISECONDS) + Duration(connectToDeviceMessage.timeoutInMs.toLong(), TimeUnit.MILLISECONDS), + BondingMode.values()[connectToDeviceMessage.bondingMode], ) } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/BondingMode.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/BondingMode.kt new file mode 100644 index 00000000..1ac092a8 --- /dev/null +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/BondingMode.kt @@ -0,0 +1,9 @@ +package com.signify.hue.flutterreactiveble.model + +import android.content.Context + +enum class BondingMode(val code : Int) { + NONE(0), + NOT_REQUIRED(1), + REQUIRED(2) +} \ No newline at end of file diff --git a/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart index 58584bef..a56a3dcf 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart @@ -7,6 +7,7 @@ abstract class ArgsToProtobufConverter { String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, + BondingMode? bondingMode, ); pb.DisconnectFromDeviceRequest createDisconnectDeviceArgs(String deviceId); @@ -38,6 +39,12 @@ abstract class ArgsToProtobufConverter { ConnectionPriority priority, ); + pb.LaunchCompanionRequest createLaunchCompanionWorkflowRequest({ + required String pattern, + required bool singleDeviceScan, + required bool forceConfirmation, + }); + pb.ScanForDevicesRequest createScanForDevicesRequest({ required List? withServices, required ScanMode scanMode, @@ -57,6 +64,7 @@ class ArgsToProtobufConverterImpl implements ArgsToProtobufConverter { String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, + BondingMode? bondingMode, ) { final args = pb.ConnectToDeviceRequest()..deviceId = id; @@ -79,6 +87,9 @@ class ArgsToProtobufConverterImpl implements ArgsToProtobufConverter { args.servicesWithCharacteristicsToDiscover = pb.ServicesWithCharacteristics()..items.addAll(items); } + + args.bondingMode = bondingMode?.index ?? BondingMode.none.index; + return args; } @@ -168,6 +179,17 @@ class ArgsToProtobufConverterImpl implements ArgsToProtobufConverter { return args; } + @override + pb.LaunchCompanionRequest createLaunchCompanionWorkflowRequest({ + required String pattern, + required bool singleDeviceScan, + required bool forceConfirmation, + }) => + pb.LaunchCompanionRequest() + ..pattern = pattern + ..singleDeviceScan = singleDeviceScan + ..forceConfirmation = forceConfirmation; + @override pb.ScanForDevicesRequest createScanForDevicesRequest({ required List? withServices, diff --git a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart index 4a84df7e..b7d0ae55 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart @@ -7,6 +7,8 @@ import '../select_from.dart'; abstract class ProtobufConverter { BleStatus bleStatusFrom(List data); + AssociationInfo associationInfoFrom(List data); + ScanResult scanResultFrom(List data); ConnectionStateUpdate connectionStateUpdateFrom(List data); @@ -40,6 +42,13 @@ class ProtobufConverterImpl implements ProtobufConverter { ); } + @override + AssociationInfo associationInfoFrom(List data) { + final pbVersion = pb.AssociationInfo.fromBuffer(data); + + return AssociationInfo(deviceMacAddress: pbVersion.deviceMacAddress); + } + @override ScanResult scanResultFrom(List data) { final message = pb.DeviceScanInfo.fromBuffer(data); diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart index 694a067a..c7a6f185 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart @@ -13,6 +13,110 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; +class LaunchCompanionRequest extends $pb.GeneratedMessage { + factory LaunchCompanionRequest() => create(); + LaunchCompanionRequest._() : super(); + factory LaunchCompanionRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory LaunchCompanionRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'LaunchCompanionRequest', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'pattern') + ..aOB(2, _omitFieldNames ? '' : 'singleDeviceScan', protoName: 'singleDeviceScan') + ..aOB(3, _omitFieldNames ? '' : 'forceConfirmation', protoName: 'forceConfirmation') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + LaunchCompanionRequest clone() => LaunchCompanionRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + LaunchCompanionRequest copyWith(void Function(LaunchCompanionRequest) updates) => super.copyWith((message) => updates(message as LaunchCompanionRequest)) as LaunchCompanionRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static LaunchCompanionRequest create() => LaunchCompanionRequest._(); + LaunchCompanionRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static LaunchCompanionRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static LaunchCompanionRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get pattern => $_getSZ(0); + @$pb.TagNumber(1) + set pattern($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasPattern() => $_has(0); + @$pb.TagNumber(1) + void clearPattern() => clearField(1); + + @$pb.TagNumber(2) + $core.bool get singleDeviceScan => $_getBF(1); + @$pb.TagNumber(2) + set singleDeviceScan($core.bool v) { $_setBool(1, v); } + @$pb.TagNumber(2) + $core.bool hasSingleDeviceScan() => $_has(1); + @$pb.TagNumber(2) + void clearSingleDeviceScan() => clearField(2); + + @$pb.TagNumber(3) + $core.bool get forceConfirmation => $_getBF(2); + @$pb.TagNumber(3) + set forceConfirmation($core.bool v) { $_setBool(2, v); } + @$pb.TagNumber(3) + $core.bool hasForceConfirmation() => $_has(2); + @$pb.TagNumber(3) + void clearForceConfirmation() => clearField(3); +} + +class AssociationInfo extends $pb.GeneratedMessage { + factory AssociationInfo() => create(); + AssociationInfo._() : super(); + factory AssociationInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory AssociationInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'AssociationInfo', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceMacAddress', protoName: 'deviceMacAddress') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + AssociationInfo clone() => AssociationInfo()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + AssociationInfo copyWith(void Function(AssociationInfo) updates) => super.copyWith((message) => updates(message as AssociationInfo)) as AssociationInfo; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AssociationInfo create() => AssociationInfo._(); + AssociationInfo createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static AssociationInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static AssociationInfo? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get deviceMacAddress => $_getSZ(0); + @$pb.TagNumber(1) + set deviceMacAddress($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasDeviceMacAddress() => $_has(0); + @$pb.TagNumber(1) + void clearDeviceMacAddress() => clearField(1); +} + class ScanForDevicesRequest extends $pb.GeneratedMessage { factory ScanForDevicesRequest() => create(); ScanForDevicesRequest._() : super(); @@ -183,6 +287,7 @@ class ConnectToDeviceRequest extends $pb.GeneratedMessage { ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') ..aOM(2, _omitFieldNames ? '' : 'servicesWithCharacteristicsToDiscover', protoName: 'servicesWithCharacteristicsToDiscover', subBuilder: ServicesWithCharacteristics.create) ..a<$core.int>(3, _omitFieldNames ? '' : 'timeoutInMs', $pb.PbFieldType.O3, protoName: 'timeoutInMs') + ..a<$core.int>(4, _omitFieldNames ? '' : 'bondingMode', $pb.PbFieldType.O3, protoName: 'bondingMode') ..hasRequiredFields = false ; @@ -235,6 +340,15 @@ class ConnectToDeviceRequest extends $pb.GeneratedMessage { $core.bool hasTimeoutInMs() => $_has(2); @$pb.TagNumber(3) void clearTimeoutInMs() => clearField(3); + + @$pb.TagNumber(4) + $core.int get bondingMode => $_getIZ(3); + @$pb.TagNumber(4) + set bondingMode($core.int v) { $_setSignedInt32(3, v); } + @$pb.TagNumber(4) + $core.bool hasBondingMode() => $_has(3); + @$pb.TagNumber(4) + void clearBondingMode() => clearField(4); } class DeviceInfo extends $pb.GeneratedMessage { diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart index a0c0134c..3aef75e2 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart @@ -13,6 +13,35 @@ import 'dart:convert' as $convert; import 'dart:core' as $core; import 'dart:typed_data' as $typed_data; +@$core.Deprecated('Use launchCompanionRequestDescriptor instead') +const LaunchCompanionRequest$json = { + '1': 'LaunchCompanionRequest', + '2': [ + {'1': 'pattern', '3': 1, '4': 1, '5': 9, '10': 'pattern'}, + {'1': 'singleDeviceScan', '3': 2, '4': 1, '5': 8, '10': 'singleDeviceScan'}, + {'1': 'forceConfirmation', '3': 3, '4': 1, '5': 8, '10': 'forceConfirmation'}, + ], +}; + +/// Descriptor for `LaunchCompanionRequest`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List launchCompanionRequestDescriptor = $convert.base64Decode( + 'ChZMYXVuY2hDb21wYW5pb25SZXF1ZXN0EhgKB3BhdHRlcm4YASABKAlSB3BhdHRlcm4SKgoQc2' + 'luZ2xlRGV2aWNlU2NhbhgCIAEoCFIQc2luZ2xlRGV2aWNlU2NhbhIsChFmb3JjZUNvbmZpcm1h' + 'dGlvbhgDIAEoCFIRZm9yY2VDb25maXJtYXRpb24='); + +@$core.Deprecated('Use associationInfoDescriptor instead') +const AssociationInfo$json = { + '1': 'AssociationInfo', + '2': [ + {'1': 'deviceMacAddress', '3': 1, '4': 1, '5': 9, '10': 'deviceMacAddress'}, + ], +}; + +/// Descriptor for `AssociationInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List associationInfoDescriptor = $convert.base64Decode( + 'Cg9Bc3NvY2lhdGlvbkluZm8SKgoQZGV2aWNlTWFjQWRkcmVzcxgBIAEoCVIQZGV2aWNlTWFjQW' + 'RkcmVzcw=='); + @$core.Deprecated('Use scanForDevicesRequestDescriptor instead') const ScanForDevicesRequest$json = { '1': 'ScanForDevicesRequest', @@ -61,6 +90,7 @@ const ConnectToDeviceRequest$json = { {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, {'1': 'servicesWithCharacteristicsToDiscover', '3': 2, '4': 1, '5': 11, '6': '.ServicesWithCharacteristics', '10': 'servicesWithCharacteristicsToDiscover'}, {'1': 'timeoutInMs', '3': 3, '4': 1, '5': 5, '10': 'timeoutInMs'}, + {'1': 'bondingMode', '3': 4, '4': 1, '5': 5, '10': 'bondingMode'}, ], }; @@ -69,7 +99,8 @@ final $typed_data.Uint8List connectToDeviceRequestDescriptor = $convert.base64De 'ChZDb25uZWN0VG9EZXZpY2VSZXF1ZXN0EhoKCGRldmljZUlkGAEgASgJUghkZXZpY2VJZBJyCi' 'VzZXJ2aWNlc1dpdGhDaGFyYWN0ZXJpc3RpY3NUb0Rpc2NvdmVyGAIgASgLMhwuU2VydmljZXNX' 'aXRoQ2hhcmFjdGVyaXN0aWNzUiVzZXJ2aWNlc1dpdGhDaGFyYWN0ZXJpc3RpY3NUb0Rpc2Nvdm' - 'VyEiAKC3RpbWVvdXRJbk1zGAMgASgFUgt0aW1lb3V0SW5Ncw=='); + 'VyEiAKC3RpbWVvdXRJbk1zGAMgASgFUgt0aW1lb3V0SW5NcxIgCgtib25kaW5nTW9kZRgEIAEo' + 'BVILYm9uZGluZ01vZGU='); @$core.Deprecated('Use deviceInfoDescriptor instead') const DeviceInfo$json = { diff --git a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart index f6f1fdd4..9c19b840 100644 --- a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart +++ b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart @@ -95,6 +95,30 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { return _bleMethodChannel.invokeMethod("deinitialize"); } + @override + Future launchCompanionWorkflow({ + required String pattern, + required bool singleDeviceScan, + required bool forceConfirmation, + }) async { + final bytes = await _bleMethodChannel.invokeMethod>( + 'launchCompanionWorkflow', + _argsToProtobufConverter + .createLaunchCompanionWorkflowRequest( + pattern: pattern, + singleDeviceScan: singleDeviceScan, + forceConfirmation: forceConfirmation, + ) + .writeToBuffer(), + ); + + if (bytes == null) { + return null; + } + + return _protobufConverter.associationInfoFrom(bytes); + } + @override Stream scanForDevices({ required List withServices, @@ -123,6 +147,7 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, + BondingMode? bondingMode, ) { _logger?.log( 'Connect to device: $id, servicesWithCharacteristicsToDiscover: $servicesWithCharacteristicsToDiscover, timeout: $connectionTimeout', @@ -135,6 +160,7 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { id, servicesWithCharacteristicsToDiscover, connectionTimeout, + bondingMode, ) .writeToBuffer(), ) diff --git a/packages/reactive_ble_mobile/protos/bledata.proto b/packages/reactive_ble_mobile/protos/bledata.proto index c9aae8f4..cf8ee471 100644 --- a/packages/reactive_ble_mobile/protos/bledata.proto +++ b/packages/reactive_ble_mobile/protos/bledata.proto @@ -2,6 +2,16 @@ syntax = "proto3"; option java_package = "com.signify.hue.flutterreactiveble"; option java_outer_classname = "ProtobufModel"; +message LaunchCompanionRequest { + string pattern = 1; + bool singleDeviceScan = 2; + bool forceConfirmation = 3; +} + +message AssociationInfo { + string deviceMacAddress = 1; +} + message ScanForDevicesRequest { repeated Uuid serviceUuids = 1; int32 scanMode = 2; @@ -23,6 +33,7 @@ message ConnectToDeviceRequest { string deviceId = 1; ServicesWithCharacteristics servicesWithCharacteristicsToDiscover = 2; int32 timeoutInMs = 3; + int32 bondingMode = 4; } message DeviceInfo { diff --git a/packages/reactive_ble_mobile/pubspec.yaml b/packages/reactive_ble_mobile/pubspec.yaml index 494a8ff1..719b504d 100644 --- a/packages/reactive_ble_mobile/pubspec.yaml +++ b/packages/reactive_ble_mobile/pubspec.yaml @@ -18,6 +18,10 @@ dev_dependencies: sdk: flutter mockito: ^5.0.14 +dependency_overrides: + reactive_ble_platform_interface: + path: ../reactive_ble_platform_interface + flutter: plugin: platforms: diff --git a/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart b/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart index fd01cad3..a0fa25e7 100644 --- a/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart +++ b/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart @@ -1,6 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:reactive_ble_mobile/src/converter/args_to_protubuf_converter.dart'; import 'package:reactive_ble_mobile/src/generated/bledata.pb.dart' as pb; +import 'package:reactive_ble_platform_interface/reactive_ble_platform_interface.dart'; import 'package:reactive_ble_platform_interface/src/model/connection_priority.dart'; import 'package:reactive_ble_platform_interface/src/model/qualified_characteristic.dart'; import 'package:reactive_ble_platform_interface/src/model/scan_mode.dart'; @@ -14,6 +15,7 @@ void main() { const deviceId = '123'; Map>? servicesToDiscover; Duration? timeout; + BondingMode? bondingMode; late pb.ConnectToDeviceRequest result; group('And servicesToDiscover is not null', () { @@ -27,7 +29,7 @@ void main() { setUp(() { timeout = const Duration(seconds: 2); result = _sut.createConnectToDeviceArgs( - deviceId, servicesToDiscover, timeout); + deviceId, servicesToDiscover, timeout, bondingMode); }); test('It converts deviceId', () { @@ -52,7 +54,7 @@ void main() { setUp(() { timeout = null; result = _sut.createConnectToDeviceArgs( - deviceId, servicesToDiscover, timeout); + deviceId, servicesToDiscover, timeout, bondingMode); }); test('It sets timeout to default value', () { expect(result.timeoutInMs, 0); @@ -64,7 +66,7 @@ void main() { setUp(() { servicesToDiscover = null; result = _sut.createConnectToDeviceArgs( - deviceId, servicesToDiscover, timeout); + deviceId, servicesToDiscover, timeout, bondingMode); }); test('It converts servicesToDiscover to default', () { diff --git a/packages/reactive_ble_mobile/test/reactive_ble_platform_test.dart b/packages/reactive_ble_mobile/test/reactive_ble_platform_test.dart index d9a20f98..2b0ff17b 100644 --- a/packages/reactive_ble_mobile/test/reactive_ble_platform_test.dart +++ b/packages/reactive_ble_mobile/test/reactive_ble_platform_test.dart @@ -65,14 +65,14 @@ void main() { late pb.ConnectToDeviceRequest request; setUp(() { request = pb.ConnectToDeviceRequest(); - when(_argsConverter.createConnectToDeviceArgs('id', any, any)) + when(_argsConverter.createConnectToDeviceArgs('id', any, any, any)) .thenReturn(request); }); test( 'It invokes methodchannel with correct method and arguments', () async { - await _sut.connectToDevice('id', {}, null).first; + await _sut.connectToDevice('id', {}, null, null).first; verify(_methodChannel.invokeMethod( 'connectToDevice', request.writeToBuffer(), @@ -81,7 +81,7 @@ void main() { ); test('It emits 1 item', () async { - final length = await _sut.connectToDevice('id', {}, null).length; + final length = await _sut.connectToDevice('id', {}, null, null).length; expect(length, 1); }); }); diff --git a/packages/reactive_ble_platform_interface/lib/src/model/association_info.dart b/packages/reactive_ble_platform_interface/lib/src/model/association_info.dart new file mode 100644 index 00000000..2de6be83 --- /dev/null +++ b/packages/reactive_ble_platform_interface/lib/src/model/association_info.dart @@ -0,0 +1,16 @@ +import 'package:functional_data/functional_data.dart'; +import 'package:meta/meta.dart'; + +part 'association_info.g.dart'; + +//ignore_for_file: annotate_overrides + +@immutable +@FunctionalData() +class AssociationInfo extends $AssociationInfo { + final String deviceMacAddress; + + const AssociationInfo({ + required this.deviceMacAddress, + }); +} diff --git a/packages/reactive_ble_platform_interface/lib/src/model/association_info.g.dart b/packages/reactive_ble_platform_interface/lib/src/model/association_info.g.dart new file mode 100644 index 00000000..7eb666b7 --- /dev/null +++ b/packages/reactive_ble_platform_interface/lib/src/model/association_info.g.dart @@ -0,0 +1,64 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'association_info.dart'; + +// ************************************************************************** +// FunctionalDataGenerator +// ************************************************************************** + +abstract class $AssociationInfo { + const $AssociationInfo(); + + String get deviceMacAddress; + + AssociationInfo copyWith({ + String? deviceMacAddress, + }) => + AssociationInfo( + deviceMacAddress: deviceMacAddress ?? this.deviceMacAddress, + ); + + AssociationInfo copyUsing( + void Function(AssociationInfo$Change change) mutator) { + final change = AssociationInfo$Change._( + this.deviceMacAddress, + ); + mutator(change); + return AssociationInfo( + deviceMacAddress: change.deviceMacAddress, + ); + } + + @override + String toString() => "AssociationInfo(deviceMacAddress: $deviceMacAddress)"; + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) => + other is AssociationInfo && + other.runtimeType == runtimeType && + deviceMacAddress == other.deviceMacAddress; + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + return deviceMacAddress.hashCode; + } +} + +class AssociationInfo$Change { + AssociationInfo$Change._( + this.deviceMacAddress, + ); + + String deviceMacAddress; +} + +// ignore: avoid_classes_with_only_static_members +class AssociationInfo$ { + static final deviceMacAddress = Lens( + (deviceMacAddressContainer) => deviceMacAddressContainer.deviceMacAddress, + (deviceMacAddressContainer, deviceMacAddress) => + deviceMacAddressContainer.copyWith(deviceMacAddress: deviceMacAddress), + ); +} diff --git a/packages/reactive_ble_platform_interface/lib/src/model/bonding_mode.dart b/packages/reactive_ble_platform_interface/lib/src/model/bonding_mode.dart new file mode 100644 index 00000000..dc5b1bb9 --- /dev/null +++ b/packages/reactive_ble_platform_interface/lib/src/model/bonding_mode.dart @@ -0,0 +1,13 @@ +/// Specify the bonding mode for the device. +enum BondingMode { + /// Bonding will not be attempted. + none, + + /// Bonding will be attempted, but will not be required. + /// If bonding fails, the connection will still be established. + notRequired, + + /// Bonding will be attempted and will be required. + /// If bonding fails, the connection will be terminated. + required, +} diff --git a/packages/reactive_ble_platform_interface/lib/src/models.dart b/packages/reactive_ble_platform_interface/lib/src/models.dart index 62d3b3e7..08e33599 100644 --- a/packages/reactive_ble_platform_interface/lib/src/models.dart +++ b/packages/reactive_ble_platform_interface/lib/src/models.dart @@ -1,4 +1,6 @@ +export './model/association_info.dart'; export './model/ble_status.dart'; +export './model/bonding_mode.dart'; export './model/characteristic_value.dart'; export './model/clear_gatt_cache_error.dart'; export './model/connection_priority.dart'; diff --git a/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart b/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart index 4baed12b..4738915e 100644 --- a/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart +++ b/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart @@ -68,6 +68,16 @@ abstract class ReactiveBlePlatform extends PlatformInterface { throw UnimplementedError('deInitialize() has not been implemented.'); } + /// Launch Companion workflow for connecting a Ble device (Android only) + Future launchCompanionWorkflow({ + required String pattern, + required bool singleDeviceScan, + required bool forceConfirmation, + }) { + throw UnimplementedError( + 'launchCompanionWorkflow() has not been implemented.'); + } + /// Stream that handles triggers scanning for Ble devices. /// /// As long as the stream has been `listened` to the scanning continues. When @@ -95,6 +105,7 @@ abstract class ReactiveBlePlatform extends PlatformInterface { String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, + BondingMode? bondingMode, ) { throw UnimplementedError('connectToDevice has not been implemented.'); } From d2925623c96ed69d8c6ae50ef2ac4a46a5bf3a9d Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Tue, 4 Jul 2023 15:42:58 +0100 Subject: [PATCH 02/17] revert some changes --- example/pubspec.lock | 4 ++-- packages/flutter_reactive_ble/pubspec.yaml | 5 ----- packages/reactive_ble_mobile/pubspec.yaml | 4 ---- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 84b5635b..9c3c21b7 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -218,7 +218,7 @@ packages: dependency: "direct main" description: path: "../packages/flutter_reactive_ble" - relative: false + relative: true source: path version: "5.1.1" flutter_test: @@ -438,7 +438,7 @@ packages: dependency: "direct overridden" description: path: "../packages/reactive_ble_mobile" - relative: false + relative: true source: path version: "5.1.1" reactive_ble_platform_interface: diff --git a/packages/flutter_reactive_ble/pubspec.yaml b/packages/flutter_reactive_ble/pubspec.yaml index 226080f3..9ae69a98 100644 --- a/packages/flutter_reactive_ble/pubspec.yaml +++ b/packages/flutter_reactive_ble/pubspec.yaml @@ -31,8 +31,3 @@ dev_dependencies: functional_data_generator: ^1.1.2 mockito: ^5.0.14 -dependency_overrides: - reactive_ble_mobile: - path: ../reactive_ble_mobile - reactive_ble_platform_interface: - path: ../reactive_ble_platform_interface diff --git a/packages/reactive_ble_mobile/pubspec.yaml b/packages/reactive_ble_mobile/pubspec.yaml index 719b504d..494a8ff1 100644 --- a/packages/reactive_ble_mobile/pubspec.yaml +++ b/packages/reactive_ble_mobile/pubspec.yaml @@ -18,10 +18,6 @@ dev_dependencies: sdk: flutter mockito: ^5.0.14 -dependency_overrides: - reactive_ble_platform_interface: - path: ../reactive_ble_platform_interface - flutter: plugin: platforms: From 6df7e74046dd46cf099bf021799ceb2b43ee2049 Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Wed, 5 Jul 2023 18:22:58 +0100 Subject: [PATCH 03/17] adds a method to retrieve the GAP device name property on ios --- .../lib/src/device_connector.dart | 6 + .../lib/src/reactive_ble.dart | 13 ++ ...connected_device_operation_test.mocks.dart | 11 + .../test/device_connector_test.mocks.dart | 29 +++ .../test/device_scanner_test.mocks.dart | 11 + .../test/reactive_ble_test.mocks.dart | 31 +++ .../ios/Classes/BleData/bledata.pb.swift | 212 ++++++++++++++++++ .../ios/Classes/Plugin/PluginController.swift | 25 +++ .../Plugin/SwiftReactiveBlePlugin.swift | 3 + .../ios/Classes/ReactiveBle/Central.swift | 8 + .../converter/args_to_protubuf_converter.dart | 6 + .../lib/src/generated/bledata.pb.dart | 94 ++++++++ .../lib/src/generated/bledata.pbjson.dart | 26 +++ .../lib/src/reactive_ble_mobile_platform.dart | 11 + .../reactive_ble_mobile/protos/bledata.proto | 9 + .../args_to_protobuf_converter_test.dart | 4 - .../src/reactive_ble_platform_interface.dart | 5 + 17 files changed, 500 insertions(+), 4 deletions(-) diff --git a/packages/flutter_reactive_ble/lib/src/device_connector.dart b/packages/flutter_reactive_ble/lib/src/device_connector.dart index b0dc6964..075bd713 100644 --- a/packages/flutter_reactive_ble/lib/src/device_connector.dart +++ b/packages/flutter_reactive_ble/lib/src/device_connector.dart @@ -13,6 +13,8 @@ abstract class DeviceConnector { BondingMode? bondingMode, }); + Future retrieveDeviceName(String id); + Stream connectToAdvertisingDevice({ required String id, required List withServices, @@ -80,6 +82,10 @@ class DeviceConnectorImpl implements DeviceConnector { return autoconnectingRepeater.stream; } + @override + Future retrieveDeviceName(String id) => + _blePlatform.retrieveDeviceName(id); + @override Stream connectToAdvertisingDevice({ required String id, diff --git a/packages/flutter_reactive_ble/lib/src/reactive_ble.dart b/packages/flutter_reactive_ble/lib/src/reactive_ble.dart index b4cec8f3..709fc598 100644 --- a/packages/flutter_reactive_ble/lib/src/reactive_ble.dart +++ b/packages/flutter_reactive_ble/lib/src/reactive_ble.dart @@ -282,6 +282,19 @@ class FlutterReactiveBle { ), ); + /// iOS only. Retrieves the name of the device with the given id. + /// + /// This operation can only succeed when the host is `connected` with the peripheral. + /// + /// It is using the the CBPeripheral `name` property to retrieve the name. + /// See the [documentation](https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519029-name) for details. + /// Under the hood, this uses the GAP profile to retrieve the name. + /// + /// This operation is only supported on Apple platforms. + /// It will throw an [UnimplementedError] on other platforms. + Future retrieveDeviceName(String id) => + _deviceConnector.retrieveDeviceName(id); + /// Scans for a specific device and connects to it in case a device containing the specified [id] /// is found and that is advertising the services specified in [withServices]. /// diff --git a/packages/flutter_reactive_ble/test/connected_device_operation_test.mocks.dart b/packages/flutter_reactive_ble/test/connected_device_operation_test.mocks.dart index f639f72b..6593bb57 100644 --- a/packages/flutter_reactive_ble/test/connected_device_operation_test.mocks.dart +++ b/packages/flutter_reactive_ble/test/connected_device_operation_test.mocks.dart @@ -104,6 +104,17 @@ class MockReactiveBlePlatform extends _i1.Mock returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); @override + _i4.Future launchCompanionWorkflow({required String? pattern}) => + (super.noSuchMethod( + Invocation.method( + #launchCompanionWorkflow, + [], + {#pattern: pattern}, + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + @override _i4.Stream scanForDevices({ required List<_i2.Uuid>? withServices, required _i2.ScanMode? scanMode, diff --git a/packages/flutter_reactive_ble/test/device_connector_test.mocks.dart b/packages/flutter_reactive_ble/test/device_connector_test.mocks.dart index 5b5047e9..db7b796d 100644 --- a/packages/flutter_reactive_ble/test/device_connector_test.mocks.dart +++ b/packages/flutter_reactive_ble/test/device_connector_test.mocks.dart @@ -116,6 +116,17 @@ class MockReactiveBlePlatform extends _i1.Mock returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); @override + _i4.Future launchCompanionWorkflow({required String? pattern}) => + (super.noSuchMethod( + Invocation.method( + #launchCompanionWorkflow, + [], + {#pattern: pattern}, + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + @override _i4.Stream scanForDevices({ required List<_i2.Uuid>? withServices, required _i2.ScanMode? scanMode, @@ -321,6 +332,24 @@ class MockDeviceScanner extends _i1.Mock implements _i5.DeviceScanner { _i1.throwOnMissingStub(this); } + @override + _i4.Future launchCompanionWorkflow({ + required String? pattern, + required bool? singleDeviceScan, + required bool? forceConfirmation, + }) => + (super.noSuchMethod( + Invocation.method( + #launchCompanionWorkflow, + [], + { + #pattern: pattern, + #singleDeviceScan: singleDeviceScan, + #forceConfirmation: forceConfirmation, + }, + ), + returnValue: _i4.Future.value(), + ) as _i4.Future); @override _i4.Stream<_i2.DiscoveredDevice> scanForDevices({ required List<_i2.Uuid>? withServices, diff --git a/packages/flutter_reactive_ble/test/device_scanner_test.mocks.dart b/packages/flutter_reactive_ble/test/device_scanner_test.mocks.dart index fd20c095..1c3afb28 100644 --- a/packages/flutter_reactive_ble/test/device_scanner_test.mocks.dart +++ b/packages/flutter_reactive_ble/test/device_scanner_test.mocks.dart @@ -104,6 +104,17 @@ class MockReactiveBlePlatform extends _i1.Mock returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); @override + _i4.Future launchCompanionWorkflow({required String? pattern}) => + (super.noSuchMethod( + Invocation.method( + #launchCompanionWorkflow, + [], + {#pattern: pattern}, + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + @override _i4.Stream scanForDevices({ required List<_i2.Uuid>? withServices, required _i2.ScanMode? scanMode, diff --git a/packages/flutter_reactive_ble/test/reactive_ble_test.mocks.dart b/packages/flutter_reactive_ble/test/reactive_ble_test.mocks.dart index a5c05e36..4886c1d1 100644 --- a/packages/flutter_reactive_ble/test/reactive_ble_test.mocks.dart +++ b/packages/flutter_reactive_ble/test/reactive_ble_test.mocks.dart @@ -107,6 +107,17 @@ class MockReactiveBlePlatform extends _i1.Mock returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); @override + _i4.Future launchCompanionWorkflow({required String? pattern}) => + (super.noSuchMethod( + Invocation.method( + #launchCompanionWorkflow, + [], + {#pattern: pattern}, + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + @override _i4.Stream scanForDevices({ required List<_i2.Uuid>? withServices, required _i2.ScanMode? scanMode, @@ -465,6 +476,7 @@ class MockDeviceConnector extends _i1.Mock implements _i6.DeviceConnector { required String? id, Map<_i2.Uuid, List<_i2.Uuid>>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, + dynamic bondingMode, }) => (super.noSuchMethod( Invocation.method( @@ -475,6 +487,7 @@ class MockDeviceConnector extends _i1.Mock implements _i6.DeviceConnector { #servicesWithCharacteristicsToDiscover: servicesWithCharacteristicsToDiscover, #connectionTimeout: connectionTimeout, + #bondingMode: bondingMode, }, ), returnValue: _i4.Stream<_i2.ConnectionStateUpdate>.empty(), @@ -512,6 +525,24 @@ class MockDeviceScanner extends _i1.Mock implements _i7.DeviceScanner { _i1.throwOnMissingStub(this); } + @override + _i4.Future launchCompanionWorkflow({ + required String? pattern, + required bool? singleDeviceScan, + required bool? forceConfirmation, + }) => + (super.noSuchMethod( + Invocation.method( + #launchCompanionWorkflow, + [], + { + #pattern: pattern, + #singleDeviceScan: singleDeviceScan, + #forceConfirmation: forceConfirmation, + }, + ), + returnValue: _i4.Future.value(), + ) as _i4.Future); @override _i4.Stream<_i2.DiscoveredDevice> scanForDevices({ required List<_i2.Uuid>? withServices, diff --git a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift index 7f659fe1..889dc99f 100644 --- a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift +++ b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift @@ -20,6 +20,34 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } +struct LaunchCompanionRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + var pattern: String = String() + + var singleDeviceScan: Bool = false + + var forceConfirmation: Bool = false + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +struct AssociationInfo { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + var deviceMacAddress: String = String() + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + struct ScanForDevicesRequest { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -97,6 +125,8 @@ struct ConnectToDeviceRequest { var timeoutInMs: Int32 = 0 + var bondingMode: Int32 = 0 + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -129,6 +159,32 @@ struct DeviceInfo { fileprivate var _failure: GenericFailure? = nil } +struct GetDeviceNameRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + var deviceID: String = String() + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +struct DeviceNameInfo { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + var id: String = String() + + var deviceName: String = String() + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + struct DisconnectFromDeviceRequest { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -636,10 +692,14 @@ struct IsConnectable { } #if swift(>=5.5) && canImport(_Concurrency) +extension LaunchCompanionRequest: @unchecked Sendable {} +extension AssociationInfo: @unchecked Sendable {} extension ScanForDevicesRequest: @unchecked Sendable {} extension DeviceScanInfo: @unchecked Sendable {} extension ConnectToDeviceRequest: @unchecked Sendable {} extension DeviceInfo: @unchecked Sendable {} +extension GetDeviceNameRequest: @unchecked Sendable {} +extension DeviceNameInfo: @unchecked Sendable {} extension DisconnectFromDeviceRequest: @unchecked Sendable {} extension ClearGattCacheRequest: @unchecked Sendable {} extension ClearGattCacheInfo: @unchecked Sendable {} @@ -669,6 +729,82 @@ extension IsConnectable: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. +extension LaunchCompanionRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "LaunchCompanionRequest" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "pattern"), + 2: .same(proto: "singleDeviceScan"), + 3: .same(proto: "forceConfirmation"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.pattern) }() + case 2: try { try decoder.decodeSingularBoolField(value: &self.singleDeviceScan) }() + case 3: try { try decoder.decodeSingularBoolField(value: &self.forceConfirmation) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.pattern.isEmpty { + try visitor.visitSingularStringField(value: self.pattern, fieldNumber: 1) + } + if self.singleDeviceScan != false { + try visitor.visitSingularBoolField(value: self.singleDeviceScan, fieldNumber: 2) + } + if self.forceConfirmation != false { + try visitor.visitSingularBoolField(value: self.forceConfirmation, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: LaunchCompanionRequest, rhs: LaunchCompanionRequest) -> Bool { + if lhs.pattern != rhs.pattern {return false} + if lhs.singleDeviceScan != rhs.singleDeviceScan {return false} + if lhs.forceConfirmation != rhs.forceConfirmation {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension AssociationInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "AssociationInfo" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "deviceMacAddress"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.deviceMacAddress) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.deviceMacAddress.isEmpty { + try visitor.visitSingularStringField(value: self.deviceMacAddress, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: AssociationInfo, rhs: AssociationInfo) -> Bool { + if lhs.deviceMacAddress != rhs.deviceMacAddress {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension ScanForDevicesRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = "ScanForDevicesRequest" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -797,6 +933,7 @@ extension ConnectToDeviceRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI 1: .same(proto: "deviceId"), 2: .same(proto: "servicesWithCharacteristicsToDiscover"), 3: .same(proto: "timeoutInMs"), + 4: .same(proto: "bondingMode"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -808,6 +945,7 @@ extension ConnectToDeviceRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI case 1: try { try decoder.decodeSingularStringField(value: &self.deviceID) }() case 2: try { try decoder.decodeSingularMessageField(value: &self._servicesWithCharacteristicsToDiscover) }() case 3: try { try decoder.decodeSingularInt32Field(value: &self.timeoutInMs) }() + case 4: try { try decoder.decodeSingularInt32Field(value: &self.bondingMode) }() default: break } } @@ -827,6 +965,9 @@ extension ConnectToDeviceRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI if self.timeoutInMs != 0 { try visitor.visitSingularInt32Field(value: self.timeoutInMs, fieldNumber: 3) } + if self.bondingMode != 0 { + try visitor.visitSingularInt32Field(value: self.bondingMode, fieldNumber: 4) + } try unknownFields.traverse(visitor: &visitor) } @@ -834,6 +975,7 @@ extension ConnectToDeviceRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI if lhs.deviceID != rhs.deviceID {return false} if lhs._servicesWithCharacteristicsToDiscover != rhs._servicesWithCharacteristicsToDiscover {return false} if lhs.timeoutInMs != rhs.timeoutInMs {return false} + if lhs.bondingMode != rhs.bondingMode {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -887,6 +1029,76 @@ extension DeviceInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio } } +extension GetDeviceNameRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "GetDeviceNameRequest" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "deviceId"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.deviceID) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.deviceID.isEmpty { + try visitor.visitSingularStringField(value: self.deviceID, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: GetDeviceNameRequest, rhs: GetDeviceNameRequest) -> Bool { + if lhs.deviceID != rhs.deviceID {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension DeviceNameInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "DeviceNameInfo" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "id"), + 2: .same(proto: "deviceName"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.id) }() + case 2: try { try decoder.decodeSingularStringField(value: &self.deviceName) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.id.isEmpty { + try visitor.visitSingularStringField(value: self.id, fieldNumber: 1) + } + if !self.deviceName.isEmpty { + try visitor.visitSingularStringField(value: self.deviceName, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: DeviceNameInfo, rhs: DeviceNameInfo) -> Bool { + if lhs.id != rhs.id {return false} + if lhs.deviceName != rhs.deviceName {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension DisconnectFromDeviceRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = "DisconnectFromDeviceRequest" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ diff --git a/packages/reactive_ble_mobile/ios/Classes/Plugin/PluginController.swift b/packages/reactive_ble_mobile/ios/Classes/Plugin/PluginController.swift index 7341fb22..c23d809b 100644 --- a/packages/reactive_ble_mobile/ios/Classes/Plugin/PluginController.swift +++ b/packages/reactive_ble_mobile/ios/Classes/Plugin/PluginController.swift @@ -258,6 +258,31 @@ final class PluginController { } } + func retrieveDeviceName(name: String, args: GetDeviceNameRequest, completion: @escaping PlatformMethodCompletionHandler) { + guard let central = central + else { + completion(.failure(PluginError.notInitialized.asFlutterError)) + return + } + + guard let deviceID = UUID(uuidString: args.deviceID) + else { + completion(.failure(PluginError.invalidMethodCall(method: name, details: "\"deviceID\" is invalid").asFlutterError)) + return + } + + do { + let name: String? = try central.retrievePeripheralName(for: deviceID) + let message = DeviceNameInfo.with { + $0.id = deviceID.uuidString + $0.deviceName = name ?? "" + } + completion(.success(message)) + } catch { + completion(.success(nil)) + } + } + func disconnectFromDevice(name: String, args: ConnectToDeviceRequest, completion: @escaping PlatformMethodCompletionHandler) { guard let central = central else { diff --git a/packages/reactive_ble_mobile/ios/Classes/Plugin/SwiftReactiveBlePlugin.swift b/packages/reactive_ble_mobile/ios/Classes/Plugin/SwiftReactiveBlePlugin.swift index e0a4fb93..68391518 100644 --- a/packages/reactive_ble_mobile/ios/Classes/Plugin/SwiftReactiveBlePlugin.swift +++ b/packages/reactive_ble_mobile/ios/Classes/Plugin/SwiftReactiveBlePlugin.swift @@ -96,6 +96,9 @@ public class SwiftReactiveBlePlugin: NSObject, FlutterPlugin { AnyPlatformMethod(UnaryPlatformMethod(name: "connectToDevice") { (name, context, args: ConnectToDeviceRequest, completion) in context.connectToDevice(name: name, args: args, completion: completion) }), + AnyPlatformMethod(UnaryPlatformMethod(name: "retrieveDeviceName") { (name, context, args: GetDeviceNameRequest, completion) in + context.retrieveDeviceName(name: name, args: args, completion: completion) + }), AnyPlatformMethod(UnaryPlatformMethod(name: "disconnectFromDevice") { (name, context, args: ConnectToDeviceRequest, completion) in context.disconnectFromDevice(name: name, args: args, completion: completion) }), diff --git a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift index 95f2f9dc..dcfe55eb 100644 --- a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift +++ b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift @@ -167,6 +167,14 @@ final class Central { .values .forEach(centralManager.cancelPeripheralConnection) } + + func retrievePeripheralName( + for peripheralID: PeripheralID + ) throws -> String? { + let peripheral = try resolve(known: peripheralID) + + return peripheral.name + } func discoverServicesWithCharacteristics( for peripheralID: PeripheralID, diff --git a/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart index a56a3dcf..214dc710 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart @@ -10,6 +10,8 @@ abstract class ArgsToProtobufConverter { BondingMode? bondingMode, ); + pb.GetDeviceNameRequest createGetDeviceNameArgs(String id); + pb.DisconnectFromDeviceRequest createDisconnectDeviceArgs(String deviceId); pb.ReadCharacteristicRequest createReadCharacteristicRequest( @@ -93,6 +95,10 @@ class ArgsToProtobufConverterImpl implements ArgsToProtobufConverter { return args; } + @override + pb.GetDeviceNameRequest createGetDeviceNameArgs(String deviceId) => + pb.GetDeviceNameRequest()..deviceId = deviceId; + @override pb.DisconnectFromDeviceRequest createDisconnectDeviceArgs(String deviceId) => pb.DisconnectFromDeviceRequest()..deviceId = deviceId; diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart index c7a6f185..99f484de 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart @@ -415,6 +415,100 @@ class DeviceInfo extends $pb.GeneratedMessage { GenericFailure ensureFailure() => $_ensure(2); } +class GetDeviceNameRequest extends $pb.GeneratedMessage { + factory GetDeviceNameRequest() => create(); + GetDeviceNameRequest._() : super(); + factory GetDeviceNameRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory GetDeviceNameRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GetDeviceNameRequest', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + GetDeviceNameRequest clone() => GetDeviceNameRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + GetDeviceNameRequest copyWith(void Function(GetDeviceNameRequest) updates) => super.copyWith((message) => updates(message as GetDeviceNameRequest)) as GetDeviceNameRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetDeviceNameRequest create() => GetDeviceNameRequest._(); + GetDeviceNameRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static GetDeviceNameRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static GetDeviceNameRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get deviceId => $_getSZ(0); + @$pb.TagNumber(1) + set deviceId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasDeviceId() => $_has(0); + @$pb.TagNumber(1) + void clearDeviceId() => clearField(1); +} + +class DeviceNameInfo extends $pb.GeneratedMessage { + factory DeviceNameInfo() => create(); + DeviceNameInfo._() : super(); + factory DeviceNameInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory DeviceNameInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DeviceNameInfo', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'id') + ..aOS(2, _omitFieldNames ? '' : 'deviceName', protoName: 'deviceName') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + DeviceNameInfo clone() => DeviceNameInfo()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + DeviceNameInfo copyWith(void Function(DeviceNameInfo) updates) => super.copyWith((message) => updates(message as DeviceNameInfo)) as DeviceNameInfo; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeviceNameInfo create() => DeviceNameInfo._(); + DeviceNameInfo createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static DeviceNameInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static DeviceNameInfo? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get id => $_getSZ(0); + @$pb.TagNumber(1) + set id($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get deviceName => $_getSZ(1); + @$pb.TagNumber(2) + set deviceName($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasDeviceName() => $_has(1); + @$pb.TagNumber(2) + void clearDeviceName() => clearField(2); +} + class DisconnectFromDeviceRequest extends $pb.GeneratedMessage { factory DisconnectFromDeviceRequest() => create(); DisconnectFromDeviceRequest._() : super(); diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart index 3aef75e2..407bbee7 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart @@ -118,6 +118,32 @@ final $typed_data.Uint8List deviceInfoDescriptor = $convert.base64Decode( 'Nvbm5lY3Rpb25TdGF0ZRIpCgdmYWlsdXJlGAMgASgLMg8uR2VuZXJpY0ZhaWx1cmVSB2ZhaWx1' 'cmU='); +@$core.Deprecated('Use getDeviceNameRequestDescriptor instead') +const GetDeviceNameRequest$json = { + '1': 'GetDeviceNameRequest', + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + ], +}; + +/// Descriptor for `GetDeviceNameRequest`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List getDeviceNameRequestDescriptor = $convert.base64Decode( + 'ChRHZXREZXZpY2VOYW1lUmVxdWVzdBIaCghkZXZpY2VJZBgBIAEoCVIIZGV2aWNlSWQ='); + +@$core.Deprecated('Use deviceNameInfoDescriptor instead') +const DeviceNameInfo$json = { + '1': 'DeviceNameInfo', + '2': [ + {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + {'1': 'deviceName', '3': 2, '4': 1, '5': 9, '10': 'deviceName'}, + ], +}; + +/// Descriptor for `DeviceNameInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List deviceNameInfoDescriptor = $convert.base64Decode( + 'Cg5EZXZpY2VOYW1lSW5mbxIOCgJpZBgBIAEoCVICaWQSHgoKZGV2aWNlTmFtZRgCIAEoCVIKZG' + 'V2aWNlTmFtZQ=='); + @$core.Deprecated('Use disconnectFromDeviceRequestDescriptor instead') const DisconnectFromDeviceRequest$json = { '1': 'DisconnectFromDeviceRequest', diff --git a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart index 9c19b840..c667417b 100644 --- a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart +++ b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart @@ -167,6 +167,17 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { .asStream(); } + @override + Future retrieveDeviceName(String id) { + _logger?.log( + 'Retrieve device name: $id', + ); + return _bleMethodChannel.invokeMethod( + 'retrieveDeviceName', + _argsToProtobufConverter.createGetDeviceNameArgs(id).writeToBuffer(), + ); + } + @override Future disconnectDevice(String deviceId) { _logger?.log( diff --git a/packages/reactive_ble_mobile/protos/bledata.proto b/packages/reactive_ble_mobile/protos/bledata.proto index cf8ee471..bb4648b9 100644 --- a/packages/reactive_ble_mobile/protos/bledata.proto +++ b/packages/reactive_ble_mobile/protos/bledata.proto @@ -42,6 +42,15 @@ message DeviceInfo { GenericFailure failure = 3; } +message GetDeviceNameRequest { + string deviceId = 1; +} + +message DeviceNameInfo { + string id = 1; + string deviceName = 2; +} + message DisconnectFromDeviceRequest { string deviceId = 1; } diff --git a/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart b/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart index a0fa25e7..c4ec9fd6 100644 --- a/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart +++ b/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart @@ -2,10 +2,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:reactive_ble_mobile/src/converter/args_to_protubuf_converter.dart'; import 'package:reactive_ble_mobile/src/generated/bledata.pb.dart' as pb; import 'package:reactive_ble_platform_interface/reactive_ble_platform_interface.dart'; -import 'package:reactive_ble_platform_interface/src/model/connection_priority.dart'; -import 'package:reactive_ble_platform_interface/src/model/qualified_characteristic.dart'; -import 'package:reactive_ble_platform_interface/src/model/scan_mode.dart'; -import 'package:reactive_ble_platform_interface/src/model/uuid.dart'; void main() { group('$ArgsToProtobufConverter', () { diff --git a/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart b/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart index 4738915e..833eead7 100644 --- a/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart +++ b/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart @@ -110,6 +110,11 @@ abstract class ReactiveBlePlatform extends PlatformInterface { throw UnimplementedError('connectToDevice has not been implemented.'); } + /// Retrieve the device name. + Future retrieveDeviceName(String id) { + throw UnimplementedError('retrieveDeviceName has not been implemented.'); + } + /// Operation that disconnects the host with the peripheral. Future disconnectDevice(String deviceId) { throw UnimplementedError('disconnectDevice has not been implemented.'); From c2baf0fb2f17fb21f66f73ae02f6609515bdaccb Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Thu, 6 Jul 2023 00:06:11 +0100 Subject: [PATCH 04/17] Missing converter for native response --- .../lib/src/converter/protobuf_converter.dart | 8 ++++++++ .../lib/src/reactive_ble_mobile_platform.dart | 11 +++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart index b7d0ae55..d3cf29ca 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart @@ -11,6 +11,8 @@ abstract class ProtobufConverter { ScanResult scanResultFrom(List data); + String deviceNameFrom(List data); + ConnectionStateUpdate connectionStateUpdateFrom(List data); Result?> clearGattCacheResultFrom( @@ -82,6 +84,12 @@ class ProtobufConverterImpl implements ProtobufConverter { ); } + @override + String deviceNameFrom(List data) { + final deviceNameInfo = pb.DeviceNameInfo.fromBuffer(data); + return deviceNameInfo.deviceName; + } + @override ConnectionStateUpdate connectionStateUpdateFrom(List data) { final deviceInfo = pb.DeviceInfo.fromBuffer(data); diff --git a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart index c667417b..108b62cc 100644 --- a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart +++ b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart @@ -172,10 +172,13 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { _logger?.log( 'Retrieve device name: $id', ); - return _bleMethodChannel.invokeMethod( - 'retrieveDeviceName', - _argsToProtobufConverter.createGetDeviceNameArgs(id).writeToBuffer(), - ); + return _bleMethodChannel + .invokeMethod>( + 'retrieveDeviceName', + _argsToProtobufConverter.createGetDeviceNameArgs(id).writeToBuffer(), + ) + .then((data) => _protobufConverter.deviceNameFrom(data!)); + ; } @override From 5b446519df69e256279e211048fc2a41c1ecd68a Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sun, 9 Jul 2023 09:15:10 +0100 Subject: [PATCH 05/17] refactoring bonding --- .../lib/src/device_connector.dart | 11 +- .../lib/src/discovered_devices_registry.dart | 3 +- .../lib/src/reactive_ble.dart | 8 +- .../flutterreactiveble/PluginController.kt | 24 +- .../hue/flutterreactiveble/ble/BleClient.kt | 14 +- .../flutterreactiveble/ble/BondingManager.kt | 44 +--- .../flutterreactiveble/ble/DeviceConnector.kt | 36 ++- .../ble/ReactiveBleClient.kt | 17 +- .../DeviceConnectionHandler.kt | 9 +- .../converters/ProtobufMessageConverter.kt | 234 ++++++++++-------- .../flutterreactiveble/model/BondingMode.kt | 9 - .../ble/DeviceConnectorTest.kt | 1 - .../ios/Classes/BleData/bledata.pb.swift | 151 ++++++++++- .../converter/args_to_protubuf_converter.dart | 10 +- .../lib/src/converter/protobuf_converter.dart | 21 ++ .../lib/src/generated/bledata.pb.dart | 98 +++++++- .../lib/src/generated/bledata.pbenum.dart | 23 ++ .../lib/src/generated/bledata.pbjson.dart | 41 ++- .../lib/src/reactive_ble_mobile_platform.dart | 17 +- .../reactive_ble_mobile/protos/bledata.proto | 16 +- .../args_to_protobuf_converter_test.dart | 7 +- .../test/reactive_ble_platform_test.dart | 6 +- .../lib/src/model/bonding_mode.dart | 13 - .../lib/src/model/bonding_status.dart | 10 + .../lib/src/models.dart | 2 +- .../src/reactive_ble_platform_interface.dart | 6 +- 26 files changed, 589 insertions(+), 242 deletions(-) delete mode 100644 packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/BondingMode.kt delete mode 100644 packages/reactive_ble_platform_interface/lib/src/model/bonding_mode.dart create mode 100644 packages/reactive_ble_platform_interface/lib/src/model/bonding_status.dart diff --git a/packages/flutter_reactive_ble/lib/src/device_connector.dart b/packages/flutter_reactive_ble/lib/src/device_connector.dart index 075bd713..700d6e81 100644 --- a/packages/flutter_reactive_ble/lib/src/device_connector.dart +++ b/packages/flutter_reactive_ble/lib/src/device_connector.dart @@ -6,11 +6,14 @@ import 'package:reactive_ble_platform_interface/reactive_ble_platform_interface. abstract class DeviceConnector { Stream get deviceConnectionStateUpdateStream; + Future establishBond({ + required String deviceId, + }); + Stream connect({ required String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, - BondingMode? bondingMode, }); Future retrieveDeviceName(String id); @@ -51,12 +54,15 @@ class DeviceConnectorImpl implements DeviceConnector { Stream get deviceConnectionStateUpdateStream => _blePlatform.connectionUpdateStream; + @override + Future establishBond({required String deviceId}) async => + _blePlatform.establishBond(deviceId); + @override Stream connect({ required String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, - BondingMode? bondingMode, }) { final specificConnectedDeviceStream = deviceConnectionStateUpdateStream .where((update) => update.deviceId == id) @@ -73,7 +79,6 @@ class DeviceConnectorImpl implements DeviceConnector { id, servicesWithCharacteristicsToDiscover, connectionTimeout, - bondingMode, ) .asyncExpand((_) => specificConnectedDeviceStream), onCancel: () => _blePlatform.disconnectDevice(id), diff --git a/packages/flutter_reactive_ble/lib/src/discovered_devices_registry.dart b/packages/flutter_reactive_ble/lib/src/discovered_devices_registry.dart index 23d9749a..7d6deabe 100644 --- a/packages/flutter_reactive_ble/lib/src/discovered_devices_registry.dart +++ b/packages/flutter_reactive_ble/lib/src/discovered_devices_registry.dart @@ -18,8 +18,7 @@ abstract class DiscoveredDevicesRegistry { class DiscoveredDevicesRegistryImpl implements DiscoveredDevicesRegistry { DiscoveredDevicesRegistryImpl({required this.getTimestamp}); - DiscoveredDevicesRegistryImpl.standard() - : this(getTimestamp: () => DateTime.now()); + DiscoveredDevicesRegistryImpl.standard() : this(getTimestamp: DateTime.now); @override final DateTime Function() getTimestamp; diff --git a/packages/flutter_reactive_ble/lib/src/reactive_ble.dart b/packages/flutter_reactive_ble/lib/src/reactive_ble.dart index 709fc598..8ac5a129 100644 --- a/packages/flutter_reactive_ble/lib/src/reactive_ble.dart +++ b/packages/flutter_reactive_ble/lib/src/reactive_ble.dart @@ -254,6 +254,12 @@ class FlutterReactiveBle { ); } + Future establishBond({required String deviceId}) async { + await initialize(); + + return _deviceConnector.establishBond(deviceId: deviceId); + } + /// Establishes a connection to a BLE device. /// /// Disconnecting the device is achieved by cancelling the stream subscription. @@ -270,7 +276,6 @@ class FlutterReactiveBle { required String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, - BondingMode? bondingMode, }) => initialize().asStream().asyncExpand( (_) => _deviceConnector.connect( @@ -278,7 +283,6 @@ class FlutterReactiveBle { servicesWithCharacteristicsToDiscover: servicesWithCharacteristicsToDiscover, connectionTimeout: connectionTimeout, - bondingMode: bondingMode, ), ); diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt index 384cf260..56a24265 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt @@ -37,6 +37,7 @@ class PluginController : PluginRegistry.ActivityResultListener { "deinitialize" to this::deinitializeClient, "launchCompanionWorkflow" to this::launchCompanionFlow, "scanForDevices" to this::scanForDevices, + "establishBond" to this::establishBond, "connectToDevice" to this::connectToDevice, "clearGattCache" to this::clearGattCache, "disconnectFromDevice" to this::disconnectFromDevice, @@ -110,7 +111,11 @@ class PluginController : PluginRegistry.ActivityResultListener { result ) } else { - result.error("NOT_SUPPORTED", "Companion flow is only supported on Android Oreo and above", null) + result.error( + "NOT_SUPPORTED", + "Companion flow is only supported on Android Oreo and above", + null + ) } } @@ -119,9 +124,19 @@ class PluginController : PluginRegistry.ActivityResultListener { result.success(null) } + private fun establishBond(call: MethodCall, result: Result) { + val establishBondMessage = pb.EstablishBondRequest.parseFrom(call.arguments as ByteArray) + deviceConnectionHandler.establishBond(establishBondMessage).subscribe({ + result.success(protoConverter.convertBondInfo(it).toByteArray()) + }, { + result.error("establish_bond_error", it.message, null) + }).discard() + } + private fun connectToDevice(call: MethodCall, result: Result) { result.success(null) val connectDeviceMessage = pb.ConnectToDeviceRequest.parseFrom(call.arguments as ByteArray) + deviceConnectionHandler.connectToDevice(connectDeviceMessage) } @@ -349,7 +364,10 @@ class PluginController : PluginRegistry.ActivityResultListener { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { if (requestCode == CompanionHandler.SELECT_DEVICE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { - Log.d(TAG, "received result from device selection activity, parsing via companion handler") + Log.d( + TAG, + "received result from device selection activity, parsing via companion handler" + ) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { Log.d(TAG, "not running on Android O, ignoring result") @@ -359,7 +377,7 @@ class PluginController : PluginRegistry.ActivityResultListener { Log.d(TAG, "running on Android O, parsing result") val scanResult = companionHandler.onActivityResult(data) ?: return false -Log.d(TAG, "got the scan result: ${scanResult.device.address}") + Log.d(TAG, "got the scan result: ${scanResult.device.address}") } return false diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt index 17b7968f..6b0848c7 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt @@ -2,7 +2,6 @@ package com.signify.hue.flutterreactiveble.ble import android.os.ParcelUuid import com.polidea.rxandroidble2.RxBleDeviceServices -import com.signify.hue.flutterreactiveble.model.BondingMode import com.signify.hue.flutterreactiveble.model.ScanMode import com.signify.hue.flutterreactiveble.utils.Duration import io.reactivex.Completable @@ -18,7 +17,18 @@ interface BleClient { fun initializeClient() fun scanForDevices(services: List, scanMode: ScanMode, requireLocationServicesEnabled: Boolean): Observable - fun connectToDevice(deviceId: String, timeout: Duration, bondingMode: BondingMode) + + /** + * Establishes a bond with the device. + * + * The single will emit the final bond state of the device. + * The value is one of the constants in [android.bluetooth.BluetoothDevice]: + * - [android.bluetooth.BluetoothDevice.BOND_NONE] + * - [android.bluetooth.BluetoothDevice.BOND_BONDING] + * - [android.bluetooth.BluetoothDevice.BOND_BONDED] + */ + fun establishBond(deviceId: String): Single + fun connectToDevice(deviceId: String, timeout: Duration) fun disconnectDevice(deviceId: String) fun disconnectAllDevices() fun discoverServices(deviceId: String): Single diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt index 48df26b8..8df912f3 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt @@ -2,15 +2,13 @@ package com.signify.hue.flutterreactiveble.ble import android.annotation.SuppressLint import android.bluetooth.BluetoothDevice +import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.content.BroadcastReceiver import android.os.Build -import android.util.Log import com.polidea.rxandroidble2.RxBleDevice -import com.signify.hue.flutterreactiveble.model.BondingMode -import io.reactivex.Completable +import io.reactivex.Single import io.reactivex.disposables.Disposables /** @@ -20,8 +18,6 @@ import io.reactivex.disposables.Disposables class BondingFailedException : RuntimeException() object BondingManager { - const val TAG = "CompanionHandler" - /** * @throws BondingFailedException */ @@ -29,48 +25,36 @@ object BondingManager { @SuppressLint("MissingPermission") fun bondWithDevice( context: Context, - rxBleDevice: RxBleDevice, - bondingMode: BondingMode - ): Completable { - if (bondingMode == BondingMode.NONE) { - return Completable.complete() - } - - return Completable.create { completion -> - Log.d(TAG, "pairWithDevice: ${rxBleDevice.bluetoothDevice.bondState}") + rxBleDevice: RxBleDevice + ): Single { + return Single.create { completion -> when (rxBleDevice.bluetoothDevice.bondState) { - BluetoothDevice.BOND_BONDED -> completion.onComplete() + BluetoothDevice.BOND_BONDED -> completion.onSuccess(BluetoothDevice.BOND_BONDED) else -> { val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val deviceBeingPaired: BluetoothDevice? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java) + intent.getParcelableExtra( + BluetoothDevice.EXTRA_DEVICE, + BluetoothDevice::class.java + ) } else { @Suppress("DEPRECATION") intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) } - Log.d(TAG, "pairWithDevice: deviceBeingPaired: $deviceBeingPaired") - if (deviceBeingPaired?.address == rxBleDevice.bluetoothDevice.address) { val state = intent.getIntExtra( BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE ) - Log.d(TAG, "pairWithDevice: state: $state") - when (state) { - BluetoothDevice.BOND_BONDED -> completion.onComplete() - BluetoothDevice.BOND_NONE -> - // When bonding is required, we throw an exception if the bonding fails. - if (bondingMode == BondingMode.REQUIRED) { - completion.tryOnError(BondingFailedException()) - } else { // BondingMode.NOT_REQUIRED - completion.onComplete() - } + BluetoothDevice.BOND_BONDED -> completion.onSuccess(state) + BluetoothDevice.BOND_NONE -> completion.onSuccess(state) + // BOND_BONDING is a intermediate state - do not send this back. } } } @@ -89,8 +73,6 @@ object BondingManager { val createBondResult = rxBleDevice.bluetoothDevice.createBond() - Log.d(TAG, "pairWithDevice: createBondResult: $createBondResult") - if (!createBondResult) { completion.tryOnError(BondingFailedException()) } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt index 79413f85..b64bac8c 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt @@ -1,11 +1,9 @@ package com.signify.hue.flutterreactiveble.ble -import android.content.Context import androidx.annotation.VisibleForTesting import com.polidea.rxandroidble2.RxBleConnection import com.polidea.rxandroidble2.RxBleCustomOperation import com.polidea.rxandroidble2.RxBleDevice -import com.signify.hue.flutterreactiveble.model.BondingMode import com.signify.hue.flutterreactiveble.model.ConnectionState import com.signify.hue.flutterreactiveble.model.toConnectionState import com.signify.hue.flutterreactiveble.utils.Duration @@ -18,12 +16,10 @@ import io.reactivex.subjects.BehaviorSubject import java.util.concurrent.TimeUnit internal class DeviceConnector( - private val context: Context, private val device: RxBleDevice, private val connectionTimeout: Duration, private val updateListeners: (update: ConnectionUpdate) -> Unit, private val connectionQueue: ConnectionQueue, - private val bondingMode: BondingMode = BondingMode.NONE, ) { companion object { @@ -117,7 +113,7 @@ internal class DeviceConnector( ) ) } else { - connectDevice(rxBleDevice, shouldNotTimeout, bondingMode) + connectDevice(rxBleDevice, shouldNotTimeout) .map { EstablishedConnection( rxBleDevice.macAddress, @@ -158,25 +154,21 @@ internal class DeviceConnector( private fun connectDevice( rxBleDevice: RxBleDevice, shouldNotTimeout: Boolean, - bondingMode: BondingMode, ): Observable = - BondingManager.bondWithDevice(this.context, rxBleDevice, bondingMode) - .andThen( - rxBleDevice.establishConnection(shouldNotTimeout) - - .compose { - if (shouldNotTimeout) { - it - } else { - it.timeout( - Observable.timer(connectionTimeout.value, connectionTimeout.unit), - Function> { - Observable.never() - } - ) + rxBleDevice.establishConnection(shouldNotTimeout) + + .compose { + if (shouldNotTimeout) { + it + } else { + it.timeout( + Observable.timer(connectionTimeout.value, connectionTimeout.unit), + Function> { + Observable.never() } - } - ) + ) + } + } internal fun clearGattCache(): Completable = currentConnection?.let { connection -> when (connection) { diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt index 6e5aa8df..bdc08e76 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt @@ -19,7 +19,6 @@ import com.polidea.rxandroidble2.scan.ScanSettings import com.signify.hue.flutterreactiveble.ble.extensions.writeCharWithResponse import com.signify.hue.flutterreactiveble.ble.extensions.writeCharWithoutResponse import com.signify.hue.flutterreactiveble.converters.extractManufacturerData -import com.signify.hue.flutterreactiveble.model.BondingMode import com.signify.hue.flutterreactiveble.model.ScanMode import com.signify.hue.flutterreactiveble.model.toScanSettings import com.signify.hue.flutterreactiveble.utils.Duration @@ -98,8 +97,13 @@ open class ReactiveBleClient(private val context: Context) : BleClient { } } - override fun connectToDevice(deviceId: String, timeout: Duration, bondingMode: BondingMode) { - allConnections.add(getConnection(deviceId, timeout, bondingMode) + override fun establishBond(deviceId: String): Single { + val device = rxBleClient.getBleDevice(deviceId) + return BondingManager.bondWithDevice(context, device) + } + + override fun connectToDevice(deviceId: String, timeout: Duration) { + allConnections.add(getConnection(deviceId, timeout) .subscribe({ result -> when (result) { is EstablishedConnection -> { @@ -241,17 +245,16 @@ open class ReactiveBleClient(private val context: Context) : BleClient { .map { it.toBleState() } @VisibleForTesting - internal open fun createDeviceConnector(device: RxBleDevice, timeout: Duration, bondingMode: BondingMode) = - DeviceConnector(context, device, timeout, connectionUpdateBehaviorSubject::onNext, connectionQueue, bondingMode) + internal open fun createDeviceConnector(device: RxBleDevice, timeout: Duration) = + DeviceConnector(device, timeout, connectionUpdateBehaviorSubject::onNext, connectionQueue) private fun getConnection( deviceId: String, timeout: Duration = Duration(0, TimeUnit.MILLISECONDS), - bondingMode: BondingMode = BondingMode.NONE, ): Observable { val device = rxBleClient.getBleDevice(deviceId) val connector = - activeConnections.getOrPut(deviceId) { createDeviceConnector(device, timeout, bondingMode) } + activeConnections.getOrPut(deviceId) { createDeviceConnector(device, timeout) } return connector.connection } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt index 8aed2ca2..2fcc71f5 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt @@ -2,9 +2,9 @@ package com.signify.hue.flutterreactiveble.channelhandlers import com.signify.hue.flutterreactiveble.ProtobufModel as pb import com.signify.hue.flutterreactiveble.converters.ProtobufMessageConverter -import com.signify.hue.flutterreactiveble.model.BondingMode import com.signify.hue.flutterreactiveble.utils.Duration import io.flutter.plugin.common.EventChannel +import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import java.util.concurrent.TimeUnit @@ -27,11 +27,14 @@ class DeviceConnectionHandler(private val bleClient: com.signify.hue.flutterreac connectionUpdatesDisposable.dispose() } + fun establishBond(connectToDeviceMessage: pb.EstablishBondRequest): Single { + return bleClient.establishBond(connectToDeviceMessage.deviceId) + } + fun connectToDevice(connectToDeviceMessage: pb.ConnectToDeviceRequest) { bleClient.connectToDevice( connectToDeviceMessage.deviceId, - Duration(connectToDeviceMessage.timeoutInMs.toLong(), TimeUnit.MILLISECONDS), - BondingMode.values()[connectToDeviceMessage.bondingMode], + Duration(connectToDeviceMessage.timeoutInMs.toLong(), TimeUnit.MILLISECONDS) ) } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt index de654976..31e5ca17 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt @@ -4,6 +4,8 @@ import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattService import com.google.protobuf.ByteString import com.polidea.rxandroidble2.RxBleDeviceServices +import com.signify.hue.flutterreactiveble.ProtobufModel.EstablishBondInfo +import com.signify.hue.flutterreactiveble.ProtobufModel.EstablishBondInfo.BondState import com.signify.hue.flutterreactiveble.ble.ConnectionUpdateSuccess import com.signify.hue.flutterreactiveble.ble.MtuNegotiateFailed import com.signify.hue.flutterreactiveble.ble.MtuNegotiateResult @@ -32,92 +34,101 @@ class ProtobufMessageConverter { private val uuidConverter = UuidConverter() fun convertScanInfo(scanInfo: ScanInfo): pb.DeviceScanInfo = - pb.DeviceScanInfo.newBuilder() - .setId(scanInfo.deviceId) - .setName(scanInfo.name) - .setRssi(scanInfo.rssi) - .setIsConnectable(pb.IsConnectable.newBuilder() - .setCode(scanInfo.connectable.code) - .build()) - .addAllServiceData(createServiceDataEntry(scanInfo.serviceData)) - .addAllServiceUuids(createServiceUuids(scanInfo.serviceUuids)) - .setManufacturerData(ByteString.copyFrom(scanInfo.manufacturerData)) + pb.DeviceScanInfo.newBuilder() + .setId(scanInfo.deviceId) + .setName(scanInfo.name) + .setRssi(scanInfo.rssi) + .setIsConnectable( + pb.IsConnectable.newBuilder() + .setCode(scanInfo.connectable.code) .build() + ) + .addAllServiceData(createServiceDataEntry(scanInfo.serviceData)) + .addAllServiceUuids(createServiceUuids(scanInfo.serviceUuids)) + .setManufacturerData(ByteString.copyFrom(scanInfo.manufacturerData)) + .build() fun convertScanErrorInfo(errorMessage: String?): pb.DeviceScanInfo = - pb.DeviceScanInfo.newBuilder() - .setFailure(pb.GenericFailure.newBuilder() - .setCode(ScanErrorType.UNKNOWN.code) - .setMessage(errorMessage ?: "") - .build()) + pb.DeviceScanInfo.newBuilder() + .setFailure( + pb.GenericFailure.newBuilder() + .setCode(ScanErrorType.UNKNOWN.code) + .setMessage(errorMessage ?: "") .build() + ) + .build() fun convertToDeviceInfo(connection: ConnectionUpdateSuccess): pb.DeviceInfo = - pb.DeviceInfo.newBuilder() - .setId(connection.deviceId) - .setConnectionState(connection.connectionState) - .build() + pb.DeviceInfo.newBuilder() + .setId(connection.deviceId) + .setConnectionState(connection.connectionState) + .build() fun convertConnectionErrorToDeviceInfo( - deviceId: String, - errorMessage: String? + deviceId: String, + errorMessage: String? ): pb.DeviceInfo { return pb.DeviceInfo.newBuilder() - .setId(deviceId) - .setConnectionState(ConnectionState.DISCONNECTED.code) - .setFailure(pb.GenericFailure.newBuilder() - .setCode(ConnectionErrorType.FAILEDTOCONNECT.code) - .setMessage(errorMessage ?: "") - .build()) - .build() + .setId(deviceId) + .setConnectionState(ConnectionState.DISCONNECTED.code) + .setFailure( + pb.GenericFailure.newBuilder() + .setCode(ConnectionErrorType.FAILEDTOCONNECT.code) + .setMessage(errorMessage ?: "") + .build() + ) + .build() } - fun convertClearGattCacheError(code: ClearGattCacheErrorType, message: String?): pb.ClearGattCacheInfo { + fun convertClearGattCacheError( + code: ClearGattCacheErrorType, + message: String? + ): pb.ClearGattCacheInfo { val failure = pb.GenericFailure.newBuilder().setCode(code.code) message?.let(failure::setMessage) return pb.ClearGattCacheInfo.newBuilder().setFailure(failure).build() } fun convertCharacteristicInfo( - request: pb.CharacteristicAddress, - value: ByteArray + request: pb.CharacteristicAddress, + value: ByteArray ): pb.CharacteristicValueInfo { val characteristicAddress = createCharacteristicAddress(request) return pb.CharacteristicValueInfo.newBuilder() - .setCharacteristic(characteristicAddress) - .setValue(ByteString.copyFrom(value)) - .build() + .setCharacteristic(characteristicAddress) + .setValue(ByteString.copyFrom(value)) + .build() } fun convertCharacteristicError( - request: pb.CharacteristicAddress, - error: String? + request: pb.CharacteristicAddress, + error: String? ): pb.CharacteristicValueInfo { val characteristicAdress = createCharacteristicAddress(request) val failure = pb.GenericFailure.newBuilder() - .setCode(CharacteristicErrorType.UNKNOWN.code) - .setMessage(error ?: "Unknown error") + .setCode(CharacteristicErrorType.UNKNOWN.code) + .setMessage(error ?: "Unknown error") return pb.CharacteristicValueInfo.newBuilder() - .setCharacteristic(characteristicAdress) - .setFailure(failure) - .build() + .setCharacteristic(characteristicAdress) + .setFailure(failure) + .build() } fun convertWriteCharacteristicInfo( - request: pb.WriteCharacteristicRequest, - error: String? + request: pb.WriteCharacteristicRequest, + error: String? ): pb.WriteCharacteristicInfo { val builder = pb.WriteCharacteristicInfo.newBuilder() - .setCharacteristic(request.characteristic) + .setCharacteristic(request.characteristic) error?.let { val failure = pb.GenericFailure.newBuilder() - .setCode(CharacteristicErrorType.UNKNOWN.code) - .setMessage(error) + .setCode(CharacteristicErrorType.UNKNOWN.code) + .setMessage(error) builder.setFailure(failure) } @@ -126,86 +137,89 @@ class ProtobufMessageConverter { } fun convertNegotiateMtuInfo(result: MtuNegotiateResult): pb.NegotiateMtuInfo = - when (result) { - is MtuNegotiateSuccesful -> pb.NegotiateMtuInfo.newBuilder() - .setDeviceId(result.deviceId) - .setMtuSize(result.size) - .build() - is MtuNegotiateFailed -> { - - val failure = pb.GenericFailure.newBuilder() - .setCode(NegotiateMtuErrorType.UNKNOWN.code) - .setMessage(result.errorMessage) - .build() - - pb.NegotiateMtuInfo.newBuilder() - .setDeviceId(result.deviceId) - .setFailure(failure) - .build() - } + when (result) { + is MtuNegotiateSuccesful -> pb.NegotiateMtuInfo.newBuilder() + .setDeviceId(result.deviceId) + .setMtuSize(result.size) + .build() + + is MtuNegotiateFailed -> { + + val failure = pb.GenericFailure.newBuilder() + .setCode(NegotiateMtuErrorType.UNKNOWN.code) + .setMessage(result.errorMessage) + .build() + + pb.NegotiateMtuInfo.newBuilder() + .setDeviceId(result.deviceId) + .setFailure(failure) + .build() } + } fun convertRequestConnectionPriorityInfo( - result: RequestConnectionPriorityResult + result: RequestConnectionPriorityResult ): pb.ChangeConnectionPriorityInfo { return when (result) { is RequestConnectionPrioritySuccess -> pb.ChangeConnectionPriorityInfo.newBuilder() - .setDeviceId(result.deviceId) - .build() + .setDeviceId(result.deviceId) + .build() + is RequestConnectionPriorityFailed -> { val failure = pb.GenericFailure.newBuilder() - .setCode(0) - .setMessage(result.errorMessage) - .build() + .setCode(0) + .setMessage(result.errorMessage) + .build() pb.ChangeConnectionPriorityInfo.newBuilder() - .setDeviceId(result.deviceId) - .setFailure(failure) - .build() + .setDeviceId(result.deviceId) + .setFailure(failure) + .build() } } } fun convertDiscoverServicesInfo( - deviceId: String, - services: RxBleDeviceServices + deviceId: String, + services: RxBleDeviceServices ): pb.DiscoverServicesInfo { return pb.DiscoverServicesInfo.newBuilder() - .setDeviceId(deviceId) - .addAllServices(services.bluetoothGattServices.map { fromBluetoothGattService(it) }) - .build() + .setDeviceId(deviceId) + .addAllServices(services.bluetoothGattServices.map { fromBluetoothGattService(it) }) + .build() } private fun fromBluetoothGattService(gattService: BluetoothGattService): pb.DiscoveredService { return pb.DiscoveredService.newBuilder() - .setServiceUuid(createUuidFromParcelUuid(gattService.uuid)) - .addAllCharacteristicUuids(gattService.characteristics.map { createUuidFromParcelUuid(it.uuid) }) - .addAllCharacteristics(gattService.characteristics.map { - val prop = it.properties - val readable = (prop and BluetoothGattCharacteristic.PROPERTY_READ) > 0 - val write = (prop and BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 - val writeNoResp = (prop and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0 - val notify = (prop and BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0 - val indicate = (prop and BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0 - - pb.DiscoveredCharacteristic.newBuilder() - .setCharacteristicId(createUuidFromParcelUuid(it.uuid)) - .setServiceId(createUuidFromParcelUuid(it.service.uuid)) - .setIsReadable(readable) - .setIsWritableWithResponse(write) - .setIsWritableWithoutResponse(writeNoResp) - .setIsNotifiable(notify) - .setIsIndicatable(indicate) - .build() - }) - .addAllIncludedServices(gattService.includedServices.map { convertInternalService(it) }) - .build() + .setServiceUuid(createUuidFromParcelUuid(gattService.uuid)) + .addAllCharacteristicUuids(gattService.characteristics.map { createUuidFromParcelUuid(it.uuid) }) + .addAllCharacteristics(gattService.characteristics.map { + val prop = it.properties + val readable = (prop and BluetoothGattCharacteristic.PROPERTY_READ) > 0 + val write = (prop and BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 + val writeNoResp = + (prop and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0 + val notify = (prop and BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0 + val indicate = (prop and BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0 + + pb.DiscoveredCharacteristic.newBuilder() + .setCharacteristicId(createUuidFromParcelUuid(it.uuid)) + .setServiceId(createUuidFromParcelUuid(it.service.uuid)) + .setIsReadable(readable) + .setIsWritableWithResponse(write) + .setIsWritableWithoutResponse(writeNoResp) + .setIsNotifiable(notify) + .setIsIndicatable(indicate) + .build() + }) + .addAllIncludedServices(gattService.includedServices.map { convertInternalService(it) }) + .build() } private fun convertInternalService(gattService: BluetoothGattService): pb.DiscoveredService { val root = pb.DiscoveredService.newBuilder() - .setServiceUuid(createUuidFromParcelUuid(gattService.uuid)) - .addAllCharacteristicUuids(gattService.characteristics.map { createUuidFromParcelUuid(it.uuid) }) + .setServiceUuid(createUuidFromParcelUuid(gattService.uuid)) + .addAllCharacteristicUuids(gattService.characteristics.map { createUuidFromParcelUuid(it.uuid) }) val children = gattService.includedServices.map { convertInternalService(it) @@ -216,9 +230,9 @@ class ProtobufMessageConverter { private fun createCharacteristicAddress(request: pb.CharacteristicAddress): pb.CharacteristicAddress.Builder? { return pb.CharacteristicAddress.newBuilder() - .setDeviceId(request.deviceId) - .setServiceUuid(request.serviceUuid) - .setCharacteristicUuid(request.characteristicUuid) + .setDeviceId(request.deviceId) + .setServiceUuid(request.serviceUuid) + .setCharacteristicUuid(request.characteristicUuid) } private fun createServiceDataEntry(serviceData: Map): List { @@ -227,10 +241,12 @@ class ProtobufMessageConverter { // Needed ugly for-loop because we support API23 that does not support kotlin foreach for (entry: Map.Entry in serviceData) { - serviceDataEntries.add(pb.ServiceDataEntry.newBuilder() + serviceDataEntries.add( + pb.ServiceDataEntry.newBuilder() .setServiceUuid(createUuidFromParcelUuid(entry.key)) .setData(ByteString.copyFrom(entry.value)) - .build()) + .build() + ) } return serviceDataEntries @@ -245,4 +261,10 @@ class ProtobufMessageConverter { return pb.Uuid.newBuilder().setData(ByteString.copyFrom(convertedUuid)).build() } + + fun convertBondInfo(status: Int): pb.EstablishBondInfo { + return pb.EstablishBondInfo.newBuilder() + .setStatus(BondState.forNumber(status)) + .build() + } } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/BondingMode.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/BondingMode.kt deleted file mode 100644 index 1ac092a8..00000000 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/BondingMode.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.signify.hue.flutterreactiveble.model - -import android.content.Context - -enum class BondingMode(val code : Int) { - NONE(0), - NOT_REQUIRED(1), - REQUIRED(2) -} \ No newline at end of file diff --git a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnectorTest.kt b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnectorTest.kt index 76f2b35c..91a4f498 100644 --- a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnectorTest.kt +++ b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnectorTest.kt @@ -9,7 +9,6 @@ import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.verify -import io.mockk.verifySequence import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject import org.junit.jupiter.api.AfterEach diff --git a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift index 889dc99f..3e0690a0 100644 --- a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift +++ b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift @@ -107,6 +107,74 @@ struct DeviceScanInfo { fileprivate var _isConnectable: IsConnectable? = nil } +struct EstablishBondRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + var deviceID: String = String() + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +struct EstablishBondInfo { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + var status: EstablishBondInfo.BondState = .bondBonding + + var unknownFields = SwiftProtobuf.UnknownStorage() + + enum BondState: SwiftProtobuf.Enum { + typealias RawValue = Int + case bondBonding // = 0 + case bondBonded // = 1 + case bondNone // = 2 + case UNRECOGNIZED(Int) + + init() { + self = .bondBonding + } + + init?(rawValue: Int) { + switch rawValue { + case 0: self = .bondBonding + case 1: self = .bondBonded + case 2: self = .bondNone + default: self = .UNRECOGNIZED(rawValue) + } + } + + var rawValue: Int { + switch self { + case .bondBonding: return 0 + case .bondBonded: return 1 + case .bondNone: return 2 + case .UNRECOGNIZED(let i): return i + } + } + + } + + init() {} +} + +#if swift(>=4.2) + +extension EstablishBondInfo.BondState: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + static var allCases: [EstablishBondInfo.BondState] = [ + .bondBonding, + .bondBonded, + .bondNone, + ] +} + +#endif // swift(>=4.2) + struct ConnectToDeviceRequest { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -125,8 +193,6 @@ struct ConnectToDeviceRequest { var timeoutInMs: Int32 = 0 - var bondingMode: Int32 = 0 - var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -696,6 +762,9 @@ extension LaunchCompanionRequest: @unchecked Sendable {} extension AssociationInfo: @unchecked Sendable {} extension ScanForDevicesRequest: @unchecked Sendable {} extension DeviceScanInfo: @unchecked Sendable {} +extension EstablishBondRequest: @unchecked Sendable {} +extension EstablishBondInfo: @unchecked Sendable {} +extension EstablishBondInfo.BondState: @unchecked Sendable {} extension ConnectToDeviceRequest: @unchecked Sendable {} extension DeviceInfo: @unchecked Sendable {} extension GetDeviceNameRequest: @unchecked Sendable {} @@ -927,13 +996,84 @@ extension DeviceScanInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement } } +extension EstablishBondRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "EstablishBondRequest" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "deviceId"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.deviceID) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.deviceID.isEmpty { + try visitor.visitSingularStringField(value: self.deviceID, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: EstablishBondRequest, rhs: EstablishBondRequest) -> Bool { + if lhs.deviceID != rhs.deviceID {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension EstablishBondInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "EstablishBondInfo" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "status"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.status) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if self.status != .bondBonding { + try visitor.visitSingularEnumField(value: self.status, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: EstablishBondInfo, rhs: EstablishBondInfo) -> Bool { + if lhs.status != rhs.status {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension EstablishBondInfo.BondState: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "BOND_BONDING"), + 1: .same(proto: "BOND_BONDED"), + 2: .same(proto: "BOND_NONE"), + ] +} + extension ConnectToDeviceRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = "ConnectToDeviceRequest" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "deviceId"), 2: .same(proto: "servicesWithCharacteristicsToDiscover"), 3: .same(proto: "timeoutInMs"), - 4: .same(proto: "bondingMode"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -945,7 +1085,6 @@ extension ConnectToDeviceRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI case 1: try { try decoder.decodeSingularStringField(value: &self.deviceID) }() case 2: try { try decoder.decodeSingularMessageField(value: &self._servicesWithCharacteristicsToDiscover) }() case 3: try { try decoder.decodeSingularInt32Field(value: &self.timeoutInMs) }() - case 4: try { try decoder.decodeSingularInt32Field(value: &self.bondingMode) }() default: break } } @@ -965,9 +1104,6 @@ extension ConnectToDeviceRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI if self.timeoutInMs != 0 { try visitor.visitSingularInt32Field(value: self.timeoutInMs, fieldNumber: 3) } - if self.bondingMode != 0 { - try visitor.visitSingularInt32Field(value: self.bondingMode, fieldNumber: 4) - } try unknownFields.traverse(visitor: &visitor) } @@ -975,7 +1111,6 @@ extension ConnectToDeviceRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI if lhs.deviceID != rhs.deviceID {return false} if lhs._servicesWithCharacteristicsToDiscover != rhs._servicesWithCharacteristicsToDiscover {return false} if lhs.timeoutInMs != rhs.timeoutInMs {return false} - if lhs.bondingMode != rhs.bondingMode {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart index 214dc710..99ba5667 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart @@ -3,11 +3,12 @@ import 'package:reactive_ble_platform_interface/reactive_ble_platform_interface. import '../generated/bledata.pb.dart' as pb; abstract class ArgsToProtobufConverter { + pb.EstablishBondRequest createEstablishBondArgs(String id); + pb.ConnectToDeviceRequest createConnectToDeviceArgs( String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, - BondingMode? bondingMode, ); pb.GetDeviceNameRequest createGetDeviceNameArgs(String id); @@ -61,12 +62,15 @@ abstract class ArgsToProtobufConverter { class ArgsToProtobufConverterImpl implements ArgsToProtobufConverter { const ArgsToProtobufConverterImpl(); + @override + pb.EstablishBondRequest createEstablishBondArgs(String id) => + pb.EstablishBondRequest()..deviceId = id; + @override pb.ConnectToDeviceRequest createConnectToDeviceArgs( String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, - BondingMode? bondingMode, ) { final args = pb.ConnectToDeviceRequest()..deviceId = id; @@ -90,8 +94,6 @@ class ArgsToProtobufConverterImpl implements ArgsToProtobufConverter { pb.ServicesWithCharacteristics()..items.addAll(items); } - args.bondingMode = bondingMode?.index ?? BondingMode.none.index; - return args; } diff --git a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart index d3cf29ca..99be71be 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart @@ -5,6 +5,8 @@ import '../generated/bledata.pb.dart' as pb; import '../select_from.dart'; abstract class ProtobufConverter { + BondingStatus bondingStatusFrom(List data); + BleStatus bleStatusFrom(List data); AssociationInfo associationInfoFrom(List data); @@ -34,6 +36,25 @@ abstract class ProtobufConverter { class ProtobufConverterImpl implements ProtobufConverter { const ProtobufConverterImpl(); + @override + BondingStatus bondingStatusFrom(List data) { + final message = pb.EstablishBondInfo.fromBuffer(data); + switch (message.status) { + case pb.EstablishBondInfo_BondState.BOND_BONDING: + return BondingStatus.bonding; + case pb.EstablishBondInfo_BondState.BOND_BONDED: + return BondingStatus.bonded; + case pb.EstablishBondInfo_BondState.BOND_NONE: + return BondingStatus.none; + } + + throw ArgumentError.value( + message.status, + 'message.status', + 'Unknown bonding status', + ); + } + @override BleStatus bleStatusFrom(List data) { final message = pb.BleStatusInfo.fromBuffer(data); diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart index 99f484de..958e03f0 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart @@ -13,6 +13,10 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; +import 'bledata.pbenum.dart'; + +export 'bledata.pbenum.dart'; + class LaunchCompanionRequest extends $pb.GeneratedMessage { factory LaunchCompanionRequest() => create(); LaunchCompanionRequest._() : super(); @@ -277,6 +281,90 @@ class DeviceScanInfo extends $pb.GeneratedMessage { IsConnectable ensureIsConnectable() => $_ensure(7); } +class EstablishBondRequest extends $pb.GeneratedMessage { + factory EstablishBondRequest() => create(); + EstablishBondRequest._() : super(); + factory EstablishBondRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory EstablishBondRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EstablishBondRequest', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + EstablishBondRequest clone() => EstablishBondRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EstablishBondRequest copyWith(void Function(EstablishBondRequest) updates) => super.copyWith((message) => updates(message as EstablishBondRequest)) as EstablishBondRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EstablishBondRequest create() => EstablishBondRequest._(); + EstablishBondRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static EstablishBondRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EstablishBondRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get deviceId => $_getSZ(0); + @$pb.TagNumber(1) + set deviceId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasDeviceId() => $_has(0); + @$pb.TagNumber(1) + void clearDeviceId() => clearField(1); +} + +class EstablishBondInfo extends $pb.GeneratedMessage { + factory EstablishBondInfo() => create(); + EstablishBondInfo._() : super(); + factory EstablishBondInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory EstablishBondInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EstablishBondInfo', createEmptyInstance: create) + ..e(1, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: EstablishBondInfo_BondState.BOND_BONDING, valueOf: EstablishBondInfo_BondState.valueOf, enumValues: EstablishBondInfo_BondState.values) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + EstablishBondInfo clone() => EstablishBondInfo()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EstablishBondInfo copyWith(void Function(EstablishBondInfo) updates) => super.copyWith((message) => updates(message as EstablishBondInfo)) as EstablishBondInfo; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EstablishBondInfo create() => EstablishBondInfo._(); + EstablishBondInfo createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static EstablishBondInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EstablishBondInfo? _defaultInstance; + + @$pb.TagNumber(1) + EstablishBondInfo_BondState get status => $_getN(0); + @$pb.TagNumber(1) + set status(EstablishBondInfo_BondState v) { setField(1, v); } + @$pb.TagNumber(1) + $core.bool hasStatus() => $_has(0); + @$pb.TagNumber(1) + void clearStatus() => clearField(1); +} + class ConnectToDeviceRequest extends $pb.GeneratedMessage { factory ConnectToDeviceRequest() => create(); ConnectToDeviceRequest._() : super(); @@ -287,7 +375,6 @@ class ConnectToDeviceRequest extends $pb.GeneratedMessage { ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') ..aOM(2, _omitFieldNames ? '' : 'servicesWithCharacteristicsToDiscover', protoName: 'servicesWithCharacteristicsToDiscover', subBuilder: ServicesWithCharacteristics.create) ..a<$core.int>(3, _omitFieldNames ? '' : 'timeoutInMs', $pb.PbFieldType.O3, protoName: 'timeoutInMs') - ..a<$core.int>(4, _omitFieldNames ? '' : 'bondingMode', $pb.PbFieldType.O3, protoName: 'bondingMode') ..hasRequiredFields = false ; @@ -340,15 +427,6 @@ class ConnectToDeviceRequest extends $pb.GeneratedMessage { $core.bool hasTimeoutInMs() => $_has(2); @$pb.TagNumber(3) void clearTimeoutInMs() => clearField(3); - - @$pb.TagNumber(4) - $core.int get bondingMode => $_getIZ(3); - @$pb.TagNumber(4) - set bondingMode($core.int v) { $_setSignedInt32(3, v); } - @$pb.TagNumber(4) - $core.bool hasBondingMode() => $_has(3); - @$pb.TagNumber(4) - void clearBondingMode() => clearField(4); } class DeviceInfo extends $pb.GeneratedMessage { diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart index 213ad0f8..02e83cd2 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart @@ -9,3 +9,26 @@ // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +class EstablishBondInfo_BondState extends $pb.ProtobufEnum { + static const EstablishBondInfo_BondState BOND_BONDING = EstablishBondInfo_BondState._(0, _omitEnumNames ? '' : 'BOND_BONDING'); + static const EstablishBondInfo_BondState BOND_BONDED = EstablishBondInfo_BondState._(1, _omitEnumNames ? '' : 'BOND_BONDED'); + static const EstablishBondInfo_BondState BOND_NONE = EstablishBondInfo_BondState._(2, _omitEnumNames ? '' : 'BOND_NONE'); + + static const $core.List values = [ + BOND_BONDING, + BOND_BONDED, + BOND_NONE, + ]; + + static final $core.Map<$core.int, EstablishBondInfo_BondState> _byValue = $pb.ProtobufEnum.initByValue(values); + static EstablishBondInfo_BondState? valueOf($core.int value) => _byValue[value]; + + const EstablishBondInfo_BondState._($core.int v, $core.String n) : super(v, n); +} + + +const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart index 407bbee7..9ab5b5af 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart @@ -83,6 +83,43 @@ final $typed_data.Uint8List deviceScanInfoDescriptor = $convert.base64Decode( 'DHNlcnZpY2VVdWlkcxISCgRyc3NpGAUgASgFUgRyc3NpEjQKDWlzQ29ubmVjdGFibGUYCCABKA' 'syDi5Jc0Nvbm5lY3RhYmxlUg1pc0Nvbm5lY3RhYmxl'); +@$core.Deprecated('Use establishBondRequestDescriptor instead') +const EstablishBondRequest$json = { + '1': 'EstablishBondRequest', + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + ], +}; + +/// Descriptor for `EstablishBondRequest`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List establishBondRequestDescriptor = $convert.base64Decode( + 'ChRFc3RhYmxpc2hCb25kUmVxdWVzdBIaCghkZXZpY2VJZBgBIAEoCVIIZGV2aWNlSWQ='); + +@$core.Deprecated('Use establishBondInfoDescriptor instead') +const EstablishBondInfo$json = { + '1': 'EstablishBondInfo', + '2': [ + {'1': 'status', '3': 1, '4': 1, '5': 14, '6': '.EstablishBondInfo.BondState', '10': 'status'}, + ], + '4': [EstablishBondInfo_BondState$json], +}; + +@$core.Deprecated('Use establishBondInfoDescriptor instead') +const EstablishBondInfo_BondState$json = { + '1': 'BondState', + '2': [ + {'1': 'BOND_BONDING', '2': 0}, + {'1': 'BOND_BONDED', '2': 1}, + {'1': 'BOND_NONE', '2': 2}, + ], +}; + +/// Descriptor for `EstablishBondInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List establishBondInfoDescriptor = $convert.base64Decode( + 'ChFFc3RhYmxpc2hCb25kSW5mbxI0CgZzdGF0dXMYASABKA4yHC5Fc3RhYmxpc2hCb25kSW5mby' + '5Cb25kU3RhdGVSBnN0YXR1cyI9CglCb25kU3RhdGUSEAoMQk9ORF9CT05ESU5HEAASDwoLQk9O' + 'RF9CT05ERUQQARINCglCT05EX05PTkUQAg=='); + @$core.Deprecated('Use connectToDeviceRequestDescriptor instead') const ConnectToDeviceRequest$json = { '1': 'ConnectToDeviceRequest', @@ -90,7 +127,6 @@ const ConnectToDeviceRequest$json = { {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, {'1': 'servicesWithCharacteristicsToDiscover', '3': 2, '4': 1, '5': 11, '6': '.ServicesWithCharacteristics', '10': 'servicesWithCharacteristicsToDiscover'}, {'1': 'timeoutInMs', '3': 3, '4': 1, '5': 5, '10': 'timeoutInMs'}, - {'1': 'bondingMode', '3': 4, '4': 1, '5': 5, '10': 'bondingMode'}, ], }; @@ -99,8 +135,7 @@ final $typed_data.Uint8List connectToDeviceRequestDescriptor = $convert.base64De 'ChZDb25uZWN0VG9EZXZpY2VSZXF1ZXN0EhoKCGRldmljZUlkGAEgASgJUghkZXZpY2VJZBJyCi' 'VzZXJ2aWNlc1dpdGhDaGFyYWN0ZXJpc3RpY3NUb0Rpc2NvdmVyGAIgASgLMhwuU2VydmljZXNX' 'aXRoQ2hhcmFjdGVyaXN0aWNzUiVzZXJ2aWNlc1dpdGhDaGFyYWN0ZXJpc3RpY3NUb0Rpc2Nvdm' - 'VyEiAKC3RpbWVvdXRJbk1zGAMgASgFUgt0aW1lb3V0SW5NcxIgCgtib25kaW5nTW9kZRgEIAEo' - 'BVILYm9uZGluZ01vZGU='); + 'VyEiAKC3RpbWVvdXRJbk1zGAMgASgFUgt0aW1lb3V0SW5Ncw=='); @$core.Deprecated('Use deviceInfoDescriptor instead') const DeviceInfo$json = { diff --git a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart index 108b62cc..351e7331 100644 --- a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart +++ b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart @@ -142,12 +142,25 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { .asStream(); } + @override + Future establishBond(String deviceId) { + _logger?.log('Establish bond with device: $deviceId'); + + return _bleMethodChannel + .invokeMethod>( + "establishBond", + _argsToProtobufConverter + .createEstablishBondArgs(deviceId) + .writeToBuffer(), + ) + .then((data) => _protobufConverter.bondingStatusFrom(data!)); + } + @override Stream connectToDevice( String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, - BondingMode? bondingMode, ) { _logger?.log( 'Connect to device: $id, servicesWithCharacteristicsToDiscover: $servicesWithCharacteristicsToDiscover, timeout: $connectionTimeout', @@ -160,7 +173,6 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { id, servicesWithCharacteristicsToDiscover, connectionTimeout, - bondingMode, ) .writeToBuffer(), ) @@ -178,7 +190,6 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { _argsToProtobufConverter.createGetDeviceNameArgs(id).writeToBuffer(), ) .then((data) => _protobufConverter.deviceNameFrom(data!)); - ; } @override diff --git a/packages/reactive_ble_mobile/protos/bledata.proto b/packages/reactive_ble_mobile/protos/bledata.proto index bb4648b9..c7a0cb74 100644 --- a/packages/reactive_ble_mobile/protos/bledata.proto +++ b/packages/reactive_ble_mobile/protos/bledata.proto @@ -29,11 +29,25 @@ message DeviceScanInfo { IsConnectable isConnectable = 8; } +message EstablishBondRequest { + string deviceId = 1; +} + +message EstablishBondInfo { + enum BondState { + UNKNOWN = 0; + BOND_NONE = 10; + BOND_BONDING = 11; + BOND_BONDED = 12; + } + + BondState status = 1; +} + message ConnectToDeviceRequest { string deviceId = 1; ServicesWithCharacteristics servicesWithCharacteristicsToDiscover = 2; int32 timeoutInMs = 3; - int32 bondingMode = 4; } message DeviceInfo { diff --git a/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart b/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart index c4ec9fd6..c976bc89 100644 --- a/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart +++ b/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart @@ -11,7 +11,6 @@ void main() { const deviceId = '123'; Map>? servicesToDiscover; Duration? timeout; - BondingMode? bondingMode; late pb.ConnectToDeviceRequest result; group('And servicesToDiscover is not null', () { @@ -25,7 +24,7 @@ void main() { setUp(() { timeout = const Duration(seconds: 2); result = _sut.createConnectToDeviceArgs( - deviceId, servicesToDiscover, timeout, bondingMode); + deviceId, servicesToDiscover, timeout); }); test('It converts deviceId', () { @@ -50,7 +49,7 @@ void main() { setUp(() { timeout = null; result = _sut.createConnectToDeviceArgs( - deviceId, servicesToDiscover, timeout, bondingMode); + deviceId, servicesToDiscover, timeout); }); test('It sets timeout to default value', () { expect(result.timeoutInMs, 0); @@ -62,7 +61,7 @@ void main() { setUp(() { servicesToDiscover = null; result = _sut.createConnectToDeviceArgs( - deviceId, servicesToDiscover, timeout, bondingMode); + deviceId, servicesToDiscover, timeout); }); test('It converts servicesToDiscover to default', () { diff --git a/packages/reactive_ble_mobile/test/reactive_ble_platform_test.dart b/packages/reactive_ble_mobile/test/reactive_ble_platform_test.dart index 2b0ff17b..d9a20f98 100644 --- a/packages/reactive_ble_mobile/test/reactive_ble_platform_test.dart +++ b/packages/reactive_ble_mobile/test/reactive_ble_platform_test.dart @@ -65,14 +65,14 @@ void main() { late pb.ConnectToDeviceRequest request; setUp(() { request = pb.ConnectToDeviceRequest(); - when(_argsConverter.createConnectToDeviceArgs('id', any, any, any)) + when(_argsConverter.createConnectToDeviceArgs('id', any, any)) .thenReturn(request); }); test( 'It invokes methodchannel with correct method and arguments', () async { - await _sut.connectToDevice('id', {}, null, null).first; + await _sut.connectToDevice('id', {}, null).first; verify(_methodChannel.invokeMethod( 'connectToDevice', request.writeToBuffer(), @@ -81,7 +81,7 @@ void main() { ); test('It emits 1 item', () async { - final length = await _sut.connectToDevice('id', {}, null, null).length; + final length = await _sut.connectToDevice('id', {}, null).length; expect(length, 1); }); }); diff --git a/packages/reactive_ble_platform_interface/lib/src/model/bonding_mode.dart b/packages/reactive_ble_platform_interface/lib/src/model/bonding_mode.dart deleted file mode 100644 index dc5b1bb9..00000000 --- a/packages/reactive_ble_platform_interface/lib/src/model/bonding_mode.dart +++ /dev/null @@ -1,13 +0,0 @@ -/// Specify the bonding mode for the device. -enum BondingMode { - /// Bonding will not be attempted. - none, - - /// Bonding will be attempted, but will not be required. - /// If bonding fails, the connection will still be established. - notRequired, - - /// Bonding will be attempted and will be required. - /// If bonding fails, the connection will be terminated. - required, -} diff --git a/packages/reactive_ble_platform_interface/lib/src/model/bonding_status.dart b/packages/reactive_ble_platform_interface/lib/src/model/bonding_status.dart new file mode 100644 index 00000000..62320b9d --- /dev/null +++ b/packages/reactive_ble_platform_interface/lib/src/model/bonding_status.dart @@ -0,0 +1,10 @@ +enum BondingStatus { + /// The device is not bonded. + none, + + /// Bonding is in progress + bonding, + + /// Bonding is complete + bonded, +} diff --git a/packages/reactive_ble_platform_interface/lib/src/models.dart b/packages/reactive_ble_platform_interface/lib/src/models.dart index 08e33599..32c35e90 100644 --- a/packages/reactive_ble_platform_interface/lib/src/models.dart +++ b/packages/reactive_ble_platform_interface/lib/src/models.dart @@ -1,6 +1,6 @@ export './model/association_info.dart'; export './model/ble_status.dart'; -export './model/bonding_mode.dart'; +export './model/bonding_status.dart'; export './model/characteristic_value.dart'; export './model/clear_gatt_cache_error.dart'; export './model/connection_priority.dart'; diff --git a/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart b/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart index 833eead7..cfea257f 100644 --- a/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart +++ b/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart @@ -97,6 +97,11 @@ abstract class ReactiveBlePlatform extends PlatformInterface { throw UnimplementedError('clearGattCache() has not been implemented.'); } + /// Creates a bond with the peripheral. This is a Android-only operation. + Future establishBond(String deviceId) { + throw UnimplementedError('establishBond() has not been implemented.'); + } + /// Connects to a specific device and the connection remains `established` until /// the stream is `cancelled` or the connection is closed by the peripheral. /// @@ -105,7 +110,6 @@ abstract class ReactiveBlePlatform extends PlatformInterface { String id, Map>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, - BondingMode? bondingMode, ) { throw UnimplementedError('connectToDevice has not been implemented.'); } From 686cd2c399169ccff5ea6de90bebb9c9158349bb Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sun, 9 Jul 2023 09:19:06 +0100 Subject: [PATCH 06/17] establishBond => establishBonding --- .../lib/src/device_connector.dart | 6 +- .../lib/src/reactive_ble.dart | 4 +- .../flutterreactiveble/PluginController.kt | 2 +- .../DeviceConnectionHandler.kt | 2 +- .../converters/ProtobufMessageConverter.kt | 9 ++- .../ios/Classes/BleData/bledata.pb.swift | 65 ++++++++++--------- .../converter/args_to_protubuf_converter.dart | 6 +- .../lib/src/converter/protobuf_converter.dart | 8 +-- .../lib/src/generated/bledata.pb.dart | 58 ++++++++--------- .../lib/src/generated/bledata.pbenum.dart | 22 ++++--- .../lib/src/generated/bledata.pbjson.dart | 43 ++++++------ .../lib/src/reactive_ble_mobile_platform.dart | 6 +- .../reactive_ble_mobile/protos/bledata.proto | 4 +- .../src/reactive_ble_platform_interface.dart | 4 +- 14 files changed, 123 insertions(+), 116 deletions(-) diff --git a/packages/flutter_reactive_ble/lib/src/device_connector.dart b/packages/flutter_reactive_ble/lib/src/device_connector.dart index 700d6e81..369687d6 100644 --- a/packages/flutter_reactive_ble/lib/src/device_connector.dart +++ b/packages/flutter_reactive_ble/lib/src/device_connector.dart @@ -6,7 +6,7 @@ import 'package:reactive_ble_platform_interface/reactive_ble_platform_interface. abstract class DeviceConnector { Stream get deviceConnectionStateUpdateStream; - Future establishBond({ + Future establishBonding({ required String deviceId, }); @@ -55,8 +55,8 @@ class DeviceConnectorImpl implements DeviceConnector { _blePlatform.connectionUpdateStream; @override - Future establishBond({required String deviceId}) async => - _blePlatform.establishBond(deviceId); + Future establishBonding({required String deviceId}) async => + _blePlatform.establishBonding(deviceId); @override Stream connect({ diff --git a/packages/flutter_reactive_ble/lib/src/reactive_ble.dart b/packages/flutter_reactive_ble/lib/src/reactive_ble.dart index 8ac5a129..075239e0 100644 --- a/packages/flutter_reactive_ble/lib/src/reactive_ble.dart +++ b/packages/flutter_reactive_ble/lib/src/reactive_ble.dart @@ -254,10 +254,10 @@ class FlutterReactiveBle { ); } - Future establishBond({required String deviceId}) async { + Future establishBonding({required String deviceId}) async { await initialize(); - return _deviceConnector.establishBond(deviceId: deviceId); + return _deviceConnector.establishBonding(deviceId: deviceId); } /// Establishes a connection to a BLE device. diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt index 56a24265..9901c77d 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt @@ -125,7 +125,7 @@ class PluginController : PluginRegistry.ActivityResultListener { } private fun establishBond(call: MethodCall, result: Result) { - val establishBondMessage = pb.EstablishBondRequest.parseFrom(call.arguments as ByteArray) + val establishBondMessage = pb.EstablishBondingRequest.parseFrom(call.arguments as ByteArray) deviceConnectionHandler.establishBond(establishBondMessage).subscribe({ result.success(protoConverter.convertBondInfo(it).toByteArray()) }, { diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt index 2fcc71f5..c9801b18 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt @@ -27,7 +27,7 @@ class DeviceConnectionHandler(private val bleClient: com.signify.hue.flutterreac connectionUpdatesDisposable.dispose() } - fun establishBond(connectToDeviceMessage: pb.EstablishBondRequest): Single { + fun establishBond(connectToDeviceMessage: pb.EstablishBondingRequest): Single { return bleClient.establishBond(connectToDeviceMessage.deviceId) } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt index 31e5ca17..c49087b9 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt @@ -4,8 +4,7 @@ import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattService import com.google.protobuf.ByteString import com.polidea.rxandroidble2.RxBleDeviceServices -import com.signify.hue.flutterreactiveble.ProtobufModel.EstablishBondInfo -import com.signify.hue.flutterreactiveble.ProtobufModel.EstablishBondInfo.BondState +import com.signify.hue.flutterreactiveble.ProtobufModel import com.signify.hue.flutterreactiveble.ble.ConnectionUpdateSuccess import com.signify.hue.flutterreactiveble.ble.MtuNegotiateFailed import com.signify.hue.flutterreactiveble.ble.MtuNegotiateResult @@ -262,9 +261,9 @@ class ProtobufMessageConverter { return pb.Uuid.newBuilder().setData(ByteString.copyFrom(convertedUuid)).build() } - fun convertBondInfo(status: Int): pb.EstablishBondInfo { - return pb.EstablishBondInfo.newBuilder() - .setStatus(BondState.forNumber(status)) + fun convertBondInfo(status: Int): pb.EstablishBondingInfo { + return pb.EstablishBondingInfo.newBuilder() + .setStatus(ProtobufModel.EstablishBondingInfo.BondState.forNumber(status)) .build() } } diff --git a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift index 3e0690a0..b29ea840 100644 --- a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift +++ b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift @@ -107,7 +107,7 @@ struct DeviceScanInfo { fileprivate var _isConnectable: IsConnectable? = nil } -struct EstablishBondRequest { +struct EstablishBondingRequest { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -119,40 +119,43 @@ struct EstablishBondRequest { init() {} } -struct EstablishBondInfo { +struct EstablishBondingInfo { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - var status: EstablishBondInfo.BondState = .bondBonding + var status: EstablishBondingInfo.BondState = .unknown var unknownFields = SwiftProtobuf.UnknownStorage() enum BondState: SwiftProtobuf.Enum { typealias RawValue = Int - case bondBonding // = 0 - case bondBonded // = 1 - case bondNone // = 2 + case unknown // = 0 + case bondNone // = 10 + case bondBonding // = 11 + case bondBonded // = 12 case UNRECOGNIZED(Int) init() { - self = .bondBonding + self = .unknown } init?(rawValue: Int) { switch rawValue { - case 0: self = .bondBonding - case 1: self = .bondBonded - case 2: self = .bondNone + case 0: self = .unknown + case 10: self = .bondNone + case 11: self = .bondBonding + case 12: self = .bondBonded default: self = .UNRECOGNIZED(rawValue) } } var rawValue: Int { switch self { - case .bondBonding: return 0 - case .bondBonded: return 1 - case .bondNone: return 2 + case .unknown: return 0 + case .bondNone: return 10 + case .bondBonding: return 11 + case .bondBonded: return 12 case .UNRECOGNIZED(let i): return i } } @@ -164,12 +167,13 @@ struct EstablishBondInfo { #if swift(>=4.2) -extension EstablishBondInfo.BondState: CaseIterable { +extension EstablishBondingInfo.BondState: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static var allCases: [EstablishBondInfo.BondState] = [ + static var allCases: [EstablishBondingInfo.BondState] = [ + .unknown, + .bondNone, .bondBonding, .bondBonded, - .bondNone, ] } @@ -762,9 +766,9 @@ extension LaunchCompanionRequest: @unchecked Sendable {} extension AssociationInfo: @unchecked Sendable {} extension ScanForDevicesRequest: @unchecked Sendable {} extension DeviceScanInfo: @unchecked Sendable {} -extension EstablishBondRequest: @unchecked Sendable {} -extension EstablishBondInfo: @unchecked Sendable {} -extension EstablishBondInfo.BondState: @unchecked Sendable {} +extension EstablishBondingRequest: @unchecked Sendable {} +extension EstablishBondingInfo: @unchecked Sendable {} +extension EstablishBondingInfo.BondState: @unchecked Sendable {} extension ConnectToDeviceRequest: @unchecked Sendable {} extension DeviceInfo: @unchecked Sendable {} extension GetDeviceNameRequest: @unchecked Sendable {} @@ -996,8 +1000,8 @@ extension DeviceScanInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement } } -extension EstablishBondRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - static let protoMessageName: String = "EstablishBondRequest" +extension EstablishBondingRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "EstablishBondingRequest" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "deviceId"), ] @@ -1021,15 +1025,15 @@ extension EstablishBondRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImp try unknownFields.traverse(visitor: &visitor) } - static func ==(lhs: EstablishBondRequest, rhs: EstablishBondRequest) -> Bool { + static func ==(lhs: EstablishBondingRequest, rhs: EstablishBondingRequest) -> Bool { if lhs.deviceID != rhs.deviceID {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension EstablishBondInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - static let protoMessageName: String = "EstablishBondInfo" +extension EstablishBondingInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "EstablishBondingInfo" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "status"), ] @@ -1047,24 +1051,25 @@ extension EstablishBondInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem } func traverse(visitor: inout V) throws { - if self.status != .bondBonding { + if self.status != .unknown { try visitor.visitSingularEnumField(value: self.status, fieldNumber: 1) } try unknownFields.traverse(visitor: &visitor) } - static func ==(lhs: EstablishBondInfo, rhs: EstablishBondInfo) -> Bool { + static func ==(lhs: EstablishBondingInfo, rhs: EstablishBondingInfo) -> Bool { if lhs.status != rhs.status {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension EstablishBondInfo.BondState: SwiftProtobuf._ProtoNameProviding { +extension EstablishBondingInfo.BondState: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "BOND_BONDING"), - 1: .same(proto: "BOND_BONDED"), - 2: .same(proto: "BOND_NONE"), + 0: .same(proto: "UNKNOWN"), + 10: .same(proto: "BOND_NONE"), + 11: .same(proto: "BOND_BONDING"), + 12: .same(proto: "BOND_BONDED"), ] } diff --git a/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart index 99ba5667..7e600177 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart @@ -3,7 +3,7 @@ import 'package:reactive_ble_platform_interface/reactive_ble_platform_interface. import '../generated/bledata.pb.dart' as pb; abstract class ArgsToProtobufConverter { - pb.EstablishBondRequest createEstablishBondArgs(String id); + pb.EstablishBondingRequest createEstablishBondingArgs(String id); pb.ConnectToDeviceRequest createConnectToDeviceArgs( String id, @@ -63,8 +63,8 @@ class ArgsToProtobufConverterImpl implements ArgsToProtobufConverter { const ArgsToProtobufConverterImpl(); @override - pb.EstablishBondRequest createEstablishBondArgs(String id) => - pb.EstablishBondRequest()..deviceId = id; + pb.EstablishBondingRequest createEstablishBondingArgs(String id) => + pb.EstablishBondingRequest()..deviceId = id; @override pb.ConnectToDeviceRequest createConnectToDeviceArgs( diff --git a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart index 99be71be..552624bb 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart @@ -38,13 +38,13 @@ class ProtobufConverterImpl implements ProtobufConverter { @override BondingStatus bondingStatusFrom(List data) { - final message = pb.EstablishBondInfo.fromBuffer(data); + final message = pb.EstablishBondingInfo.fromBuffer(data); switch (message.status) { - case pb.EstablishBondInfo_BondState.BOND_BONDING: + case pb.EstablishBondingInfo_BondState.BOND_BONDING: return BondingStatus.bonding; - case pb.EstablishBondInfo_BondState.BOND_BONDED: + case pb.EstablishBondingInfo_BondState.BOND_BONDED: return BondingStatus.bonded; - case pb.EstablishBondInfo_BondState.BOND_NONE: + case pb.EstablishBondingInfo_BondState.BOND_NONE: return BondingStatus.none; } diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart index 958e03f0..2276d15c 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart @@ -281,13 +281,13 @@ class DeviceScanInfo extends $pb.GeneratedMessage { IsConnectable ensureIsConnectable() => $_ensure(7); } -class EstablishBondRequest extends $pb.GeneratedMessage { - factory EstablishBondRequest() => create(); - EstablishBondRequest._() : super(); - factory EstablishBondRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory EstablishBondRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); +class EstablishBondingRequest extends $pb.GeneratedMessage { + factory EstablishBondingRequest() => create(); + EstablishBondingRequest._() : super(); + factory EstablishBondingRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory EstablishBondingRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EstablishBondRequest', createEmptyInstance: create) + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EstablishBondingRequest', createEmptyInstance: create) ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') ..hasRequiredFields = false ; @@ -296,22 +296,22 @@ class EstablishBondRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') - EstablishBondRequest clone() => EstablishBondRequest()..mergeFromMessage(this); + EstablishBondingRequest clone() => EstablishBondingRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - EstablishBondRequest copyWith(void Function(EstablishBondRequest) updates) => super.copyWith((message) => updates(message as EstablishBondRequest)) as EstablishBondRequest; + EstablishBondingRequest copyWith(void Function(EstablishBondingRequest) updates) => super.copyWith((message) => updates(message as EstablishBondingRequest)) as EstablishBondingRequest; $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static EstablishBondRequest create() => EstablishBondRequest._(); - EstablishBondRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); + static EstablishBondingRequest create() => EstablishBondingRequest._(); + EstablishBondingRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static EstablishBondRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static EstablishBondRequest? _defaultInstance; + static EstablishBondingRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EstablishBondingRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get deviceId => $_getSZ(0); @@ -323,14 +323,14 @@ class EstablishBondRequest extends $pb.GeneratedMessage { void clearDeviceId() => clearField(1); } -class EstablishBondInfo extends $pb.GeneratedMessage { - factory EstablishBondInfo() => create(); - EstablishBondInfo._() : super(); - factory EstablishBondInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory EstablishBondInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); +class EstablishBondingInfo extends $pb.GeneratedMessage { + factory EstablishBondingInfo() => create(); + EstablishBondingInfo._() : super(); + factory EstablishBondingInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory EstablishBondingInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EstablishBondInfo', createEmptyInstance: create) - ..e(1, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: EstablishBondInfo_BondState.BOND_BONDING, valueOf: EstablishBondInfo_BondState.valueOf, enumValues: EstablishBondInfo_BondState.values) + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EstablishBondingInfo', createEmptyInstance: create) + ..e(1, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: EstablishBondingInfo_BondState.UNKNOWN, valueOf: EstablishBondingInfo_BondState.valueOf, enumValues: EstablishBondingInfo_BondState.values) ..hasRequiredFields = false ; @@ -338,27 +338,27 @@ class EstablishBondInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') - EstablishBondInfo clone() => EstablishBondInfo()..mergeFromMessage(this); + EstablishBondingInfo clone() => EstablishBondingInfo()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - EstablishBondInfo copyWith(void Function(EstablishBondInfo) updates) => super.copyWith((message) => updates(message as EstablishBondInfo)) as EstablishBondInfo; + EstablishBondingInfo copyWith(void Function(EstablishBondingInfo) updates) => super.copyWith((message) => updates(message as EstablishBondingInfo)) as EstablishBondingInfo; $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static EstablishBondInfo create() => EstablishBondInfo._(); - EstablishBondInfo createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); + static EstablishBondingInfo create() => EstablishBondingInfo._(); + EstablishBondingInfo createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static EstablishBondInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static EstablishBondInfo? _defaultInstance; + static EstablishBondingInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EstablishBondingInfo? _defaultInstance; @$pb.TagNumber(1) - EstablishBondInfo_BondState get status => $_getN(0); + EstablishBondingInfo_BondState get status => $_getN(0); @$pb.TagNumber(1) - set status(EstablishBondInfo_BondState v) { setField(1, v); } + set status(EstablishBondingInfo_BondState v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasStatus() => $_has(0); @$pb.TagNumber(1) diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart index 02e83cd2..d37b7722 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart @@ -13,21 +13,23 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; -class EstablishBondInfo_BondState extends $pb.ProtobufEnum { - static const EstablishBondInfo_BondState BOND_BONDING = EstablishBondInfo_BondState._(0, _omitEnumNames ? '' : 'BOND_BONDING'); - static const EstablishBondInfo_BondState BOND_BONDED = EstablishBondInfo_BondState._(1, _omitEnumNames ? '' : 'BOND_BONDED'); - static const EstablishBondInfo_BondState BOND_NONE = EstablishBondInfo_BondState._(2, _omitEnumNames ? '' : 'BOND_NONE'); - - static const $core.List values = [ +class EstablishBondingInfo_BondState extends $pb.ProtobufEnum { + static const EstablishBondingInfo_BondState UNKNOWN = EstablishBondingInfo_BondState._(0, _omitEnumNames ? '' : 'UNKNOWN'); + static const EstablishBondingInfo_BondState BOND_NONE = EstablishBondingInfo_BondState._(10, _omitEnumNames ? '' : 'BOND_NONE'); + static const EstablishBondingInfo_BondState BOND_BONDING = EstablishBondingInfo_BondState._(11, _omitEnumNames ? '' : 'BOND_BONDING'); + static const EstablishBondingInfo_BondState BOND_BONDED = EstablishBondingInfo_BondState._(12, _omitEnumNames ? '' : 'BOND_BONDED'); + + static const $core.List values = [ + UNKNOWN, + BOND_NONE, BOND_BONDING, BOND_BONDED, - BOND_NONE, ]; - static final $core.Map<$core.int, EstablishBondInfo_BondState> _byValue = $pb.ProtobufEnum.initByValue(values); - static EstablishBondInfo_BondState? valueOf($core.int value) => _byValue[value]; + static final $core.Map<$core.int, EstablishBondingInfo_BondState> _byValue = $pb.ProtobufEnum.initByValue(values); + static EstablishBondingInfo_BondState? valueOf($core.int value) => _byValue[value]; - const EstablishBondInfo_BondState._($core.int v, $core.String n) : super(v, n); + const EstablishBondingInfo_BondState._($core.int v, $core.String n) : super(v, n); } diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart index 9ab5b5af..793d3520 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart @@ -83,42 +83,43 @@ final $typed_data.Uint8List deviceScanInfoDescriptor = $convert.base64Decode( 'DHNlcnZpY2VVdWlkcxISCgRyc3NpGAUgASgFUgRyc3NpEjQKDWlzQ29ubmVjdGFibGUYCCABKA' 'syDi5Jc0Nvbm5lY3RhYmxlUg1pc0Nvbm5lY3RhYmxl'); -@$core.Deprecated('Use establishBondRequestDescriptor instead') -const EstablishBondRequest$json = { - '1': 'EstablishBondRequest', +@$core.Deprecated('Use establishBondingRequestDescriptor instead') +const EstablishBondingRequest$json = { + '1': 'EstablishBondingRequest', '2': [ {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, ], }; -/// Descriptor for `EstablishBondRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List establishBondRequestDescriptor = $convert.base64Decode( - 'ChRFc3RhYmxpc2hCb25kUmVxdWVzdBIaCghkZXZpY2VJZBgBIAEoCVIIZGV2aWNlSWQ='); +/// Descriptor for `EstablishBondingRequest`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List establishBondingRequestDescriptor = $convert.base64Decode( + 'ChdFc3RhYmxpc2hCb25kaW5nUmVxdWVzdBIaCghkZXZpY2VJZBgBIAEoCVIIZGV2aWNlSWQ='); -@$core.Deprecated('Use establishBondInfoDescriptor instead') -const EstablishBondInfo$json = { - '1': 'EstablishBondInfo', +@$core.Deprecated('Use establishBondingInfoDescriptor instead') +const EstablishBondingInfo$json = { + '1': 'EstablishBondingInfo', '2': [ - {'1': 'status', '3': 1, '4': 1, '5': 14, '6': '.EstablishBondInfo.BondState', '10': 'status'}, + {'1': 'status', '3': 1, '4': 1, '5': 14, '6': '.EstablishBondingInfo.BondState', '10': 'status'}, ], - '4': [EstablishBondInfo_BondState$json], + '4': [EstablishBondingInfo_BondState$json], }; -@$core.Deprecated('Use establishBondInfoDescriptor instead') -const EstablishBondInfo_BondState$json = { +@$core.Deprecated('Use establishBondingInfoDescriptor instead') +const EstablishBondingInfo_BondState$json = { '1': 'BondState', '2': [ - {'1': 'BOND_BONDING', '2': 0}, - {'1': 'BOND_BONDED', '2': 1}, - {'1': 'BOND_NONE', '2': 2}, + {'1': 'UNKNOWN', '2': 0}, + {'1': 'BOND_NONE', '2': 10}, + {'1': 'BOND_BONDING', '2': 11}, + {'1': 'BOND_BONDED', '2': 12}, ], }; -/// Descriptor for `EstablishBondInfo`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List establishBondInfoDescriptor = $convert.base64Decode( - 'ChFFc3RhYmxpc2hCb25kSW5mbxI0CgZzdGF0dXMYASABKA4yHC5Fc3RhYmxpc2hCb25kSW5mby' - '5Cb25kU3RhdGVSBnN0YXR1cyI9CglCb25kU3RhdGUSEAoMQk9ORF9CT05ESU5HEAASDwoLQk9O' - 'RF9CT05ERUQQARINCglCT05EX05PTkUQAg=='); +/// Descriptor for `EstablishBondingInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List establishBondingInfoDescriptor = $convert.base64Decode( + 'ChRFc3RhYmxpc2hCb25kaW5nSW5mbxI3CgZzdGF0dXMYASABKA4yHy5Fc3RhYmxpc2hCb25kaW' + '5nSW5mby5Cb25kU3RhdGVSBnN0YXR1cyJKCglCb25kU3RhdGUSCwoHVU5LTk9XThAAEg0KCUJP' + 'TkRfTk9ORRAKEhAKDEJPTkRfQk9ORElORxALEg8KC0JPTkRfQk9OREVEEAw='); @$core.Deprecated('Use connectToDeviceRequestDescriptor instead') const ConnectToDeviceRequest$json = { diff --git a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart index 351e7331..bdf17a9b 100644 --- a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart +++ b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart @@ -143,14 +143,14 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { } @override - Future establishBond(String deviceId) { + Future establishBonding(String deviceId) { _logger?.log('Establish bond with device: $deviceId'); return _bleMethodChannel .invokeMethod>( - "establishBond", + "establishBonding", _argsToProtobufConverter - .createEstablishBondArgs(deviceId) + .createEstablishBondingArgs(deviceId) .writeToBuffer(), ) .then((data) => _protobufConverter.bondingStatusFrom(data!)); diff --git a/packages/reactive_ble_mobile/protos/bledata.proto b/packages/reactive_ble_mobile/protos/bledata.proto index c7a0cb74..45d93677 100644 --- a/packages/reactive_ble_mobile/protos/bledata.proto +++ b/packages/reactive_ble_mobile/protos/bledata.proto @@ -29,11 +29,11 @@ message DeviceScanInfo { IsConnectable isConnectable = 8; } -message EstablishBondRequest { +message EstablishBondingRequest { string deviceId = 1; } -message EstablishBondInfo { +message EstablishBondingInfo { enum BondState { UNKNOWN = 0; BOND_NONE = 10; diff --git a/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart b/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart index cfea257f..e7ab2ebf 100644 --- a/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart +++ b/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart @@ -98,8 +98,8 @@ abstract class ReactiveBlePlatform extends PlatformInterface { } /// Creates a bond with the peripheral. This is a Android-only operation. - Future establishBond(String deviceId) { - throw UnimplementedError('establishBond() has not been implemented.'); + Future establishBonding(String deviceId) { + throw UnimplementedError('establishBonding() has not been implemented.'); } /// Connects to a specific device and the connection remains `established` until From 814cb5a2dc34b9eb501a7fb5ec30b7dec0760d0b Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sun, 9 Jul 2023 09:22:19 +0100 Subject: [PATCH 07/17] deviceNamePattern --- .../channelhandlers/CompanionHandler.kt | 6 +----- .../ios/Classes/BleData/bledata.pb.swift | 12 ++++++------ .../src/converter/args_to_protubuf_converter.dart | 6 +++--- .../lib/src/generated/bledata.pb.dart | 10 +++++----- .../lib/src/generated/bledata.pbjson.dart | 8 ++++---- .../lib/src/reactive_ble_mobile_platform.dart | 2 +- packages/reactive_ble_mobile/protos/bledata.proto | 2 +- 7 files changed, 21 insertions(+), 25 deletions(-) diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt index 661e9f4d..24e6936a 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt @@ -33,13 +33,11 @@ class CompanionHandler(private val context: Context, private val bleClient: BleC result: MethodChannel.Result ) { val deviceFilter: BluetoothLeDeviceFilter = BluetoothLeDeviceFilter.Builder() - .setNamePattern(Pattern.compile(parseFrom.pattern)) + .setNamePattern(Pattern.compile(parseFrom.deviceNamePattern)) .build() val pairingRequestBuilder = AssociationRequest.Builder() - // Find only devices that match this request filter. .addDeviceFilter(deviceFilter) - // Stop scanning as soon as one device matching the filter is found. .setSingleDevice(parseFrom.singleDeviceScan) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -55,8 +53,6 @@ class CompanionHandler(private val context: Context, private val bleClient: BleC deviceManager.associate(pairingRequestBuilder.build(), executor, object : CompanionDeviceManager.Callback() { - // // Called when a device is found. Launch the IntentSender so the user - // // can select the device they want to pair with. override fun onAssociationPending(intentSender: IntentSender) { Log.d(TAG, "onAssociationPending: $intentSender") intentSender.let { diff --git a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift index b29ea840..54278230 100644 --- a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift +++ b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift @@ -25,7 +25,7 @@ struct LaunchCompanionRequest { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - var pattern: String = String() + var deviceNamePattern: String = String() var singleDeviceScan: Bool = false @@ -805,7 +805,7 @@ extension IsConnectable: @unchecked Sendable {} extension LaunchCompanionRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = "LaunchCompanionRequest" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "pattern"), + 1: .same(proto: "deviceNamePattern"), 2: .same(proto: "singleDeviceScan"), 3: .same(proto: "forceConfirmation"), ] @@ -816,7 +816,7 @@ extension LaunchCompanionRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI // allocates stack space for every case branch when no optimizations are // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { - case 1: try { try decoder.decodeSingularStringField(value: &self.pattern) }() + case 1: try { try decoder.decodeSingularStringField(value: &self.deviceNamePattern) }() case 2: try { try decoder.decodeSingularBoolField(value: &self.singleDeviceScan) }() case 3: try { try decoder.decodeSingularBoolField(value: &self.forceConfirmation) }() default: break @@ -825,8 +825,8 @@ extension LaunchCompanionRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI } func traverse(visitor: inout V) throws { - if !self.pattern.isEmpty { - try visitor.visitSingularStringField(value: self.pattern, fieldNumber: 1) + if !self.deviceNamePattern.isEmpty { + try visitor.visitSingularStringField(value: self.deviceNamePattern, fieldNumber: 1) } if self.singleDeviceScan != false { try visitor.visitSingularBoolField(value: self.singleDeviceScan, fieldNumber: 2) @@ -838,7 +838,7 @@ extension LaunchCompanionRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI } static func ==(lhs: LaunchCompanionRequest, rhs: LaunchCompanionRequest) -> Bool { - if lhs.pattern != rhs.pattern {return false} + if lhs.deviceNamePattern != rhs.deviceNamePattern {return false} if lhs.singleDeviceScan != rhs.singleDeviceScan {return false} if lhs.forceConfirmation != rhs.forceConfirmation {return false} if lhs.unknownFields != rhs.unknownFields {return false} diff --git a/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart index 7e600177..75360301 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/args_to_protubuf_converter.dart @@ -43,7 +43,7 @@ abstract class ArgsToProtobufConverter { ); pb.LaunchCompanionRequest createLaunchCompanionWorkflowRequest({ - required String pattern, + required String deviceNamePattern, required bool singleDeviceScan, required bool forceConfirmation, }); @@ -189,12 +189,12 @@ class ArgsToProtobufConverterImpl implements ArgsToProtobufConverter { @override pb.LaunchCompanionRequest createLaunchCompanionWorkflowRequest({ - required String pattern, + required String deviceNamePattern, required bool singleDeviceScan, required bool forceConfirmation, }) => pb.LaunchCompanionRequest() - ..pattern = pattern + ..deviceNamePattern = deviceNamePattern ..singleDeviceScan = singleDeviceScan ..forceConfirmation = forceConfirmation; diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart index 2276d15c..8e8b1a9b 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart @@ -24,7 +24,7 @@ class LaunchCompanionRequest extends $pb.GeneratedMessage { factory LaunchCompanionRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'LaunchCompanionRequest', createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'pattern') + ..aOS(1, _omitFieldNames ? '' : 'deviceNamePattern', protoName: 'deviceNamePattern') ..aOB(2, _omitFieldNames ? '' : 'singleDeviceScan', protoName: 'singleDeviceScan') ..aOB(3, _omitFieldNames ? '' : 'forceConfirmation', protoName: 'forceConfirmation') ..hasRequiredFields = false @@ -52,13 +52,13 @@ class LaunchCompanionRequest extends $pb.GeneratedMessage { static LaunchCompanionRequest? _defaultInstance; @$pb.TagNumber(1) - $core.String get pattern => $_getSZ(0); + $core.String get deviceNamePattern => $_getSZ(0); @$pb.TagNumber(1) - set pattern($core.String v) { $_setString(0, v); } + set deviceNamePattern($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) - $core.bool hasPattern() => $_has(0); + $core.bool hasDeviceNamePattern() => $_has(0); @$pb.TagNumber(1) - void clearPattern() => clearField(1); + void clearDeviceNamePattern() => clearField(1); @$pb.TagNumber(2) $core.bool get singleDeviceScan => $_getBF(1); diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart index 793d3520..b98c0369 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart @@ -17,7 +17,7 @@ import 'dart:typed_data' as $typed_data; const LaunchCompanionRequest$json = { '1': 'LaunchCompanionRequest', '2': [ - {'1': 'pattern', '3': 1, '4': 1, '5': 9, '10': 'pattern'}, + {'1': 'deviceNamePattern', '3': 1, '4': 1, '5': 9, '10': 'deviceNamePattern'}, {'1': 'singleDeviceScan', '3': 2, '4': 1, '5': 8, '10': 'singleDeviceScan'}, {'1': 'forceConfirmation', '3': 3, '4': 1, '5': 8, '10': 'forceConfirmation'}, ], @@ -25,9 +25,9 @@ const LaunchCompanionRequest$json = { /// Descriptor for `LaunchCompanionRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List launchCompanionRequestDescriptor = $convert.base64Decode( - 'ChZMYXVuY2hDb21wYW5pb25SZXF1ZXN0EhgKB3BhdHRlcm4YASABKAlSB3BhdHRlcm4SKgoQc2' - 'luZ2xlRGV2aWNlU2NhbhgCIAEoCFIQc2luZ2xlRGV2aWNlU2NhbhIsChFmb3JjZUNvbmZpcm1h' - 'dGlvbhgDIAEoCFIRZm9yY2VDb25maXJtYXRpb24='); + 'ChZMYXVuY2hDb21wYW5pb25SZXF1ZXN0EiwKEWRldmljZU5hbWVQYXR0ZXJuGAEgASgJUhFkZX' + 'ZpY2VOYW1lUGF0dGVybhIqChBzaW5nbGVEZXZpY2VTY2FuGAIgASgIUhBzaW5nbGVEZXZpY2VT' + 'Y2FuEiwKEWZvcmNlQ29uZmlybWF0aW9uGAMgASgIUhFmb3JjZUNvbmZpcm1hdGlvbg=='); @$core.Deprecated('Use associationInfoDescriptor instead') const AssociationInfo$json = { diff --git a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart index bdf17a9b..a9f8cc8d 100644 --- a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart +++ b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart @@ -105,7 +105,7 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { 'launchCompanionWorkflow', _argsToProtobufConverter .createLaunchCompanionWorkflowRequest( - pattern: pattern, + deviceNamePattern: pattern, singleDeviceScan: singleDeviceScan, forceConfirmation: forceConfirmation, ) diff --git a/packages/reactive_ble_mobile/protos/bledata.proto b/packages/reactive_ble_mobile/protos/bledata.proto index 45d93677..f56f5b2c 100644 --- a/packages/reactive_ble_mobile/protos/bledata.proto +++ b/packages/reactive_ble_mobile/protos/bledata.proto @@ -3,7 +3,7 @@ option java_package = "com.signify.hue.flutterreactiveble"; option java_outer_classname = "ProtobufModel"; message LaunchCompanionRequest { - string pattern = 1; + string deviceNamePattern = 1; bool singleDeviceScan = 2; bool forceConfirmation = 3; } From 4c6d478a21637ecd6ba539d33be7d30217150bbb Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sun, 9 Jul 2023 09:33:30 +0100 Subject: [PATCH 08/17] AssociationInfo => DeviceAssociationInfo & docs --- .../lib/src/device_scanner.dart | 4 +- .../lib/src/reactive_ble.dart | 2 +- .../channelhandlers/CompanionHandler.kt | 4 +- .../ios/Classes/BleData/bledata.pb.swift | 22 +++---- .../lib/src/converter/protobuf_converter.dart | 8 +-- .../lib/src/generated/bledata.pb.dart | 38 +++++------ .../lib/src/generated/bledata.pbjson.dart | 16 ++--- .../lib/src/reactive_ble_mobile_platform.dart | 2 +- .../reactive_ble_mobile/protos/bledata.proto | 4 +- .../lib/src/model/association_info.dart | 16 ----- .../lib/src/model/association_info.g.dart | 64 ------------------- .../src/model/device_association_info.dart | 19 ++++++ .../src/model/device_association_info.g.dart | 64 +++++++++++++++++++ .../lib/src/models.dart | 2 +- .../src/reactive_ble_platform_interface.dart | 23 ++++++- 15 files changed, 156 insertions(+), 132 deletions(-) delete mode 100644 packages/reactive_ble_platform_interface/lib/src/model/association_info.dart delete mode 100644 packages/reactive_ble_platform_interface/lib/src/model/association_info.g.dart create mode 100644 packages/reactive_ble_platform_interface/lib/src/model/device_association_info.dart create mode 100644 packages/reactive_ble_platform_interface/lib/src/model/device_association_info.g.dart diff --git a/packages/flutter_reactive_ble/lib/src/device_scanner.dart b/packages/flutter_reactive_ble/lib/src/device_scanner.dart index 4baa001b..c1e4b677 100644 --- a/packages/flutter_reactive_ble/lib/src/device_scanner.dart +++ b/packages/flutter_reactive_ble/lib/src/device_scanner.dart @@ -5,7 +5,7 @@ import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; abstract class DeviceScanner { ScanSession? get currentScan; - Future launchCompanionWorkflow({ + Future launchCompanionWorkflow({ required String pattern, required bool singleDeviceScan, required bool forceConfirmation, @@ -45,7 +45,7 @@ class DeviceScannerImpl implements DeviceScanner { ScanSession? get currentScan => _currentScanSession; @override - Future launchCompanionWorkflow({ + Future launchCompanionWorkflow({ required String pattern, required bool singleDeviceScan, required bool forceConfirmation, diff --git a/packages/flutter_reactive_ble/lib/src/reactive_ble.dart b/packages/flutter_reactive_ble/lib/src/reactive_ble.dart index 075239e0..31572fa7 100644 --- a/packages/flutter_reactive_ble/lib/src/reactive_ble.dart +++ b/packages/flutter_reactive_ble/lib/src/reactive_ble.dart @@ -217,7 +217,7 @@ class FlutterReactiveBle { deviceId, priority); } - Future launchCompanionWorkflow({ + Future launchCompanionWorkflow({ required String pattern, bool singleDeviceScan = true, bool forceConfirmation = false, diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt index 24e6936a..9b0269f0 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt @@ -71,8 +71,8 @@ class CompanionHandler(private val context: Context, private val bleClient: BleC Log.d(TAG, "onAssociationCreated: $associationInfo") result.success( - ProtobufModel.AssociationInfo.newBuilder() - .setDeviceMacAddress( + ProtobufModel.DeviceAssociationInfo.newBuilder() + .setMacAddress( associationInfo.deviceMacAddress!!.toString().uppercase() ) .build() diff --git a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift index 54278230..a60779b9 100644 --- a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift +++ b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift @@ -36,12 +36,12 @@ struct LaunchCompanionRequest { init() {} } -struct AssociationInfo { +struct DeviceAssociationInfo { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - var deviceMacAddress: String = String() + var macAddress: String = String() var unknownFields = SwiftProtobuf.UnknownStorage() @@ -763,7 +763,7 @@ struct IsConnectable { #if swift(>=5.5) && canImport(_Concurrency) extension LaunchCompanionRequest: @unchecked Sendable {} -extension AssociationInfo: @unchecked Sendable {} +extension DeviceAssociationInfo: @unchecked Sendable {} extension ScanForDevicesRequest: @unchecked Sendable {} extension DeviceScanInfo: @unchecked Sendable {} extension EstablishBondingRequest: @unchecked Sendable {} @@ -846,10 +846,10 @@ extension LaunchCompanionRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageI } } -extension AssociationInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - static let protoMessageName: String = "AssociationInfo" +extension DeviceAssociationInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "DeviceAssociationInfo" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "deviceMacAddress"), + 1: .same(proto: "macAddress"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -858,21 +858,21 @@ extension AssociationInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen // allocates stack space for every case branch when no optimizations are // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { - case 1: try { try decoder.decodeSingularStringField(value: &self.deviceMacAddress) }() + case 1: try { try decoder.decodeSingularStringField(value: &self.macAddress) }() default: break } } } func traverse(visitor: inout V) throws { - if !self.deviceMacAddress.isEmpty { - try visitor.visitSingularStringField(value: self.deviceMacAddress, fieldNumber: 1) + if !self.macAddress.isEmpty { + try visitor.visitSingularStringField(value: self.macAddress, fieldNumber: 1) } try unknownFields.traverse(visitor: &visitor) } - static func ==(lhs: AssociationInfo, rhs: AssociationInfo) -> Bool { - if lhs.deviceMacAddress != rhs.deviceMacAddress {return false} + static func ==(lhs: DeviceAssociationInfo, rhs: DeviceAssociationInfo) -> Bool { + if lhs.macAddress != rhs.macAddress {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart index 552624bb..41bfa80e 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart @@ -9,7 +9,7 @@ abstract class ProtobufConverter { BleStatus bleStatusFrom(List data); - AssociationInfo associationInfoFrom(List data); + DeviceAssociationInfo associationInfoFrom(List data); ScanResult scanResultFrom(List data); @@ -66,10 +66,10 @@ class ProtobufConverterImpl implements ProtobufConverter { } @override - AssociationInfo associationInfoFrom(List data) { - final pbVersion = pb.AssociationInfo.fromBuffer(data); + DeviceAssociationInfo associationInfoFrom(List data) { + final pbVersion = pb.DeviceAssociationInfo.fromBuffer(data); - return AssociationInfo(deviceMacAddress: pbVersion.deviceMacAddress); + return DeviceAssociationInfo(macAddress: pbVersion.macAddress); } @override diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart index 8e8b1a9b..f8de3df1 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart @@ -79,14 +79,14 @@ class LaunchCompanionRequest extends $pb.GeneratedMessage { void clearForceConfirmation() => clearField(3); } -class AssociationInfo extends $pb.GeneratedMessage { - factory AssociationInfo() => create(); - AssociationInfo._() : super(); - factory AssociationInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory AssociationInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'AssociationInfo', createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'deviceMacAddress', protoName: 'deviceMacAddress') +class DeviceAssociationInfo extends $pb.GeneratedMessage { + factory DeviceAssociationInfo() => create(); + DeviceAssociationInfo._() : super(); + factory DeviceAssociationInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory DeviceAssociationInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DeviceAssociationInfo', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'macAddress', protoName: 'macAddress') ..hasRequiredFields = false ; @@ -94,31 +94,31 @@ class AssociationInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') - AssociationInfo clone() => AssociationInfo()..mergeFromMessage(this); + DeviceAssociationInfo clone() => DeviceAssociationInfo()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - AssociationInfo copyWith(void Function(AssociationInfo) updates) => super.copyWith((message) => updates(message as AssociationInfo)) as AssociationInfo; + DeviceAssociationInfo copyWith(void Function(DeviceAssociationInfo) updates) => super.copyWith((message) => updates(message as DeviceAssociationInfo)) as DeviceAssociationInfo; $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static AssociationInfo create() => AssociationInfo._(); - AssociationInfo createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); + static DeviceAssociationInfo create() => DeviceAssociationInfo._(); + DeviceAssociationInfo createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static AssociationInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static AssociationInfo? _defaultInstance; + static DeviceAssociationInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static DeviceAssociationInfo? _defaultInstance; @$pb.TagNumber(1) - $core.String get deviceMacAddress => $_getSZ(0); + $core.String get macAddress => $_getSZ(0); @$pb.TagNumber(1) - set deviceMacAddress($core.String v) { $_setString(0, v); } + set macAddress($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) - $core.bool hasDeviceMacAddress() => $_has(0); + $core.bool hasMacAddress() => $_has(0); @$pb.TagNumber(1) - void clearDeviceMacAddress() => clearField(1); + void clearMacAddress() => clearField(1); } class ScanForDevicesRequest extends $pb.GeneratedMessage { diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart index b98c0369..940e9e2c 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart @@ -29,18 +29,18 @@ final $typed_data.Uint8List launchCompanionRequestDescriptor = $convert.base64De 'ZpY2VOYW1lUGF0dGVybhIqChBzaW5nbGVEZXZpY2VTY2FuGAIgASgIUhBzaW5nbGVEZXZpY2VT' 'Y2FuEiwKEWZvcmNlQ29uZmlybWF0aW9uGAMgASgIUhFmb3JjZUNvbmZpcm1hdGlvbg=='); -@$core.Deprecated('Use associationInfoDescriptor instead') -const AssociationInfo$json = { - '1': 'AssociationInfo', +@$core.Deprecated('Use deviceAssociationInfoDescriptor instead') +const DeviceAssociationInfo$json = { + '1': 'DeviceAssociationInfo', '2': [ - {'1': 'deviceMacAddress', '3': 1, '4': 1, '5': 9, '10': 'deviceMacAddress'}, + {'1': 'macAddress', '3': 1, '4': 1, '5': 9, '10': 'macAddress'}, ], }; -/// Descriptor for `AssociationInfo`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List associationInfoDescriptor = $convert.base64Decode( - 'Cg9Bc3NvY2lhdGlvbkluZm8SKgoQZGV2aWNlTWFjQWRkcmVzcxgBIAEoCVIQZGV2aWNlTWFjQW' - 'RkcmVzcw=='); +/// Descriptor for `DeviceAssociationInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List deviceAssociationInfoDescriptor = $convert.base64Decode( + 'ChVEZXZpY2VBc3NvY2lhdGlvbkluZm8SHgoKbWFjQWRkcmVzcxgBIAEoCVIKbWFjQWRkcmVzcw' + '=='); @$core.Deprecated('Use scanForDevicesRequestDescriptor instead') const ScanForDevicesRequest$json = { diff --git a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart index a9f8cc8d..98baae5a 100644 --- a/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart +++ b/packages/reactive_ble_mobile/lib/src/reactive_ble_mobile_platform.dart @@ -96,7 +96,7 @@ class ReactiveBleMobilePlatform extends ReactiveBlePlatform { } @override - Future launchCompanionWorkflow({ + Future launchCompanionWorkflow({ required String pattern, required bool singleDeviceScan, required bool forceConfirmation, diff --git a/packages/reactive_ble_mobile/protos/bledata.proto b/packages/reactive_ble_mobile/protos/bledata.proto index f56f5b2c..46664349 100644 --- a/packages/reactive_ble_mobile/protos/bledata.proto +++ b/packages/reactive_ble_mobile/protos/bledata.proto @@ -8,8 +8,8 @@ message LaunchCompanionRequest { bool forceConfirmation = 3; } -message AssociationInfo { - string deviceMacAddress = 1; +message DeviceAssociationInfo { + string macAddress = 1; } message ScanForDevicesRequest { diff --git a/packages/reactive_ble_platform_interface/lib/src/model/association_info.dart b/packages/reactive_ble_platform_interface/lib/src/model/association_info.dart deleted file mode 100644 index 2de6be83..00000000 --- a/packages/reactive_ble_platform_interface/lib/src/model/association_info.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:functional_data/functional_data.dart'; -import 'package:meta/meta.dart'; - -part 'association_info.g.dart'; - -//ignore_for_file: annotate_overrides - -@immutable -@FunctionalData() -class AssociationInfo extends $AssociationInfo { - final String deviceMacAddress; - - const AssociationInfo({ - required this.deviceMacAddress, - }); -} diff --git a/packages/reactive_ble_platform_interface/lib/src/model/association_info.g.dart b/packages/reactive_ble_platform_interface/lib/src/model/association_info.g.dart deleted file mode 100644 index 7eb666b7..00000000 --- a/packages/reactive_ble_platform_interface/lib/src/model/association_info.g.dart +++ /dev/null @@ -1,64 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'association_info.dart'; - -// ************************************************************************** -// FunctionalDataGenerator -// ************************************************************************** - -abstract class $AssociationInfo { - const $AssociationInfo(); - - String get deviceMacAddress; - - AssociationInfo copyWith({ - String? deviceMacAddress, - }) => - AssociationInfo( - deviceMacAddress: deviceMacAddress ?? this.deviceMacAddress, - ); - - AssociationInfo copyUsing( - void Function(AssociationInfo$Change change) mutator) { - final change = AssociationInfo$Change._( - this.deviceMacAddress, - ); - mutator(change); - return AssociationInfo( - deviceMacAddress: change.deviceMacAddress, - ); - } - - @override - String toString() => "AssociationInfo(deviceMacAddress: $deviceMacAddress)"; - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) => - other is AssociationInfo && - other.runtimeType == runtimeType && - deviceMacAddress == other.deviceMacAddress; - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode { - return deviceMacAddress.hashCode; - } -} - -class AssociationInfo$Change { - AssociationInfo$Change._( - this.deviceMacAddress, - ); - - String deviceMacAddress; -} - -// ignore: avoid_classes_with_only_static_members -class AssociationInfo$ { - static final deviceMacAddress = Lens( - (deviceMacAddressContainer) => deviceMacAddressContainer.deviceMacAddress, - (deviceMacAddressContainer, deviceMacAddress) => - deviceMacAddressContainer.copyWith(deviceMacAddress: deviceMacAddress), - ); -} diff --git a/packages/reactive_ble_platform_interface/lib/src/model/device_association_info.dart b/packages/reactive_ble_platform_interface/lib/src/model/device_association_info.dart new file mode 100644 index 00000000..10d9bf2f --- /dev/null +++ b/packages/reactive_ble_platform_interface/lib/src/model/device_association_info.dart @@ -0,0 +1,19 @@ +import 'package:functional_data/functional_data.dart'; +import 'package:meta/meta.dart'; + +part 'device_association_info.g.dart'; + +//ignore_for_file: annotate_overrides + +/// Result of a device association request performed by the Android companion +/// workflow. The resulting [macAddress] can be used to connect to the device +/// directly. +@immutable +@FunctionalData() +class DeviceAssociationInfo extends $DeviceAssociationInfo { + final String macAddress; + + const DeviceAssociationInfo({ + required this.macAddress, + }); +} diff --git a/packages/reactive_ble_platform_interface/lib/src/model/device_association_info.g.dart b/packages/reactive_ble_platform_interface/lib/src/model/device_association_info.g.dart new file mode 100644 index 00000000..00c34818 --- /dev/null +++ b/packages/reactive_ble_platform_interface/lib/src/model/device_association_info.g.dart @@ -0,0 +1,64 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'device_association_info.dart'; + +// ************************************************************************** +// FunctionalDataGenerator +// ************************************************************************** + +abstract class $DeviceAssociationInfo { + const $DeviceAssociationInfo(); + + String get macAddress; + + DeviceAssociationInfo copyWith({ + String? macAddress, + }) => + DeviceAssociationInfo( + macAddress: macAddress ?? this.macAddress, + ); + + DeviceAssociationInfo copyUsing( + void Function(DeviceAssociationInfo$Change change) mutator) { + final change = DeviceAssociationInfo$Change._( + this.macAddress, + ); + mutator(change); + return DeviceAssociationInfo( + macAddress: change.macAddress, + ); + } + + @override + String toString() => "DeviceAssociationInfo(macAddress: $macAddress)"; + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) => + other is DeviceAssociationInfo && + other.runtimeType == runtimeType && + macAddress == other.macAddress; + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + return macAddress.hashCode; + } +} + +class DeviceAssociationInfo$Change { + DeviceAssociationInfo$Change._( + this.macAddress, + ); + + String macAddress; +} + +// ignore: avoid_classes_with_only_static_members +class DeviceAssociationInfo$ { + static final macAddress = Lens( + (macAddressContainer) => macAddressContainer.macAddress, + (macAddressContainer, macAddress) => + macAddressContainer.copyWith(macAddress: macAddress), + ); +} diff --git a/packages/reactive_ble_platform_interface/lib/src/models.dart b/packages/reactive_ble_platform_interface/lib/src/models.dart index 32c35e90..9dcf0375 100644 --- a/packages/reactive_ble_platform_interface/lib/src/models.dart +++ b/packages/reactive_ble_platform_interface/lib/src/models.dart @@ -1,10 +1,10 @@ -export './model/association_info.dart'; export './model/ble_status.dart'; export './model/bonding_status.dart'; export './model/characteristic_value.dart'; export './model/clear_gatt_cache_error.dart'; export './model/connection_priority.dart'; export './model/connection_state_update.dart'; +export './model/device_association_info.dart'; export './model/discovered_characteristic.dart'; export './model/discovered_device.dart'; export './model/discovered_service.dart'; diff --git a/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart b/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart index e7ab2ebf..a7acb7cf 100644 --- a/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart +++ b/packages/reactive_ble_platform_interface/lib/src/reactive_ble_platform_interface.dart @@ -69,7 +69,28 @@ abstract class ReactiveBlePlatform extends PlatformInterface { } /// Launch Companion workflow for connecting a Ble device (Android only) - Future launchCompanionWorkflow({ + /// + /// This method launches the companion workflow for connecting a BLE device. + /// + /// Calling this method will open the native companion user interface. The user + /// will be able to select a device from a list of BLE devices that are currently + /// advertising. The user will then be able to select the device. + /// + /// The result of the selection will be returned as a [DeviceAssociationInfo]. + /// + /// The [pattern] parameter is used to filter the list of BLE devices that are + /// displayed to the user. The pattern is matched against the advertised name. + /// It is a regular expression. + /// + /// The [singleDeviceScan] parameter is used to determine if the user should be + /// able to select multiple devices or not. If set to true, no list interface + /// will be shown to the user. Instead, the user will confirm the selection + /// directly. + /// + /// The [forceConfirmation] parameter is used to determine if the user should + /// explictely confirm the selection or not. If set to true, the user will be + /// asked to confirm the selection. + Future launchCompanionWorkflow({ required String pattern, required bool singleDeviceScan, required bool forceConfirmation, From 41e41fb330044b5098be1552f9edad0d07f3c293 Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sun, 9 Jul 2023 14:59:36 +0100 Subject: [PATCH 09/17] BondingManagerTest --- .../reactive_ble_mobile/android/build.gradle | 1 + .../flutterreactiveble/ble/BondingManager.kt | 10 +- .../ble/ReactiveBleClient.kt | 2 +- .../ble/BondingManagerTest.kt | 124 ++++++++++++++++++ 4 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManagerTest.kt diff --git a/packages/reactive_ble_mobile/android/build.gradle b/packages/reactive_ble_mobile/android/build.gradle index cd082abd..73e5036c 100644 --- a/packages/reactive_ble_mobile/android/build.gradle +++ b/packages/reactive_ble_mobile/android/build.gradle @@ -101,6 +101,7 @@ dependencies { implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.0" + testImplementation("org.junit.jupiter:junit-jupiter-params:5.7.0") testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.7.0" testImplementation "io.mockk:mockk:1.11.0" testImplementation "com.google.truth:truth:1.0" diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt index 8df912f3..db859249 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt @@ -17,21 +17,18 @@ import io.reactivex.disposables.Disposables class BondingFailedException : RuntimeException() -object BondingManager { +class BondingManager(private val context: Context) { /** * @throws BondingFailedException */ // TODO: try to understand why the popup is being displayed in the notification center (sometimes) ???!!! @SuppressLint("MissingPermission") - fun bondWithDevice( - context: Context, - rxBleDevice: RxBleDevice - ): Single { + fun bondWithDevice(rxBleDevice: RxBleDevice): Single { return Single.create { completion -> when (rxBleDevice.bluetoothDevice.bondState) { + BluetoothDevice.BOND_BONDED -> completion.onSuccess(BluetoothDevice.BOND_BONDED) else -> { - val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val deviceBeingPaired: BluetoothDevice? = @@ -76,7 +73,6 @@ object BondingManager { if (!createBondResult) { completion.tryOnError(BondingFailedException()) } - } } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt index bdc08e76..a0a75ee5 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt @@ -99,7 +99,7 @@ open class ReactiveBleClient(private val context: Context) : BleClient { override fun establishBond(deviceId: String): Single { val device = rxBleClient.getBleDevice(deviceId) - return BondingManager.bondWithDevice(context, device) + return BondingManager(context).bondWithDevice(device) } override fun connectToDevice(deviceId: String, timeout: Duration) { diff --git a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManagerTest.kt b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManagerTest.kt new file mode 100644 index 00000000..564a9e0e --- /dev/null +++ b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManagerTest.kt @@ -0,0 +1,124 @@ +package com.signify.hue.flutterreactiveble.ble + +import android.bluetooth.BluetoothDevice +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.polidea.rxandroidble2.RxBleDevice +import io.mockk.CapturingSlot +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +@DisplayName("BondingManager unit tests") +class BondingManagerTest { + @MockK + private lateinit var context: Context + + @MockK + private lateinit var device: RxBleDevice + + @MockK + private lateinit var bluetoothDevice: BluetoothDevice + + private lateinit var sut: BondingManager + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + every { bluetoothDevice.address }.returns("ab:cd:ef:12:34:56") + every { device.bluetoothDevice }.returns(bluetoothDevice) + + sut = BondingManager(context) + } + + @Nested + @DisplayName("Bonding => to success") + inner class BondingSuccessTest { + + private var receiver: CapturingSlot = CapturingSlot() + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + every { bluetoothDevice.bondState }.returns(BluetoothDevice.BOND_NONE) + every { context.registerReceiver(capture(receiver), any()) }.returns(null) + every { bluetoothDevice.createBond() }.returns(true) + every { context.unregisterReceiver(any()) }.returns(Unit) + } + + @MockK + private lateinit var deviceIntent: Intent + + @ParameterizedTest + @DisplayName("create bond") + @ValueSource( + ints = [ + BluetoothDevice.BOND_BONDED, + BluetoothDevice.BOND_NONE + ] + ) + fun createBond(bondState: Int) { + val result = sut.bondWithDevice(device).test() + + every { deviceIntent.action }.returns(BluetoothDevice.ACTION_BOND_STATE_CHANGED) + every { deviceIntent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, any()) }.returns( + bondState + ) + every { deviceIntent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) }.returns( + bluetoothDevice + ) + + receiver.captured.onReceive(context, deviceIntent) + + assert(result.errorCount() == 0) + assert(result.values()[0] == bondState) + } + } + + @Nested + @DisplayName("Already bonded") + inner class AlreadyBonded { + + @BeforeEach + fun setup() { + every { bluetoothDevice.bondState }.returns(BluetoothDevice.BOND_BONDED) + } + + @Test + @DisplayName("create bond") + fun createBond() { + val result = sut.bondWithDevice(device).test() + assert(result.errorCount() == 0) + assert(result.values()[0] == BluetoothDevice.BOND_BONDED) + } + } + + @Nested + @DisplayName("Bonding Failed") + inner class FailedBondingTest { + + @BeforeEach + fun setup() { + every { bluetoothDevice.bondState }.returns(BluetoothDevice.BOND_NONE) + every { context.registerReceiver(any(), any()) }.returns(null) + every { bluetoothDevice.createBond() }.returns(false) + } + + @Test + @DisplayName("create bond") + fun createBond() { + val result = sut.bondWithDevice(device).test() + assert(result.errorCount() == 1) + assert(result.errors().first() is BondingFailedException) + } + } +} \ No newline at end of file From 3185d81a32ec041eaefbeb4b0ad3362185a0e7dc Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Mon, 10 Jul 2023 06:35:42 +0100 Subject: [PATCH 10/17] more tests --- ...connected_device_operation_test.mocks.dart | 35 ++- .../test/device_connector_test.mocks.dart | 41 ++- .../test/device_scanner_test.mocks.dart | 35 ++- .../test/reactive_ble_test.mocks.dart | 62 ++++- .../converters/ProtobufMessageConverter.kt | 13 +- .../ProtobufMessageConverterTest.kt | 85 ++++-- .../ios/Classes/BleData/bledata.pb.swift | 41 ++- .../lib/src/converter/protobuf_converter.dart | 6 +- .../lib/src/generated/bledata.pb.dart | 2 +- .../lib/src/generated/bledata.pbenum.dart | 14 +- .../lib/src/generated/bledata.pbjson.dart | 11 +- .../reactive_ble_mobile/protos/bledata.proto | 7 +- .../args_to_protobuf_converter_test.dart | 51 ++++ .../converter/protobuf_converter_test.dart | 10 + .../reactive_ble_platform_test.mocks.dart | 248 ++++++++++++++---- 15 files changed, 507 insertions(+), 154 deletions(-) diff --git a/packages/flutter_reactive_ble/test/connected_device_operation_test.mocks.dart b/packages/flutter_reactive_ble/test/connected_device_operation_test.mocks.dart index 6593bb57..a3362f4a 100644 --- a/packages/flutter_reactive_ble/test/connected_device_operation_test.mocks.dart +++ b/packages/flutter_reactive_ble/test/connected_device_operation_test.mocks.dart @@ -104,16 +104,23 @@ class MockReactiveBlePlatform extends _i1.Mock returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); @override - _i4.Future launchCompanionWorkflow({required String? pattern}) => + _i4.Future<_i2.DeviceAssociationInfo?> launchCompanionWorkflow({ + required String? pattern, + required bool? singleDeviceScan, + required bool? forceConfirmation, + }) => (super.noSuchMethod( Invocation.method( #launchCompanionWorkflow, [], - {#pattern: pattern}, + { + #pattern: pattern, + #singleDeviceScan: singleDeviceScan, + #forceConfirmation: forceConfirmation, + }, ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + returnValue: _i4.Future<_i2.DeviceAssociationInfo?>.value(), + ) as _i4.Future<_i2.DeviceAssociationInfo?>); @override _i4.Stream scanForDevices({ required List<_i2.Uuid>? withServices, @@ -154,6 +161,16 @@ class MockReactiveBlePlatform extends _i1.Mock _i2.Result<_i2.Unit, _i2.GenericFailure<_i2.ClearGattCacheError>?>>); @override + _i4.Future<_i2.BondingStatus> establishBonding(String? deviceId) => + (super.noSuchMethod( + Invocation.method( + #establishBonding, + [deviceId], + ), + returnValue: + _i4.Future<_i2.BondingStatus>.value(_i2.BondingStatus.none), + ) as _i4.Future<_i2.BondingStatus>); + @override _i4.Stream connectToDevice( String? id, Map<_i2.Uuid, List<_i2.Uuid>>? servicesWithCharacteristicsToDiscover, @@ -171,6 +188,14 @@ class MockReactiveBlePlatform extends _i1.Mock returnValue: _i4.Stream.empty(), ) as _i4.Stream); @override + _i4.Future retrieveDeviceName(String? id) => (super.noSuchMethod( + Invocation.method( + #retrieveDeviceName, + [id], + ), + returnValue: _i4.Future.value(), + ) as _i4.Future); + @override _i4.Future disconnectDevice(String? deviceId) => (super.noSuchMethod( Invocation.method( #disconnectDevice, diff --git a/packages/flutter_reactive_ble/test/device_connector_test.mocks.dart b/packages/flutter_reactive_ble/test/device_connector_test.mocks.dart index db7b796d..48710408 100644 --- a/packages/flutter_reactive_ble/test/device_connector_test.mocks.dart +++ b/packages/flutter_reactive_ble/test/device_connector_test.mocks.dart @@ -116,16 +116,23 @@ class MockReactiveBlePlatform extends _i1.Mock returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); @override - _i4.Future launchCompanionWorkflow({required String? pattern}) => + _i4.Future<_i2.DeviceAssociationInfo?> launchCompanionWorkflow({ + required String? pattern, + required bool? singleDeviceScan, + required bool? forceConfirmation, + }) => (super.noSuchMethod( Invocation.method( #launchCompanionWorkflow, [], - {#pattern: pattern}, + { + #pattern: pattern, + #singleDeviceScan: singleDeviceScan, + #forceConfirmation: forceConfirmation, + }, ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + returnValue: _i4.Future<_i2.DeviceAssociationInfo?>.value(), + ) as _i4.Future<_i2.DeviceAssociationInfo?>); @override _i4.Stream scanForDevices({ required List<_i2.Uuid>? withServices, @@ -166,6 +173,16 @@ class MockReactiveBlePlatform extends _i1.Mock _i2.Result<_i2.Unit, _i2.GenericFailure<_i2.ClearGattCacheError>?>>); @override + _i4.Future<_i2.BondingStatus> establishBonding(String? deviceId) => + (super.noSuchMethod( + Invocation.method( + #establishBonding, + [deviceId], + ), + returnValue: + _i4.Future<_i2.BondingStatus>.value(_i2.BondingStatus.none), + ) as _i4.Future<_i2.BondingStatus>); + @override _i4.Stream connectToDevice( String? id, Map<_i2.Uuid, List<_i2.Uuid>>? servicesWithCharacteristicsToDiscover, @@ -183,6 +200,14 @@ class MockReactiveBlePlatform extends _i1.Mock returnValue: _i4.Stream.empty(), ) as _i4.Stream); @override + _i4.Future retrieveDeviceName(String? id) => (super.noSuchMethod( + Invocation.method( + #retrieveDeviceName, + [id], + ), + returnValue: _i4.Future.value(), + ) as _i4.Future); + @override _i4.Future disconnectDevice(String? deviceId) => (super.noSuchMethod( Invocation.method( #disconnectDevice, @@ -333,7 +358,7 @@ class MockDeviceScanner extends _i1.Mock implements _i5.DeviceScanner { } @override - _i4.Future launchCompanionWorkflow({ + _i4.Future<_i2.DeviceAssociationInfo?> launchCompanionWorkflow({ required String? pattern, required bool? singleDeviceScan, required bool? forceConfirmation, @@ -348,8 +373,8 @@ class MockDeviceScanner extends _i1.Mock implements _i5.DeviceScanner { #forceConfirmation: forceConfirmation, }, ), - returnValue: _i4.Future.value(), - ) as _i4.Future); + returnValue: _i4.Future<_i2.DeviceAssociationInfo?>.value(), + ) as _i4.Future<_i2.DeviceAssociationInfo?>); @override _i4.Stream<_i2.DiscoveredDevice> scanForDevices({ required List<_i2.Uuid>? withServices, diff --git a/packages/flutter_reactive_ble/test/device_scanner_test.mocks.dart b/packages/flutter_reactive_ble/test/device_scanner_test.mocks.dart index 1c3afb28..11e8e1a2 100644 --- a/packages/flutter_reactive_ble/test/device_scanner_test.mocks.dart +++ b/packages/flutter_reactive_ble/test/device_scanner_test.mocks.dart @@ -104,16 +104,23 @@ class MockReactiveBlePlatform extends _i1.Mock returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); @override - _i4.Future launchCompanionWorkflow({required String? pattern}) => + _i4.Future<_i2.DeviceAssociationInfo?> launchCompanionWorkflow({ + required String? pattern, + required bool? singleDeviceScan, + required bool? forceConfirmation, + }) => (super.noSuchMethod( Invocation.method( #launchCompanionWorkflow, [], - {#pattern: pattern}, + { + #pattern: pattern, + #singleDeviceScan: singleDeviceScan, + #forceConfirmation: forceConfirmation, + }, ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + returnValue: _i4.Future<_i2.DeviceAssociationInfo?>.value(), + ) as _i4.Future<_i2.DeviceAssociationInfo?>); @override _i4.Stream scanForDevices({ required List<_i2.Uuid>? withServices, @@ -154,6 +161,16 @@ class MockReactiveBlePlatform extends _i1.Mock _i2.Result<_i2.Unit, _i2.GenericFailure<_i2.ClearGattCacheError>?>>); @override + _i4.Future<_i2.BondingStatus> establishBonding(String? deviceId) => + (super.noSuchMethod( + Invocation.method( + #establishBonding, + [deviceId], + ), + returnValue: + _i4.Future<_i2.BondingStatus>.value(_i2.BondingStatus.none), + ) as _i4.Future<_i2.BondingStatus>); + @override _i4.Stream connectToDevice( String? id, Map<_i2.Uuid, List<_i2.Uuid>>? servicesWithCharacteristicsToDiscover, @@ -171,6 +188,14 @@ class MockReactiveBlePlatform extends _i1.Mock returnValue: _i4.Stream.empty(), ) as _i4.Stream); @override + _i4.Future retrieveDeviceName(String? id) => (super.noSuchMethod( + Invocation.method( + #retrieveDeviceName, + [id], + ), + returnValue: _i4.Future.value(), + ) as _i4.Future); + @override _i4.Future disconnectDevice(String? deviceId) => (super.noSuchMethod( Invocation.method( #disconnectDevice, diff --git a/packages/flutter_reactive_ble/test/reactive_ble_test.mocks.dart b/packages/flutter_reactive_ble/test/reactive_ble_test.mocks.dart index 4886c1d1..2db8c78a 100644 --- a/packages/flutter_reactive_ble/test/reactive_ble_test.mocks.dart +++ b/packages/flutter_reactive_ble/test/reactive_ble_test.mocks.dart @@ -107,16 +107,23 @@ class MockReactiveBlePlatform extends _i1.Mock returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); @override - _i4.Future launchCompanionWorkflow({required String? pattern}) => + _i4.Future<_i2.DeviceAssociationInfo?> launchCompanionWorkflow({ + required String? pattern, + required bool? singleDeviceScan, + required bool? forceConfirmation, + }) => (super.noSuchMethod( Invocation.method( #launchCompanionWorkflow, [], - {#pattern: pattern}, + { + #pattern: pattern, + #singleDeviceScan: singleDeviceScan, + #forceConfirmation: forceConfirmation, + }, ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + returnValue: _i4.Future<_i2.DeviceAssociationInfo?>.value(), + ) as _i4.Future<_i2.DeviceAssociationInfo?>); @override _i4.Stream scanForDevices({ required List<_i2.Uuid>? withServices, @@ -157,6 +164,16 @@ class MockReactiveBlePlatform extends _i1.Mock _i2.Result<_i2.Unit, _i2.GenericFailure<_i2.ClearGattCacheError>?>>); @override + _i4.Future<_i2.BondingStatus> establishBonding(String? deviceId) => + (super.noSuchMethod( + Invocation.method( + #establishBonding, + [deviceId], + ), + returnValue: + _i4.Future<_i2.BondingStatus>.value(_i2.BondingStatus.none), + ) as _i4.Future<_i2.BondingStatus>); + @override _i4.Stream connectToDevice( String? id, Map<_i2.Uuid, List<_i2.Uuid>>? servicesWithCharacteristicsToDiscover, @@ -174,6 +191,14 @@ class MockReactiveBlePlatform extends _i1.Mock returnValue: _i4.Stream.empty(), ) as _i4.Stream); @override + _i4.Future retrieveDeviceName(String? id) => (super.noSuchMethod( + Invocation.method( + #retrieveDeviceName, + [id], + ), + returnValue: _i4.Future.value(), + ) as _i4.Future); + @override _i4.Future disconnectDevice(String? deviceId) => (super.noSuchMethod( Invocation.method( #disconnectDevice, @@ -472,11 +497,21 @@ class MockDeviceConnector extends _i1.Mock implements _i6.DeviceConnector { returnValue: _i4.Stream<_i2.ConnectionStateUpdate>.empty(), ) as _i4.Stream<_i2.ConnectionStateUpdate>); @override + _i4.Future<_i2.BondingStatus> establishBonding({required String? deviceId}) => + (super.noSuchMethod( + Invocation.method( + #establishBonding, + [], + {#deviceId: deviceId}, + ), + returnValue: + _i4.Future<_i2.BondingStatus>.value(_i2.BondingStatus.none), + ) as _i4.Future<_i2.BondingStatus>); + @override _i4.Stream<_i2.ConnectionStateUpdate> connect({ required String? id, Map<_i2.Uuid, List<_i2.Uuid>>? servicesWithCharacteristicsToDiscover, Duration? connectionTimeout, - dynamic bondingMode, }) => (super.noSuchMethod( Invocation.method( @@ -487,12 +522,19 @@ class MockDeviceConnector extends _i1.Mock implements _i6.DeviceConnector { #servicesWithCharacteristicsToDiscover: servicesWithCharacteristicsToDiscover, #connectionTimeout: connectionTimeout, - #bondingMode: bondingMode, }, ), returnValue: _i4.Stream<_i2.ConnectionStateUpdate>.empty(), ) as _i4.Stream<_i2.ConnectionStateUpdate>); @override + _i4.Future retrieveDeviceName(String? id) => (super.noSuchMethod( + Invocation.method( + #retrieveDeviceName, + [id], + ), + returnValue: _i4.Future.value(), + ) as _i4.Future); + @override _i4.Stream<_i2.ConnectionStateUpdate> connectToAdvertisingDevice({ required String? id, required List<_i2.Uuid>? withServices, @@ -526,7 +568,7 @@ class MockDeviceScanner extends _i1.Mock implements _i7.DeviceScanner { } @override - _i4.Future launchCompanionWorkflow({ + _i4.Future<_i2.DeviceAssociationInfo?> launchCompanionWorkflow({ required String? pattern, required bool? singleDeviceScan, required bool? forceConfirmation, @@ -541,8 +583,8 @@ class MockDeviceScanner extends _i1.Mock implements _i7.DeviceScanner { #forceConfirmation: forceConfirmation, }, ), - returnValue: _i4.Future.value(), - ) as _i4.Future); + returnValue: _i4.Future<_i2.DeviceAssociationInfo?>.value(), + ) as _i4.Future<_i2.DeviceAssociationInfo?>); @override _i4.Stream<_i2.DiscoveredDevice> scanForDevices({ required List<_i2.Uuid>? withServices, diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt index c49087b9..d7b4e801 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt @@ -1,10 +1,12 @@ package com.signify.hue.flutterreactiveble.converters +import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattService import com.google.protobuf.ByteString import com.polidea.rxandroidble2.RxBleDeviceServices import com.signify.hue.flutterreactiveble.ProtobufModel +import com.signify.hue.flutterreactiveble.ProtobufModel.EstablishBondingInfo.BondState import com.signify.hue.flutterreactiveble.ble.ConnectionUpdateSuccess import com.signify.hue.flutterreactiveble.ble.MtuNegotiateFailed import com.signify.hue.flutterreactiveble.ble.MtuNegotiateResult @@ -262,8 +264,13 @@ class ProtobufMessageConverter { } fun convertBondInfo(status: Int): pb.EstablishBondingInfo { - return pb.EstablishBondingInfo.newBuilder() - .setStatus(ProtobufModel.EstablishBondingInfo.BondState.forNumber(status)) - .build() + val bondState = when (status) { + BluetoothDevice.BOND_BONDED -> BondState.BONDED + BluetoothDevice.BOND_BONDING -> BondState.BONDING + BluetoothDevice.BOND_NONE -> BondState.NONE + else -> BondState.NONE + } + + return pb.EstablishBondingInfo.newBuilder().setStatus(bondState).build() } } diff --git a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt index 6b5f3246..8e67b041 100644 --- a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt +++ b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt @@ -1,7 +1,9 @@ package com.signify.hue.flutterreactiveble.converters +import android.bluetooth.BluetoothDevice import com.google.common.truth.Truth.assertThat import com.google.protobuf.ByteString +import com.signify.hue.flutterreactiveble.ProtobufModel.EstablishBondingInfo.BondState import com.signify.hue.flutterreactiveble.ble.Connectable import com.signify.hue.flutterreactiveble.ble.ConnectionUpdateSuccess import com.signify.hue.flutterreactiveble.ble.MtuNegotiateFailed @@ -11,9 +13,13 @@ import com.signify.hue.flutterreactiveble.model.NegotiateMtuErrorType import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource import java.util.UUID +import java.util.stream.Stream import com.signify.hue.flutterreactiveble.ProtobufModel as pb + class ProtobufMessageConverterTest { val protobufConverter = ProtobufMessageConverter() @@ -119,7 +125,9 @@ class ProtobufMessageConverterTest { fun `converts result as parameter in device connection message`() { val result = 0 val connection = ConnectionUpdateSuccess("", result) - assertThat(protobufConverter.convertToDeviceInfo(connection).connectionState).isEqualTo(result) + assertThat(protobufConverter.convertToDeviceInfo(connection).connectionState).isEqualTo( + result + ) } } @@ -131,15 +139,21 @@ class ProtobufMessageConverterTest { fun `converts to a characteristicvalueInfo object `() { val request = createCharacteristicRequest("a", UUID.randomUUID()) - assertThat(protobufConverter.convertCharacteristicInfo(request.characteristic, byteArrayOf(1))) - .isInstanceOf(pb.CharacteristicValueInfo::class.java) + assertThat( + protobufConverter.convertCharacteristicInfo( + request.characteristic, + byteArrayOf(1) + ) + ) + .isInstanceOf(pb.CharacteristicValueInfo::class.java) } @Test fun `converts a char value and request into a characteristic info value `() { val request = createCharacteristicRequest("a", UUID.randomUUID()) val expectedValue = byteArrayOf(1) - val valueInfo = protobufConverter.convertCharacteristicInfo(request.characteristic, expectedValue) + val valueInfo = + protobufConverter.convertCharacteristicInfo(request.characteristic, expectedValue) assertThat(valueInfo.value).isEqualTo(ByteString.copyFrom(expectedValue)) } @@ -171,7 +185,9 @@ class ProtobufMessageConverterTest { @Test fun `sets default value for error in case no error occurred`() { val result = MtuNegotiateSuccesful("id", 3) - assertThat(protobufConverter.convertNegotiateMtuInfo(result).failure.message).isEqualTo("") + assertThat(protobufConverter.convertNegotiateMtuInfo(result).failure.message).isEqualTo( + "" + ) } @Test @@ -179,14 +195,38 @@ class ProtobufMessageConverterTest { val errorMessage = "whoops" val result = MtuNegotiateFailed("id", errorMessage) assertThat(protobufConverter.convertNegotiateMtuInfo(result).failure.message) - .isEqualTo(errorMessage) + .isEqualTo(errorMessage) } @Test fun `converts error code`() { val result = MtuNegotiateFailed("id", "") assertThat(protobufConverter.convertNegotiateMtuInfo(result).failure.code) - .isEqualTo(NegotiateMtuErrorType.UNKNOWN.code) + .isEqualTo(NegotiateMtuErrorType.UNKNOWN.code) + } + } + + @DisplayName("Convert to bondinfo") + inner class BondInfoTest { + @Test + @MethodSource("provideParameters") + fun `converts bonded`(bondState: BondState, androidConstant: Int) { + assertThat(protobufConverter.convertBondInfo(androidConstant)).isEqualTo(bondState) + } + + private fun provideParameters(): Stream? { + return Stream.of( + Arguments.of(BondState.BONDED, BluetoothDevice.BOND_BONDED), + Arguments.of(BondState.BONDING, BluetoothDevice.BOND_BONDING), + Arguments.of(BondState.NONE, BluetoothDevice.BOND_NONE) + ) + } + + @Test + fun `converts unknown to none`() { + val result = BluetoothDevice.BOND_BONDED + + assertThat(protobufConverter.convertBondInfo(result)).isEqualTo(pb.EstablishBondingInfo.BondState.BONDED) } } @@ -204,29 +244,32 @@ class ProtobufMessageConverterTest { val manufacturerData = "123".toByteArray() return ScanInfo( - deviceId = macAdress, - name = deviceName, - rssi = rssi, - connectable = Connectable.UNKNOWN, - serviceData = serviceData, - manufacturerData = manufacturerData, - serviceUuids = listOf(serviceUuid), + deviceId = macAdress, + name = deviceName, + rssi = rssi, + connectable = Connectable.UNKNOWN, + serviceData = serviceData, + manufacturerData = manufacturerData, + serviceUuids = listOf(serviceUuid), ) } - private fun createCharacteristicRequest(deviceId: String, serviceUuid: UUID): pb.ReadCharacteristicRequest { + private fun createCharacteristicRequest( + deviceId: String, + serviceUuid: UUID + ): pb.ReadCharacteristicRequest { val uuidConverter = UuidConverter() val uuid = pb.Uuid.newBuilder() - .setData(ByteString.copyFrom(uuidConverter.byteArrayFromUuid(serviceUuid))) + .setData(ByteString.copyFrom(uuidConverter.byteArrayFromUuid(serviceUuid))) val characteristicAddress = pb.CharacteristicAddress.newBuilder() - .setDeviceId(deviceId) - .setServiceUuid(uuid) - .setCharacteristicUuid(uuid) + .setDeviceId(deviceId) + .setServiceUuid(uuid) + .setCharacteristicUuid(uuid) return pb.ReadCharacteristicRequest.newBuilder() - .setCharacteristic(characteristicAddress) - .build() + .setCharacteristic(characteristicAddress) + .build() } } diff --git a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift index a60779b9..4b5539c7 100644 --- a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift +++ b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift @@ -124,38 +124,35 @@ struct EstablishBondingInfo { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - var status: EstablishBondingInfo.BondState = .unknown + var status: EstablishBondingInfo.BondState = .none var unknownFields = SwiftProtobuf.UnknownStorage() enum BondState: SwiftProtobuf.Enum { typealias RawValue = Int - case unknown // = 0 - case bondNone // = 10 - case bondBonding // = 11 - case bondBonded // = 12 + case none // = 0 + case bonding // = 1 + case bonded // = 2 case UNRECOGNIZED(Int) init() { - self = .unknown + self = .none } init?(rawValue: Int) { switch rawValue { - case 0: self = .unknown - case 10: self = .bondNone - case 11: self = .bondBonding - case 12: self = .bondBonded + case 0: self = .none + case 1: self = .bonding + case 2: self = .bonded default: self = .UNRECOGNIZED(rawValue) } } var rawValue: Int { switch self { - case .unknown: return 0 - case .bondNone: return 10 - case .bondBonding: return 11 - case .bondBonded: return 12 + case .none: return 0 + case .bonding: return 1 + case .bonded: return 2 case .UNRECOGNIZED(let i): return i } } @@ -170,10 +167,9 @@ struct EstablishBondingInfo { extension EstablishBondingInfo.BondState: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. static var allCases: [EstablishBondingInfo.BondState] = [ - .unknown, - .bondNone, - .bondBonding, - .bondBonded, + .none, + .bonding, + .bonded, ] } @@ -1051,7 +1047,7 @@ extension EstablishBondingInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImp } func traverse(visitor: inout V) throws { - if self.status != .unknown { + if self.status != .none { try visitor.visitSingularEnumField(value: self.status, fieldNumber: 1) } try unknownFields.traverse(visitor: &visitor) @@ -1066,10 +1062,9 @@ extension EstablishBondingInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImp extension EstablishBondingInfo.BondState: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNKNOWN"), - 10: .same(proto: "BOND_NONE"), - 11: .same(proto: "BOND_BONDING"), - 12: .same(proto: "BOND_BONDED"), + 0: .same(proto: "NONE"), + 1: .same(proto: "BONDING"), + 2: .same(proto: "BONDED"), ] } diff --git a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart index 41bfa80e..2317371c 100644 --- a/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart +++ b/packages/reactive_ble_mobile/lib/src/converter/protobuf_converter.dart @@ -40,11 +40,11 @@ class ProtobufConverterImpl implements ProtobufConverter { BondingStatus bondingStatusFrom(List data) { final message = pb.EstablishBondingInfo.fromBuffer(data); switch (message.status) { - case pb.EstablishBondingInfo_BondState.BOND_BONDING: + case pb.EstablishBondingInfo_BondState.BONDING: return BondingStatus.bonding; - case pb.EstablishBondingInfo_BondState.BOND_BONDED: + case pb.EstablishBondingInfo_BondState.BONDED: return BondingStatus.bonded; - case pb.EstablishBondingInfo_BondState.BOND_NONE: + case pb.EstablishBondingInfo_BondState.NONE: return BondingStatus.none; } diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart index f8de3df1..51b67ff7 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart @@ -330,7 +330,7 @@ class EstablishBondingInfo extends $pb.GeneratedMessage { factory EstablishBondingInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EstablishBondingInfo', createEmptyInstance: create) - ..e(1, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: EstablishBondingInfo_BondState.UNKNOWN, valueOf: EstablishBondingInfo_BondState.valueOf, enumValues: EstablishBondingInfo_BondState.values) + ..e(1, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: EstablishBondingInfo_BondState.NONE, valueOf: EstablishBondingInfo_BondState.valueOf, enumValues: EstablishBondingInfo_BondState.values) ..hasRequiredFields = false ; diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart index d37b7722..c8a652ee 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart @@ -14,16 +14,14 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; class EstablishBondingInfo_BondState extends $pb.ProtobufEnum { - static const EstablishBondingInfo_BondState UNKNOWN = EstablishBondingInfo_BondState._(0, _omitEnumNames ? '' : 'UNKNOWN'); - static const EstablishBondingInfo_BondState BOND_NONE = EstablishBondingInfo_BondState._(10, _omitEnumNames ? '' : 'BOND_NONE'); - static const EstablishBondingInfo_BondState BOND_BONDING = EstablishBondingInfo_BondState._(11, _omitEnumNames ? '' : 'BOND_BONDING'); - static const EstablishBondingInfo_BondState BOND_BONDED = EstablishBondingInfo_BondState._(12, _omitEnumNames ? '' : 'BOND_BONDED'); + static const EstablishBondingInfo_BondState NONE = EstablishBondingInfo_BondState._(0, _omitEnumNames ? '' : 'NONE'); + static const EstablishBondingInfo_BondState BONDING = EstablishBondingInfo_BondState._(1, _omitEnumNames ? '' : 'BONDING'); + static const EstablishBondingInfo_BondState BONDED = EstablishBondingInfo_BondState._(2, _omitEnumNames ? '' : 'BONDED'); static const $core.List values = [ - UNKNOWN, - BOND_NONE, - BOND_BONDING, - BOND_BONDED, + NONE, + BONDING, + BONDED, ]; static final $core.Map<$core.int, EstablishBondingInfo_BondState> _byValue = $pb.ProtobufEnum.initByValue(values); diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart index 940e9e2c..89f0d5c6 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart @@ -108,18 +108,17 @@ const EstablishBondingInfo$json = { const EstablishBondingInfo_BondState$json = { '1': 'BondState', '2': [ - {'1': 'UNKNOWN', '2': 0}, - {'1': 'BOND_NONE', '2': 10}, - {'1': 'BOND_BONDING', '2': 11}, - {'1': 'BOND_BONDED', '2': 12}, + {'1': 'NONE', '2': 0}, + {'1': 'BONDING', '2': 1}, + {'1': 'BONDED', '2': 2}, ], }; /// Descriptor for `EstablishBondingInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List establishBondingInfoDescriptor = $convert.base64Decode( 'ChRFc3RhYmxpc2hCb25kaW5nSW5mbxI3CgZzdGF0dXMYASABKA4yHy5Fc3RhYmxpc2hCb25kaW' - '5nSW5mby5Cb25kU3RhdGVSBnN0YXR1cyJKCglCb25kU3RhdGUSCwoHVU5LTk9XThAAEg0KCUJP' - 'TkRfTk9ORRAKEhAKDEJPTkRfQk9ORElORxALEg8KC0JPTkRfQk9OREVEEAw='); + '5nSW5mby5Cb25kU3RhdGVSBnN0YXR1cyIuCglCb25kU3RhdGUSCAoETk9ORRAAEgsKB0JPTkRJ' + 'TkcQARIKCgZCT05ERUQQAg=='); @$core.Deprecated('Use connectToDeviceRequestDescriptor instead') const ConnectToDeviceRequest$json = { diff --git a/packages/reactive_ble_mobile/protos/bledata.proto b/packages/reactive_ble_mobile/protos/bledata.proto index 46664349..b6f8a20b 100644 --- a/packages/reactive_ble_mobile/protos/bledata.proto +++ b/packages/reactive_ble_mobile/protos/bledata.proto @@ -35,10 +35,9 @@ message EstablishBondingRequest { message EstablishBondingInfo { enum BondState { - UNKNOWN = 0; - BOND_NONE = 10; - BOND_BONDING = 11; - BOND_BONDED = 12; + NONE = 0; + BONDING = 1; + BONDED = 2; } BondState status = 1; diff --git a/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart b/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart index c976bc89..4e6a874b 100644 --- a/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart +++ b/packages/reactive_ble_mobile/test/converter/args_to_protobuf_converter_test.dart @@ -7,6 +7,19 @@ void main() { group('$ArgsToProtobufConverter', () { const _sut = ArgsToProtobufConverterImpl(); + group('Establish bonding args', () { + const deviceId = '123'; + late pb.EstablishBondingRequest result; + + setUp(() { + result = _sut.createEstablishBondingArgs(deviceId); + }); + + test('It converts deviceId', () { + expect(result.deviceId, deviceId); + }); + }); + group('Connect to device args', () { const deviceId = '123'; Map>? servicesToDiscover; @@ -71,6 +84,19 @@ void main() { }); }); + group('Get Device Name', () { + const deviceId = '123'; + late pb.GetDeviceNameRequest result; + + setUp(() { + result = _sut.createGetDeviceNameArgs(deviceId); + }); + + test('It converts deviceId', () { + expect(result.deviceId, deviceId); + }); + }); + group('Disconnect device', () { const deviceId = '123'; late pb.DisconnectFromDeviceRequest result; @@ -217,6 +243,31 @@ void main() { }); }); + group('Launch Companion Workflow', () { + const deviceNamePattern = '123'; + const singleDeviceScan = true; + const forceConfirmation = false; + late pb.LaunchCompanionRequest result; + + setUp(() { + result = _sut.createLaunchCompanionWorkflowRequest( + deviceNamePattern: deviceNamePattern, + singleDeviceScan: singleDeviceScan, + forceConfirmation: forceConfirmation, + ); + }); + + test('It converts device name pattern', () { + expect(result.deviceNamePattern, deviceNamePattern); + }); + test('It converts single device scan flag', () { + expect(result.singleDeviceScan, singleDeviceScan); + }); + test('It converts force confirmation flag', () { + expect(result.forceConfirmation, forceConfirmation); + }); + }); + group('Scan for devices request', () { late pb.ScanForDevicesRequest result; const scanMode = ScanMode.lowLatency; diff --git a/packages/reactive_ble_mobile/test/converter/protobuf_converter_test.dart b/packages/reactive_ble_mobile/test/converter/protobuf_converter_test.dart index cda827bf..99420675 100644 --- a/packages/reactive_ble_mobile/test/converter/protobuf_converter_test.dart +++ b/packages/reactive_ble_mobile/test/converter/protobuf_converter_test.dart @@ -3,6 +3,7 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:reactive_ble_mobile/src/converter/protobuf_converter.dart'; import 'package:reactive_ble_mobile/src/generated/bledata.pb.dart' as pb; +import 'package:reactive_ble_mobile/src/generated/bledata.pbenum.dart'; import 'package:reactive_ble_platform_interface/reactive_ble_platform_interface.dart'; void main() { @@ -463,6 +464,15 @@ void main() { }); }); + group('decoding ${pb.EstablishBondingInfo}', () { + test('converts valid status', () { + final message = pb.EstablishBondingInfo() + ..status = EstablishBondingInfo_BondState.BONDED; + expect(sut.bondingStatusFrom(message.writeToBuffer()), + BondingStatus.bonded); + }); + }); + group('decoding ${pb.BleStatusInfo}', () { test('converts valid status', () { final message = pb.BleStatusInfo()..status = 5; diff --git a/packages/reactive_ble_mobile/test/reactive_ble_platform_test.mocks.dart b/packages/reactive_ble_mobile/test/reactive_ble_platform_test.mocks.dart index 034d0794..447cf90f 100644 --- a/packages/reactive_ble_mobile/test/reactive_ble_platform_test.mocks.dart +++ b/packages/reactive_ble_mobile/test/reactive_ble_platform_test.mocks.dart @@ -28,9 +28,20 @@ import 'package:reactive_ble_platform_interface/reactive_ble_platform_interface. // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeConnectToDeviceRequest_0 extends _i1.SmartFake +class _FakeEstablishBondingRequest_0 extends _i1.SmartFake + implements _i2.EstablishBondingRequest { + _FakeEstablishBondingRequest_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeConnectToDeviceRequest_1 extends _i1.SmartFake implements _i2.ConnectToDeviceRequest { - _FakeConnectToDeviceRequest_0( + _FakeConnectToDeviceRequest_1( Object parent, Invocation parentInvocation, ) : super( @@ -39,9 +50,20 @@ class _FakeConnectToDeviceRequest_0 extends _i1.SmartFake ); } -class _FakeDisconnectFromDeviceRequest_1 extends _i1.SmartFake +class _FakeGetDeviceNameRequest_2 extends _i1.SmartFake + implements _i2.GetDeviceNameRequest { + _FakeGetDeviceNameRequest_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDisconnectFromDeviceRequest_3 extends _i1.SmartFake implements _i2.DisconnectFromDeviceRequest { - _FakeDisconnectFromDeviceRequest_1( + _FakeDisconnectFromDeviceRequest_3( Object parent, Invocation parentInvocation, ) : super( @@ -50,9 +72,9 @@ class _FakeDisconnectFromDeviceRequest_1 extends _i1.SmartFake ); } -class _FakeReadCharacteristicRequest_2 extends _i1.SmartFake +class _FakeReadCharacteristicRequest_4 extends _i1.SmartFake implements _i2.ReadCharacteristicRequest { - _FakeReadCharacteristicRequest_2( + _FakeReadCharacteristicRequest_4( Object parent, Invocation parentInvocation, ) : super( @@ -61,9 +83,9 @@ class _FakeReadCharacteristicRequest_2 extends _i1.SmartFake ); } -class _FakeWriteCharacteristicRequest_3 extends _i1.SmartFake +class _FakeWriteCharacteristicRequest_5 extends _i1.SmartFake implements _i2.WriteCharacteristicRequest { - _FakeWriteCharacteristicRequest_3( + _FakeWriteCharacteristicRequest_5( Object parent, Invocation parentInvocation, ) : super( @@ -72,9 +94,9 @@ class _FakeWriteCharacteristicRequest_3 extends _i1.SmartFake ); } -class _FakeNotifyCharacteristicRequest_4 extends _i1.SmartFake +class _FakeNotifyCharacteristicRequest_6 extends _i1.SmartFake implements _i2.NotifyCharacteristicRequest { - _FakeNotifyCharacteristicRequest_4( + _FakeNotifyCharacteristicRequest_6( Object parent, Invocation parentInvocation, ) : super( @@ -83,9 +105,9 @@ class _FakeNotifyCharacteristicRequest_4 extends _i1.SmartFake ); } -class _FakeNotifyNoMoreCharacteristicRequest_5 extends _i1.SmartFake +class _FakeNotifyNoMoreCharacteristicRequest_7 extends _i1.SmartFake implements _i2.NotifyNoMoreCharacteristicRequest { - _FakeNotifyNoMoreCharacteristicRequest_5( + _FakeNotifyNoMoreCharacteristicRequest_7( Object parent, Invocation parentInvocation, ) : super( @@ -94,9 +116,9 @@ class _FakeNotifyNoMoreCharacteristicRequest_5 extends _i1.SmartFake ); } -class _FakeNegotiateMtuRequest_6 extends _i1.SmartFake +class _FakeNegotiateMtuRequest_8 extends _i1.SmartFake implements _i2.NegotiateMtuRequest { - _FakeNegotiateMtuRequest_6( + _FakeNegotiateMtuRequest_8( Object parent, Invocation parentInvocation, ) : super( @@ -105,9 +127,20 @@ class _FakeNegotiateMtuRequest_6 extends _i1.SmartFake ); } -class _FakeChangeConnectionPriorityRequest_7 extends _i1.SmartFake +class _FakeChangeConnectionPriorityRequest_9 extends _i1.SmartFake implements _i2.ChangeConnectionPriorityRequest { - _FakeChangeConnectionPriorityRequest_7( + _FakeChangeConnectionPriorityRequest_9( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeLaunchCompanionRequest_10 extends _i1.SmartFake + implements _i2.LaunchCompanionRequest { + _FakeLaunchCompanionRequest_10( Object parent, Invocation parentInvocation, ) : super( @@ -116,9 +149,9 @@ class _FakeChangeConnectionPriorityRequest_7 extends _i1.SmartFake ); } -class _FakeScanForDevicesRequest_8 extends _i1.SmartFake +class _FakeScanForDevicesRequest_11 extends _i1.SmartFake implements _i2.ScanForDevicesRequest { - _FakeScanForDevicesRequest_8( + _FakeScanForDevicesRequest_11( Object parent, Invocation parentInvocation, ) : super( @@ -127,9 +160,9 @@ class _FakeScanForDevicesRequest_8 extends _i1.SmartFake ); } -class _FakeClearGattCacheRequest_9 extends _i1.SmartFake +class _FakeClearGattCacheRequest_12 extends _i1.SmartFake implements _i2.ClearGattCacheRequest { - _FakeClearGattCacheRequest_9( + _FakeClearGattCacheRequest_12( Object parent, Invocation parentInvocation, ) : super( @@ -138,9 +171,20 @@ class _FakeClearGattCacheRequest_9 extends _i1.SmartFake ); } -class _FakeDiscoverServicesRequest_10 extends _i1.SmartFake +class _FakeDiscoverServicesRequest_13 extends _i1.SmartFake implements _i2.DiscoverServicesRequest { - _FakeDiscoverServicesRequest_10( + _FakeDiscoverServicesRequest_13( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDeviceAssociationInfo_14 extends _i1.SmartFake + implements _i3.DeviceAssociationInfo { + _FakeDeviceAssociationInfo_14( Object parent, Invocation parentInvocation, ) : super( @@ -149,8 +193,8 @@ class _FakeDiscoverServicesRequest_10 extends _i1.SmartFake ); } -class _FakeScanResult_11 extends _i1.SmartFake implements _i3.ScanResult { - _FakeScanResult_11( +class _FakeScanResult_15 extends _i1.SmartFake implements _i3.ScanResult { + _FakeScanResult_15( Object parent, Invocation parentInvocation, ) : super( @@ -159,9 +203,9 @@ class _FakeScanResult_11 extends _i1.SmartFake implements _i3.ScanResult { ); } -class _FakeConnectionStateUpdate_12 extends _i1.SmartFake +class _FakeConnectionStateUpdate_16 extends _i1.SmartFake implements _i3.ConnectionStateUpdate { - _FakeConnectionStateUpdate_12( + _FakeConnectionStateUpdate_16( Object parent, Invocation parentInvocation, ) : super( @@ -170,9 +214,9 @@ class _FakeConnectionStateUpdate_12 extends _i1.SmartFake ); } -class _FakeResult_13 extends _i1.SmartFake +class _FakeResult_17 extends _i1.SmartFake implements _i3.Result { - _FakeResult_13( + _FakeResult_17( Object parent, Invocation parentInvocation, ) : super( @@ -181,9 +225,9 @@ class _FakeResult_13 extends _i1.SmartFake ); } -class _FakeCharacteristicValue_14 extends _i1.SmartFake +class _FakeCharacteristicValue_18 extends _i1.SmartFake implements _i3.CharacteristicValue { - _FakeCharacteristicValue_14( + _FakeCharacteristicValue_18( Object parent, Invocation parentInvocation, ) : super( @@ -192,9 +236,9 @@ class _FakeCharacteristicValue_14 extends _i1.SmartFake ); } -class _FakeWriteCharacteristicInfo_15 extends _i1.SmartFake +class _FakeWriteCharacteristicInfo_19 extends _i1.SmartFake implements _i3.WriteCharacteristicInfo { - _FakeWriteCharacteristicInfo_15( + _FakeWriteCharacteristicInfo_19( Object parent, Invocation parentInvocation, ) : super( @@ -203,9 +247,9 @@ class _FakeWriteCharacteristicInfo_15 extends _i1.SmartFake ); } -class _FakeConnectionPriorityInfo_16 extends _i1.SmartFake +class _FakeConnectionPriorityInfo_20 extends _i1.SmartFake implements _i3.ConnectionPriorityInfo { - _FakeConnectionPriorityInfo_16( + _FakeConnectionPriorityInfo_20( Object parent, Invocation parentInvocation, ) : super( @@ -214,8 +258,8 @@ class _FakeConnectionPriorityInfo_16 extends _i1.SmartFake ); } -class _FakeMethodCodec_17 extends _i1.SmartFake implements _i4.MethodCodec { - _FakeMethodCodec_17( +class _FakeMethodCodec_21 extends _i1.SmartFake implements _i4.MethodCodec { + _FakeMethodCodec_21( Object parent, Invocation parentInvocation, ) : super( @@ -224,9 +268,9 @@ class _FakeMethodCodec_17 extends _i1.SmartFake implements _i4.MethodCodec { ); } -class _FakeBinaryMessenger_18 extends _i1.SmartFake +class _FakeBinaryMessenger_22 extends _i1.SmartFake implements _i5.BinaryMessenger { - _FakeBinaryMessenger_18( + _FakeBinaryMessenger_22( Object parent, Invocation parentInvocation, ) : super( @@ -244,6 +288,21 @@ class MockArgsToProtobufConverter extends _i1.Mock _i1.throwOnMissingStub(this); } + @override + _i2.EstablishBondingRequest createEstablishBondingArgs(String? id) => + (super.noSuchMethod( + Invocation.method( + #createEstablishBondingArgs, + [id], + ), + returnValue: _FakeEstablishBondingRequest_0( + this, + Invocation.method( + #createEstablishBondingArgs, + [id], + ), + ), + ) as _i2.EstablishBondingRequest); @override _i2.ConnectToDeviceRequest createConnectToDeviceArgs( String? id, @@ -259,7 +318,7 @@ class MockArgsToProtobufConverter extends _i1.Mock connectionTimeout, ], ), - returnValue: _FakeConnectToDeviceRequest_0( + returnValue: _FakeConnectToDeviceRequest_1( this, Invocation.method( #createConnectToDeviceArgs, @@ -272,6 +331,21 @@ class MockArgsToProtobufConverter extends _i1.Mock ), ) as _i2.ConnectToDeviceRequest); @override + _i2.GetDeviceNameRequest createGetDeviceNameArgs(String? id) => + (super.noSuchMethod( + Invocation.method( + #createGetDeviceNameArgs, + [id], + ), + returnValue: _FakeGetDeviceNameRequest_2( + this, + Invocation.method( + #createGetDeviceNameArgs, + [id], + ), + ), + ) as _i2.GetDeviceNameRequest); + @override _i2.DisconnectFromDeviceRequest createDisconnectDeviceArgs( String? deviceId) => (super.noSuchMethod( @@ -279,7 +353,7 @@ class MockArgsToProtobufConverter extends _i1.Mock #createDisconnectDeviceArgs, [deviceId], ), - returnValue: _FakeDisconnectFromDeviceRequest_1( + returnValue: _FakeDisconnectFromDeviceRequest_3( this, Invocation.method( #createDisconnectDeviceArgs, @@ -295,7 +369,7 @@ class MockArgsToProtobufConverter extends _i1.Mock #createReadCharacteristicRequest, [characteristic], ), - returnValue: _FakeReadCharacteristicRequest_2( + returnValue: _FakeReadCharacteristicRequest_4( this, Invocation.method( #createReadCharacteristicRequest, @@ -316,7 +390,7 @@ class MockArgsToProtobufConverter extends _i1.Mock value, ], ), - returnValue: _FakeWriteCharacteristicRequest_3( + returnValue: _FakeWriteCharacteristicRequest_5( this, Invocation.method( #createWriteCharacteristicRequest, @@ -335,7 +409,7 @@ class MockArgsToProtobufConverter extends _i1.Mock #createNotifyCharacteristicRequest, [characteristic], ), - returnValue: _FakeNotifyCharacteristicRequest_4( + returnValue: _FakeNotifyCharacteristicRequest_6( this, Invocation.method( #createNotifyCharacteristicRequest, @@ -351,7 +425,7 @@ class MockArgsToProtobufConverter extends _i1.Mock #createNotifyNoMoreCharacteristicRequest, [characteristic], ), - returnValue: _FakeNotifyNoMoreCharacteristicRequest_5( + returnValue: _FakeNotifyNoMoreCharacteristicRequest_7( this, Invocation.method( #createNotifyNoMoreCharacteristicRequest, @@ -372,7 +446,7 @@ class MockArgsToProtobufConverter extends _i1.Mock mtu, ], ), - returnValue: _FakeNegotiateMtuRequest_6( + returnValue: _FakeNegotiateMtuRequest_8( this, Invocation.method( #createNegotiateMtuRequest, @@ -396,7 +470,7 @@ class MockArgsToProtobufConverter extends _i1.Mock priority, ], ), - returnValue: _FakeChangeConnectionPriorityRequest_7( + returnValue: _FakeChangeConnectionPriorityRequest_9( this, Invocation.method( #createChangeConnectionPrioRequest, @@ -408,6 +482,35 @@ class MockArgsToProtobufConverter extends _i1.Mock ), ) as _i2.ChangeConnectionPriorityRequest); @override + _i2.LaunchCompanionRequest createLaunchCompanionWorkflowRequest({ + required String? deviceNamePattern, + required bool? singleDeviceScan, + required bool? forceConfirmation, + }) => + (super.noSuchMethod( + Invocation.method( + #createLaunchCompanionWorkflowRequest, + [], + { + #deviceNamePattern: deviceNamePattern, + #singleDeviceScan: singleDeviceScan, + #forceConfirmation: forceConfirmation, + }, + ), + returnValue: _FakeLaunchCompanionRequest_10( + this, + Invocation.method( + #createLaunchCompanionWorkflowRequest, + [], + { + #deviceNamePattern: deviceNamePattern, + #singleDeviceScan: singleDeviceScan, + #forceConfirmation: forceConfirmation, + }, + ), + ), + ) as _i2.LaunchCompanionRequest); + @override _i2.ScanForDevicesRequest createScanForDevicesRequest({ required List<_i3.Uuid>? withServices, required _i3.ScanMode? scanMode, @@ -423,7 +526,7 @@ class MockArgsToProtobufConverter extends _i1.Mock #requireLocationServicesEnabled: requireLocationServicesEnabled, }, ), - returnValue: _FakeScanForDevicesRequest_8( + returnValue: _FakeScanForDevicesRequest_11( this, Invocation.method( #createScanForDevicesRequest, @@ -443,7 +546,7 @@ class MockArgsToProtobufConverter extends _i1.Mock #createClearGattCacheRequest, [deviceId], ), - returnValue: _FakeClearGattCacheRequest_9( + returnValue: _FakeClearGattCacheRequest_12( this, Invocation.method( #createClearGattCacheRequest, @@ -458,7 +561,7 @@ class MockArgsToProtobufConverter extends _i1.Mock #createDiscoverServicesRequest, [deviceId], ), - returnValue: _FakeDiscoverServicesRequest_10( + returnValue: _FakeDiscoverServicesRequest_13( this, Invocation.method( #createDiscoverServicesRequest, @@ -476,6 +579,14 @@ class MockProtobufConverter extends _i1.Mock implements _i7.ProtobufConverter { _i1.throwOnMissingStub(this); } + @override + _i3.BondingStatus bondingStatusFrom(List? data) => (super.noSuchMethod( + Invocation.method( + #bondingStatusFrom, + [data], + ), + returnValue: _i3.BondingStatus.none, + ) as _i3.BondingStatus); @override _i3.BleStatus bleStatusFrom(List? data) => (super.noSuchMethod( Invocation.method( @@ -485,12 +596,27 @@ class MockProtobufConverter extends _i1.Mock implements _i7.ProtobufConverter { returnValue: _i3.BleStatus.unknown, ) as _i3.BleStatus); @override + _i3.DeviceAssociationInfo associationInfoFrom(List? data) => + (super.noSuchMethod( + Invocation.method( + #associationInfoFrom, + [data], + ), + returnValue: _FakeDeviceAssociationInfo_14( + this, + Invocation.method( + #associationInfoFrom, + [data], + ), + ), + ) as _i3.DeviceAssociationInfo); + @override _i3.ScanResult scanResultFrom(List? data) => (super.noSuchMethod( Invocation.method( #scanResultFrom, [data], ), - returnValue: _FakeScanResult_11( + returnValue: _FakeScanResult_15( this, Invocation.method( #scanResultFrom, @@ -499,13 +625,21 @@ class MockProtobufConverter extends _i1.Mock implements _i7.ProtobufConverter { ), ) as _i3.ScanResult); @override + String deviceNameFrom(List? data) => (super.noSuchMethod( + Invocation.method( + #deviceNameFrom, + [data], + ), + returnValue: '', + ) as String); + @override _i3.ConnectionStateUpdate connectionStateUpdateFrom(List? data) => (super.noSuchMethod( Invocation.method( #connectionStateUpdateFrom, [data], ), - returnValue: _FakeConnectionStateUpdate_12( + returnValue: _FakeConnectionStateUpdate_16( this, Invocation.method( #connectionStateUpdateFrom, @@ -520,7 +654,7 @@ class MockProtobufConverter extends _i1.Mock implements _i7.ProtobufConverter { #clearGattCacheResultFrom, [data], ), - returnValue: _FakeResult_13<_i3.Unit, + returnValue: _FakeResult_17<_i3.Unit, _i3.GenericFailure<_i3.ClearGattCacheError>?>( this, Invocation.method( @@ -537,7 +671,7 @@ class MockProtobufConverter extends _i1.Mock implements _i7.ProtobufConverter { #characteristicValueFrom, [data], ), - returnValue: _FakeCharacteristicValue_14( + returnValue: _FakeCharacteristicValue_18( this, Invocation.method( #characteristicValueFrom, @@ -552,7 +686,7 @@ class MockProtobufConverter extends _i1.Mock implements _i7.ProtobufConverter { #writeCharacteristicInfoFrom, [data], ), - returnValue: _FakeWriteCharacteristicInfo_15( + returnValue: _FakeWriteCharacteristicInfo_19( this, Invocation.method( #writeCharacteristicInfoFrom, @@ -567,7 +701,7 @@ class MockProtobufConverter extends _i1.Mock implements _i7.ProtobufConverter { #connectionPriorityInfoFrom, [data], ), - returnValue: _FakeConnectionPriorityInfo_16( + returnValue: _FakeConnectionPriorityInfo_20( this, Invocation.method( #connectionPriorityInfoFrom, @@ -610,7 +744,7 @@ class MockMethodChannel extends _i1.Mock implements _i8.MethodChannel { @override _i4.MethodCodec get codec => (super.noSuchMethod( Invocation.getter(#codec), - returnValue: _FakeMethodCodec_17( + returnValue: _FakeMethodCodec_21( this, Invocation.getter(#codec), ), @@ -618,7 +752,7 @@ class MockMethodChannel extends _i1.Mock implements _i8.MethodChannel { @override _i5.BinaryMessenger get binaryMessenger => (super.noSuchMethod( Invocation.getter(#binaryMessenger), - returnValue: _FakeBinaryMessenger_18( + returnValue: _FakeBinaryMessenger_22( this, Invocation.getter(#binaryMessenger), ), From 1be27654225f00a124ee1d4dbe47752cf8dc5c57 Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Mon, 10 Jul 2023 16:30:09 +0100 Subject: [PATCH 11/17] shorten --- .../reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift index dcfe55eb..ebffca7e 100644 --- a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift +++ b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift @@ -171,9 +171,7 @@ final class Central { func retrievePeripheralName( for peripheralID: PeripheralID ) throws -> String? { - let peripheral = try resolve(known: peripheralID) - - return peripheral.name + return try resolve(known: peripheralID).name } func discoverServicesWithCharacteristics( From eecbfeb16b992eca7229fd1ee0863ae4b6fff08a Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Mon, 10 Jul 2023 20:33:44 +0100 Subject: [PATCH 12/17] format --- .swiftlint.yml | 23 ++++++++++++++++ .../Classes/BleData extras/BLEStatus.swift | 2 +- .../Classes/BleData extras/FailureCodes.swift | 2 +- .../ios/Classes/Plugin/Common/EventSink.swift | 1 - .../ios/Classes/Plugin/PluginController.swift | 27 +++++++++---------- .../Plugin/SwiftReactiveBlePlugin.swift | 2 +- .../ios/Classes/ReactiveBle/Central.swift | 22 +++++++-------- .../ReactiveBle/CentralManagerDelegate.swift | 2 +- .../CharacteristicNotifyTaskController.swift | 6 ++--- .../Tasks/Connect/ConnectTaskController.swift | 2 +- .../Tasks/Connect/ConnectTaskSpec.swift | 2 +- .../PeripheralTaskRegistry.swift | 3 +-- 12 files changed, 57 insertions(+), 37 deletions(-) create mode 100644 .swiftlint.yml diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 00000000..0442567c --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,23 @@ +disabled_rules: + - cyclomatic_complexity + - identifier_name + - multiple_closures_with_trailing_closure + - nesting + - type_name + - unused_closure_parameter + +excluded: # case-sensitive paths to ignore during linting. Takes precedence over `included` + - Pods + - "**/*.pb.swift" # exclude files with a wildcard + +line_length: 300 +type_body_length: + - 600 + +function_body_length: + - 150 + +file_length: + warning: 800 + +reporter: "xcode" diff --git a/packages/reactive_ble_mobile/ios/Classes/BleData extras/BLEStatus.swift b/packages/reactive_ble_mobile/ios/Classes/BleData extras/BLEStatus.swift index 25b0d36e..7f48f181 100644 --- a/packages/reactive_ble_mobile/ios/Classes/BleData extras/BLEStatus.swift +++ b/packages/reactive_ble_mobile/ios/Classes/BleData extras/BLEStatus.swift @@ -1,7 +1,7 @@ import enum CoreBluetooth.CBManagerState func encode(_ centralState: CBManagerState) -> Int32 { - switch (centralState) { + switch centralState { case .unknown, .resetting: return 0 case .unsupported: diff --git a/packages/reactive_ble_mobile/ios/Classes/BleData extras/FailureCodes.swift b/packages/reactive_ble_mobile/ios/Classes/BleData extras/FailureCodes.swift index 4c608970..b7fdc2c9 100644 --- a/packages/reactive_ble_mobile/ios/Classes/BleData extras/FailureCodes.swift +++ b/packages/reactive_ble_mobile/ios/Classes/BleData extras/FailureCodes.swift @@ -25,6 +25,6 @@ enum MaximumWriteValueLengthRetrieval: Int { } enum RequestConnectionPriorityFailure: Int { - + case operationNotSupported = 1 } diff --git a/packages/reactive_ble_mobile/ios/Classes/Plugin/Common/EventSink.swift b/packages/reactive_ble_mobile/ios/Classes/Plugin/Common/EventSink.swift index 713e5378..0e220d37 100644 --- a/packages/reactive_ble_mobile/ios/Classes/Plugin/Common/EventSink.swift +++ b/packages/reactive_ble_mobile/ios/Classes/Plugin/Common/EventSink.swift @@ -29,4 +29,3 @@ struct EventSink { } } } - diff --git a/packages/reactive_ble_mobile/ios/Classes/Plugin/PluginController.swift b/packages/reactive_ble_mobile/ios/Classes/Plugin/PluginController.swift index c23d809b..fcf11263 100644 --- a/packages/reactive_ble_mobile/ios/Classes/Plugin/PluginController.swift +++ b/packages/reactive_ble_mobile/ios/Classes/Plugin/PluginController.swift @@ -22,7 +22,7 @@ final class PluginController { } } } - var messageQueue: [CharacteristicValueInfo] = []; + var messageQueue: [CharacteristicValueInfo] = [] var connectedDeviceSink: EventSink? var characteristicValueUpdateSink: EventSink? @@ -128,11 +128,11 @@ final class PluginController { } } let sink = context.characteristicValueUpdateSink - if (sink != nil) { + if sink != nil { sink!.add(.success(message)) } else { // In case message arrives before sink is created - context.messageQueue.append(message); + context.messageQueue.append(message) } } @@ -205,8 +205,7 @@ final class PluginController { let servicesWithCharacteristicsToDiscover: ServicesWithCharacteristicsToDiscover if args.hasServicesWithCharacteristicsToDiscover { let items = args.servicesWithCharacteristicsToDiscover.items.reduce( - into: [ServiceID: [CharacteristicID]](), - { dict, item in + into: [ServiceID: [CharacteristicID]](), { dict, item in let serviceID = CBUUID(data: item.serviceID.data) let characteristicIDs = item.characteristics.map { CBUUID(data: $0.data) } @@ -221,7 +220,7 @@ final class PluginController { let timeout = args.timeoutInMs > 0 ? TimeInterval(args.timeoutInMs) / 1000 : nil completion(.success(nil)) - + if let sink = connectedDeviceSink { let message = DeviceInfo.with { $0.id = args.deviceID @@ -231,7 +230,7 @@ final class PluginController { } else { print("Warning! No event channel set up to report a connection update") } - + do { try central.connect( to: deviceID, @@ -321,10 +320,10 @@ final class PluginController { Uuid.with { $0.data = characteristic.uuid.data } } $0.characteristics = (service.characteristics ?? []).map { characteristic in - DiscoveredCharacteristic.with{ - $0.characteristicID = Uuid.with{$0.data = characteristic.uuid.data} + DiscoveredCharacteristic.with { + $0.characteristicID = Uuid.with {$0.data = characteristic.uuid.data} if characteristic.service?.uuid.data != nil { - $0.serviceID = Uuid.with{$0.data = characteristic.service!.uuid.data} + $0.serviceID = Uuid.with {$0.data = characteristic.service!.uuid.data} } $0.isReadable = characteristic.properties.contains(.read) $0.isWritableWithResponse = characteristic.properties.contains(.write) @@ -333,7 +332,7 @@ final class PluginController { $0.isIndicatable = characteristic.properties.contains(.indicate) } } - + $0.includedServices = (service.includedServices ?? []).map(makeDiscoveredService) } } @@ -488,20 +487,20 @@ final class PluginController { completion(.success(result)) } } - + func writeCharacteristicWithoutResponse(name: String, args: WriteCharacteristicRequest, completion: @escaping PlatformMethodCompletionHandler) { guard let central = central else { completion(.failure(PluginError.notInitialized.asFlutterError)) return } - + guard let characteristic = QualifiedCharacteristicIDFactory().make(from: args.characteristic) else { completion(.failure(PluginError.invalidMethodCall(method: name, details: "characteristic, service, and peripheral IDs are required").asFlutterError)) return } - + let result: WriteCharacteristicInfo do { try central.writeWithoutResponse( diff --git a/packages/reactive_ble_mobile/ios/Classes/Plugin/SwiftReactiveBlePlugin.swift b/packages/reactive_ble_mobile/ios/Classes/Plugin/SwiftReactiveBlePlugin.swift index 68391518..c08dfa51 100644 --- a/packages/reactive_ble_mobile/ios/Classes/Plugin/SwiftReactiveBlePlugin.swift +++ b/packages/reactive_ble_mobile/ios/Classes/Plugin/SwiftReactiveBlePlugin.swift @@ -140,7 +140,7 @@ public class SwiftReactiveBlePlugin: NSObject, FlutterPlugin { }), AnyPlatformMethod(UnaryPlatformMethod(name: "negotiateMtuSize") { (name, context, args: NegotiateMtuRequest, completion) in context.reportMaximumWriteValueLength(name: name, args: args, completion: completion) - }), + }) ]) public func handle(_ call: FlutterMethodCall, result completion: @escaping FlutterResult) { diff --git a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift index ebffca7e..d0f10683 100644 --- a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift +++ b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift @@ -143,7 +143,7 @@ final class Central { discover: servicesWithCharacteristicsToDiscover, completion: central.onServicesWithCharacteristicsInitialDiscovery ) - case .failedToConnect(_), .disconnected(_): + case .failedToConnect, .disconnected: break } } @@ -167,7 +167,7 @@ final class Central { .values .forEach(centralManager.cancelPeripheralConnection) } - + func retrievePeripheralName( for peripheralID: PeripheralID ) throws -> String? { @@ -178,7 +178,7 @@ final class Central { for peripheralID: PeripheralID, discover servicesWithCharacteristicsToDiscover: ServicesWithCharacteristicsToDiscover, completion: @escaping ServicesWithCharacteristicsDiscoveryHandler - ) throws -> Void { + ) throws { let peripheral = try resolve(connected: peripheralID) discoverServicesWithCharacteristics( @@ -192,7 +192,7 @@ final class Central { for peripheral: CBPeripheral, discover servicesWithCharacteristicsToDiscover: ServicesWithCharacteristicsToDiscover, completion: @escaping ServicesWithCharacteristicsDiscoveryHandler - ) -> Void { + ) { servicesWithCharacteristicsDiscoveryRegistry.registerTask( key: peripheral.identifier, params: .init(servicesWithCharacteristicsToDiscover: servicesWithCharacteristicsToDiscover), @@ -232,10 +232,10 @@ final class Central { guard characteristic.properties.contains(.read) else { throw Failure.notReadable(qualifiedCharacteristic) } - + guard let peripheral = characteristic.service?.peripheral else { throw Failure.peripheralIsUnknown(qualifiedCharacteristic.peripheralID) } - + peripheral.readValue(for: characteristic) } @@ -257,16 +257,16 @@ final class Central { completion(central, qualifiedCharacteristic, error) } ) - + guard let peripheral = characteristic.service?.peripheral - else{ throw Failure.peripheralIsUnknown(qualifiedCharacteristic.peripheralID) } + else { throw Failure.peripheralIsUnknown(qualifiedCharacteristic.peripheralID) } characteristicWriteRegistry.updateTask( key: qualifiedCharacteristic, action: { $0.start(peripheral: peripheral) } ) } - + func writeWithoutResponse( value: Data, characteristic qualifiedCharacteristic: QualifiedCharacteristic @@ -275,10 +275,10 @@ final class Central { guard characteristic.properties.contains(.writeWithoutResponse) else { throw Failure.notWritable(qualifiedCharacteristic) } - + guard let response = characteristic.service?.peripheral?.writeValue(value, for: characteristic, type: .withoutResponse) else { throw Failure.characteristicNotFound(qualifiedCharacteristic) } - + return response } diff --git a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/CentralManagerDelegate.swift b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/CentralManagerDelegate.swift index c7a94132..22244dcc 100644 --- a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/CentralManagerDelegate.swift +++ b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/CentralManagerDelegate.swift @@ -30,7 +30,7 @@ final class CentralManagerDelegate: NSObject, CBCentralManagerDelegate { onStateChange(central.state) } - func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber) { + func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi: NSNumber) { onDiscovery(peripheral, advertisementData, rssi.intValue) } diff --git a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/CharacteristicNotify/CharacteristicNotifyTaskController.swift b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/CharacteristicNotify/CharacteristicNotifyTaskController.swift index a0119c50..b5c74ba2 100644 --- a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/CharacteristicNotify/CharacteristicNotifyTaskController.swift +++ b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/CharacteristicNotify/CharacteristicNotifyTaskController.swift @@ -13,7 +13,7 @@ struct CharacteristicNotifyTaskController: PeripheralTaskController { func start(characteristic: CBCharacteristic) -> SubjectTask { guard let peripheral = characteristic.service?.peripheral else { return task.with(state: task.state.finished(CharacteristicNotifyError.unExpected)) } - + peripheral.setNotifyValue(task.params.state.isOn, for: characteristic) return task.with(state: task.state.processing(.applying)) } @@ -26,7 +26,7 @@ struct CharacteristicNotifyTaskController: PeripheralTaskController { return task.with(state: task.state.finished(error)) } - private enum CharacteristicNotifyError: Error{ - case unExpected; + private enum CharacteristicNotifyError: Error { + case unExpected } } diff --git a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/Connect/ConnectTaskController.swift b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/Connect/ConnectTaskController.swift index 5eb67c19..b5019d1f 100644 --- a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/Connect/ConnectTaskController.swift +++ b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/Connect/ConnectTaskController.swift @@ -39,7 +39,7 @@ struct ConnectTaskController: PeripheralTaskController { case .processing(since: _, .connecting): centralManager.cancelPeripheralConnection(peripheral) return task.with(state: task.state.finished(.failedToConnect(error))) - case .finished(in: _, _): + case .finished: assert(false) return task } diff --git a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/Connect/ConnectTaskSpec.swift b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/Connect/ConnectTaskSpec.swift index 208f0e99..2d5d8b87 100644 --- a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/Connect/ConnectTaskSpec.swift +++ b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/Connect/ConnectTaskSpec.swift @@ -1,7 +1,7 @@ struct ConnectTaskSpec: PeripheralTaskSpec { typealias Key = PeripheralID - + struct Params {} enum Stage { diff --git a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/PeripheralTaskRegistry/PeripheralTaskRegistry.swift b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/PeripheralTaskRegistry/PeripheralTaskRegistry.swift index 37e6f8fb..c0315a05 100644 --- a/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/PeripheralTaskRegistry/PeripheralTaskRegistry.swift +++ b/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/PeripheralTaskRegistry/PeripheralTaskRegistry.swift @@ -46,8 +46,7 @@ final class PeripheralTaskRegistry { tasks.update(record.with(task: updatedTask)) if case .pending = record.task.state, case .processing = updatedTask.state, - let timeout = record.task.timeout - { + let timeout = record.task.timeout { scheduleTaskTimeout(record.uniqueID, timeout) } } From 7145afe40293460de8b4748268e909f5399eb8bd Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Tue, 22 Aug 2023 10:02:42 +0100 Subject: [PATCH 13/17] Add implementation for Android < 13 --- .../flutterreactiveble/PluginController.kt | 27 ++++------ .../channelhandlers/CompanionHandler.kt | 54 ++++++++++++------- .../DeviceConnectionHandler.kt | 2 +- .../converters/ProtobufMessageConverter.kt | 3 +- .../ProtobufMessageConverterTest.kt | 4 +- 5 files changed, 48 insertions(+), 42 deletions(-) diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt index 9901c77d..2b2be975 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt @@ -4,7 +4,6 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.os.Build -import android.util.Log import com.signify.hue.flutterreactiveble.ble.RequestConnectionPriorityFailed import com.signify.hue.flutterreactiveble.channelhandlers.BleStatusHandler import com.signify.hue.flutterreactiveble.channelhandlers.CharNotificationHandler @@ -37,7 +36,7 @@ class PluginController : PluginRegistry.ActivityResultListener { "deinitialize" to this::deinitializeClient, "launchCompanionWorkflow" to this::launchCompanionFlow, "scanForDevices" to this::scanForDevices, - "establishBond" to this::establishBond, + "establishBonding" to this::establishBonding, "connectToDevice" to this::connectToDevice, "clearGattCache" to this::clearGattCache, "disconnectFromDevice" to this::disconnectFromDevice, @@ -73,7 +72,7 @@ class PluginController : PluginRegistry.ActivityResultListener { charNotificationChannel = EventChannel(messenger, "flutter_reactive_ble_char_update") val bleStatusChannel = EventChannel(messenger, "flutter_reactive_ble_status") - companionHandler = CompanionHandler(context, bleClient) + companionHandler = CompanionHandler() scandevicesHandler = ScanDevicesHandler(bleClient) deviceConnectionHandler = DeviceConnectionHandler(bleClient) charNotificationHandler = CharNotificationHandler(bleClient) @@ -124,12 +123,12 @@ class PluginController : PluginRegistry.ActivityResultListener { result.success(null) } - private fun establishBond(call: MethodCall, result: Result) { - val establishBondMessage = pb.EstablishBondingRequest.parseFrom(call.arguments as ByteArray) - deviceConnectionHandler.establishBond(establishBondMessage).subscribe({ - result.success(protoConverter.convertBondInfo(it).toByteArray()) + private fun establishBonding(call: MethodCall, result: Result) { + val establishBondingMessage = pb.EstablishBondingRequest.parseFrom(call.arguments as ByteArray) + deviceConnectionHandler.establishBonding(establishBondingMessage).subscribe({ + result.success(protoConverter.convertBondingInfo(it).toByteArray()) }, { - result.error("establish_bond_error", it.message, null) + result.error("establish_bonding_error", it.message, null) }).discard() } @@ -364,20 +363,14 @@ class PluginController : PluginRegistry.ActivityResultListener { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { if (requestCode == CompanionHandler.SELECT_DEVICE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { - Log.d( - TAG, - "received result from device selection activity, parsing via companion handler" - ) - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { - Log.d(TAG, "not running on Android O, ignoring result") return false; } - Log.d(TAG, "running on Android O, parsing result") + // When + companionHandler.onActivityResult(data) ?: return false - val scanResult = companionHandler.onActivityResult(data) ?: return false - Log.d(TAG, "got the scan result: ${scanResult.device.address}") + return true } return false diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt index 9b0269f0..c775c380 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt @@ -13,20 +13,21 @@ import android.os.Build import android.util.Log import androidx.annotation.RequiresApi import com.signify.hue.flutterreactiveble.ProtobufModel -import com.signify.hue.flutterreactiveble.ble.BleClient import io.flutter.plugin.common.MethodChannel import java.lang.ref.WeakReference import java.util.concurrent.Executor import java.util.regex.Pattern -class CompanionHandler(private val context: Context, private val bleClient: BleClient) { +class CompanionHandler { companion object { - const val SELECT_DEVICE_REQUEST_CODE = 426312 + const val SELECT_DEVICE_REQUEST_CODE = 4389 const val TAG = "CompanionHandler" } private var activity = WeakReference(null) + private var tmpResult: MethodChannel.Result? = null + @RequiresApi(Build.VERSION_CODES.O) fun launchCompanionFlow( parseFrom: ProtobufModel.LaunchCompanionRequest, @@ -44,8 +45,14 @@ class CompanionHandler(private val context: Context, private val bleClient: BleC pairingRequestBuilder.setForceConfirmation(parseFrom.forceConfirmation) } + val activity = activity.get() ?: return result.error( + "CompanionHandler", + "Activity is null", + null + ) + val deviceManager = - context.getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager + activity.getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager val executor = Executor { it.run() } @@ -54,22 +61,17 @@ class CompanionHandler(private val context: Context, private val bleClient: BleC executor, object : CompanionDeviceManager.Callback() { override fun onAssociationPending(intentSender: IntentSender) { - Log.d(TAG, "onAssociationPending: $intentSender") - intentSender.let { - activity.get()?.startIntentSenderForResult( - it, - SELECT_DEVICE_REQUEST_CODE, - null, - 0, - 0, - 0 - ) - } + activity.startIntentSenderForResult( + intentSender, + SELECT_DEVICE_REQUEST_CODE, + null, + 0, + 0, + 0 + ) } override fun onAssociationCreated(associationInfo: AssociationInfo) { - Log.d(TAG, "onAssociationCreated: $associationInfo") - result.success( ProtobufModel.DeviceAssociationInfo.newBuilder() .setMacAddress( @@ -78,7 +80,6 @@ class CompanionHandler(private val context: Context, private val bleClient: BleC .build() .toByteArray() ) - associationInfo.deviceMacAddress } override fun onFailure(errorMessage: CharSequence?) { @@ -92,13 +93,15 @@ class CompanionHandler(private val context: Context, private val bleClient: BleC object : CompanionDeviceManager.Callback() { @Deprecated("Deprecated in Java") override fun onDeviceFound(chooserLauncher: IntentSender) { - activity.get()?.startIntentSenderForResult( + tmpResult = result + activity.startIntentSenderForResult( chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0 ) } override fun onFailure(error: CharSequence?) { + Log.e(TAG, "onFailure: $error") result.error("CompanionHandler", error.toString(), null) } }, null @@ -112,7 +115,6 @@ class CompanionHandler(private val context: Context, private val bleClient: BleC @RequiresApi(Build.VERSION_CODES.O) fun onActivityResult(data: Intent?): ScanResult? { - Log.d(TAG, "onActivityResult: $data") return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val associationInfo = data?.getParcelableExtra( CompanionDeviceManager.EXTRA_ASSOCIATION, @@ -130,6 +132,18 @@ class CompanionHandler(private val context: Context, private val bleClient: BleC val scanResult: ScanResult? = data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE) + + if (scanResult != null) { + tmpResult?.success( + ProtobufModel.DeviceAssociationInfo.newBuilder() + .setMacAddress(scanResult.device.address.uppercase()) + .build() + .toByteArray() + ) + + tmpResult = null + } + null } } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt index c9801b18..3aacb021 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt @@ -27,7 +27,7 @@ class DeviceConnectionHandler(private val bleClient: com.signify.hue.flutterreac connectionUpdatesDisposable.dispose() } - fun establishBond(connectToDeviceMessage: pb.EstablishBondingRequest): Single { + fun establishBonding(connectToDeviceMessage: pb.EstablishBondingRequest): Single { return bleClient.establishBond(connectToDeviceMessage.deviceId) } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt index d7b4e801..d7c8bc64 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt @@ -5,7 +5,6 @@ import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattService import com.google.protobuf.ByteString import com.polidea.rxandroidble2.RxBleDeviceServices -import com.signify.hue.flutterreactiveble.ProtobufModel import com.signify.hue.flutterreactiveble.ProtobufModel.EstablishBondingInfo.BondState import com.signify.hue.flutterreactiveble.ble.ConnectionUpdateSuccess import com.signify.hue.flutterreactiveble.ble.MtuNegotiateFailed @@ -263,7 +262,7 @@ class ProtobufMessageConverter { return pb.Uuid.newBuilder().setData(ByteString.copyFrom(convertedUuid)).build() } - fun convertBondInfo(status: Int): pb.EstablishBondingInfo { + fun convertBondingInfo(status: Int): pb.EstablishBondingInfo { val bondState = when (status) { BluetoothDevice.BOND_BONDED -> BondState.BONDED BluetoothDevice.BOND_BONDING -> BondState.BONDING diff --git a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt index 8e67b041..3fce209b 100644 --- a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt +++ b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt @@ -211,7 +211,7 @@ class ProtobufMessageConverterTest { @Test @MethodSource("provideParameters") fun `converts bonded`(bondState: BondState, androidConstant: Int) { - assertThat(protobufConverter.convertBondInfo(androidConstant)).isEqualTo(bondState) + assertThat(protobufConverter.convertBondingInfo(androidConstant)).isEqualTo(bondState) } private fun provideParameters(): Stream? { @@ -226,7 +226,7 @@ class ProtobufMessageConverterTest { fun `converts unknown to none`() { val result = BluetoothDevice.BOND_BONDED - assertThat(protobufConverter.convertBondInfo(result)).isEqualTo(pb.EstablishBondingInfo.BondState.BONDED) + assertThat(protobufConverter.convertBondingInfo(result)).isEqualTo(pb.EstablishBondingInfo.BondState.BONDED) } } From 345f7ee93fc5ffae6b92227afd90f3c62321cdae Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Tue, 16 Jan 2024 23:44:42 +0000 Subject: [PATCH 14/17] various --- .../flutterreactiveble/ble/BondingManager.kt | 81 +++++++++---------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt index db859249..e2d77004 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BondingManager.kt @@ -25,58 +25,55 @@ class BondingManager(private val context: Context) { @SuppressLint("MissingPermission") fun bondWithDevice(rxBleDevice: RxBleDevice): Single { return Single.create { completion -> - when (rxBleDevice.bluetoothDevice.bondState) { + if (rxBleDevice.bluetoothDevice.bondState == BluetoothDevice.BOND_BONDED) { + completion.onSuccess(BluetoothDevice.BOND_BONDED) + return@create + } - BluetoothDevice.BOND_BONDED -> completion.onSuccess(BluetoothDevice.BOND_BONDED) - else -> { - val receiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val deviceBeingPaired: BluetoothDevice? = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra( - BluetoothDevice.EXTRA_DEVICE, - BluetoothDevice::class.java - ) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) - } + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val deviceBeingPaired: BluetoothDevice? = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableExtra( + BluetoothDevice.EXTRA_DEVICE, + BluetoothDevice::class.java + ) + } else { + @Suppress("DEPRECATION") + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) + } - if (deviceBeingPaired?.address == rxBleDevice.bluetoothDevice.address) { - val state = intent.getIntExtra( - BluetoothDevice.EXTRA_BOND_STATE, - BluetoothDevice.BOND_NONE - ) + if (deviceBeingPaired?.address == rxBleDevice.bluetoothDevice.address) { + val state = intent.getIntExtra( + BluetoothDevice.EXTRA_BOND_STATE, + BluetoothDevice.BOND_NONE + ) - when (state) { - BluetoothDevice.BOND_BONDED -> completion.onSuccess(state) - BluetoothDevice.BOND_NONE -> completion.onSuccess(state) - // BOND_BONDING is a intermediate state - do not send this back. - } - } + when (state) { + BluetoothDevice.BOND_BONDED -> completion.onSuccess(state) + BluetoothDevice.BOND_NONE -> completion.onSuccess(state) + // BOND_BONDING is a intermediate state - do not send this back. } } + } + } - completion.setDisposable(Disposables.fromAction { - context.unregisterReceiver( - receiver - ) - }) + completion.setDisposable(Disposables.fromAction { + context.unregisterReceiver( + receiver + ) + }) - context.registerReceiver( - receiver, - IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED) - ) + context.registerReceiver( + receiver, + IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED) + ) - val createBondResult = rxBleDevice.bluetoothDevice.createBond() + val createBondResult = rxBleDevice.bluetoothDevice.createBond() - if (!createBondResult) { - completion.tryOnError(BondingFailedException()) - } - } + if (!createBondResult) { + completion.tryOnError(BondingFailedException()) } - } - } } From 65a9783a69724c87187ccedc71a30cd799e1795b Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sun, 7 Apr 2024 21:44:18 +0100 Subject: [PATCH 15/17] regenerate protobuf --- .../ios/Classes/BleData/bledata.pb.swift | 2 +- .../lib/src/generated/bledata.pb.dart | 960 ++++++++++-------- .../lib/src/generated/bledata.pbenum.dart | 8 +- .../lib/src/generated/bledata.pbjson.dart | 836 +++++++-------- .../lib/src/generated/bledata.pbserver.dart | 9 +- 5 files changed, 938 insertions(+), 877 deletions(-) diff --git a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift index c288516b..d9dea177 100644 --- a/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift +++ b/packages/reactive_ble_mobile/ios/Classes/BleData/bledata.pb.swift @@ -166,7 +166,7 @@ struct EstablishBondingInfo { extension EstablishBondingInfo.BondState: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static var allCases: [EstablishBondingInfo.BondState] = [ + static let allCases: [EstablishBondingInfo.BondState] = [ .none, .bonding, .bonded, diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart index 9094419e..6ff58dfc 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pb.dart @@ -1,9 +1,13 @@ -/// +// // Generated code. Do not modify. // source: bledata.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import import 'dart:core' as $core; @@ -14,7 +18,23 @@ import 'bledata.pbenum.dart'; export 'bledata.pbenum.dart'; class LaunchCompanionRequest extends $pb.GeneratedMessage { - factory LaunchCompanionRequest() => create(); + factory LaunchCompanionRequest({ + $core.String? deviceNamePattern, + $core.bool? singleDeviceScan, + $core.bool? forceConfirmation, + }) { + final $result = create(); + if (deviceNamePattern != null) { + $result.deviceNamePattern = deviceNamePattern; + } + if (singleDeviceScan != null) { + $result.singleDeviceScan = singleDeviceScan; + } + if (forceConfirmation != null) { + $result.forceConfirmation = forceConfirmation; + } + return $result; + } LaunchCompanionRequest._() : super(); factory LaunchCompanionRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory LaunchCompanionRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -76,7 +96,15 @@ class LaunchCompanionRequest extends $pb.GeneratedMessage { } class DeviceAssociationInfo extends $pb.GeneratedMessage { - factory DeviceAssociationInfo() => create(); + factory DeviceAssociationInfo({ + $core.String? macAddress, + }) { + final $result = create(); + if (macAddress != null) { + $result.macAddress = macAddress; + } + return $result; + } DeviceAssociationInfo._() : super(); factory DeviceAssociationInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DeviceAssociationInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -118,33 +146,34 @@ class DeviceAssociationInfo extends $pb.GeneratedMessage { } class ScanForDevicesRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ScanForDevicesRequest', createEmptyInstance: create) - ..pc(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceUuids', $pb.PbFieldType.PM, protoName: 'serviceUuids', subBuilder: Uuid.create) - ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'scanMode', $pb.PbFieldType.O3, protoName: 'scanMode') - ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'requireLocationServicesEnabled', protoName: 'requireLocationServicesEnabled') - ..hasRequiredFields = false - ; - - ScanForDevicesRequest._() : super(); factory ScanForDevicesRequest({ $core.Iterable? serviceUuids, $core.int? scanMode, $core.bool? requireLocationServicesEnabled, }) { - final _result = create(); + final $result = create(); if (serviceUuids != null) { - _result.serviceUuids.addAll(serviceUuids); + $result.serviceUuids.addAll(serviceUuids); } if (scanMode != null) { - _result.scanMode = scanMode; + $result.scanMode = scanMode; } if (requireLocationServicesEnabled != null) { - _result.requireLocationServicesEnabled = requireLocationServicesEnabled; + $result.requireLocationServicesEnabled = requireLocationServicesEnabled; } - return _result; + return $result; } + ScanForDevicesRequest._() : super(); factory ScanForDevicesRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ScanForDevicesRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ScanForDevicesRequest', createEmptyInstance: create) + ..pc(1, _omitFieldNames ? '' : 'serviceUuids', $pb.PbFieldType.PM, protoName: 'serviceUuids', subBuilder: Uuid.create) + ..a<$core.int>(2, _omitFieldNames ? '' : 'scanMode', $pb.PbFieldType.O3, protoName: 'scanMode') + ..aOB(3, _omitFieldNames ? '' : 'requireLocationServicesEnabled', protoName: 'requireLocationServicesEnabled') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -154,8 +183,10 @@ class ScanForDevicesRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ScanForDevicesRequest copyWith(void Function(ScanForDevicesRequest) updates) => super.copyWith((message) => updates(message as ScanForDevicesRequest)) as ScanForDevicesRequest; // ignore: deprecated_member_use + ScanForDevicesRequest copyWith(void Function(ScanForDevicesRequest) updates) => super.copyWith((message) => updates(message as ScanForDevicesRequest)) as ScanForDevicesRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ScanForDevicesRequest create() => ScanForDevicesRequest._(); ScanForDevicesRequest createEmptyInstance() => create(); @@ -187,19 +218,6 @@ class ScanForDevicesRequest extends $pb.GeneratedMessage { } class DeviceScanInfo extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DeviceScanInfo', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') - ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name') - ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'failure', subBuilder: GenericFailure.create) - ..pc(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceData', $pb.PbFieldType.PM, protoName: 'serviceData', subBuilder: ServiceDataEntry.create) - ..a<$core.int>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rssi', $pb.PbFieldType.O3) - ..a<$core.List<$core.int>>(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'manufacturerData', $pb.PbFieldType.OY, protoName: 'manufacturerData') - ..pc(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceUuids', $pb.PbFieldType.PM, protoName: 'serviceUuids', subBuilder: Uuid.create) - ..aOM(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isConnectable', protoName: 'isConnectable', subBuilder: IsConnectable.create) - ..hasRequiredFields = false - ; - - DeviceScanInfo._() : super(); factory DeviceScanInfo({ $core.String? id, $core.String? name, @@ -210,35 +228,49 @@ class DeviceScanInfo extends $pb.GeneratedMessage { $core.Iterable? serviceUuids, IsConnectable? isConnectable, }) { - final _result = create(); + final $result = create(); if (id != null) { - _result.id = id; + $result.id = id; } if (name != null) { - _result.name = name; + $result.name = name; } if (failure != null) { - _result.failure = failure; + $result.failure = failure; } if (serviceData != null) { - _result.serviceData.addAll(serviceData); + $result.serviceData.addAll(serviceData); } if (rssi != null) { - _result.rssi = rssi; + $result.rssi = rssi; } if (manufacturerData != null) { - _result.manufacturerData = manufacturerData; + $result.manufacturerData = manufacturerData; } if (serviceUuids != null) { - _result.serviceUuids.addAll(serviceUuids); + $result.serviceUuids.addAll(serviceUuids); } if (isConnectable != null) { - _result.isConnectable = isConnectable; + $result.isConnectable = isConnectable; } - return _result; + return $result; } + DeviceScanInfo._() : super(); factory DeviceScanInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DeviceScanInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DeviceScanInfo', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'id') + ..aOS(2, _omitFieldNames ? '' : 'name') + ..aOM(3, _omitFieldNames ? '' : 'failure', subBuilder: GenericFailure.create) + ..pc(4, _omitFieldNames ? '' : 'serviceData', $pb.PbFieldType.PM, protoName: 'serviceData', subBuilder: ServiceDataEntry.create) + ..a<$core.int>(5, _omitFieldNames ? '' : 'rssi', $pb.PbFieldType.O3) + ..a<$core.List<$core.int>>(6, _omitFieldNames ? '' : 'manufacturerData', $pb.PbFieldType.OY, protoName: 'manufacturerData') + ..pc(7, _omitFieldNames ? '' : 'serviceUuids', $pb.PbFieldType.PM, protoName: 'serviceUuids', subBuilder: Uuid.create) + ..aOM(8, _omitFieldNames ? '' : 'isConnectable', protoName: 'isConnectable', subBuilder: IsConnectable.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -248,8 +280,10 @@ class DeviceScanInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - DeviceScanInfo copyWith(void Function(DeviceScanInfo) updates) => super.copyWith((message) => updates(message as DeviceScanInfo)) as DeviceScanInfo; // ignore: deprecated_member_use + DeviceScanInfo copyWith(void Function(DeviceScanInfo) updates) => super.copyWith((message) => updates(message as DeviceScanInfo)) as DeviceScanInfo; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static DeviceScanInfo create() => DeviceScanInfo._(); DeviceScanInfo createEmptyInstance() => create(); @@ -324,7 +358,15 @@ class DeviceScanInfo extends $pb.GeneratedMessage { } class EstablishBondingRequest extends $pb.GeneratedMessage { - factory EstablishBondingRequest() => create(); + factory EstablishBondingRequest({ + $core.String? deviceId, + }) { + final $result = create(); + if (deviceId != null) { + $result.deviceId = deviceId; + } + return $result; + } EstablishBondingRequest._() : super(); factory EstablishBondingRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory EstablishBondingRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -366,7 +408,15 @@ class EstablishBondingRequest extends $pb.GeneratedMessage { } class EstablishBondingInfo extends $pb.GeneratedMessage { - factory EstablishBondingInfo() => create(); + factory EstablishBondingInfo({ + EstablishBondingInfo_BondState? status, + }) { + final $result = create(); + if (status != null) { + $result.status = status; + } + return $result; + } EstablishBondingInfo._() : super(); factory EstablishBondingInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory EstablishBondingInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -408,33 +458,34 @@ class EstablishBondingInfo extends $pb.GeneratedMessage { } class ConnectToDeviceRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ConnectToDeviceRequest', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..aOM(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'servicesWithCharacteristicsToDiscover', protoName: 'servicesWithCharacteristicsToDiscover', subBuilder: ServicesWithCharacteristics.create) - ..a<$core.int>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'timeoutInMs', $pb.PbFieldType.O3, protoName: 'timeoutInMs') - ..hasRequiredFields = false - ; - - ConnectToDeviceRequest._() : super(); factory ConnectToDeviceRequest({ $core.String? deviceId, ServicesWithCharacteristics? servicesWithCharacteristicsToDiscover, $core.int? timeoutInMs, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } if (servicesWithCharacteristicsToDiscover != null) { - _result.servicesWithCharacteristicsToDiscover = servicesWithCharacteristicsToDiscover; + $result.servicesWithCharacteristicsToDiscover = servicesWithCharacteristicsToDiscover; } if (timeoutInMs != null) { - _result.timeoutInMs = timeoutInMs; + $result.timeoutInMs = timeoutInMs; } - return _result; + return $result; } + ConnectToDeviceRequest._() : super(); factory ConnectToDeviceRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ConnectToDeviceRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ConnectToDeviceRequest', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..aOM(2, _omitFieldNames ? '' : 'servicesWithCharacteristicsToDiscover', protoName: 'servicesWithCharacteristicsToDiscover', subBuilder: ServicesWithCharacteristics.create) + ..a<$core.int>(3, _omitFieldNames ? '' : 'timeoutInMs', $pb.PbFieldType.O3, protoName: 'timeoutInMs') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -444,8 +495,10 @@ class ConnectToDeviceRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ConnectToDeviceRequest copyWith(void Function(ConnectToDeviceRequest) updates) => super.copyWith((message) => updates(message as ConnectToDeviceRequest)) as ConnectToDeviceRequest; // ignore: deprecated_member_use + ConnectToDeviceRequest copyWith(void Function(ConnectToDeviceRequest) updates) => super.copyWith((message) => updates(message as ConnectToDeviceRequest)) as ConnectToDeviceRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ConnectToDeviceRequest create() => ConnectToDeviceRequest._(); ConnectToDeviceRequest createEmptyInstance() => create(); @@ -485,33 +538,34 @@ class ConnectToDeviceRequest extends $pb.GeneratedMessage { } class DeviceInfo extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DeviceInfo', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') - ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'connectionState', $pb.PbFieldType.O3, protoName: 'connectionState') - ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'failure', subBuilder: GenericFailure.create) - ..hasRequiredFields = false - ; - - DeviceInfo._() : super(); factory DeviceInfo({ $core.String? id, $core.int? connectionState, GenericFailure? failure, }) { - final _result = create(); + final $result = create(); if (id != null) { - _result.id = id; + $result.id = id; } if (connectionState != null) { - _result.connectionState = connectionState; + $result.connectionState = connectionState; } if (failure != null) { - _result.failure = failure; + $result.failure = failure; } - return _result; + return $result; } + DeviceInfo._() : super(); factory DeviceInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DeviceInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DeviceInfo', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'id') + ..a<$core.int>(2, _omitFieldNames ? '' : 'connectionState', $pb.PbFieldType.O3, protoName: 'connectionState') + ..aOM(3, _omitFieldNames ? '' : 'failure', subBuilder: GenericFailure.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -521,8 +575,10 @@ class DeviceInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - DeviceInfo copyWith(void Function(DeviceInfo) updates) => super.copyWith((message) => updates(message as DeviceInfo)) as DeviceInfo; // ignore: deprecated_member_use + DeviceInfo copyWith(void Function(DeviceInfo) updates) => super.copyWith((message) => updates(message as DeviceInfo)) as DeviceInfo; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static DeviceInfo create() => DeviceInfo._(); DeviceInfo createEmptyInstance() => create(); @@ -562,7 +618,15 @@ class DeviceInfo extends $pb.GeneratedMessage { } class GetDeviceNameRequest extends $pb.GeneratedMessage { - factory GetDeviceNameRequest() => create(); + factory GetDeviceNameRequest({ + $core.String? deviceId, + }) { + final $result = create(); + if (deviceId != null) { + $result.deviceId = deviceId; + } + return $result; + } GetDeviceNameRequest._() : super(); factory GetDeviceNameRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory GetDeviceNameRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -604,7 +668,19 @@ class GetDeviceNameRequest extends $pb.GeneratedMessage { } class DeviceNameInfo extends $pb.GeneratedMessage { - factory DeviceNameInfo() => create(); + factory DeviceNameInfo({ + $core.String? id, + $core.String? deviceName, + }) { + final $result = create(); + if (id != null) { + $result.id = id; + } + if (deviceName != null) { + $result.deviceName = deviceName; + } + return $result; + } DeviceNameInfo._() : super(); factory DeviceNameInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DeviceNameInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -656,23 +732,24 @@ class DeviceNameInfo extends $pb.GeneratedMessage { } class DisconnectFromDeviceRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DisconnectFromDeviceRequest', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..hasRequiredFields = false - ; - - DisconnectFromDeviceRequest._() : super(); factory DisconnectFromDeviceRequest({ $core.String? deviceId, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } - return _result; + return $result; } + DisconnectFromDeviceRequest._() : super(); factory DisconnectFromDeviceRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DisconnectFromDeviceRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DisconnectFromDeviceRequest', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -682,8 +759,10 @@ class DisconnectFromDeviceRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - DisconnectFromDeviceRequest copyWith(void Function(DisconnectFromDeviceRequest) updates) => super.copyWith((message) => updates(message as DisconnectFromDeviceRequest)) as DisconnectFromDeviceRequest; // ignore: deprecated_member_use + DisconnectFromDeviceRequest copyWith(void Function(DisconnectFromDeviceRequest) updates) => super.copyWith((message) => updates(message as DisconnectFromDeviceRequest)) as DisconnectFromDeviceRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static DisconnectFromDeviceRequest create() => DisconnectFromDeviceRequest._(); DisconnectFromDeviceRequest createEmptyInstance() => create(); @@ -703,23 +782,24 @@ class DisconnectFromDeviceRequest extends $pb.GeneratedMessage { } class ClearGattCacheRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ClearGattCacheRequest', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..hasRequiredFields = false - ; - - ClearGattCacheRequest._() : super(); factory ClearGattCacheRequest({ $core.String? deviceId, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } - return _result; + return $result; } + ClearGattCacheRequest._() : super(); factory ClearGattCacheRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ClearGattCacheRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ClearGattCacheRequest', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -729,8 +809,10 @@ class ClearGattCacheRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ClearGattCacheRequest copyWith(void Function(ClearGattCacheRequest) updates) => super.copyWith((message) => updates(message as ClearGattCacheRequest)) as ClearGattCacheRequest; // ignore: deprecated_member_use + ClearGattCacheRequest copyWith(void Function(ClearGattCacheRequest) updates) => super.copyWith((message) => updates(message as ClearGattCacheRequest)) as ClearGattCacheRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ClearGattCacheRequest create() => ClearGattCacheRequest._(); ClearGattCacheRequest createEmptyInstance() => create(); @@ -750,23 +832,24 @@ class ClearGattCacheRequest extends $pb.GeneratedMessage { } class ClearGattCacheInfo extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ClearGattCacheInfo', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'failure', subBuilder: GenericFailure.create) - ..hasRequiredFields = false - ; - - ClearGattCacheInfo._() : super(); factory ClearGattCacheInfo({ GenericFailure? failure, }) { - final _result = create(); + final $result = create(); if (failure != null) { - _result.failure = failure; + $result.failure = failure; } - return _result; + return $result; } + ClearGattCacheInfo._() : super(); factory ClearGattCacheInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ClearGattCacheInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ClearGattCacheInfo', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'failure', subBuilder: GenericFailure.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -776,8 +859,10 @@ class ClearGattCacheInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ClearGattCacheInfo copyWith(void Function(ClearGattCacheInfo) updates) => super.copyWith((message) => updates(message as ClearGattCacheInfo)) as ClearGattCacheInfo; // ignore: deprecated_member_use + ClearGattCacheInfo copyWith(void Function(ClearGattCacheInfo) updates) => super.copyWith((message) => updates(message as ClearGattCacheInfo)) as ClearGattCacheInfo; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ClearGattCacheInfo create() => ClearGattCacheInfo._(); ClearGattCacheInfo createEmptyInstance() => create(); @@ -799,23 +884,24 @@ class ClearGattCacheInfo extends $pb.GeneratedMessage { } class NotifyCharacteristicRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'NotifyCharacteristicRequest', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) - ..hasRequiredFields = false - ; - - NotifyCharacteristicRequest._() : super(); factory NotifyCharacteristicRequest({ CharacteristicAddress? characteristic, }) { - final _result = create(); + final $result = create(); if (characteristic != null) { - _result.characteristic = characteristic; + $result.characteristic = characteristic; } - return _result; + return $result; } + NotifyCharacteristicRequest._() : super(); factory NotifyCharacteristicRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory NotifyCharacteristicRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'NotifyCharacteristicRequest', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -825,8 +911,10 @@ class NotifyCharacteristicRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - NotifyCharacteristicRequest copyWith(void Function(NotifyCharacteristicRequest) updates) => super.copyWith((message) => updates(message as NotifyCharacteristicRequest)) as NotifyCharacteristicRequest; // ignore: deprecated_member_use + NotifyCharacteristicRequest copyWith(void Function(NotifyCharacteristicRequest) updates) => super.copyWith((message) => updates(message as NotifyCharacteristicRequest)) as NotifyCharacteristicRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static NotifyCharacteristicRequest create() => NotifyCharacteristicRequest._(); NotifyCharacteristicRequest createEmptyInstance() => create(); @@ -848,23 +936,24 @@ class NotifyCharacteristicRequest extends $pb.GeneratedMessage { } class NotifyNoMoreCharacteristicRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'NotifyNoMoreCharacteristicRequest', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) - ..hasRequiredFields = false - ; - - NotifyNoMoreCharacteristicRequest._() : super(); factory NotifyNoMoreCharacteristicRequest({ CharacteristicAddress? characteristic, }) { - final _result = create(); + final $result = create(); if (characteristic != null) { - _result.characteristic = characteristic; + $result.characteristic = characteristic; } - return _result; + return $result; } + NotifyNoMoreCharacteristicRequest._() : super(); factory NotifyNoMoreCharacteristicRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory NotifyNoMoreCharacteristicRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'NotifyNoMoreCharacteristicRequest', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -874,8 +963,10 @@ class NotifyNoMoreCharacteristicRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - NotifyNoMoreCharacteristicRequest copyWith(void Function(NotifyNoMoreCharacteristicRequest) updates) => super.copyWith((message) => updates(message as NotifyNoMoreCharacteristicRequest)) as NotifyNoMoreCharacteristicRequest; // ignore: deprecated_member_use + NotifyNoMoreCharacteristicRequest copyWith(void Function(NotifyNoMoreCharacteristicRequest) updates) => super.copyWith((message) => updates(message as NotifyNoMoreCharacteristicRequest)) as NotifyNoMoreCharacteristicRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static NotifyNoMoreCharacteristicRequest create() => NotifyNoMoreCharacteristicRequest._(); NotifyNoMoreCharacteristicRequest createEmptyInstance() => create(); @@ -897,23 +988,24 @@ class NotifyNoMoreCharacteristicRequest extends $pb.GeneratedMessage { } class ReadCharacteristicRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ReadCharacteristicRequest', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) - ..hasRequiredFields = false - ; - - ReadCharacteristicRequest._() : super(); factory ReadCharacteristicRequest({ CharacteristicAddress? characteristic, }) { - final _result = create(); + final $result = create(); if (characteristic != null) { - _result.characteristic = characteristic; + $result.characteristic = characteristic; } - return _result; + return $result; } + ReadCharacteristicRequest._() : super(); factory ReadCharacteristicRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ReadCharacteristicRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ReadCharacteristicRequest', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -923,8 +1015,10 @@ class ReadCharacteristicRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ReadCharacteristicRequest copyWith(void Function(ReadCharacteristicRequest) updates) => super.copyWith((message) => updates(message as ReadCharacteristicRequest)) as ReadCharacteristicRequest; // ignore: deprecated_member_use + ReadCharacteristicRequest copyWith(void Function(ReadCharacteristicRequest) updates) => super.copyWith((message) => updates(message as ReadCharacteristicRequest)) as ReadCharacteristicRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ReadCharacteristicRequest create() => ReadCharacteristicRequest._(); ReadCharacteristicRequest createEmptyInstance() => create(); @@ -946,33 +1040,34 @@ class ReadCharacteristicRequest extends $pb.GeneratedMessage { } class CharacteristicValueInfo extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CharacteristicValueInfo', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) - ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'value', $pb.PbFieldType.OY) - ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'failure', subBuilder: GenericFailure.create) - ..hasRequiredFields = false - ; - - CharacteristicValueInfo._() : super(); factory CharacteristicValueInfo({ CharacteristicAddress? characteristic, $core.List<$core.int>? value, GenericFailure? failure, }) { - final _result = create(); + final $result = create(); if (characteristic != null) { - _result.characteristic = characteristic; + $result.characteristic = characteristic; } if (value != null) { - _result.value = value; + $result.value = value; } if (failure != null) { - _result.failure = failure; + $result.failure = failure; } - return _result; + return $result; } + CharacteristicValueInfo._() : super(); factory CharacteristicValueInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory CharacteristicValueInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CharacteristicValueInfo', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) + ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OY) + ..aOM(3, _omitFieldNames ? '' : 'failure', subBuilder: GenericFailure.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -982,8 +1077,10 @@ class CharacteristicValueInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - CharacteristicValueInfo copyWith(void Function(CharacteristicValueInfo) updates) => super.copyWith((message) => updates(message as CharacteristicValueInfo)) as CharacteristicValueInfo; // ignore: deprecated_member_use + CharacteristicValueInfo copyWith(void Function(CharacteristicValueInfo) updates) => super.copyWith((message) => updates(message as CharacteristicValueInfo)) as CharacteristicValueInfo; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static CharacteristicValueInfo create() => CharacteristicValueInfo._(); CharacteristicValueInfo createEmptyInstance() => create(); @@ -1025,28 +1122,29 @@ class CharacteristicValueInfo extends $pb.GeneratedMessage { } class WriteCharacteristicRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WriteCharacteristicRequest', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) - ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'value', $pb.PbFieldType.OY) - ..hasRequiredFields = false - ; - - WriteCharacteristicRequest._() : super(); factory WriteCharacteristicRequest({ CharacteristicAddress? characteristic, $core.List<$core.int>? value, }) { - final _result = create(); + final $result = create(); if (characteristic != null) { - _result.characteristic = characteristic; + $result.characteristic = characteristic; } if (value != null) { - _result.value = value; + $result.value = value; } - return _result; + return $result; } + WriteCharacteristicRequest._() : super(); factory WriteCharacteristicRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory WriteCharacteristicRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'WriteCharacteristicRequest', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) + ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1056,8 +1154,10 @@ class WriteCharacteristicRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - WriteCharacteristicRequest copyWith(void Function(WriteCharacteristicRequest) updates) => super.copyWith((message) => updates(message as WriteCharacteristicRequest)) as WriteCharacteristicRequest; // ignore: deprecated_member_use + WriteCharacteristicRequest copyWith(void Function(WriteCharacteristicRequest) updates) => super.copyWith((message) => updates(message as WriteCharacteristicRequest)) as WriteCharacteristicRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static WriteCharacteristicRequest create() => WriteCharacteristicRequest._(); WriteCharacteristicRequest createEmptyInstance() => create(); @@ -1088,28 +1188,29 @@ class WriteCharacteristicRequest extends $pb.GeneratedMessage { } class WriteCharacteristicInfo extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WriteCharacteristicInfo', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) - ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'failure', subBuilder: GenericFailure.create) - ..hasRequiredFields = false - ; - - WriteCharacteristicInfo._() : super(); factory WriteCharacteristicInfo({ CharacteristicAddress? characteristic, GenericFailure? failure, }) { - final _result = create(); + final $result = create(); if (characteristic != null) { - _result.characteristic = characteristic; + $result.characteristic = characteristic; } if (failure != null) { - _result.failure = failure; + $result.failure = failure; } - return _result; + return $result; } + WriteCharacteristicInfo._() : super(); factory WriteCharacteristicInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory WriteCharacteristicInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'WriteCharacteristicInfo', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'characteristic', subBuilder: CharacteristicAddress.create) + ..aOM(3, _omitFieldNames ? '' : 'failure', subBuilder: GenericFailure.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1119,8 +1220,10 @@ class WriteCharacteristicInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - WriteCharacteristicInfo copyWith(void Function(WriteCharacteristicInfo) updates) => super.copyWith((message) => updates(message as WriteCharacteristicInfo)) as WriteCharacteristicInfo; // ignore: deprecated_member_use + WriteCharacteristicInfo copyWith(void Function(WriteCharacteristicInfo) updates) => super.copyWith((message) => updates(message as WriteCharacteristicInfo)) as WriteCharacteristicInfo; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static WriteCharacteristicInfo create() => WriteCharacteristicInfo._(); WriteCharacteristicInfo createEmptyInstance() => create(); @@ -1153,28 +1256,29 @@ class WriteCharacteristicInfo extends $pb.GeneratedMessage { } class NegotiateMtuRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'NegotiateMtuRequest', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'mtuSize', $pb.PbFieldType.O3, protoName: 'mtuSize') - ..hasRequiredFields = false - ; - - NegotiateMtuRequest._() : super(); factory NegotiateMtuRequest({ $core.String? deviceId, $core.int? mtuSize, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } if (mtuSize != null) { - _result.mtuSize = mtuSize; + $result.mtuSize = mtuSize; } - return _result; + return $result; } + NegotiateMtuRequest._() : super(); factory NegotiateMtuRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory NegotiateMtuRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'NegotiateMtuRequest', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..a<$core.int>(2, _omitFieldNames ? '' : 'mtuSize', $pb.PbFieldType.O3, protoName: 'mtuSize') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1184,8 +1288,10 @@ class NegotiateMtuRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - NegotiateMtuRequest copyWith(void Function(NegotiateMtuRequest) updates) => super.copyWith((message) => updates(message as NegotiateMtuRequest)) as NegotiateMtuRequest; // ignore: deprecated_member_use + NegotiateMtuRequest copyWith(void Function(NegotiateMtuRequest) updates) => super.copyWith((message) => updates(message as NegotiateMtuRequest)) as NegotiateMtuRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static NegotiateMtuRequest create() => NegotiateMtuRequest._(); NegotiateMtuRequest createEmptyInstance() => create(); @@ -1214,33 +1320,34 @@ class NegotiateMtuRequest extends $pb.GeneratedMessage { } class NegotiateMtuInfo extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'NegotiateMtuInfo', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'mtuSize', $pb.PbFieldType.O3, protoName: 'mtuSize') - ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'failure', subBuilder: GenericFailure.create) - ..hasRequiredFields = false - ; - - NegotiateMtuInfo._() : super(); factory NegotiateMtuInfo({ $core.String? deviceId, $core.int? mtuSize, GenericFailure? failure, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } if (mtuSize != null) { - _result.mtuSize = mtuSize; + $result.mtuSize = mtuSize; } if (failure != null) { - _result.failure = failure; + $result.failure = failure; } - return _result; + return $result; } + NegotiateMtuInfo._() : super(); factory NegotiateMtuInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory NegotiateMtuInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'NegotiateMtuInfo', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..a<$core.int>(2, _omitFieldNames ? '' : 'mtuSize', $pb.PbFieldType.O3, protoName: 'mtuSize') + ..aOM(3, _omitFieldNames ? '' : 'failure', subBuilder: GenericFailure.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1250,8 +1357,10 @@ class NegotiateMtuInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - NegotiateMtuInfo copyWith(void Function(NegotiateMtuInfo) updates) => super.copyWith((message) => updates(message as NegotiateMtuInfo)) as NegotiateMtuInfo; // ignore: deprecated_member_use + NegotiateMtuInfo copyWith(void Function(NegotiateMtuInfo) updates) => super.copyWith((message) => updates(message as NegotiateMtuInfo)) as NegotiateMtuInfo; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static NegotiateMtuInfo create() => NegotiateMtuInfo._(); NegotiateMtuInfo createEmptyInstance() => create(); @@ -1291,23 +1400,24 @@ class NegotiateMtuInfo extends $pb.GeneratedMessage { } class BleStatusInfo extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BleStatusInfo', createEmptyInstance: create) - ..a<$core.int>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'status', $pb.PbFieldType.O3) - ..hasRequiredFields = false - ; - - BleStatusInfo._() : super(); factory BleStatusInfo({ $core.int? status, }) { - final _result = create(); + final $result = create(); if (status != null) { - _result.status = status; + $result.status = status; } - return _result; + return $result; } + BleStatusInfo._() : super(); factory BleStatusInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory BleStatusInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BleStatusInfo', createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'status', $pb.PbFieldType.O3) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1317,8 +1427,10 @@ class BleStatusInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - BleStatusInfo copyWith(void Function(BleStatusInfo) updates) => super.copyWith((message) => updates(message as BleStatusInfo)) as BleStatusInfo; // ignore: deprecated_member_use + BleStatusInfo copyWith(void Function(BleStatusInfo) updates) => super.copyWith((message) => updates(message as BleStatusInfo)) as BleStatusInfo; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static BleStatusInfo create() => BleStatusInfo._(); BleStatusInfo createEmptyInstance() => create(); @@ -1338,28 +1450,29 @@ class BleStatusInfo extends $pb.GeneratedMessage { } class ChangeConnectionPriorityRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ChangeConnectionPriorityRequest', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'priority', $pb.PbFieldType.O3) - ..hasRequiredFields = false - ; - - ChangeConnectionPriorityRequest._() : super(); factory ChangeConnectionPriorityRequest({ $core.String? deviceId, $core.int? priority, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } if (priority != null) { - _result.priority = priority; + $result.priority = priority; } - return _result; + return $result; } + ChangeConnectionPriorityRequest._() : super(); factory ChangeConnectionPriorityRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ChangeConnectionPriorityRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ChangeConnectionPriorityRequest', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..a<$core.int>(2, _omitFieldNames ? '' : 'priority', $pb.PbFieldType.O3) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1369,8 +1482,10 @@ class ChangeConnectionPriorityRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ChangeConnectionPriorityRequest copyWith(void Function(ChangeConnectionPriorityRequest) updates) => super.copyWith((message) => updates(message as ChangeConnectionPriorityRequest)) as ChangeConnectionPriorityRequest; // ignore: deprecated_member_use + ChangeConnectionPriorityRequest copyWith(void Function(ChangeConnectionPriorityRequest) updates) => super.copyWith((message) => updates(message as ChangeConnectionPriorityRequest)) as ChangeConnectionPriorityRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ChangeConnectionPriorityRequest create() => ChangeConnectionPriorityRequest._(); ChangeConnectionPriorityRequest createEmptyInstance() => create(); @@ -1399,28 +1514,29 @@ class ChangeConnectionPriorityRequest extends $pb.GeneratedMessage { } class ChangeConnectionPriorityInfo extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ChangeConnectionPriorityInfo', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..aOM(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'failure', subBuilder: GenericFailure.create) - ..hasRequiredFields = false - ; - - ChangeConnectionPriorityInfo._() : super(); factory ChangeConnectionPriorityInfo({ $core.String? deviceId, GenericFailure? failure, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } if (failure != null) { - _result.failure = failure; + $result.failure = failure; } - return _result; + return $result; } + ChangeConnectionPriorityInfo._() : super(); factory ChangeConnectionPriorityInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ChangeConnectionPriorityInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ChangeConnectionPriorityInfo', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..aOM(2, _omitFieldNames ? '' : 'failure', subBuilder: GenericFailure.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1430,8 +1546,10 @@ class ChangeConnectionPriorityInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ChangeConnectionPriorityInfo copyWith(void Function(ChangeConnectionPriorityInfo) updates) => super.copyWith((message) => updates(message as ChangeConnectionPriorityInfo)) as ChangeConnectionPriorityInfo; // ignore: deprecated_member_use + ChangeConnectionPriorityInfo copyWith(void Function(ChangeConnectionPriorityInfo) updates) => super.copyWith((message) => updates(message as ChangeConnectionPriorityInfo)) as ChangeConnectionPriorityInfo; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ChangeConnectionPriorityInfo create() => ChangeConnectionPriorityInfo._(); ChangeConnectionPriorityInfo createEmptyInstance() => create(); @@ -1462,16 +1580,6 @@ class ChangeConnectionPriorityInfo extends $pb.GeneratedMessage { } class CharacteristicAddress extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CharacteristicAddress', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..aOM(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceUuid', protoName: 'serviceUuid', subBuilder: Uuid.create) - ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristicUuid', protoName: 'characteristicUuid', subBuilder: Uuid.create) - ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceInstanceId', protoName: 'serviceInstanceId') - ..aOS(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristicInstanceId', protoName: 'characteristicInstanceId') - ..hasRequiredFields = false - ; - - CharacteristicAddress._() : super(); factory CharacteristicAddress({ $core.String? deviceId, Uuid? serviceUuid, @@ -1479,26 +1587,37 @@ class CharacteristicAddress extends $pb.GeneratedMessage { $core.String? serviceInstanceId, $core.String? characteristicInstanceId, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } if (serviceUuid != null) { - _result.serviceUuid = serviceUuid; + $result.serviceUuid = serviceUuid; } if (characteristicUuid != null) { - _result.characteristicUuid = characteristicUuid; + $result.characteristicUuid = characteristicUuid; } if (serviceInstanceId != null) { - _result.serviceInstanceId = serviceInstanceId; + $result.serviceInstanceId = serviceInstanceId; } if (characteristicInstanceId != null) { - _result.characteristicInstanceId = characteristicInstanceId; + $result.characteristicInstanceId = characteristicInstanceId; } - return _result; + return $result; } + CharacteristicAddress._() : super(); factory CharacteristicAddress.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory CharacteristicAddress.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CharacteristicAddress', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..aOM(2, _omitFieldNames ? '' : 'serviceUuid', protoName: 'serviceUuid', subBuilder: Uuid.create) + ..aOM(3, _omitFieldNames ? '' : 'characteristicUuid', protoName: 'characteristicUuid', subBuilder: Uuid.create) + ..aOS(4, _omitFieldNames ? '' : 'serviceInstanceId', protoName: 'serviceInstanceId') + ..aOS(5, _omitFieldNames ? '' : 'characteristicInstanceId', protoName: 'characteristicInstanceId') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1508,8 +1627,10 @@ class CharacteristicAddress extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - CharacteristicAddress copyWith(void Function(CharacteristicAddress) updates) => super.copyWith((message) => updates(message as CharacteristicAddress)) as CharacteristicAddress; // ignore: deprecated_member_use + CharacteristicAddress copyWith(void Function(CharacteristicAddress) updates) => super.copyWith((message) => updates(message as CharacteristicAddress)) as CharacteristicAddress; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static CharacteristicAddress create() => CharacteristicAddress._(); CharacteristicAddress createEmptyInstance() => create(); @@ -1569,28 +1690,29 @@ class CharacteristicAddress extends $pb.GeneratedMessage { } class ServiceDataEntry extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ServiceDataEntry', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceUuid', protoName: 'serviceUuid', subBuilder: Uuid.create) - ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY) - ..hasRequiredFields = false - ; - - ServiceDataEntry._() : super(); factory ServiceDataEntry({ Uuid? serviceUuid, $core.List<$core.int>? data, }) { - final _result = create(); + final $result = create(); if (serviceUuid != null) { - _result.serviceUuid = serviceUuid; + $result.serviceUuid = serviceUuid; } if (data != null) { - _result.data = data; + $result.data = data; } - return _result; + return $result; } + ServiceDataEntry._() : super(); factory ServiceDataEntry.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ServiceDataEntry.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ServiceDataEntry', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'serviceUuid', protoName: 'serviceUuid', subBuilder: Uuid.create) + ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'data', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1600,8 +1722,10 @@ class ServiceDataEntry extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ServiceDataEntry copyWith(void Function(ServiceDataEntry) updates) => super.copyWith((message) => updates(message as ServiceDataEntry)) as ServiceDataEntry; // ignore: deprecated_member_use + ServiceDataEntry copyWith(void Function(ServiceDataEntry) updates) => super.copyWith((message) => updates(message as ServiceDataEntry)) as ServiceDataEntry; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ServiceDataEntry create() => ServiceDataEntry._(); ServiceDataEntry createEmptyInstance() => create(); @@ -1632,23 +1756,24 @@ class ServiceDataEntry extends $pb.GeneratedMessage { } class ServicesWithCharacteristics extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ServicesWithCharacteristics', createEmptyInstance: create) - ..pc(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: ServiceWithCharacteristics.create) - ..hasRequiredFields = false - ; - - ServicesWithCharacteristics._() : super(); factory ServicesWithCharacteristics({ $core.Iterable? items, }) { - final _result = create(); + final $result = create(); if (items != null) { - _result.items.addAll(items); + $result.items.addAll(items); } - return _result; + return $result; } + ServicesWithCharacteristics._() : super(); factory ServicesWithCharacteristics.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ServicesWithCharacteristics.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ServicesWithCharacteristics', createEmptyInstance: create) + ..pc(1, _omitFieldNames ? '' : 'items', $pb.PbFieldType.PM, subBuilder: ServiceWithCharacteristics.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1658,8 +1783,10 @@ class ServicesWithCharacteristics extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ServicesWithCharacteristics copyWith(void Function(ServicesWithCharacteristics) updates) => super.copyWith((message) => updates(message as ServicesWithCharacteristics)) as ServicesWithCharacteristics; // ignore: deprecated_member_use + ServicesWithCharacteristics copyWith(void Function(ServicesWithCharacteristics) updates) => super.copyWith((message) => updates(message as ServicesWithCharacteristics)) as ServicesWithCharacteristics; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ServicesWithCharacteristics create() => ServicesWithCharacteristics._(); ServicesWithCharacteristics createEmptyInstance() => create(); @@ -1673,28 +1800,29 @@ class ServicesWithCharacteristics extends $pb.GeneratedMessage { } class ServiceWithCharacteristics extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ServiceWithCharacteristics', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceId', protoName: 'serviceId', subBuilder: Uuid.create) - ..pc(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristics', $pb.PbFieldType.PM, subBuilder: Uuid.create) - ..hasRequiredFields = false - ; - - ServiceWithCharacteristics._() : super(); factory ServiceWithCharacteristics({ Uuid? serviceId, $core.Iterable? characteristics, }) { - final _result = create(); + final $result = create(); if (serviceId != null) { - _result.serviceId = serviceId; + $result.serviceId = serviceId; } if (characteristics != null) { - _result.characteristics.addAll(characteristics); + $result.characteristics.addAll(characteristics); } - return _result; + return $result; } + ServiceWithCharacteristics._() : super(); factory ServiceWithCharacteristics.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ServiceWithCharacteristics.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ServiceWithCharacteristics', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'serviceId', protoName: 'serviceId', subBuilder: Uuid.create) + ..pc(2, _omitFieldNames ? '' : 'characteristics', $pb.PbFieldType.PM, subBuilder: Uuid.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1704,8 +1832,10 @@ class ServiceWithCharacteristics extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ServiceWithCharacteristics copyWith(void Function(ServiceWithCharacteristics) updates) => super.copyWith((message) => updates(message as ServiceWithCharacteristics)) as ServiceWithCharacteristics; // ignore: deprecated_member_use + ServiceWithCharacteristics copyWith(void Function(ServiceWithCharacteristics) updates) => super.copyWith((message) => updates(message as ServiceWithCharacteristics)) as ServiceWithCharacteristics; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ServiceWithCharacteristics create() => ServiceWithCharacteristics._(); ServiceWithCharacteristics createEmptyInstance() => create(); @@ -1730,23 +1860,24 @@ class ServiceWithCharacteristics extends $pb.GeneratedMessage { } class DiscoverServicesRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DiscoverServicesRequest', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..hasRequiredFields = false - ; - - DiscoverServicesRequest._() : super(); factory DiscoverServicesRequest({ $core.String? deviceId, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } - return _result; + return $result; } + DiscoverServicesRequest._() : super(); factory DiscoverServicesRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DiscoverServicesRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DiscoverServicesRequest', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1756,8 +1887,10 @@ class DiscoverServicesRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - DiscoverServicesRequest copyWith(void Function(DiscoverServicesRequest) updates) => super.copyWith((message) => updates(message as DiscoverServicesRequest)) as DiscoverServicesRequest; // ignore: deprecated_member_use + DiscoverServicesRequest copyWith(void Function(DiscoverServicesRequest) updates) => super.copyWith((message) => updates(message as DiscoverServicesRequest)) as DiscoverServicesRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static DiscoverServicesRequest create() => DiscoverServicesRequest._(); DiscoverServicesRequest createEmptyInstance() => create(); @@ -1777,28 +1910,29 @@ class DiscoverServicesRequest extends $pb.GeneratedMessage { } class DiscoverServicesInfo extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DiscoverServicesInfo', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..pc(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'services', $pb.PbFieldType.PM, subBuilder: DiscoveredService.create) - ..hasRequiredFields = false - ; - - DiscoverServicesInfo._() : super(); factory DiscoverServicesInfo({ $core.String? deviceId, $core.Iterable? services, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } if (services != null) { - _result.services.addAll(services); + $result.services.addAll(services); } - return _result; + return $result; } + DiscoverServicesInfo._() : super(); factory DiscoverServicesInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DiscoverServicesInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DiscoverServicesInfo', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..pc(2, _omitFieldNames ? '' : 'services', $pb.PbFieldType.PM, subBuilder: DiscoveredService.create) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1808,8 +1942,10 @@ class DiscoverServicesInfo extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - DiscoverServicesInfo copyWith(void Function(DiscoverServicesInfo) updates) => super.copyWith((message) => updates(message as DiscoverServicesInfo)) as DiscoverServicesInfo; // ignore: deprecated_member_use + DiscoverServicesInfo copyWith(void Function(DiscoverServicesInfo) updates) => super.copyWith((message) => updates(message as DiscoverServicesInfo)) as DiscoverServicesInfo; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static DiscoverServicesInfo create() => DiscoverServicesInfo._(); DiscoverServicesInfo createEmptyInstance() => create(); @@ -1832,16 +1968,6 @@ class DiscoverServicesInfo extends $pb.GeneratedMessage { } class DiscoveredService extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DiscoveredService', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceUuid', protoName: 'serviceUuid', subBuilder: Uuid.create) - ..pc(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristicUuids', $pb.PbFieldType.PM, protoName: 'characteristicUuids', subBuilder: Uuid.create) - ..pc(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'includedServices', $pb.PbFieldType.PM, protoName: 'includedServices', subBuilder: DiscoveredService.create) - ..pc(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristics', $pb.PbFieldType.PM, subBuilder: DiscoveredCharacteristic.create) - ..aOS(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceInstanceId', protoName: 'serviceInstanceId') - ..hasRequiredFields = false - ; - - DiscoveredService._() : super(); factory DiscoveredService({ Uuid? serviceUuid, $core.Iterable? characteristicUuids, @@ -1849,26 +1975,37 @@ class DiscoveredService extends $pb.GeneratedMessage { $core.Iterable? characteristics, $core.String? serviceInstanceId, }) { - final _result = create(); + final $result = create(); if (serviceUuid != null) { - _result.serviceUuid = serviceUuid; + $result.serviceUuid = serviceUuid; } if (characteristicUuids != null) { - _result.characteristicUuids.addAll(characteristicUuids); + $result.characteristicUuids.addAll(characteristicUuids); } if (includedServices != null) { - _result.includedServices.addAll(includedServices); + $result.includedServices.addAll(includedServices); } if (characteristics != null) { - _result.characteristics.addAll(characteristics); + $result.characteristics.addAll(characteristics); } if (serviceInstanceId != null) { - _result.serviceInstanceId = serviceInstanceId; + $result.serviceInstanceId = serviceInstanceId; } - return _result; + return $result; } + DiscoveredService._() : super(); factory DiscoveredService.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DiscoveredService.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DiscoveredService', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'serviceUuid', protoName: 'serviceUuid', subBuilder: Uuid.create) + ..pc(2, _omitFieldNames ? '' : 'characteristicUuids', $pb.PbFieldType.PM, protoName: 'characteristicUuids', subBuilder: Uuid.create) + ..pc(3, _omitFieldNames ? '' : 'includedServices', $pb.PbFieldType.PM, protoName: 'includedServices', subBuilder: DiscoveredService.create) + ..pc(4, _omitFieldNames ? '' : 'characteristics', $pb.PbFieldType.PM, subBuilder: DiscoveredCharacteristic.create) + ..aOS(5, _omitFieldNames ? '' : 'serviceInstanceId', protoName: 'serviceInstanceId') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1878,8 +2015,10 @@ class DiscoveredService extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - DiscoveredService copyWith(void Function(DiscoveredService) updates) => super.copyWith((message) => updates(message as DiscoveredService)) as DiscoveredService; // ignore: deprecated_member_use + DiscoveredService copyWith(void Function(DiscoveredService) updates) => super.copyWith((message) => updates(message as DiscoveredService)) as DiscoveredService; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static DiscoveredService create() => DiscoveredService._(); DiscoveredService createEmptyInstance() => create(); @@ -1919,19 +2058,6 @@ class DiscoveredService extends $pb.GeneratedMessage { } class DiscoveredCharacteristic extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DiscoveredCharacteristic', createEmptyInstance: create) - ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristicId', protoName: 'characteristicId', subBuilder: Uuid.create) - ..aOM(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceId', protoName: 'serviceId', subBuilder: Uuid.create) - ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isReadable', protoName: 'isReadable') - ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isWritableWithResponse', protoName: 'isWritableWithResponse') - ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isWritableWithoutResponse', protoName: 'isWritableWithoutResponse') - ..aOB(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isNotifiable', protoName: 'isNotifiable') - ..aOB(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isIndicatable', protoName: 'isIndicatable') - ..aOS(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'characteristicInstanceId', protoName: 'characteristicInstanceId') - ..hasRequiredFields = false - ; - - DiscoveredCharacteristic._() : super(); factory DiscoveredCharacteristic({ Uuid? characteristicId, Uuid? serviceId, @@ -1942,35 +2068,49 @@ class DiscoveredCharacteristic extends $pb.GeneratedMessage { $core.bool? isIndicatable, $core.String? characteristicInstanceId, }) { - final _result = create(); + final $result = create(); if (characteristicId != null) { - _result.characteristicId = characteristicId; + $result.characteristicId = characteristicId; } if (serviceId != null) { - _result.serviceId = serviceId; + $result.serviceId = serviceId; } if (isReadable != null) { - _result.isReadable = isReadable; + $result.isReadable = isReadable; } if (isWritableWithResponse != null) { - _result.isWritableWithResponse = isWritableWithResponse; + $result.isWritableWithResponse = isWritableWithResponse; } if (isWritableWithoutResponse != null) { - _result.isWritableWithoutResponse = isWritableWithoutResponse; + $result.isWritableWithoutResponse = isWritableWithoutResponse; } if (isNotifiable != null) { - _result.isNotifiable = isNotifiable; + $result.isNotifiable = isNotifiable; } if (isIndicatable != null) { - _result.isIndicatable = isIndicatable; + $result.isIndicatable = isIndicatable; } if (characteristicInstanceId != null) { - _result.characteristicInstanceId = characteristicInstanceId; + $result.characteristicInstanceId = characteristicInstanceId; } - return _result; + return $result; } + DiscoveredCharacteristic._() : super(); factory DiscoveredCharacteristic.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DiscoveredCharacteristic.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DiscoveredCharacteristic', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'characteristicId', protoName: 'characteristicId', subBuilder: Uuid.create) + ..aOM(2, _omitFieldNames ? '' : 'serviceId', protoName: 'serviceId', subBuilder: Uuid.create) + ..aOB(3, _omitFieldNames ? '' : 'isReadable', protoName: 'isReadable') + ..aOB(4, _omitFieldNames ? '' : 'isWritableWithResponse', protoName: 'isWritableWithResponse') + ..aOB(5, _omitFieldNames ? '' : 'isWritableWithoutResponse', protoName: 'isWritableWithoutResponse') + ..aOB(6, _omitFieldNames ? '' : 'isNotifiable', protoName: 'isNotifiable') + ..aOB(7, _omitFieldNames ? '' : 'isIndicatable', protoName: 'isIndicatable') + ..aOS(8, _omitFieldNames ? '' : 'characteristicInstanceId', protoName: 'characteristicInstanceId') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -1980,8 +2120,10 @@ class DiscoveredCharacteristic extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - DiscoveredCharacteristic copyWith(void Function(DiscoveredCharacteristic) updates) => super.copyWith((message) => updates(message as DiscoveredCharacteristic)) as DiscoveredCharacteristic; // ignore: deprecated_member_use + DiscoveredCharacteristic copyWith(void Function(DiscoveredCharacteristic) updates) => super.copyWith((message) => updates(message as DiscoveredCharacteristic)) as DiscoveredCharacteristic; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static DiscoveredCharacteristic create() => DiscoveredCharacteristic._(); DiscoveredCharacteristic createEmptyInstance() => create(); @@ -2068,23 +2210,24 @@ class DiscoveredCharacteristic extends $pb.GeneratedMessage { } class ReadRssiRequest extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ReadRssiRequest', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deviceId', protoName: 'deviceId') - ..hasRequiredFields = false - ; - - ReadRssiRequest._() : super(); factory ReadRssiRequest({ $core.String? deviceId, }) { - final _result = create(); + final $result = create(); if (deviceId != null) { - _result.deviceId = deviceId; + $result.deviceId = deviceId; } - return _result; + return $result; } + ReadRssiRequest._() : super(); factory ReadRssiRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ReadRssiRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ReadRssiRequest', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceId', protoName: 'deviceId') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -2094,8 +2237,10 @@ class ReadRssiRequest extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ReadRssiRequest copyWith(void Function(ReadRssiRequest) updates) => super.copyWith((message) => updates(message as ReadRssiRequest)) as ReadRssiRequest; // ignore: deprecated_member_use + ReadRssiRequest copyWith(void Function(ReadRssiRequest) updates) => super.copyWith((message) => updates(message as ReadRssiRequest)) as ReadRssiRequest; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ReadRssiRequest create() => ReadRssiRequest._(); ReadRssiRequest createEmptyInstance() => create(); @@ -2115,23 +2260,24 @@ class ReadRssiRequest extends $pb.GeneratedMessage { } class ReadRssiResult extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ReadRssiResult', createEmptyInstance: create) - ..a<$core.int>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rssi', $pb.PbFieldType.O3) - ..hasRequiredFields = false - ; - - ReadRssiResult._() : super(); factory ReadRssiResult({ $core.int? rssi, }) { - final _result = create(); + final $result = create(); if (rssi != null) { - _result.rssi = rssi; + $result.rssi = rssi; } - return _result; + return $result; } + ReadRssiResult._() : super(); factory ReadRssiResult.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ReadRssiResult.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ReadRssiResult', createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'rssi', $pb.PbFieldType.O3) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -2141,8 +2287,10 @@ class ReadRssiResult extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ReadRssiResult copyWith(void Function(ReadRssiResult) updates) => super.copyWith((message) => updates(message as ReadRssiResult)) as ReadRssiResult; // ignore: deprecated_member_use + ReadRssiResult copyWith(void Function(ReadRssiResult) updates) => super.copyWith((message) => updates(message as ReadRssiResult)) as ReadRssiResult; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static ReadRssiResult create() => ReadRssiResult._(); ReadRssiResult createEmptyInstance() => create(); @@ -2162,23 +2310,24 @@ class ReadRssiResult extends $pb.GeneratedMessage { } class Uuid extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Uuid', createEmptyInstance: create) - ..a<$core.List<$core.int>>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY) - ..hasRequiredFields = false - ; - - Uuid._() : super(); factory Uuid({ $core.List<$core.int>? data, }) { - final _result = create(); + final $result = create(); if (data != null) { - _result.data = data; + $result.data = data; } - return _result; + return $result; } + Uuid._() : super(); factory Uuid.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Uuid.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Uuid', createEmptyInstance: create) + ..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'data', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -2188,8 +2337,10 @@ class Uuid extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - Uuid copyWith(void Function(Uuid) updates) => super.copyWith((message) => updates(message as Uuid)) as Uuid; // ignore: deprecated_member_use + Uuid copyWith(void Function(Uuid) updates) => super.copyWith((message) => updates(message as Uuid)) as Uuid; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static Uuid create() => Uuid._(); Uuid createEmptyInstance() => create(); @@ -2209,28 +2360,29 @@ class Uuid extends $pb.GeneratedMessage { } class GenericFailure extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GenericFailure', createEmptyInstance: create) - ..a<$core.int>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'code', $pb.PbFieldType.O3) - ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') - ..hasRequiredFields = false - ; - - GenericFailure._() : super(); factory GenericFailure({ $core.int? code, $core.String? message, }) { - final _result = create(); + final $result = create(); if (code != null) { - _result.code = code; + $result.code = code; } if (message != null) { - _result.message = message; + $result.message = message; } - return _result; + return $result; } + GenericFailure._() : super(); factory GenericFailure.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory GenericFailure.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GenericFailure', createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'code', $pb.PbFieldType.O3) + ..aOS(2, _omitFieldNames ? '' : 'message') + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -2240,8 +2392,10 @@ class GenericFailure extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - GenericFailure copyWith(void Function(GenericFailure) updates) => super.copyWith((message) => updates(message as GenericFailure)) as GenericFailure; // ignore: deprecated_member_use + GenericFailure copyWith(void Function(GenericFailure) updates) => super.copyWith((message) => updates(message as GenericFailure)) as GenericFailure; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static GenericFailure create() => GenericFailure._(); GenericFailure createEmptyInstance() => create(); @@ -2270,23 +2424,24 @@ class GenericFailure extends $pb.GeneratedMessage { } class IsConnectable extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'IsConnectable', createEmptyInstance: create) - ..a<$core.int>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'code', $pb.PbFieldType.O3) - ..hasRequiredFields = false - ; - - IsConnectable._() : super(); factory IsConnectable({ $core.int? code, }) { - final _result = create(); + final $result = create(); if (code != null) { - _result.code = code; + $result.code = code; } - return _result; + return $result; } + IsConnectable._() : super(); factory IsConnectable.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory IsConnectable.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'IsConnectable', createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'code', $pb.PbFieldType.O3) + ..hasRequiredFields = false + ; + @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' @@ -2296,8 +2451,10 @@ class IsConnectable extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - IsConnectable copyWith(void Function(IsConnectable) updates) => super.copyWith((message) => updates(message as IsConnectable)) as IsConnectable; // ignore: deprecated_member_use + IsConnectable copyWith(void Function(IsConnectable) updates) => super.copyWith((message) => updates(message as IsConnectable)) as IsConnectable; + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') static IsConnectable create() => IsConnectable._(); IsConnectable createEmptyInstance() => create(); @@ -2316,3 +2473,6 @@ class IsConnectable extends $pb.GeneratedMessage { void clearCode() => clearField(1); } + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart index 02610f8b..8a2d2991 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbenum.dart @@ -1,9 +1,13 @@ -/// +// // Generated code. Do not modify. // source: bledata.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import import 'dart:core' as $core; diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart index 36fe24f6..ca459502 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbjson.dart @@ -1,679 +1,571 @@ -/// +// // Generated code. Do not modify. // source: bledata.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name -import 'dart:core' as $core; +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + import 'dart:convert' as $convert; +import 'dart:core' as $core; import 'dart:typed_data' as $typed_data; +@$core.Deprecated('Use launchCompanionRequestDescriptor instead') +const LaunchCompanionRequest$json = { + '1': 'LaunchCompanionRequest', + '2': [ + {'1': 'deviceNamePattern', '3': 1, '4': 1, '5': 9, '10': 'deviceNamePattern'}, + {'1': 'singleDeviceScan', '3': 2, '4': 1, '5': 8, '10': 'singleDeviceScan'}, + {'1': 'forceConfirmation', '3': 3, '4': 1, '5': 8, '10': 'forceConfirmation'}, + ], +}; + +/// Descriptor for `LaunchCompanionRequest`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List launchCompanionRequestDescriptor = $convert.base64Decode( + 'ChZMYXVuY2hDb21wYW5pb25SZXF1ZXN0EiwKEWRldmljZU5hbWVQYXR0ZXJuGAEgASgJUhFkZX' + 'ZpY2VOYW1lUGF0dGVybhIqChBzaW5nbGVEZXZpY2VTY2FuGAIgASgIUhBzaW5nbGVEZXZpY2VT' + 'Y2FuEiwKEWZvcmNlQ29uZmlybWF0aW9uGAMgASgIUhFmb3JjZUNvbmZpcm1hdGlvbg=='); + +@$core.Deprecated('Use deviceAssociationInfoDescriptor instead') +const DeviceAssociationInfo$json = { + '1': 'DeviceAssociationInfo', + '2': [ + {'1': 'macAddress', '3': 1, '4': 1, '5': 9, '10': 'macAddress'}, + ], +}; + +/// Descriptor for `DeviceAssociationInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List deviceAssociationInfoDescriptor = $convert.base64Decode( + 'ChVEZXZpY2VBc3NvY2lhdGlvbkluZm8SHgoKbWFjQWRkcmVzcxgBIAEoCVIKbWFjQWRkcmVzcw' + '=='); + @$core.Deprecated('Use scanForDevicesRequestDescriptor instead') -const ScanForDevicesRequest$json = const { +const ScanForDevicesRequest$json = { '1': 'ScanForDevicesRequest', - '2': const [ - const { - '1': 'serviceUuids', - '3': 1, - '4': 3, - '5': 11, - '6': '.Uuid', - '10': 'serviceUuids' - }, - const {'1': 'scanMode', '3': 2, '4': 1, '5': 5, '10': 'scanMode'}, - const { - '1': 'requireLocationServicesEnabled', - '3': 3, - '4': 1, - '5': 8, - '10': 'requireLocationServicesEnabled' - }, + '2': [ + {'1': 'serviceUuids', '3': 1, '4': 3, '5': 11, '6': '.Uuid', '10': 'serviceUuids'}, + {'1': 'scanMode', '3': 2, '4': 1, '5': 5, '10': 'scanMode'}, + {'1': 'requireLocationServicesEnabled', '3': 3, '4': 1, '5': 8, '10': 'requireLocationServicesEnabled'}, ], }; /// Descriptor for `ScanForDevicesRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List scanForDevicesRequestDescriptor = $convert.base64Decode( - 'ChVTY2FuRm9yRGV2aWNlc1JlcXVlc3QSKQoMc2VydmljZVV1aWRzGAEgAygLMgUuVXVpZFIMc2VydmljZVV1aWRzEhoKCHNjYW5Nb2RlGAIgASgFUghzY2FuTW9kZRJGCh5yZXF1aXJlTG9jYXRpb25TZXJ2aWNlc0VuYWJsZWQYAyABKAhSHnJlcXVpcmVMb2NhdGlvblNlcnZpY2VzRW5hYmxlZA=='); + 'ChVTY2FuRm9yRGV2aWNlc1JlcXVlc3QSKQoMc2VydmljZVV1aWRzGAEgAygLMgUuVXVpZFIMc2' + 'VydmljZVV1aWRzEhoKCHNjYW5Nb2RlGAIgASgFUghzY2FuTW9kZRJGCh5yZXF1aXJlTG9jYXRp' + 'b25TZXJ2aWNlc0VuYWJsZWQYAyABKAhSHnJlcXVpcmVMb2NhdGlvblNlcnZpY2VzRW5hYmxlZA' + '=='); + @$core.Deprecated('Use deviceScanInfoDescriptor instead') -const DeviceScanInfo$json = const { +const DeviceScanInfo$json = { '1': 'DeviceScanInfo', - '2': const [ - const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, - const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'}, - const { - '1': 'failure', - '3': 3, - '4': 1, - '5': 11, - '6': '.GenericFailure', - '10': 'failure' - }, - const { - '1': 'serviceData', - '3': 4, - '4': 3, - '5': 11, - '6': '.ServiceDataEntry', - '10': 'serviceData' - }, - const { - '1': 'manufacturerData', - '3': 6, - '4': 1, - '5': 12, - '10': 'manufacturerData' - }, - const { - '1': 'serviceUuids', - '3': 7, - '4': 3, - '5': 11, - '6': '.Uuid', - '10': 'serviceUuids' - }, - const {'1': 'rssi', '3': 5, '4': 1, '5': 5, '10': 'rssi'}, - const { - '1': 'isConnectable', - '3': 8, - '4': 1, - '5': 11, - '6': '.IsConnectable', - '10': 'isConnectable' - }, + '2': [ + {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'}, + {'1': 'failure', '3': 3, '4': 1, '5': 11, '6': '.GenericFailure', '10': 'failure'}, + {'1': 'serviceData', '3': 4, '4': 3, '5': 11, '6': '.ServiceDataEntry', '10': 'serviceData'}, + {'1': 'manufacturerData', '3': 6, '4': 1, '5': 12, '10': 'manufacturerData'}, + {'1': 'serviceUuids', '3': 7, '4': 3, '5': 11, '6': '.Uuid', '10': 'serviceUuids'}, + {'1': 'rssi', '3': 5, '4': 1, '5': 5, '10': 'rssi'}, + {'1': 'isConnectable', '3': 8, '4': 1, '5': 11, '6': '.IsConnectable', '10': 'isConnectable'}, ], }; /// Descriptor for `DeviceScanInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List deviceScanInfoDescriptor = $convert.base64Decode( - 'Cg5EZXZpY2VTY2FuSW5mbxIOCgJpZBgBIAEoCVICaWQSEgoEbmFtZRgCIAEoCVIEbmFtZRIpCgdmYWlsdXJlGAMgASgLMg8uR2VuZXJpY0ZhaWx1cmVSB2ZhaWx1cmUSMwoLc2VydmljZURhdGEYBCADKAsyES5TZXJ2aWNlRGF0YUVudHJ5UgtzZXJ2aWNlRGF0YRIqChBtYW51ZmFjdHVyZXJEYXRhGAYgASgMUhBtYW51ZmFjdHVyZXJEYXRhEikKDHNlcnZpY2VVdWlkcxgHIAMoCzIFLlV1aWRSDHNlcnZpY2VVdWlkcxISCgRyc3NpGAUgASgFUgRyc3NpEjQKDWlzQ29ubmVjdGFibGUYCCABKAsyDi5Jc0Nvbm5lY3RhYmxlUg1pc0Nvbm5lY3RhYmxl'); + 'Cg5EZXZpY2VTY2FuSW5mbxIOCgJpZBgBIAEoCVICaWQSEgoEbmFtZRgCIAEoCVIEbmFtZRIpCg' + 'dmYWlsdXJlGAMgASgLMg8uR2VuZXJpY0ZhaWx1cmVSB2ZhaWx1cmUSMwoLc2VydmljZURhdGEY' + 'BCADKAsyES5TZXJ2aWNlRGF0YUVudHJ5UgtzZXJ2aWNlRGF0YRIqChBtYW51ZmFjdHVyZXJEYX' + 'RhGAYgASgMUhBtYW51ZmFjdHVyZXJEYXRhEikKDHNlcnZpY2VVdWlkcxgHIAMoCzIFLlV1aWRS' + 'DHNlcnZpY2VVdWlkcxISCgRyc3NpGAUgASgFUgRyc3NpEjQKDWlzQ29ubmVjdGFibGUYCCABKA' + 'syDi5Jc0Nvbm5lY3RhYmxlUg1pc0Nvbm5lY3RhYmxl'); + +@$core.Deprecated('Use establishBondingRequestDescriptor instead') +const EstablishBondingRequest$json = { + '1': 'EstablishBondingRequest', + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + ], +}; + +/// Descriptor for `EstablishBondingRequest`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List establishBondingRequestDescriptor = $convert.base64Decode( + 'ChdFc3RhYmxpc2hCb25kaW5nUmVxdWVzdBIaCghkZXZpY2VJZBgBIAEoCVIIZGV2aWNlSWQ='); + +@$core.Deprecated('Use establishBondingInfoDescriptor instead') +const EstablishBondingInfo$json = { + '1': 'EstablishBondingInfo', + '2': [ + {'1': 'status', '3': 1, '4': 1, '5': 14, '6': '.EstablishBondingInfo.BondState', '10': 'status'}, + ], + '4': [EstablishBondingInfo_BondState$json], +}; + +@$core.Deprecated('Use establishBondingInfoDescriptor instead') +const EstablishBondingInfo_BondState$json = { + '1': 'BondState', + '2': [ + {'1': 'NONE', '2': 0}, + {'1': 'BONDING', '2': 1}, + {'1': 'BONDED', '2': 2}, + ], +}; + +/// Descriptor for `EstablishBondingInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List establishBondingInfoDescriptor = $convert.base64Decode( + 'ChRFc3RhYmxpc2hCb25kaW5nSW5mbxI3CgZzdGF0dXMYASABKA4yHy5Fc3RhYmxpc2hCb25kaW' + '5nSW5mby5Cb25kU3RhdGVSBnN0YXR1cyIuCglCb25kU3RhdGUSCAoETk9ORRAAEgsKB0JPTkRJ' + 'TkcQARIKCgZCT05ERUQQAg=='); + @$core.Deprecated('Use connectToDeviceRequestDescriptor instead') -const ConnectToDeviceRequest$json = const { +const ConnectToDeviceRequest$json = { '1': 'ConnectToDeviceRequest', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, - const { - '1': 'servicesWithCharacteristicsToDiscover', - '3': 2, - '4': 1, - '5': 11, - '6': '.ServicesWithCharacteristics', - '10': 'servicesWithCharacteristicsToDiscover' - }, - const {'1': 'timeoutInMs', '3': 3, '4': 1, '5': 5, '10': 'timeoutInMs'}, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + {'1': 'servicesWithCharacteristicsToDiscover', '3': 2, '4': 1, '5': 11, '6': '.ServicesWithCharacteristics', '10': 'servicesWithCharacteristicsToDiscover'}, + {'1': 'timeoutInMs', '3': 3, '4': 1, '5': 5, '10': 'timeoutInMs'}, ], }; /// Descriptor for `ConnectToDeviceRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List connectToDeviceRequestDescriptor = - $convert.base64Decode( - 'ChZDb25uZWN0VG9EZXZpY2VSZXF1ZXN0EhoKCGRldmljZUlkGAEgASgJUghkZXZpY2VJZBJyCiVzZXJ2aWNlc1dpdGhDaGFyYWN0ZXJpc3RpY3NUb0Rpc2NvdmVyGAIgASgLMhwuU2VydmljZXNXaXRoQ2hhcmFjdGVyaXN0aWNzUiVzZXJ2aWNlc1dpdGhDaGFyYWN0ZXJpc3RpY3NUb0Rpc2NvdmVyEiAKC3RpbWVvdXRJbk1zGAMgASgFUgt0aW1lb3V0SW5Ncw=='); +final $typed_data.Uint8List connectToDeviceRequestDescriptor = $convert.base64Decode( + 'ChZDb25uZWN0VG9EZXZpY2VSZXF1ZXN0EhoKCGRldmljZUlkGAEgASgJUghkZXZpY2VJZBJyCi' + 'VzZXJ2aWNlc1dpdGhDaGFyYWN0ZXJpc3RpY3NUb0Rpc2NvdmVyGAIgASgLMhwuU2VydmljZXNX' + 'aXRoQ2hhcmFjdGVyaXN0aWNzUiVzZXJ2aWNlc1dpdGhDaGFyYWN0ZXJpc3RpY3NUb0Rpc2Nvdm' + 'VyEiAKC3RpbWVvdXRJbk1zGAMgASgFUgt0aW1lb3V0SW5Ncw=='); + @$core.Deprecated('Use deviceInfoDescriptor instead') -const DeviceInfo$json = const { +const DeviceInfo$json = { '1': 'DeviceInfo', - '2': const [ - const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, - const { - '1': 'connectionState', - '3': 2, - '4': 1, - '5': 5, - '10': 'connectionState' - }, - const { - '1': 'failure', - '3': 3, - '4': 1, - '5': 11, - '6': '.GenericFailure', - '10': 'failure' - }, + '2': [ + {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + {'1': 'connectionState', '3': 2, '4': 1, '5': 5, '10': 'connectionState'}, + {'1': 'failure', '3': 3, '4': 1, '5': 11, '6': '.GenericFailure', '10': 'failure'}, ], }; /// Descriptor for `DeviceInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List deviceInfoDescriptor = $convert.base64Decode( - 'CgpEZXZpY2VJbmZvEg4KAmlkGAEgASgJUgJpZBIoCg9jb25uZWN0aW9uU3RhdGUYAiABKAVSD2Nvbm5lY3Rpb25TdGF0ZRIpCgdmYWlsdXJlGAMgASgLMg8uR2VuZXJpY0ZhaWx1cmVSB2ZhaWx1cmU='); + 'CgpEZXZpY2VJbmZvEg4KAmlkGAEgASgJUgJpZBIoCg9jb25uZWN0aW9uU3RhdGUYAiABKAVSD2' + 'Nvbm5lY3Rpb25TdGF0ZRIpCgdmYWlsdXJlGAMgASgLMg8uR2VuZXJpY0ZhaWx1cmVSB2ZhaWx1' + 'cmU='); + +@$core.Deprecated('Use getDeviceNameRequestDescriptor instead') +const GetDeviceNameRequest$json = { + '1': 'GetDeviceNameRequest', + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + ], +}; + +/// Descriptor for `GetDeviceNameRequest`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List getDeviceNameRequestDescriptor = $convert.base64Decode( + 'ChRHZXREZXZpY2VOYW1lUmVxdWVzdBIaCghkZXZpY2VJZBgBIAEoCVIIZGV2aWNlSWQ='); + +@$core.Deprecated('Use deviceNameInfoDescriptor instead') +const DeviceNameInfo$json = { + '1': 'DeviceNameInfo', + '2': [ + {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + {'1': 'deviceName', '3': 2, '4': 1, '5': 9, '10': 'deviceName'}, + ], +}; + +/// Descriptor for `DeviceNameInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List deviceNameInfoDescriptor = $convert.base64Decode( + 'Cg5EZXZpY2VOYW1lSW5mbxIOCgJpZBgBIAEoCVICaWQSHgoKZGV2aWNlTmFtZRgCIAEoCVIKZG' + 'V2aWNlTmFtZQ=='); + @$core.Deprecated('Use disconnectFromDeviceRequestDescriptor instead') -const DisconnectFromDeviceRequest$json = const { +const DisconnectFromDeviceRequest$json = { '1': 'DisconnectFromDeviceRequest', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, ], }; /// Descriptor for `DisconnectFromDeviceRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List disconnectFromDeviceRequestDescriptor = - $convert.base64Decode( - 'ChtEaXNjb25uZWN0RnJvbURldmljZVJlcXVlc3QSGgoIZGV2aWNlSWQYASABKAlSCGRldmljZUlk'); +final $typed_data.Uint8List disconnectFromDeviceRequestDescriptor = $convert.base64Decode( + 'ChtEaXNjb25uZWN0RnJvbURldmljZVJlcXVlc3QSGgoIZGV2aWNlSWQYASABKAlSCGRldmljZU' + 'lk'); + @$core.Deprecated('Use clearGattCacheRequestDescriptor instead') -const ClearGattCacheRequest$json = const { +const ClearGattCacheRequest$json = { '1': 'ClearGattCacheRequest', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, ], }; /// Descriptor for `ClearGattCacheRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List clearGattCacheRequestDescriptor = - $convert.base64Decode( - 'ChVDbGVhckdhdHRDYWNoZVJlcXVlc3QSGgoIZGV2aWNlSWQYASABKAlSCGRldmljZUlk'); +final $typed_data.Uint8List clearGattCacheRequestDescriptor = $convert.base64Decode( + 'ChVDbGVhckdhdHRDYWNoZVJlcXVlc3QSGgoIZGV2aWNlSWQYASABKAlSCGRldmljZUlk'); + @$core.Deprecated('Use clearGattCacheInfoDescriptor instead') -const ClearGattCacheInfo$json = const { +const ClearGattCacheInfo$json = { '1': 'ClearGattCacheInfo', - '2': const [ - const { - '1': 'failure', - '3': 1, - '4': 1, - '5': 11, - '6': '.GenericFailure', - '10': 'failure' - }, + '2': [ + {'1': 'failure', '3': 1, '4': 1, '5': 11, '6': '.GenericFailure', '10': 'failure'}, ], }; /// Descriptor for `ClearGattCacheInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List clearGattCacheInfoDescriptor = $convert.base64Decode( - 'ChJDbGVhckdhdHRDYWNoZUluZm8SKQoHZmFpbHVyZRgBIAEoCzIPLkdlbmVyaWNGYWlsdXJlUgdmYWlsdXJl'); + 'ChJDbGVhckdhdHRDYWNoZUluZm8SKQoHZmFpbHVyZRgBIAEoCzIPLkdlbmVyaWNGYWlsdXJlUg' + 'dmYWlsdXJl'); + @$core.Deprecated('Use notifyCharacteristicRequestDescriptor instead') -const NotifyCharacteristicRequest$json = const { +const NotifyCharacteristicRequest$json = { '1': 'NotifyCharacteristicRequest', - '2': const [ - const { - '1': 'characteristic', - '3': 1, - '4': 1, - '5': 11, - '6': '.CharacteristicAddress', - '10': 'characteristic' - }, + '2': [ + {'1': 'characteristic', '3': 1, '4': 1, '5': 11, '6': '.CharacteristicAddress', '10': 'characteristic'}, ], }; /// Descriptor for `NotifyCharacteristicRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List notifyCharacteristicRequestDescriptor = - $convert.base64Decode( - 'ChtOb3RpZnlDaGFyYWN0ZXJpc3RpY1JlcXVlc3QSPgoOY2hhcmFjdGVyaXN0aWMYASABKAsyFi5DaGFyYWN0ZXJpc3RpY0FkZHJlc3NSDmNoYXJhY3RlcmlzdGlj'); +final $typed_data.Uint8List notifyCharacteristicRequestDescriptor = $convert.base64Decode( + 'ChtOb3RpZnlDaGFyYWN0ZXJpc3RpY1JlcXVlc3QSPgoOY2hhcmFjdGVyaXN0aWMYASABKAsyFi' + '5DaGFyYWN0ZXJpc3RpY0FkZHJlc3NSDmNoYXJhY3RlcmlzdGlj'); + @$core.Deprecated('Use notifyNoMoreCharacteristicRequestDescriptor instead') -const NotifyNoMoreCharacteristicRequest$json = const { +const NotifyNoMoreCharacteristicRequest$json = { '1': 'NotifyNoMoreCharacteristicRequest', - '2': const [ - const { - '1': 'characteristic', - '3': 1, - '4': 1, - '5': 11, - '6': '.CharacteristicAddress', - '10': 'characteristic' - }, + '2': [ + {'1': 'characteristic', '3': 1, '4': 1, '5': 11, '6': '.CharacteristicAddress', '10': 'characteristic'}, ], }; /// Descriptor for `NotifyNoMoreCharacteristicRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List notifyNoMoreCharacteristicRequestDescriptor = - $convert.base64Decode( - 'CiFOb3RpZnlOb01vcmVDaGFyYWN0ZXJpc3RpY1JlcXVlc3QSPgoOY2hhcmFjdGVyaXN0aWMYASABKAsyFi5DaGFyYWN0ZXJpc3RpY0FkZHJlc3NSDmNoYXJhY3RlcmlzdGlj'); +final $typed_data.Uint8List notifyNoMoreCharacteristicRequestDescriptor = $convert.base64Decode( + 'CiFOb3RpZnlOb01vcmVDaGFyYWN0ZXJpc3RpY1JlcXVlc3QSPgoOY2hhcmFjdGVyaXN0aWMYAS' + 'ABKAsyFi5DaGFyYWN0ZXJpc3RpY0FkZHJlc3NSDmNoYXJhY3RlcmlzdGlj'); + @$core.Deprecated('Use readCharacteristicRequestDescriptor instead') -const ReadCharacteristicRequest$json = const { +const ReadCharacteristicRequest$json = { '1': 'ReadCharacteristicRequest', - '2': const [ - const { - '1': 'characteristic', - '3': 1, - '4': 1, - '5': 11, - '6': '.CharacteristicAddress', - '10': 'characteristic' - }, + '2': [ + {'1': 'characteristic', '3': 1, '4': 1, '5': 11, '6': '.CharacteristicAddress', '10': 'characteristic'}, ], }; /// Descriptor for `ReadCharacteristicRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List readCharacteristicRequestDescriptor = - $convert.base64Decode( - 'ChlSZWFkQ2hhcmFjdGVyaXN0aWNSZXF1ZXN0Ej4KDmNoYXJhY3RlcmlzdGljGAEgASgLMhYuQ2hhcmFjdGVyaXN0aWNBZGRyZXNzUg5jaGFyYWN0ZXJpc3RpYw=='); +final $typed_data.Uint8List readCharacteristicRequestDescriptor = $convert.base64Decode( + 'ChlSZWFkQ2hhcmFjdGVyaXN0aWNSZXF1ZXN0Ej4KDmNoYXJhY3RlcmlzdGljGAEgASgLMhYuQ2' + 'hhcmFjdGVyaXN0aWNBZGRyZXNzUg5jaGFyYWN0ZXJpc3RpYw=='); + @$core.Deprecated('Use characteristicValueInfoDescriptor instead') -const CharacteristicValueInfo$json = const { +const CharacteristicValueInfo$json = { '1': 'CharacteristicValueInfo', - '2': const [ - const { - '1': 'characteristic', - '3': 1, - '4': 1, - '5': 11, - '6': '.CharacteristicAddress', - '10': 'characteristic' - }, - const {'1': 'value', '3': 2, '4': 1, '5': 12, '10': 'value'}, - const { - '1': 'failure', - '3': 3, - '4': 1, - '5': 11, - '6': '.GenericFailure', - '10': 'failure' - }, + '2': [ + {'1': 'characteristic', '3': 1, '4': 1, '5': 11, '6': '.CharacteristicAddress', '10': 'characteristic'}, + {'1': 'value', '3': 2, '4': 1, '5': 12, '10': 'value'}, + {'1': 'failure', '3': 3, '4': 1, '5': 11, '6': '.GenericFailure', '10': 'failure'}, ], }; /// Descriptor for `CharacteristicValueInfo`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List characteristicValueInfoDescriptor = - $convert.base64Decode( - 'ChdDaGFyYWN0ZXJpc3RpY1ZhbHVlSW5mbxI+Cg5jaGFyYWN0ZXJpc3RpYxgBIAEoCzIWLkNoYXJhY3RlcmlzdGljQWRkcmVzc1IOY2hhcmFjdGVyaXN0aWMSFAoFdmFsdWUYAiABKAxSBXZhbHVlEikKB2ZhaWx1cmUYAyABKAsyDy5HZW5lcmljRmFpbHVyZVIHZmFpbHVyZQ=='); +final $typed_data.Uint8List characteristicValueInfoDescriptor = $convert.base64Decode( + 'ChdDaGFyYWN0ZXJpc3RpY1ZhbHVlSW5mbxI+Cg5jaGFyYWN0ZXJpc3RpYxgBIAEoCzIWLkNoYX' + 'JhY3RlcmlzdGljQWRkcmVzc1IOY2hhcmFjdGVyaXN0aWMSFAoFdmFsdWUYAiABKAxSBXZhbHVl' + 'EikKB2ZhaWx1cmUYAyABKAsyDy5HZW5lcmljRmFpbHVyZVIHZmFpbHVyZQ=='); + @$core.Deprecated('Use writeCharacteristicRequestDescriptor instead') -const WriteCharacteristicRequest$json = const { +const WriteCharacteristicRequest$json = { '1': 'WriteCharacteristicRequest', - '2': const [ - const { - '1': 'characteristic', - '3': 1, - '4': 1, - '5': 11, - '6': '.CharacteristicAddress', - '10': 'characteristic' - }, - const {'1': 'value', '3': 2, '4': 1, '5': 12, '10': 'value'}, + '2': [ + {'1': 'characteristic', '3': 1, '4': 1, '5': 11, '6': '.CharacteristicAddress', '10': 'characteristic'}, + {'1': 'value', '3': 2, '4': 1, '5': 12, '10': 'value'}, ], }; /// Descriptor for `WriteCharacteristicRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List writeCharacteristicRequestDescriptor = - $convert.base64Decode( - 'ChpXcml0ZUNoYXJhY3RlcmlzdGljUmVxdWVzdBI+Cg5jaGFyYWN0ZXJpc3RpYxgBIAEoCzIWLkNoYXJhY3RlcmlzdGljQWRkcmVzc1IOY2hhcmFjdGVyaXN0aWMSFAoFdmFsdWUYAiABKAxSBXZhbHVl'); +final $typed_data.Uint8List writeCharacteristicRequestDescriptor = $convert.base64Decode( + 'ChpXcml0ZUNoYXJhY3RlcmlzdGljUmVxdWVzdBI+Cg5jaGFyYWN0ZXJpc3RpYxgBIAEoCzIWLk' + 'NoYXJhY3RlcmlzdGljQWRkcmVzc1IOY2hhcmFjdGVyaXN0aWMSFAoFdmFsdWUYAiABKAxSBXZh' + 'bHVl'); + @$core.Deprecated('Use writeCharacteristicInfoDescriptor instead') -const WriteCharacteristicInfo$json = const { +const WriteCharacteristicInfo$json = { '1': 'WriteCharacteristicInfo', - '2': const [ - const { - '1': 'characteristic', - '3': 1, - '4': 1, - '5': 11, - '6': '.CharacteristicAddress', - '10': 'characteristic' - }, - const { - '1': 'failure', - '3': 3, - '4': 1, - '5': 11, - '6': '.GenericFailure', - '10': 'failure' - }, + '2': [ + {'1': 'characteristic', '3': 1, '4': 1, '5': 11, '6': '.CharacteristicAddress', '10': 'characteristic'}, + {'1': 'failure', '3': 3, '4': 1, '5': 11, '6': '.GenericFailure', '10': 'failure'}, ], }; /// Descriptor for `WriteCharacteristicInfo`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List writeCharacteristicInfoDescriptor = - $convert.base64Decode( - 'ChdXcml0ZUNoYXJhY3RlcmlzdGljSW5mbxI+Cg5jaGFyYWN0ZXJpc3RpYxgBIAEoCzIWLkNoYXJhY3RlcmlzdGljQWRkcmVzc1IOY2hhcmFjdGVyaXN0aWMSKQoHZmFpbHVyZRgDIAEoCzIPLkdlbmVyaWNGYWlsdXJlUgdmYWlsdXJl'); +final $typed_data.Uint8List writeCharacteristicInfoDescriptor = $convert.base64Decode( + 'ChdXcml0ZUNoYXJhY3RlcmlzdGljSW5mbxI+Cg5jaGFyYWN0ZXJpc3RpYxgBIAEoCzIWLkNoYX' + 'JhY3RlcmlzdGljQWRkcmVzc1IOY2hhcmFjdGVyaXN0aWMSKQoHZmFpbHVyZRgDIAEoCzIPLkdl' + 'bmVyaWNGYWlsdXJlUgdmYWlsdXJl'); + @$core.Deprecated('Use negotiateMtuRequestDescriptor instead') -const NegotiateMtuRequest$json = const { +const NegotiateMtuRequest$json = { '1': 'NegotiateMtuRequest', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, - const {'1': 'mtuSize', '3': 2, '4': 1, '5': 5, '10': 'mtuSize'}, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + {'1': 'mtuSize', '3': 2, '4': 1, '5': 5, '10': 'mtuSize'}, ], }; /// Descriptor for `NegotiateMtuRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List negotiateMtuRequestDescriptor = $convert.base64Decode( - 'ChNOZWdvdGlhdGVNdHVSZXF1ZXN0EhoKCGRldmljZUlkGAEgASgJUghkZXZpY2VJZBIYCgdtdHVTaXplGAIgASgFUgdtdHVTaXpl'); + 'ChNOZWdvdGlhdGVNdHVSZXF1ZXN0EhoKCGRldmljZUlkGAEgASgJUghkZXZpY2VJZBIYCgdtdH' + 'VTaXplGAIgASgFUgdtdHVTaXpl'); + @$core.Deprecated('Use negotiateMtuInfoDescriptor instead') -const NegotiateMtuInfo$json = const { +const NegotiateMtuInfo$json = { '1': 'NegotiateMtuInfo', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, - const {'1': 'mtuSize', '3': 2, '4': 1, '5': 5, '10': 'mtuSize'}, - const { - '1': 'failure', - '3': 3, - '4': 1, - '5': 11, - '6': '.GenericFailure', - '10': 'failure' - }, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + {'1': 'mtuSize', '3': 2, '4': 1, '5': 5, '10': 'mtuSize'}, + {'1': 'failure', '3': 3, '4': 1, '5': 11, '6': '.GenericFailure', '10': 'failure'}, ], }; /// Descriptor for `NegotiateMtuInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List negotiateMtuInfoDescriptor = $convert.base64Decode( - 'ChBOZWdvdGlhdGVNdHVJbmZvEhoKCGRldmljZUlkGAEgASgJUghkZXZpY2VJZBIYCgdtdHVTaXplGAIgASgFUgdtdHVTaXplEikKB2ZhaWx1cmUYAyABKAsyDy5HZW5lcmljRmFpbHVyZVIHZmFpbHVyZQ=='); + 'ChBOZWdvdGlhdGVNdHVJbmZvEhoKCGRldmljZUlkGAEgASgJUghkZXZpY2VJZBIYCgdtdHVTaX' + 'plGAIgASgFUgdtdHVTaXplEikKB2ZhaWx1cmUYAyABKAsyDy5HZW5lcmljRmFpbHVyZVIHZmFp' + 'bHVyZQ=='); + @$core.Deprecated('Use bleStatusInfoDescriptor instead') -const BleStatusInfo$json = const { +const BleStatusInfo$json = { '1': 'BleStatusInfo', - '2': const [ - const {'1': 'status', '3': 1, '4': 1, '5': 5, '10': 'status'}, + '2': [ + {'1': 'status', '3': 1, '4': 1, '5': 5, '10': 'status'}, ], }; /// Descriptor for `BleStatusInfo`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List bleStatusInfoDescriptor = $convert - .base64Decode('Cg1CbGVTdGF0dXNJbmZvEhYKBnN0YXR1cxgBIAEoBVIGc3RhdHVz'); +final $typed_data.Uint8List bleStatusInfoDescriptor = $convert.base64Decode( + 'Cg1CbGVTdGF0dXNJbmZvEhYKBnN0YXR1cxgBIAEoBVIGc3RhdHVz'); + @$core.Deprecated('Use changeConnectionPriorityRequestDescriptor instead') -const ChangeConnectionPriorityRequest$json = const { +const ChangeConnectionPriorityRequest$json = { '1': 'ChangeConnectionPriorityRequest', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, - const {'1': 'priority', '3': 2, '4': 1, '5': 5, '10': 'priority'}, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + {'1': 'priority', '3': 2, '4': 1, '5': 5, '10': 'priority'}, ], }; /// Descriptor for `ChangeConnectionPriorityRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List changeConnectionPriorityRequestDescriptor = - $convert.base64Decode( - 'Ch9DaGFuZ2VDb25uZWN0aW9uUHJpb3JpdHlSZXF1ZXN0EhoKCGRldmljZUlkGAEgASgJUghkZXZpY2VJZBIaCghwcmlvcml0eRgCIAEoBVIIcHJpb3JpdHk='); +final $typed_data.Uint8List changeConnectionPriorityRequestDescriptor = $convert.base64Decode( + 'Ch9DaGFuZ2VDb25uZWN0aW9uUHJpb3JpdHlSZXF1ZXN0EhoKCGRldmljZUlkGAEgASgJUghkZX' + 'ZpY2VJZBIaCghwcmlvcml0eRgCIAEoBVIIcHJpb3JpdHk='); + @$core.Deprecated('Use changeConnectionPriorityInfoDescriptor instead') -const ChangeConnectionPriorityInfo$json = const { +const ChangeConnectionPriorityInfo$json = { '1': 'ChangeConnectionPriorityInfo', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, - const { - '1': 'failure', - '3': 2, - '4': 1, - '5': 11, - '6': '.GenericFailure', - '10': 'failure' - }, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + {'1': 'failure', '3': 2, '4': 1, '5': 11, '6': '.GenericFailure', '10': 'failure'}, ], }; /// Descriptor for `ChangeConnectionPriorityInfo`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List changeConnectionPriorityInfoDescriptor = - $convert.base64Decode( - 'ChxDaGFuZ2VDb25uZWN0aW9uUHJpb3JpdHlJbmZvEhoKCGRldmljZUlkGAEgASgJUghkZXZpY2VJZBIpCgdmYWlsdXJlGAIgASgLMg8uR2VuZXJpY0ZhaWx1cmVSB2ZhaWx1cmU='); +final $typed_data.Uint8List changeConnectionPriorityInfoDescriptor = $convert.base64Decode( + 'ChxDaGFuZ2VDb25uZWN0aW9uUHJpb3JpdHlJbmZvEhoKCGRldmljZUlkGAEgASgJUghkZXZpY2' + 'VJZBIpCgdmYWlsdXJlGAIgASgLMg8uR2VuZXJpY0ZhaWx1cmVSB2ZhaWx1cmU='); + @$core.Deprecated('Use characteristicAddressDescriptor instead') -const CharacteristicAddress$json = const { +const CharacteristicAddress$json = { '1': 'CharacteristicAddress', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, - const { - '1': 'serviceUuid', - '3': 2, - '4': 1, - '5': 11, - '6': '.Uuid', - '10': 'serviceUuid' - }, - const { - '1': 'characteristicUuid', - '3': 3, - '4': 1, - '5': 11, - '6': '.Uuid', - '10': 'characteristicUuid' - }, - const { - '1': 'serviceInstanceId', - '3': 4, - '4': 1, - '5': 9, - '10': 'serviceInstanceId' - }, - const { - '1': 'characteristicInstanceId', - '3': 5, - '4': 1, - '5': 9, - '10': 'characteristicInstanceId' - }, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + {'1': 'serviceUuid', '3': 2, '4': 1, '5': 11, '6': '.Uuid', '10': 'serviceUuid'}, + {'1': 'characteristicUuid', '3': 3, '4': 1, '5': 11, '6': '.Uuid', '10': 'characteristicUuid'}, + {'1': 'serviceInstanceId', '3': 4, '4': 1, '5': 9, '10': 'serviceInstanceId'}, + {'1': 'characteristicInstanceId', '3': 5, '4': 1, '5': 9, '10': 'characteristicInstanceId'}, ], }; /// Descriptor for `CharacteristicAddress`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List characteristicAddressDescriptor = $convert.base64Decode( - 'ChVDaGFyYWN0ZXJpc3RpY0FkZHJlc3MSGgoIZGV2aWNlSWQYASABKAlSCGRldmljZUlkEicKC3NlcnZpY2VVdWlkGAIgASgLMgUuVXVpZFILc2VydmljZVV1aWQSNQoSY2hhcmFjdGVyaXN0aWNVdWlkGAMgASgLMgUuVXVpZFISY2hhcmFjdGVyaXN0aWNVdWlkEiwKEXNlcnZpY2VJbnN0YW5jZUlkGAQgASgJUhFzZXJ2aWNlSW5zdGFuY2VJZBI6ChhjaGFyYWN0ZXJpc3RpY0luc3RhbmNlSWQYBSABKAlSGGNoYXJhY3RlcmlzdGljSW5zdGFuY2VJZA=='); + 'ChVDaGFyYWN0ZXJpc3RpY0FkZHJlc3MSGgoIZGV2aWNlSWQYASABKAlSCGRldmljZUlkEicKC3' + 'NlcnZpY2VVdWlkGAIgASgLMgUuVXVpZFILc2VydmljZVV1aWQSNQoSY2hhcmFjdGVyaXN0aWNV' + 'dWlkGAMgASgLMgUuVXVpZFISY2hhcmFjdGVyaXN0aWNVdWlkEiwKEXNlcnZpY2VJbnN0YW5jZU' + 'lkGAQgASgJUhFzZXJ2aWNlSW5zdGFuY2VJZBI6ChhjaGFyYWN0ZXJpc3RpY0luc3RhbmNlSWQY' + 'BSABKAlSGGNoYXJhY3RlcmlzdGljSW5zdGFuY2VJZA=='); + @$core.Deprecated('Use serviceDataEntryDescriptor instead') -const ServiceDataEntry$json = const { +const ServiceDataEntry$json = { '1': 'ServiceDataEntry', - '2': const [ - const { - '1': 'serviceUuid', - '3': 1, - '4': 1, - '5': 11, - '6': '.Uuid', - '10': 'serviceUuid' - }, - const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'}, + '2': [ + {'1': 'serviceUuid', '3': 1, '4': 1, '5': 11, '6': '.Uuid', '10': 'serviceUuid'}, + {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'}, ], }; /// Descriptor for `ServiceDataEntry`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List serviceDataEntryDescriptor = $convert.base64Decode( - 'ChBTZXJ2aWNlRGF0YUVudHJ5EicKC3NlcnZpY2VVdWlkGAEgASgLMgUuVXVpZFILc2VydmljZVV1aWQSEgoEZGF0YRgCIAEoDFIEZGF0YQ=='); + 'ChBTZXJ2aWNlRGF0YUVudHJ5EicKC3NlcnZpY2VVdWlkGAEgASgLMgUuVXVpZFILc2VydmljZV' + 'V1aWQSEgoEZGF0YRgCIAEoDFIEZGF0YQ=='); + @$core.Deprecated('Use servicesWithCharacteristicsDescriptor instead') -const ServicesWithCharacteristics$json = const { +const ServicesWithCharacteristics$json = { '1': 'ServicesWithCharacteristics', - '2': const [ - const { - '1': 'items', - '3': 1, - '4': 3, - '5': 11, - '6': '.ServiceWithCharacteristics', - '10': 'items' - }, + '2': [ + {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.ServiceWithCharacteristics', '10': 'items'}, ], }; /// Descriptor for `ServicesWithCharacteristics`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List servicesWithCharacteristicsDescriptor = - $convert.base64Decode( - 'ChtTZXJ2aWNlc1dpdGhDaGFyYWN0ZXJpc3RpY3MSMQoFaXRlbXMYASADKAsyGy5TZXJ2aWNlV2l0aENoYXJhY3RlcmlzdGljc1IFaXRlbXM='); +final $typed_data.Uint8List servicesWithCharacteristicsDescriptor = $convert.base64Decode( + 'ChtTZXJ2aWNlc1dpdGhDaGFyYWN0ZXJpc3RpY3MSMQoFaXRlbXMYASADKAsyGy5TZXJ2aWNlV2' + 'l0aENoYXJhY3RlcmlzdGljc1IFaXRlbXM='); + @$core.Deprecated('Use serviceWithCharacteristicsDescriptor instead') -const ServiceWithCharacteristics$json = const { +const ServiceWithCharacteristics$json = { '1': 'ServiceWithCharacteristics', - '2': const [ - const { - '1': 'serviceId', - '3': 1, - '4': 1, - '5': 11, - '6': '.Uuid', - '10': 'serviceId' - }, - const { - '1': 'characteristics', - '3': 2, - '4': 3, - '5': 11, - '6': '.Uuid', - '10': 'characteristics' - }, + '2': [ + {'1': 'serviceId', '3': 1, '4': 1, '5': 11, '6': '.Uuid', '10': 'serviceId'}, + {'1': 'characteristics', '3': 2, '4': 3, '5': 11, '6': '.Uuid', '10': 'characteristics'}, ], }; /// Descriptor for `ServiceWithCharacteristics`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List serviceWithCharacteristicsDescriptor = - $convert.base64Decode( - 'ChpTZXJ2aWNlV2l0aENoYXJhY3RlcmlzdGljcxIjCglzZXJ2aWNlSWQYASABKAsyBS5VdWlkUglzZXJ2aWNlSWQSLwoPY2hhcmFjdGVyaXN0aWNzGAIgAygLMgUuVXVpZFIPY2hhcmFjdGVyaXN0aWNz'); +final $typed_data.Uint8List serviceWithCharacteristicsDescriptor = $convert.base64Decode( + 'ChpTZXJ2aWNlV2l0aENoYXJhY3RlcmlzdGljcxIjCglzZXJ2aWNlSWQYASABKAsyBS5VdWlkUg' + 'lzZXJ2aWNlSWQSLwoPY2hhcmFjdGVyaXN0aWNzGAIgAygLMgUuVXVpZFIPY2hhcmFjdGVyaXN0' + 'aWNz'); + @$core.Deprecated('Use discoverServicesRequestDescriptor instead') -const DiscoverServicesRequest$json = const { +const DiscoverServicesRequest$json = { '1': 'DiscoverServicesRequest', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, ], }; /// Descriptor for `DiscoverServicesRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List discoverServicesRequestDescriptor = - $convert.base64Decode( - 'ChdEaXNjb3ZlclNlcnZpY2VzUmVxdWVzdBIaCghkZXZpY2VJZBgBIAEoCVIIZGV2aWNlSWQ='); +final $typed_data.Uint8List discoverServicesRequestDescriptor = $convert.base64Decode( + 'ChdEaXNjb3ZlclNlcnZpY2VzUmVxdWVzdBIaCghkZXZpY2VJZBgBIAEoCVIIZGV2aWNlSWQ='); + @$core.Deprecated('Use discoverServicesInfoDescriptor instead') -const DiscoverServicesInfo$json = const { +const DiscoverServicesInfo$json = { '1': 'DiscoverServicesInfo', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, - const { - '1': 'services', - '3': 2, - '4': 3, - '5': 11, - '6': '.DiscoveredService', - '10': 'services' - }, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + {'1': 'services', '3': 2, '4': 3, '5': 11, '6': '.DiscoveredService', '10': 'services'}, ], }; /// Descriptor for `DiscoverServicesInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List discoverServicesInfoDescriptor = $convert.base64Decode( - 'ChREaXNjb3ZlclNlcnZpY2VzSW5mbxIaCghkZXZpY2VJZBgBIAEoCVIIZGV2aWNlSWQSLgoIc2VydmljZXMYAiADKAsyEi5EaXNjb3ZlcmVkU2VydmljZVIIc2VydmljZXM='); + 'ChREaXNjb3ZlclNlcnZpY2VzSW5mbxIaCghkZXZpY2VJZBgBIAEoCVIIZGV2aWNlSWQSLgoIc2' + 'VydmljZXMYAiADKAsyEi5EaXNjb3ZlcmVkU2VydmljZVIIc2VydmljZXM='); + @$core.Deprecated('Use discoveredServiceDescriptor instead') -const DiscoveredService$json = const { +const DiscoveredService$json = { '1': 'DiscoveredService', - '2': const [ - const { - '1': 'serviceUuid', - '3': 1, - '4': 1, - '5': 11, - '6': '.Uuid', - '10': 'serviceUuid' - }, - const { - '1': 'characteristicUuids', - '3': 2, - '4': 3, - '5': 11, - '6': '.Uuid', - '10': 'characteristicUuids' - }, - const { - '1': 'includedServices', - '3': 3, - '4': 3, - '5': 11, - '6': '.DiscoveredService', - '10': 'includedServices' - }, - const { - '1': 'characteristics', - '3': 4, - '4': 3, - '5': 11, - '6': '.DiscoveredCharacteristic', - '10': 'characteristics' - }, - const { - '1': 'serviceInstanceId', - '3': 5, - '4': 1, - '5': 9, - '10': 'serviceInstanceId' - }, + '2': [ + {'1': 'serviceUuid', '3': 1, '4': 1, '5': 11, '6': '.Uuid', '10': 'serviceUuid'}, + {'1': 'characteristicUuids', '3': 2, '4': 3, '5': 11, '6': '.Uuid', '10': 'characteristicUuids'}, + {'1': 'includedServices', '3': 3, '4': 3, '5': 11, '6': '.DiscoveredService', '10': 'includedServices'}, + {'1': 'characteristics', '3': 4, '4': 3, '5': 11, '6': '.DiscoveredCharacteristic', '10': 'characteristics'}, + {'1': 'serviceInstanceId', '3': 5, '4': 1, '5': 9, '10': 'serviceInstanceId'}, ], }; /// Descriptor for `DiscoveredService`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List discoveredServiceDescriptor = $convert.base64Decode( - 'ChFEaXNjb3ZlcmVkU2VydmljZRInCgtzZXJ2aWNlVXVpZBgBIAEoCzIFLlV1aWRSC3NlcnZpY2VVdWlkEjcKE2NoYXJhY3RlcmlzdGljVXVpZHMYAiADKAsyBS5VdWlkUhNjaGFyYWN0ZXJpc3RpY1V1aWRzEj4KEGluY2x1ZGVkU2VydmljZXMYAyADKAsyEi5EaXNjb3ZlcmVkU2VydmljZVIQaW5jbHVkZWRTZXJ2aWNlcxJDCg9jaGFyYWN0ZXJpc3RpY3MYBCADKAsyGS5EaXNjb3ZlcmVkQ2hhcmFjdGVyaXN0aWNSD2NoYXJhY3RlcmlzdGljcxIsChFzZXJ2aWNlSW5zdGFuY2VJZBgFIAEoCVIRc2VydmljZUluc3RhbmNlSWQ='); + 'ChFEaXNjb3ZlcmVkU2VydmljZRInCgtzZXJ2aWNlVXVpZBgBIAEoCzIFLlV1aWRSC3NlcnZpY2' + 'VVdWlkEjcKE2NoYXJhY3RlcmlzdGljVXVpZHMYAiADKAsyBS5VdWlkUhNjaGFyYWN0ZXJpc3Rp' + 'Y1V1aWRzEj4KEGluY2x1ZGVkU2VydmljZXMYAyADKAsyEi5EaXNjb3ZlcmVkU2VydmljZVIQaW' + '5jbHVkZWRTZXJ2aWNlcxJDCg9jaGFyYWN0ZXJpc3RpY3MYBCADKAsyGS5EaXNjb3ZlcmVkQ2hh' + 'cmFjdGVyaXN0aWNSD2NoYXJhY3RlcmlzdGljcxIsChFzZXJ2aWNlSW5zdGFuY2VJZBgFIAEoCV' + 'IRc2VydmljZUluc3RhbmNlSWQ='); + @$core.Deprecated('Use discoveredCharacteristicDescriptor instead') -const DiscoveredCharacteristic$json = const { +const DiscoveredCharacteristic$json = { '1': 'DiscoveredCharacteristic', - '2': const [ - const { - '1': 'characteristicId', - '3': 1, - '4': 1, - '5': 11, - '6': '.Uuid', - '10': 'characteristicId' - }, - const { - '1': 'serviceId', - '3': 2, - '4': 1, - '5': 11, - '6': '.Uuid', - '10': 'serviceId' - }, - const {'1': 'isReadable', '3': 3, '4': 1, '5': 8, '10': 'isReadable'}, - const { - '1': 'isWritableWithResponse', - '3': 4, - '4': 1, - '5': 8, - '10': 'isWritableWithResponse' - }, - const { - '1': 'isWritableWithoutResponse', - '3': 5, - '4': 1, - '5': 8, - '10': 'isWritableWithoutResponse' - }, - const {'1': 'isNotifiable', '3': 6, '4': 1, '5': 8, '10': 'isNotifiable'}, - const {'1': 'isIndicatable', '3': 7, '4': 1, '5': 8, '10': 'isIndicatable'}, - const { - '1': 'characteristicInstanceId', - '3': 8, - '4': 1, - '5': 9, - '10': 'characteristicInstanceId' - }, + '2': [ + {'1': 'characteristicId', '3': 1, '4': 1, '5': 11, '6': '.Uuid', '10': 'characteristicId'}, + {'1': 'serviceId', '3': 2, '4': 1, '5': 11, '6': '.Uuid', '10': 'serviceId'}, + {'1': 'isReadable', '3': 3, '4': 1, '5': 8, '10': 'isReadable'}, + {'1': 'isWritableWithResponse', '3': 4, '4': 1, '5': 8, '10': 'isWritableWithResponse'}, + {'1': 'isWritableWithoutResponse', '3': 5, '4': 1, '5': 8, '10': 'isWritableWithoutResponse'}, + {'1': 'isNotifiable', '3': 6, '4': 1, '5': 8, '10': 'isNotifiable'}, + {'1': 'isIndicatable', '3': 7, '4': 1, '5': 8, '10': 'isIndicatable'}, + {'1': 'characteristicInstanceId', '3': 8, '4': 1, '5': 9, '10': 'characteristicInstanceId'}, ], }; /// Descriptor for `DiscoveredCharacteristic`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List discoveredCharacteristicDescriptor = - $convert.base64Decode( - 'ChhEaXNjb3ZlcmVkQ2hhcmFjdGVyaXN0aWMSMQoQY2hhcmFjdGVyaXN0aWNJZBgBIAEoCzIFLlV1aWRSEGNoYXJhY3RlcmlzdGljSWQSIwoJc2VydmljZUlkGAIgASgLMgUuVXVpZFIJc2VydmljZUlkEh4KCmlzUmVhZGFibGUYAyABKAhSCmlzUmVhZGFibGUSNgoWaXNXcml0YWJsZVdpdGhSZXNwb25zZRgEIAEoCFIWaXNXcml0YWJsZVdpdGhSZXNwb25zZRI8Chlpc1dyaXRhYmxlV2l0aG91dFJlc3BvbnNlGAUgASgIUhlpc1dyaXRhYmxlV2l0aG91dFJlc3BvbnNlEiIKDGlzTm90aWZpYWJsZRgGIAEoCFIMaXNOb3RpZmlhYmxlEiQKDWlzSW5kaWNhdGFibGUYByABKAhSDWlzSW5kaWNhdGFibGUSOgoYY2hhcmFjdGVyaXN0aWNJbnN0YW5jZUlkGAggASgJUhhjaGFyYWN0ZXJpc3RpY0luc3RhbmNlSWQ='); +final $typed_data.Uint8List discoveredCharacteristicDescriptor = $convert.base64Decode( + 'ChhEaXNjb3ZlcmVkQ2hhcmFjdGVyaXN0aWMSMQoQY2hhcmFjdGVyaXN0aWNJZBgBIAEoCzIFLl' + 'V1aWRSEGNoYXJhY3RlcmlzdGljSWQSIwoJc2VydmljZUlkGAIgASgLMgUuVXVpZFIJc2Vydmlj' + 'ZUlkEh4KCmlzUmVhZGFibGUYAyABKAhSCmlzUmVhZGFibGUSNgoWaXNXcml0YWJsZVdpdGhSZX' + 'Nwb25zZRgEIAEoCFIWaXNXcml0YWJsZVdpdGhSZXNwb25zZRI8Chlpc1dyaXRhYmxlV2l0aG91' + 'dFJlc3BvbnNlGAUgASgIUhlpc1dyaXRhYmxlV2l0aG91dFJlc3BvbnNlEiIKDGlzTm90aWZpYW' + 'JsZRgGIAEoCFIMaXNOb3RpZmlhYmxlEiQKDWlzSW5kaWNhdGFibGUYByABKAhSDWlzSW5kaWNh' + 'dGFibGUSOgoYY2hhcmFjdGVyaXN0aWNJbnN0YW5jZUlkGAggASgJUhhjaGFyYWN0ZXJpc3RpY0' + 'luc3RhbmNlSWQ='); + @$core.Deprecated('Use readRssiRequestDescriptor instead') -const ReadRssiRequest$json = const { +const ReadRssiRequest$json = { '1': 'ReadRssiRequest', - '2': const [ - const {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, + '2': [ + {'1': 'deviceId', '3': 1, '4': 1, '5': 9, '10': 'deviceId'}, ], }; /// Descriptor for `ReadRssiRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List readRssiRequestDescriptor = $convert.base64Decode( 'Cg9SZWFkUnNzaVJlcXVlc3QSGgoIZGV2aWNlSWQYASABKAlSCGRldmljZUlk'); + @$core.Deprecated('Use readRssiResultDescriptor instead') -const ReadRssiResult$json = const { +const ReadRssiResult$json = { '1': 'ReadRssiResult', - '2': const [ - const {'1': 'rssi', '3': 1, '4': 1, '5': 5, '10': 'rssi'}, + '2': [ + {'1': 'rssi', '3': 1, '4': 1, '5': 5, '10': 'rssi'}, ], }; /// Descriptor for `ReadRssiResult`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List readRssiResultDescriptor = - $convert.base64Decode('Cg5SZWFkUnNzaVJlc3VsdBISCgRyc3NpGAEgASgFUgRyc3Np'); +final $typed_data.Uint8List readRssiResultDescriptor = $convert.base64Decode( + 'Cg5SZWFkUnNzaVJlc3VsdBISCgRyc3NpGAEgASgFUgRyc3Np'); + @$core.Deprecated('Use uuidDescriptor instead') -const Uuid$json = const { +const Uuid$json = { '1': 'Uuid', - '2': const [ - const {'1': 'data', '3': 1, '4': 1, '5': 12, '10': 'data'}, + '2': [ + {'1': 'data', '3': 1, '4': 1, '5': 12, '10': 'data'}, ], }; /// Descriptor for `Uuid`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List uuidDescriptor = - $convert.base64Decode('CgRVdWlkEhIKBGRhdGEYASABKAxSBGRhdGE='); +final $typed_data.Uint8List uuidDescriptor = $convert.base64Decode( + 'CgRVdWlkEhIKBGRhdGEYASABKAxSBGRhdGE='); + @$core.Deprecated('Use genericFailureDescriptor instead') -const GenericFailure$json = const { +const GenericFailure$json = { '1': 'GenericFailure', - '2': const [ - const {'1': 'code', '3': 1, '4': 1, '5': 5, '10': 'code'}, - const {'1': 'message', '3': 2, '4': 1, '5': 9, '10': 'message'}, + '2': [ + {'1': 'code', '3': 1, '4': 1, '5': 5, '10': 'code'}, + {'1': 'message', '3': 2, '4': 1, '5': 9, '10': 'message'}, ], }; /// Descriptor for `GenericFailure`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List genericFailureDescriptor = $convert.base64Decode( - 'Cg5HZW5lcmljRmFpbHVyZRISCgRjb2RlGAEgASgFUgRjb2RlEhgKB21lc3NhZ2UYAiABKAlSB21lc3NhZ2U='); + 'Cg5HZW5lcmljRmFpbHVyZRISCgRjb2RlGAEgASgFUgRjb2RlEhgKB21lc3NhZ2UYAiABKAlSB2' + '1lc3NhZ2U='); + @$core.Deprecated('Use isConnectableDescriptor instead') -const IsConnectable$json = const { +const IsConnectable$json = { '1': 'IsConnectable', - '2': const [ - const {'1': 'code', '3': 1, '4': 1, '5': 5, '10': 'code'}, + '2': [ + {'1': 'code', '3': 1, '4': 1, '5': 5, '10': 'code'}, ], }; /// Descriptor for `IsConnectable`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List isConnectableDescriptor = - $convert.base64Decode('Cg1Jc0Nvbm5lY3RhYmxlEhIKBGNvZGUYASABKAVSBGNvZGU='); +final $typed_data.Uint8List isConnectableDescriptor = $convert.base64Decode( + 'Cg1Jc0Nvbm5lY3RhYmxlEhIKBGNvZGUYASABKAVSBGNvZGU='); + diff --git a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbserver.dart b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbserver.dart index d859c88b..bca5dade 100644 --- a/packages/reactive_ble_mobile/lib/src/generated/bledata.pbserver.dart +++ b/packages/reactive_ble_mobile/lib/src/generated/bledata.pbserver.dart @@ -1,9 +1,14 @@ -/// +// // Generated code. Do not modify. // source: bledata.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import export 'bledata.pb.dart'; From 467d3105b4e40faf55b607c6eef8641cdb81b0fa Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Wed, 10 Apr 2024 16:45:01 +0100 Subject: [PATCH 16/17] fix merge error --- example/android/gradle/wrapper/gradle-wrapper.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 8bc9958a..89e56bdb 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,3 +1,4 @@ +#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 5d8740df49fbbf17858e13bd16ef4f481a5158fa Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Thu, 11 Apr 2024 16:37:50 +0100 Subject: [PATCH 17/17] restore android build --- .../com/signify/hue/flutterreactiveble/PluginController.kt | 3 ++- .../hue/flutterreactiveble/channelhandlers/CompanionHandler.kt | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt index dfe1c44e..8fa4d89a 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt @@ -19,13 +19,14 @@ import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel.Result +import io.flutter.plugin.common.PluginRegistry.ActivityResultListener import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import java.util.UUID import com.signify.hue.flutterreactiveble.ProtobufModel as pb @Suppress("TooManyFunctions") -class PluginController { +class PluginController : ActivityResultListener { private val pluginMethods = mapOf Unit>( "initialize" to this::initializeClient, diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt index 9d47daa5..3f1bd92f 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CompanionHandler.kt @@ -110,7 +110,6 @@ class CompanionHandler { } override fun onFailure(error: CharSequence?) { - Log.e(TAG, "onFailure: $error") result.error("CompanionHandler", error.toString(), null) } },