Browse Source

V2.7.6
1.新增语音播报最新和语音暂停
2.新增USB扫码头扫描二维码数据
3.马里奥设备重启

xulh 6 months ago
parent
commit
eea4768951
31 changed files with 1815 additions and 41 deletions
  1. 5 0
      app/build.gradle
  2. 9 0
      app/src/main/AndroidManifest.xml
  3. 3 0
      app/src/main/java/com/hh/arome/bean/ampe/AmpeActionDataBean.java
  4. 15 2
      app/src/main/java/com/hh/arome/bean/ampe/AmpeAppletRequestBean.java
  5. 3 0
      app/src/main/java/com/hh/arome/bean/ampe/AmpeAppletResultBean.java
  6. 7 1
      app/src/main/java/com/hh/arome/common/Constants.java
  7. 1 1
      app/src/main/java/com/hh/arome/common/deviceui/BaseDeviceUI.java
  8. 13 0
      app/src/main/java/com/hh/arome/common/deviceui/D2DeviceUI.java
  9. 3 0
      app/src/main/java/com/hh/arome/common/deviceui/IDeviceUI.java
  10. 14 0
      app/src/main/java/com/hh/arome/common/deviceui/K8DeviceUI.java
  11. 8 0
      app/src/main/java/com/hh/arome/common/deviceui/MlaDeviceUI.java
  12. 5 0
      app/src/main/java/com/hh/arome/common/deviceui/NullDeviceUI.java
  13. 15 0
      app/src/main/java/com/hh/arome/common/deviceui/ZpDeviceUI.java
  14. 7 7
      app/src/main/java/com/hh/arome/common/scan/idcard/manymodels/TbIDCardScan.java
  15. 27 11
      app/src/main/java/com/hh/arome/common/voice/MTTSDemo.java
  16. 6 7
      app/src/main/java/com/hh/arome/ui/MainActivity.java
  17. 8 6
      app/src/main/java/com/hh/arome/ui/PasswordActivity.java
  18. 2 2
      config.gradle
  19. 665 0
      lib_base/src/main/java/com/common/lib_base/utils/log/CommonLogUtils.java
  20. 4 4
      lib_print/src/main/java/com/common/print/many/TbPrinter.java
  21. 1 0
      lib_usb_qr/.gitignore
  22. 66 0
      lib_usb_qr/build.gradle
  23. 0 0
      lib_usb_qr/consumer-rules.pro
  24. 21 0
      lib_usb_qr/proguard-rules.pro
  25. 26 0
      lib_usb_qr/src/androidTest/java/com/common/usb/qr/ExampleInstrumentedTest.java
  26. 8 0
      lib_usb_qr/src/main/AndroidManifest.xml
  27. 203 0
      lib_usb_qr/src/main/java/com/common/usb/qr/QrUsb.java
  28. 319 0
      lib_usb_qr/src/main/java/com/common/usb/qr/service/UsbService.java
  29. 333 0
      lib_usb_qr/src/main/java/com/common/usb/qr/utils/HexUtil.java
  30. 17 0
      lib_usb_qr/src/test/java/com/common/usb/qr/ExampleUnitTest.java
  31. 1 0
      settings.gradle

+ 5 - 0
app/build.gradle

