3 Commits 75373d1f04 ... e1f339d0da

Author SHA1 Message Date
  Raihan Rizal e1f339d0da feat: close connection 5 months ago
  Raihan Rizal b1563220f6 feat: check connection status 5 months ago
  Raihan Rizal 811c8e580a feat: bluetoothConnectionInsecure 5 months ago

+ 20 - 124
android/src/main/java/id/kalanusa/flutter_zsdk/FlutterZsdkPlugin.java

@@ -18,6 +18,8 @@ import java.util.ArrayList;
18 18
 import java.util.Map;
19 19
 import java.util.Objects;
20 20
 
21
+import id.kalanusa.flutter_zsdk.bluetoothconnectionhandler.BluetoothConnectionHandler;
22
+import id.kalanusa.flutter_zsdk.bluetoothconnectionhandler.BluetoothConnectionHolder;
21 23
 import id.kalanusa.flutter_zsdk.bluetoothdiscoveryhandler.BluetoothDiscoveryHandler;
22 24
 import id.kalanusa.flutter_zsdk.bluetoothdiscoveryhandler.BluetoothPrinter;
23 25
 import io.flutter.Log;
@@ -34,7 +36,7 @@ import io.flutter.plugin.common.MethodChannel.Result;
34 36
 import io.flutter.plugin.common.PluginRegistry;
35 37
 
36 38
 /** FlutterZsdkPlugin */
37
-public class FlutterZsdkPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.RequestPermissionsResultListener {
39
+public class FlutterZsdkPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
38 40
   /// The MethodChannel that will the communication between Flutter and native Android
39 41
   ///
40 42
   /// This local reference serves to register the plugin with the Flutter Engine and unregister it
@@ -49,6 +51,11 @@ public class FlutterZsdkPlugin implements FlutterPlugin, MethodCallHandler, Acti
49 51
   private DartExecutor dartExecutor;
50 52
   private BinaryMessenger binaryMessenger;
51 53
 
54
+  public BluetoothConnectionHolder bluetoothConnectionHolder;
55
+
56
+  public BluetoothConnectionHandler bluetoothConnectionHandler;
57
+
58
+
52 59
   @Override
53 60
   public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
54 61
     channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_zsdk");
@@ -62,7 +69,6 @@ public class FlutterZsdkPlugin implements FlutterPlugin, MethodCallHandler, Acti
62 69
   @Override
63 70
   public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
64 71
     activity = binding.getActivity();
65
-    binding.addRequestPermissionsResultListener(this);
66 72
 
67 73
     new EventChannel(binaryMessenger, bluetoothDiscoveryEventChannel).setStreamHandler(new BluetoothDiscoveryHandler(context, activity));
68 74
   }
@@ -84,134 +90,24 @@ public class FlutterZsdkPlugin implements FlutterPlugin, MethodCallHandler, Acti
84 90
 
85 91
   @Override
86 92
   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
-    }
93
-  }
93
+    Log.w("From Native (Android)", "ini di FLutterZsdkPlugin");
94 94
 
95
-  @Override
96
-  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
97
-    channel.setMethodCallHandler(null);
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 ...
95
+    switch(call.method) {
96
+      case "bluetoothOpenConnection":
97
+      case "isConnected":
98
+      case "bluetoothCloseConnection":
99
+        if (bluetoothConnectionHandler == null) {
100
+          bluetoothConnectionHandler = new BluetoothConnectionHandler(bluetoothConnectionHolder);
117 101
         }
118
-      }
119
-    }
120
-
121
-    return true;
122
-  }
123 102
 
103
+        bluetoothConnectionHandler.handle(call, result);
104
+        break;
124 105
 
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 106
     }
147 107
   }
148 108
 
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
-    );
109
+  @Override
110
+  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
111
+    channel.setMethodCallHandler(null);
170 112
   }
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
-  };
217 113
 }

+ 91 - 0
android/src/main/java/id/kalanusa/flutter_zsdk/bluetoothconnectionhandler/BluetoothConnectionHandler.java

