Pārlūkot izejas kodu

feat: bluetooth discovery

Raihan Rizal 5 mēneši atpakaļ
vecāks
revīzija
75373d1f04
44 mainītis faili ar 1039 papildinājumiem un 40 dzēšanām
  1. 2 0
      .gitignore
  2. 1 3
      README.md
  3. 42 0
      android/build.gradle
  4. BIN
      android/gradle/wrapper/gradle-wrapper.jar
  5. 7 0
      android/gradle/wrapper/gradle-wrapper.properties
  6. 249 0
      android/gradlew
  7. 92 0
      android/gradlew.bat
  8. BIN
      android/libs/ZSDK_ANDROID_API.jar
  9. BIN
      android/libs/commons-io-2.17.0.jar
  10. BIN
      android/libs/commons-lang3-3.17.0.jar
  11. BIN
      android/libs/commons-net-3.11.1.jar
  12. BIN
      android/libs/commons-validator-1.9.0.jar
  13. BIN
      android/libs/core-1.58.0.0.jar
  14. BIN
      android/libs/httpcore-4.4.16.jar
  15. BIN
      android/libs/httpmime-4.5.14.jar
  16. BIN
      android/libs/jackson-annotations-2.17.2.jar
  17. BIN
      android/libs/jackson-core-2.17.2.jar
  18. BIN
      android/libs/jackson-databind-2.17.2.jar
  19. BIN
      android/libs/opencsv-2.3.jar
  20. BIN
      android/libs/pkix-1.54.0.0.jar
  21. BIN
      android/libs/prov-1.58.0.0.jar
  22. BIN
      android/libs/snmp6_1z.jar
  23. 7 2
      android/src/main/AndroidManifest.xml
  24. 185 2
      android/src/main/java/id/kalanusa/flutter_zsdk/FlutterZsdkPlugin.java
  25. 118 0
      android/src/main/java/id/kalanusa/flutter_zsdk/bluetoothdiscoveryhandler/BluetoothDiscoveryHandler.java
  26. 22 0
      android/src/main/java/id/kalanusa/flutter_zsdk/bluetoothdiscoveryhandler/BluetoothPrinter.java
  27. 18 1
      example/android/app/build.gradle
  28. 1 0
      example/android/gradle.properties
  29. 2 1
      example/android/gradle/wrapper/gradle-wrapper.properties
  30. 1 1
      example/android/settings.gradle
  31. 89 8
      example/lib/main.dart
  32. 8 0
      example/lib/models/bluetooth_printer.dart
  33. 70 1
      example/pubspec.lock
  34. 3 0
      example/windows/flutter/generated_plugin_registrant.cc
  35. 1 0
      example/windows/flutter/generated_plugins.cmake
  36. 7 1
      lib/flutter_zsdk.dart
  37. 0 17
      lib/flutter_zsdk_method_channel.dart
  38. 84 0
      lib/src/flutter_zsdk_method_channel.dart
  39. 5 0
      lib/flutter_zsdk_platform_interface.dart
  40. 6 0
      lib/src/models/bluetooth_printer.dart
  41. 10 0
      lib/src/models/flutter_zsdk_exception.dart
  42. 2 0
      pubspec.yaml
  43. 1 1
      test/flutter_zsdk_method_channel_test.dart
  44. 6 2
      test/flutter_zsdk_test.dart

+ 2 - 0
.gitignore

@@ -27,3 +27,5 @@ migrate_working_dir/
27 27
 **/doc/api/
28 28
 .dart_tool/
29 29
 build/
30
+
31
+.vscode

+ 1 - 3
README.md

@@ -1,6 +1,5 @@
1 1
 # flutter_zsdk
2
-
3
-A new Flutter plugin project.
2
+Flutter Pluggin for Zebra Multiplatform SDK
4 3
 
5 4
 ## Getting Started
6 5
 
@@ -12,4 +11,3 @@ Android and/or iOS.
12 11
 For help getting started with Flutter development, view the