@@ -210,6 +210,11 @@ dependencies {
     // 打印
     implementation(name: 'printlib-debug', ext: 'aar')
     implementation project(path: ':lib_print')
+
+    // USB二维码
+    implementation project(path: ':lib_usb_qr')
+
+
     // 身份证
 //    implementation project(path: ':lib_idcard')
     // 微信xlog日志

+ 9 - 0
app/src/main/AndroidManifest.xml

@@ -214,6 +214,15 @@
                 android:name="android.support.FILE_PROVIDER_PATHS"
                 android:resource="@xml/file_paths" />
         </provider>
+
+
+
+        <!-- smile所需——指定特定包名的应用 -->
+       <!-- <queries>
+            <package android:name="com.alipay.zoloz.smile" />
+        </queries>-->
+
+
     </application>
 
 </manifest>

+ 3 - 0
app/src/main/java/com/hh/arome/bean/ampe/AmpeActionDataBean.java

@@ -2,6 +2,9 @@ package com.hh.arome.bean.ampe;
 
 import com.alipay.arome.aromecli.AromeServiceInvoker;
 
+/**
+ * 接收到小程序的指令请求
+ */
 public class AmpeActionDataBean {
 
     AromeServiceInvoker.BridgeCallback bridgeCallback;

+ 15 - 2
app/src/main/java/com/hh/arome/bean/ampe/AmpeAppletRequestBean.java

@@ -242,9 +242,12 @@ public class AmpeAppletRequestBean {
         //
         String text;
 
+        // 语音所需
         Float pitch;
         Float rate;
 
+        Integer speechQueue;
+
         public String getText() {
             return text;
         }
@@ -269,6 +272,15 @@ public class AmpeAppletRequestBean {
             this.rate = rate;
         }
 
+        public Integer getSpeechQueue() {
+            return speechQueue;
+        }
+
+        public void setSpeechQueue(Integer speechQueue) {
+            this.speechQueue = speechQueue;
+        }
+
+
         @Override
         public String toString() {
             return "ParamsDTO{" +
@@ -281,8 +293,9 @@ public class AmpeAppletRequestBean {
                     ", certNo='" + certNo + '\'' +
                     ", certPhotoBase64Str='" + certPhotoBase64Str + '\'' +
                     ", text='" + text + '\'' +
-                    ", pitch=" + pitch +
-                    ", rate=" + rate +
+                    ", pitch=" + pitch + '\'' +
+                    ", rate=" + rate + '\'' +
+                    ", speechQueue=" + speechQueue +
                     '}';
         }
 

+ 3 - 0
app/src/main/java/com/hh/arome/bean/ampe/AmpeAppletResultBean.java

@@ -2,6 +2,9 @@ package com.hh.arome.bean.ampe;
 
 import com.common.lib_base.utils.StringUtils;
 
+/**
+ * 返回给小程序的结果
+ */
 public class AmpeAppletResultBean {
 
 

+ 7 - 1
app/src/main/java/com/hh/arome/common/Constants.java

@@ -103,6 +103,7 @@ public class Constants {
         public static final String LOG_NODE_AIDL = "aidl---";
         public static final String LOG_NODE_INFRARED_INDUCTION = "红外感应---";
         public static final String LOG_PRINT = "打印---";
+        public static final String LOG_SPEECH = "语音---";
 
 
         // 对应web的日志类型
@@ -144,6 +145,8 @@ public class Constants {
         public static final String ACTION_DEVICE = "getDeviceInfo";
         // 语音播报
         public static final String ACTION_SPEECH = "speech";
+        // 语音播报停止
+        public static final String ACTION_SPEECH_STOP = "speechStop";
         // 手输证件号
         public static final String ACTION_ENTERIDNUMBER  = "enterIDNumber";
         //小票打印
@@ -217,6 +220,7 @@ public class Constants {
 
     public static class DeviceModel {
 
+        // YX08
         public static final String K8 = "K8";
 
         public static final String F10 = "F10";
@@ -224,7 +228,7 @@ public class Constants {
         // 紫鹏
         public static final String ZP = "HYIP1000-EI";
 
-        // 访客机d2
+        // 访客机d2——YX02
         public static final String D2 = "D2";
 
         public static final String ZP_A10 = "A10";
@@ -235,6 +239,8 @@ public class Constants {
         // 马里奥-Y9
         public static final String T1YB01 = "T1YB01";
 
+        // 马里奥-Y9——蚂蚁OS
+        public static final String T1YB01A = "T1YB01A";
 
     }
 

+ 1 - 1
app/src/main/java/com/hh/arome/common/deviceui/BaseDeviceUI.java

@@ -46,7 +46,7 @@ public class BaseDeviceUI {
         } else if (Constants.DeviceModel.D2.equals(model)) {
             // d2
             iDeviceUI = D2DeviceUI.getInstance();
-        } else if (Constants.DeviceModel.T1YB01.equals(model)) {
+        } else if (Constants.DeviceModel.T1YB01.equals(model) || Constants.DeviceModel.T1YB01A.equals(model)) {
             // y9
             iDeviceUI = MlaDeviceUI.getInstance();
         } else {

+ 13 - 0
app/src/main/java/com/hh/arome/common/deviceui/D2DeviceUI.java

@@ -4,6 +4,7 @@ import android.app.Activity;
 import android.content.Intent;
 
 import com.common.face.api.FaceUtil;
+import com.common.pos.api.util.ShellUtils;
 
 /**
  * 天波-D2
@@ -81,5 +82,17 @@ public class D2DeviceUI implements IDeviceUI{
         FaceUtil.GPIOSet("ir_led_en",0);//1开,0关
     }
 
+    @Override
+    public void reboot() {
+
+        try {
+            // posutil
+            ShellUtils.execCommand("reboot", true);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
 
 }

+ 3 - 0
app/src/main/java/com/hh/arome/common/deviceui/IDeviceUI.java

@@ -22,6 +22,9 @@ public interface IDeviceUI {
     // 关闭补光灯
     void hideFillLight(Activity activity);
 
+    // 重启设备
+    void reboot();
+
 
 }
 

+ 14 - 0
app/src/main/java/com/hh/arome/common/deviceui/K8DeviceUI.java

@@ -3,6 +3,8 @@ package com.hh.arome.common.deviceui;
 import android.app.Activity;
 import android.content.Intent;
 
+import com.common.pos.api.util.ShellUtils;
+
 /**
  * K8、F10
  */
@@ -66,4 +68,16 @@ public class K8DeviceUI implements IDeviceUI {
     public void hideFillLight(Activity activity) {
 
     }
+
+    @Override
+    public void reboot() {
+
+        try {
+            // posutil
+            ShellUtils.execCommand("reboot", true);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
 }

+ 8 - 0
app/src/main/java/com/hh/arome/common/deviceui/MlaDeviceUI.java

@@ -113,4 +113,12 @@ public class MlaDeviceUI implements IDeviceUI{
     public void hideFillLight(Activity activity) {
 
     }
+
+    @Override
+    public void reboot() {
+        OrbbecSSDKApi.getInstance().startFunc("C.SYS","reboot",null,null, null);
+    }
+
+
+
 }

+ 5 - 0
app/src/main/java/com/hh/arome/common/deviceui/NullDeviceUI.java

@@ -56,4 +56,9 @@ public class NullDeviceUI implements IDeviceUI {
     public void hideFillLight(Activity activity) {
 
     }
+
+    @Override
+    public void reboot() {
+
+    }
 }

+ 15 - 0
app/src/main/java/com/hh/arome/common/deviceui/ZpDeviceUI.java

@@ -3,6 +3,8 @@ package com.hh.arome.common.deviceui;
 import android.app.Activity;
 import android.content.Intent;
 
+import com.common.pos.api.util.ShellUtils;
+
 /**
  * 紫鹏
  */
@@ -71,5 +73,18 @@ public class ZpDeviceUI implements IDeviceUI{
     @Override
     public void hideFillLight(Activity activity) {
 
+    }
+
+    @Override
+    public void reboot() {
+
+        try {
+            // posutil
+            ShellUtils.execCommand("reboot", true);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+
     }
 }

+ 7 - 7
app/src/main/java/com/hh/arome/common/scan/idcard/manymodels/TbIDCardScan.java

@@ -125,27 +125,27 @@ public class TbIDCardScan implements IIDCardScan {
         if (t2oReader.isUSBReader(mContext)) {
             boolean ret = t2oReader.openReader(mContext);
             if (ret) {
-                AppLogUtils.e("天波--open usb reader success");
+                AppLogUtils.e("--open usb reader success");
                 idCardInitResultCallback.onIDCardInitSuccess();
                 isInitSuccess = true;
-                AppLogUtils.e("天波--ret---isInitSuccess:"+isInitSuccess);
+                AppLogUtils.e("--ret---isInitSuccess:"+isInitSuccess);
             } else {
-                AppLogUtils.e("天波--open usb reader fail");
+                AppLogUtils.e("--open usb reader fail");
                 if(!isInitSuccess){
-                    idCardInitResultCallback.onIDCardInitError("-1", "天波--open usb reader fail");
+                    idCardInitResultCallback.onIDCardInitError("-1", "--open usb reader fail");
                 }
                 retryCount++;
             }
         } else {
             boolean ret = t2oReader.openReader();
             if (ret) {
-                AppLogUtils.e("天波--open serial reader success");
+                AppLogUtils.e("--open serial reader success");
                 isInitSuccess = true;
                 idCardInitResultCallback.onIDCardInitSuccess();
             } else {
-                AppLogUtils.e("天波--open serial reader fail");
+                AppLogUtils.e("--open serial reader fail");
                 if(!isInitSuccess){
-                    idCardInitResultCallback.onIDCardInitError("-1", "天波--open serial reader fail");
+                    idCardInitResultCallback.onIDCardInitError("-1", "--open serial reader fail");
                 }
                 retryCount++;
             }

+ 27 - 11
app/src/main/java/com/hh/arome/common/voice/MTTSDemo.java

@@ -18,7 +18,8 @@ public class MTTSDemo implements TextToSpeech.OnInitListener {
     private static volatile MTTSDemo instance = null;
 
     //将构造方法私有化,禁止外部通过构造方法创建实例
-    private MTTSDemo() {}
+    private MTTSDemo() {
+    }
 
     //提供一个公共的访问方法,用于获取该类的唯一实例
     public static MTTSDemo getInstance(Context context) {
@@ -34,8 +35,8 @@ public class MTTSDemo implements TextToSpeech.OnInitListener {
         }
         return instance;
     }
-    
-    
+
+
     private TextToSpeech mTTS;
     private Context mContext;
 
@@ -58,7 +59,7 @@ public class MTTSDemo implements TextToSpeech.OnInitListener {
             int languageCode = mTTS.setLanguage(Locale.CHINESE);
             //判断是否支持这种语言,Android原生不支持中文,使用科大讯飞的tts引擎就可以了
             if (languageCode == TextToSpeech.LANG_NOT_SUPPORTED) {
-                AppLogUtils.w(true,"TTS语音播报 onInit: 不支持这种语言");
+                AppLogUtils.w(true, "TTS语音播报 onInit: 不支持这种语言");
             } else {
                 //不支持就改成英文
                 mTTS.setLanguage(Locale.US);
@@ -67,13 +68,10 @@ public class MTTSDemo implements TextToSpeech.OnInitListener {
     }
 
 
-    public void jump(){
+    public void jump() {
         String enginePackageName = mTTS.getDefaultEngine();
 
 
-
-
-
     }
 
 
@@ -91,7 +89,7 @@ public class MTTSDemo implements TextToSpeech.OnInitListener {
     }
 
 
-    public void speak(String text,float pitch,float rate) {
+    public void speak(String text, float pitch, float rate, Integer speechQueue) {
 
 
 //        // 获取当前引擎的参数
@@ -113,7 +111,25 @@ public class MTTSDemo implements TextToSpeech.OnInitListener {
         mTTS.setPitch(pitch);
         //设置语速
         mTTS.setSpeechRate(rate);
-        mTTS.speak(text, TextToSpeech.QUEUE_ADD, null);
+
+
+        // 设置播报队列方式
+        if(speechQueue == null){
+            speechQueue = 0;
+        }
+        if (speechQueue == 0) {
+            // TextToSpeech.QUEUE_ADD添加到队列后面,依次将前面的读完轮序
+            mTTS.speak(text, TextToSpeech.QUEUE_ADD, null);
+        }else if(speechQueue == 1) {
+            // TextToSpeech.QUEUE_FLUSH刷新队列,将之前的队列取消阅读现在的文字
+            mTTS.speak(text, TextToSpeech.QUEUE_FLUSH, null);
+        }
+
+    }
+
+
+    public void speakStop() {
+        mTTS.stop();
     }
 
 
@@ -121,7 +137,7 @@ public class MTTSDemo implements TextToSpeech.OnInitListener {
      * 销毁播报方法,直接调用
      */
     public void stopTTS() {
-        if(mTTS!=null){
+        if (mTTS != null) {
             mTTS.stop();
             mTTS.shutdown();
             mTTS = null;

File diff suppressed because it is too large
+ 6 - 7
app/src/main/java/com/hh/arome/ui/MainActivity.java


+ 8 - 6
app/src/main/java/com/hh/arome/ui/PasswordActivity.java

@@ -18,6 +18,8 @@ import com.hh.arome.R;
 import com.hh.arome.bean.DeviceConfigResultBean;
 import com.hh.arome.common.ConfigData;
 import com.hh.arome.common.Constants;
+import com.hh.arome.common.deviceui.BaseDeviceUI;
+import com.hh.arome.common.deviceui.IDeviceUI;
 import com.hh.arome.ui.base.BaseHHActivity;
 import com.hh.arome.ui.setting.SettingActivity;
 import com.hh.arome.utlis.device.DeviceUtils;
@@ -29,6 +31,8 @@ public class PasswordActivity extends BaseHHActivity implements View.OnClickList
 
     String deviceModel = DeviceUtils.getDeviceModel();
 
+    IDeviceUI iDeviceUI;
+
     @Override
     public int setLayout() {
         if(Constants.DeviceModel.D2.equals(deviceModel)){
@@ -65,6 +69,8 @@ public class PasswordActivity extends BaseHHActivity implements View.OnClickList
     @Override
     protected void initData() {
         tv_title_center.setText("请输入密码");
+
+        iDeviceUI = BaseDeviceUI.getInstance().getDeviceUI();
     }
 
     @Override
@@ -184,12 +190,8 @@ public class PasswordActivity extends BaseHHActivity implements View.OnClickList
     // 重启设备
     private void restartDevice() {
 
-        try {
-            // posutil
-            ShellUtils.execCommand("reboot", true);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
+        iDeviceUI.reboot();
+
     }
 
 

+ 2 - 2
config.gradle

@@ -7,8 +7,8 @@ ext {
 //            minSdkVersion    : 24,
             minSdkVersion    : 21,
             targetSdkVersion : 22,
-            versionCode      : 26,
-            versionName      : "2.7.5"
+            versionCode      : 27,
+            versionName      : "2.7.6"
     ]
     //依赖 配置
     dependencies = [

+ 665 - 0
lib_base/src/main/java/com/common/lib_base/utils/log/CommonLogUtils.java

@@ -0,0 +1,665 @@
+package com.common.lib_base.utils.log;
+
+import android.annotation.SuppressLint;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.IntRange;
+
+import com.blankj.utilcode.util.FileIOUtils;
+import com.blankj.utilcode.util.FileUtils;
+import com.blankj.utilcode.util.PathUtils;
+import com.common.lib_base.BuildConfig;
+import com.common.lib_base.CommonApplication;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.text.Format;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Formatter;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+/**
+ * <pre>
+ *     @author yangchong
+ *     blog  :
+ *     time  : 2018/07/2
+ *     desc  : Log 相关工具类
+ *     revise:
+ * </pre>
+ */
+public final class CommonLogUtils {
+
+    /*
+     getConfig                : 获取 log 配置
+     Config.setLogSwitch      : 设置 log 总开关
+     Config.setConsoleSwitch  : 设置 log 控制台开关
+     Config.setGlobalTag      : 设置 log 全局 tag
+     Config.setLogHeadSwitch  : 设置 log 头部信息开关
+     Config.setLog2FileSwitch : 设置 log 文件开关
+     Config.setDir            : 设置 log 文件存储目录
+     Config.setFilePrefix     : 设置 log 文件前缀
+     Config.setBorderSwitch   : 设置 log 边框开关
+     Config.setSingleTagSwitch: 设置 log 单一 tag 开关(为美化 AS 3.1 的 Logcat)
+     Config.setConsoleFilter  : 设置 log 控制台过滤器
+     Config.setFileFilter     : 设置 log 文件过滤器
+     Config.setStackDeep      : 设置 log 栈深度
+     Config.setStackOffset    : 设置 log 栈偏移
+     log                      : 自定义 tag 的 type 日志
+     v                        : tag 为类名的 Verbose 日志
+     vTag                     : 自定义 tag 的 Verbose 日志
+     d                        : tag 为类名的 Debug 日志
+     dTag                     : 自定义 tag 的 Debug 日志
+     i                        : tag 为类名的 Info 日志
+     iTag                     : 自定义 tag 的 Info 日志
+     w                        : tag 为类名的 Warn 日志
+     wTag                     : 自定义 tag 的 Warn 日志
+     e                        : tag 为类名的 Error 日志
+     eTag                     : 自定义 tag 的 Error 日志
+     a                        : tag 为类名的 Assert 日志
+     aTag                     : 自定义 tag 的 Assert 日志
+     file                     : log 到文件
+     json                     : log 字符串之 json
+     xml                      : log 字符串之 xml
+     */
+
+    public static final int V = Log.VERBOSE;
+    public static final int D = Log.DEBUG;
+    public static final int I = Log.INFO;
+    public static final int W = Log.WARN;
+    public static final int E = Log.ERROR;
+
+    @IntDef({V, D, I, W, E})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface TYPE {
+    }
+
+    private static final char[] T = new char[]{'V', 'D', 'I', 'W', 'E', 'A'};
+
+    private static final int FILE = 0x10;
+    private static final int JSON = 0x20;
+    private static final int XML = 0x30;
+
+    private static final String FILE_SEP = System.getProperty("file.separator");
+    // 换行符,功能和"\n"是一致的,但是此种写法屏蔽了 Windows和Linux的区别 ,更保险一些
+    private static final String LINE_SEP = System.getProperty("line.separator");
+    private static final String TOP_CORNER = "┌";
+    private static final String MIDDLE_CORNER = "├";
+    private static final String LEFT_BORDER = "│ ";
+    private static final String BOTTOM_CORNER = "└";
+    private static final String SIDE_DIVIDER = "────────────────────────────────────────────────────────";
+    private static final String MIDDLE_DIVIDER = "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄";
+    private static final String TOP_BORDER = TOP_CORNER + SIDE_DIVIDER + SIDE_DIVIDER;
+    private static final String MIDDLE_BORDER = MIDDLE_CORNER + MIDDLE_DIVIDER + MIDDLE_DIVIDER;
+    private static final String BOTTOM_BORDER = BOTTOM_CORNER + SIDE_DIVIDER + SIDE_DIVIDER;
+    private static final int MAX_LEN = 4000;
+    @SuppressLint("SimpleDateFormat")
+    private static final Format FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS ");
+    private static final String NOTHING = "log nothing";
+    private static final String NULL = BuildConfig.LOG_TAG;
+    private static final String ARGS = "args";
+    private static final Config CONFIG = new Config();
+
+    private static String sDefaultDir;// log 默认存储目录
+    private static String sDir;       // log 存储目录
+    private static String sFilePrefix = "hwmcLog";// log 文件前缀
+    private static boolean sLogSwitch = true;  // log 总开关,默认开
+    private static boolean sLog2ConsoleSwitch = true;  // logcat 是否打印,默认打印
+    private static String sGlobalTag = BuildConfig.LOG_TAG;  // log 标签
+    private static boolean sTagIsSpace = true;  // log 标签是否为空白
+    private static boolean sLogHeadSwitch = true;  // log 头部开关,默认开
+    private static boolean sLog2FileSwitch = false; // log 写入文件开关,默认关
+    private static boolean sLogBorderSwitch = true;  // log 边框开关,默认开
+    private static int sConsoleFilter = V;     // log 控制台过滤器
+    private static int sFileFilter = V;     // log 文件过滤器
+    private static int sStackDeep = 1;     // log 栈深度
+
+    private CommonLogUtils() {
+        throw new UnsupportedOperationException("u can't instantiate me...");
+    }
+
+    public static Config getConfig() {
+        return CONFIG;
+    }
+
+    public static void v(final Object... contents) {
+        log(V, sGlobalTag, contents);
+    }
+
+    public static void vTag(final String tag, final Object... contents) {
+        log(V, tag, contents);
+    }
+
+    public static void d(final Object... contents) {
+        log(D, sGlobalTag, contents);
+    }
+
+    public static void dTag(final String tag, final Object... contents) {
+        log(D, tag, contents);
+    }
+
+    public static void i(final Object... contents) {
+        log(I, sGlobalTag, contents);
+    }
+
+    public static void iTag(final String tag, final Object... contents) {
+        log(I, tag, contents);
+    }
+
+    public static void w(final Object... contents) {
+        log(W, sGlobalTag, contents);
+    }
+
+    public static void wTag(final String tag, final Object... contents) {
+        log(W, tag, contents);
+    }
+
+    public static void e(final Object... contents) {
+        log(E, sGlobalTag, contents);
+    }
+
+    public static void eTag(final String tag, final Object... contents) {
+        log(E, tag, contents);
+    }
+
+    public static void file(final Object content) {
+        log(FILE | D, sGlobalTag, content);
+    }
+
+    public static void file(@TYPE final int type, final Object content) {
+        log(FILE | type, sGlobalTag, content);
+    }
+
+    public static void file(final String tag, final Object content) {
+        log(FILE | D, tag, content);
+    }
+
+    public static void file(@TYPE final int type, final String tag, final Object content) {
+        log(FILE | type, tag, content);
+    }
+
+    public static void json(final String content) {
+        log(JSON | D, sGlobalTag, content);
+    }
+
+    public static void json(@TYPE final int type, final String content) {
+        log(JSON | type, sGlobalTag, content);
+    }
+
+    public static void json(final String tag, final String content) {
+        log(JSON | D, tag, content);
+    }
+
+    public static void json(@TYPE final int type, final String tag, final String content) {
+        log(JSON | type, tag, content);
+    }
+
+    public static void xml(final String content) {
+        log(XML | D, sGlobalTag, content);
+    }
+
+    public static void xml(@TYPE final int type, final String content) {
+        log(XML | type, sGlobalTag, content);
+    }
+
+    public static void xml(final String tag, final String content) {
+        log(XML | D, tag, content);
+    }
+
+    public static void xml(@TYPE final int type, final String tag, final String content) {
+        log(XML | type, tag, content);
+    }
+
+    private static void log(final int type, final String tag, final Object... contents) {
+        if (!sLogSwitch || (!sLog2ConsoleSwitch && !sLog2FileSwitch)) return;
+        int type_low = type & 0x0f, type_high = type & 0xf0;
+        if (type_low < sConsoleFilter && type_low < sFileFilter) return;
+        final TagHead tagHead = processTagAndHead(tag);
+//        Log.e("TEST", "tagHead--" + tagHead.toString());
+        String body = processBody(type_high, contents);
+//        Log.e("TEST", "body--" + body.toString());
+        if (sLog2ConsoleSwitch && type_low >= sConsoleFilter && type_high != FILE) {
+            print2Console(type_low, tagHead.tag, tagHead.consoleHead, body);
+        }
+        if ((sLog2FileSwitch || type_high == FILE) && type_low >= sFileFilter) {
+//            print2File(type_low, tagHead.tag, tagHead.fileHead + body);
+        }
+    }
+
+
+    private static TagHead processTagAndHead(String tag) {
+        if (!sTagIsSpace && !sLogHeadSwitch) {
+            tag = sGlobalTag;
+        } else {
+            final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
+            StackTraceElement targetElement = stackTrace[3];
+            String methodName = targetElement.getMethodName();
+//            Log.e("TEST", "methodName--"+methodName);
+            String fileName = targetElement.getFileName();
+//            Log.e("TEST", "fileName--"+fileName);
+            String className;
+            // 混淆可能会导致获取为空 加-keepattributes SourceFile,LineNumberTable
+            if (fileName == null) {
+                className = targetElement.getClassName();
+                String[] classNameInfo = className.split("\\.");
+                if (classNameInfo.length > 0) {
+                    className = classNameInfo[classNameInfo.length - 1];
+                }
+                int index = className.indexOf('$');
+                if (index != -1) {
+                    className = className.substring(0, index);
+                }
+                fileName = className + ".java";
+            } else {
+                int index = fileName.indexOf('.');// 混淆可能导致文件名被改变从而找不到"."
+                className = index == -1 ? fileName : fileName.substring(0, index);
+            }
+            if (sTagIsSpace) tag = isSpace(tag) ? className : tag;
+            if (sLogHeadSwitch) {
+                String tName = Thread.currentThread().getName();
+                final String head = new Formatter()
+                        .format("%s, %s(%s:%d)",
+                                tName,
+                                targetElement.getMethodName(),
+                                fileName,
+                                targetElement.getLineNumber())
+                        .toString();
+                final String fileHead = " [" + head + "]: ";
+                if (sStackDeep <= 1) {
+//                    Log.e("TEST", "sStackDeep-<= 1");
+                    return new TagHead(tag, new String[]{head}, fileHead);
+                } else {
+//                    Log.e("TEST", "sStackDeep-> 1");
+                    final String[] consoleHead =
+                            new String[Math.min(sStackDeep, stackTrace.length - 3)];
+                    consoleHead[0] = head;
+                    int spaceLen = tName.length() + 2;
+                    String space = new Formatter().format("%" + spaceLen + "s", "").toString();
+                    for (int i = 1, len = consoleHead.length; i < len; ++i) {
+                        targetElement = stackTrace[i + 3];
+                        consoleHead[i] = new Formatter()
+                                .format("%s%s(%s:%d)",
+                                        space,
+                                        targetElement.getMethodName(),
+                                        targetElement.getFileName(),
+                                        targetElement.getLineNumber())
+                                .toString();
+
+//                        Log.e("TEST", "MethodName--" + targetElement.getMethodName());
+
+                    }
+//                    Log.e("TEST", "tag--" + tag);
+//                    Log.e("TEST", "consoleHead--" + consoleHead);
+//                    Log.e("TEST", "fileHead--" + fileHead);
+                    return new TagHead(tag, consoleHead, fileHead);
+                }
+            }
+        }
+        return new TagHead(tag, null, ": ");
+    }
+
+    private static String processBody(final int type, final Object... contents) {
+        String body = NULL;
+        if (contents != null) {
+            if (contents.length == 1) {
+                Object object = contents[0];
+                if (object != null) body = object.toString();
+                if (type == JSON) {
+                    body = formatJson(body);
+                } else if (type == XML) {
+                    body = formatXml(body);
+                }
+            } else {
+                StringBuilder sb = new StringBuilder();
+                for (int i = 0, len = contents.length; i < len; ++i) {
+                    Object content = contents[i];
+                    sb.append(ARGS)
+                            .append("[")
+                            .append(i)
+                            .append("]")
+                            .append(" = ")
+                            .append(content == null ? NULL : content.toString())
+                            .append(LINE_SEP);
+                }
+                body = sb.toString();
+            }
+        }
+        return body.length() == 0 ? NOTHING : body;
+    }
+
+    /**
+     * 解析json
+     *
+     * @param json json
+     * @return
+     */
+    private static String formatJson(String json) {
+        try {
+            if (json.startsWith("{")) {
+                json = new JSONObject(json).toString(4);
+            } else if (json.startsWith("[")) {
+                json = new JSONArray(json).toString(4);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return json;
+    }
+
+
+    /**
+     * 解析xml
+     *
+     * @param xml xml
+     * @return
+     */
+    private static String formatXml(String xml) {
+        try {
+            Source xmlInput = new StreamSource(new StringReader(xml));
+            StreamResult xmlOutput = new StreamResult(new StringWriter());
+            Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+            transformer.transform(xmlInput, xmlOutput);
+            xml = xmlOutput.getWriter().toString().replaceFirst(">", ">" + LINE_SEP);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return xml;
+    }
+
+    private static void print2Console(final int type,
+                                      final String tag,
+                                      final String[] head,
+                                      final String msg) {
+        printBorder(type, tag, true);
+        printHead(type, tag, head);
+        printMsg(type, tag, msg);
+        printBorder(type, tag, false);
+
+    }
+
+    private static void printBorder(final int type, final String tag, boolean isTop) {
+        if (sLogBorderSwitch) {
+            Log.println(type, tag, isTop ? TOP_BORDER : BOTTOM_BORDER);
+//            saveLog(LogType.Logcat, isTop ? TOP_BORDER : BOTTOM_BORDER);
+        }
+    }
+
+    private static void printHead(final int type, final String tag, final String[] head) {
+        if (head != null) {
+            for (String aHead : head) {
+                Log.println(type, tag, sLogBorderSwitch ? LEFT_BORDER + aHead : aHead);
+                Log.i("TEST", "type--" + type);
+                Log.i("TEST", "tag--" + tag);
+                Log.i("TEST", sLogBorderSwitch ? LEFT_BORDER + aHead : aHead + "      printHead ");
+//                saveLog(LogType.Logcat, sLogBorderSwitch ? LEFT_BORDER + aHead : aHead);
+            }
+            if (sLogBorderSwitch) {
+                Log.println(type, tag, MIDDLE_BORDER);
+                Log.i("TEST", MIDDLE_BORDER + "      sLogBorderSwitch ");
+//                saveLog(LogType.Logcat, MIDDLE_BORDER);
+            }
+        }
+    }
+
+    private static void printMsg(final int type, final String tag, final String msg) {
+        int len = msg.length();
+        int countOfSub = len / MAX_LEN;
+        if (countOfSub > 0) {
+            int index = 0;
+            for (int i = 0; i < countOfSub; i++) {
+                printSubMsg(type, tag, msg.substring(index, index + MAX_LEN));
+                index += MAX_LEN;
+            }
+            if (index != len) {
+                printSubMsg(type, tag, msg.substring(index, len));
+            }
+        } else {
+            printSubMsg(type, tag, msg);
+        }
+    }
+
+    private static void printSubMsg(final int type, final String tag, final String msg) {
+        if (!sLogBorderSwitch) {
+            Log.println(type, tag, msg);
+//            saveLog(LogType.Logcat, msg);
+            return;
+        }
+        String[] lines = msg.split(LINE_SEP);
+        for (String line : lines) {
+            Log.println(type, tag, LEFT_BORDER + line);
+//            saveLog(LogType.Logcat, LEFT_BORDER + line);
+        }
+    }
+
+    /**
+     * 将日志写入到文件中,思路是先拼接字符串,然后再写入文件
+     *
+     * @param type 类型
+     * @param tag  tag
+     * @param msg  msg
+     */
+    private static void print2File(final int type, final String tag, final String msg) {
+        Date now = new Date(System.currentTimeMillis());
+        String format = FORMAT.format(now);
+        String date = format.substring(0, 5);
+        String time = format.substring(6);
+        final String fullPath =
+                (sDir == null ? sDefaultDir : sDir) + sFilePrefix + "-" + date + ".txt";
+        if (!createOrExistsFile(fullPath)) {
+            Log.e(tag, "log to " + fullPath + " failed!");
+            return;
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append(time)
+                .append(T[type - V])
+                .append("/")
+                .append(tag)
+                .append(msg)
+                .append(LINE_SEP);
+        final String content = sb.toString();
+
+        //一边打印日志一边写入文件
+//        if (PrintToFileUtil.input2File(content, fullPath)) {
+//            Log.d(tag, "log to " + fullPath + " success!");
+//        } else {
+//            Log.e(tag, "log to " + fullPath + " failed!");
+//        }
+    }
+
+    private static boolean createOrExistsFile(final String filePath) {
+        File file = new File(filePath);
+        if (file.exists()) return file.isFile();
+        if (!createOrExistsDir(file.getParentFile())) return false;
+        try {
+            boolean isCreate = file.createNewFile();
+//            if (isCreate) {
+//                String head = PrintToFileUtil.printDeviceInfo();
+//                PrintToFileUtil.input2File(head, filePath);
+//            }
+            return isCreate;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+
+    private static boolean createOrExistsDir(final File file) {
+        return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
+    }
+
+    /**
+     * 判断是否为空
+     *
+     * @param s s
+     * @return
+     */
+    private static boolean isSpace(final String s) {
+        if (s == null) return true;
+        for (int i = 0, len = s.length(); i < len; ++i) {
+            if (!Character.isWhitespace(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * config 采用builder模式
+     */
+    public static class Config {
+        private Config() {
+            if (sDefaultDir != null) return;
+//            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
+//                    && MyApplication.getContext().getExternalCacheDir() != null)
+//                sDefaultDir = MyApplication.getContext().getExternalCacheDir() + FILE_SEP + "log" + FILE_SEP;
+//            else {
+//                sDefaultDir = MyApplication.getContext().getCacheDir() + FILE_SEP + "log" + FILE_SEP;
+//            }
+        }
+
+        public Config setLogSwitch(final boolean logSwitch) {
+            sLogSwitch = logSwitch;
+            return this;
+        }
+
+        public Config setConsoleSwitch(final boolean consoleSwitch) {
+            sLog2ConsoleSwitch = consoleSwitch;
+            return this;
+        }
+
+        public Config setGlobalTag(final String tag) {
+            if (isSpace(tag)) {
+                sGlobalTag = "";
+                sTagIsSpace = true;
+            } else {
+                sGlobalTag = tag;
+                sTagIsSpace = false;
+            }
+            return this;
+        }
+
+        public Config setLogHeadSwitch(final boolean logHeadSwitch) {
+            sLogHeadSwitch = logHeadSwitch;
+            return this;
+        }
+
+        public Config setLog2FileSwitch(final boolean log2FileSwitch) {
+            sLog2FileSwitch = log2FileSwitch;
+            return this;
+        }
+
+        public Config setDir(final String dir) {
+            if (isSpace(dir)) {
+                sDir = null;
+            } else {
+                sDir = dir.endsWith(FILE_SEP) ? dir : dir + FILE_SEP;
+            }
+            return this;
+        }
+
+        public Config setDir(final File dir) {
+            sDir = dir == null ? null : dir.getAbsolutePath() + FILE_SEP;
+            return this;
+        }
+
+        public Config setFilePrefix(final String filePrefix) {
+            if (isSpace(filePrefix)) {
+                sFilePrefix = "ycLog";
+            } else {
+                sFilePrefix = filePrefix;
+            }
+            return this;
+        }
+
+        public Config setBorderSwitch(final boolean borderSwitch) {
+            sLogBorderSwitch = borderSwitch;
+            return this;
+        }
+
+        public Config setConsoleFilter(@TYPE final int consoleFilter) {
+            sConsoleFilter = consoleFilter;
+            return this;
+        }
+
+        public Config setFileFilter(@TYPE final int fileFilter) {
+            sFileFilter = fileFilter;
+            return this;
+        }
+
+        public Config setStackDeep(@IntRange(from = 1) final int stackDeep) {
+            sStackDeep = stackDeep;
+            return this;
+        }
+
+        @Override
+        public String toString() {
+            return "switch: " + sLogSwitch
+                    + LINE_SEP + "console: " + sLog2ConsoleSwitch
+                    + LINE_SEP + "tag: " + (sTagIsSpace ? "null" : sGlobalTag)
+                    + LINE_SEP + "head: " + sLogHeadSwitch
+                    + LINE_SEP + "file: " + sLog2FileSwitch
+                    + LINE_SEP + "dir: " + (sDir == null ? sDefaultDir : sDir)
+                    + LINE_SEP + "filePrefix" + sFilePrefix
+                    + LINE_SEP + "border: " + sLogBorderSwitch
+                    + LINE_SEP + "consoleFilter: " + T[sConsoleFilter - V]
+                    + LINE_SEP + "fileFilter: " + T[sFileFilter - V]
+                    + LINE_SEP + "stackDeep: " + sStackDeep;
+        }
+    }
+
+    private static class TagHead {
+        String tag;
+        String[] consoleHead;
+        String fileHead;
+
+        TagHead(String tag, String[] consoleHead, String fileHead) {
+            this.tag = tag;
+            this.consoleHead = consoleHead;
+            this.fileHead = fileHead;
+        }
+
+
+        @Override
+        public String toString() {
+            return "TagHead{" +
+                    "tag='" + tag + '\'' +
+                    ", consoleHead=" + Arrays.toString(consoleHead) +
+                    ", fileHead='" + fileHead + '\'' +
+                    '}';
+        }
+    }
+
+
+
+
+}

+ 4 - 4
lib_print/src/main/java/com/common/print/many/TbPrinter.java

@@ -119,16 +119,16 @@ public class TbPrinter implements IPrinter {
                                 printInitResultCallback.onPrintInitSuccess();
                             }else if(checkStatus == ThermalPrinter.STATUS_NO_PAPER){
                                 // 打印机缺纸
-                                printInitResultCallback.onPrintInitError(checkStatus+"","天波打印机——打印机缺纸");
+                                printInitResultCallback.onPrintInitError(checkStatus+"","打印机——打印机缺纸");
                             }else if(checkStatus == ThermalPrinter.STATUS_OVER_HEAT){
                                 // 打印机机芯过热
-                                printInitResultCallback.onPrintInitError(checkStatus+"","天波打印机——机芯过热");
+                                printInitResultCallback.onPrintInitError(checkStatus+"","打印机——机芯过热");
                             }else if(checkStatus == ThermalPrinter.STATUS_OVER_FLOW){
                                 // 打印机缓存已满
-                                printInitResultCallback.onPrintInitError(checkStatus+"","天波打印机——缓存已满");
+                                printInitResultCallback.onPrintInitError(checkStatus+"","打印机——缓存已满");
                             }else if(checkStatus == ThermalPrinter.STATUS_UNKNOWN){
                                 // 打印机状态未知
-                                printInitResultCallback.onPrintInitError(checkStatus+"","天波打印机——状态未知");
+                                printInitResultCallback.onPrintInitError(checkStatus+"","打印机——状态未知");
                             }
 
                         }

+ 1 - 0
lib_usb_qr/.gitignore

@@ -0,0 +1 @@
+/build

+ 66 - 0
lib_usb_qr/build.gradle

@@ -0,0 +1,66 @@
+plugins {
+    id 'com.android.library'
+}
+
+android {
+    compileSdkVersion rootProject.ext.android["compileSdkVersion"]
+    buildToolsVersion rootProject.ext.android["buildToolsVersion"]
+    defaultConfig {
+        minSdkVersion 16
+        targetSdkVersion rootProject.ext.android["targetSdkVersion"]
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles 'consumer-rules.pro'
+
+    }
+
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+        }
+
+        debug {
+            jniDebuggable true
+            debuggable true
+        }
+
+        yufabu {
+            jniDebuggable true
+            debuggable true
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    sourceSets.main {
+        jni.srcDirs = []//disable automatic ndk-build call
+        jniLibs.srcDirs = ['libs']
+    }
+
+}
+
+dependencies {
+
+    implementation 'androidx.appcompat:appcompat:1.1.0'
+    implementation 'com.google.android.material:material:1.1.0'
+    testImplementation 'junit:junit:4.+'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+
+    // usb串口通信
+    implementation 'com.github.felHR85:UsbSerial:4.5.2'
+
+    // 添加依赖
+//    implementation project(path: ':lib_arome')
+    implementation project(path: ':lib_base')
+
+
+
+}

+ 0 - 0
lib_usb_qr/consumer-rules.pro


+ 21 - 0
lib_usb_qr/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 26 - 0
lib_usb_qr/src/androidTest/java/com/common/usb/qr/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package com.common.usb.qr;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("com.common.usb.qr.test", appContext.getPackageName());
+    }
+}

+ 8 - 0
lib_usb_qr/src/main/AndroidManifest.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.common.usb.qr">
+
+    <application>
+        <service android:name=".service.UsbService" />
+    </application>
+</manifest>

+ 203 - 0
lib_usb_qr/src/main/java/com/common/usb/qr/QrUsb.java

@@ -0,0 +1,203 @@
+package com.common.usb.qr;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+
+
+import com.common.lib_base.utils.log.CommonLogUtils;
+import com.common.usb.qr.service.UsbService;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import static android.content.Context.BIND_AUTO_CREATE;
+
+public class QrUsb {
+
+    public interface QrResultCallback{
+        void onScanSuccess(String qrCode);
+        void onScanFail();
+
+        void onInitSuccess();
+        void onInitFail(String errMsg);
+
+    }
+
+
+    QrResultCallback qrResultCallback;
+    Context mContext;
+
+    public void init(Context context,QrResultCallback qrResultCallback){
+        this.mContext = context;
+        this.qrResultCallback = qrResultCallback;
+
+//        Common.e("USB-init");
+        CommonLogUtils.e("USB-init");
+
+        //二维码
+        mHandler = new MyHandler();
+
+        setFilters();  //从UsbService开始监听通知
+        startService(UsbService.class, usbConnection, null); //   启动UsbService(如果它之前没有启动)并绑定它
+
+        OpenUSB();
+
+    }
+
+
+
+    private void setFilters() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(UsbService.ACTION_USB_PERMISSION_GRANTED);
+        filter.addAction(UsbService.ACTION_NO_USB);
+        filter.addAction(UsbService.ACTION_USB_DISCONNECTED);
+        filter.addAction(UsbService.ACTION_USB_NOT_SUPPORTED);
+        filter.addAction(UsbService.ACTION_USB_PERMISSION_NOT_GRANTED);
+        mContext
+                .registerReceiver(
+                        mUsbReceiver,
+                        filter);
+    }
+
+
+    private void startService(Class<?> service, ServiceConnection serviceConnection, Bundle extras) {
+        if (!UsbService.SERVICE_CONNECTED) {
+            Intent startService = new Intent(mContext, service);
+            if (extras != null && !extras.isEmpty()) {
+                Set<String> keys = extras.keySet();
+                for (String key : keys) {
+                    String extra = extras.getString(key);
+                    startService.putExtra(key, extra);
+                }
+            }
+            mContext.startService(startService);
+        }
+        Intent bindingIntent = new Intent(mContext, service);
+        mContext.bindService(bindingIntent, serviceConnection, BIND_AUTO_CREATE);
+    }
+
+
+    private String[] PERMISSIONS_STORAGE = {
+            "android.permission.READ_EXTERNAL_STORAGE",
+            "android.permission.WRITE_EXTERNAL_STORAGE",
+            "android.permission.MOUNT_UNMOUNT_FILESYSTEMS"};
+    private UsbManager usbManager;
+    private PendingIntent mPendingIntent;
+    private final String ACTION_USB_PERMISSION = "com.donsee.devices.DonseeDeviceActivity.USB_PERMISSION";
+    private int pid = 57407;
+    private int vid = 1742;
+    private UsbDevice usbDevice;
+
+
+    private void OpenUSB() {
+        usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+        if (Build.VERSION.SDK_INT >= 31) {
+            mPendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
+        } else {
+            mPendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
+        }
+        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
+        mContext.registerReceiver(mUsbReceiver, filter);
+        HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
+        if (!deviceList.isEmpty()) {
+            for (UsbDevice next : deviceList.values()) {
+                if (next.getVendorId() == vid && next.getProductId() == pid) {
+                    usbDevice = next;
+                    if (!usbManager.hasPermission(usbDevice)) {
+                        usbManager.requestPermission(usbDevice, mPendingIntent);
+                    }
+                }
+            }
+        }
+    }
+
+
+    //二维码阅读器(usb转虚拟串口通信后)
+    private UsbService usbService;
+    private MyHandler mHandler;
+    private final ServiceConnection usbConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName arg0, IBinder arg1) {
+            usbService = ((UsbService.UsbBinder) arg1).getService();
+            usbService.setHandler(mHandler);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName arg0) {
+            usbService = null;
+        }
+    };
+    /*
+     * 这里将收到来自UsbService的通知。
+     */
+    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case UsbService.ACTION_USB_PERMISSION_GRANTED: // USB 准备完成
+                    CommonLogUtils.e( "------------USB Ready");
+                    qrResultCallback.onInitSuccess();
+                    break;
+                case UsbService.ACTION_USB_PERMISSION_NOT_GRANTED: // USB 未授权
+                    CommonLogUtils.e("------------USB Permission not granted");
+                    qrResultCallback.onInitFail("USB Permission not granted,请检查OTG连接、USB线连接");
+                    break;
+                case UsbService.ACTION_NO_USB: // 无USB连接
+                    CommonLogUtils.e( "------------No USB connected");
+                    qrResultCallback.onInitFail("No USB connected,请检查OTG连接、USB线连接");
+                    break;
+                case UsbService.ACTION_USB_DISCONNECTED: // USB 断开
+                    CommonLogUtils.e( "------------USB disconnected");
+                    qrResultCallback.onInitFail("USB disconnected,请检查OTG连接、USB线连接");
+                    break;
+                case UsbService.ACTION_USB_NOT_SUPPORTED: // 不支持USB
+                    CommonLogUtils.e( "------------USB device not supported");
+                    qrResultCallback.onInitFail("USB device not supported,请检查OTG连接、USB线连接");
+                    break;
+            }
+        }
+    };
+
+
+    /*
+     * 此处理程序将传递给UsbServices。通过此处理程序显示从串口接收的数据。
+     */
+    private class MyHandler extends Handler {
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case UsbService.MESSAGE_FROM_SERIAL_PORT:
+                    String data = (String) msg.obj;
+                    if (data != null && !data.equals("")) {
+                        CommonLogUtils.e("二维码数据返回 "+ data);
+                        qrResultCallback.onScanSuccess(data);
+                    }
+                    break;
+                case UsbService.CTS_CHANGE:
+                    CommonLogUtils.e("CTS_CHANGE");
+                    break;
+                case UsbService.DSR_CHANGE:
+                    CommonLogUtils.e("DSR_CHANGE");
+                    break;
+            }
+        }
+    }
+
+
+
+
+
+}

+ 319 - 0
lib_usb_qr/src/main/java/com/common/usb/qr/service/UsbService.java

@@ -0,0 +1,319 @@
+package com.common.usb.qr.service;
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+
+import com.felhr.usbserial.CDCSerialDevice;
+import com.felhr.usbserial.UsbSerialDevice;
+import com.felhr.usbserial.UsbSerialInterface;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class UsbService extends Service {
+
+    public static final String ACTION_USB_READY = "com.felhr.connectivityservices.USB_READY";
+    public static final String ACTION_USB_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
+    public static final String ACTION_USB_DETACHED = "android.hardware.usb.action.USB_DEVICE_DETACHED";
+    public static final String ACTION_USB_NOT_SUPPORTED = "com.felhr.usbservice.USB_NOT_SUPPORTED";
+    public static final String ACTION_NO_USB = "com.felhr.usbservice.NO_USB";
+    public static final String ACTION_USB_PERMISSION_GRANTED = "com.felhr.usbservice.USB_PERMISSION_GRANTED";
+    public static final String ACTION_USB_PERMISSION_NOT_GRANTED = "com.felhr.usbservice.USB_PERMISSION_NOT_GRANTED";
+    public static final String ACTION_USB_DISCONNECTED = "com.felhr.usbservice.USB_DISCONNECTED";
+    public static final String ACTION_CDC_DRIVER_NOT_WORKING = "com.felhr.connectivityservices.ACTION_CDC_DRIVER_NOT_WORKING";
+    public static final String ACTION_USB_DEVICE_NOT_WORKING = "com.felhr.connectivityservices.ACTION_USB_DEVICE_NOT_WORKING";
+    public static final int MESSAGE_FROM_SERIAL_PORT = 0;
+    public static final int CTS_CHANGE = 1;
+    public static final int DSR_CHANGE = 2;
+    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
+    private static final int BAUD_RATE = 9600; // BaudRate. Change this value if you need
+    public static boolean SERVICE_CONNECTED = false;
+
+    private IBinder binder = new UsbBinder();
+
+    private Context context;
+    private Handler mHandler;
+    private UsbManager usbManager;
+    private UsbDevice device;
+    private UsbDeviceConnection connection;
+    private UsbSerialDevice serialPort;
+
+    private boolean serialPortConnected;
+    /*
+     *  Data received from serial port will be received here. Just populate onReceivedData with your code
+     *  In this particular example. byte stream is converted to String and send to UI thread to
+     *  be treated there.
+     */
+    private UsbSerialInterface.UsbReadCallback mCallback = new UsbSerialInterface.UsbReadCallback() {
+        @Override
+        public void onReceivedData(byte[] arg0) {
+            if (arg0.length<=0){
+                return;
+            }
+            try {
+                String data = new String(DeleteElement(arg0), "UTF-8");
+                if (mHandler != null)
+                    mHandler.obtainMessage(MESSAGE_FROM_SERIAL_PORT, data).sendToTarget();
+            } catch (UnsupportedEncodingException e) {
+                e.printStackTrace();
+            }
+        }
+    };
+
+
+    /**
+     * 数据处理(剔除\r\n的情况)
+     */
+    public static byte[] DeleteElement(byte[] arg0){
+        List<Byte> list=new ArrayList<>();
+        for (int i = 0; i < arg0.length; i++) {
+            if (arg0[i]!=13&&arg0[i]!=10){
+                list.add(arg0[i]);
+            }
+        }
+        byte[] bytes=new byte[list.size()];
+        for (int i = 0; i < list.size(); i++) {
+            bytes[i]=list.get(i);
+        }
+        return bytes;
+    }
+
+    /*
+     * State changes in the CTS line will be received here
+     */
+    private UsbSerialInterface.UsbCTSCallback ctsCallback = new UsbSerialInterface.UsbCTSCallback() {
+        @Override
+        public void onCTSChanged(boolean state) {
+            if(mHandler != null)
+                mHandler.obtainMessage(CTS_CHANGE).sendToTarget();
+        }
+    };
+
+    /*
+     * State changes in the DSR line will be received here
+     */
+    private UsbSerialInterface.UsbDSRCallback dsrCallback = new UsbSerialInterface.UsbDSRCallback() {
+        @Override
+        public void onDSRChanged(boolean state) {
+            if(mHandler != null)
+                mHandler.obtainMessage(DSR_CHANGE).sendToTarget();
+        }
+    };
+    /*
+     * Different notifications from OS will be received here (USB attached, detached, permission responses...)
+     * About BroadcastReceiver: http://developer.android.com/reference/android/content/BroadcastReceiver.html
+     */
+    private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context arg0, Intent arg1) {
+            if (arg1.getAction().equals(ACTION_USB_PERMISSION)) {
+                boolean granted = arg1.getExtras().getBoolean(UsbManager.EXTRA_PERMISSION_GRANTED);
+                if (granted) // User accepted our USB connection. Try to open the device as a serial port
+                {
+                    Intent intent = new Intent(ACTION_USB_PERMISSION_GRANTED);
+                    arg0.sendBroadcast(intent);
+                    connection = usbManager.openDevice(device);
+                    new ConnectionThread().start();
+                } else // User not accepted our USB connection. Send an Intent to the Main Activity
+                {
+                    Intent intent = new Intent(ACTION_USB_PERMISSION_NOT_GRANTED);
+                    arg0.sendBroadcast(intent);
+                }
+            } else if (arg1.getAction().equals(ACTION_USB_ATTACHED)) {
+                if (!serialPortConnected)
+                    findSerialPortDevice(); // A USB device has been attached. Try to open it as a Serial port
+            } else if (arg1.getAction().equals(ACTION_USB_DETACHED)) {
+                // Usb device was disconnected. send an intent to the Main Activity
+                Intent intent = new Intent(ACTION_USB_DISCONNECTED);
+                arg0.sendBroadcast(intent);
+                if (serialPortConnected) {
+                    serialPort.close();
+                }
+                serialPortConnected = false;
+            }
+        }
+    };
+
+    /*
+     * onCreate will be executed when service is started. It configures an IntentFilter to listen for
+     * incoming Intents (USB ATTACHED, USB DETACHED...) and it tries to open a serial port.
+     */
+    @Override
+    public void onCreate() {
+        this.context = this;
+        serialPortConnected = false;
+        UsbService.SERVICE_CONNECTED = true;
+        setFilter();
+        usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
+        findSerialPortDevice();
+    }
+
+    /* MUST READ about services
+     * http://developer.android.com/guide/components/services.html
+     * http://developer.android.com/guide/components/bound-services.html
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        return binder;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        return Service.START_NOT_STICKY;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        UsbService.SERVICE_CONNECTED = false;
+    }
+
+    /*
+     * This function will be called from MainActivity to write data through Serial Port
+     */
+    public void write(byte[] data) {
+        if (serialPort != null)
+            serialPort.write(data);
+    }
+
+    public void setHandler(Handler mHandler) {
+        this.mHandler = mHandler;
+    }
+
+    private void findSerialPortDevice() {
+        // This snippet will try to open the first encountered usb device connected, excluding usb root hubs
+        HashMap<String, UsbDevice> usbDevices = usbManager.getDeviceList();
+        if (!usbDevices.isEmpty()) {
+            boolean keep = true;
+            for (Map.Entry<String, UsbDevice> entry : usbDevices.entrySet()) {
+                device = entry.getValue();
+                int deviceVID = device.getVendorId();
+                int devicePID = device.getProductId();
+
+                if ((deviceVID == 0x27DD && devicePID == 0x0002)
+                        || (deviceVID == 0x0103 && devicePID == 0x6061)
+                        || (deviceVID == 0x26F1 && devicePID == 0x5650)
+                        || (deviceVID == 0x26F1 && devicePID == 0xD001)
+                        || (deviceVID == 0x0483 && devicePID == 0x5740)
+                        || (deviceVID == 0x26F1 && devicePID == 0x8802)
+                        || (deviceVID == 0x152A && devicePID == 0x880F)) {
+                    // There is a device connected to our Android device. Try to open it as a Serial Port.
+                    requestUserPermission();
+                    keep = false;
+                } else {
+                    connection = null;
+                    device = null;
+                }
+
+
+                if (!keep)
+                    break;
+            }
+            if (!keep) {
+                // There is no USB devices connected (but usb host were listed). Send an intent to MainActivity.
+                Intent intent = new Intent(ACTION_NO_USB);
+                sendBroadcast(intent);
+            }
+        } else {
+            // There is no USB devices connected. Send an intent to MainActivity
+            Intent intent = new Intent(ACTION_NO_USB);
+            sendBroadcast(intent);
+        }
+    }
+
+    private void setFilter() {
+        try {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ACTION_USB_PERMISSION);
+            filter.addAction(ACTION_USB_DETACHED);
+            filter.addAction(ACTION_USB_ATTACHED);
+            registerReceiver(usbReceiver, filter);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /*
+     * Request user permission. The response will be received in the BroadcastReceiver
+     */
+    private void requestUserPermission() {
+        PendingIntent mPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
+        usbManager.requestPermission(device, mPendingIntent);
+    }
+
+    public class UsbBinder extends Binder {
+        public UsbService getService() {
+            return UsbService.this;
+        }
+    }
+
+    /*
+     * A simple thread to open a serial port.
+     * Although it should be a fast operation. moving usb operations away from UI thread is a good thing.
+     */
+    private class ConnectionThread extends Thread {
+        @Override
+        public void run() {
+            try {
+                serialPort = UsbSerialDevice.createUsbSerialDevice(device, connection);
+                if (serialPort != null) {
+                    if (serialPort.open()) {
+                        serialPortConnected = true;
+                        serialPort.setBaudRate(BAUD_RATE);
+                        serialPort.setDataBits(UsbSerialInterface.DATA_BITS_8);
+                        serialPort.setStopBits(UsbSerialInterface.STOP_BITS_1);
+                        serialPort.setParity(UsbSerialInterface.PARITY_NONE);
+                        /**
+                         * Current flow control Options:
+                         * UsbSerialInterface.FLOW_CONTROL_OFF
+                         * UsbSerialInterface.FLOW_CONTROL_RTS_CTS only for CP2102 and FT232
+                         * UsbSerialInterface.FLOW_CONTROL_DSR_DTR only for CP2102 and FT232
+                         */
+                        serialPort.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
+                        serialPort.read(mCallback);
+                        serialPort.getCTS(ctsCallback);
+                        serialPort.getDSR(dsrCallback);
+
+                        //
+                        // Some Arduinos would need some sleep because firmware wait some time to know whether a new sketch is going
+                        // to be uploaded or not
+                        //Thread.sleep(2000); // sleep some. YMMV with different chips.
+
+                        // Everything went as expected. Send an intent to MainActivity
+                        Intent intent = new Intent(ACTION_USB_READY);
+                        context.sendBroadcast(intent);
+                    } else {
+                        // Serial port could not be opened, maybe an I/O error or if CDC driver was chosen, it does not really fit
+                        // Send an Intent to Main Activity
+                        if (serialPort instanceof CDCSerialDevice) {
+                            Intent intent = new Intent(ACTION_CDC_DRIVER_NOT_WORKING);
+                            context.sendBroadcast(intent);
+                        } else {
+                            Intent intent = new Intent(ACTION_USB_DEVICE_NOT_WORKING);
+                            context.sendBroadcast(intent);
+                        }
+                    }
+                } else {
+                    // No driver for given device, even generic CDC driver could not be loaded
+                    Intent intent = new Intent(ACTION_USB_NOT_SUPPORTED);
+                    context.sendBroadcast(intent);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}

+ 333 - 0
lib_usb_qr/src/main/java/com/common/usb/qr/utils/HexUtil.java

@@ -0,0 +1,333 @@
+package com.common.usb.qr.utils;
+
+import android.util.Log;
+
+public class HexUtil {
+    private static final String TAG="HexUtil";
+    /**
+     * 用于建立十六进制字符的输出的小写字符数组
+     */
+    private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+    /**
+     * 用于建立十六进制字符的输出的大写字符数组
+     */
+    private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+    /**
+     * 将字节数组转换为十六进制字符数组
+     *
+     * @param data byte[]
+     * @return 十六进制char[]
+     */
+    public static char[] encodeHex(byte[] data) {
+        return encodeHex(data, true);
+    }
+
+    /**
+     * 将字节数组转换为十六进制字符数组
+     *
+     * @param data        byte[]
+     * @param toLowerCase <code>true</code> 传换成小写格式 , <code>false</code> 传换成大写格式
+     * @return 十六进制char[]
+     */
+    public static char[] encodeHex(byte[] data, boolean toLowerCase) {
+        return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+    }
+
+    /**
+     * 将字节数组转换为十六进制字符数组
+     *
+     * @param data     byte[]
+     * @param toDigits 用于控制输出的char[]
+     * @return 十六进制char[]
+     */
+    protected static char[] encodeHex(byte[] data, char[] toDigits) {
+        int l = data.length;
+        char[] out = new char[l << 1];
+        // two characters form the hex value.
+        for (int i = 0, j = 0; i < l; i++) {
+            out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+            out[j++] = toDigits[0x0F & data[i]];
+        }
+        return out;
+    }
+
+    /**
+     * 将字节数组转换为十六进制字符串
+     *
+     * @param data byte[]
+     * @return 十六进制String
+     */
+    public static String encodeHexStr(byte[] data) {
+        return encodeHexStr(data, true);
+    }
+    static public String ByteArrToHex(byte[] inBytArr)//?????????hex?????
+    {
+        StringBuilder strBuilder=new StringBuilder();
+        int j=inBytArr.length;
+        for (int i = 0; i < j; i++)
+        {
+            strBuilder.append(Byte2Hex(inBytArr[i]));
+            strBuilder.append(" ");
+        }
+        return strBuilder.toString();
+    }
+    static public String Byte2Hex(Byte inByte)//1????2??Hex???
+    {
+        return String.format("%02x", inByte).toUpperCase();
+    }
+    /**
+     * 将字节数组转换为十六进制字符串
+     *
+     * @param data        byte[]
+     * @param toLowerCase <code>true</code> 传换成小写格式 , <code>false</code> 传换成大写格式
+     * @return 十六进制String
+     */
+    public static String encodeHexStr(byte[] data, boolean toLowerCase) {
+        return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+    }
+
+    /**
+     * 将字节数组转换为十六进制字符串
+     *
+     * @param data     byte[]
+     * @param toDigits 用于控制输出的char[]
+     * @return 十六进制String
+     */
+    protected static String encodeHexStr(byte[] data, char[] toDigits) {
+        if (data == null) {
+            Log.e(TAG,"this data is null.");
+            return "";
+        }
+        return new String(encodeHex(data, toDigits));
+    }
+
+    /**
+     * 将十六进制字符串转换为字节数组
+     *
+     * @param data
+     * @return
+     */
+    public static byte[] decodeHex(String data) {
+        if (data == null) {
+            Log.e(TAG,"this data is null.");
+            return new byte[0];
+        }
+        return decodeHex(data.toCharArray());
+    }
+
+    /**
+     * 将十六进制字符数组转换为字节数组
+     *
+     * @param data 十六进制char[]
+     * @return byte[]
+     * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常
+     */
+    public static byte[] decodeHex(char[] data) {
+
+        int len = data.length;
+
+        if ((len & 0x01) != 0) {
+            throw new RuntimeException("Odd number of characters.");
+        }
+
+        byte[] out = new byte[len >> 1];
+
+        // two characters form the hex value.
+        for (int i = 0, j = 0; j < len; i++) {
+            int f = toDigit(data[j], j) << 4;
+            j++;
+            f = f | toDigit(data[j], j);
+            j++;
+            out[i] = (byte) (f & 0xFF);
+        }
+
+        return out;
+    }
+
+    /**
+     * 将十六进制字符转换成一个整数
+     *
+     * @param ch    十六进制char
+     * @param index 十六进制字符在字符数组中的位置
+     * @return 一个整数
+     * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常
+     */
+    protected static int toDigit(char ch, int index) {
+        int digit = Character.digit(ch, 16);
+        if (digit == -1) {
+            throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
+        }
+        return digit;
+    }
+
+    /**
+     * 截取字节数组
+     *
+     * @param src   byte []  数组源  这里填16进制的 数组
+     * @param begin 起始位置 源数组的起始位置。0位置有效
+     * @param count 截取长度
+     * @return
+     */
+    public static byte[] subBytes(byte[] src, int begin, int count) {
+        byte[] bs = new byte[count];
+        System.arraycopy(src, begin, bs, 0, count);  // bs 目的数组  0 截取后存放的数值起始位置。0位置有效
+        return bs;
+    }
+
+    /**
+     * int转byte数组
+     *
+     * @param bb
+     * @param x
+     * @param index 第几位开始
+     * @param flag 标识高低位顺序,高位在前为true,低位在前为false
+     */
+    public static void intToByte(byte[] bb, int x, int index, boolean flag) {
+        if (flag) {
+            bb[index + 0] = (byte) (x >> 24);
+            bb[index + 1] = (byte) (x >> 16);
+            bb[index + 2] = (byte) (x >> 8);
+            bb[index + 3] = (byte) (x >> 0);
+        } else {
+            bb[index + 3] = (byte) (x >> 24);
+            bb[index + 2] = (byte) (x >> 16);
+            bb[index + 1] = (byte) (x >> 8);
+            bb[index + 0] = (byte) (x >> 0);
+        }
+    }
+
+    /**
+     * byte数组转int
+     *
+     * @param bb
+     * @param index 第几位开始
+     * @param flag 标识高低位顺序,高位在前为true,低位在前为false
+     * @return
+     */
+    public static int byteToInt(byte[] bb, int index, boolean flag) {
+        if (flag) {
+            return (int) ((((bb[index + 0] & 0xff) << 24)
+                    | ((bb[index + 1] & 0xff) << 16)
+                    | ((bb[index + 2] & 0xff) << 8)
+                    | ((bb[index + 3] & 0xff) << 0)));
+        } else {
+            return (int) ((((bb[index + 3] & 0xff) << 24)
+                    | ((bb[index + 2] & 0xff) << 16)
+                    | ((bb[index + 1] & 0xff) << 8)
+                    | ((bb[index + 0] & 0xff) << 0)));
+        }
+    }
+
+
+    /**
+     * 字节数组逆序
+     *
+     * @param data
+     * @return
+     */
+    public static byte[] reverse(byte[] data) {
+        byte[] reverseData = new byte[data.length];
+        for (int i = 0; i < data.length; i++) {
+            reverseData[i] = data[data.length - 1 - i];
+        }
+        return reverseData;
+    }
+
+    /**
+     * 蓝牙传输 16进制 高低位 读数的 转换
+     *
+     * @param data 截取数据源,字节数组
+     * @param index 截取数据开始位置
+     * @param count 截取数据长度,只能为2、4、8个字节
+     * @param flag 标识高低位顺序,高位在前为true,低位在前为false
+     * @return
+     */
+    public static long byteToLong(byte[] data, int index, int count, boolean flag) {
+        long lg = 0;
+        if (flag) {
+            switch (count) {
+                case 2:
+                    lg = ((((long) data[index + 0] & 0xff) << 8)
+                            | (((long) data[index + 1] & 0xff) << 0));
+                    break;
+
+                case 4:
+                    lg = ((((long) data[index + 0] & 0xff) << 24)
+                            | (((long) data[index + 1] & 0xff) << 16)
+                            | (((long) data[index + 2] & 0xff) << 8)
+                            | (((long) data[index + 3] & 0xff) << 0));
+                    break;
+
+                case 8:
+                    lg = ((((long) data[index + 0] & 0xff) << 56)
+                            | (((long) data[index + 1] & 0xff) << 48)
+                            | (((long) data[index + 2] & 0xff) << 40)
+                            | (((long) data[index + 3] & 0xff) << 32)
+                            | (((long) data[index + 4] & 0xff) << 24)
+                            | (((long) data[index + 5] & 0xff) << 16)
+                            | (((long) data[index + 6] & 0xff) << 8)
+                            | (((long) data[index + 7] & 0xff) << 0));
+                    break;
+            }
+            return lg;
+        } else {
+            switch (count) {
+                case 2:
+                    lg = ((((long) data[index + 1] & 0xff) << 8)
+                            | (((long) data[index + 0] & 0xff) << 0));
+                    break;
+                case 4:
+                    lg = ((((long) data[index + 3] & 0xff) << 24)
+                            | (((long) data[index + 2] & 0xff) << 16)
+                            | (((long) data[index + 1] & 0xff) << 8)
+                            | (((long) data[index + 0] & 0xff) << 0));
+                    break;
+                case 8:
+                    lg = ((((long) data[index + 7] & 0xff) << 56)
+                            | (((long) data[index + 6] & 0xff) << 48)
+                            | (((long) data[index + 5] & 0xff) << 40)
+                            | (((long) data[index + 4] & 0xff) << 32)
+                            | (((long) data[index + 3] & 0xff) << 24)
+                            | (((long) data[index + 2] & 0xff) << 16)
+                            | (((long) data[index + 1] & 0xff) << 8)
+                            | (((long) data[index + 0] & 0xff) << 0));
+                    break;
+            }
+            return lg;
+        }
+    }
+
+    /**
+     * 将十六值转换后的string数据通过ascii码转换为明文字符
+     * @param hex
+     * @return
+     */
+    public static String hex2Str(String hex) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < hex.length() - 1; i += 2) {
+            String h = hex.substring(i, (i + 2));
+            int decimal = Integer.parseInt(h, 16);
+            sb.append((char) decimal);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * String明文转ASCII码hex字符串
+     * @param str
+     * @return
+     */
+    public static String str2Hex(String str) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            // 这里的第二个参数16表示十六进制char
+            sb.append(Integer.toString(c, 16));
+            // 或用toHexString方法直接转成16进制
+            // sb.append(Integer.toHexString(c));
+        }
+        return sb.toString();
+    }
+}

+ 17 - 0
lib_usb_qr/src/test/java/com/common/usb/qr/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.common.usb.qr;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 1 - 0
settings.gradle

@@ -1,3 +1,4 @@
+include ':lib_usb_qr'
 include ':lib_arome'
 include ':MultiReaderLib',':idCard',":recorderlib"
 include ':app', ':lib_base','SeriaApp',':lib_print'

Some files were not shown because too many files changed in this diff