Browse Source

Merge branch 'zhou_dev'

* zhou_dev:
  去掉abiFilters
  abiFilters 添加 x86_64
  fix bug:  java.lang.UnsatisfiedLinkError: JNI_ERR returned from JNI_OnLoad in "/data/app/~~fQ1ReUB9uBx0IyUovshiQg==/com.szls.lszlgl-ozYAHLRC0AkdtBf_BeOaZg==/lib/arm64/libLabelPrinterSDK.so"
  支持精臣型号A8开头的打印机
  将连接的蓝牙设备信息传给服务器
  创建本地数据库并写入信息
  打印机对接完成
  集成数据库和打印机(未完结)
  添加数据库drift
  打印机打印的图片尺寸调整成适合70*80
  fix: 解决冲突缺少的括号
  蓝牙打印完成
  配对、连接、以及对应loading添加完成
  打印机蓝牙权限、搜索、连接功能
  开始扫描
  flutter使用的版本是3.19.4,对应升级gradle配置

# Conflicts:
#	lib/config/pics.dart
周素华 10 months ago
parent
commit
dcfa81de4e
60 changed files with 2880 additions and 28 deletions
  1. 24 0
      android/app/build.gradle
  2. BIN
      android/app/jniLibs/arm64-v8a/libConfigFileINI.so
  3. BIN
      android/app/jniLibs/arm64-v8a/libLabelPrinterSDK.so
  4. BIN
      android/app/jniLibs/arm64-v8a/libSimpleLogModule.so
  5. BIN
      android/app/jniLibs/arm64-v8a/libc++_shared.so
  6. BIN
      android/app/jniLibs/armeabi-v7a/libConfigFileINI.so
  7. BIN
      android/app/jniLibs/armeabi-v7a/libLabelPrinterSDK.so
  8. BIN
      android/app/jniLibs/armeabi-v7a/libSimpleLogModule.so
  9. BIN
      android/app/jniLibs/armeabi-v7a/libc++_shared.so
  10. BIN
      android/app/jniLibs/x86/libConfigFileINI.so
  11. BIN
      android/app/jniLibs/x86/libLabelPrinterSDK.so
  12. BIN
      android/app/jniLibs/x86/libSimpleLogModule.so
  13. BIN
      android/app/jniLibs/x86/libc++_shared.so
  14. BIN
      android/app/jniLibs/x86_64/libConfigFileINI.so
  15. BIN
      android/app/jniLibs/x86_64/libLabelPrinterSDK.so
  16. BIN
      android/app/jniLibs/x86_64/libSimpleLogModule.so
  17. BIN
      android/app/jniLibs/x86_64/libc++_shared.so
  18. BIN
      android/app/libs/3.2.2-release.aar
  19. BIN
      android/app/libs/LPAPI-2019-11-20-R.jar
  20. BIN
      android/app/libs/LabelPrinterJavaSDK.jar
  21. BIN
      android/app/libs/image-1.8.4.6.aar
  22. 17 2
      android/app/src/main/AndroidManifest.xml
  23. 21 0
      android/app/src/main/java/com/szls/lszlgl/MainActivity.java
  24. 24 0
      android/app/src/main/java/com/szls/lszlgl/app/MyApplication.java
  25. 472 0
      android/app/src/main/java/io/flutter/plugins/BluetoothPlugin.java
  26. 58 0
      android/app/src/main/java/io/flutter/plugins/bean/BlueDeviceInfo.java
  27. 85 0
      android/app/src/main/java/io/flutter/plugins/utils/BTPPrintUtil.java
  28. 31 0
      android/app/src/main/java/io/flutter/plugins/utils/BluetoothUtils.java
  29. 300 0
      android/app/src/main/java/io/flutter/plugins/utils/PrintUtil.java
  30. 0 13
      android/build.gradle
  31. 10 4
      android/settings.gradle
  32. BIN
      assets/images/print_ble.png
  33. BIN
      assets/images/search_ble.png
  34. 7 1
      lib/config/pics.dart
  35. 27 0
      lib/drfit/dao/device_info_table_dao.dart
  36. 8 0
      lib/drfit/dao/device_info_table_dao.g.dart
  37. 63 0
      lib/drfit/database.dart
  38. 657 0
      lib/drfit/database.g.dart
  39. 26 0
      lib/drfit/device_info_table.dart
  40. 28 0
      lib/drfit/model_factory.dart
  41. 8 0
      lib/main.dart
  42. 42 0
      lib/model/req/device_req.dart
  43. 27 0
      lib/model/req/device_req.g.dart
  44. 1 1
      lib/model/req/login_req.g.dart
  45. 5 5
      lib/model/req/sample_task_list_req.g.dart
  46. 7 0
      lib/network/api.dart
  47. 30 0
      lib/network/api.g.dart
  48. 1 0
      lib/network/my_api.dart
  49. 2 0
      lib/page/home/home_page.dart
  50. 9 1
      lib/page/login/login_page.dart
  51. 95 0
      lib/page/print/connect_print_page.dart
  52. 322 0
      lib/page/print/print_page.dart
  53. 12 1
      lib/page/sample_task/reap_sample_detail/reap_sample_task_page.dart
  54. 183 0
      lib/plugin/bluetooth_plugin.dart
  55. 19 0
      lib/router/my_router.dart
  56. 166 0
      lib/service/print_service.dart
  57. 12 0
      lib/service/user_service.dart
  58. 1 0
      lib/utils/file_utils.dart
  59. 75 0
      lib/utils/permission_utils.dart
  60. 5 0
      pubspec.yaml

+ 24 - 0
android/app/build.gradle

@@ -32,6 +32,12 @@ android {
32 32
         targetCompatibility JavaVersion.VERSION_1_8
33 33
     }
34 34
 
