├── app
├── .gitignore
├── libs
│ └── printersdkv1.jar
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── colors.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ ├── bluetooth_device_name_item.xml
│ │ │ │ ├── dialog_bluetooth_list.xml
│ │ │ │ └── activity_main.xml
│ │ │ ├── xml
│ │ │ │ └── device_filter.xml
│ │ │ ├── drawable
│ │ │ │ ├── background_corner.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── jniLibs
│ │ │ ├── x86
│ │ │ │ └── libserial_port.so
│ │ │ ├── armeabi
│ │ │ │ └── libserial_port.so
│ │ │ ├── x86_64
│ │ │ │ └── libserial_port.so
│ │ │ ├── arm64-v8a
│ │ │ │ └── libserial_port.so
│ │ │ └── armeabi-v7a
│ │ │ │ └── libserial_port.so
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── yechaoa
│ │ │ │ └── printertest
│ │ │ │ ├── PrinterCommand.java
│ │ │ │ ├── App.java
│ │ │ │ ├── ThreadFactoryBuilder.java
│ │ │ │ ├── ThreadPool.java
│ │ │ │ ├── BluetoothListActivity.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── DeviceConnFactoryManager.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── yechaoa
│ │ │ └── printertest
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── yechaoa
│ │ └── printertest
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── pic
├── 1.jpg
├── 2.jpg
├── 3.jpg
├── 4.png
└── 5.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── .idea
├── vcs.xml
├── runConfigurations.xml
├── modules.xml
├── gradle.xml
├── misc.xml
└── codeStyles
│ └── Project.xml
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/pic/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/pic/1.jpg
--------------------------------------------------------------------------------
/pic/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/pic/2.jpg
--------------------------------------------------------------------------------
/pic/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/pic/3.jpg
--------------------------------------------------------------------------------
/pic/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/pic/4.png
--------------------------------------------------------------------------------
/pic/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/pic/5.png
--------------------------------------------------------------------------------
/app/libs/printersdkv1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/libs/printersdkv1.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 蓝牙打印
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86/libserial_port.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/jniLibs/x86/libserial_port.so
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/jniLibs/armeabi/libserial_port.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/jniLibs/armeabi/libserial_port.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86_64/libserial_port.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/jniLibs/x86_64/libserial_port.so
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/jniLibs/arm64-v8a/libserial_port.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/jniLibs/arm64-v8a/libserial_port.so
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/jniLibs/armeabi-v7a/libserial_port.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/jniLibs/armeabi-v7a/libserial_port.so
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yechaoa/PrinterDemo/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Nov 22 10:15:02 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/bluetooth_device_name_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yechaoa/printertest/PrinterCommand.java:
--------------------------------------------------------------------------------
1 | package com.yechaoa.printertest;
2 |
3 | /**
4 | * Created by Administrator
5 | */
6 | public enum PrinterCommand {
7 | /**
8 | * ESC指令
9 | */
10 | ESC,
11 | /**
12 | * TSC指令
13 | */
14 | TSC,
15 | /**
16 | * CPCL指令
17 | */
18 | CPCL
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
8 | #383431
9 | #FFFFFF
10 | #F5F5F5
11 | #000000
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/test/java/com/yechaoa/printertest/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.yechaoa.printertest;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/yechaoa/printertest/App.java:
--------------------------------------------------------------------------------
1 | package com.yechaoa.printertest;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | /**
7 | * Created by yechao on 2020/3/26/026.
8 | * Describe :
9 | */
10 | public class App extends Application {
11 |
12 | private static Context mContext;
13 |
14 | @Override
15 | public void onCreate() {
16 | super.onCreate();
17 | mContext = getApplicationContext();
18 | }
19 |
20 | public static Context getContext() {
21 | return mContext;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/device_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_corner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yechaoa/printertest/ThreadFactoryBuilder.java:
--------------------------------------------------------------------------------
1 | package com.yechaoa.printertest;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import java.util.concurrent.ThreadFactory;
6 |
7 | /**
8 | * Created by Administrator
9 | */
10 | public class ThreadFactoryBuilder implements ThreadFactory {
11 |
12 | private String name;
13 | private int counter;
14 |
15 | public ThreadFactoryBuilder(String name) {
16 | this.name = name;
17 | counter = 1;
18 | }
19 |
20 | @Override
21 | public Thread newThread(@NonNull Runnable runnable) {
22 | Thread thread = new Thread(runnable, name);
23 | thread.setName("ThreadFactoryBuilder_" + name + "_" + counter);
24 | return thread;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1024m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/yechaoa/printertest/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.yechaoa.printertest;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.yechaoa.printertest", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.yechaoa.printertest"
7 | minSdkVersion 16
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(include: ['*.jar'], dir: 'libs')
23 | implementation 'com.android.support:appcompat-v7:28.0.0'
24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
28 | implementation files('libs/printersdkv1.jar')
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_bluetooth_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
26 |
27 |
34 |
35 |
42 |
43 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | 1.8
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yechaoa/printertest/ThreadPool.java:
--------------------------------------------------------------------------------
1 | package com.yechaoa.printertest;
2 |
3 | import java.util.concurrent.ArrayBlockingQueue;
4 | import java.util.concurrent.BlockingQueue;
5 | import java.util.concurrent.ThreadFactory;
6 | import java.util.concurrent.ThreadPoolExecutor;
7 | import java.util.concurrent.TimeUnit;
8 |
9 | /**
10 | * Created by Administrator
11 | */
12 | public class ThreadPool {
13 |
14 | private static ThreadPool threadPool;
15 | /**
16 | * java线程池
17 | */
18 | private ThreadPoolExecutor threadPoolExecutor;
19 |
20 | /**
21 | * 最大线程数
22 | */
23 | private final static int MAX_POOL_COUNTS = 20;
24 |
25 | /**
26 | * 线程存活时间
27 | */
28 | private final static long ALIVETIME = 200L;
29 |
30 | /**
31 | * 核心线程数
32 | */
33 | private final static int CORE_POOL_SIZE = 20;
34 |
35 | /**
36 | * 线程池缓存队列
37 | */
38 | private BlockingQueue mWorkQueue = new ArrayBlockingQueue<>(CORE_POOL_SIZE);
39 |
40 | /**
41 | * 为线程池提供创建新线程的功能
42 | */
43 | private ThreadFactory threadFactory = new ThreadFactoryBuilder("ThreadPool");
44 |
45 | private ThreadPool() {
46 | //初始化线程池 核心线程数为20,最大线程数30,线程存活200L,线程队列mWorkQueue,
47 | threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_COUNTS, ALIVETIME, TimeUnit.SECONDS, mWorkQueue, threadFactory);
48 | }
49 |
50 | public static ThreadPool getInstantiation() {
51 | if (threadPool == null) {
52 | threadPool = new ThreadPool();
53 | }
54 | return threadPool;
55 | }
56 |
57 | /**
58 | * 为线程池添加任务
59 | */
60 | public void addTask(Runnable runnable) {
61 | if (runnable == null) {
62 | throw new NullPointerException("addTask(Runnable runnable)传入参数为空");
63 | }
64 | if (threadPoolExecutor != null && threadPoolExecutor.getActiveCount() < MAX_POOL_COUNTS) {
65 | threadPoolExecutor.execute(runnable);
66 | }
67 | }
68 |
69 | public void stopThreadPool() {
70 | if (threadPoolExecutor != null) {
71 | threadPoolExecutor.shutdown();
72 | threadPoolExecutor = null;
73 | threadPool = null;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yechaoa/printertest/BluetoothListActivity.java:
--------------------------------------------------------------------------------
1 | package com.yechaoa.printertest;
2 |
3 | import android.app.Activity;
4 | import android.bluetooth.BluetoothAdapter;
5 | import android.bluetooth.BluetoothDevice;
6 | import android.content.BroadcastReceiver;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.IntentFilter;
10 | import android.os.Bundle;
11 | import android.util.Log;
12 | import android.view.View;
13 | import android.view.Window;
14 | import android.widget.AdapterView;
15 | import android.widget.AdapterView.OnItemClickListener;
16 | import android.widget.ArrayAdapter;
17 | import android.widget.ListView;
18 | import android.widget.TextView;
19 | import android.widget.Toast;
20 |
21 | import java.util.Set;
22 |
23 | /**
24 | * 主要负责打开、搜索、显示蓝牙
25 | */
26 | public class BluetoothListActivity extends Activity {
27 |
28 | private ListView lvPairedDevice;
29 | private BluetoothAdapter mBluetoothAdapter;
30 | private ArrayAdapter mDevicesArrayAdapter;
31 | public static final String EXTRA_DEVICE_ADDRESS = "address";
32 | public static final int REQUEST_ENABLE_BT = 2;
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
38 | setContentView(R.layout.dialog_bluetooth_list);
39 |
40 | lvPairedDevice = findViewById(R.id.lvPairedDevices);
41 | findViewById(R.id.btBluetoothScan).setOnClickListener(new View.OnClickListener() {
42 | @Override
43 | public void onClick(View view) {
44 | view.setVisibility(View.GONE);
45 | discoveryDevice();
46 | }
47 | });
48 |
49 | // 设置广播信息过滤 并注册
50 | // Register for broadcasts when a device is discovered
51 | IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
52 | this.registerReceiver(mFindBlueToothReceiver, filter);
53 | // Register for broadcasts when discovery has finished
54 | filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
55 | this.registerReceiver(mFindBlueToothReceiver, filter);
56 |
57 | initBluetooth();
58 | }
59 |
60 | /**
61 | * 初始化蓝牙
62 | */
63 | private void initBluetooth() {
64 | // 获取蓝牙适配器
65 | mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
66 | // 检查蓝牙是否可用
67 | if (mBluetoothAdapter == null) {
68 | Toast.makeText(this, "当前设备不支持蓝牙", Toast.LENGTH_SHORT).show();
69 | } else {
70 | // 检查蓝牙是否打开
71 | if (!mBluetoothAdapter.isEnabled()) {
72 | Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
73 | startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
74 | } else {
75 | getDeviceList();
76 | }
77 | }
78 | }
79 |
80 | @Override
81 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
82 | super.onActivityResult(requestCode, resultCode, data);
83 | if (requestCode == REQUEST_ENABLE_BT) {
84 | if (resultCode == Activity.RESULT_OK) {
85 | // bluetooth is opened
86 | getDeviceList();
87 | } else {
88 | // bluetooth is not open
89 | Toast.makeText(this, "蓝牙没有开启", Toast.LENGTH_SHORT).show();
90 | }
91 | }
92 | }
93 |
94 | /**
95 | * 蓝牙设备列表
96 | */
97 | protected void getDeviceList() {
98 | // 初始化一个数组适配器,用来显示已匹对和未匹对的设备
99 | mDevicesArrayAdapter = new ArrayAdapter<>(this, R.layout.bluetooth_device_name_item);
100 | lvPairedDevice.setAdapter(mDevicesArrayAdapter);
101 | lvPairedDevice.setOnItemClickListener(mDeviceClickListener);
102 | // 已匹对数据
103 | Set pairedDevices = mBluetoothAdapter.getBondedDevices();
104 | // 添加一个item显示信息
105 | mDevicesArrayAdapter.add("已配对:");
106 | if (pairedDevices.size() > 0) {
107 | //遍历填充数据
108 | for (BluetoothDevice device : pairedDevices) {
109 | mDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
110 | }
111 | } else {
112 | mDevicesArrayAdapter.add("没有已配对设备");
113 | }
114 | }
115 |
116 | /**
117 | * 接收扫描设备的广播
118 | * changes the title when discovery is finished
119 | */
120 | private final BroadcastReceiver mFindBlueToothReceiver = new BroadcastReceiver() {
121 | @Override
122 | public void onReceive(Context context, Intent intent) {
123 | String action = intent.getAction();
124 | // 每当发现一个蓝牙设备时
125 | if (BluetoothDevice.ACTION_FOUND.equals(action)) {
126 | // Get the BluetoothDevice object from the Intent
127 | //获取设备
128 | BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
129 | // If it's already paired, skip it, because it's been listed
130 | // 未匹对的情况下添加显示
131 | if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
132 | mDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
133 | }
134 | // 扫描结束
135 | } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
136 | setProgressBarIndeterminateVisibility(false);
137 | setTitle("选择蓝牙设备");
138 | //此处-2是减去我们手动添加的两个区分显示的item
139 | Log.i("tag", "finish discovery" + (mDevicesArrayAdapter.getCount() - 2));
140 | if (mDevicesArrayAdapter.getCount() == 0) {
141 | mDevicesArrayAdapter.add("没有找到蓝牙设备");
142 | }
143 | }
144 | }
145 | };
146 |
147 | /**
148 | * 扫描设备
149 | */
150 | private void discoveryDevice() {
151 | setProgressBarIndeterminateVisibility(true);
152 | setTitle("扫描中");
153 | // 添加一个item区分显示信息
154 | mDevicesArrayAdapter.add("未配对:");
155 | // If we're already discovering, stop it
156 | if (mBluetoothAdapter.isDiscovering()) {
157 | mBluetoothAdapter.cancelDiscovery();
158 | }
159 | // 开始扫描,每扫描到一个设备,都会发送一个广播
160 | mBluetoothAdapter.startDiscovery();
161 | }
162 |
163 | /**
164 | * The on-click listener for all devices in the ListViews
165 | */
166 | private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
167 | @Override
168 | public void onItemClick(AdapterView> av, View v, int arg2, long arg3) {
169 | // Cancel discovery because it's costly and we're about to connect
170 | // Get the device MAC address, which is the last 17 chars in the View
171 | String info = ((TextView) v).getText().toString();
172 | String noDevices = "没有已配对设备";
173 | String noNewDevice = "没有找到蓝牙设备";
174 | Log.i("TAG", info);
175 | // info 不是我们手动添加的信息 即表示为真实蓝牙设备信息
176 | if (!info.equals(noDevices) && !info.equals(noNewDevice) && !info.equals("未配对") && !info.equals("已配对")) {
177 | mBluetoothAdapter.cancelDiscovery();
178 | //mac 地址
179 | String address = info.substring(info.length() - 17);
180 | // 设置信息并返回
181 | // Set result and finish this Activity
182 | Intent intent = new Intent();
183 | intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
184 | setResult(Activity.RESULT_OK, intent);
185 | finish();
186 | }
187 | }
188 | };
189 |
190 | @Override
191 | protected void onDestroy() {
192 | super.onDestroy();
193 | // Make sure we're not doing discovery anymore
194 | if (mBluetoothAdapter != null) {
195 | mBluetoothAdapter.cancelDiscovery();
196 | }
197 | // Unregister broadcast listeners
198 | unregisterReceiver(mFindBlueToothReceiver);
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yechaoa/printertest/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.yechaoa.printertest;
2 |
3 | import android.Manifest;
4 | import android.annotation.SuppressLint;
5 | import android.content.BroadcastReceiver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.IntentFilter;
9 | import android.content.pm.PackageManager;
10 | import android.os.Bundle;
11 | import android.os.Handler;
12 | import android.os.Message;
13 | import android.support.v4.app.ActivityCompat;
14 | import android.support.v4.content.ContextCompat;
15 | import android.support.v7.app.AppCompatActivity;
16 | import android.util.Log;
17 | import android.view.View;
18 | import android.widget.TextView;
19 | import android.widget.Toast;
20 |
21 | import com.printer.command.EscCommand;
22 | import com.printer.command.LabelCommand;
23 |
24 | import java.util.ArrayList;
25 | import java.util.Vector;
26 |
27 | import static android.hardware.usb.UsbManager.ACTION_USB_DEVICE_DETACHED;
28 | import static com.yechaoa.printertest.DeviceConnFactoryManager.ACTION_QUERY_PRINTER_STATE;
29 | import static com.yechaoa.printertest.DeviceConnFactoryManager.CONN_STATE_FAILED;
30 |
31 | public class MainActivity extends AppCompatActivity {
32 |
33 | private TextView mTvState;
34 |
35 | /**
36 | * 权限请求码
37 | */
38 | private static final int REQUEST_CODE = 0x001;
39 |
40 | /**
41 | * 蓝牙所需权限
42 | */
43 | private String[] permissions = {
44 | Manifest.permission.ACCESS_FINE_LOCATION,
45 | Manifest.permission.ACCESS_COARSE_LOCATION,
46 | Manifest.permission.BLUETOOTH
47 | };
48 |
49 | /**
50 | * 未授予的权限
51 | */
52 | private ArrayList per = new ArrayList<>();
53 |
54 | /**
55 | * 蓝牙请求码
56 | */
57 | public static final int BLUETOOTH_REQUEST_CODE = 0x002;
58 |
59 | private ThreadPool threadPool;//线程
60 |
61 | /**
62 | * 判断打印机所使用指令是否是ESC指令
63 | */
64 | private int id = 0;
65 |
66 | /**
67 | * 打印机是否连接
68 | */
69 | private static final int CONN_PRINTER = 0x003;
70 | /**
71 | * 使用打印机指令错误
72 | */
73 | private static final int PRINTER_COMMAND_ERROR = 0x004;
74 |
75 | /**
76 | * 连接状态断开
77 | */
78 | private static final int CONN_STATE_DISCONN = 0x005;
79 |
80 | @Override
81 | protected void onCreate(Bundle savedInstanceState) {
82 | super.onCreate(savedInstanceState);
83 | setContentView(R.layout.activity_main);
84 |
85 | mTvState = findViewById(R.id.tv_state);
86 |
87 | checkPermission();
88 | requestPermission();
89 |
90 | }
91 |
92 | private void checkPermission() {
93 | for (String permission : permissions) {
94 | if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, permission)) {
95 | per.add(permission);
96 | }
97 | }
98 | }
99 |
100 | private void requestPermission() {
101 | if (per.size() > 0) {
102 | String[] p = new String[per.size()];
103 | ActivityCompat.requestPermissions(this, per.toArray(p), REQUEST_CODE);
104 | }
105 | }
106 |
107 |
108 | public void btnConnect(View view) {
109 | startActivityForResult(new Intent(MainActivity.this, BluetoothListActivity.class), BLUETOOTH_REQUEST_CODE);
110 | }
111 |
112 | @Override
113 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
114 | super.onActivityResult(requestCode, resultCode, data);
115 | if (resultCode == RESULT_OK) {
116 | //蓝牙连接
117 | if (requestCode == BLUETOOTH_REQUEST_CODE) {
118 | closePort();
119 | //获取蓝牙mac地址
120 | String macAddress = data.getStringExtra(BluetoothListActivity.EXTRA_DEVICE_ADDRESS);
121 | //初始化DeviceConnFactoryManager 并设置信息
122 | new DeviceConnFactoryManager.Build()
123 | //设置标识符
124 | .setId(id)
125 | //设置连接方式
126 | .setConnMethod(DeviceConnFactoryManager.CONN_METHOD.BLUETOOTH)
127 | //设置连接的蓝牙mac地址
128 | .setMacAddress(macAddress)
129 | .build();
130 | //配置完信息,就可以打开端口连接了
131 | Log.i("TAG", "onActivityResult: 连接蓝牙" + id);
132 | threadPool = ThreadPool.getInstantiation();
133 | threadPool.addTask(new Runnable() {
134 | @Override
135 | public void run() {
136 | DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].openPort();
137 | }
138 | });
139 | }
140 | }
141 | }
142 |
143 | /**
144 | * 重新连接回收上次连接的对象,避免内存泄漏
145 | */
146 | private void closePort() {
147 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] != null && DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].mPort != null) {
148 | DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].reader.cancel();
149 | DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].mPort.closePort();
150 | DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].mPort = null;
151 | }
152 | }
153 |
154 | @Override
155 | protected void onStart() {
156 | super.onStart();
157 | /*
158 | * 注册接收连接状态的广播
159 | */
160 | IntentFilter filter = new IntentFilter();
161 | filter.addAction(ACTION_QUERY_PRINTER_STATE);
162 | filter.addAction(DeviceConnFactoryManager.ACTION_CONN_STATE);
163 | registerReceiver(receiver, filter);
164 | }
165 |
166 | @Override
167 | protected void onStop() {
168 | super.onStop();
169 | unregisterReceiver(receiver);
170 | }
171 |
172 | /**
173 | * 连接状态的广播
174 | */
175 | private BroadcastReceiver receiver = new BroadcastReceiver() {
176 | @Override
177 | public void onReceive(Context context, Intent intent) {
178 | String action = intent.getAction();
179 | if (DeviceConnFactoryManager.ACTION_CONN_STATE.equals(action)) {
180 | int state = intent.getIntExtra(DeviceConnFactoryManager.STATE, -1);
181 | int deviceId = intent.getIntExtra(DeviceConnFactoryManager.DEVICE_ID, -1);
182 | switch (state) {
183 | case DeviceConnFactoryManager.CONN_STATE_DISCONNECT:
184 | if (id == deviceId) mTvState.setText("未连接");
185 | break;
186 | case DeviceConnFactoryManager.CONN_STATE_CONNECTING:
187 | mTvState.setText("连接中");
188 | break;
189 | case DeviceConnFactoryManager.CONN_STATE_CONNECTED:
190 | mTvState.setText("已连接");
191 | Toast.makeText(MainActivity.this, "已连接", Toast.LENGTH_SHORT).show();
192 | break;
193 | case CONN_STATE_FAILED:
194 | mTvState.setText("未连接");
195 | Toast.makeText(MainActivity.this, "连接失败!重试或重启打印机试试", Toast.LENGTH_SHORT).show();
196 | break;
197 | }
198 | /* Usb连接断开、蓝牙连接断开广播 */
199 | } else if (ACTION_USB_DEVICE_DETACHED.equals(action)) {
200 | mHandler.obtainMessage(CONN_STATE_DISCONN).sendToTarget();
201 | }
202 | }
203 | };
204 |
205 | @SuppressLint("HandlerLeak")
206 | private Handler mHandler = new Handler() {
207 | @Override
208 | public void handleMessage(Message msg) {
209 | switch (msg.what) {
210 | case CONN_STATE_DISCONN:
211 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] != null || !DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].getConnState()) {
212 | DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].closePort(id);
213 | Toast.makeText(MainActivity.this, "成功断开连接", Toast.LENGTH_SHORT).show();
214 | }
215 | break;
216 | case PRINTER_COMMAND_ERROR:
217 | Toast.makeText(MainActivity.this, "请选择正确的打印机指令", Toast.LENGTH_SHORT).show();
218 | break;
219 | case CONN_PRINTER:
220 | Toast.makeText(MainActivity.this, "请先连接打印机", Toast.LENGTH_SHORT).show();
221 | break;
222 | }
223 | }
224 | };
225 |
226 |
227 | /**
228 | * 打印标签
229 | */
230 | public void btnPrint(View view) {
231 | printLabel();
232 | }
233 |
234 | public void printLabel() {
235 | Log.i("TAG", "准备打印");
236 | threadPool = ThreadPool.getInstantiation();
237 | threadPool.addTask(new Runnable() {
238 | @Override
239 | public void run() {
240 | //先判断打印机是否连接
241 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] == null ||
242 | !DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].getConnState()) {
243 | mHandler.obtainMessage(CONN_PRINTER).sendToTarget();
244 | return;
245 | }
246 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].getCurrentPrinterCommand() == PrinterCommand.TSC) {
247 | Log.i("TAG", "开始打印");
248 | sendLabel();
249 | } else {
250 | mHandler.obtainMessage(PRINTER_COMMAND_ERROR).sendToTarget();
251 | }
252 | }
253 | });
254 | }
255 |
256 | private void sendLabel() {
257 | LabelCommand tsc = new LabelCommand();
258 | tsc.addSize(40, 30); // 设置标签尺寸,按照实际尺寸设置
259 | tsc.addGap(1); // 设置标签间隙,按照实际尺寸设置,如果为无间隙纸则设置为0
260 | tsc.addDirection(LabelCommand.DIRECTION.FORWARD, LabelCommand.MIRROR.NORMAL);// 设置打印方向
261 | tsc.addQueryPrinterStatus(LabelCommand.RESPONSE_MODE.ON);//开启带Response的打印,用于连续打印
262 | tsc.addReference(0, 0);// 设置原点坐标
263 | tsc.addTear(EscCommand.ENABLE.ON); // 撕纸模式开启
264 | tsc.addCls();// 清除打印缓冲区
265 |
266 | // 绘制简体中文
267 | tsc.addText(30, 30, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
268 | "这是标题");
269 | tsc.addText(200, 30, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
270 | "序号:" + "1");
271 |
272 | tsc.addText(30, 90, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
273 | "价格:" + "99.00");
274 | tsc.addText(30, 140, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
275 | "数量:" + "99");
276 | tsc.addText(30, 190, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
277 | "日期:" + "2020-02-02");
278 |
279 | // 绘制图片
280 | // Bitmap b = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
281 | // tsc.addBitmap(20, 50, LabelCommand.BITMAP_MODE.OVERWRITE, b.getWidth(), b);
282 |
283 | //二维码
284 | tsc.addQRCode(200, 90, LabelCommand.EEC.LEVEL_L, 4, LabelCommand.ROTATION.ROTATION_0, "www.baidu.com");
285 |
286 | tsc.addPrint(1, 1); // 打印标签
287 | tsc.addSound(2, 100); // 打印标签后 蜂鸣器响
288 |
289 | /* 发送数据 */
290 | Vector data = tsc.getCommand();
291 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] == null) {
292 | Log.i("TAG", "sendLabel: 打印机为空");
293 | return;
294 | }
295 | DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].sendDataImmediately(data);
296 | }
297 |
298 | /**
299 | * 多次打印
300 | */
301 | public void btnPrint3(View view) {
302 | printLabel3();
303 | }
304 |
305 | public void printLabel3() {
306 | Log.i("TAG", "准备打印");
307 | threadPool = ThreadPool.getInstantiation();
308 | threadPool.addTask(new Runnable() {
309 | @Override
310 | public void run() {
311 | //先判断打印机是否连接
312 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] == null ||
313 | !DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].getConnState()) {
314 | mHandler.obtainMessage(CONN_PRINTER).sendToTarget();
315 | return;
316 | }
317 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].getCurrentPrinterCommand() == PrinterCommand.TSC) {
318 | Log.i("TAG", "开始打印");
319 | //循环多次打印
320 | for (int i = 0; i < 3; i++) {
321 | sendLabel3(i);
322 | }
323 | } else {
324 | mHandler.obtainMessage(PRINTER_COMMAND_ERROR).sendToTarget();
325 | }
326 | }
327 | });
328 | }
329 |
330 | private void sendLabel3(int count) {
331 | LabelCommand tsc = new LabelCommand();
332 | tsc.addSize(40, 30); // 设置标签尺寸,按照实际尺寸设置
333 | tsc.addGap(1); // 设置标签间隙,按照实际尺寸设置,如果为无间隙纸则设置为0
334 | tsc.addDirection(LabelCommand.DIRECTION.FORWARD, LabelCommand.MIRROR.NORMAL);// 设置打印方向
335 | tsc.addQueryPrinterStatus(LabelCommand.RESPONSE_MODE.ON);//开启带Response的打印,用于连续打印
336 | tsc.addReference(0, 0);// 设置原点坐标
337 | tsc.addTear(EscCommand.ENABLE.ON); // 撕纸模式开启
338 | tsc.addCls();// 清除打印缓冲区
339 |
340 | // 绘制简体中文
341 | tsc.addText(30, 30, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
342 | "这是标题");
343 | tsc.addText(200, 30, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
344 | "序号:" + count);
345 |
346 | tsc.addText(30, 90, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
347 | "价格:" + "99.00");
348 | tsc.addText(30, 140, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
349 | "数量:" + "99");
350 | tsc.addText(30, 190, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
351 | "日期:" + "2020-02-02");
352 |
353 | //二维码
354 | tsc.addQRCode(200, 90, LabelCommand.EEC.LEVEL_L, 4, LabelCommand.ROTATION.ROTATION_0, "www.baidu.com");
355 |
356 | tsc.addPrint(1, 1); // 打印标签
357 | tsc.addSound(2, 100); // 打印标签后 蜂鸣器响
358 |
359 | /* 发送数据 */
360 | Vector data = tsc.getCommand();
361 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] == null) {
362 | Log.i("TAG", "sendLabel: 打印机为空");
363 | return;
364 | }
365 | DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].sendDataImmediately(data);
366 | }
367 |
368 | /**
369 | * 断开连接
370 | */
371 | public void btnDisConn(View view) {
372 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] == null ||
373 | !DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].getConnState()) {
374 | Toast.makeText(this, "请先连接打印机", Toast.LENGTH_SHORT).show();
375 | return;
376 | }
377 | mHandler.obtainMessage(CONN_STATE_DISCONN).sendToTarget();
378 | }
379 |
380 | @Override
381 | public void onDestroy() {
382 | super.onDestroy();
383 | Log.i("TAG", "onDestroy");
384 | DeviceConnFactoryManager.closeAllPort();
385 | if (threadPool != null) {
386 | threadPool.stopThreadPool();
387 | threadPool = null;
388 | }
389 | }
390 |
391 | }
392 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PrinterDemo
2 |
3 | Android蓝牙连打印机,博客地址:http://blog.csdn.net/yechaoa/article/details/78666446
4 |
5 | # 效果
6 |
7 | 
8 | 
9 | 
10 |
11 |
12 | > 特别声明:较之前的版本有很大的调整,之前的版本是未适配8.0的,现在是适配了的,接入方式也不一样,包括代码逻辑都有调整,不过梳理清楚流程之后也就不难了,非常简单。(更新时间:2020-03-27)
13 |
14 |
15 | # 开发环境
16 | - Gprinter佳博打印机,型号GP-2120TU
17 | - Android Studio 3.6.1,gradle 3.5.3
18 |
19 | > 本文可能内容比较长,但是难度不大。
20 |
21 | # 涉及知识点
22 | - 蓝牙相关
23 | - 多线程开发
24 | - 线程池
25 | - 构建者模式
26 | - 单例模式
27 | - 运行时权限
28 | - BroadcastReceiver
29 | - startActivityForResult
30 | - 等等
31 |
32 |
33 | # 集成配置
34 |
35 | 1.添加jar包,在app目录下新建libs文件夹,拷入jar文件并Add As Library
36 |
37 | 
38 |
39 | 2.在main目录下新建jniLibs目录,并拷入so文件
40 |
41 | 
42 |
43 | 3.配置我们的manifest文件,添加必要的权限
44 |
45 | ```
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
58 | ```
59 |
60 | ok,基本的配置完成,开始进入正题。
61 |
62 | 基本大的流程分为两部分,一是蓝牙连接,二是打印,下面开始一一介绍。
63 |
64 |
65 |
66 | # 蓝牙连接
67 |
68 | 分为几个小步骤
69 |
70 | 1.因为蓝牙涉及到隐私权限,所以先检查、请求权限
71 | ```
72 | private void checkPermission() {
73 | for (String permission : permissions) {
74 | if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, permission)) {
75 | per.add(permission);
76 | }
77 | }
78 | }
79 |
80 | private void requestPermission() {
81 | if (per.size() > 0) {
82 | String[] p = new String[per.size()];
83 | ActivityCompat.requestPermissions(this, per.toArray(p), REQUEST_CODE);
84 | }
85 | }
86 | ```
87 |
88 | 2.点击连接按钮,通过startActivityForResult启动一个蓝牙列表页面,实际上显示为一个dialog
89 | ```
90 | public void btnConnect(View view) {
91 | startActivityForResult(new Intent(MainActivity.this, BluetoothListActivity.class), BLUETOOTH_REQUEST_CODE);
92 | }
93 | ```
94 |
95 | 3.这个页面的作用就是判断蓝牙是否可用、是否开启,显示已配对和未配对的蓝牙设备列表
96 |
97 | ```
98 | /**
99 | * 初始化蓝牙
100 | */
101 | private void initBluetooth() {
102 | // 获取蓝牙适配器
103 | mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
104 | // 检查蓝牙是否可用
105 | if (mBluetoothAdapter == null) {
106 | Toast.makeText(this, "当前设备不支持蓝牙", Toast.LENGTH_SHORT).show();
107 | } else {
108 | // 检查蓝牙是否打开
109 | if (!mBluetoothAdapter.isEnabled()) {
110 | Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
111 | startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
112 | } else {
113 | getDeviceList();
114 | }
115 | }
116 | }
117 |
118 | @Override
119 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
120 | super.onActivityResult(requestCode, resultCode, data);
121 | if (requestCode == REQUEST_ENABLE_BT) {
122 | if (resultCode == Activity.RESULT_OK) {
123 | // bluetooth is opened
124 | getDeviceList();
125 | } else {
126 | // bluetooth is not open
127 | Toast.makeText(this, "蓝牙没有开启", Toast.LENGTH_SHORT).show();
128 | }
129 | }
130 | }
131 |
132 | /**
133 | * 蓝牙设备列表
134 | */
135 | protected void getDeviceList() {
136 | // 初始化一个数组适配器,用来显示已匹对和未匹对的设备
137 | mDevicesArrayAdapter = new ArrayAdapter<>(this, R.layout.bluetooth_device_name_item);
138 | lvPairedDevice.setAdapter(mDevicesArrayAdapter);
139 | lvPairedDevice.setOnItemClickListener(mDeviceClickListener);
140 | // 已匹对数据
141 | Set pairedDevices = mBluetoothAdapter.getBondedDevices();
142 | // 添加一个item显示信息
143 | mDevicesArrayAdapter.add("已配对:");
144 | if (pairedDevices.size() > 0) {
145 | //遍历填充数据
146 | for (BluetoothDevice device : pairedDevices) {
147 | mDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
148 | }
149 | } else {
150 | mDevicesArrayAdapter.add("没有已配对设备");
151 | }
152 | }
153 |
154 | /**
155 | * 接收扫描设备的广播
156 | * changes the title when discovery is finished
157 | */
158 | private final BroadcastReceiver mFindBlueToothReceiver = new BroadcastReceiver() {
159 | @Override
160 | public void onReceive(Context context, Intent intent) {
161 | String action = intent.getAction();
162 | // 每当发现一个蓝牙设备时
163 | if (BluetoothDevice.ACTION_FOUND.equals(action)) {
164 | // Get the BluetoothDevice object from the Intent
165 | //获取设备
166 | BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
167 | // If it's already paired, skip it, because it's been listed
168 | // 未匹对的情况下添加显示
169 | if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
170 | mDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
171 | }
172 | // 扫描结束
173 | } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
174 | setProgressBarIndeterminateVisibility(false);
175 | setTitle("选择蓝牙设备");
176 | //此处-2是减去我们手动添加的两个区分显示的item
177 | Log.i("tag", "finish discovery" + (mDevicesArrayAdapter.getCount() - 2));
178 | if (mDevicesArrayAdapter.getCount() == 0) {
179 | mDevicesArrayAdapter.add("没有找到蓝牙设备");
180 | }
181 | }
182 | }
183 | };
184 |
185 | /**
186 | * 扫描设备
187 | */
188 | private void discoveryDevice() {
189 | setProgressBarIndeterminateVisibility(true);
190 | setTitle("扫描中");
191 | // 添加一个item区分显示信息
192 | mDevicesArrayAdapter.add("未配对:");
193 | // If we're already discovering, stop it
194 | if (mBluetoothAdapter.isDiscovering()) {
195 | mBluetoothAdapter.cancelDiscovery();
196 | }
197 | // 开始扫描,每扫描到一个设备,都会发送一个广播
198 | mBluetoothAdapter.startDiscovery();
199 | }
200 | ```
201 |
202 | 4.点击一个列表item的时候 即表示连接此设备,通过setResult返回该item对应的设备mac地址
203 |
204 | ```
205 | private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
206 | @Override
207 | public void onItemClick(AdapterView> av, View v, int arg2, long arg3) {
208 | // Cancel discovery because it's costly and we're about to connect
209 | // Get the device MAC address, which is the last 17 chars in the View
210 | String info = ((TextView) v).getText().toString();
211 | String noDevices = "没有已配对设备";
212 | String noNewDevice = "没有找到蓝牙设备";
213 | Log.i("TAG", info);
214 | // info 不是我们手动添加的信息 即表示为真实蓝牙设备信息
215 | if (!info.equals(noDevices) && !info.equals(noNewDevice) && !info.equals("未配对") && !info.equals("已配对")) {
216 | mBluetoothAdapter.cancelDiscovery();
217 | //mac 地址
218 | String address = info.substring(info.length() - 17);
219 | // 设置信息并返回
220 | // Set result and finish this Activity
221 | Intent intent = new Intent();
222 | intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
223 | setResult(Activity.RESULT_OK, intent);
224 | finish();
225 | }
226 | }
227 | };
228 | ```
229 |
230 | 5.在MainActivity的onActivityResult中获取mac地址,并通过设备连接管理类DeviceConnFactoryManager进行连接
231 | ```
232 | @Override
233 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
234 | super.onActivityResult(requestCode, resultCode, data);
235 | if (resultCode == RESULT_OK) {
236 | //蓝牙连接
237 | if (requestCode == BLUETOOTH_REQUEST_CODE) {
238 | closePort();
239 | //获取蓝牙mac地址
240 | String macAddress = data.getStringExtra(BluetoothListActivity.EXTRA_DEVICE_ADDRESS);
241 | //初始化DeviceConnFactoryManager 并设置信息
242 | new DeviceConnFactoryManager.Build()
243 | //设置标识符
244 | .setId(id)
245 | //设置连接方式
246 | .setConnMethod(DeviceConnFactoryManager.CONN_METHOD.BLUETOOTH)
247 | //设置连接的蓝牙mac地址
248 | .setMacAddress(macAddress)
249 | .build();
250 | //配置完信息,就可以打开端口连接了
251 | Log.i("TAG", "onActivityResult: 连接蓝牙" + id);
252 | threadPool = ThreadPool.getInstantiation();
253 | threadPool.addTask(new Runnable() {
254 | @Override
255 | public void run() {
256 | DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].openPort();
257 | }
258 | });
259 | }
260 | }
261 | }
262 | ```
263 | 这里可以看到,我们是先拿到了mac地址,然后通过管理类的构建者模式进行设置参数,最后再通过管理类调用openPort方法去连接的。
264 |
265 | 6.在连接管理类中通过jar包封装的方法进行连接,并通过广播Broadcast把连接状态发送出去
266 |
267 | ```
268 | public void openPort() {
269 | deviceConnFactoryManagers[id].isOpenPort = false;
270 | sendStateBroadcast(CONN_STATE_CONNECTING);
271 | switch (deviceConnFactoryManagers[id].connMethod) {
272 | case BLUETOOTH:
273 | System.out.println("id -> " + id);
274 | mPort = new BluetoothPort(macAddress);
275 | isOpenPort = deviceConnFactoryManagers[id].mPort.openPort();
276 | break;
277 | default:
278 | break;
279 | }
280 |
281 | //端口打开成功后,检查连接打印机所使用的打印机指令ESC、TSC
282 | if (isOpenPort) {
283 | queryCommand();
284 | } else {
285 | if (this.mPort != null) {
286 | this.mPort = null;
287 | }
288 | sendStateBroadcast(CONN_STATE_FAILED);
289 | }
290 | }
291 | ```
292 | 这里通过实例化BluetoothPort把mac地址传入,然后调用了PortManager的openPort方法返回了一个状态。
293 |
294 | 往下看,这个switch代码块之后就是对这个状态的判断,当状态为已连接的时候调用了queryCommand方法,这个方法里面有一些操作,其中一个就是通过广播把已连接的状态发出去。
295 |
296 | 7.在MainActivity中接收广播,并根据状态对界面进行显示处理
297 | ```
298 | private BroadcastReceiver receiver = new BroadcastReceiver() {
299 | @Override
300 | public void onReceive(Context context, Intent intent) {
301 | String action = intent.getAction();
302 | if (DeviceConnFactoryManager.ACTION_CONN_STATE.equals(action)) {
303 | int state = intent.getIntExtra(DeviceConnFactoryManager.STATE, -1);
304 | int deviceId = intent.getIntExtra(DeviceConnFactoryManager.DEVICE_ID, -1);
305 | switch (state) {
306 | case DeviceConnFactoryManager.CONN_STATE_DISCONNECT:
307 | if (id == deviceId) mTvState.setText("未连接");
308 | break;
309 | case DeviceConnFactoryManager.CONN_STATE_CONNECTING:
310 | mTvState.setText("连接中");
311 | break;
312 | case DeviceConnFactoryManager.CONN_STATE_CONNECTED:
313 | mTvState.setText("已连接");
314 | Toast.makeText(MainActivity.this, "已连接", Toast.LENGTH_SHORT).show();
315 | break;
316 | case CONN_STATE_FAILED:
317 | mTvState.setText("未连接");
318 | Toast.makeText(MainActivity.this, "连接失败!重试或重启打印机试试", Toast.LENGTH_SHORT).show();
319 | break;
320 | }
321 | /* Usb连接断开、蓝牙连接断开广播 */
322 | } else if (ACTION_USB_DEVICE_DETACHED.equals(action)) {
323 | mHandler.obtainMessage(CONN_STATE_DISCONN).sendToTarget();
324 | }
325 | }
326 | };
327 | ```
328 | 既然知道状态了就可以根据自己的需求去处理,这里只是简单的提示。
329 |
330 | ok,到这连接的部分就介绍完毕了,下面开始介绍打印的部分。
331 |
332 | # 蓝牙打印
333 |
334 | 打印其实是软件与硬件之间的交互,而这部分恰恰是sdk的职责所在,所以留给我们的任务已经不多了,也就相对简单很多。
335 |
336 | 1.通过线程池添加打印任务
337 | ```
338 | public void printLabel() {
339 | Log.i("TAG", "准备打印");
340 | threadPool = ThreadPool.getInstantiation();
341 | threadPool.addTask(new Runnable() {
342 | @Override
343 | public void run() {
344 | ...
345 | }
346 | });
347 | }
348 | ```
349 | 因为打印是耗时任务,所以不管是为了避免卡顿还是ANR,我们应该用线程池进行优化,为了方便再用单例封装起来。
350 |
351 | 2.打印之前也要先做蓝牙状态的判断,只有做到足够的严谨,才能看起来万无一失。。
352 | ```
353 | public void printLabel() {
354 | Log.i("TAG", "准备打印");
355 | threadPool = ThreadPool.getInstantiation();
356 | threadPool.addTask(new Runnable() {
357 | @Override
358 | public void run() {
359 | //先判断打印机是否连接
360 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] == null ||
361 | !DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].getConnState()) {
362 | mHandler.obtainMessage(CONN_PRINTER).sendToTarget();
363 | return;
364 | }
365 | ...
366 | }
367 | });
368 | }
369 | ```
370 | 这里呢,因为是在子线程,所以通过handler在主线程更新ui或者给个提示
371 |
372 | ```
373 | @SuppressLint("HandlerLeak")
374 | private Handler mHandler = new Handler() {
375 | @Override
376 | public void handleMessage(Message msg) {
377 | switch (msg.what) {
378 | case CONN_STATE_DISCONN:
379 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] != null || !DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].getConnState()) {
380 | DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].closePort(id);
381 | Toast.makeText(MainActivity.this, "成功断开连接", Toast.LENGTH_SHORT).show();
382 | }
383 | break;
384 | case PRINTER_COMMAND_ERROR:
385 | Toast.makeText(MainActivity.this, "请选择正确的打印机指令", Toast.LENGTH_SHORT).show();
386 | break;
387 | case CONN_PRINTER:
388 | Toast.makeText(MainActivity.this, "请先连接打印机", Toast.LENGTH_SHORT).show();
389 | break;
390 | }
391 | }
392 | };
393 | ```
394 |
395 | 3.一切正常,我们开始走打印流程
396 | ```
397 | public void printLabel() {
398 | Log.i("TAG", "准备打印");
399 | threadPool = ThreadPool.getInstantiation();
400 | threadPool.addTask(new Runnable() {
401 | @Override
402 | public void run() {
403 | //先判断打印机是否连接
404 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] == null ||
405 | !DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].getConnState()) {
406 | mHandler.obtainMessage(CONN_PRINTER).sendToTarget();
407 | return;
408 | }
409 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].getCurrentPrinterCommand() == PrinterCommand.TSC) {
410 | Log.i("TAG", "开始打印");
411 | sendLabel();
412 | } else {
413 | mHandler.obtainMessage(PRINTER_COMMAND_ERROR).sendToTarget();
414 | }
415 | }
416 | });
417 | }
418 | ```
419 |
420 | 4.设置打印数据,发送打印数据
421 | ```
422 | private void sendLabel() {
423 | LabelCommand tsc = new LabelCommand();
424 | tsc.addSize(40, 30); // 设置标签尺寸,按照实际尺寸设置
425 | tsc.addGap(1); // 设置标签间隙,按照实际尺寸设置,如果为无间隙纸则设置为0
426 | tsc.addDirection(LabelCommand.DIRECTION.FORWARD, LabelCommand.MIRROR.NORMAL);// 设置打印方向
427 | tsc.addQueryPrinterStatus(LabelCommand.RESPONSE_MODE.ON);//开启带Response的打印,用于连续打印
428 | tsc.addReference(0, 0);// 设置原点坐标
429 | tsc.addTear(EscCommand.ENABLE.ON); // 撕纸模式开启
430 | tsc.addCls();// 清除打印缓冲区
431 |
432 | // 绘制简体中文
433 | tsc.addText(30, 30, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
434 | "这是标题");
435 | tsc.addText(200, 30, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
436 | "序号:" + "1");
437 |
438 | tsc.addText(30, 90, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
439 | "价格:" + "99.00");
440 | tsc.addText(30, 140, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
441 | "数量:" + "99");
442 | tsc.addText(30, 190, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,
443 | "日期:" + "2020-02-02");
444 |
445 | // 绘制图片
446 | // Bitmap b = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
447 | // tsc.addBitmap(20, 50, LabelCommand.BITMAP_MODE.OVERWRITE, b.getWidth(), b);
448 |
449 | //二维码
450 | tsc.addQRCode(200, 90, LabelCommand.EEC.LEVEL_L, 4, LabelCommand.ROTATION.ROTATION_0, "www.baidu.com");
451 |
452 | tsc.addPrint(1, 1); // 打印标签
453 | tsc.addSound(2, 100); // 打印标签后 蜂鸣器响
454 |
455 | /* 发送数据 */
456 | Vector data = tsc.getCommand();
457 | if (DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id] == null) {
458 | Log.i("TAG", "sendLabel: 打印机为空");
459 | return;
460 | }
461 | DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].sendDataImmediately(data);
462 | }
463 | ```
464 | 数据及显示的位置,根据自己的需求做调整。
465 |
466 | ok,以上基本是核心代码了,注释都有,按照上面这个顺序来走的话思路还是很清晰的
467 |
468 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yechaoa/printertest/DeviceConnFactoryManager.java:
--------------------------------------------------------------------------------
1 | package com.yechaoa.printertest;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.hardware.usb.UsbDevice;
9 | import android.os.Bundle;
10 | import android.os.Handler;
11 | import android.os.Message;
12 | import android.util.Log;
13 | import android.widget.Toast;
14 |
15 | import com.printer.io.BluetoothPort;
16 | import com.printer.io.EthernetPort;
17 | import com.printer.io.PortManager;
18 | import com.printer.io.SerialPort;
19 | import com.printer.io.UsbPort;
20 |
21 | import java.io.IOException;
22 | import java.util.Vector;
23 | import java.util.concurrent.ScheduledExecutorService;
24 | import java.util.concurrent.ScheduledThreadPoolExecutor;
25 | import java.util.concurrent.TimeUnit;
26 |
27 | import static android.hardware.usb.UsbManager.ACTION_USB_DEVICE_DETACHED;
28 |
29 | /**
30 | * Time 2017/8/2
31 | */
32 | public class DeviceConnFactoryManager {
33 |
34 | public static final byte FLAG = 0x10;
35 | public static final String ACTION_CONN_STATE = "action_connect_state";
36 | public static final String ACTION_QUERY_PRINTER_STATE = "action_query_printer_state";
37 | public static final String STATE = "state";
38 | public static final String DEVICE_ID = "id";
39 | public static final int CONN_STATE_DISCONNECT = 0x90;
40 | public static final int CONN_STATE_CONNECTING = CONN_STATE_DISCONNECT << 1;
41 | public static final int CONN_STATE_FAILED = CONN_STATE_DISCONNECT << 2;
42 | public static final int CONN_STATE_CONNECTED = CONN_STATE_DISCONNECT << 3;
43 | private static final String TAG = DeviceConnFactoryManager.class.getSimpleName();
44 | /**
45 | * ESC查询打印机实时状态 缺纸状态
46 | */
47 | private static final int ESC_STATE_PAPER_ERR = 0x20;
48 | /**
49 | * ESC指令查询打印机实时状态 打印机开盖状态
50 | */
51 | private static final int ESC_STATE_COVER_OPEN = 0x04;
52 | /**
53 | * ESC指令查询打印机实时状态 打印机报错状态
54 | */
55 | private static final int ESC_STATE_ERR_OCCURS = 0x40;
56 | /**
57 | * ESC低电量
58 | */
59 | private static final int ESC_LOW_POWER = 0x31;
60 | /**
61 | * ESC中电量
62 | */
63 | private static final int ESC_AMID_POWER = 0x32;
64 | /**
65 | * ESC高电量
66 | */
67 | private static final int ESC_HIGH_POWER = 0x33;
68 | /**
69 | * ESC正在充电
70 | */
71 | private static final int ESC_CHARGING = 0x35;
72 | /**
73 | * TSC指令查询打印机实时状态 打印机缺纸状态
74 | */
75 | private static final int TSC_STATE_PAPER_ERR = 0x04;
76 | /**
77 | * TSC指令查询打印机实时状态 打印机开盖状态
78 | */
79 | private static final int TSC_STATE_COVER_OPEN = 0x01;
80 | /**
81 | * TSC指令查询打印机实时状态 打印机出错状态
82 | */
83 | private static final int TSC_STATE_ERR_OCCURS = 0x80;
84 | /**
85 | * CPCL指令查询打印机实时状态 打印机缺纸状态
86 | */
87 | private static final int CPCL_STATE_PAPER_ERR = 0x01;
88 | /**
89 | * CPCL指令查询打印机实时状态 打印机开盖状态
90 | */
91 | private static final int CPCL_STATE_COVER_OPEN = 0x02;
92 | private static final int READ_DATA = 10000;
93 | private static final String READ_DATA_CNT = "read_data_cnt";
94 | private static final String READ_BUFFER_ARRAY = "read_buffer_array";
95 | public static boolean whichFlag = true;
96 | private static DeviceConnFactoryManager[] deviceConnFactoryManagers = new DeviceConnFactoryManager[4];
97 | public PortManager mPort;
98 | public CONN_METHOD connMethod;
99 | public PrinterReader reader;
100 | private String ip;
101 | private int port;
102 | private String macAddress;
103 | private UsbDevice mUsbDevice;
104 | private Context mContext;
105 | private String serialPortPath;
106 | private int baudrate;
107 | private int id;
108 |
109 | BroadcastReceiver usbStateReceiver = new BroadcastReceiver() {
110 | @Override
111 | public void onReceive(Context context, Intent intent) {
112 | String action = intent.getAction();
113 | if (ACTION_USB_DEVICE_DETACHED.equals(action)) {
114 | sendStateBroadcast(CONN_STATE_DISCONNECT);
115 | }
116 | }
117 | };
118 |
119 | private boolean isOpenPort;
120 | /**
121 | * ESC查询打印机实时状态指令
122 | */
123 | private byte[] esc = {0x10, 0x04, 0x02};
124 | /**
125 | * TSC查询打印机状态指令
126 | */
127 | private byte[] tsc = {0x1b, '!', '?'};
128 | private byte[] cpcl = {0x1b, 0x68};
129 | private byte[] sendCommand;
130 | /**
131 | * 判断打印机所使用指令是否是ESC指令
132 | */
133 | private PrinterCommand currentPrinterCommand;
134 |
135 | @SuppressLint("HandlerLeak")
136 | private Handler mHandler = new Handler() {
137 | @Override
138 | public void handleMessage(Message msg) {
139 | switch (msg.what) {
140 | case READ_DATA:
141 | int cnt = msg.getData().getInt(READ_DATA_CNT); //数据长度 >0;
142 | byte[] buffer = msg.getData().getByteArray(READ_BUFFER_ARRAY); //数据
143 | //这里只对查询状态返回值做处理,其它返回值可参考编程手册来解析
144 | if (buffer == null) {
145 | return;
146 | }
147 | int result = judgeResponseType(buffer[0]); //数据右移
148 | String status = "打印机连接正常";
149 | if (sendCommand == esc) {
150 | //设置当前打印机模式为ESC模式
151 | if (currentPrinterCommand == null) {
152 | currentPrinterCommand = PrinterCommand.ESC;
153 | sendStateBroadcast(CONN_STATE_CONNECTED);
154 | } else {//查询打印机状态
155 | if (result == 0) {//打印机状态查询
156 | Intent intent = new Intent(ACTION_QUERY_PRINTER_STATE);
157 | intent.putExtra(DEVICE_ID, id);
158 | App.getContext().sendBroadcast(intent);
159 | } else if (result == 1) {//查询打印机实时状态
160 | if (whichFlag) {
161 | if ((buffer[0] & ESC_STATE_PAPER_ERR) > 0) {
162 | status += "打印机缺纸";
163 | }
164 | if ((buffer[0] & ESC_STATE_COVER_OPEN) > 0) {
165 | status += "打印机开盖";
166 | }
167 | if ((buffer[0] & ESC_STATE_ERR_OCCURS) > 0) {
168 | status += "打印机出错";
169 | }
170 | } else {
171 | if ((buffer[0] == ESC_LOW_POWER)) {
172 | status = "电量:低电量";
173 | } else if ((buffer[0] == ESC_AMID_POWER)) {
174 | status = "电量:中电量";
175 | } else if ((buffer[0] == ESC_HIGH_POWER)) {
176 | status = "电量:高电量";
177 | } else if ((buffer[0] == ESC_CHARGING)) {
178 | status = "正在充电";
179 | }
180 | }
181 | Log.i("TAG", "状态:" + status);
182 | String mode = "打印模式:ESC";
183 | Toast.makeText(App.getContext(), mode + " " + status, Toast.LENGTH_SHORT).show();
184 | }
185 | }
186 | } else if (sendCommand == tsc) {
187 | //设置当前打印机模式为TSC模式
188 | if (currentPrinterCommand == null) {
189 | currentPrinterCommand = PrinterCommand.TSC;
190 | sendStateBroadcast(CONN_STATE_CONNECTED);
191 | } else {
192 | if (cnt == 1) {//查询打印机实时状态
193 | if ((buffer[0] & ESC_STATE_PAPER_ERR) > 0) {
194 | status += "打印机缺纸";
195 | }
196 | if ((buffer[0] & ESC_STATE_COVER_OPEN) > 0) {
197 | status += "打印机开盖";
198 | }
199 | if ((buffer[0] & ESC_STATE_ERR_OCCURS) > 0) {
200 | status += "打印机出错";
201 | }
202 | Log.i("TAG", "状态:" + status);
203 | String mode = "打印模式:TSC";
204 | Toast.makeText(App.getContext(), mode + " " + status, Toast.LENGTH_SHORT).show();
205 | } else {//打印机状态查询
206 | Intent intent = new Intent(ACTION_QUERY_PRINTER_STATE);
207 | intent.putExtra(DEVICE_ID, id);
208 | App.getContext().sendBroadcast(intent);
209 | }
210 | }
211 | } else if (sendCommand == cpcl) {
212 | if (currentPrinterCommand == null) {
213 | currentPrinterCommand = PrinterCommand.CPCL;
214 | sendStateBroadcast(CONN_STATE_CONNECTED);
215 | } else {
216 | if (cnt == 1) {
217 | if ((buffer[0] & ESC_STATE_PAPER_ERR) > 0) {
218 | status += "打印机缺纸";
219 | }
220 | if ((buffer[0] & ESC_STATE_COVER_OPEN) > 0) {
221 | status += "打印机开盖";
222 | }
223 | Log.i("TAG", "状态:" + status);
224 | String mode = "打印模式:CPCL";
225 | Toast.makeText(App.getContext(), mode + " " + status, Toast.LENGTH_SHORT).show();
226 | } else {//打印机状态查询
227 | Intent intent = new Intent(ACTION_QUERY_PRINTER_STATE);
228 | intent.putExtra(DEVICE_ID, id);
229 | App.getContext().sendBroadcast(intent);
230 | }
231 | }
232 | }
233 | break;
234 | default:
235 | break;
236 | }
237 | }
238 | };
239 |
240 | private DeviceConnFactoryManager(Build build) {
241 | this.connMethod = build.connMethod;
242 | this.macAddress = build.macAddress;
243 | this.port = build.port;
244 | this.ip = build.ip;
245 | this.mUsbDevice = build.usbDevice;
246 | this.mContext = build.context;
247 | this.serialPortPath = build.serialPortPath;
248 | this.baudrate = build.baudrate;
249 | this.id = build.id;
250 | deviceConnFactoryManagers[id] = this;
251 | }
252 |
253 | public static DeviceConnFactoryManager[] getDeviceConnFactoryManagers() {
254 | return deviceConnFactoryManagers;
255 | }
256 |
257 | public static void closeAllPort() {
258 | for (DeviceConnFactoryManager deviceConnFactoryManager : deviceConnFactoryManagers) {
259 | if (deviceConnFactoryManager != null) {
260 | Log.e(TAG, "cloaseAllPort() id -> " + deviceConnFactoryManager.id);
261 | deviceConnFactoryManager.closePort(deviceConnFactoryManager.id);
262 | deviceConnFactoryManagers[deviceConnFactoryManager.id] = null;
263 | }
264 | }
265 | }
266 |
267 | /**
268 | * 关闭端口(断开连接)
269 | */
270 | public void closePort(int id) {
271 | if (this.mPort != null) {
272 | System.out.println("id -> " + id);
273 | reader.cancel();
274 | boolean b = this.mPort.closePort();
275 | if (b) {
276 | this.mPort = null;
277 | isOpenPort = false;
278 | currentPrinterCommand = null;
279 | }
280 | }
281 | sendStateBroadcast(CONN_STATE_DISCONNECT);
282 | }
283 |
284 | /**
285 | * 打开端口(开始连接)
286 | */
287 | public void openPort() {
288 | deviceConnFactoryManagers[id].isOpenPort = false;
289 | sendStateBroadcast(CONN_STATE_CONNECTING);
290 | switch (deviceConnFactoryManagers[id].connMethod) {
291 | case BLUETOOTH:
292 | System.out.println("id -> " + id);
293 | mPort = new BluetoothPort(macAddress);
294 | isOpenPort = deviceConnFactoryManagers[id].mPort.openPort();
295 | break;
296 | case USB:
297 | mPort = new UsbPort(mContext, mUsbDevice);
298 | isOpenPort = mPort.openPort();
299 | if (isOpenPort) {
300 | IntentFilter filter = new IntentFilter(ACTION_USB_DEVICE_DETACHED);
301 | mContext.registerReceiver(usbStateReceiver, filter);
302 | }
303 | break;
304 | case WIFI:
305 | mPort = new EthernetPort(ip, port);
306 | isOpenPort = mPort.openPort();
307 | break;
308 | case SERIAL_PORT:
309 | mPort = new SerialPort(serialPortPath, baudrate, 0);
310 | isOpenPort = mPort.openPort();
311 | break;
312 | default:
313 | break;
314 | }
315 |
316 | //端口打开成功后,检查连接打印机所使用的打印机指令ESC、TSC
317 | if (isOpenPort) {
318 | queryCommand();
319 | } else {
320 | if (this.mPort != null) {
321 | this.mPort = null;
322 | }
323 | sendStateBroadcast(CONN_STATE_FAILED);
324 | }
325 | }
326 |
327 | private void sendStateBroadcast(int state) {
328 | Intent intent = new Intent(ACTION_CONN_STATE);
329 | intent.putExtra(STATE, state);
330 | intent.putExtra(DEVICE_ID, id);
331 | App.getContext().sendBroadcast(intent);
332 | }
333 |
334 | /**
335 | * 查询当前连接打印机所使用打印机指令(ESC(EscCommand.java)、TSC(LabelCommand.java))
336 | */
337 | private void queryCommand() {
338 | //开启读取打印机返回数据线程
339 | reader = new PrinterReader();
340 | reader.start(); //读取数据线程
341 | //查询打印机所使用指令
342 | queryPrinterCommand(); //
343 | }
344 |
345 | /**
346 | * 查询打印机当前使用的指令(TSC、ESC)
347 | */
348 | private void queryPrinterCommand() {
349 | //线程池添加任务
350 | ThreadPool.getInstantiation().addTask(new Runnable() {
351 | @Override
352 | public void run() {
353 | //发送ESC查询打印机状态指令
354 | sendCommand = esc;
355 | Vector data = new Vector<>(esc.length);
356 | for (int i = 0; i < esc.length; i++) {
357 | data.add(esc[i]);
358 | }
359 | sendDataImmediately(data); //发送esc数据
360 | //开启计时器,隔2000毫秒没有没返回值时发送TSC查询打印机状态指令
361 | final ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder("Timer");
362 | final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1, threadFactoryBuilder);
363 | scheduledExecutorService.schedule(threadFactoryBuilder.newThread(new Runnable() {
364 | @Override
365 | public void run() {
366 | if (currentPrinterCommand == null || currentPrinterCommand != PrinterCommand.ESC) {
367 | Log.e(TAG, Thread.currentThread().getName());
368 | //发送TSC查询打印机状态指令
369 | sendCommand = tsc;
370 | Vector data = new Vector<>(tsc.length);
371 | for (int i = 0; i < tsc.length; i++) {
372 | data.add(tsc[i]);
373 | }
374 | sendDataImmediately(data);
375 | //开启计时器,隔2000毫秒没有没返回值时发送CPCL查询打印机状态指令
376 | scheduledExecutorService.schedule(threadFactoryBuilder.newThread(new Runnable() {
377 | @Override
378 | public void run() {
379 | if (currentPrinterCommand == null || (currentPrinterCommand != PrinterCommand.ESC && currentPrinterCommand != PrinterCommand.TSC)) {
380 | Log.e(TAG, Thread.currentThread().getName());
381 | //发送CPCL查询打印机状态指令
382 | sendCommand = cpcl;
383 | Vector data = new Vector(cpcl.length);
384 | for (int i = 0; i < cpcl.length; i++) {
385 | data.add(cpcl[i]);
386 | }
387 | sendDataImmediately(data);
388 | //开启计时器,隔2000毫秒打印机没有响应者停止读取打印机数据线程并且关闭端口
389 | scheduledExecutorService.schedule(threadFactoryBuilder.newThread(new Runnable() {
390 | @Override
391 | public void run() {
392 | if (currentPrinterCommand == null) {
393 | if (reader != null) {
394 | reader.cancel();
395 | mPort.closePort();
396 | isOpenPort = false;
397 | mPort = null;
398 | sendStateBroadcast(CONN_STATE_FAILED);
399 | }
400 | }
401 | }
402 | }), 2000, TimeUnit.MILLISECONDS);
403 | }
404 | }
405 | }), 2000, TimeUnit.MILLISECONDS);
406 | }
407 | }
408 | }), 2000, TimeUnit.MILLISECONDS);
409 | }
410 | });
411 | }
412 |
413 | public void sendDataImmediately(final Vector data) {
414 | if (this.mPort == null) {
415 | return;
416 | }
417 | try {
418 | this.mPort.writeDataImmediately(data, 0, data.size());
419 | } catch (Exception e) {
420 | e.printStackTrace();
421 | }
422 | }
423 |
424 | /**
425 | * 获取端口连接方式
426 | *
427 | * @return
428 | */
429 | public CONN_METHOD getConnMethod() {
430 | return connMethod;
431 | }
432 |
433 | /**
434 | * 获取端口打开状态(true 打开,false 未打开)
435 | *
436 | * @return
437 | */
438 | public boolean getConnState() {
439 | return isOpenPort;
440 | }
441 |
442 | /**
443 | * 获取连接蓝牙的物理地址
444 | *
445 | * @return
446 | */
447 | public String getMacAddress() {
448 | return macAddress;
449 | }
450 |
451 | /**
452 | * 获取连接网口端口号
453 | *
454 | * @return
455 | */
456 | public int getPort() {
457 | return port;
458 | }
459 |
460 | /**
461 | * 获取连接网口的IP
462 | *
463 | * @return
464 | */
465 | public String getIp() {
466 | return ip;
467 | }
468 |
469 | /**
470 | * 获取连接的USB设备信息
471 | *
472 | * @return
473 | */
474 | public UsbDevice usbDevice() {
475 | return mUsbDevice;
476 | }
477 |
478 | /**
479 | * 获取串口号
480 | *
481 | * @return
482 | */
483 | public String getSerialPortPath() {
484 | return serialPortPath;
485 | }
486 |
487 | /**
488 | * 获取波特率
489 | *
490 | * @return
491 | */
492 | public int getBaudrate() {
493 | return baudrate;
494 | }
495 |
496 | /**
497 | * 获取当前打印机指令
498 | *
499 | * @return PrinterCommand
500 | */
501 | public PrinterCommand getCurrentPrinterCommand() {
502 | return deviceConnFactoryManagers[id].currentPrinterCommand;
503 | }
504 |
505 | public int readDataImmediately(byte[] buffer) throws IOException {
506 | return this.mPort.readData(buffer);
507 | }
508 |
509 | /**
510 | * 判断是实时状态(10 04 02)还是查询状态(1D 72 01)
511 | */
512 | private int judgeResponseType(byte r) {
513 | return (byte) ((r & FLAG) >> 4);
514 | }
515 |
516 | /**
517 | * 几种连接方式
518 | */
519 | public enum CONN_METHOD {
520 | //蓝牙连接
521 | BLUETOOTH("BLUETOOTH"),
522 | //USB连接
523 | USB("USB"),
524 | //wifi连接
525 | WIFI("WIFI"),
526 | //串口连接
527 | SERIAL_PORT("SERIAL_PORT");
528 |
529 | private String name;
530 |
531 | private CONN_METHOD(String name) {
532 | this.name = name;
533 | }
534 |
535 | @Override
536 | public String toString() {
537 | return this.name;
538 | }
539 | }
540 |
541 | /**
542 | * 使用构建者模式传入数据,清晰 解耦
543 | */
544 | public static final class Build {
545 | private String ip;
546 | private String macAddress;
547 | private UsbDevice usbDevice;
548 | private int port;
549 | private CONN_METHOD connMethod;
550 | private Context context;
551 | private String serialPortPath;
552 | private int baudrate;
553 | private int id;//设置标识符
554 |
555 | public DeviceConnFactoryManager.Build setIp(String ip) {
556 | this.ip = ip;
557 | return this;
558 | }
559 |
560 | public DeviceConnFactoryManager.Build setMacAddress(String macAddress) {
561 | this.macAddress = macAddress;
562 | return this;
563 | }
564 |
565 | public DeviceConnFactoryManager.Build setUsbDevice(UsbDevice usbDevice) {
566 | this.usbDevice = usbDevice;
567 | return this;
568 | }
569 |
570 | public DeviceConnFactoryManager.Build setPort(int port) {
571 | this.port = port;
572 | return this;
573 | }
574 |
575 | public DeviceConnFactoryManager.Build setConnMethod(CONN_METHOD connMethod) {
576 | this.connMethod = connMethod;
577 | return this;
578 | }
579 |
580 | public DeviceConnFactoryManager.Build setContext(Context context) {
581 | this.context = context;
582 | return this;
583 | }
584 |
585 | public DeviceConnFactoryManager.Build setId(int id) {
586 | this.id = id;
587 | return this;
588 | }
589 |
590 | public DeviceConnFactoryManager.Build setSerialPort(String serialPortPath) {
591 | this.serialPortPath = serialPortPath;
592 | return this;
593 | }
594 |
595 | public DeviceConnFactoryManager.Build setBaudrate(int baudrate) {
596 | this.baudrate = baudrate;
597 | return this;
598 | }
599 |
600 | public DeviceConnFactoryManager build() {
601 | return new DeviceConnFactoryManager(this);
602 | }
603 | }
604 |
605 | /**
606 | * 开线程读取数据
607 | */
608 | public class PrinterReader extends Thread {
609 | private boolean isRun = false;
610 |
611 | private byte[] buffer = new byte[100];
612 |
613 | public PrinterReader() {
614 | isRun = true;
615 | }
616 |
617 | @Override
618 | public void run() {
619 | try {
620 | while (isRun) {
621 | //读取打印机返回信息
622 | int len = readDataImmediately(buffer);
623 | if (len > 0) {
624 | Message message = Message.obtain();
625 | message.what = READ_DATA;
626 | Bundle bundle = new Bundle();
627 | bundle.putInt(READ_DATA_CNT, len); //数据长度
628 | bundle.putByteArray(READ_BUFFER_ARRAY, buffer); //数据
629 | message.setData(bundle);
630 | mHandler.sendMessage(message);
631 | }
632 | }
633 | } catch (Exception e) {
634 | if (deviceConnFactoryManagers[id] != null) {
635 | closePort(id);
636 | }
637 | }
638 | }
639 |
640 | public void cancel() {
641 | isRun = false;
642 | }
643 | }
644 |
645 | }
--------------------------------------------------------------------------------