13 12
 [online documentation](https://flutter.dev/docs), which offers tutorials,
14 13
 samples, guidance on mobile development, and a full API reference.
15
-

+ 42 - 0
android/build.gradle

@@ -3,6 +3,7 @@ version '1.0'
3 3
 
4 4
 buildscript {
5 5
     repositories {
6
+        jcenter()
6 7
         google()
7 8
         mavenCentral()
8 9
     }
@@ -14,6 +15,7 @@ buildscript {
14 15
 
15 16
 rootProject.allprojects {
16 17
     repositories {
18
+        jcenter()
17 19
         google()
18 20
         mavenCentral()
19 21
     }
@@ -35,6 +37,7 @@ android {
35 37
 
36 38
     defaultConfig {
37 39
         minSdkVersion 19
40
+        multiDexEnabled true
38 41
     }
39 42
 
40 43
     dependencies {
@@ -42,6 +45,11 @@ android {
42 45
         testImplementation 'org.mockito:mockito-core:5.0.0'
43 46
     }
44 47
 
48
+    lintOptions {
49
+        abortOnError false
50
+        checkReleaseBuilds false
51
+    }
52
+
45 53
     testOptions {
46 54
         unitTests.all {
47 55
             testLogging {
@@ -51,4 +59,38 @@ android {
51 59
             }
52 60
         }
53 61
     }
62
+
63
+    packagingOptions {
64
+        exclude 'META-INF/LICENSE.txt'
65
+        exclude 'META-INF/NOTICE.txt'
66
+        exclude 'META-INF/NOTICE'
67
+        exclude 'META-INF/LICENSE'
68
+        exclude 'META-INF/DEPENDENCIES'
69
+    }
70
+}
71
+
72
+dependencies {
73
+    implementation files('libs\\commons-io-2.17.0.jar')
74
+    implementation files('libs\\commons-lang3-3.17.0.jar')
75
+    implementation files('libs\\commons-net-3.11.1.jar')
76
+    implementation files('libs\\commons-validator-1.9.0.jar')
77
+    implementation files('libs\\core-1.58.0.0.jar')
78
+    implementation files('libs\\httpcore-4.4.16.jar')
79
+    implementation files('libs\\httpmime-4.5.14.jar')
80
+    
81
+    implementation files('libs\\jackson-annotations-2.17.2.jar')
82
+    implementation files('libs\\jackson-core-2.17.2.jar')
83
+    implementation files('libs\\jackson-databind-2.17.2.jar')
84
+
85
+    // implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.3' // Downgraded
86
+    // implementation 'com.fasterxml.jackson.core:jackson-core:2.13.3' // Downgraded
87
+    // implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3' // Downgraded
88
+
89
+    implementation files('libs\\opencsv-2.3.jar')
90
+    implementation files('libs\\pkix-1.54.0.0.jar')
91
+    implementation files('libs\\prov-1.58.0.0.jar')
92
+    implementation files('libs\\snmp6_1z.jar')
93
+    implementation files('libs\\ZSDK_ANDROID_API.jar')
94
+    // implementation fileTree(dir: 'libs', include: ['*.jar'])
95
+    implementation 'androidx.multidex:multidex:2.0.1'
54 96
 }

BIN
android/gradle/wrapper/gradle-wrapper.jar


+ 7 - 0
android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,7 @@
1
+distributionBase=GRADLE_USER_HOME
2
+distributionPath=wrapper/dists
3
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
4
+networkTimeout=10000
5
+validateDistributionUrl=true
6
+zipStoreBase=GRADLE_USER_HOME
7
+zipStorePath=wrapper/dists

+ 249 - 0
android/gradlew

@@ -0,0 +1,249 @@
1
+#!/bin/sh
2
+
3
+#
4
+# Copyright © 2015-2021 the original authors.
5
+#
6
+# Licensed under the Apache License, Version 2.0 (the "License");
7
+# you may not use this file except in compliance with the License.
8
+# You may obtain a copy of the License at
9
+#
10
+#      https://www.apache.org/licenses/LICENSE-2.0
11
+#
12
+# Unless required by applicable law or agreed to in writing, software
13
+# distributed under the License is distributed on an "AS IS" BASIS,
14
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+# See the License for the specific language governing permissions and
16
+# limitations under the License.
17
+#
18
+
19
+##############################################################################
20
+#
21
+#   Gradle start up script for POSIX generated by Gradle.
22
+#
23
+#   Important for running:
24
+#
25
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26
+#       noncompliant, but you have some other compliant shell such as ksh or
27
+#       bash, then to run this script, type that shell name before the whole
28
+#       command line, like:
29
+#
30
+#           ksh Gradle
31
+#
32
+#       Busybox and similar reduced shells will NOT work, because this script
33
+#       requires all of these POSIX shell features:
34
+#         * functions;
35
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37
+#         * compound commands having a testable exit status, especially «case»;
38
+#         * various built-in commands including «command», «set», and «ulimit».
39
+#
40
+#   Important for patching:
41
+#
42
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
43
+#       by Bash, Ksh, etc; in particular arrays are avoided.
44
+#
45
+#       The "traditional" practice of packing multiple parameters into a
46
+#       space-separated string is a well documented source of bugs and security
47
+#       problems, so this is (mostly) avoided, by progressively accumulating
48
+#       options in "$@", and eventually passing that to Java.
49
+#
50
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52
+#       see the in-line comments for details.
53
+#
54
+#       There are tweaks for specific operating systems such as AIX, CygWin,
55
+#       Darwin, MinGW, and NonStop.
56
+#
57
+#   (3) This script is generated from the Groovy template
58
+#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59
+#       within the Gradle project.
60
+#
61
+#       You can find Gradle at https://github.com/gradle/gradle/.
62
+#
63
+##############################################################################
64
+
65
+# Attempt to set APP_HOME
66
+
67
+# Resolve links: $0 may be a link
68
+app_path=$0
69
+
70
+# Need this for daisy-chained symlinks.
71
+while
72
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
73
+    [ -h "$app_path" ]
74
+do
75
+    ls=$( ls -ld "$app_path" )
76
+    link=${ls#*' -> '}
77
+    case $link in             #(
78
+      /*)   app_path=$link ;; #(
79
+      *)    app_path=$APP_HOME$link ;;
80
+    esac
81
+done
82
+
83
+# This is normally unused
84
+# shellcheck disable=SC2034
85
+APP_BASE_NAME=${0##*/}
86
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88
+
89
+# Use the maximum available, or set MAX_FD != -1 to use that value.
90
+MAX_FD=maximum
91
+
92
+warn () {
93
+    echo "$*"
94
+} >&2
95
+
96
+die () {
97
+    echo
98
+    echo "$*"
99
+    echo
100
+    exit 1
101
+} >&2
102
+
103
+# OS specific support (must be 'true' or 'false').
104
+cygwin=false
105
+msys=false
106
+darwin=false
107
+nonstop=false
108
+case "$( uname )" in                #(
109
+  CYGWIN* )         cygwin=true  ;; #(
110
+  Darwin* )         darwin=true  ;; #(
111
+  MSYS* | MINGW* )  msys=true    ;; #(
112
+  NONSTOP* )        nonstop=true ;;
113
+esac
114
+
115
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116
+
117
+
118
+# Determine the Java command to use to start the JVM.
119
+if [ -n "$JAVA_HOME" ] ; then
120
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121
+        # IBM's JDK on AIX uses strange locations for the executables
122
+        JAVACMD=$JAVA_HOME/jre/sh/java
123
+    else
124
+        JAVACMD=$JAVA_HOME/bin/java
125
+    fi
126
+    if [ ! -x "$JAVACMD" ] ; then
127
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128
+
129
+Please set the JAVA_HOME variable in your environment to match the
130
+location of your Java installation."
131
+    fi
132
+else
133
+    JAVACMD=java
134
+    if ! command -v java >/dev/null 2>&1
135
+    then
136
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137
+
138
+Please set the JAVA_HOME variable in your environment to match the
139
+location of your Java installation."
140
+    fi
141
+fi
142
+
143
+# Increase the maximum file descriptors if we can.
144
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145
+    case $MAX_FD in #(
146
+      max*)
147
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148
+        # shellcheck disable=SC2039,SC3045
149
+        MAX_FD=$( ulimit -H -n ) ||
150
+            warn "Could not query maximum file descriptor limit"
151
+    esac
152
+    case $MAX_FD in  #(
153
+      '' | soft) :;; #(
154
+      *)
155
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156
+        # shellcheck disable=SC2039,SC3045
157
+        ulimit -n "$MAX_FD" ||
158
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
159
+    esac
160
+fi
161
+
162
+# Collect all arguments for the java command, stacking in reverse order:
163
+#   * args from the command line
164
+#   * the main class name
165
+#   * -classpath
166
+#   * -D...appname settings
167
+#   * --module-path (only if needed)
168
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169
+
170
+# For Cygwin or MSYS, switch paths to Windows format before running java
171
+if "$cygwin" || "$msys" ; then
172
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174
+
175
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
176
+
177
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
178
+    for arg do
179
+        if
180
+            case $arg in                                #(
181
+              -*)   false ;;                            # don't mess with options #(
182
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
183
+                    [ -e "$t" ] ;;                      #(
184
+              *)    false ;;
185
+            esac
186
+        then
187
+            arg=$( cygpath --path --ignore --mixed "$arg" )
188
+        fi
189
+        # Roll the args list around exactly as many times as the number of
190
+        # args, so each arg winds up back in the position where it started, but
191
+        # possibly modified.
192
+        #
193
+        # NB: a `for` loop captures its iteration list before it begins, so
194
+        # changing the positional parameters here affects neither the number of
195
+        # iterations, nor the values presented in `arg`.
196
+        shift                   # remove old arg
197
+        set -- "$@" "$arg"      # push replacement arg
198
+    done
199
+fi
200
+
201
+
202
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204
+
205
+# Collect all arguments for the java command:
206
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207
+#     and any embedded shellness will be escaped.
208
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209
+#     treated as '${Hostname}' itself on the command line.
210
+
211
+set -- \
212
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
213
+        -classpath "$CLASSPATH" \
214
+        org.gradle.wrapper.GradleWrapperMain \
215
+        "$@"
216
+
217
+# Stop when "xargs" is not available.
218
+if ! command -v xargs >/dev/null 2>&1
219
+then
220
+    die "xargs is not available"
221
+fi
222
+
223
+# Use "xargs" to parse quoted args.
224
+#
225
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226
+#
227
+# In Bash we could simply go:
228
+#
229
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230
+#   set -- "${ARGS[@]}" "$@"
231
+#
232
+# but POSIX shell has neither arrays nor command substitution, so instead we
233
+# post-process each arg (as a line of input to sed) to backslash-escape any
234
+# character that might be a shell metacharacter, then use eval to reverse
235
+# that process (while maintaining the separation between arguments), and wrap
236
+# the whole thing up as a single "set" statement.
237
+#
238
+# This will of course break if any of these variables contains a newline or
239
+# an unmatched quote.
240
+#
241
+
242
+eval "set -- $(
243
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244
+        xargs -n1 |
245
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246
+        tr '\n' ' '
247
+    )" '"$@"'
248
+
249
+exec "$JAVACMD" "$@"