35
+    sourceSets {
36
+        main {
37
+            jniLibs.srcDirs = ['jniLibs']
38
+        }
39
+    }
40
+
35 41
     defaultConfig {
36 42
         applicationId "com.szls.lszlgl"
37 43
         // You can update the following values to match your application needs.
@@ -42,6 +48,8 @@ android {
42 48
         versionName flutterVersionName
43 49
     }
44 50
 
51
+    println "targetSdkVersion: ${flutter.targetSdkVersion}"
52
+
45 53
     // 签名配置
46 54
     signingConfigs {
47 55
         release {
@@ -60,11 +68,15 @@ android {
60 68
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
61 69
             signingConfig signingConfigs.release
62 70
             manifestPlaceholders = [APP_NAME: "国粮质检"]
71
+            shrinkResources false // 重要,打印机有so库,fix bug: https://github.com/flutter/flutter/issues/47527
72
+            minifyEnabled false // 重要,打印机有so库,fix bug: https://github.com/flutter/flutter/issues/47527
63 73
         }
64 74
         profile {
65 75
             signingConfig signingConfigs.release
66 76
             manifestPlaceholders = [APP_NAME: "国粮质检-测试"]
67 77
             applicationIdSuffix ".debug"
78
+            shrinkResources false // 重要,打印机有so库,fix bug: https://github.com/flutter/flutter/issues/47527
79
+            minifyEnabled false // 重要,打印机有so库,fix bug: https://github.com/flutter/flutter/issues/47527
68 80
         }
69 81
         debug {
70 82
             signingConfig signingConfigs.release
@@ -86,6 +98,18 @@ android {
86 98
     dependencies {
87 99
         implementation fileTree(include: ['*.jar'], dir: 'libs')
88 100
         implementation 'com.amap.api:location:5.6.0'
101
+
102
+        //打印库
103
+        implementation files('libs/3.2.2-release.aar')
104
+        //接⼊机型包含B50/B50W/B11/T6/T7/T8系列打印机需要引⼊该包,如不包含则可以不引⼊
105
+        implementation files('libs/LPAPI-2019-11-20-R.jar')
106
+        implementation files('libs/image-1.8.4.6.aar')
107
+
108
+        // btp打印机库
109
+        implementation files('libs/LabelPrinterJavaSDK.jar')
110
+
111
+        //权限库
112
+        implementation 'com.guolindev.permissionx:permissionx:1.7.1'
89 113
     }
90 114
 }
91 115
 

BIN
android/app/jniLibs/arm64-v8a/libConfigFileINI.so


BIN
android/app/jniLibs/arm64-v8a/libLabelPrinterSDK.so


BIN
android/app/jniLibs/arm64-v8a/libSimpleLogModule.so


BIN
android/app/jniLibs/arm64-v8a/libc++_shared.so


BIN
android/app/jniLibs/armeabi-v7a/libConfigFileINI.so


BIN
android/app/jniLibs/armeabi-v7a/libLabelPrinterSDK.so


BIN
android/app/jniLibs/armeabi-v7a/libSimpleLogModule.so


BIN
android/app/jniLibs/armeabi-v7a/libc++_shared.so


BIN
android/app/jniLibs/x86/libConfigFileINI.so


BIN
android/app/jniLibs/x86/libLabelPrinterSDK.so


BIN
android/app/jniLibs/x86/libSimpleLogModule.so


BIN
android/app/jniLibs/x86/libc++_shared.so


BIN
android/app/jniLibs/x86_64/libConfigFileINI.so


BIN
android/app/jniLibs/x86_64/libLabelPrinterSDK.so


BIN
android/app/jniLibs/x86_64/libSimpleLogModule.so


BIN
android/app/jniLibs/x86_64/libc++_shared.so


BIN
android/app/libs/3.2.2-release.aar


BIN
android/app/libs/LPAPI-2019-11-20-R.jar


BIN
android/app/libs/LabelPrinterJavaSDK.jar


BIN
android/app/libs/image-1.8.4.6.aar


+ 17 - 2
android/app/src/main/AndroidManifest.xml

@@ -1,4 +1,5 @@
1
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
1
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+    xmlns:tools="http://schemas.android.com/tools">
2 3
     <!--允许访问网络,必选权限-->
3 4
     <uses-permission android:name="android.permission.INTERNET" />
4 5
 
@@ -38,8 +39,22 @@
38 39
     <!--允许安装应用-->
39 40
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
40 41
 
42
+    <!-- 蓝⽛打印机权限 -->
43
+    <!-- 蓝⽛权限 -->
44
+    <uses-permission android:name="android.permission.BLUETOOTH" />
45
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
46
+    <!-- android 5以上 android 12以下,蓝⽛搜索需要位置权限 -->
47
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
48
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
49
+        />
50
+    <!--android 12 需要申请蓝⽛搜索权限、连接权限 ⽆需申请位置权限 -->
51
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
52
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" tools:targetApi="s" />
53
+
54
+
55
+
41 56
     <application
42
-        android:name="${applicationName}"
57
+        android:name=".app.MyApplication"
43 58
         android:icon="@drawable/ic_launcher"
44 59
         android:label="${APP_NAME}"
45 60
         android:requestLegacyExternalStorage="true">

+ 21 - 0
android/app/src/main/java/com/szls/lszlgl/MainActivity.java

@@ -1,6 +1,27 @@
1 1
 package com.szls.lszlgl;
2 2
 
3
+import android.util.Log;
4
+
5
+import androidx.annotation.NonNull;
6
+
3 7
 import io.flutter.embedding.android.FlutterActivity;
8
+import io.flutter.embedding.engine.FlutterEngine;
9
+import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister;
10
+import io.flutter.plugins.BluetoothPlugin;
4 11
 
5 12
 public class MainActivity extends FlutterActivity {
13
+
14
+    static {
15
+        System.loadLibrary("LabelPrinterSDK");
16
+    }
17
+    @Override
18
+    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
19
+        super.configureFlutterEngine(flutterEngine);
20
+
21
+        Log.e("AAAA", ">>> configureFlutterEngine");
22
+        GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
23
+
24
+        //插件实例的注册,就是自己写个类,然后实现Flutter提供的FlutterPlugin接口
25
+        flutterEngine.getPlugins().add(new BluetoothPlugin(this));
26
+    }
6 27
 }

+ 24 - 0
android/app/src/main/java/com/szls/lszlgl/app/MyApplication.java

@@ -0,0 +1,24 @@
1
+package com.szls.lszlgl.app;
2
+
3
+import android.app.Application;
4
+import android.content.Context;
5
+
6
+/**
7
+ * 自定义Application
8
+ *
9
+ * @author zhangbin
10
+ */
11
+public class MyApplication extends Application {
12
+    private static MyApplication instance;
13
+
14
+    @Override
15
+    public void onCreate() {
16
+
17
+        super.onCreate();
18
+        instance = this;
19
+    }
20
+
21
+    public static MyApplication getInstance() {
22
+        return instance;
23
+    }
24
+}

+ 472 - 0
android/app/src/main/java/io/flutter/plugins/BluetoothPlugin.java

@@ -0,0 +1,472 @@
1
+package io.flutter.plugins;
2
+
3
+import android.Manifest;
4
+import android.annotation.SuppressLint;
5
+import android.bluetooth.BluetoothAdapter;
6
+import android.bluetooth.BluetoothDevice;
7
+import android.content.BroadcastReceiver;
8
+import android.content.Context;
9
+import android.content.Intent;
10
+import android.content.IntentFilter;
11
+import android.content.SharedPreferences;
12
+import android.content.pm.PackageManager;
13
+import android.graphics.Bitmap;
14
+import android.graphics.BitmapFactory;
15
+import android.location.LocationManager;
16
+import android.os.Build;
17
+import android.text.TextUtils;
18
+import android.util.Log;
19
+import android.view.View;
20
+import android.widget.Toast;
21
+
22
+import androidx.annotation.NonNull;
23
+import androidx.core.app.ActivityCompat;
24
+import androidx.fragment.app.FragmentActivity;
25
+
26
+import com.gengcon.www.jcprintersdk.callback.PrintCallback;
27
+import com.permissionx.guolindev.PermissionX;
28
+import com.szls.lszlgl.MainActivity;
29
+
30
+import java.io.ByteArrayOutputStream;
31
+import java.nio.ByteBuffer;
32
+import java.security.Permission;
33
+import java.util.ArrayList;
34
+import java.util.HashMap;
35
+import java.util.List;
36
+import java.util.concurrent.ExecutorService;
37
+import java.util.concurrent.LinkedBlockingDeque;
38
+import java.util.concurrent.ThreadFactory;
39
+import java.util.concurrent.ThreadPoolExecutor;
40
+import java.util.concurrent.TimeUnit;
41
+
42
+import dev.fluttercommunity.plus.androidintent.IntentSender;
43
+import dev.fluttercommunity.plus.androidintent.MethodCallHandlerImpl;
44
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
45
+import io.flutter.embedding.engine.plugins.activity.ActivityAware;
46
+import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
47
+import io.flutter.plugin.common.BinaryMessenger;
48
+import io.flutter.plugin.common.EventChannel;
49
+import io.flutter.plugin.common.MethodCall;
50
+import io.flutter.plugin.common.MethodChannel;
51
+
52
+import io.flutter.plugin.common.EventChannel.EventSink;
53
+import io.flutter.plugin.common.EventChannel.StreamHandler;
54
+import io.flutter.plugins.bean.BlueDeviceInfo;
55
+import io.flutter.plugins.utils.BTPPrintUtil;
56
+import io.flutter.plugins.utils.BluetoothUtils;
57
+import io.flutter.plugins.utils.PrintUtil;
58
+
59
+public class BluetoothPlugin implements FlutterPlugin, MethodChannel.MethodCallHandler {
60
+
61
+    private String methodChannelName = "io.flutter.plugins/bluetooth";
62
+    private MethodChannel methodChannel;
63
+
64
+    private ExecutorService executorService;
65
+
66
+    private static final String USER_DEFINED = "自定义";
67
+    private static final String delimiterStr = "_NIMBOT_";
68
+
69
+    private String printNameStart = "";
70
+
71
+    OnDiscoveryDeviceStreamHandler onDiscoveryDeviceStreamHandler;
72
+    private EventChannel onReceiveDeviceChannel;
73
+
74
+    OnDeviceStateStreamHandler onDeviceStateStreamHandler;
75
+    private EventChannel onDeviceStateChannel;
76
+
77
+    private BlueDeviceInfo itemPosition;
78
+
79
+    Context _applicationContext;
80
+    private BluetoothAdapter mBluetoothAdapter;
81
+
82
+
83
+    public BluetoothPlugin(MainActivity activity) {
84
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
85
+//        _mainActivity = activity;
86
+
87
+        //注册广播
88
+        IntentFilter intentFilter = new IntentFilter();
89
+        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
90
+        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
91
+        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
92
+        intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
93
+        intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
94
+        intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
95
+        intentFilter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
96
+        Log.d("ble", "初始化:注册广播 ");
97
+        activity.registerReceiver(receiver, intentFilter);
98
+        Log.d("ble", "初始化: 注册完成");
99
+
100
+        //注册线程池
101
+        ThreadFactory threadFactory = runnable -> {
102
+            Thread thread = new Thread(runnable);
103
+            thread.setName("connect_activity_pool_%d");
104
+            return thread;
105
+        };
106
+        executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(1024), threadFactory, new ThreadPoolExecutor.AbortPolicy());
107
+
108
+
109
+    }
110
+
111
+    @Override
112
+    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
113
+        setupChannels(flutterPluginBinding.getBinaryMessenger(), flutterPluginBinding.getApplicationContext());
114
+    }
115
+
116
+    private void setupChannels(BinaryMessenger binaryMessenger, Context applicationContext) {
117
+
118
+        _applicationContext = applicationContext.getApplicationContext();
119
+
120
+        methodChannel = new MethodChannel(binaryMessenger, methodChannelName + "/methods");
121
+        methodChannel.setMethodCallHandler(this);
122
+
123
+        onReceiveDeviceChannel = new EventChannel(binaryMessenger, methodChannelName + "/onDiscoveryDevice");
124
+        onDiscoveryDeviceStreamHandler = new OnDiscoveryDeviceStreamHandler();
125
+        onReceiveDeviceChannel.setStreamHandler(onDiscoveryDeviceStreamHandler);
126
+
127
+        onDeviceStateChannel = new EventChannel(binaryMessenger, methodChannelName + "/onDeviceState");
128
+        onDeviceStateStreamHandler = new OnDeviceStateStreamHandler();
129
+        onDeviceStateChannel.setStreamHandler(onDeviceStateStreamHandler);
130
+    }
131
+
132
+    @Override
133
+    public void onDetachedFromEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
134
+        teardownChannels();
135
+    }
136
+
137
+    @Override
138
+    public void onMethodCall(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
139
+        switch (methodCall.method) {
140
+
141
+            case "isBleOpen":
142
+                Log.d("ble", "isSupportBle");
143
+                result.success(mBluetoothAdapter.isEnabled());
144
+                break;
145
+            case "isSDKIntGreaterOrEqual":
146
+                Log.d("ble", "isSDKIntGreaterOrEqual");
147
+                result.success(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S);
148
+                break;
149
+            case "startBluetoothDiscovery":
150
+                Log.d("ble", "startBluetoothDiscovery");
151
+                startBluetoothDiscovery(result);
152
+                break;
153
+            case "startBluetoothPair":
154
+                Log.d("ble", "startBluetoothPair");
155
+                startBluetoothPair(methodCall, result);
156
+                break;
157
+            case "startBluetoothConnect":
158
+                Log.d("ble", "startBluetoothConnect :" + methodCall.arguments);
159
+                startBluetoothConnect(methodCall, result);
160
+                break;
161
+            case "endBluetoothConnect":
162
+                Log.d("ble", "endBluetoothConnect :" + methodCall.arguments);
163
+                endBluetoothConnect(methodCall, result);
164
+                break;
165
+            case "hasBluetoothConnectDevice":
166
+                Log.d("ble", "hasBluetoothConnectDevice :" + methodCall.arguments);
167
+                hasBluetoothConnectDevice(methodCall, result);
168
+                break;
169
+            case "startBluetoothPrintBitMap":
170
+                Log.d("ble", "startBluetoothPrint :" + methodCall.arguments);
171
+                startBluetoothPrintBitMap(methodCall, result);
172
+                break;
173
+            case "startBluetoothPrintBarCode":
174
+                Log.d("ble", "startBluetoothPrintBarCode :" + methodCall.arguments);
175
+                startBluetoothPrintBarCode(methodCall, result);
176
+                break;
177
+
178
+
179
+            case "autoLogin":
180
+
181
+                break;
182
+            case "isLogined":
183
+
184
+                break;
185
+            case "currentAccount":
186
+                break;
187
+            case "logout":
188
+                break;
189
+            case "markAllMessagesRead":
190
+                break;
191
+            default: {
192
+                result.notImplemented();
193
+                break;
194
+            }
195
+        }
196
+    }
197
+
198
+    private void teardownChannels() {
199
+        methodChannel.setMethodCallHandler(null);
200
+        methodChannel = null;
201
+//        // 注销广播接收器
202
+//        if (mBluetoothAdapter != null && mBluetoothAdapter.isDiscovering()) {
203
+//            mBluetoothAdapter.cancelDiscovery();
204
+//        }
205
+//        requireActivity().unregisterReceiver(receiver);
206
+    }
207
+
208
+    void startBluetoothDiscovery(@NonNull MethodChannel.Result result) {
209
+
210
+        if (ActivityCompat.checkSelfPermission(_applicationContext, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
211
+
212
+            Log.d("ble", "checkSelfPermission false");
213
+            return;
214
+        }
215
+
216
+        if (mBluetoothAdapter.isDiscovering()) {
217
+
218
+            if (mBluetoothAdapter.cancelDiscovery()) {
219
+                executorService.execute(() -> {
220
+                    try {
221
+                        //取消后等待1s后再次搜索
222
+                        Thread.sleep(1000);
223
+                        mBluetoothAdapter.startDiscovery();
224
+                        Log.d("ble", "测试:开始搜索7");
225
+                    } catch (InterruptedException e) {
226
+                        Log.d("ble", "测试:开始搜索8");
227
+                        e.printStackTrace();
228
+                    }
229
+                });
230
+
231
+
232
+            }
233
+        } else {
234
+            Log.d("ble", "测试:开始搜索9");
235
+            mBluetoothAdapter.startDiscovery();
236
+            Log.d("ble", "测试:开始搜索10");
237
+        }
238
+    }
239
+
240
+    void startBluetoothPair(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
241
+
242
+        if (ActivityCompat.checkSelfPermission(_applicationContext, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
243
+            return;
244
+        }
245
+        if (mBluetoothAdapter.isDiscovering()) {
246
+            mBluetoothAdapter.cancelDiscovery();
247
+        }
248
+
249
+        String deviceInfo = methodCall.arguments.toString();
250
+        String[] deviceInfoList = deviceInfo.split(delimiterStr);
251
+        for (int i = 0; i < deviceInfoList.length; i++) {
252
+            Log.d("ble", i + ":" + deviceInfoList[i]);
253
+        }
254
+        Log.d("ble", "startBluetoothPair : ---" + deviceInfo + "----0:" + deviceInfoList[0]);
255
+        itemPosition = new BlueDeviceInfo(deviceInfoList[0], deviceInfoList[1], Integer.parseInt(deviceInfoList[2]));
256
+        BluetoothDevice bluetoothDevice = mBluetoothAdapter.getRemoteDevice(itemPosition.getDeviceHardwareAddress());
257
+
258
+        executorService.submit(() -> {
259
+            Log.d("ble", "配对: 开始");
260
+            boolean returnValue = false;
261
+            try {
262
+                returnValue = BluetoothUtils.createBond(bluetoothDevice);
263
+            } catch (Exception e) {
264
+                Log.d("ble", "闪退日志" + e.getMessage());
265
+            }
266
+            Log.d("ble", "配对: 进行中:" + returnValue);
267
+        });
268
+    }
269
+
270
+    void startBluetoothConnect(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
271
+
272
+        if (ActivityCompat.checkSelfPermission(_applicationContext, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
273
+            return;
274
+        }
275
+        if (mBluetoothAdapter.isDiscovering()) {
276
+            mBluetoothAdapter.cancelDiscovery();
277
+        }
278
+
279
+        String deviceInfo = methodCall.arguments.toString();
280
+        String[] deviceInfoList = deviceInfo.split(delimiterStr);
281
+        for (int i = 0; i < deviceInfoList.length; i++) {
282
+            Log.d("ble", i + ":" + deviceInfoList[i]);
283
+        }
284
+        Log.d("ble", "startBluetoothConnect : ---" + deviceInfo + "----0:" + deviceInfoList[0]);
285
+        itemPosition = new BlueDeviceInfo(deviceInfoList[0], deviceInfoList[1], Integer.parseInt(deviceInfoList[2]));
286
+        BluetoothDevice bluetoothDevice = mBluetoothAdapter.getRemoteDevice(itemPosition.getDeviceHardwareAddress());
287
+
288
+        executorService.submit(() -> {
289
+            BlueDeviceInfo blueDeviceInfo = new BlueDeviceInfo(bluetoothDevice.getName(), bluetoothDevice.getAddress(), itemPosition.getConnectState());
290
+            PrintUtil.setConnectedType(-1);
291
+            int connectResult = 1;
292
+            if (blueDeviceInfo.getDeviceName().contains("BTP")) {
293
+                connectResult = BTPPrintUtil.connectBluetoothPrinter(blueDeviceInfo.getDeviceHardwareAddress());
294
+            } else {
295
+                connectResult = PrintUtil.connectBluetoothPrinter(blueDeviceInfo.getDeviceHardwareAddress());
296
+            }
297
+            Log.d("ble", "测试:连接结果 " + connectResult); // 0是成功
298
+            result.success(connectResult);
299
+        });
300
+    }
301
+
302
+    void endBluetoothConnect(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
303
+        PrintUtil.close();
304
+        result.success(0);
305
+    }
306
+
307
+    void hasBluetoothConnectDevice(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
308
+        if (PrintUtil.isConnection() == 0 && PrintUtil.getConnectedType() == 0) {
309
+            Log.d("ble", "测试:配对状态改变:判断连接状态 2");
310
+            result.success(true);
311
+        } else {
312
+            Log.d("ble", "测试:配对状态改变:判断连接状态4 ");
313
+            result.success(false);
314
+        }
315
+    }
316
+
317
+    void startBluetoothPrintBitMap(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
318
+
319
+        byte[] imageByte = (byte[]) methodCall.arguments;
320
+
321
+        Log.d("ble", "imageByte:" + imageByte);
322
+        Bitmap bitmap = Bytes2Bimap(imageByte);
323
+
324
+        BluetoothDevice bluetoothDevice = mBluetoothAdapter.getRemoteDevice(itemPosition.getDeviceHardwareAddress());
325
+        if (ActivityCompat.checkSelfPermission(_applicationContext, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
326
+            return;
327
+        }
328
+        BlueDeviceInfo blueDeviceInfo = new BlueDeviceInfo(bluetoothDevice.getName(), bluetoothDevice.getAddress(), itemPosition.getConnectState());
329
+        if(blueDeviceInfo.getDeviceName().contains("BTP")) {
330
+            BTPPrintUtil.printBitMap(bitmap, blueDeviceInfo.getDeviceName(), result);
331
+        } else {
332
+            PrintUtil.printBitMap(bitmap, result);
333
+        }
334
+    }
335
+
336
+    void startBluetoothPrintBarCode(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
337
+
338
+        byte[] imageByte = (byte[]) methodCall.arguments;
339
+
340
+        Log.d("ble", "imageByte:" + imageByte);
341
+        Bitmap bitmap = Bytes2Bimap(imageByte);
342
+
343
+        BluetoothDevice bluetoothDevice = mBluetoothAdapter.getRemoteDevice(itemPosition.getDeviceHardwareAddress());
344
+        if (ActivityCompat.checkSelfPermission(_applicationContext, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
345
+            return;
346
+        }
347
+        BlueDeviceInfo blueDeviceInfo = new BlueDeviceInfo(bluetoothDevice.getName(), bluetoothDevice.getAddress(), itemPosition.getConnectState());
348
+        if(blueDeviceInfo.getDeviceName().contains("BTP")) {
349
+            BTPPrintUtil.printBitMap(bitmap, blueDeviceInfo.getDeviceName(), result);
350
+        } else {
351
+            PrintUtil.printBitMap(bitmap, result);
352
+        }
353
+    }
354
+
355
+    public Bitmap Bytes2Bimap(byte[] b) {
356
+        if (b.length != 0) {
357
+            return BitmapFactory.decodeByteArray(b, 0, b.length);
358
+        } else {
359
+            return null;
360
+        }
361
+    }
362
+
363
+    private final BroadcastReceiver receiver = new BroadcastReceiver() {
364
+
365
+        @SuppressLint("MissingPermission")
366
+        @Override
367
+        public void onReceive(Context context, Intent intent) {
368
+            String action = intent.getAction();
369
+            //蓝牙发现
370
+            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
371
+                Log.v("ble", "测试:搜索中");
372
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
373
+
374
+                if (device != null) {
375
+                    @SuppressLint("MissingPermission") String deviceName = device.getName();
376
+                    String deviceHardwareAddress = device.getAddress();
377
+                    @SuppressLint("MissingPermission") int deviceStatus = device.getBondState();
378
+
379
+
380
+                    @SuppressLint("MissingPermission") boolean supportBluetoothType = device.getType() == BluetoothDevice.DEVICE_TYPE_CLASSIC || device.getType() == BluetoothDevice.DEVICE_TYPE_DUAL;
381
+                    boolean supportPrintName;
382
+
383
+
384
+                    if (USER_DEFINED.equals(printNameStart)) {
385
+                        printNameStart = "";
386
+                    }
387
+
388
+                    if (TextUtils.isEmpty(printNameStart)) {
389
+                        supportPrintName = deviceName != null;
390
+                    } else {
391
+                        supportPrintName = deviceName != null && deviceName.startsWith(printNameStart);
392
+                    }
393
+
394
+                    if (supportBluetoothType && supportPrintName) {
395
+
396
+                        Log.d("ble", "测试:打印机名称- " + deviceName + ",设备地址:" + deviceHardwareAddress + ",设备类型:" + device.getType() + ", 设备状态:"+deviceStatus);
397
+                        Log.d("ble",  ":" + "onDiscoveryDeviceStreamHandler" +  (onDiscoveryDeviceStreamHandler != null) + "onDiscoveryDeviceStreamHandler.sink != null:" + (onDiscoveryDeviceStreamHandler.sink));
398
+                        if (onDiscoveryDeviceStreamHandler != null && onDiscoveryDeviceStreamHandler.sink != null) {
399
+                            onDiscoveryDeviceStreamHandler.sink.success(deviceName+delimiterStr+deviceHardwareAddress+delimiterStr+deviceStatus);
400
+                        }
401
+                    }
402
+                }
403
+            } else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
404
+                Log.v("ble", "测试:开始搜索");
405
+                Log.d("ble",  ":" + "onDeviceStateStreamHandler" +  (onDeviceStateStreamHandler != null) + "onDeviceStateStreamHandler.sink != null:" + (onDeviceStateStreamHandler.sink));
406
+                if (onDeviceStateStreamHandler != null && onDeviceStateStreamHandler.sink != null) {
407
+                    onDeviceStateStreamHandler.sink.success("scanStart");
408
+                }
409
+            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
410
+                Log.v("ble", "测试:搜索结束");
411
+//                bind.spinKit.setVisibility(View.GONE);
412
+                Log.d("ble",  ":" + "onDeviceStateStreamHandler" +  (onDeviceStateStreamHandler != null) + "onDeviceStateStreamHandler.sink != null:" + (onDeviceStateStreamHandler.sink));
413
+                if (onDeviceStateStreamHandler != null && onDeviceStateStreamHandler.sink != null) {
414
+                    onDeviceStateStreamHandler.sink.success("scanEnd");
415
+                }
416
+            } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
417
+                int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
418
+                Log.d("ble", "测试:配对状态改变:0 " + state);
419
+                if(itemPosition != null) {
420
+                    itemPosition.setConnectState(state);
421
+                    if (onDiscoveryDeviceStreamHandler != null && onDiscoveryDeviceStreamHandler.sink != null) {
422
+                        onDiscoveryDeviceStreamHandler.sink.success(itemPosition.getDeviceName()+delimiterStr+itemPosition.getDeviceHardwareAddress()+delimiterStr+itemPosition.getConnectState());
423
+                    }
424
+                }
425
+            } else if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) {
426
+
427
+                if (onDeviceStateStreamHandler != null && onDeviceStateStreamHandler.sink != null) {
428
+                    onDeviceStateStreamHandler.sink.success("pairEnd");
429
+                }
430
+            }
431
+        }
432
+    };
433
+
434
+
435
+}
436
+
437
+
438
+class OnDiscoveryDeviceStreamHandler implements EventChannel.StreamHandler {
439
+
440
+    public EventChannel.EventSink sink;
441
+
442
+    @Override
443
+    public void onListen(Object o, EventChannel.EventSink eventSink) {
444
+
445
+        Log.d("ble", "OnDiscoveryDeviceStreamHandler: onListen");
446
+        sink = eventSink;
447
+    }
448
+    @Override
449
+    public void onCancel(Object o) {
450
+
451
+        Log.e("zhousuhua","OnReceiveDataStreamHandler=== onCancel");
452
+        sink = null;
453
+    }
454
+}
455
+
456
+class OnDeviceStateStreamHandler implements EventChannel.StreamHandler {
457
+
458
+    public EventChannel.EventSink sink;
459
+
460
+    @Override
461
+    public void onListen(Object o, EventChannel.EventSink eventSink) {
462
+
463
+        Log.d("ble", "OnDeviceStateStreamHandler: onListen");
464
+        sink = eventSink;
465
+    }
466
+    @Override
467
+    public void onCancel(Object o) {
468
+
469
+        Log.e("zhousuhua","OnReceiveDataStreamHandler=== onCancel");
470
+        sink = null;
471
+    }
472
+}

+ 58 - 0
android/app/src/main/java/io/flutter/plugins/bean/BlueDeviceInfo.java

@@ -0,0 +1,58 @@
1
+package io.flutter.plugins.bean;
2
+
3
+import androidx.annotation.Nullable;
4
+
5
+public class BlueDeviceInfo {
6
+
7
+
8
+    String deviceName;
9
+    String deviceHardwareAddress;
10
+    int connectState;
11
+
12
+    public BlueDeviceInfo(String deviceName, String deviceHardwareAddress, int connectState) {
13
+        this.deviceName = deviceName;
14
+        this.deviceHardwareAddress = deviceHardwareAddress;
15
+        this.connectState = connectState;
16
+    }
17
+
18
+    public String getDeviceName() {
19
+        return deviceName;
20
+    }
21
+
22
+    public void setDeviceName(String deviceName) {
23
+        this.deviceName = deviceName;
24
+    }
25
+
26
+    public String getDeviceHardwareAddress() {
27
+        return deviceHardwareAddress;
28
+    }
29
+
30
+    public void setDeviceHardwareAddress(String deviceHardwareAddress) {
31
+        this.deviceHardwareAddress = deviceHardwareAddress;
32
+    }
33
+
34
+    public int getConnectState() {
35
+        return connectState;
36
+    }
37
+
38
+    public void setConnectState(int connectState) {
39
+        this.connectState = connectState;
40
+    }
41
+
42
+
43
+    @Override
44
+    public int hashCode() {
45
+        return deviceName.hashCode();
46
+    }
47
+
48
+    @Override
49
+    public boolean equals(@Nullable Object obj) {
50
+        BlueDeviceInfo blueDeviceInfo = (BlueDeviceInfo) obj;
51
+        if (blueDeviceInfo != null) {
52
+            return deviceName.equals(blueDeviceInfo.deviceName);
53
+        }
54
+
55
+        return false;
56
+
57
+    }
58
+}

+ 85 - 0
android/app/src/main/java/io/flutter/plugins/utils/BTPPrintUtil.java

@@ -0,0 +1,85 @@
1
+package io.flutter.plugins.utils;
2
+
3
+import android.graphics.Bitmap;
4
+import android.graphics.BitmapFactory;
5
+import android.util.Log;
6
+
7
+import androidx.annotation.NonNull;
8
+
9
+import com.snbc.sdk.LabelPrinter;
10
+
11
+import java.io.ByteArrayOutputStream;
12
+import java.nio.ByteBuffer;
13
+
14
+import io.flutter.plugin.common.MethodChannel;
15
+
16
+public class BTPPrintUtil {
17
+
18
+
19
+    /**
20
+     * 单例实例,使用 volatile 保证多线程可见性和有序性
21
+     */
22
+    private static volatile LabelPrinter labelPrinter;
23
+
24
+    private static int nLang = 2;
25
+
26
+    /**
27
+     * 获取 LabelPrinter 单例实例
28
+     *
29
+     * @return LabelPrinter 实例
30
+     */
31
+    public static LabelPrinter getInstance() {
32
+        // 双重检查锁定以确保只初始化一次实例
33
+        if (labelPrinter == null) {
34
+            synchronized (BTPPrintUtil.class) {
35
+                if (labelPrinter == null) {
36
+                    labelPrinter = new LabelPrinter();
37
+                }
38
+            }
39
+        }
40
+
41
+        return labelPrinter;
42
+    }
43
+
44
+    /**
45
+     * 通过打印机mac地址进行蓝牙连接开启打印机(同步)
46
+     *
47
+     * @param address 打印机地址
48
+     * @return 成功与否
49
+     */
50
+    public static int connectBluetoothPrinter(String address) {
51
+//        return 1;
52
+        // 获取单例实例以确保线程安全
53
+        return getInstance().ConnectPrinter(7, address, 4);
54
+    }
55
+
56
+    public static void printBitMap(Bitmap bitmap, String deviceName, @NonNull MethodChannel.Result result) {
57
+
58
+        int x = 0;
59
+        if(deviceName.contains("BTP-UP321")) {
60
+            x = 0;
61
+        } else if(deviceName.contains("BTP-P398PLUS")) {
62
+            x = 40;
63
+        }
64
+        int bytes = bitmap.getByteCount();
65
+        ByteBuffer buf = ByteBuffer.allocate(bytes);
66
+        bitmap.copyPixelsToBuffer(buf);
67
+        ByteArrayOutputStream LionData=new ByteArrayOutputStream();
68
+        bitmap.compress(Bitmap.CompressFormat.PNG, 100, LionData);
69
+
70
+        getInstance().SetLabelSize(560,640);
71
+        int code = getInstance().PrintImageData(x, 0, LionData.toByteArray());
72
+        getInstance().PrintLabel(1,1);
73
+        result.success(code == 0 ? true : false);
74
+    }
75
+
76
+    public  static void printBarCodeWithText(String barCode, String text, @NonNull MethodChannel.Result result) {
77
+
78
+        getInstance().SetLabelSize(560,640);
79
+        labelPrinter.PrintBarcodeQR(80, 20,  0, "SHXM2024007404", 'H', 18, 2);
80
+        labelPrinter.PrintText(100, 500, "8", "SHXM2024007404", 0, 7, 3, 0);
81
+        int code = getInstance().PrintLabel(1,1);
82
+        result.success(code == 0 ? true : false);
83
+    }
84
+
85
+}

+ 31 - 0
android/app/src/main/java/io/flutter/plugins/utils/BluetoothUtils.java

@@ -0,0 +1,31 @@
1
+package io.flutter.plugins.utils;
2
+
3
+import android.bluetooth.BluetoothDevice;
4
+
5
+import java.lang.reflect.Method;
6
+
7
+/**
8
+ * 蓝牙工具类,用于处理蓝牙设备配对等操作。
9
+ * @author zhangbin
10
+ */
11
+public class BluetoothUtils {
12
+
13
+    /**
14
+     * 与设备配对
15
+     *
16
+     * @param btDevice 要配对的蓝牙设备
17
+     * @return 配对是否成功
18
+     * @throws Exception 反射调用可能引发异常
19
+     */
20
+    public static boolean createBond(BluetoothDevice btDevice) throws Exception {
21
+        // 获取 BluetoothDevice 类的类对象
22
+        Class<?> btClass = BluetoothDevice.class;
23
+
24
+        // 获取 createBond 方法的引用
25
+        Method createBondMethod = btClass.getMethod("createBond");
26
+        // 调用 createBond 方法,返回配对结果
27
+        return (Boolean) createBondMethod.invoke(btDevice);
28
+    }
29
+
30
+
31
+}

+ 300 - 0
android/app/src/main/java/io/flutter/plugins/utils/PrintUtil.java

@@ -0,0 +1,300 @@
1
+package io.flutter.plugins.utils;
2
+
3
+
4
+import android.app.Application;
5
+import android.content.res.AssetManager;
6
+import android.graphics.Bitmap;
7
+import android.graphics.BitmapFactory;
8
+import android.graphics.Canvas;
9
+import android.graphics.Color;
10
+import android.graphics.Paint;
11
+import android.util.Log;
12
+import android.widget.Toast;
13
+
14
+import androidx.annotation.NonNull;
15
+
16
+import com.gengcon.www.jcprintersdk.JCPrintApi;
17
+import com.gengcon.www.jcprintersdk.callback.Callback;
18
+import com.gengcon.www.jcprintersdk.callback.PrintCallback;
19
+import com.szls.lszlgl.app.MyApplication;
20
+
21
+import java.io.File;
22
+import java.io.InputStream;
23
+import java.util.ArrayList;
24
+import java.util.HashMap;
25
+import java.util.List;
26
+
27
+import io.flutter.plugin.common.MethodChannel;
28
+
29
+/**
30
+ * 打印工具类
31
+ *
32
+ * @author zhangbin
33
+ */
34
+public class PrintUtil {
35
+    private static final String TAG = "PrintUtil";
36
+    private static int mConnectedType = -1;
37
+    /**
38
+     * 单例实例,使用 volatile 保证多线程可见性和有序性
39
+     */
40
+    private static volatile JCPrintApi api;
41
+
42
+    /**
43
+     * 回调接口,用于处理打印机状态变化事件
44
+     */
45
+    private static final Callback CALLBACK = new Callback() {
46
+        private static final String TAG = "PrintUtil";
47
+
48
+        /**
49
+         * 连接成功回调
50
+         *
51
+         * @param address 设备地址,蓝牙为蓝牙 MAC 地址,WIFI 为 IP 地址
52
+         * @param type   连接类型,0 表示蓝牙连接,1 表示 WIFI 连接
53
+         */
54
+        @Override
55
+        public void onConnectSuccess(String address, int type) {
56
+            mConnectedType = type;
57
+        }
58
+
59
+        /**
60
+         * 断开连接回调
61
+         * 当设备断开连接时,将调用此方法。
62
+         */
63
+        @Override
64
+        public void onDisConnect() {
65
+            mConnectedType = -1;
66
+        }
67
+
68
+        /**
69
+         * 电量变化回调
70
+         * 当设备电量发生变化时,将调用此方法。
71
+         *
72
+         * @param powerLevel 电量等级,取值范围为 1 到 4,代表有 1 到 4 格电,满电是 4 格
73
+         */
74
+        @Override
75
+        public void onElectricityChange(int powerLevel) {
76
+
77
+        }
78
+
79
+        /**
80
+         * 监测上盖状态变化回调
81
+         * 当上盖状态发生变化时,将调用此方法。目前该回调仅支持 H10/D101/D110/D11/B21/B16/B32/Z401/B3S/B203/B1/B18 系列打印机。
82
+         *
83
+         * @param coverStatus 上盖状态,0 表示上盖打开,1 表示上盖关闭
84
+         */
85
+        @Override
86
+        public void onCoverStatus(int coverStatus) {
87
+
88
+        }
89
+
90
+        /**
91
+         * 监测纸张状态变化
92
+         * 当纸张状态发生变化时,将调用此方法。目前该回调仅支持H10/D101/D110/D11/B21/B16/B32/Z401/B203/B1/B18 系列打印机。
93
+         *
94
+         * @param paperStatus 0为不缺纸 1为缺纸
95
+         */
96
+        @Override
97
+        public void onPaperStatus(int paperStatus) {
98
+
99
+        }
100
+
101
+        /**
102
+         * 监测标签rfid读取状态变化
103
+         * 当标签rfid读取状态发生变化时,将调用此方法。
104
+         *
105
+         * @param rfidReadStatus 0为未读取到标签RFID 1为成功读取到标签RFID 目前该回调仅支持H10/D101/D110/D11/B21/B16/B32/Z401/B203/B1/B18 系列打印机。
106
+         */
107
+        @Override
108
+        public void onRfidReadStatus(int rfidReadStatus) {
109
+
110
+        }
111
+
112
+
113
+        /**
114
+         * 监测碳带rfid读取状态变化
115
+         * 当碳带rfid读取状态发生变化时,将调用此方法。
116
+         *
117
+         * @param ribbonRfidReadStatus 0为未读取到碳带RFID 1为成功读取到碳带RFID 目前该回调仅支持B18/B32/Z401/P1/P1S 系列打印机。
118
+         */
119
+        @Override
120
+        public void onRibbonRfidReadStatus(int ribbonRfidReadStatus) {
121
+
122
+        }
123
+
124
+        /**
125
+         * 监测碳带状态变化
126
+         * 当纸张状态发生变化时,将调用此方法
127
+         *
128
+         * @param ribbonStatus 0为无碳带 1为有碳带 目前该回调仅支持B18/B32/Z401/P1/P1S系列打印机。
129
+         */
130
+        @Override
131
+        public void onRibbonStatus(int ribbonStatus) {
132
+
133
+        }
134
+
135
+
136
+        /**
137
+         * 固件异常回调,需要升级
138
+         * 当设备连接成功但出现固件异常时,将调用此方法,表示需要进行固件升级。
139
+         */
140
+        @Override
141
+        public void onFirmErrors() {
142
+
143
+        }
144
+    };
145
+
146
+
147
+    /**
148
+     * 获取 JCPrintApi 单例实例
149
+     *
150
+     * @return JCPrintApi 实例
151
+     */
152
+    public static JCPrintApi getInstance() {
153
+        // 双重检查锁定以确保只初始化一次实例
154
+        if (api == null) {
155
+            synchronized (PrintUtil.class) {
156
+                if (api == null) {
157
+                    api = JCPrintApi.getInstance(CALLBACK);
158
+                    //api.init已废弃,使用initSdk替代,方法名含义更准确
159
+
160
+                    api.initSdk(MyApplication.getInstance());
161
+                    //获取内置目录路径
162
+                    File directory = MyApplication.getInstance().getFilesDir();
163
+                    //获取自定义字体路径
164
+                    File customFontDirectory = new File(directory, "custom_font");
165
+                    api.initDefaultImageLibrarySettings(customFontDirectory.getAbsolutePath(), "");
166
+
167
+                }
168
+            }
169
+        }
170
+
171
+        return api;
172
+
173
+    }
174
+
175
+
176
+    /**
177
+     * 通过打印机mac地址进行蓝牙连接开启打印机(同步)
178
+     *
179
+     * @param address 打印机地址
180
+     * @return 成功与否
181
+     */
182
+    public static int connectBluetoothPrinter(String address) {
183
+        // 获取单例实例以确保线程安全
184
+        JCPrintApi localApi = getInstance();
185
+        //api.openPrinterByAddress(address),使用connectBluetoothPrinter替代,方法名含义更准确
186
+        return localApi.connectBluetoothPrinter(address);
187
+    }
188
+
189
+    /**
190
+     * 关闭打印机
191
+     */
192
+    public static void close() {
193
+        // 获取单例实例以确保线程安全
194
+        JCPrintApi localApi = getInstance();
195
+        localApi.close();
196
+    }
197
+
198
+
199
+    public static int getConnectedType() {
200
+        return mConnectedType;
201
+    }
202
+
203
+    public static void setConnectedType(int connectedType) {
204
+        mConnectedType = connectedType;
205
+    }
206
+
207
+    /**
208
+     * 检查打印机是否连接
209
+     *
210
+     * @return 连接状态代码
211
+     */
212
+    public static int isConnection() {
213
+        // 获取单例实例以确保线程安全
214
+        JCPrintApi localApi = getInstance();
215
+        return localApi.isConnection();
216
+    }
217
+
218
+    public static void printBitMap(Bitmap bitmap, @NonNull MethodChannel.Result result) {
219
+        if (PrintUtil.isConnection() != 0) {
220
+//            handler.post(() -> Toast.makeText(MyApplication.getInstance(), "未连接打印机", Toast.LENGTH_SHORT).show());
221
+            result.success(false);
222
+            return;
223
+        }
224
+
225
+        //重置错误状态变量
226
+        final boolean[] isError = {false};
227
+        //重置取消打印状态变量
228
+        final boolean[] isCancel = {false};
229
+
230
+        final int[] orientation = {0};
231
+
232
+        int pageCount = 1;
233
+        int quantity = 1;
234
+        int printMultiple = 8;
235
+        final int[] generatedPrintDataPageCount = {0};
236
+        int totalQuantity = pageCount * quantity;
237
+
238
+        //setTotalQuantityOfPrints已废弃,使用方法含义更明确的setTotalPrintQuantity
239
+        PrintUtil.getInstance().setTotalPrintQuantity(totalQuantity);
240
+        /*
241
+         * 参数1:打印浓度 ,参数2:纸张类型 参数3:打印模式
242
+         * 打印浓度 B50/B50W/T6/T7/T8 建议设置6或8,Z401/B32建议设置8,B3S/B21/B203/B1建议设置3
243
+         */
244
+        PrintUtil.getInstance().startPrintJob(3, 3, 1, new PrintCallback() {
245
+            @Override
246
+            public void onProgress(int pageIndex, int quantityIndex, HashMap<String, Object> hashMap) {
247
+//                handler.post(() -> fragment.setStateStr("打印进度:已打印到第" + pageIndex + "页,第" + quantityIndex + "份"));
248
+                Log.d("ble", "测试:打印进度:已打印到第: " + pageIndex);
249
+                //打印进度回调
250
+                if (pageIndex == pageCount && quantityIndex == quantity) {
251
+                    Log.d("ble", "测试:onProgress: 结束打印");
252
+                    //endJob已废弃,使用方法含义更明确的endPrintJob
253
+                    if (PrintUtil.getInstance().endPrintJob()) {
254
+                        Log.d("ble", "结束打印成功");
255
+                    } else {
256
+                        Log.d("ble", "结束打印失败");
257
+                    }
258
+                    result.success(true);
259
+                }
260
+            }
261
+
262
+            @Override
263
+            public void onError(int i) {
264
+
265
+            }
266
+
267
+
268
+            @Override
269
+            public void onError(int errorCode, int printState) {
270
+                Log.d("ble", "测试:onError");
271
+                isError[0] = true;
272
+                result.success(false);
273
+            }
274
+
275
+            @Override
276
+            public void onCancelJob(boolean isSuccess) {
277
+                //取消打印成功回调
278
+                isCancel[0] = true;
279
+            }
280
+
281
+            @Override
282
+            public void onBufferFree(int pageIndex, int bufferSize) {
283
+                if (isError[0] || isCancel[0] || pageIndex > pageCount) {
284
+                    Log.d("ble", "测试:onBufferFree error");
285
+                    return;
286
+                }
287
+
288
+                if (generatedPrintDataPageCount[0] < pageCount) {
289
+
290
+                    int bitmapWidth = bitmap.getWidth();
291
+                    int bitmapHeight = bitmap.getHeight();
292
+                    Log.d("ble", "bitmapWidth:"+bitmapWidth+",bitmapHeight:"+bitmapHeight);
293
+                    PrintUtil.getInstance().commitImageData(orientation[0], bitmap, (int) (bitmapWidth / printMultiple), (int) (bitmapHeight / printMultiple), 1, 0, 0, 0, 0, "");
294
+                }
295
+            }
296
+        });
297
+    }
298
+
299
+
300
+}

+ 0 - 13
android/build.gradle

@@ -1,16 +1,3 @@
1
-buildscript {
2
-    ext.kotlin_version = '1.8.22'
3
-    repositories {
4
-        google()
5
-        mavenCentral()
6
-    }
7
-
8
-    dependencies {
9
-        classpath 'com.android.tools.build:gradle:7.3.0'
10
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11
-    }
12
-}
13
-
14 1
 allprojects {
15 2
     repositories {
16 3
         google()

+ 10 - 4
android/settings.gradle

@@ -10,11 +10,17 @@ pluginManagement {
10 10
 
11 11
     includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
12 12
 
13
-    plugins {
14
-        id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
13
+    repositories {
14
+        google()
15
+        mavenCentral()
16
+        gradlePluginPortal()
15 17
     }
16 18
 }
17 19
 
18
-include ":app"
20
+plugins {
21
+    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
22
+    id "com.android.application" version "7.3.0" apply false
23
+    id "org.jetbrains.kotlin.android" version "1.7.10" apply false
24
+}
19 25
 
20
-apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
26
+include ":app"

BIN
assets/images/print_ble.png


BIN
assets/images/search_ble.png


+ 7 - 1
lib/config/pics.dart

@@ -41,4 +41,10 @@ const String imgScanBack = 'assets/images/scan_back.webp';
41 41
 // 扫一扫 闪光灯
42 42
 const String imgScanFlash= 'assets/images/scan_flash.webp';
43 43
 // 扫一扫 扫描中
44
-const String imgScanScanning= 'assets/images/scan_scanning.webp';
44
+const String imgScanScanning= 'assets/images/scan_scanning.webp';
45
+
46
+// 搜索图标
47
+const String imgBleSearch = 'assets/images/search_ble.png';
48
+// 打印图标
49
+const String imgBlePrint = 'assets/images/print_ble.png';
50
+

+ 27 - 0
lib/drfit/dao/device_info_table_dao.dart

@@ -0,0 +1,27 @@
1
+import 'package:drift/drift.dart';
2
+import 'package:lszlgl/drfit/database.dart';
3
+import 'package:lszlgl/drfit/device_info_table.dart';
4
+import 'package:lszlgl/service/user_service.dart';
5
+
6
+part 'device_info_table_dao.g.dart';
7
+
8
+@DriftAccessor(tables: [DeviceInfoTable])
9
+class DeviceInfoTableDao extends DatabaseAccessor<MyDatabase> {
10
+  DeviceInfoTableDao(MyDatabase db) : super(db);
11
+
12
+  Future<int> addOneDeviceComp(DeviceInfoTableCompanion deviceInfoTableCompanion) async {
13
+    return into(db.deviceInfoTable).insert(deviceInfoTableCompanion, mode: InsertMode.insertOrReplace);
14
+  }
15
+
16
+  Future<List<DeviceInfo>> queryAllDeviceInfo() async {
17
+
18
+    int deviceInfoTableTime = UserService.get().getDeviceInfoTableTime();
19
+    return (select(db.deviceInfoTable)
20
+      ..where((tbl) => tbl.createdAt.isBiggerThanValue(deviceInfoTableTime))
21
+      ..orderBy([(row) => OrderingTerm.asc(row.createdAt)])).get();
22
+  }
23
+
24
+
25
+
26
+
27
+}

+ 8 - 0
lib/drfit/dao/device_info_table_dao.g.dart

@@ -0,0 +1,8 @@
1
+// GENERATED CODE - DO NOT MODIFY BY HAND
2
+
3
+part of 'device_info_table_dao.dart';
4
+
5
+// ignore_for_file: type=lint
6
+mixin _$DeviceInfoTableDaoMixin on DatabaseAccessor<MyDatabase> {
7
+  $DeviceInfoTableTable get deviceInfoTable => attachedDatabase.deviceInfoTable;
8
+}

+ 63 - 0
lib/drfit/database.dart

@@ -0,0 +1,63 @@
1
+import 'dart:io';
2
+
3
+import 'package:dio/dio.dart';
4
+import 'package:drift/drift.dart';
5
+import 'package:drift/native.dart';
6
+import 'package:lszlgl/main.dart';
7
+import 'package:lszlgl/model/req/device_req.dart';
8
+import 'package:path_provider/path_provider.dart';
9
+import 'package:path/path.dart' as p;
10
+
11
+import '../network/my_api.dart';
12
+import '../service/user_service.dart';
13
+import 'dao/device_info_table_dao.dart';
14
+import 'device_info_table.dart';
15
+
16
+
17
+part 'database.g.dart';
18
+
19
+@DriftDatabase(tables: [DeviceInfoTable], daos: [DeviceInfoTableDao])
20
+class MyDatabase extends _$MyDatabase {
21
+  MyDatabase() : super(_openConnection());
22
+  // MyDatabase(super.e);
23
+
24
+
25
+  @override
26
+  int get schemaVersion => 1;
27
+
28
+
29
+  /// 同步数据到服务器
30
+  Future<void> savaBleDataToServer() async {
31
+    List<DeviceInfo> deviceInfos = await database.deviceInfoTableDao.queryAllDeviceInfo();
32
+
33
+    List<DeviceReq> deviceRes = [];
34
+    for(DeviceInfo device in deviceInfos) {
35
+      deviceRes.add(DeviceReq.fromJson(device.toJson()));
36
+    }
37
+    if(deviceRes.isEmpty) {
38
+      return;
39
+    }
40
+    try {
41
+      // 获取用户数据
42
+      var user = await MyApi.get().postDeviceInfos(deviceRes);
43
+      logger.d('====user:$user');
44
+      // 记录末次同步id
45
+      UserService.get().saveDeviceInfoTableTime(deviceInfos.last.createdAt);
46
+    } on DioException catch (_) {
47
+    } on Exception catch (a, _) {
48
+    }
49
+
50
+  }
51
+
52
+}
53
+
54
+LazyDatabase _openConnection() {
55
+
56
+  return LazyDatabase(() async {
57
+
58
+    final documentsDir = await getApplicationDocumentsDirectory();
59
+    final file = File(p.join(documentsDir.path, 'myDatabase.db'));
60
+
61
+    return NativeDatabase.createInBackground(file);
62
+  });
63
+}

+ 657 - 0
lib/drfit/database.g.dart

@@ -0,0 +1,657 @@
1
+// GENERATED CODE - DO NOT MODIFY BY HAND
2
+
3
+part of 'database.dart';
4
+
5
+// ignore_for_file: type=lint
6
+class $DeviceInfoTableTable extends DeviceInfoTable
7
+    with TableInfo<$DeviceInfoTableTable, DeviceInfo> {
8
+  @override
9
+  final GeneratedDatabase attachedDatabase;
10
+  final String? _alias;
11
+  $DeviceInfoTableTable(this.attachedDatabase, [this._alias]);
12
+  static const VerificationMeta _usernameMeta =
13
+      const VerificationMeta('username');
14
+  @override
15
+  late final GeneratedColumn<String> username = GeneratedColumn<String>(
16
+      'username', aliasedName, false,
17
+      type: DriftSqlType.string, requiredDuringInsert: true);
18
+  static const VerificationMeta _phoneBrandMeta =
19
+      const VerificationMeta('phoneBrand');
20
+  @override
21
+  late final GeneratedColumn<String> phoneBrand = GeneratedColumn<String>(
22
+      'phone_brand', aliasedName, false,
23
+      type: DriftSqlType.string, requiredDuringInsert: true);
24
+  static const VerificationMeta _phoneModelMeta =
25
+      const VerificationMeta('phoneModel');
26
+  @override
27
+  late final GeneratedColumn<String> phoneModel = GeneratedColumn<String>(
28
+      'phone_model', aliasedName, false,
29
+      type: DriftSqlType.string, requiredDuringInsert: true);
30
+  static const VerificationMeta _phoneReleaseMeta =
31
+      const VerificationMeta('phoneRelease');
32
+  @override
33
+  late final GeneratedColumn<String> phoneRelease = GeneratedColumn<String>(
34
+      'phone_release', aliasedName, false,
35
+      type: DriftSqlType.string, requiredDuringInsert: true);
36
+  static const VerificationMeta _phoneAddressMeta =
37
+      const VerificationMeta('phoneAddress');
38
+  @override
39
+  late final GeneratedColumn<String> phoneAddress = GeneratedColumn<String>(
40
+      'phone_address', aliasedName, false,
41
+      type: DriftSqlType.string, requiredDuringInsert: true);
42
+  static const VerificationMeta _bleMacMeta = const VerificationMeta('bleMac');
43
+  @override
44
+  late final GeneratedColumn<String> bleMac = GeneratedColumn<String>(
45
+      'ble_mac', aliasedName, false,
46
+      type: DriftSqlType.string, requiredDuringInsert: true);
47
+  static const VerificationMeta _bleNameMeta =
48
+      const VerificationMeta('bleName');
49
+  @override
50
+  late final GeneratedColumn<String> bleName = GeneratedColumn<String>(
51
+      'ble_name', aliasedName, false,
52
+      type: DriftSqlType.string, requiredDuringInsert: true);
53
+  static const VerificationMeta _createdAtMeta =
54
+      const VerificationMeta('createdAt');
55
+  @override
56
+  late final GeneratedColumn<int> createdAt = GeneratedColumn<int>(
57
+      'created_at', aliasedName, false,
58
+      type: DriftSqlType.int, requiredDuringInsert: true);
59
+  @override
60
+  List<GeneratedColumn> get $columns => [
61
+        username,
62
+        phoneBrand,
63
+        phoneModel,
64
+        phoneRelease,
65
+        phoneAddress,
66
+        bleMac,
67
+        bleName,
68
+        createdAt
69
+      ];
70
+  @override
71
+  String get aliasedName => _alias ?? actualTableName;
72
+  @override
73
+  String get actualTableName => $name;
74
+  static const String $name = 'device_info_table';
75
+  @override
76
+  VerificationContext validateIntegrity(Insertable<DeviceInfo> instance,
77
+      {bool isInserting = false}) {
78
+    final context = VerificationContext();
79
+    final data = instance.toColumns(true);
80
+    if (data.containsKey('username')) {
81
+      context.handle(_usernameMeta,
82
+          username.isAcceptableOrUnknown(data['username']!, _usernameMeta));
83
+    } else if (isInserting) {
84
+      context.missing(_usernameMeta);
85
+    }
86
+    if (data.containsKey('phone_brand')) {
87
+      context.handle(
88
+          _phoneBrandMeta,
89
+          phoneBrand.isAcceptableOrUnknown(
90
+              data['phone_brand']!, _phoneBrandMeta));
91
+    } else if (isInserting) {
92
+      context.missing(_phoneBrandMeta);
93
+    }
94
+    if (data.containsKey('phone_model')) {
95
+      context.handle(
96
+          _phoneModelMeta,
97
+          phoneModel.isAcceptableOrUnknown(
98
+              data['phone_model']!, _phoneModelMeta));
99
+    } else if (isInserting) {
100
+      context.missing(_phoneModelMeta);
101
+    }
102
+    if (data.containsKey('phone_release')) {
103
+      context.handle(
104
+          _phoneReleaseMeta,
105
+          phoneRelease.isAcceptableOrUnknown(
106
+              data['phone_release']!, _phoneReleaseMeta));
107
+    } else if (isInserting) {
108
+      context.missing(_phoneReleaseMeta);
109
+    }
110
+    if (data.containsKey('phone_address')) {
111
+      context.handle(
112
+          _phoneAddressMeta,
113
+          phoneAddress.isAcceptableOrUnknown(
114
+              data['phone_address']!, _phoneAddressMeta));
115
+    } else if (isInserting) {
116
+      context.missing(_phoneAddressMeta);
117
+    }
118
+    if (data.containsKey('ble_mac')) {
119
+      context.handle(_bleMacMeta,
120
+          bleMac.isAcceptableOrUnknown(data['ble_mac']!, _bleMacMeta));
121
+    } else if (isInserting) {
122
+      context.missing(_bleMacMeta);
123
+    }
124
+    if (data.containsKey('ble_name')) {
125
+      context.handle(_bleNameMeta,
126
+          bleName.isAcceptableOrUnknown(data['ble_name']!, _bleNameMeta));
127
+    } else if (isInserting) {
128
+      context.missing(_bleNameMeta);
129
+    }
130
+    if (data.containsKey('created_at')) {
131
+      context.handle(_createdAtMeta,
132
+          createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
133
+    } else if (isInserting) {
134
+      context.missing(_createdAtMeta);
135
+    }
136
+    return context;
137
+  }
138
+
139
+  @override
140
+  Set<GeneratedColumn> get $primaryKey => {bleMac, username};
141
+  @override
142
+  DeviceInfo map(Map<String, dynamic> data, {String? tablePrefix}) {
143
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
144
+    return DeviceInfo(
145
+      username: attachedDatabase.typeMapping
146
+          .read(DriftSqlType.string, data['${effectivePrefix}username'])!,
147
+      phoneBrand: attachedDatabase.typeMapping
148
+          .read(DriftSqlType.string, data['${effectivePrefix}phone_brand'])!,
149
+      phoneModel: attachedDatabase.typeMapping
150
+          .read(DriftSqlType.string, data['${effectivePrefix}phone_model'])!,
151
+      phoneRelease: attachedDatabase.typeMapping
152
+          .read(DriftSqlType.string, data['${effectivePrefix}phone_release'])!,
153
+      phoneAddress: attachedDatabase.typeMapping
154
+          .read(DriftSqlType.string, data['${effectivePrefix}phone_address'])!,
155
+      bleMac: attachedDatabase.typeMapping
156
+          .read(DriftSqlType.string, data['${effectivePrefix}ble_mac'])!,
157
+      bleName: attachedDatabase.typeMapping
158
+          .read(DriftSqlType.string, data['${effectivePrefix}ble_name'])!,
159
+      createdAt: attachedDatabase.typeMapping
160
+          .read(DriftSqlType.int, data['${effectivePrefix}created_at'])!,
161
+    );
162
+  }
163
+
164
+  @override
165
+  $DeviceInfoTableTable createAlias(String alias) {
166
+    return $DeviceInfoTableTable(attachedDatabase, alias);
167
+  }
168
+}
169
+
170
+class DeviceInfo extends DataClass implements Insertable<DeviceInfo> {
171
+  /// 登录用户名
172
+  final String username;
173
+
174
+  /// 登录手机品牌,如Redmi
175
+  final String phoneBrand;
176
+
177
+  /// 登录手机认证型号,如22120RN86C
178
+  final String phoneModel;
179
+
180
+  /// 登录手机版本,如14
181
+  final String phoneRelease;
182
+
183
+  /// 登录手机所在地址,如"北京市西城区月坛北街18号靠近物资大院"
184
+  final String phoneAddress;
185
+
186
+  /// 登录蓝牙mac地址
187
+  final String bleMac;
188
+
189
+  /// 登录蓝牙名称
190
+  final String bleName;
191
+
192
+  /// 日期
193
+  final int createdAt;
194
+  const DeviceInfo(
195
+      {required this.username,
196
+      required this.phoneBrand,
197
+      required this.phoneModel,
198
+      required this.phoneRelease,
199
+      required this.phoneAddress,
200
+      required this.bleMac,
201
+      required this.bleName,
202
+      required this.createdAt});
203
+  @override
204
+  Map<String, Expression> toColumns(bool nullToAbsent) {
205
+    final map = <String, Expression>{};
206
+    map['username'] = Variable<String>(username);
207
+    map['phone_brand'] = Variable<String>(phoneBrand);
208
+    map['phone_model'] = Variable<String>(phoneModel);
209
+    map['phone_release'] = Variable<String>(phoneRelease);
210
+    map['phone_address'] = Variable<String>(phoneAddress);
211
+    map['ble_mac'] = Variable<String>(bleMac);
212
+    map['ble_name'] = Variable<String>(bleName);
213
+    map['created_at'] = Variable<int>(createdAt);
214
+    return map;
215
+  }
216
+
217
+  DeviceInfoTableCompanion toCompanion(bool nullToAbsent) {
218
+    return DeviceInfoTableCompanion(
219
+      username: Value(username),
220
+      phoneBrand: Value(phoneBrand),
221
+      phoneModel: Value(phoneModel),
222
+      phoneRelease: Value(phoneRelease),
223
+      phoneAddress: Value(phoneAddress),
224
+      bleMac: Value(bleMac),
225
+      bleName: Value(bleName),
226
+      createdAt: Value(createdAt),
227
+    );
228
+  }
229
+
230
+  factory DeviceInfo.fromJson(Map<String, dynamic> json,
231
+      {ValueSerializer? serializer}) {
232
+    serializer ??= driftRuntimeOptions.defaultSerializer;
233
+    return DeviceInfo(
234
+      username: serializer.fromJson<String>(json['username']),
235
+      phoneBrand: serializer.fromJson<String>(json['phoneBrand']),
236
+      phoneModel: serializer.fromJson<String>(json['phoneModel']),
237
+      phoneRelease: serializer.fromJson<String>(json['phoneRelease']),
238
+      phoneAddress: serializer.fromJson<String>(json['phoneAddress']),
239
+      bleMac: serializer.fromJson<String>(json['bleMac']),
240
+      bleName: serializer.fromJson<String>(json['bleName']),
241
+      createdAt: serializer.fromJson<int>(json['createdAt']),
242
+    );
243
+  }
244
+  @override
245
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
246
+    serializer ??= driftRuntimeOptions.defaultSerializer;
247
+    return <String, dynamic>{
248
+      'username': serializer.toJson<String>(username),
249
+      'phoneBrand': serializer.toJson<String>(phoneBrand),
250
+      'phoneModel': serializer.toJson<String>(phoneModel),
251
+      'phoneRelease': serializer.toJson<String>(phoneRelease),
252
+      'phoneAddress': serializer.toJson<String>(phoneAddress),
253
+      'bleMac': serializer.toJson<String>(bleMac),
254
+      'bleName': serializer.toJson<String>(bleName),
255
+      'createdAt': serializer.toJson<int>(createdAt),
256
+    };
257
+  }
258
+
259
+  DeviceInfo copyWith(
260
+          {String? username,
261
+          String? phoneBrand,
262
+          String? phoneModel,
263
+          String? phoneRelease,
264
+          String? phoneAddress,
265
+          String? bleMac,
266
+          String? bleName,
267
+          int? createdAt}) =>
268
+      DeviceInfo(
269
+        username: username ?? this.username,
270
+        phoneBrand: phoneBrand ?? this.phoneBrand,
271
+        phoneModel: phoneModel ?? this.phoneModel,
272
+        phoneRelease: phoneRelease ?? this.phoneRelease,
273
+        phoneAddress: phoneAddress ?? this.phoneAddress,
274
+        bleMac: bleMac ?? this.bleMac,
275
+        bleName: bleName ?? this.bleName,
276
+        createdAt: createdAt ?? this.createdAt,
277
+      );
278
+  @override
279
+  String toString() {
280
+    return (StringBuffer('DeviceInfo(')
281
+          ..write('username: $username, ')
282
+          ..write('phoneBrand: $phoneBrand, ')
283
+          ..write('phoneModel: $phoneModel, ')
284
+          ..write('phoneRelease: $phoneRelease, ')
285
+          ..write('phoneAddress: $phoneAddress, ')
286
+          ..write('bleMac: $bleMac, ')
287
+          ..write('bleName: $bleName, ')
288
+          ..write('createdAt: $createdAt')
289
+          ..write(')'))
290
+        .toString();
291
+  }
292
+
293
+  @override
294
+  int get hashCode => Object.hash(username, phoneBrand, phoneModel,
295
+      phoneRelease, phoneAddress, bleMac, bleName, createdAt);
296
+  @override
297
+  bool operator ==(Object other) =>
298
+      identical(this, other) ||
299
+      (other is DeviceInfo &&
300
+          other.username == this.username &&
301
+          other.phoneBrand == this.phoneBrand &&
302
+          other.phoneModel == this.phoneModel &&
303
+          other.phoneRelease == this.phoneRelease &&
304
+          other.phoneAddress == this.phoneAddress &&
305
+          other.bleMac == this.bleMac &&
306
+          other.bleName == this.bleName &&
307
+          other.createdAt == this.createdAt);
308
+}
309
+
310
+class DeviceInfoTableCompanion extends UpdateCompanion<DeviceInfo> {
311
+  final Value<String> username;
312
+  final Value<String> phoneBrand;
313
+  final Value<String> phoneModel;
314
+  final Value<String> phoneRelease;
315
+  final Value<String> phoneAddress;
316
+  final Value<String> bleMac;
317
+  final Value<String> bleName;
318
+  final Value<int> createdAt;
319
+  final Value<int> rowid;
320
+  const DeviceInfoTableCompanion({
321
+    this.username = const Value.absent(),
322
+    this.phoneBrand = const Value.absent(),
323
+    this.phoneModel = const Value.absent(),
324
+    this.phoneRelease = const Value.absent(),
325
+    this.phoneAddress = const Value.absent(),
326
+    this.bleMac = const Value.absent(),
327
+    this.bleName = const Value.absent(),
328
+    this.createdAt = const Value.absent(),
329
+    this.rowid = const Value.absent(),
330
+  });
331
+  DeviceInfoTableCompanion.insert({
332
+    required String username,
333
+    required String phoneBrand,
334
+    required String phoneModel,
335
+    required String phoneRelease,
336
+    required String phoneAddress,
337
+    required String bleMac,
338
+    required String bleName,
339
+    required int createdAt,
340
+    this.rowid = const Value.absent(),
341
+  })  : username = Value(username),
342
+        phoneBrand = Value(phoneBrand),
343
+        phoneModel = Value(phoneModel),
344
+        phoneRelease = Value(phoneRelease),
345
+        phoneAddress = Value(phoneAddress),
346
+        bleMac = Value(bleMac),
347
+        bleName = Value(bleName),
348
+        createdAt = Value(createdAt);
349
+  static Insertable<DeviceInfo> custom({
350
+    Expression<String>? username,
351
+    Expression<String>? phoneBrand,
352
+    Expression<String>? phoneModel,
353
+    Expression<String>? phoneRelease,
354
+    Expression<String>? phoneAddress,
355
+    Expression<String>? bleMac,
356
+    Expression<String>? bleName,
357
+    Expression<int>? createdAt,
358
+    Expression<int>? rowid,
359
+  }) {
360
+    return RawValuesInsertable({
361
+      if (username != null) 'username': username,
362
+      if (phoneBrand != null) 'phone_brand': phoneBrand,
363
+      if (phoneModel != null) 'phone_model': phoneModel,
364
+      if (phoneRelease != null) 'phone_release': phoneRelease,
365
+      if (phoneAddress != null) 'phone_address': phoneAddress,
366
+      if (bleMac != null) 'ble_mac': bleMac,
367
+      if (bleName != null) 'ble_name': bleName,
368
+      if (createdAt != null) 'created_at': createdAt,
369
+      if (rowid != null) 'rowid': rowid,
370
+    });
371
+  }
372
+
373
+  DeviceInfoTableCompanion copyWith(
374
+      {Value<String>? username,
375
+      Value<String>? phoneBrand,
376
+      Value<String>? phoneModel,
377
+      Value<String>? phoneRelease,
378
+      Value<String>? phoneAddress,
379
+      Value<String>? bleMac,
380
+      Value<String>? bleName,
381
+      Value<int>? createdAt,
382
+      Value<int>? rowid}) {
383
+    return DeviceInfoTableCompanion(
384
+      username: username ?? this.username,
385
+      phoneBrand: phoneBrand ?? this.phoneBrand,
386
+      phoneModel: phoneModel ?? this.phoneModel,
387
+      phoneRelease: phoneRelease ?? this.phoneRelease,
388
+      phoneAddress: phoneAddress ?? this.phoneAddress,
389
+      bleMac: bleMac ?? this.bleMac,
390
+      bleName: bleName ?? this.bleName,
391
+      createdAt: createdAt ?? this.createdAt,
392
+      rowid: rowid ?? this.rowid,
393
+    );
394
+  }
395
+
396
+  @override
397
+  Map<String, Expression> toColumns(bool nullToAbsent) {
398
+    final map = <String, Expression>{};
399
+    if (username.present) {
400
+      map['username'] = Variable<String>(username.value);
401
+    }
402
+    if (phoneBrand.present) {
403
+      map['phone_brand'] = Variable<String>(phoneBrand.value);
404
+    }
405
+    if (phoneModel.present) {
406
+      map['phone_model'] = Variable<String>(phoneModel.value);
407
+    }
408
+    if (phoneRelease.present) {
409
+      map['phone_release'] = Variable<String>(phoneRelease.value);
410
+    }
411
+    if (phoneAddress.present) {
412
+      map['phone_address'] = Variable<String>(phoneAddress.value);
413
+    }
414
+    if (bleMac.present) {
415
+      map['ble_mac'] = Variable<String>(bleMac.value);
416
+    }
417
+    if (bleName.present) {
418
+      map['ble_name'] = Variable<String>(bleName.value);
419
+    }
420
+    if (createdAt.present) {
421
+      map['created_at'] = Variable<int>(createdAt.value);
422
+    }
423
+    if (rowid.present) {
424
+      map['rowid'] = Variable<int>(rowid.value);
425
+    }
426
+    return map;
427
+  }
428
+
429
+  @override
430
+  String toString() {
431
+    return (StringBuffer('DeviceInfoTableCompanion(')
432
+          ..write('username: $username, ')
433
+          ..write('phoneBrand: $phoneBrand, ')
434
+          ..write('phoneModel: $phoneModel, ')
435
+          ..write('phoneRelease: $phoneRelease, ')
436
+          ..write('phoneAddress: $phoneAddress, ')
437
+          ..write('bleMac: $bleMac, ')
438
+          ..write('bleName: $bleName, ')
439
+          ..write('createdAt: $createdAt, ')
440
+          ..write('rowid: $rowid')
441
+          ..write(')'))
442
+        .toString();
443
+  }
444
+}
445
+
446
+abstract class _$MyDatabase extends GeneratedDatabase {
447
+  _$MyDatabase(QueryExecutor e) : super(e);
448
+  _$MyDatabaseManager get managers => _$MyDatabaseManager(this);
449
+  late final $DeviceInfoTableTable deviceInfoTable =
450
+      $DeviceInfoTableTable(this);
451
+  late final DeviceInfoTableDao deviceInfoTableDao =
452
+      DeviceInfoTableDao(this as MyDatabase);
453
+  @override
454
+  Iterable<TableInfo<Table, Object?>> get allTables =>
455
+      allSchemaEntities.whereType<TableInfo<Table, Object?>>();
456
+  @override
457
+  List<DatabaseSchemaEntity> get allSchemaEntities => [deviceInfoTable];
458
+}
459
+
460
+typedef $$DeviceInfoTableTableInsertCompanionBuilder = DeviceInfoTableCompanion
461
+    Function({
462
+  required String username,
463
+  required String phoneBrand,
464
+  required String phoneModel,
465
+  required String phoneRelease,
466
+  required String phoneAddress,
467
+  required String bleMac,
468
+  required String bleName,
469
+  required int createdAt,
470
+  Value<int> rowid,
471
+});
472
+typedef $$DeviceInfoTableTableUpdateCompanionBuilder = DeviceInfoTableCompanion
473
+    Function({
474
+  Value<String> username,
475
+  Value<String> phoneBrand,
476
+  Value<String> phoneModel,
477
+  Value<String> phoneRelease,
478
+  Value<String> phoneAddress,
479
+  Value<String> bleMac,
480
+  Value<String> bleName,
481
+  Value<int> createdAt,
482
+  Value<int> rowid,
483
+});
484
+
485
+class $$DeviceInfoTableTableTableManager extends RootTableManager<
486
+    _$MyDatabase,
487
+    $DeviceInfoTableTable,
488
+    DeviceInfo,
489
+    $$DeviceInfoTableTableFilterComposer,
490
+    $$DeviceInfoTableTableOrderingComposer,
491
+    $$DeviceInfoTableTableProcessedTableManager,
492
+    $$DeviceInfoTableTableInsertCompanionBuilder,
493
+    $$DeviceInfoTableTableUpdateCompanionBuilder> {
494
+  $$DeviceInfoTableTableTableManager(
495
+      _$MyDatabase db, $DeviceInfoTableTable table)
496
+      : super(TableManagerState(
497
+          db: db,
498
+          table: table,
499
+          filteringComposer:
500
+              $$DeviceInfoTableTableFilterComposer(ComposerState(db, table)),
501
+          orderingComposer:
502
+              $$DeviceInfoTableTableOrderingComposer(ComposerState(db, table)),
503
+          getChildManagerBuilder: (p) =>
504
+              $$DeviceInfoTableTableProcessedTableManager(p),
505
+          getUpdateCompanionBuilder: ({
506
+            Value<String> username = const Value.absent(),
507
+            Value<String> phoneBrand = const Value.absent(),
508
+            Value<String> phoneModel = const Value.absent(),
509
+            Value<String> phoneRelease = const Value.absent(),
510
+            Value<String> phoneAddress = const Value.absent(),
511
+            Value<String> bleMac = const Value.absent(),
512
+            Value<String> bleName = const Value.absent(),
513
+            Value<int> createdAt = const Value.absent(),
514
+            Value<int> rowid = const Value.absent(),
515
+          }) =>
516
+              DeviceInfoTableCompanion(
517
+            username: username,
518
+            phoneBrand: phoneBrand,
519
+            phoneModel: phoneModel,
520
+            phoneRelease: phoneRelease,
521
+            phoneAddress: phoneAddress,
522
+            bleMac: bleMac,
523
+            bleName: bleName,
524
+            createdAt: createdAt,
525
+            rowid: rowid,
526
+          ),
527
+          getInsertCompanionBuilder: ({
528
+            required String username,
529
+            required String phoneBrand,
530
+            required String phoneModel,
531
+            required String phoneRelease,
532
+            required String phoneAddress,
533
+            required String bleMac,
534
+            required String bleName,
535
+            required int createdAt,
536
+            Value<int> rowid = const Value.absent(),
537
+          }) =>
538
+              DeviceInfoTableCompanion.insert(
539
+            username: username,
540
+            phoneBrand: phoneBrand,
541
+            phoneModel: phoneModel,
542
+            phoneRelease: phoneRelease,
543
+            phoneAddress: phoneAddress,
544
+            bleMac: bleMac,
545
+            bleName: bleName,
546
+            createdAt: createdAt,
547
+            rowid: rowid,
548
+          ),
549
+        ));
550
+}
551
+
552
+class $$DeviceInfoTableTableProcessedTableManager extends ProcessedTableManager<
553
+    _$MyDatabase,
554
+    $DeviceInfoTableTable,
555
+    DeviceInfo,
556
+    $$DeviceInfoTableTableFilterComposer,
557
+    $$DeviceInfoTableTableOrderingComposer,
558
+    $$DeviceInfoTableTableProcessedTableManager,
559
+    $$DeviceInfoTableTableInsertCompanionBuilder,
560
+    $$DeviceInfoTableTableUpdateCompanionBuilder> {
561
+  $$DeviceInfoTableTableProcessedTableManager(super.$state);
562
+}
563
+
564
+class $$DeviceInfoTableTableFilterComposer
565
+    extends FilterComposer<_$MyDatabase, $DeviceInfoTableTable> {
566
+  $$DeviceInfoTableTableFilterComposer(super.$state);
567
+  ColumnFilters<String> get username => $state.composableBuilder(
568
+      column: $state.table.username,
569
+      builder: (column, joinBuilders) =>
570
+          ColumnFilters(column, joinBuilders: joinBuilders));
571
+
572
+  ColumnFilters<String> get phoneBrand => $state.composableBuilder(
573
+      column: $state.table.phoneBrand,
574
+      builder: (column, joinBuilders) =>
575
+          ColumnFilters(column, joinBuilders: joinBuilders));
576
+
577
+  ColumnFilters<String> get phoneModel => $state.composableBuilder(
578
+      column: $state.table.phoneModel,
579
+      builder: (column, joinBuilders) =>
580
+          ColumnFilters(column, joinBuilders: joinBuilders));
581
+
582
+  ColumnFilters<String> get phoneRelease => $state.composableBuilder(
583
+      column: $state.table.phoneRelease,
584
+      builder: (column, joinBuilders) =>
585
+          ColumnFilters(column, joinBuilders: joinBuilders));
586
+
587
+  ColumnFilters<String> get phoneAddress => $state.composableBuilder(
588
+      column: $state.table.phoneAddress,
589
+      builder: (column, joinBuilders) =>
590
+          ColumnFilters(column, joinBuilders: joinBuilders));
591
+
592
+  ColumnFilters<String> get bleMac => $state.composableBuilder(
593
+      column: $state.table.bleMac,
594
+      builder: (column, joinBuilders) =>
595
+          ColumnFilters(column, joinBuilders: joinBuilders));
596
+
597
+  ColumnFilters<String> get bleName => $state.composableBuilder(
598
+      column: $state.table.bleName,
599
+      builder: (column, joinBuilders) =>
600
+          ColumnFilters(column, joinBuilders: joinBuilders));
601
+
602
+  ColumnFilters<int> get createdAt => $state.composableBuilder(
603
+      column: $state.table.createdAt,
604
+      builder: (column, joinBuilders) =>
605
+          ColumnFilters(column, joinBuilders: joinBuilders));
606
+}
607
+
608
+class $$DeviceInfoTableTableOrderingComposer
609
+    extends OrderingComposer<_$MyDatabase, $DeviceInfoTableTable> {
610
+  $$DeviceInfoTableTableOrderingComposer(super.$state);
611
+  ColumnOrderings<String> get username => $state.composableBuilder(
612
+      column: $state.table.username,
613
+      builder: (column, joinBuilders) =>
614
+          ColumnOrderings(column, joinBuilders: joinBuilders));
615
+
616
+  ColumnOrderings<String> get phoneBrand => $state.composableBuilder(
617
+      column: $state.table.phoneBrand,
618
+      builder: (column, joinBuilders) =>
619
+          ColumnOrderings(column, joinBuilders: joinBuilders));
620
+
621
+  ColumnOrderings<String> get phoneModel => $state.composableBuilder(
622
+      column: $state.table.phoneModel,
623
+      builder: (column, joinBuilders) =>
624
+          ColumnOrderings(column, joinBuilders: joinBuilders));
625
+
626
+  ColumnOrderings<String> get phoneRelease => $state.composableBuilder(
627
+      column: $state.table.phoneRelease,
628
+      builder: (column, joinBuilders) =>
629
+          ColumnOrderings(column, joinBuilders: joinBuilders));
630
+
631
+  ColumnOrderings<String> get phoneAddress => $state.composableBuilder(
632
+      column: $state.table.phoneAddress,
633
+      builder: (column, joinBuilders) =>
634
+          ColumnOrderings(column, joinBuilders: joinBuilders));
635
+
636
+  ColumnOrderings<String> get bleMac => $state.composableBuilder(
637
+      column: $state.table.bleMac,
638
+      builder: (column, joinBuilders) =>
639
+          ColumnOrderings(column, joinBuilders: joinBuilders));
640
+
641
+  ColumnOrderings<String> get bleName => $state.composableBuilder(
642
+      column: $state.table.bleName,
643
+      builder: (column, joinBuilders) =>
644
+          ColumnOrderings(column, joinBuilders: joinBuilders));
645
+
646
+  ColumnOrderings<int> get createdAt => $state.composableBuilder(
647
+      column: $state.table.createdAt,
648
+      builder: (column, joinBuilders) =>
649
+          ColumnOrderings(column, joinBuilders: joinBuilders));
650
+}
651
+
652
+class _$MyDatabaseManager {
653
+  final _$MyDatabase _db;
654
+  _$MyDatabaseManager(this._db);
655
+  $$DeviceInfoTableTableTableManager get deviceInfoTable =>
656
+      $$DeviceInfoTableTableTableManager(_db, _db.deviceInfoTable);
657
+}

+ 26 - 0
lib/drfit/device_info_table.dart

@@ -0,0 +1,26 @@
1
+import 'package:drift/drift.dart';
2
+
3
+
4
+@DataClassName('DeviceInfo')
5
+class DeviceInfoTable extends Table {
6
+
7
+  /// 登录用户名
8
+  TextColumn get username => text()();
9
+  /// 登录手机品牌,如Redmi
10
+  TextColumn get phoneBrand => text()();
11
+  /// 登录手机认证型号,如22120RN86C
12
+  TextColumn get phoneModel=> text()();
13
+  /// 登录手机版本,如14
14
+  TextColumn get phoneRelease => text()();
15
+  /// 登录手机所在地址,如"北京市西城区月坛北街18号靠近物资大院"
16
+  TextColumn get phoneAddress => text()();
17
+  /// 登录蓝牙mac地址
18
+  TextColumn get bleMac => text()();
19
+  /// 登录蓝牙名称
20
+  TextColumn get bleName => text()();
21
+  /// 日期
22
+  IntColumn get createdAt => integer()();
23
+
24
+  @override
25
+  Set<Column<Object>>? get primaryKey => {bleMac, username};
26
+}

+ 28 - 0
lib/drfit/model_factory.dart

@@ -0,0 +1,28 @@
1
+
2
+
3
+import 'package:device_info_plus/device_info_plus.dart';
4
+import 'package:drift/drift.dart';
5
+import 'package:lszlgl/drfit/database.dart';
6
+import 'package:lszlgl/service/print_service.dart';
7
+
8
+import '../model/rsp/user_rsp.dart';
9
+import '../service/user_service.dart';
10
+
11
+class ModelFactory {
12
+  static Future<DeviceInfoTableCompanion> convertToTSlideComp(String bleMac, String bleName, String phoneAddress) async {
13
+    UserRsp? userRsp =  UserService.get().getUser();
14
+    AndroidDeviceInfo? androidDeviceInfo = await PrintService.getDeviceInfo();
15
+    var tsc = DeviceInfoTableCompanion(
16
+      username: Value.absentIfNull(userRsp?.username),
17
+      phoneBrand: Value.absentIfNull(androidDeviceInfo?.board),
18
+      phoneModel: Value.absentIfNull(androidDeviceInfo?.model),
19
+      phoneRelease: Value.absentIfNull(androidDeviceInfo?.version.release),
20
+      phoneAddress: Value(phoneAddress),
21
+      bleMac: Value(bleMac),
22
+      bleName: Value(bleName),
23
+      createdAt: Value(DateTime.now().millisecondsSinceEpoch)
24
+    );
25
+    return tsc;
26
+  }
27
+
28
+}

+ 8 - 0
lib/main.dart

@@ -1,4 +1,7 @@
1 1
 import 'dart:async';
2
+import 'dart:io';
3
+import 'package:drift/drift.dart' as drift;
4
+import 'package:drift/native.dart';
2 5
 import 'package:flutter/foundation.dart';
3 6
 import 'package:flutter/material.dart';
4 7
 import 'package:flutter/services.dart';
@@ -6,19 +9,24 @@ import 'package:flutter_localizations/flutter_localizations.dart';
6 9
 import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
7 10
 import 'package:logger/logger.dart';
8 11
 import 'package:lszlgl/config/colors.dart';
12
+import 'package:lszlgl/drfit/database.dart';
9 13
 import 'package:lszlgl/router/my_navigator.dart';
10 14
 
11 15
 late Logger logger;
16
+late MyDatabase database;
12 17
 
13 18
 void main() async {
14 19
   logger = Logger(printer: PrettyPrinter(methodCount: 0));
20
+
15 21
   initReportException(() async {
16 22
     SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light.copyWith(statusBarColor: Colors.transparent));
17 23
     SystemChrome.setPreferredOrientations([
18 24
       DeviceOrientation.portraitUp,
19 25
       DeviceOrientation.portraitDown,
20 26
     ]);
27
+
21 28
     runApp(const MyApp());
29
+    database = MyDatabase();
22 30
   });
23 31
 }
24 32
 

+ 42 - 0
lib/model/req/device_req.dart

@@ -0,0 +1,42 @@
1
+
2
+import 'package:json_annotation/json_annotation.dart';
3
+
4
+part 'device_req.g.dart';
5
+
6
+@JsonSerializable()
7
+class DeviceReq {
8
+
9
+  /// 登录用户名
10
+  final String username;
11
+
12
+  /// 登录手机品牌,如Redmi
13
+  final String phoneBrand;
14
+
15
+  /// 登录手机认证型号,如22120RN86C
16
+  final String phoneModel;
17
+
18
+  /// 登录手机版本,如14
19
+  final String phoneRelease;
20
+
21
+  /// 登录手机所在地址,如"北京市西城区月坛北街18号靠近物资大院"
22
+  final String phoneAddress;
23
+
24
+  /// 登录蓝牙mac地址
25
+  final String bleMac;
26
+
27
+  /// 登录蓝牙名称
28
+  final String bleName;
29
+  const DeviceReq(
30
+      {required this.username,
31
+        required this.phoneBrand,
32
+        required this.phoneModel,
33
+        required this.phoneRelease,
34
+        required this.phoneAddress,
35
+        required this.bleMac,
36
+        required this.bleName});
37
+
38
+
39
+  factory DeviceReq.fromJson(Map<String, dynamic> json) => _$DeviceReqFromJson(json);
40
+
41
+  Map<String, dynamic> toJson() => _$DeviceReqToJson(this);
42
+}

+ 27 - 0
lib/model/req/device_req.g.dart

@@ -0,0 +1,27 @@
1
+// GENERATED CODE - DO NOT MODIFY BY HAND
2
+
3
+part of 'device_req.dart';
4
+
5
+// **************************************************************************
6
+// JsonSerializableGenerator
7
+// **************************************************************************
8
+
9
+DeviceReq _$DeviceReqFromJson(Map<String, dynamic> json) => DeviceReq(
10
+      username: json['username'] as String,
11
+      phoneBrand: json['phoneBrand'] as String,
12
+      phoneModel: json['phoneModel'] as String,
13
+      phoneRelease: json['phoneRelease'] as String,
14
+      phoneAddress: json['phoneAddress'] as String,
15
+      bleMac: json['bleMac'] as String,
16
+      bleName: json['bleName'] as String,
17
+    );
18
+
19
+Map<String, dynamic> _$DeviceReqToJson(DeviceReq instance) => <String, dynamic>{
20
+      'username': instance.username,
21
+      'phoneBrand': instance.phoneBrand,
22
+      'phoneModel': instance.phoneModel,
23
+      'phoneRelease': instance.phoneRelease,
24
+      'phoneAddress': instance.phoneAddress,
25
+      'bleMac': instance.bleMac,
26
+      'bleName': instance.bleName,
27
+    };

+ 1 - 1
lib/model/req/login_req.g.dart

@@ -9,7 +9,7 @@ part of 'login_req.dart';
9 9
 LoginReq _$LoginReqFromJson(Map<String, dynamic> json) => LoginReq(
10 10
       username: json['username'] as String?,
11 11
       password: json['password'] as String?,
12
-      platform: json['platform'] as int? ?? 1,
12
+      platform: (json['platform'] as num?)?.toInt() ?? 1,
13 13
     );
14 14
 
15 15
 Map<String, dynamic> _$LoginReqToJson(LoginReq instance) => <String, dynamic>{

+ 5 - 5
lib/model/req/sample_task_list_req.g.dart

@@ -8,13 +8,13 @@ part of 'sample_task_list_req.dart';
8 8
 
9 9
 SampleTaskListReq _$SampleTaskListReqFromJson(Map<String, dynamic> json) =>
10 10
     SampleTaskListReq(
11
-      pageNo: json['pageNo'] as int? ?? 1,
12
-      pageSize: json['pageSize'] as int? ?? 10,
13
-      deliveryStatus: json['deliveryStatus'] as int?,
14
-      rwlx: json['rwlx'] as int?,
11
+      pageNo: (json['pageNo'] as num?)?.toInt() ?? 1,
12
+      pageSize: (json['pageSize'] as num?)?.toInt() ?? 10,
13
+      deliveryStatus: (json['deliveryStatus'] as num?)?.toInt(),
14
+      rwlx: (json['rwlx'] as num?)?.toInt(),
15 15
       cypzName: json['cypzName'] as String?,
16 16
       jyzb: json['jyzb'] as String?,
17
-      ypdj: json['ypdj'] as int?,
17
+      ypdj: (json['ypdj'] as num?)?.toInt(),
18 18
       qydq: json['qydq'] as String?,
19 19
     );
20 20
 

+ 7 - 0
lib/network/api.dart

@@ -10,6 +10,8 @@ import 'package:lszlgl/model/rsp/user_rsp.dart';
10 10
 import 'package:lszlgl/network/base_dio.dart';
11 11
 import 'package:retrofit/retrofit.dart';
12 12
 
13
+import '../drfit/database.dart';
14
+import '../model/req/device_req.dart';
13 15
 import '../model/rsp/district_rsp.dart';
14 16
 
15 17
 part 'api.g.dart';
@@ -83,4 +85,9 @@ abstract class Api {
83 85
   /// 上传图片
84 86
   @POST('/admin-api/infra/file/upload')
85 87
   Future<ApiRsp<String?>> upload(@Part(name: 'file') File file);
88
+
89
+  /// 提交设备绑定信息
90
+  @POST('/admin-api/zj/base-device/createBatch')
91
+  Future<ApiRsp> postDeviceInfos(@Body() List<DeviceReq> req);
92
+
86 93
 }

+ 30 - 0
lib/network/api.g.dart

@@ -513,6 +513,36 @@ class _Api implements Api {
513 513
     return value;
514 514
   }
515 515
 
516
+  @override
517
+  Future<ApiRsp<dynamic>> postDeviceInfos(List<DeviceReq> req) async {
518
+    const _extra = <String, dynamic>{};
519
+    final queryParameters = <String, dynamic>{};
520
+    final _headers = <String, dynamic>{};
521
+    final _data = req.map((e) => e.toJson()).toList();
522
+    final _result = await _dio
523
+        .fetch<Map<String, dynamic>>(_setStreamType<ApiRsp<dynamic>>(Options(
524
+      method: 'POST',
525
+      headers: _headers,
526
+      extra: _extra,
527
+    )
528
+            .compose(
529
+              _dio.options,
530
+              '/admin-api/zj/base-device/createBatch',
531
+              queryParameters: queryParameters,
532
+              data: _data,
533
+            )
534
+            .copyWith(
535
+                baseUrl: _combineBaseUrls(
536
+              _dio.options.baseUrl,
537
+              baseUrl,
538
+            ))));
539
+    final value = ApiRsp<dynamic>.fromJson(
540
+      _result.data!,
541
+      (json) => json as dynamic,
542
+    );
543
+    return value;
544
+  }
545
+
516 546
   RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
517 547
     if (T != dynamic &&
518 548
         !(requestOptions.responseType == ResponseType.bytes ||

+ 1 - 0
lib/network/my_api.dart

@@ -10,6 +10,7 @@ class MyApi {
10 10
   static String testUrl = 'http://121.36.17.6:19090';
11 11
 
12 12
   static Api get({Dio? dio, String? baseUrl}) {
13
+
13 14
     if (kReleaseMode) {
14 15
       baseUrl ??= productUrl;
15 16
     } else {

+ 2 - 0
lib/page/home/home_page.dart

@@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
4 4
 import 'package:lszlgl/base/base_state.dart';
5 5
 import 'package:lszlgl/page/sample_task/sample_task_list_tab_page.dart';
6 6
 
7
+import '../print/print_page.dart';
8
+
7 9
 /// 首页
8 10
 class HomePage extends StatefulWidget {
9 11
   const HomePage({Key? key}) : super(key: key);

+ 9 - 1
lib/page/login/login_page.dart

@@ -12,9 +12,13 @@ import 'package:lszlgl/service/user_service.dart';
12 12
 import 'package:lszlgl/widget/button.dart';
13 13
 
14 14
 import '../../config/reresh_config.dart';
15
+import '../../drfit/database.dart';
15 16
 import '../../network/base_dio.dart';
16 17
 import '../../utils/location_utils.dart';
17 18
 import '../../utils/sp_utils.dart';
19
+import 'package:lszlgl/service/print_service.dart';
20
+import 'package:path_provider/path_provider.dart';
21
+import 'package:path/path.dart' as p;
18 22
 
19 23
 /// 登录页面
20 24
 class LoginPage extends StatefulWidget {
@@ -83,7 +87,7 @@ class _LoginPageState extends BaseLifecycleState<LoginPage> {
83 87
 
84 88
   @override
85 89
   void onFirstShow(Duration timeStamp) async {
86
-    MyNavigator.showLoading();
90
+    // MyNavigator.showLoading();
87 91
 
88 92
     /// 初始化基础库 start
89 93
     BaseDio.get().init();
@@ -92,6 +96,10 @@ class _LoginPageState extends BaseLifecycleState<LoginPage> {
92 96
     LocationUtils.updatePrivacyShow(true, true);
93 97
     LocationUtils.updatePrivacyAgree(true);
94 98
     LocationUtils.setApiKey(kReleaseMode ? '2c783509376e267b24d63b21681686fa' : '7d0c033909f84adc14a0e60a835f044f', '');
99
+    /// 获取手机设备信息
100
+    PrintService.getDeviceInfo();
101
+    /// 同步数据库里的蓝牙设备信息到服务器
102
+    database.savaBleDataToServer();
95 103
 
96 104
     /// 初始化基础库 end
97 105
     MyNavigator.dismissLoading();

+ 95 - 0
lib/page/print/connect_print_page.dart

@@ -0,0 +1,95 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:flutter/services.dart';
3
+import 'package:lszlgl/base/base_lifecycle_state.dart';
4
+import 'package:lszlgl/drfit/model_factory.dart';
5
+import 'package:lszlgl/main.dart';
6
+import 'package:lszlgl/service/print_service.dart';
7
+import 'package:lszlgl/widget/loading_widget.dart';
8
+import 'package:lszlgl/widget/page_widget.dart';
9
+import 'package:signature/signature.dart';
10
+import 'package:lszlgl/widget/button.dart';
11
+
12
+import '../../drfit/database.dart';
13
+import '../../plugin/bluetooth_plugin.dart';
14
+import '../home/home_page.dart';
15
+
16
+class ConnectPrintPageArgs {
17
+  int count;
18
+
19
+  ConnectPrintPageArgs({this.count = 1});
20
+
21
+  @override
22
+  String toString() {
23
+    return {'count': count}.toString();
24
+  }
25
+}
26
+
27
+/// 电子签名
28
+class ConnectPrintPage extends StatefulWidget {
29
+  final ConnectPrintPageArgs args;
30
+
31
+  const ConnectPrintPage({
32
+    super.key,
33
+    required this.args,
34
+  });
35
+
36
+  @override
37
+  State<ConnectPrintPage> createState() => _ConnectPrintPageState();
38
+}
39
+
40
+class _ConnectPrintPageState extends BaseLifecycleState<ConnectPrintPage> {
41
+
42
+
43
+  
44
+
45
+  @override
46
+  Widget build(BuildContext context) {
47
+    return myScaffold(
48
+      child: Column(
49
+        children: [
50
+          myAppBar(title: '连接打印机'),
51
+          const SizedBox(height: 18),
52
+          // ...List.generate(
53
+          //   serviceList.length,
54
+          //       (index) => buildServiceItem(serviceList[index]),
55
+          // ).toList(),
56
+          
57
+        ],
58
+      ),
59
+    );
60
+  }
61
+
62
+  // Widget buildServiceItem(ServiceModel service) {
63
+  //   return GestureDetector(
64
+  //     onTap: service.onTap,
65
+  //     child: Container(
66
+  //       margin: const EdgeInsets.only(left: 12, right: 12, bottom: 22),
67
+  //       padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
68
+  //       clipBehavior: Clip.hardEdge,
69
+  //       decoration: BoxDecoration(
70
+  //         borderRadius: const BorderRadius.all(Radius.circular(12)),
71
+  //         boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.1), offset: const Offset(0, 5), blurRadius: 4)],
72
+  //         image: const DecorationImage(image: AssetImage(imgHomeListBg), fit: BoxFit.fill),
73
+  //       ),
74
+  //       child: Row(
75
+  //         children: [
76
+  //           Image.asset(service.icon, height: 64),
77
+  //           const SizedBox(width: 12),
78
+  //           Expanded(
79
+  //             child: Column(
80
+  //               mainAxisSize: MainAxisSize.min,
81
+  //               children: [
82
+  //                 Text(
83
+  //                   "${service.name}${scanDeviceState == DeviceState.scanStart ? "中..." : ""}",
84
+  //                   textAlign: TextAlign.center,
85
+  //                   style: const TextStyle(color: Color(0xFF333333), fontSize: 20, fontWeight: FontWeight.w500),
86
+  //                 ),
87
+  //               ],
88
+  //             ),
89
+  //           ),
90
+  //         ],
91
+  //       ),
92
+  //     ),
93
+  //   );
94
+  // }
95
+}

+ 322 - 0
lib/page/print/print_page.dart

@@ -0,0 +1,322 @@
1
+import 'dart:async';
2
+import 'dart:convert';
3
+
4
+import 'package:amap_flutter_location/amap_location_option.dart';
5
+import 'package:flutter/material.dart';
6
+import 'package:flutter/services.dart';
7
+import 'package:lszlgl/base/base_lifecycle_state.dart';
8
+import 'package:lszlgl/page/print/connect_print_page.dart';
9
+import 'package:signature/signature.dart';
10
+import 'package:lszlgl/widget/button.dart';
11
+
12
+import '../../drfit/database.dart';
13
+import '../../drfit/model_factory.dart';
14
+import '../../main.dart';
15
+import '../../plugin/bluetooth_plugin.dart';
16
+import '../../service/print_service.dart';
17
+import '../../utils/location_utils.dart';
18
+import '../home/home_page.dart';
19
+import 'dart:ui' as ui;
20
+
21
+class PrintPageArgs {
22
+  /// 二维码数据
23
+  Uint8List? bytes;
24
+
25
+  PrintPageArgs({this.bytes});
26
+
27
+  @override
28
+  String toString() {
29
+    return {'bytes': bytes}.toString();
30
+  }
31
+}
32
+
33
+/// 电子签名
34
+class PrintPage extends StatefulWidget {
35
+  final PrintPageArgs args;
36
+
37
+  const PrintPage({
38
+    super.key,
39
+    required this.args,
40
+  });
41
+
42
+  @override
43
+  State<PrintPage> createState() => _PrintPageState();
44
+}
45
+
46
+class _PrintPageState extends BaseLifecycleState<PrintPage> {
47
+
48
+  late List<ServiceModel> serviceList;
49
+
50
+  late List<BlueDeviceInfo> deviceList;
51
+  late List<String> deviceMacList;
52
+
53
+  String scanDeviceState = '';
54
+
55
+  StreamSubscription? locationListener;
56
+  StreamSubscription? onReceiveDataStreamListener;
57
+  StreamSubscription? onDeviceStateStreamListener;
58
+  String phoneAddress = '';
59
+
60
+
61
+  @override
62
+  void initState() {
63
+    super.initState();
64
+    serviceList = [
65
+      ServiceModel(name: '搜索', icon: imgBleSearch, onTap: () => startScan()),
66
+      ServiceModel(name: '打印', icon: imgBlePrint, onTap: () => startPrint()),
67
+    ];
68
+    deviceList = [];
69
+    deviceMacList = [];
70
+
71
+    PrintService.hasBluetoothConnectDevice().then((result) {
72
+      if(result == false) {
73
+        PrintService.connectedDeviceList = [];
74
+        PrintService.connectedDeviceMacList = [];
75
+      }
76
+    });
77
+
78
+    getLocation();
79
+  }
80
+
81
+  /// 去打印
82
+  Future<void> startPrint() async {
83
+
84
+    int targetWidth = 560;
85
+    if(PrintService.connectedDeviceList.last.deviceName.contains("BTP-UP321")) {
86
+      targetWidth = 500;
87
+    }
88
+
89
+    final codec = await ui.instantiateImageCodec(
90
+      widget.args.bytes!,
91
+      targetHeight: 590,  // 640
92
+      targetWidth: targetWidth, // 560
93
+    );
94
+
95
+    final smallImage = (await codec.getNextFrame()).image;
96
+    ByteData? smallBytes = await smallImage.toByteData(format: ui.ImageByteFormat.png);
97
+    Uint8List? smallUint8List = smallBytes?.buffer.asUint8List();
98
+
99
+
100
+    MyNavigator.showLoading(msg: '打印中...');
101
+    await PrintService.startBluetoothPrintBitMap(smallUint8List!);
102
+    MyNavigator.dismiss();
103
+    MyNavigator.showToast('打印成功');
104
+  }
105
+
106
+  /// 去搜索
107
+  Future<void> startScan() async {
108
+
109
+    setState(() {
110
+      deviceList = [];
111
+      deviceMacList = [];
112
+    });
113
+    await PrintService.startBluetoothDiscovery();
114
+  }
115
+
116
+  Future<int> savaToSqlite(BlueDeviceInfo deviceInfo) async {
117
+    DeviceInfoTableCompanion tableCompanion = await ModelFactory.convertToTSlideComp(deviceInfo.deviceMac, deviceInfo.deviceName, phoneAddress);
118
+    return await database.deviceInfoTableDao.addOneDeviceComp(tableCompanion);
119
+  }
120
+
121
+  /// 去连接
122
+  Future<void> startConnect(BlueDeviceInfo deviceInfo) async {
123
+    if(deviceInfo.connectStateStr.contains('未配对')) {
124
+      MyNavigator.showLoading(msg: '配对中...');
125
+      await PrintService.startBluetoothPair(deviceInfo);
126
+    } else if(deviceInfo.connectStateStr.contains('已配对')) {
127
+      MyNavigator.showLoading(msg: '连接中...');
128
+      int connectResult =  await PrintService.startBluetoothConnect(deviceInfo);
129
+      if(connectResult == 0) { // 连接成功
130
+        MyNavigator.dismiss();
131
+        MyNavigator.showToast('连接成功');
132
+        deviceInfo.connectSuccess();
133
+        //addOneSlideComp
134
+
135
+        if(deviceMacList.contains(deviceInfo.deviceMac)) {
136
+          setState(() {
137
+            deviceMacList.removeWhere((element) => element == deviceInfo.deviceMac);
138
+            deviceList.removeWhere((element) => element.deviceMac == deviceInfo.deviceMac);
139
+          });
140
+        }
141
+        setState(() {
142
+          PrintService.connectedDeviceMacList.add(deviceInfo.deviceMac);
143
+          PrintService.connectedDeviceList.add(deviceInfo);
144
+        });
145
+        await savaToSqlite(deviceInfo); // 保存记录到数据库
146
+        database.savaBleDataToServer(); // 同步记录到服务器
147
+      } else {
148
+        MyNavigator.dismiss();
149
+        MyNavigator.showToast('连接失败');
150
+      }
151
+    } else if(deviceInfo.connectStateStr.contains('断开')) {
152
+      int connectResult =  await PrintService.endBluetoothConnect(deviceInfo);
153
+      if(connectResult == 0) {  // 断开成功
154
+        deviceInfo.disConnectSuccess();
155
+        if(PrintService.connectedDeviceMacList.contains(deviceInfo.deviceMac)) {
156
+          setState(() {
157
+            PrintService.connectedDeviceMacList.removeWhere((element) => element == deviceInfo.deviceMac);
158
+            PrintService.connectedDeviceList.removeWhere((element) => element.deviceMac == deviceInfo.deviceMac);
159
+          });
160
+        }
161
+        setState(() {
162
+          deviceMacList.add(deviceInfo.deviceMac);
163
+          deviceList.add(deviceInfo);
164
+        });
165
+      }
166
+    }
167
+  }
168
+
169
+  void getLocation() async {
170
+    bool granted = await LocationUtils.requestLocationPermission();
171
+    if (!granted) {
172
+      MyNavigator.showToast('请打开APP的定位权限');
173
+      return;
174
+    }
175
+    LocationUtils.setLocationOption(AMapLocationOption(onceLocation: true));
176
+    LocationUtils.startLocation();
177
+  }
178
+
179
+  void initLocation() {
180
+    locationListener = LocationUtils.onLocationChanged().listen((value) async {
181
+
182
+      phoneAddress = value['address'] as String;
183
+    });
184
+  }
185
+
186
+  @override
187
+  void onInit() {
188
+
189
+    onReceiveDataStreamListener = BluetoothPlugin.instance.onReceiveDataStream.listen((deviceInfo) {
190
+      if(deviceInfo != null) {
191
+        if(deviceMacList.contains(deviceInfo.deviceMac)) {
192
+          setState(() {
193
+            deviceMacList.removeWhere((element) => element == deviceInfo.deviceMac);
194
+            deviceList.removeWhere((element) => element.deviceMac == deviceInfo.deviceMac);
195
+          });
196
+        }
197
+        setState(() {
198
+          // 只显示支持的打印机
199
+          if(deviceInfo.deviceName.contains('BTP') || deviceInfo.deviceName.contains('B3S') || deviceInfo.deviceName.contains('A8')) {
200
+            deviceMacList.add(deviceInfo.deviceMac);
201
+            deviceList.add(deviceInfo);
202
+          }
203
+        });
204
+      }
205
+    });
206
+
207
+    onDeviceStateStreamListener = BluetoothPlugin.instance.onDeviceStateStream.listen((deviceState) {
208
+      if(deviceState == DeviceState.pairEnd) {
209
+        MyNavigator.dismiss();
210
+      }
211
+      setState(() {
212
+        scanDeviceState = deviceState ?? "";
213
+      });
214
+    });
215
+
216
+    initLocation();
217
+  }
218
+
219
+  @override
220
+  void onDestroy() {
221
+    LocationUtils.stopLocation();
222
+    LocationUtils.destroy();
223
+    locationListener?.cancel();
224
+    onDeviceStateStreamListener?.cancel();
225
+    onReceiveDataStreamListener?.cancel();
226
+  }
227
+
228
+  @override
229
+  Widget build(BuildContext context) {
230
+    return myScaffold(
231
+      child: Column(
232
+        children: [
233
+          myAppBar(title: '打印二维码'),
234
+          const SizedBox(height: 18),
235
+          buildServiceItem(serviceList[0]),
236
+          const Text('已连接打印机'),
237
+          ...List.generate(
238
+            PrintService.connectedDeviceList.length,
239
+                (index) => buildDeviceItem(PrintService.connectedDeviceList[index]),
240
+          ).toList(),
241
+          const Text('可用打印机'),
242
+          ...List.generate(
243
+            deviceList.length,
244
+                (index) => buildDeviceItem(deviceList[index]),
245
+          ).toList(),
246
+          Offstage(
247
+            offstage: PrintService.connectedDeviceList.isEmpty,
248
+            child: buildServiceItem(serviceList[1]),
249
+          )
250
+        ],
251
+      ),
252
+    );
253
+  }
254
+
255
+  Widget buildServiceItem(ServiceModel service) {
256
+    return GestureDetector(
257
+      onTap: service.onTap,
258
+      child: Container(
259
+        margin: const EdgeInsets.only(left: 12, right: 12, bottom: 22),
260
+        padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
261
+        clipBehavior: Clip.hardEdge,
262
+        decoration: BoxDecoration(
263
+          borderRadius: const BorderRadius.all(Radius.circular(12)),
264
+          boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.1), offset: const Offset(0, 5), blurRadius: 4)],
265
+          image: const DecorationImage(image: AssetImage(imgHomeListBg), fit: BoxFit.fill),
266
+        ),
267
+        child: Row(
268
+          children: [
269
+            Image.asset(service.icon, height: 64),
270
+            const SizedBox(width: 12),
271
+            Expanded(
272
+              child: Column(
273
+                mainAxisSize: MainAxisSize.min,
274
+                children: [
275
+                  Text(
276
+                    "${service.name}${(service.name.contains('搜索')&&scanDeviceState == DeviceState.scanStart) ? "中..." : ""}",
277
+                    textAlign: TextAlign.center,
278
+                    style: const TextStyle(color: Color(0xFF333333), fontSize: 20, fontWeight: FontWeight.w500),
279
+                  ),
280
+                ],
281
+              ),
282
+            ),
283
+          ],
284
+        ),
285
+      ),
286
+    );
287
+  }
288
+
289
+  Widget buildDeviceItem(BlueDeviceInfo deviceInfo) {
290
+    return GestureDetector(
291
+      onTap: () {
292
+        // 连接
293
+        startConnect(deviceInfo);
294
+      },
295
+      child: Container(
296
+        margin: const EdgeInsets.only(left: 12, right: 12, bottom: 22),
297
+        padding: const EdgeInsets.symmetric(vertical: 10),
298
+        clipBehavior: Clip.hardEdge,
299
+        decoration: BoxDecoration(
300
+          borderRadius: const BorderRadius.all(Radius.circular(12)),
301
+          boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.1), offset: const Offset(0, 5), blurRadius: 4)],
302
+          image: const DecorationImage(image: AssetImage(imgHomeListBg), fit: BoxFit.fill),
303
+        ),
304
+        child: Row(
305
+          mainAxisAlignment: MainAxisAlignment.spaceAround,
306
+          children: [
307
+            Text(
308
+              deviceInfo.deviceName,
309
+              textAlign: TextAlign.center,
310
+              style: const TextStyle(color: Color(0xFF333333), fontSize: 15, fontWeight: FontWeight.w500),
311
+            ),
312
+            Text(
313
+              deviceInfo.connectStateStr,
314
+              textAlign: TextAlign.center,
315
+              style: const TextStyle(color: Color(0xFF333333), fontSize: 15, fontWeight: FontWeight.w500),
316
+            ),
317
+          ],
318
+        ),
319
+      ),
320
+    );
321
+  }
322
+}

+ 12 - 1
lib/page/sample_task/reap_sample_detail/reap_sample_task_page.dart

@@ -8,6 +8,7 @@ import 'package:image_gallery_saver/image_gallery_saver.dart';
8 8
 import 'package:lszlgl/base/base_lifecycle_state.dart';
9 9
 import 'package:lszlgl/config/colors.dart';
10 10
 import 'package:lszlgl/model/rsp/sample_task_rsp.dart';
11
+import 'package:lszlgl/page/print/print_page.dart';
11 12
 import 'package:lszlgl/page/sample_task/reap_sample_detail/reap_sample_basic_detail_page.dart';
12 13
 import 'package:lszlgl/page/sample_task/reap_sample_detail/reap_sample_disaster_detail_page.dart';
13 14
 import 'package:lszlgl/page/sample_task/reap_sample_detail/reap_sample_medicine_detail_page.dart';
@@ -18,9 +19,11 @@ import 'package:lszlgl/page/signature/signature_page.dart';
18 19
 import '../../../base/base_vm.dart';
19 20
 import '../../../main.dart';
20 21
 import '../../../network/my_api.dart';
22
+import '../../../plugin/bluetooth_plugin.dart';
21 23
 import '../../../utils/file_utils.dart';
22 24
 import '../../../widget/button.dart';
23 25
 import '../../../widget/page_widget.dart';
26
+import 'dart:ui' as ui;
24 27
 
25 28
 class ReapSampleTaskPageArgs {
26 29
   final bool detail;
@@ -189,7 +192,7 @@ class _ReapSampleTaskPageState extends BaseLifecycleState<ReapSampleTaskPage> wi
189 192
             ),
190 193
             Row(
191 194
               children: [
192
-                const Expanded(child: MyButton('打印')),
195
+                Expanded(child: MyButton('打印', onTap: () => printPic(picInfo.name))),
193 196
                 const SizedBox(width: 16),
194 197
                 Expanded(child: MyButton('保存图片', onTap: () => savePic(picInfo.name))),
195 198
               ],
@@ -200,6 +203,14 @@ class _ReapSampleTaskPageState extends BaseLifecycleState<ReapSampleTaskPage> wi
200 203
     );
201 204
   }
202 205
 
206
+  Future<void> printPic(String? name) async {
207
+
208
+    Uint8List? bytes = await FileUtils.getBitmapFromContext(ewmKey.currentContext);
209
+
210
+    var args = PrintPageArgs(bytes: bytes);
211
+    await MyRouter.startPrint(args: args);
212
+  }
213
+
203 214
   void savePic(String? name) async {
204 215
     MyNavigator.showLoading(msg: '保存中...');
205 216
     Uint8List? bytes = await FileUtils.getBitmapFromContext(ewmKey.currentContext);

+ 183 - 0
lib/plugin/bluetooth_plugin.dart

@@ -0,0 +1,183 @@
1
+import 'dart:io';
2
+
3
+import 'package:flutter/services.dart';
4
+import 'package:logger/logger.dart';
5
+
6
+import '../main.dart';
7
+import '../router/my_navigator.dart';
8
+
9
+class BluetoothPlugin {
10
+  factory BluetoothPlugin() => _instance;
11
+  BluetoothPlugin._();
12
+  static final BluetoothPlugin _instance = BluetoothPlugin._();
13
+  static BluetoothPlugin get instance => _instance;
14
+
15
+   final String delimiterStr = "_NIMBOT_";
16
+  final MethodChannel _channel = const MethodChannel("io.flutter.plugins/bluetooth/methods");
17
+  final EventChannel _onReceiveDataStream = const EventChannel('io.flutter.plugins/bluetooth/onDiscoveryDevice');
18
+  final EventChannel _onDeviceStateStream = const EventChannel('io.flutter.plugins/bluetooth/onDeviceState');
19
+
20
+
21
+  //是否打开蓝牙
22
+  Future<bool> isBleOpen() async {
23
+    if (Platform.isIOS) {
24
+      return true;
25
+    }
26
+    return _channel.invokeMethod('isBleOpen').then<bool>((d) => d);
27
+  }
28
+
29
+  //安卓版本是否大于等于31
30
+  Future<bool> isSDKIntGreaterOrEqual31() async {
31
+    if (Platform.isIOS) {
32
+      return true;
33
+    }
34
+    return _channel.invokeMethod('isSDKIntGreaterOrEqual').then<bool>((d) => d);
35
+  }
36
+
37
+  //开始扫描
38
+  Future<int> startBluetoothDiscovery() async {
39
+    if (Platform.isIOS) {
40
+      return 0;
41
+    }
42
+    return _channel.invokeMethod('startBluetoothDiscovery').then<int>((d) => d);
43
+  }
44
+
45
+  //开始配对
46
+  Future<int> startBluetoothPair(BlueDeviceInfo deviceInfo) async {
47
+    if (Platform.isIOS) {
48
+      return 0;
49
+    }
50
+    String deviceInfoStr = deviceInfo.deviceName+delimiterStr+deviceInfo.deviceMac+delimiterStr+deviceInfo.connectState;
51
+    return _channel.invokeMethod('startBluetoothPair', deviceInfoStr).then<int>((d) => d);
52
+  }
53
+
54
+  //开始连接
55
+  Future<int> startBluetoothConnect(BlueDeviceInfo deviceInfo) async {
56
+
57
+    if (Platform.isIOS) {
58
+      return 0;
59
+    }
60
+    String deviceInfoStr = deviceInfo.deviceName+delimiterStr+deviceInfo.deviceMac+delimiterStr+deviceInfo.connectState;
61
+    return _channel.invokeMethod('startBluetoothConnect', deviceInfoStr).then<int>((d) => d);
62
+  }
63
+
64
+  //断开连接
65
+  Future<int> endBluetoothConnect(BlueDeviceInfo deviceInfo) async {
66
+
67
+    if (Platform.isIOS) {
68
+      return 0;
69
+    }
70
+    String deviceInfoStr = deviceInfo.deviceName+delimiterStr+deviceInfo.deviceMac+delimiterStr+deviceInfo.connectState;
71
+    return _channel.invokeMethod('endBluetoothConnect', deviceInfoStr).then<int>((d) => d);
72
+  }
73
+
74
+  //断开连接
75
+  Future<bool> hasBluetoothConnectDevice() async {
76
+
77
+    if (Platform.isIOS) {
78
+      return false;
79
+    }
80
+    return _channel.invokeMethod('hasBluetoothConnectDevice').then<bool>((d) => d);
81
+  }
82
+
83
+
84
+  //开始打印
85
+  Future<bool> startBluetoothPrintBitMap(Uint8List bytes) async {
86
+
87
+    if (Platform.isIOS) {
88
+      return false;
89
+    }
90
+    return _channel.invokeMethod('startBluetoothPrintBitMap', bytes).then<bool>((d) => d);
91
+  }
92
+
93
+  //开始打印
94
+  Future<bool> startBluetoothPrintBarCodeWithText(String barCode, String text) async {
95
+
96
+    if (Platform.isIOS) {
97
+      return false;
98
+    }
99
+    return _channel.invokeMethod('startBluetoothPrintBarCodeWithText', {"barCode": barCode, "text": text}).then<bool>((d) => d);
100
+  }
101
+
102
+
103
+
104
+  /// 接收数据监听
105
+  Stream<BlueDeviceInfo?> get onReceiveDataStream async* {
106
+
107
+    yield* _onReceiveDataStream
108
+        .receiveBroadcastStream()
109
+        .map((buffer) {
110
+
111
+      String str = buffer;
112
+      List<String> strList = str.split(delimiterStr);
113
+
114
+      if(strList.length == 3) {
115
+        BlueDeviceInfo deviceInfo = BlueDeviceInfo(deviceName: strList[0], deviceMac: strList[1], connectState: strList[2]);
116
+        return deviceInfo;
117
+      }
118
+      return null;
119
+    });
120
+  }
121
+
122
+  /// 接收数据监听
123
+  Stream<String?> get onDeviceStateStream async* {
124
+
125
+    yield* _onDeviceStateStream
126
+        .receiveBroadcastStream()
127
+        .map((buffer) {
128
+
129
+      return buffer.toString();
130
+    });
131
+  }
132
+}
133
+
134
+
135
+class DeviceState {
136
+
137
+  static String scanStart = "scanStart";
138
+
139
+  static String scanEnd = "scanEnd";
140
+
141
+  static String pairEnd = "pairEnd";
142
+}
143
+
144
+class BlueDeviceInfo {
145
+
146
+  String deviceName;
147
+  String deviceMac;
148
+  String connectState;
149
+
150
+  String? _connectStateStr;
151
+
152
+  BlueDeviceInfo({required this.deviceName, required this.deviceMac, required this.connectState});
153
+
154
+  String get connectStateStr {
155
+
156
+    if(connectState == '10') {
157
+      _connectStateStr = "未配对(点击进行配对)";
158
+    }
159
+    if(connectState == '11') {
160
+      _connectStateStr = "配对中";
161
+    }
162
+    if(connectState == '12') {
163
+      _connectStateStr = "已配对(点击进行连接)";
164
+    }
165
+    if(connectState == '13') {
166
+      _connectStateStr = "断开(点击断开连接)";
167
+    }
168
+    return _connectStateStr ?? "未知";
169
+  }
170
+
171
+  void connectSuccess() {
172
+    connectState = '13';
173
+  }
174
+
175
+  void disConnectSuccess() {
176
+    connectState = '12';
177
+  }
178
+
179
+  @override
180
+  String toString() {
181
+    return {'deviceName:': deviceName, "mac:": deviceMac, "state:": connectState}.toString();
182
+  }
183
+}

+ 19 - 0
lib/router/my_router.dart

@@ -1,6 +1,8 @@
1 1
 import 'package:lszlgl/base/base_lifecycle_state.dart';
2 2
 import 'package:lszlgl/page/login/login_page.dart';
3 3
 import 'package:lszlgl/page/main_tab_page.dart';
4
+import 'package:lszlgl/page/print/connect_print_page.dart';
5
+import 'package:lszlgl/page/print/print_page.dart';
4 6
 import 'package:lszlgl/page/sample_task/reap_sample_detail/reap_sample_task_page.dart';
5 7
 import 'package:lszlgl/page/sample_task/sample_task_list_tab_page.dart';
6 8
 import 'package:lszlgl/page/user_center/account_manage_page.dart';
@@ -30,6 +32,10 @@ const rReapSampleTaskPage = '/ReapSampleTaskPage';
30 32
 const rStockSampleTaskPage = '/StockSampleTaskPage';
31 33
 // 电子签名
32 34
 const rSignaturePage = '/SignaturePage';
35
+// 打印
36
+const rPrintPage = '/PrintPage';
37
+// 连接
38
+const rConnectPrintPage = '/ConnectPrintPage';
33 39
 // 扫一扫
34 40
 const rQrCodeScanPage = '/QrCodeScanPage';
35 41
 
@@ -44,6 +50,8 @@ final Map<String, MyNavigatorBuilder> rRouteMap = {
44 50
   rReapSampleTaskPage: (context, args) => ReapSampleTaskPage(args: args as ReapSampleTaskPageArgs),
45 51
   rStockSampleTaskPage: (context, args) => StockSampleTaskPage(args: args as StockSampleTaskPageArgs),
46 52
   rSignaturePage: (context, args) => SignaturePage(args: args as SignaturePageArgs),
53
+  rPrintPage: (context, args) => PrintPage(args: args as PrintPageArgs),
54
+  rConnectPrintPage: (context, args) => ConnectPrintPage(args: args as ConnectPrintPageArgs),
47 55
   rQrCodeScanPage: (context, args) => const QrCodeScanPage(),
48 56
 };
49 57
 
@@ -108,6 +116,17 @@ class MyRouter {
108 116
     return MyNavigator.push(rSignaturePage, args: args ?? SignaturePageArgs());
109 117
   }
110 118
 
119
+  /// 打印任务
120
+  static Future<dynamic> startPrint({PrintPageArgs? args}) {
121
+    return MyNavigator.push(rPrintPage, args: args ?? PrintPageArgs());
122
+  }
123
+
124
+  /// 打印任务
125
+  static Future<dynamic> startConnectPrint({ConnectPrintPageArgs? args}) {
126
+    return MyNavigator.push(
127
+        rConnectPrintPage, args: args ?? ConnectPrintPageArgs());
128
+  }
129
+  
111 130
   /// 扫一扫
112 131
   static Future<dynamic> startQrCodeScan() {
113 132
     return MyNavigator.push(rQrCodeScanPage);

+ 166 - 0
lib/service/print_service.dart

@@ -0,0 +1,166 @@
1
+
2
+
3
+import 'dart:ffi';
4
+import 'dart:io';
5
+import 'package:device_info_plus/device_info_plus.dart';
6
+import 'package:lszlgl/utils/permission_utils.dart';
7
+import 'package:permission_handler/permission_handler.dart';
8
+
9
+import '../plugin/bluetooth_plugin.dart';
10
+import '../router/my_navigator.dart';
11
+import 'package:flutter/material.dart';
12
+import 'package:flutter/services.dart';
13
+
14
+class PrintService {
15
+  PrintService._();
16
+
17
+  static final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
18
+
19
+  static List<BlueDeviceInfo> connectedDeviceList = [];
20
+  static List<String> connectedDeviceMacList = [];
21
+
22
+  static AndroidDeviceInfo? deviceInfo;
23
+
24
+  static Future<AndroidDeviceInfo?> getDeviceInfo() async {
25
+    if (Platform.isIOS) {
26
+
27
+    } else if(Platform.isAndroid) {
28
+      if(deviceInfo != null) {
29
+        deviceInfo;
30
+      }
31
+      AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
32
+      deviceInfo = androidInfo;
33
+      return deviceInfo;
34
+    }
35
+  }
36
+
37
+  //是否打开蓝牙
38
+  static Future<bool> isBleOpen() async {
39
+    return await BluetoothPlugin.instance.isBleOpen();
40
+  }
41
+
42
+  //安卓版本是否大于等于31
43
+  static Future<bool> isSDKIntGreaterOrEqual() async {
44
+    return await BluetoothPlugin.instance.isSDKIntGreaterOrEqual31();
45
+  }
46
+
47
+  //是否支持蓝牙操作
48
+  static Future<bool> canExecAction() async {
49
+    if (await isBleOpen()) {
50
+
51
+      bool allGranted = false;
52
+      String permissionName = '';
53
+      // permissionRequest
54
+     if(await isSDKIntGreaterOrEqual()) {
55
+
56
+       if (await PermissionHandler.handleWith(Permission.bluetoothScan)) {
57
+         allGranted = true;
58
+       } else {
59
+         permissionName = Permission.bluetoothScan.toString();
60
+         allGranted = false;
61
+       }
62
+       if(await PermissionHandler.handleWith(Permission.bluetoothConnect)) {
63
+         allGranted = true;
64
+       } else {
65
+         permissionName = '$permissionName ${Permission.bluetoothConnect}';
66
+         allGranted = false;
67
+       }
68
+     }
69
+      //所有权限申请通过
70
+      if (await PermissionHandler.handleWith(Permission.location)) {
71
+        allGranted = true;
72
+      } else {
73
+        permissionName = Permission.location.toString();
74
+        allGranted = false;
75
+      }
76
+     if(allGranted) {
77
+       return true;
78
+     } else {
79
+       MyNavigator.showToast('权限打开失败:$permissionName');
80
+       return false;
81
+     }
82
+    } else {
83
+      // 蓝牙未开启
84
+      MyNavigator.showToast('蓝牙未开启');
85
+      return false;
86
+    }
87
+  }
88
+
89
+  //开始扫描
90
+  static Future<int> startBluetoothDiscovery() async {
91
+
92
+    if(await canExecAction()) {
93
+      return await BluetoothPlugin.instance.startBluetoothDiscovery();
94
+    } else {
95
+      return 0;
96
+    }
97
+  }
98
+
99
+  //开始配对
100
+  static Future<int> startBluetoothPair(BlueDeviceInfo deviceInfo) async {
101
+
102
+    if(await canExecAction()) {
103
+      return await BluetoothPlugin.instance.startBluetoothPair(deviceInfo);
104
+    } else {
105
+      return 0;
106
+    }
107
+  }
108
+
109
+  //开始连接
110
+  static Future<int> startBluetoothConnect(BlueDeviceInfo deviceInfo) async {
111
+
112
+    if(await canExecAction()) {
113
+      return await BluetoothPlugin.instance.startBluetoothConnect(deviceInfo);
114
+    } else {
115
+      return 0;
116
+    }
117
+  }
118
+
119
+  //断开连接
120
+  static Future<int> endBluetoothConnect(BlueDeviceInfo deviceInfo) async {
121
+
122
+    if(await canExecAction()) {
123
+      return await BluetoothPlugin.instance.endBluetoothConnect(deviceInfo);
124
+    } else {
125
+      return 0;
126
+    }
127
+  }
128
+
129
+  //是否有连接中的设备
130
+  static Future<bool> hasBluetoothConnectDevice() async {
131
+
132
+    if(await canExecAction()) {
133
+      return await BluetoothPlugin.instance.hasBluetoothConnectDevice();
134
+    } else {
135
+      return false;
136
+    }
137
+  }
138
+
139
+  //开始打印图片
140
+  static Future<bool> startBluetoothPrintBitMap(Uint8List bytes) async {
141
+
142
+    if(await canExecAction()) {
143
+       return await BluetoothPlugin.instance.startBluetoothPrintBitMap(bytes);
144
+    } else {
145
+      return false;
146
+    }
147
+  }
148
+
149
+  //开始打印二维码和文本
150
+  static Future<bool> startBluetoothPrintBarCodeWithText(String barCode, String text) async {
151
+
152
+    if(await canExecAction()) {
153
+      return await BluetoothPlugin.instance.startBluetoothPrintBarCodeWithText(barCode, text);
154
+    } else {
155
+      return false;
156
+    }
157
+  }
158
+
159
+
160
+
161
+
162
+
163
+
164
+}
165
+
166
+

+ 12 - 0
lib/service/user_service.dart

@@ -54,4 +54,16 @@ class UserService {
54 54
     if (user == null) return null;
55 55
     return UserRsp.fromJson(jsonDecode(user));
56 56
   }
57
+
58
+  /// 存储数据库DeviceInfoTable表的id号,用于记录同步的位置
59
+  Future<void> saveDeviceInfoTableTime(int timeId) async {
60
+
61
+    await SPUtils.getInstance().saveInt('DeviceInfoTableTimeInt', timeId);
62
+  }
63
+
64
+  /// 获取登录信息
65
+  int getDeviceInfoTableTime() {
66
+    return SPUtils.getInstance().getInt('DeviceInfoTableTimeInt') ?? -1;
67
+  }
68
+
57 69
 }

+ 1 - 0
lib/utils/file_utils.dart

@@ -4,6 +4,7 @@ import 'dart:ui' as ui;
4 4
 
5 5
 import 'package:flutter/cupertino.dart';
6 6
 import 'package:flutter/rendering.dart';
7
+import 'package:lszlgl/main.dart';
7 8
 import 'package:path_provider/path_provider.dart';
8 9
 
9 10
 class FileUtils {

+ 75 - 0
lib/utils/permission_utils.dart

@@ -0,0 +1,75 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:permission_handler/permission_handler.dart';
3
+
4
+import '../router/my_navigator.dart';
5
+
6
+
7
+class PermissionHandler {
8
+  static Future<bool> handleWith(Permission permission,
9
+      {bool showTip = true}) async {
10
+    if (await permission.request().isGranted ||
11
+        await permission.request().isLimited) {
12
+      return true;
13
+    }
14
+
15
+    if (await permission.isPermanentlyDenied ||
16
+        await permission.isRestricted ||
17
+        await permission.isDenied) {
18
+      if (showTip) {
19
+
20
+        MyNavigator.showDialog(
21
+          builder: (_) => PermissionAlertDialog(permission: permission),
22
+        );
23
+      }
24
+    }
25
+    return false;
26
+  }
27
+}
28
+
29
+class PermissionAlertDialog extends StatelessWidget {
30
+  final Permission permission;
31
+
32
+  const PermissionAlertDialog({Key? key, required this.permission}) : super(key: key);
33
+
34
+  @override
35
+  Widget build(BuildContext context) {
36
+    String content = '';
37
+    if (this.permission is PermissionWithService) {
38
+      if (this.permission == Permission.location) {
39
+        content = '请前往系统设置打开定位权限';
40
+      }
41
+    }
42
+
43
+    if (this.permission is Permission) {
44
+      if (this.permission == Permission.photos) {
45
+        content = '请前往系统设置打开相册权限';
46
+      } else if (this.permission == Permission.camera) {
47
+        content = '请前往系统设置打开相机权限';
48
+      } else if (this.permission == Permission.microphone) {
49
+        content = '请前往系统设置打开麦克风权限';
50
+      } else if (this.permission == Permission.storage) {
51
+        content = '请前往系统设置打开存储权限';
52
+      } else if (this.permission == Permission.bluetoothScan) {
53
+        content = '请前往系统设置打开蓝牙权限';
54
+      } else {
55
+        content = '请前往系统设置打开该权限';
56
+      }
57
+    }
58
+
59
+    return AlertDialog(
60
+      title: Text('提示'),
61
+      content: Text(content),
62
+      backgroundColor: Colors.white,
63
+      elevation: 24,
64
+      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
65
+      actions: <Widget>[
66
+        TextButton(
67
+          child: Text('知道了'),
68
+          onPressed: () {
69
+            // XRouter.pop(context);
70
+          },
71
+        ),
72
+      ],
73
+    );
74
+  }
75
+}

+ 5 - 0
pubspec.yaml

@@ -89,6 +89,10 @@ dependencies:
89 89
   flutter_spinkit: ^5.2.1
90 90
   # 保存到相册
91 91
   image_gallery_saver: ^2.0.3
92
+  # 数据库
93
+  drift: ^2.18.0
94
+  #
95
+  sqlite3_flutter_libs: ^0.5.0
92 96
 
93 97
 dev_dependencies:
94 98
   flutter_test:
@@ -105,6 +109,7 @@ dev_dependencies:
105 109
   build_runner: '>=2.3.0 <4.0.0'
106 110
   # JSON转实体类
107 111
   json_serializable: ^6.6.2
112
+  drift_dev: ^2.2.0
108 113
 
109 114
 # For information on the generic Dart part of this file, see the
110 115
 # following page: https://dart.dev/tools/pub/pubspec