@@ -0,0 +1,91 @@
1
+package id.kalanusa.flutter_zsdk.bluetoothconnectionhandler;
2
+
3
+import android.os.Looper;
4
+
5
+import androidx.annotation.NonNull;
6
+
7
+import com.zebra.sdk.comm.BluetoothConnectionInsecure;
8
+import com.zebra.sdk.comm.Connection;
9
+import com.zebra.sdk.comm.ConnectionException;
10
+
11
+import io.flutter.Log;
12
+import io.flutter.plugin.common.MethodCall;
13
+import io.flutter.plugin.common.MethodChannel;
14
+
15
+public class BluetoothConnectionHandler {
16
+    BluetoothConnectionHolder bluetoothConnectionHolder;
17
+    String macAddress;
18
+
19
+    public BluetoothConnectionHandler(BluetoothConnectionHolder bluetoothConnectionHolder) {
20
+        this.bluetoothConnectionHolder = bluetoothConnectionHolder;
21
+    }
22
+
23
+    public void handle(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
24
+
25
+        switch (call.method) {
26
+            case "bluetoothOpenConnection":
27
+                if (bluetoothConnectionHolder == null) {
28
+                    bluetoothConnectionHolder = new BluetoothConnectionHolder(new BluetoothConnectionInsecure(call.argument("macAddress")));
29
+                }
30
+
31
+                openConnection(result);
32
+                break;
33
+
34
+            case "isConnected":
35
+                checkConnection(result);
36
+                break;
37
+
38
+            case "bluetoothCloseConnection":
39
+                closeConnection(result);
40
+                break;
41
+        }
42
+    }
43
+
44
+    public void openConnection(@NonNull MethodChannel.Result result) {
45
+        new Thread(new Runnable() {
46
+            @Override
47
+            public void run() {
48
+                try {
49
+                    Looper.prepare();
50
+                    bluetoothConnectionHolder.connection.open();
51
+                    Looper.myLooper().quit();
52
+                    result.success(null);
53
+                    Log.w("From Native (Android)", "open connection finished");
54
+                } catch (ConnectionException e) {
55
+                    Log.w("From Native (Android)", "Connection Exception threw " + e);
56
+                    result.error("OPEN_CONNECTION_ERROR", "Cant Connect to printers", e.getMessage());
57
+                }
58
+            }
59
+        }).start();
60
+    }
61
+
62
+    public void checkConnection(@NonNull MethodChannel.Result result) {
63
+        new Thread(new Runnable() {
64
+            @Override
65
+            public void run() {
66
+                boolean isConnected = bluetoothConnectionHolder.connection.isConnected();
67
+
68
+                result.success(isConnected);
69
+
70
+                Log.w("From Native (Android)", "isConnected: " + isConnected);
71
+            }
72
+        }).start();
73
+    }
74
+
75
+    public void closeConnection(@NonNull MethodChannel.Result result) {
76
+        new Thread(new Runnable() {
77
+            @Override
78
+            public void run() {
79
+                try {
80
+                    bluetoothConnectionHolder.connection.close();
81
+                    result.success(null);
82
+                } catch (ConnectionException e) {
83
+                    Log.w("From Native (Android)", "something went wrong when closing connection");
84
+                    result.error("CLOSE_CONNECTION_ERROR", "Something went wrong while closing the connection", e.getMessage());
85
+                }
86
+            }
87
+        }).start();
88
+    }
89
+
90
+
91
+}

+ 17 - 0
android/src/main/java/id/kalanusa/flutter_zsdk/bluetoothconnectionhandler/BluetoothConnectionHolder.java

@@ -0,0 +1,17 @@
1
+package id.kalanusa.flutter_zsdk.bluetoothconnectionhandler;
2
+
3
+import androidx.annotation.NonNull;
4
+
5
+import com.zebra.sdk.comm.Connection;
6
+
7
+public class BluetoothConnectionHolder {
8
+    public Connection connection;
9
+
10
+    public BluetoothConnectionHolder(@NonNull Connection connection) {
11
+        this.connection = connection;
12
+    }
13
+
14
+    public void updateConnection(@NonNull Connection connection) {
15
+        this.connection = connection;
16
+    }
17
+}

+ 104 - 26
example/lib/main.dart