+ 92 - 0
android/gradlew.bat

@@ -0,0 +1,92 @@
1
+@rem
2
+@rem Copyright 2015 the original author or authors.
3
+@rem
4
+@rem Licensed under the Apache License, Version 2.0 (the "License");
5
+@rem you may not use this file except in compliance with the License.
6
+@rem You may obtain a copy of the License at
7
+@rem
8
+@rem      https://www.apache.org/licenses/LICENSE-2.0
9
+@rem
10
+@rem Unless required by applicable law or agreed to in writing, software
11
+@rem distributed under the License is distributed on an "AS IS" BASIS,
12
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+@rem See the License for the specific language governing permissions and
14
+@rem limitations under the License.
15
+@rem
16
+
17
+@if "%DEBUG%"=="" @echo off
18
+@rem ##########################################################################
19
+@rem
20
+@rem  Gradle startup script for Windows
21
+@rem
22
+@rem ##########################################################################
23
+
24
+@rem Set local scope for the variables with windows NT shell
25
+if "%OS%"=="Windows_NT" setlocal
26
+
27
+set DIRNAME=%~dp0
28
+if "%DIRNAME%"=="" set DIRNAME=.
29
+@rem This is normally unused
30
+set APP_BASE_NAME=%~n0
31
+set APP_HOME=%DIRNAME%
32
+
33
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
34
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35
+
36
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38
+
39
+@rem Find java.exe
40
+if defined JAVA_HOME goto findJavaFromJavaHome
41
+
42
+set JAVA_EXE=java.exe
43
+%JAVA_EXE% -version >NUL 2>&1
44
+if %ERRORLEVEL% equ 0 goto execute
45
+
46
+echo.
47
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48
+echo.
49
+echo Please set the JAVA_HOME variable in your environment to match the
50
+echo location of your Java installation.
51
+
52
+goto fail
53
+
54
+:findJavaFromJavaHome
55
+set JAVA_HOME=%JAVA_HOME:"=%
56
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57
+
58
+if exist "%JAVA_EXE%" goto execute
59
+
60
+echo.
61
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62
+echo.
63
+echo Please set the JAVA_HOME variable in your environment to match the
64
+echo location of your Java installation.
65
+
66
+goto fail
67
+
68
+:execute
69
+@rem Setup the command line
70
+
71
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72
+
73
+
74
+@rem Execute Gradle
75
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76
+
77
+:end
78
+@rem End local scope for the variables with windows NT shell
79
+if %ERRORLEVEL% equ 0 goto mainEnd
80
+
81
+:fail
82
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83
+rem the _cmd.exe /c_ return code!
84
+set EXIT_CODE=%ERRORLEVEL%
85
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
86
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87
+exit /b %EXIT_CODE%
88
+
89
+:mainEnd
90
+if "%OS%"=="Windows_NT" endlocal
91
+
92
+:omega

BIN
android/libs/ZSDK_ANDROID_API.jar


BIN
android/libs/commons-io-2.17.0.jar


BIN
android/libs/commons-lang3-3.17.0.jar


BIN
android/libs/commons-net-3.11.1.jar


BIN
android/libs/commons-validator-1.9.0.jar


BIN
android/libs/core-1.58.0.0.jar


BIN
android/libs/httpcore-4.4.16.jar


BIN
android/libs/httpmime-4.5.14.jar


BIN
android/libs/jackson-annotations-2.17.2.jar


BIN
android/libs/jackson-core-2.17.2.jar


BIN
android/libs/jackson-databind-2.17.2.jar


BIN
android/libs/opencsv-2.3.jar


BIN
android/libs/pkix-1.54.0.0.jar


BIN
android/libs/prov-1.58.0.0.jar


BIN
android/libs/snmp6_1z.jar


+ 7 - 2
android/src/main/AndroidManifest.xml

@@ -1,3 +1,8 @@
1
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
-  package="id.kalanusa.flutter_zsdk">
1
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+    <uses-permission android:name="android.permission.BLUETOOTH"/>
3
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
4
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
5
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
6
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
7
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
3 8
 </manifest>

+ 185 - 2
android/src/main/java/id/kalanusa/flutter_zsdk/FlutterZsdkPlugin.java

@@ -1,34 +1,217 @@
1 1
 package id.kalanusa.flutter_zsdk;
2 2
 
3
+import android.Manifest;
4
+import android.app.Activity;
5
+import android.content.Context;
6
+import android.content.pm.PackageManager;
7
+import android.os.Build;
8
+import android.os.Handler;
9
+
3 10
 import androidx.annotation.NonNull;
4 11
 
12
+import com.zebra.sdk.comm.ConnectionException;
13
+import com.zebra.sdk.printer.discovery.BluetoothDiscoverer;
14
+import com.zebra.sdk.printer.discovery.DiscoveredPrinter;
15
+import com.zebra.sdk.printer.discovery.DiscoveryHandler;
16
+
17
+import java.util.ArrayList;
18
+import java.util.Map;
19
+import java.util.Objects;
20
+
21
+import id.kalanusa.flutter_zsdk.bluetoothdiscoveryhandler.BluetoothDiscoveryHandler;
22
+import id.kalanusa.flutter_zsdk.bluetoothdiscoveryhandler.BluetoothPrinter;
23
+import io.flutter.Log;
24
+import io.flutter.embedding.engine.dart.DartExecutor;
5 25
 import io.flutter.embedding.engine.plugins.FlutterPlugin;
26
+import io.flutter.embedding.engine.plugins.activity.ActivityAware;
27
+import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
28
+import io.flutter.plugin.common.BinaryMessenger;
29
+import io.flutter.plugin.common.EventChannel;
6 30
 import io.flutter.plugin.common.MethodCall;
7 31
 import io.flutter.plugin.common.MethodChannel;
8 32
 import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
9 33
 import io.flutter.plugin.common.MethodChannel.Result;
34
+import io.flutter.plugin.common.PluginRegistry;
10 35
 
11 36
 /** FlutterZsdkPlugin */
12
-public class FlutterZsdkPlugin implements FlutterPlugin, MethodCallHandler {
37
+public class FlutterZsdkPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.RequestPermissionsResultListener {
13 38
   /// The MethodChannel that will the communication between Flutter and native Android
14 39
   ///
15 40
   /// This local reference serves to register the plugin with the Flutter Engine and unregister it
16 41
   /// when the Flutter Engine is detached from the Activity
17 42
   private MethodChannel channel;
43
+  private Context context;
44
+  private Activity activity;
45
+  private ArrayList<BluetoothPrinter> discoveredPrinterList = new ArrayList<BluetoothPrinter>();
46
+  public static final String bluetoothDiscoveryEventChannel = "id.kalanusa.flutter_zsdk.channel_events/bluetooth_discovery";
47
+  private EventChannel.EventSink attachEvent;
48
+  private Handler handler;
49
+  private DartExecutor dartExecutor;
50
+  private BinaryMessenger binaryMessenger;
18 51
 
19 52
   @Override
20 53
   public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
21 54
     channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_zsdk");
22 55
     channel.setMethodCallHandler(this);
56
+    context = flutterPluginBinding.getApplicationContext();
57
+    dartExecutor = Objects.requireNonNull(flutterPluginBinding.getFlutterEngine()).getDartExecutor();
58
+    binaryMessenger = dartExecutor.getBinaryMessenger();
59
+
23 60
   }
24 61
 
25 62
   @Override
26
-  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
63
+  public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
64
+    activity = binding.getActivity();
65
+    binding.addRequestPermissionsResultListener(this);
27 66
 
67
+    new EventChannel(binaryMessenger, bluetoothDiscoveryEventChannel).setStreamHandler(new BluetoothDiscoveryHandler(context, activity));
68
+  }
69
+
70
+  @Override
71
+  public void onDetachedFromActivityForConfigChanges() {
72
+
73
+  }
74
+
75
+  @Override
76
+  public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
77
+
78
+  }
79
+
80
+  @Override
81
+  public void onDetachedFromActivity() {
82
+    activity = null;
83
+  }
84
+
85
+  @Override
86
+  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
87
+    switch(call.method) {
88
+      case "findBluetoothPrinters":
89
+        findBluetoothPrinters();
90
+      case "getPlatformVersion":
91
+        Log.w("from native", "TEST");
92
+    }
28 93
   }
29 94
 
30 95
   @Override
31 96
   public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
32 97
     channel.setMethodCallHandler(null);
33 98
   }
99
+
100
+  @Override
101
+  public boolean onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
102
+    if (requestCode == 1001) { // Check if it'sthe request we initiated
103
+      if (grantResults.length > 0) {
104
+        boolean bluetoothScanGranted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
105
+        boolean bluetoothConnectGranted = grantResults[1] == PackageManager.PERMISSION_GRANTED;
106
+        boolean locationGranted = grantResults[2] == PackageManager.PERMISSION_GRANTED;
107
+
108
+        if (bluetoothScanGranted && bluetoothConnectGranted && locationGranted) {
109
+          // All permissions granted, proceed with Bluetooth operations
110
+          // ... your Bluetooth code here ...
111
+          this.startDiscoverBluetoothPrinters();
112
+        } else {
113
+          System.out.println("Permission Denied, please allow permission to discover bluetooth devices");
114
+          // Handle denied permissions
115
+          // You might display a messageto the user or disable Bluetooth features
116
+          // ... your logic for handling denied permissions ...
117
+        }
118
+      }
119
+    }
120
+
121
+    return true;
122
+  }
123
+
124
+
125
+//  Bluetooth discovery
126
+  public void findBluetoothPrinters() {
127
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
128
+      if (context.checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED ||
129
+              context.checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED ||
130
+              context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
131
+
132
+        activity.requestPermissions(new String[]{
133
+                Manifest.permission.BLUETOOTH_SCAN,
134
+                Manifest.permission.BLUETOOTH_CONNECT,
135
+                Manifest.permission.ACCESS_FINE_LOCATION
136
+        }, 1001);
137
+      } else {
138
+        this.startDiscoverBluetoothPrinters();
139
+      }
140
+    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
141
+      if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
142
+        activity.requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1002);
143
+      } else {
144
+        this.startDiscoverBluetoothPrinters();
145
+      }
146
+    }
147
+  }
148
+
149
+  public void startDiscoverBluetoothPrinters() {
150
+    new EventChannel(dartExecutor, bluetoothDiscoveryEventChannel).setStreamHandler(
151
+            new EventChannel.StreamHandler() {
152
+
153
+              @Override
154
+              public void onListen(Object arguments, EventChannel.EventSink events) {
155
+                Log.w("From native", "Adding Listener");
156
+                attachEvent = events;
157
+                handler = new Handler();
158
+                startDiscoverBluetoothPrintersRunnable.run();
159
+              }
160
+
161
+              @Override
162
+              public void onCancel(Object arguments) {
163
+                Log.w("From native", "Cancelling Listener");
164
+                handler.removeCallbacks(startDiscoverBluetoothPrintersRunnable);
165
+                handler = null;
166
+                attachEvent = null;
167
+                System.out.println("StreamHandler - onCanceled: ");              }
168
+            }
169
+    );
170
+  }
171
+
172
+//  public void startDiscoverBluetoothPrintersRunnable() {
173
+//    new Thread(new Runnable() {
174
+//      @Override
175
+//      public void run() {
176
+//
177
+//    });
178
+//  }
179
+//
180
+  private final Runnable startDiscoverBluetoothPrintersRunnable = new Runnable() {
181
+    @Override
182
+    public void run() {
183
+      ArrayList<BluetoothPrinter> tempDiscoveredPrinterList = new ArrayList<BluetoothPrinter>();
184
+      DiscoveryHandler discoveryHandler = new DiscoveryHandler() {
185
+        @Override
186
+        public void foundPrinter(DiscoveredPrinter discoveredPrinter) {
187
+          Map<String, String> discoveredPrinterDataMap = discoveredPrinter.getDiscoveryDataMap();
188
+          System.out.println("Found printer "  + discoveredPrinterDataMap.get("FRIENDLY_NAME"));
189
+
190
+          BluetoothPrinter bluetoothPrinter = new BluetoothPrinter(discoveredPrinterDataMap.get("FRIENDLY_NAME"), discoveredPrinterDataMap.get("MAC_ADDRESS"));
191
+          tempDiscoveredPrinterList.add(bluetoothPrinter);
192
+          attachEvent.success(tempDiscoveredPrinterList);
193
+        }
194
+
195
+        @Override
196
+        public void discoveryFinished() {
197
+          // return value here, this is asynchronous
198
+          Log.w("From Native", "Discovery Success, found "+ tempDiscoveredPrinterList.size() + " Printer(s)");
199
+          attachEvent.endOfStream();
200
+        }
201
+
202
+        @Override
203
+        public void discoveryError(String s) {
204
+          System.out.println("Error when discovering bluetooth device");
205
+          System.out.println("message:" + s);
206
+        }
207
+      };
208
+
209
+      try {
210
+        BluetoothDiscoverer.findPrinters(context, discoveryHandler);
211
+      } catch (ConnectionException e) {
212
+        System.out.println("Error when finding printers");
213
+//            throw new RuntimeException(e);
214
+      }
215
+    }
216
+  };
34 217
 }