@@ -23,36 +23,38 @@ class MyApp extends StatefulWidget {
23 23
 class _MyAppState extends State<MyApp> {
24 24
   bool _isDiscovering = false;
25 25
   List<BluetoothPrinter> _discoveredBluetoothPrinters = [];
26
-  String _platformVersion = 'Unknown';
26
+  String? _connectedPrinter;
27 27
   final _flutterZsdkPlugin = FlutterZsdk();
28
+  String? _selectedBluetoothPrinterMacAddress;
29
+  bool _disconnecting = false;
28 30
 
29 31
   @override
30 32
   void initState() {
31 33
     super.initState();
32
-    initPlatformState();
34
+    // initPlatformState();
33 35
   }
34 36
 
35 37
   // Platform messages are asynchronous, so we initialize in an async method.
36
-  Future<void> initPlatformState() async {
37
-    String platformVersion;
38
-    // Platform messages may fail, so we use a try/catch PlatformException.
39
-    // We also handle the message potentially returning null.
40
-    try {
41
-      platformVersion =
42
-          await _flutterZsdkPlugin.getPlatformVersion() ?? 'Unknown platform version';
43
-    } on PlatformException {
44
-      platformVersion = 'Failed to get platform version.';
45
-    }
46
-
47
-    // If the widget was removed from the tree while the asynchronous platform
48
-    // message was in flight, we want to discard the reply rather than calling
49
-    // setState to update our non-existent appearance.
50
-    if (!mounted) return;
51
-
52
-    setState(() {
53
-      _platformVersion = platformVersion;
54
-    });
55
-  }
38
+  // Future<void> initPlatformState() async {
39
+  //   String platformVersion;
40
+  //   // Platform messages may fail, so we use a try/catch PlatformException.
41
+  //   // We also handle the message potentially returning null.
42
+  //   try {
43
+  //     platformVersion =
44
+  //         await _flutterZsdkPlugin.getPlatformVersion() ?? 'Unknown platform version';
45
+  //   } on PlatformException {
46
+  //     platformVersion = 'Failed to get platform version.';
47
+  //   }
48
+
49
+  //   // If the widget was removed from the tree while the asynchronous platform
50
+  //   // message was in flight, we want to discard the reply rather than calling
51
+  //   // setState to update our non-existent appearance.
52
+  //   if (!mounted) return;
53
+
54
+  //   setState(() {
55
+  //     _platformVersion = platformVersion;
56
+  //   });
57
+  // }
56 58
 
57 59
   StreamSubscription? _bluetoothPrinterSubscription;
58 60
 
@@ -107,6 +109,48 @@ class _MyAppState extends State<MyApp> {
107 109
     }
108 110
   }
109 111
 
112
+  Future<void> openConnection() async {
113
+    print('invoked openConnection from dart');
114
+    try {
115
+      await _flutterZsdkPlugin.openConnection(_selectedBluetoothPrinterMacAddress ?? ''); 
116
+      _connectedPrinter = _selectedBluetoothPrinterMacAddress ?? 'Unknown printer';
117
+      print('Connection opened successfully from dart');
118
+      
119
+    } on FlutterZsdkException catch (e) {
120
+      inspect(e);
121
+      showSnackBar(e.message);
122
+
123
+    } catch (e) {
124
+      inspect(e);
125
+      showSnackBar('Unexpected error while connecting to bluetooth printers');
126
+    }
127
+
128
+    setState(() {});
129
+  }
130
+
131
+  Future<void> closeConnection() async {
132
+    print('invoked closeConnection from dart');
133
+    try {
134
+      setState(() {
135
+        _disconnecting = true;
136
+      });
137
+      await _flutterZsdkPlugin.closeConnection(); 
138
+      _connectedPrinter = null;
139
+      _disconnecting = false;
140
+      print('Connection closed successfully from dart');
141
+      
142
+    } on FlutterZsdkException catch (e) {
143
+      inspect(e);
144
+      showSnackBar(e.message);
145
+
146
+    } catch (e) {
147
+      inspect(e);
148
+      showSnackBar('Unexpected error while disconnecting bluetooth printers');
149
+    }
150
+
151
+    setState(() {});
152
+  }
153
+
110 154
   @override
111 155
   Widget build(BuildContext context) {
112 156
     return Scaffold(
@@ -118,9 +162,32 @@ class _MyAppState extends State<MyApp> {
118 162
           crossAxisAlignment: CrossAxisAlignment.center,
119 163
           mainAxisAlignment: MainAxisAlignment.center,
120 164
           children: [
121
-            Text('Running on: $_platformVersion\n'),
165
+            Text('Connected printer: ${_connectedPrinter ?? "Unknown printer"}\n'),
166
+            Text('Selected printer Mac Address: $_selectedBluetoothPrinterMacAddress\n'),
122 167
             SizedBox(height: 20),
123 168
         
169
+            ElevatedButton(
170
+              child: Text('Connect to $_selectedBluetoothPrinterMacAddress'),
171
+              onPressed: _selectedBluetoothPrinterMacAddress == null || _connectedPrinter != null ? null : openConnection,
172
+            ),
173
+            SizedBox(height: 20),
174
+
175
+            ElevatedButton(
176
+              child: Text(_disconnecting ? 'Disconnecting...' : 'Disconnect from $_connectedPrinter'),
177
+              onPressed: _connectedPrinter == null || _disconnecting ? null : closeConnection,
178
+            ),
179
+            SizedBox(height: 20),
180
+
181
+            ElevatedButton(
182
+              child: Text('Check connection status'),
183
+              onPressed: () async {
184
+                bool isConnected =await _flutterZsdkPlugin.isConnected();
185
+              
186
+                print('isConnected from dart: $isConnected');
187
+              },
188
+            ),
189
+            SizedBox(height: 20),
190
+            
124 191
             ElevatedButton(
125 192
               child: Text(_isDiscovering ? 'Discovering bluetooth printers...' : 'Discover nearby bluetooth printers'),
126 193
               onPressed: _isDiscovering ? null : () async {
@@ -130,9 +197,20 @@ class _MyAppState extends State<MyApp> {
130 197
             SizedBox(height: 20),
131 198
 
132 199
             for (var printer in _discoveredBluetoothPrinters) ... [
133
-              Text(printer.friendlyName),
134
-              Text(printer.macAddress),
135
-              SizedBox(height: 10)
200
+              InkWell(
201
+                onTap: () {
202
+                  _selectedBluetoothPrinterMacAddress = printer.macAddress;
203
+                  setState(() {});
204
+                },
205
+                child: Column(
206
+                  mainAxisSize: MainAxisSize.min,
207
+                  children: [
208
+                    Text(printer.friendlyName),
209
+                    Text(printer.macAddress),
210
+                    SizedBox(height: 10)
211
+                  ],
212
+                ),
213
+              )
136 214
             ]
137 215
           ] 
138 216
           

+ 13 - 0
lib/flutter_zsdk.dart

@@ -11,4 +11,17 @@ class FlutterZsdk {
11 11
   Future<Stream<dynamic>> findBluetoothPrinters() {
12 12
     return FlutterZsdkPlatform.instance.findBluetoothPrinters();
13 13
   }
14
+
15
+  Future<void> openConnection(String macAddress) {
16
+    return FlutterZsdkPlatform.instance.openConnection(macAddress);
17
+  }
18
+
19
+  /// this api must not be called before FlutterZsdkPlugin.openConnection()
20
+  Future<bool> isConnected() {
21
+    return FlutterZsdkPlatform.instance.isConnected();
22
+  }
23
+
24
+  Future<void> closeConnection() {
25
+    return FlutterZsdkPlatform.instance.closeConnection();
26
+  }
14 27
 }

+ 32 - 0
lib/src/flutter_zsdk_method_channel.dart

@@ -1,5 +1,7 @@
1 1
 import 'dart:async';
2
+import 'dart:developer';
2 3
 import 'dart:io';
4
+import 'dart:math';
3 5
 
4 6
 import 'package:flutter/foundation.dart';
5 7
 import 'package:flutter/services.dart';
@@ -81,4 +83,34 @@ class MethodChannelFlutterZsdk extends FlutterZsdkPlatform {
81 83
     }
82 84
     
83 85
   }
86
+
87
+  @override
88
+  Future<void> openConnection(String macAddress) async {
89
+    try {
90
+      await methodChannel.invokeMethod('bluetoothOpenConnection', {'macAddress': macAddress});
91
+
92
+    } on PlatformException catch (e) {
93
+      inspect(e);
94
+      throw FlutterZsdkException("Failed to open connection to printer: ${e.details}");
95
+
96
+    } catch (e) {
97
+      throw FlutterZsdkException("Failed to open connection to printer: $e");
98
+    }
99
+  }
100
+
101
+  @override
102
+  Future<bool> isConnected() async {
103
+    bool isConnected = await methodChannel.invokeMethod('isConnected');
104
+
105
+    return isConnected;
106
+  }
107
+
108
+  @override
109
+  Future<void> closeConnection() async {
110
+    try {
111
+      await methodChannel.invokeMethod('bluetoothCloseConnection');
112
+    } catch (e) {
113
+      throw FlutterZsdkException("Failed to close connection to printer: $e");
114
+    }
115
+  }
84 116
 }

+ 12 - 0
lib/src/flutter_zsdk_platform_interface.dart

@@ -31,4 +31,16 @@ abstract class FlutterZsdkPlatform extends PlatformInterface {
31 31
   Future<Stream<dynamic>> findBluetoothPrinters() {
32 32
     throw UnimplementedError('findBluetoothPrinters() has not been implemented.');
33 33
   }
34
+
35
+  Future<void> openConnection(String macAddress) {
36
+    throw UnimplementedError('openConnection() has not been implemented.');
37
+  }
38
+
39
+  Future<bool> isConnected() {
40
+    throw UnimplementedError('isConnected() has not been implemented.');
41
+  }
42
+
43
+  Future<void> closeConnection() {
44
+    throw UnimplementedError('closeConnection() has not been implemented.');
45
+  }
34 46
 }