+ 118 - 0
android/src/main/java/id/kalanusa/flutter_zsdk/bluetoothdiscoveryhandler/BluetoothDiscoveryHandler.java

@@ -0,0 +1,118 @@
1
+package id.kalanusa.flutter_zsdk.bluetoothdiscoveryhandler;
2
+
3
+import android.app.Activity;
4
+import android.content.Context;
5
+
6
+import com.zebra.sdk.comm.ConnectionException;
7
+import com.zebra.sdk.printer.discovery.BluetoothDiscoverer;
8
+import com.zebra.sdk.printer.discovery.DiscoveredPrinter;
9
+import com.zebra.sdk.printer.discovery.DiscoveryHandler;
10
+
11
+import java.util.ArrayList;
12
+import java.util.List;
13
+import java.util.Map;
14
+
15
+import io.flutter.Log;
16
+import io.flutter.plugin.common.EventChannel;
17
+
18
+public class BluetoothDiscoveryHandler implements EventChannel.StreamHandler {
19
+
20
+    public BluetoothDiscoveryHandler(Context context, Activity activity) {
21
+        this.context = context;
22
+        this.activity = activity;
23
+    }
24
+
25
+    private Context context;
26
+    private Activity activity;
27
+    private EventChannel.EventSink eventSink;
28
+//    ArrayList<BluetoothPrinter> discoveredBluetoothPrinterList = new ArrayList<BluetoothPrinter>();
29
+    List<Map<String, String>> discoveredBluetoothPrinters = new ArrayList<>();
30
+
31
+    @Override
32
+    public void onListen(Object arguments, EventChannel.EventSink events) {
33
+        eventSink = events;
34
+        eventSink.success("SOS");
35
+
36
+
37
+        startDiscoverBluetoothPrinters();
38
+    }
39
+
40
+    @Override
41
+    public void onCancel(Object arguments) {
42
+        Log.w("From Native", "Canceling Discovery Bluetooth Subscription");
43
+        discoveredBluetoothPrinters.clear();
44
+        eventSink.success("EOS");
45
+        eventSink = null;
46
+//        context = null;
47
+//        activity = null;
48
+    }
49
+
50
+    private void startDiscoverBluetoothPrinters() {
51
+        new Thread(new Runnable() {
52
+            @Override
53
+            public void run() {
54
+                DiscoveryHandler discoveryHandler = new DiscoveryHandler() {
55
+                    @Override
56
+                    public void foundPrinter(DiscoveredPrinter discoveredPrinter) {
57
+                        Map<String, String> discoveredPrinterDataMap = discoveredPrinter.getDiscoveryDataMap();
58
+                        Log.w("From Native (Android)","Found printer " + discoveredPrinterDataMap.get("FRIENDLY_NAME"));
59
+
60
+                        BluetoothPrinter discoveredBluetoothPrinter = new BluetoothPrinter(discoveredPrinterDataMap.get("FRIENDLY_NAME"), discoveredPrinterDataMap.get("MAC_ADDRESS"));
61
+                        discoveredBluetoothPrinters.add(discoveredBluetoothPrinter.toMap());
62
+
63
+//                        List<Map<String, String>> convertedDiscoveredPinterList = new ArrayList<>();
64
+//
65
+//                        for(BluetoothPrinter bluetoothPrinter : discoveredBluetoothPrinterList) {
66
+//                            convertedDiscoveredPinterList.add(bluetoothPrinter.toMap());
67
+//                        }
68
+
69
+                        activity.runOnUiThread(new Runnable() {
70
+                            @Override
71
+                            public void run() {
72
+                                eventSink.success(discoveredBluetoothPrinters);
73
+                            }
74
+                        });
75
+                    }
76
+
77
+                    @Override
78
+                    public void discoveryFinished() {
79
+                        Log.w("From Native (Android)", "Bluetooth Discovery Finished");
80
+
81
+                        activity.runOnUiThread(new Runnable() {
82
+                            @Override
83
+                            public void run() {
84
+                                eventSink.success(discoveredBluetoothPrinters);
85
+                                eventSink.success("EOS");
86
+                            }
87
+                        });
88
+                    }
89
+
90
+                    @Override
91
+                    public void discoveryError(String s) {
92
+                        Log.w("From Native (Android)", "Bluetooth Discovery Error" + s);
93
+                        activity.runOnUiThread(new Runnable() {
94
+                            @Override
95
+                            public void run() {
96
+                                eventSink.error("DISCOVERY_ERROR", "Something went wrong, message" + s, s);
97
+                            }
98
+                        });
99
+                    }
100
+                };
101
+
102
+                try {
103
+                    BluetoothDiscoverer.findPrinters(context, discoveryHandler);
104
+                    Log.w("From Native (Android)", "Bluetooth Discovery Started");
105
+                } catch (ConnectionException e) {
106
+                    Log.w("From Native (Android)", "Bluetooth Discovery Error <ConnectionException>");
107
+                    activity.runOnUiThread(new Runnable() {
108
+                        @Override
109
+                        public void run() {
110
+                            eventSink.error("DISCOVERY_ERROR", "Something went wrong", e);
111
+                        }
112
+                    });
113
+                }
114
+
115
+            }
116
+        }).start();
117
+    }
118
+}

+ 22 - 0
android/src/main/java/id/kalanusa/flutter_zsdk/bluetoothdiscoveryhandler/BluetoothPrinter.java

@@ -0,0 +1,22 @@
1
+package id.kalanusa.flutter_zsdk.bluetoothdiscoveryhandler;
2
+
3
+import java.util.HashMap;
4
+import java.util.Map;
5
+
6
+public class BluetoothPrinter {
7
+    String friendlyName = "unknown";
8
+    String macAddress = "unknown";
9
+
10
+    public BluetoothPrinter(String friendlyName, String macAddress) {
11
+        this.friendlyName = friendlyName;
12
+        this.macAddress = macAddress;
13
+    }
14
+
15
+    public Map<String, String> toMap() {
16
+        Map<String, String> map = new HashMap<>();
17
+        map.put("friendlyName", friendlyName);
18
+        map.put("macAddress", macAddress);
19
+
20
+        return map;
21
+    }
22
+}

+ 18 - 1
example/android/app/build.gradle

@@ -37,10 +37,11 @@ android {
37 37
         applicationId "id.kalanusa.flutter_zsdk_example"
38 38
         // You can update the following values to match your application needs.
39 39
         // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
40
-        minSdkVersion flutter.minSdkVersion
40
+        minSdkVersion 21
41 41
         targetSdkVersion flutter.targetSdkVersion
42 42
         versionCode flutterVersionCode.toInteger()
43 43
         versionName flutterVersionName
44
+        multiDexEnabled true 
44 45
     }
45 46
 
46 47
     buildTypes {
@@ -50,6 +51,22 @@ android {
50 51
             signingConfig signingConfigs.debug
51 52
         }
52 53
     }
54
+
55
+    packagingOptions {
56
+        exclude 'META-INF/LICENSE.txt'
57
+        exclude 'META-INF/NOTICE.txt'
58
+        exclude 'META-INF/NOTICE'
59
+        exclude 'META-INF/LICENSE'
60
+        exclude 'META-INF/DEPENDENCIES'
61
+    }
62
+
63
+    // lintOptions {
64
+    //     checkReleaseBuilds false
65
+    // }
66
+}
67
+
68
+dependencies {
69
+    implementation("androidx.multidex:multidex:2.0.1") // Add the multidex library
53 70
 }
54 71
 
55 72
 flutter {

+ 1 - 0
example/android/gradle.properties

@@ -1,3 +1,4 @@
1 1
 org.gradle.jvmargs=-Xmx4G
2 2
 android.useAndroidX=true
3 3
 android.enableJetifier=true
4
+android.jetifier.ignorelist=jackson-core-2.17.2.jar

+ 2 - 1
example/android/gradle/wrapper/gradle-wrapper.properties

@@ -1,5 +1,6 @@
1 1
 distributionBase=GRADLE_USER_HOME
2 2
 distributionPath=wrapper/dists
3
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
4
+networkTimeout=10000
3 5
 zipStoreBase=GRADLE_USER_HOME
4 6
 zipStorePath=wrapper/dists
5
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip

+ 1 - 1
example/android/settings.gradle

@@ -19,7 +19,7 @@ pluginManagement {
19 19
 
20 20
 plugins {
21 21
     id "dev.flutter.flutter-plugin-loader" version "1.0.0"
22
-    id "com.android.application" version "7.3.0" apply false
22
+    id "com.android.application" version "8.1.3" apply false
23 23
     id "org.jetbrains.kotlin.android" version "1.7.10" apply false
24 24
 }
25 25
 

+ 89 - 8
example/lib/main.dart

@@ -1,11 +1,16 @@
1
+import 'dart:developer';
2
+import 'dart:math';
3
+
1 4
 import 'package:flutter/material.dart';
2 5
 import 'dart:async';
3 6
 
4 7
 import 'package:flutter/services.dart';
5 8
 import 'package:flutter_zsdk/flutter_zsdk.dart';
9
+import 'package:flutter_zsdk/src/models/flutter_zsdk_exception.dart';
10
+import 'package:flutter_zsdk_example/models/bluetooth_printer.dart';
6 11
 
7 12
 void main() {
8
-  runApp(const MyApp());
13
+  runApp(const MaterialApp(home: MyApp()));
9 14
 }
10 15
 
11 16
 class MyApp extends StatefulWidget {
@@ -16,6 +21,8 @@ class MyApp extends StatefulWidget {
16 21
 }
17 22
 
18 23
 class _MyAppState extends State<MyApp> {
24
+  bool _isDiscovering = false;
25
+  List<BluetoothPrinter> _discoveredBluetoothPrinters = [];
19 26
   String _platformVersion = 'Unknown';
20 27
   final _flutterZsdkPlugin = FlutterZsdk();
21 28
 
@@ -47,17 +54,91 @@ class _MyAppState extends State<MyApp> {
47 54
     });
48 55
   }
49 56
 
57
+  StreamSubscription? _bluetoothPrinterSubscription;
58
+
59
+  void showSnackBar(String message) {
60
+    ScaffoldMessenger.of(context).showSnackBar(
61
+      SnackBar(content: Text(message))
62
+    );
63
+  }
64
+
65
+  Future<void> discoverBluetoothDevices() async {
66
+    try {
67
+      Stream<dynamic> stream = await _flutterZsdkPlugin.findBluetoothPrinters();
68
+      _bluetoothPrinterSubscription = stream.listen((event) {
69
+        print(event);
70
+
71
+        if (event == 'SOS') {
72
+          _isDiscovering = true;
73
+
74
+        }
75
+
76
+        if (event is List) {
77
+          _discoveredBluetoothPrinters.clear();
78
+          for (var printer in event) {
79
+            BluetoothPrinter bluetoothPrinter = BluetoothPrinter.fromMap(printer);
80
+
81
+            _discoveredBluetoothPrinters.add(bluetoothPrinter);
82
+          }
83
+
84
+        }
85
+
86
+        if (event == 'EOS') {
87
+          _isDiscovering = false;
88
+          showSnackBar('Bluetooth discovery finished, found ${_discoveredBluetoothPrinters.length} printers');
89
+          _bluetoothPrinterSubscription?.cancel();
90
+        }
91
+
92
+        setState(() {});
93
+      });
94
+    
95
+    } on PlatformException catch (e) {
96
+      inspect(e);
97
+      showSnackBar(e.message.toString());
98
+
99
+    } on FlutterZsdkException catch (e) { 
100
+      inspect(e);
101
+      showSnackBar(e.message);
102
+
103
+    } catch (e) {
104
+      inspect(e);
105
+      showSnackBar('Unexpected error while discovering bluetooth printers');
106
+
107
+    }
108
+  }
109
+
50 110
   @override
51 111
   Widget build(BuildContext context) {
52
-    return MaterialApp(
53
-      home: Scaffold(
54
-        appBar: AppBar(
55
-          title: const Text('Plugin example app'),
56
-        ),
57
-        body: Center(
58
-          child: Text('Running on: $_platformVersion\n'),
112
+    return Scaffold(
113
+      appBar: AppBar(
114
+        title: const Text('Plugin example app'),
115
+      ),
116
+      body: Center(
117
+        child: Column(
118
+          crossAxisAlignment: CrossAxisAlignment.center,
119
+          mainAxisAlignment: MainAxisAlignment.center,
120
+          children: [
121
+            Text('Running on: $_platformVersion\n'),
122
+            SizedBox(height: 20),
123
+        
124
+            ElevatedButton(
125
+              child: Text(_isDiscovering ? 'Discovering bluetooth printers...' : 'Discover nearby bluetooth printers'),
126
+              onPressed: _isDiscovering ? null : () async {
127
+                discoverBluetoothDevices();
128
+              },
129
+            ),
130
+            SizedBox(height: 20),
131
+
132
+            for (var printer in _discoveredBluetoothPrinters) ... [
133
+              Text(printer.friendlyName),
134
+              Text(printer.macAddress),
135
+              SizedBox(height: 10)
136
+            ]
137
+          ] 
138
+          
59 139
         ),
60 140
       ),
141
+
61 142
     );
62 143
   }
63 144
 }

+ 8 - 0
example/lib/models/bluetooth_printer.dart

@@ -0,0 +1,8 @@
1
+class BluetoothPrinter {
2
+  String friendlyName;
3
+  String macAddress;
4
+
5
+  BluetoothPrinter(this.friendlyName, this.macAddress);
6
+
7
+  BluetoothPrinter.fromMap(Map<Object?, Object?> map) : friendlyName = (map['friendlyName'] as String?) ?? 'unknown', macAddress = (map['macAddress'] as String?) ?? 'unknown';
8
+}

+ 70 - 1
example/pubspec.lock

@@ -70,6 +70,14 @@ packages:
70 70
     description: flutter
71 71
     source: sdk
72 72
     version: "0.0.0"
73
+  flutter_blue_plus:
74
+    dependency: transitive
75
+    description:
76
+      name: flutter_blue_plus
77
+      sha256: d2ac9fac56c4b3b08eb68752380e2d45e64c61db629b70e61bb36c95cb65d431
78
+      url: "https://pub.dev"
79
+    source: hosted
80
+    version: "1.34.5"
73 81
   flutter_driver:
74 82
     dependency: transitive
75 83
     description: flutter
@@ -88,6 +96,11 @@ packages:
88 96
     description: flutter
89 97
     source: sdk
90 98
     version: "0.0.0"
99
+  flutter_web_plugins:
100
+    dependency: transitive
101
+    description: flutter
102
+    source: sdk
103
+    version: "0.0.0"
91 104
   flutter_zsdk:
92 105
     dependency: "direct main"
93 106
     description:
@@ -169,6 +182,54 @@ packages:
169 182
       url: "https://pub.dev"
170 183
     source: hosted
171 184
     version: "1.9.0"
185
+  permission_handler:
186
+    dependency: transitive
187
+    description:
188
+      name: permission_handler
189
+      sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
190
+      url: "https://pub.dev"
191
+    source: hosted
192
+    version: "11.3.1"
193
+  permission_handler_android:
194
+    dependency: transitive
195
+    description:
196
+      name: permission_handler_android
197
+      sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1"
198
+      url: "https://pub.dev"
199
+    source: hosted
200
+    version: "12.0.13"
201
+  permission_handler_apple:
202
+    dependency: transitive
203
+    description:
204
+      name: permission_handler_apple
205
+      sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0
206
+      url: "https://pub.dev"
207
+    source: hosted
208
+    version: "9.4.5"
209
+  permission_handler_html:
210
+    dependency: transitive
211
+    description:
212
+      name: permission_handler_html
213
+      sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
214
+      url: "https://pub.dev"
215
+    source: hosted
216
+    version: "0.1.3+5"
217
+  permission_handler_platform_interface:
218
+    dependency: transitive
219
+    description:
220
+      name: permission_handler_platform_interface
221
+      sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9
222
+      url: "https://pub.dev"
223
+    source: hosted
224
+    version: "4.2.3"
225
+  permission_handler_windows:
226
+    dependency: transitive
227
+    description:
228
+      name: permission_handler_windows
229
+      sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
230
+      url: "https://pub.dev"
231
+    source: hosted
232
+    version: "0.2.1"
172 233
   platform:
173 234
     dependency: transitive
174 235
     description:
@@ -270,6 +331,14 @@ packages:
270 331
       url: "https://pub.dev"
271 332
     source: hosted
272 333
     version: "13.0.0"
334
+  web:
335
+    dependency: transitive
336
+    description:
337
+      name: web
338
+      sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
339
+      url: "https://pub.dev"
340
+    source: hosted
341
+    version: "0.5.1"
273 342
   webdriver:
274 343
     dependency: transitive
275 344
     description:
@@ -280,4 +349,4 @@ packages:
280 349
     version: "3.0.3"
281 350
 sdks:
282 351
   dart: ">=3.3.1 <4.0.0"
283
-  flutter: ">=3.3.0"
352
+  flutter: ">=3.16.0"

+ 3 - 0
example/windows/flutter/generated_plugin_registrant.cc

@@ -7,8 +7,11 @@
7 7
 #include "generated_plugin_registrant.h"
8 8
 
9 9
 #include <flutter_zsdk/flutter_zsdk_plugin_c_api.h>
10
+#include <permission_handler_windows/permission_handler_windows_plugin.h>
10 11
 
11 12
 void RegisterPlugins(flutter::PluginRegistry* registry) {
12 13
   FlutterZsdkPluginCApiRegisterWithRegistrar(
13 14
       registry->GetRegistrarForPlugin("FlutterZsdkPluginCApi"));
15
+  PermissionHandlerWindowsPluginRegisterWithRegistrar(
16
+      registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
14 17
 }

+ 1 - 0
example/windows/flutter/generated_plugins.cmake

@@ -4,6 +4,7 @@
4 4
 
5 5
 list(APPEND FLUTTER_PLUGIN_LIST
6 6
   flutter_zsdk
7
+  permission_handler_windows
7 8
 )
8 9
 
9 10
 list(APPEND FLUTTER_FFI_PLUGIN_LIST

+ 7 - 1
lib/flutter_zsdk.dart

@@ -1,8 +1,14 @@
1 1
 
2
-import 'flutter_zsdk_platform_interface.dart';
2
+import 'package:flutter_zsdk/src/models/bluetooth_printer.dart';
3
+
4
+import 'src/flutter_zsdk_platform_interface.dart';
3 5
 
4 6
 class FlutterZsdk {
5 7
   Future<String?> getPlatformVersion() {
6 8
     return FlutterZsdkPlatform.instance.getPlatformVersion();
7 9
   }
10
+
11
+  Future<Stream<dynamic>> findBluetoothPrinters() {
12
+    return FlutterZsdkPlatform.instance.findBluetoothPrinters();
13
+  }
8 14
 }

+ 0 - 17
lib/flutter_zsdk_method_channel.dart

@@ -1,17 +0,0 @@
1
-import 'package:flutter/foundation.dart';
2
-import 'package:flutter/services.dart';
3
-
4
-import 'flutter_zsdk_platform_interface.dart';
5
-
6
-/// An implementation of [FlutterZsdkPlatform] that uses method channels.
7
-class MethodChannelFlutterZsdk extends FlutterZsdkPlatform {
8
-  /// The method channel used to interact with the native platform.
9
-  @visibleForTesting
10
-  final methodChannel = const MethodChannel('flutter_zsdk');
11
-
12
-  @override
13
-  Future<String?> getPlatformVersion() async {
14
-    final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
15
-    return version;
16
-  }
17
-}

+ 84 - 0
lib/src/flutter_zsdk_method_channel.dart

@@ -0,0 +1,84 @@
1
+import 'dart:async';
2
+import 'dart:io';
3
+
4
+import 'package:flutter/foundation.dart';
5
+import 'package:flutter/services.dart';
6
+import 'package:flutter_blue_plus/flutter_blue_plus.dart';
7
+import 'package:flutter_zsdk/src/models/bluetooth_printer.dart';
8
+import 'package:flutter_zsdk/src/models/flutter_zsdk_exception.dart';
9
+import 'package:permission_handler/permission_handler.dart';
10
+
11
+import 'flutter_zsdk_platform_interface.dart';
12
+
13
+/// An implementation of [FlutterZsdkPlatform] that uses method channels.
14
+class MethodChannelFlutterZsdk extends FlutterZsdkPlatform {
15
+  static const bluetoothDiscoveryEventChannel = EventChannel('id.kalanusa.flutter_zsdk.channel_events/bluetooth_discovery');
16
+
17
+  /// The method channel used to interact with the native platform.
18
+  @visibleForTesting
19
+  final methodChannel = const MethodChannel('flutter_zsdk');
20
+
21
+  @override
22
+  Future<String?> getPlatformVersion() async {
23
+    final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
24
+    return version;
25
+  }
26
+
27
+  @override
28
+  Future<Stream<dynamic>> findBluetoothPrinters() async {
29
+    // methodChannel.invokeMethod("findBluetoothPrinters");
30
+    // final bluetoothPrinters = await methodChannel.invokeMethod<List<BluetoothPrinter>>('findBluetoothPrinters');
31
+    // print(bluetoothPrinters);
32
+    // return bluetoothPrinters ?? [];
33
+    
34
+    // StreamSubscription streamSubscription = bluetoothDiscoveryEventChannel.receiveBroadcastStream().listen((event) {
35
+    //   print(event);
36
+    // });
37
+    if (!await FlutterBluePlus.isSupported) {
38
+      throw FlutterZsdkException("Bluetooth is not supported on this device.");
39
+    }
40
+
41
+
42
+    if (await FlutterBluePlus.adapterState.first != BluetoothAdapterState.on) {
43
+      if (Platform.isAndroid) {
44
+        try {
45
+          await FlutterBluePlus.turnOn();
46
+        } on FlutterBluePlusException catch (e) {
47
+          throw FlutterZsdkException("Failed to turn on bluetooth: ${e.description}");
48
+        }
49
+      } else {
50
+        throw FlutterZsdkException("Bluetooth is not turned on on this device.");
51
+      }
52
+    }
53
+
54
+    var bluetoothPermissionStatus = await Permission.bluetooth.status;
55
+    var bluetoothConnectPermissionStatus = await Permission.bluetoothConnect.status;
56
+    var bluetoothScanPermissionStatus = await Permission.bluetoothScan.status;
57
+    var locationPermissionStatus = await Permission.location.status;
58
+
59
+    if (!bluetoothPermissionStatus.isGranted || !bluetoothConnectPermissionStatus.isGranted || !bluetoothScanPermissionStatus.isGranted || !locationPermissionStatus.isGranted) {
60
+      Map<Permission, PermissionStatus> permissionStatuses = await [
61
+        Permission.bluetooth, 
62
+        Permission.bluetoothConnect, 
63
+        Permission.bluetoothScan, 
64
+        Permission.location
65
+      ].request();
66
+
67
+      print(permissionStatuses[Permission.bluetooth]);
68
+      print(permissionStatuses[Permission.bluetoothConnect]);
69
+      print(permissionStatuses[Permission.bluetoothScan]);
70
+      print(permissionStatuses[Permission.location]);
71
+
72
+      if (permissionStatuses[Permission.bluetooth] == PermissionStatus.granted && permissionStatuses[Permission.bluetoothConnect] == PermissionStatus.granted && permissionStatuses[Permission.bluetoothScan] == PermissionStatus.granted && permissionStatuses[Permission.location] == PermissionStatus.granted) {
73
+        return bluetoothDiscoveryEventChannel.receiveBroadcastStream();
74
+      } else {
75
+        // throw Exception("Permissions not granted, please allow permission to discover bluetooth printers.");
76
+        throw FlutterZsdkException("Permissions not granted, please allow permission to discover bluetooth printers.");
77
+      }
78
+
79
+    } else {
80
+        return bluetoothDiscoveryEventChannel.receiveBroadcastStream();
81
+    }
82
+    
83
+  }
84
+}

+ 5 - 0
lib/flutter_zsdk_platform_interface.dart

@@ -1,3 +1,4 @@
1
+import 'package:flutter_zsdk/src/models/bluetooth_printer.dart';
1 2
 import 'package:plugin_platform_interface/plugin_platform_interface.dart';
2 3
 
3 4
 import 'flutter_zsdk_method_channel.dart';
@@ -26,4 +27,8 @@ abstract class FlutterZsdkPlatform extends PlatformInterface {
26 27
   Future<String?> getPlatformVersion() {
27 28
     throw UnimplementedError('platformVersion() has not been implemented.');
28 29
   }
30
+
31
+  Future<Stream<dynamic>> findBluetoothPrinters() {
32
+    throw UnimplementedError('findBluetoothPrinters() has not been implemented.');
33
+  }
29 34
 }

+ 6 - 0
lib/src/models/bluetooth_printer.dart

@@ -0,0 +1,6 @@
1
+class BluetoothPrinter {
2
+  String friendlyName;
3
+  String macAddress;
4
+
5
+  BluetoothPrinter(this.friendlyName, this.macAddress);
6
+}

+ 10 - 0
lib/src/models/flutter_zsdk_exception.dart

@@ -0,0 +1,10 @@
1
+class FlutterZsdkException implements Exception {
2
+  String message;
3
+
4
+  FlutterZsdkException(this.message);
5
+
6
+  @override
7
+  String toString() {
8
+    return message;
9
+  }
10
+}

+ 2 - 0
pubspec.yaml

@@ -11,6 +11,8 @@ dependencies:
11 11
   flutter:
12 12
     sdk: flutter
13 13
   plugin_platform_interface: ^2.0.2
14
+  permission_handler: ^11.3.1
15
+  flutter_blue_plus: ^1.34.5
14 16
 
15 17
 dev_dependencies:
16 18
   flutter_test:

+ 1 - 1
test/flutter_zsdk_method_channel_test.dart

@@ -1,6 +1,6 @@
1 1
 import 'package:flutter/services.dart';
2 2
 import 'package:flutter_test/flutter_test.dart';
3
-import 'package:flutter_zsdk/flutter_zsdk_method_channel.dart';
3
+import 'package:flutter_zsdk/src/flutter_zsdk_method_channel.dart';
4 4
 
5 5
 void main() {
6 6
   TestWidgetsFlutterBinding.ensureInitialized();

+ 6 - 2
test/flutter_zsdk_test.dart

@@ -1,7 +1,8 @@
1 1
 import 'package:flutter_test/flutter_test.dart';
2 2
 import 'package:flutter_zsdk/flutter_zsdk.dart';
3
-import 'package:flutter_zsdk/flutter_zsdk_platform_interface.dart';
4
-import 'package:flutter_zsdk/flutter_zsdk_method_channel.dart';
3
+import 'package:flutter_zsdk/src/flutter_zsdk_platform_interface.dart';
4
+import 'package:flutter_zsdk/src/flutter_zsdk_method_channel.dart';
5
+import 'package:flutter_zsdk/src/models/bluetooth_printer.dart';
5 6
 import 'package:plugin_platform_interface/plugin_platform_interface.dart';
6 7
 
7 8
 class MockFlutterZsdkPlatform
@@ -10,6 +11,9 @@ class MockFlutterZsdkPlatform
10 11
 
11 12
   @override
12 13
   Future<String?> getPlatformVersion() => Future.value('42');
14
+
15
+  @override
16
+  Future<List<BluetoothPrinter>> findBluetoothPrinters() => Future.value([]);
13 17
 }
14 18
 
15 19
 void main() {