├── example ├── .watchmanconfig ├── .gitattributes ├── app.json ├── .eslintrc.js ├── babel.config.js ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ └── styles.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 │ │ │ │ ├── java │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ │ └── MainApplication.java │ │ │ │ └── AndroidManifest.xml │ │ │ └── debug │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ReactNativeFlipper.java │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ ├── build_defs.bzl │ │ ├── _BUCK │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── build.gradle │ ├── gradle.properties │ ├── gradlew.bat │ └── gradlew ├── ios │ ├── example │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── Info.plist │ │ ├── AppDelegate.m │ │ └── LaunchScreen.storyboard │ ├── example.xcworkspace │ │ └── contents.xcworkspacedata │ ├── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m │ ├── example-tvOSTests │ │ └── Info.plist │ ├── Podfile │ ├── example-tvOS │ │ └── Info.plist │ ├── example.xcodeproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── example.xcscheme │ │ │ └── example-tvOS.xcscheme │ └── Podfile.lock ├── .buckconfig ├── .prettierrc.js ├── index.js ├── __tests__ │ └── App-test.js ├── metro.config.js ├── package.json ├── .gitignore ├── .flowconfig └── App.js ├── .npmignore ├── android ├── src │ └── main │ │ ├── java │ │ ├── com │ │ │ └── sunmi │ │ │ │ ├── trans │ │ │ │ ├── TransBean.aidl │ │ │ │ └── TransBean.java │ │ │ │ └── v2 │ │ │ │ └── printer │ │ │ │ ├── ThreadPoolManager.java │ │ │ │ ├── SunmiV2PrinterPackage.java │ │ │ │ ├── PrinterReceiver.java │ │ │ │ ├── BluetoothUtil.java │ │ │ │ ├── MemInfo.java │ │ │ │ ├── ESCUtil.java │ │ │ │ ├── BitmapUtils.java │ │ │ │ └── BytesUtil.java │ │ └── woyou │ │ │ └── aidlservice │ │ │ └── jiuiv5 │ │ │ ├── ITax.aidl │ │ │ ├── ICallback.aidl │ │ │ └── IWoyouService.aidl │ │ └── AndroidManifest.xml └── build.gradle ├── index.js ├── .gitignore ├── package.json ├── LICENSE └── README.md /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "displayName": "example" 4 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea 3 | example 4 | android/build 5 | -------------------------------------------------------------------------------- /android/src/main/java/com/sunmi/trans/TransBean.aidl: -------------------------------------------------------------------------------- 1 | package com.sunmi.trans; 2 | 3 | parcelable TransBean; -------------------------------------------------------------------------------- /example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native-community', 4 | }; 5 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | example 3 | 4 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { NativeModules } from 'react-native'; 2 | 3 | const SunmiV2Printer = NativeModules.SunmiV2Printer; 4 | 5 | module.exports = SunmiV2Printer; -------------------------------------------------------------------------------- /example/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suraneti/react-native-sunmi-v2-printer/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/src/main/java/woyou/aidlservice/jiuiv5/ITax.aidl: -------------------------------------------------------------------------------- 1 | package woyou.aidlservice.jiuiv5; 2 | 3 | /** 4 | * 打印服务执行结果的回调 5 | */ 6 | interface ITax { 7 | 8 | oneway void onDataResult(in byte [] data); 9 | 10 | } -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /example/ios/example/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import App from '../App'; 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer'; 11 | 12 | it('renders correctly', () => { 13 | renderer.create(); 14 | }); 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | *.log 9 | out 10 | gen 11 | example/node_modules 12 | example/android/build 13 | example/android/app/build 14 | example/android/.gradle 15 | example/ios/Pods 16 | android/build -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | include ':react-native-sunmi-v2-printer' 3 | project(':react-native-sunmi-v2-printer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sunmi-v2-printer/android') 4 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 5 | include ':app' 6 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /example/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /android/src/main/java/com/sunmi/v2/printer/ThreadPoolManager.java: -------------------------------------------------------------------------------- 1 | package com.sunmi.v2.printer; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | /** 7 | * 线程管理类 8 | * 9 | * @author lenovo 10 | * 11 | */ 12 | public class ThreadPoolManager { 13 | private ExecutorService service; 14 | 15 | private ThreadPoolManager() { 16 | int num = Runtime.getRuntime().availableProcessors() * 20; 17 | service = Executors.newFixedThreadPool(num); 18 | } 19 | 20 | private static final ThreadPoolManager manager = new ThreadPoolManager(); 21 | 22 | public static ThreadPoolManager getInstance() { 23 | return manager; 24 | } 25 | 26 | public void executeTask(Runnable runnable) { 27 | service.execute(runnable); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /android/src/main/java/woyou/aidlservice/jiuiv5/ICallback.aidl: -------------------------------------------------------------------------------- 1 | package woyou.aidlservice.jiuiv5; 2 | 3 | /** 4 | * 打印服务执行结果的回调 5 | */ 6 | interface ICallback { 7 | 8 | /** 9 | * 返回接口执行的结果 10 | * 备注:此回调只表明接口执行是否成功但不表明打印机的工作结果,如需要获取打印机结果请用事务模式 11 | * @param isSuccess: true执行成功,false 执行失败 12 | */ 13 | oneway void onRunResult(boolean isSuccess); 14 | 15 | /** 16 | * 返回接口执行的结果(字符串数据) 17 | * @param result: 结果,打印机上电以来打印长度等(单位mm) 18 | */ 19 | oneway void onReturnString(String result); 20 | 21 | /** 22 | * 返回接口执行失败时发生异常情况的具体原因 23 | * code: 异常代码 24 | * msg: 异常描述 25 | */ 26 | oneway void onRaiseException(int code, String msg); 27 | 28 | /** 29 | * 返回打印机结果 30 | * code: 异常代码 0 成功 1 失败 31 | * msg: 异常描述 32 | */ 33 | oneway void onPrintResult(int code, String msg); 34 | 35 | } -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "test": "jest", 10 | "lint": "eslint ." 11 | }, 12 | "dependencies": { 13 | "react": "16.13.1", 14 | "react-native": "0.63.3", 15 | "react-native-sunmi-v2-printer": "../" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "^7.8.4", 19 | "@babel/runtime": "^7.8.4", 20 | "@react-native-community/eslint-config": "^1.1.0", 21 | "babel-jest": "^25.1.0", 22 | "eslint": "^6.5.1", 23 | "jest": "^25.1.0", 24 | "metro-react-native-babel-preset": "^0.59.0", 25 | "react-test-renderer": "16.13.1" 26 | }, 27 | "jest": { 28 | "preset": "react-native" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /example/ios/exampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:2.3.0' 8 | } 9 | } 10 | 11 | apply plugin: 'com.android.library' 12 | 13 | android { 14 | buildToolsVersion "29.0.3" 15 | 16 | defaultConfig { 17 | compileSdkVersion 33 18 | minSdkVersion 16 19 | targetSdkVersion 33 20 | versionCode 2 21 | versionName "1.0.1" 22 | } 23 | lintOptions { 24 | abortOnError false 25 | } 26 | sourceSets { 27 | main { 28 | aidl.srcDirs = ['src/main/java'] 29 | } 30 | } 31 | } 32 | 33 | repositories { 34 | mavenCentral() 35 | } 36 | 37 | dependencies { 38 | implementation 'com.facebook.react:react-native:+' 39 | implementation "com.google.zxing:core:3.4.1" 40 | } 41 | -------------------------------------------------------------------------------- /example/ios/example-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '10.0' 5 | 6 | target 'example' do 7 | config = use_native_modules! 8 | 9 | use_react_native!(:path => config["reactNativePath"]) 10 | 11 | target 'exampleTests' do 12 | inherit! :complete 13 | # Pods for testing 14 | end 15 | 16 | # Enables Flipper. 17 | # 18 | # Note that if you have use_frameworks! enabled, Flipper will not work and 19 | # you should disable these next few lines. 20 | use_flipper! 21 | post_install do |installer| 22 | flipper_post_install(installer) 23 | end 24 | end 25 | 26 | target 'example-tvOS' do 27 | # Pods for example-tvOS 28 | 29 | target 'example-tvOSTests' do 30 | inherit! :search_paths 31 | # Pods for testing 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /android/src/main/java/com/sunmi/v2/printer/SunmiV2PrinterPackage.java: -------------------------------------------------------------------------------- 1 | package com.sunmi.v2.printer; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.bridge.NativeModule; 5 | import com.facebook.react.bridge.ReactApplicationContext; 6 | import com.facebook.react.uimanager.ViewManager; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class SunmiV2PrinterPackage implements ReactPackage { 13 | @Override 14 | public List createViewManagers(ReactApplicationContext reactContext) { 15 | return Collections.emptyList(); 16 | } 17 | 18 | @Override 19 | public List createNativeModules( 20 | ReactApplicationContext reactContext) { 21 | List modules = new ArrayList<>(); 22 | 23 | modules.add(new SunmiV2PrinterModule(reactContext)); 24 | 25 | return modules; 26 | } 27 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-sunmi-v2-printer", 3 | "version": "1.0.3", 4 | "description": "Sunmi V2 Printer for React-Native", 5 | "author": "Suraneti Rodsuwan ", 6 | "contributors": [], 7 | "private": false, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/suraneti/react-native-sunmi-v2-printer.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/suraneti/react-native-sunmi-v2-printer/issues" 14 | }, 15 | "main": "index.js", 16 | "scripts": { 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "keywords": [ 20 | "react-native", 21 | "sunmi", 22 | "printer" 23 | ], 24 | "license": "MIT", 25 | "peerDependencies": { 26 | "react": ">=16", 27 | "react-native": ">=0.63.3" 28 | }, 29 | "homepage": "https://github.com/suraneti/react-native-sunmi-v2-printer#readme", 30 | "directories": { 31 | "example": "example" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /android/src/main/java/com/sunmi/v2/printer/PrinterReceiver.java: -------------------------------------------------------------------------------- 1 | package com.sunmi.v2.printer; 2 | 3 | import android.content.BroadcastReceiver; 4 | import com.facebook.react.bridge.ReactApplicationContext; 5 | import com.facebook.react.bridge.ReactContext; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import com.facebook.react.modules.core.DeviceEventManagerModule; 9 | import android.util.Log; 10 | 11 | public class PrinterReceiver extends BroadcastReceiver { 12 | public PrinterReceiver() { 13 | } 14 | 15 | @Override 16 | public void onReceive(Context context, Intent data) { 17 | String action = data.getAction(); 18 | String type = "PrinterStatus"; 19 | Log.d("PrinterReceiver", action); 20 | 21 | if (SunmiV2PrinterModule.reactApplicationContext != null) { 22 | SunmiV2PrinterModule.reactApplicationContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 23 | .emit(type, action); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 januslo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "29.0.2" 6 | minSdkVersion = 16 7 | compileSdkVersion = 29 8 | targetSdkVersion = 29 9 | } 10 | repositories { 11 | google() 12 | jcenter() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:3.5.3") 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | mavenLocal() 24 | maven { 25 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 26 | url("$rootDir/../node_modules/react-native/android") 27 | } 28 | maven { 29 | // Android JSC is installed from npm 30 | url("$rootDir/../node_modules/jsc-android/dist") 31 | } 32 | 33 | google() 34 | jcenter() 35 | maven { url 'https://www.jitpack.io' } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.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 | -------------------------------------------------------------------------------- /example/android/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 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.54.0 29 | -------------------------------------------------------------------------------- /example/android/app/_BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.example", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.example", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /android/src/main/java/com/sunmi/v2/printer/BluetoothUtil.java: -------------------------------------------------------------------------------- 1 | package com.sunmi.v2.printer; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.util.Set; 6 | import java.util.UUID; 7 | 8 | 9 | import android.bluetooth.BluetoothAdapter; 10 | import android.bluetooth.BluetoothDevice; 11 | import android.bluetooth.BluetoothSocket; 12 | 13 | public class BluetoothUtil { 14 | 15 | private static final UUID PRINTER_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); 16 | 17 | private static final String Innerprinter_Address = "00:11:22:33:44:55"; 18 | 19 | public static BluetoothAdapter getBTAdapter() { 20 | return BluetoothAdapter.getDefaultAdapter(); 21 | } 22 | 23 | public static BluetoothDevice getDevice(BluetoothAdapter bluetoothAdapter) { 24 | BluetoothDevice innerprinter_device = null; 25 | Set devices = bluetoothAdapter.getBondedDevices(); 26 | for (BluetoothDevice device : devices) { 27 | if (device.getAddress().equals(Innerprinter_Address)) { 28 | innerprinter_device = device; 29 | break; 30 | } 31 | } 32 | return innerprinter_device; 33 | } 34 | 35 | public static BluetoothSocket getSocket(BluetoothDevice device) throws IOException { 36 | BluetoothSocket socket = device.createRfcommSocketToServiceRecord(PRINTER_UUID); 37 | socket.connect(); 38 | return socket; 39 | } 40 | 41 | public static void sendData(byte[] bytes, BluetoothSocket socket) throws IOException { 42 | OutputStream out = socket.getOutputStream(); 43 | out.write(bytes, 0, bytes.length); 44 | out.flush(); 45 | out.close(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /example/ios/example-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSExceptionDomains 28 | 29 | localhost 30 | 31 | NSExceptionAllowsInsecureHTTPLoads 32 | 33 | 34 | 35 | 36 | NSLocationWhenInUseUsageDescription 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSLocationWhenInUseUsageDescription 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UIViewControllerBasedStatusBarAppearance 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /example/ios/exampleTests/exampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface exampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation exampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 38 | if (level >= RCTLogLevelError) { 39 | redboxError = message; 40 | } 41 | }); 42 | #endif 43 | 44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | 48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 50 | return YES; 51 | } 52 | return NO; 53 | }]; 54 | } 55 | 56 | #ifdef DEBUG 57 | RCTSetLogFunction(RCTDefaultLogFunction); 58 | #endif 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /android/src/main/java/com/sunmi/v2/printer/MemInfo.java: -------------------------------------------------------------------------------- 1 | package com.test.printertestdemo; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | 8 | import android.app.ActivityManager; 9 | import android.content.Context; 10 | 11 | public class MemInfo { 12 | 13 | 14 | // 获得可用的内存 15 | public static long getmem_UNUSED(Context mContext) { 16 | long MEM_UNUSED; 17 | // 得到ActivityManager 18 | ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); 19 | // 创建ActivityManager.MemoryInfo对象 20 | ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); 21 | am.getMemoryInfo(mi); 22 | // 取得剩余的内存空间 23 | MEM_UNUSED = mi.availMem/1048576; 24 | return MEM_UNUSED; 25 | } 26 | 27 | // 获得总内存 28 | public static long getmem_TOLAL() { 29 | long mTotal; 30 | // /proc/meminfo读出的内核信息进行解释 31 | String path = "/proc/meminfo"; 32 | String content = null; 33 | BufferedReader br = null; 34 | try { 35 | br = new BufferedReader(new FileReader(path), 8); 36 | String line; 37 | if ((line = br.readLine()) != null) { 38 | content = line; 39 | } 40 | } catch (FileNotFoundException e) { 41 | e.printStackTrace(); 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | } finally { 45 | if (br != null) { 46 | try { 47 | br.close(); 48 | } catch (IOException e) { 49 | e.printStackTrace(); 50 | } 51 | } 52 | } 53 | // beginIndex 54 | int begin = content.indexOf(':'); 55 | // endIndex 56 | int end = content.indexOf('k'); 57 | // 截取字符串信息 58 | content = content.substring(begin + 1, end).trim(); 59 | mTotal = Integer.parseInt(content); 60 | return mTotal; 61 | } 62 | } -------------------------------------------------------------------------------- /example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; These should not be required directly 12 | ; require from fbjs/lib instead: require('fbjs/lib/warning') 13 | node_modules/warning/.* 14 | 15 | ; Flow doesn't support platforms 16 | .*/Libraries/Utilities/LoadingView.js 17 | 18 | [untyped] 19 | .*/node_modules/@react-native-community/cli/.*/.* 20 | 21 | [include] 22 | 23 | [libs] 24 | node_modules/react-native/interface.js 25 | node_modules/react-native/flow/ 26 | 27 | [options] 28 | emoji=true 29 | 30 | esproposal.optional_chaining=enable 31 | esproposal.nullish_coalescing=enable 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 40 | module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 41 | 42 | suppress_type=$FlowIssue 43 | suppress_type=$FlowFixMe 44 | suppress_type=$FlowFixMeProps 45 | suppress_type=$FlowFixMeState 46 | 47 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 50 | 51 | [lints] 52 | sketchy-null-number=warn 53 | sketchy-null-mixed=warn 54 | sketchy-number=warn 55 | untyped-type-import=warn 56 | nonstrict-import=warn 57 | deprecated-type=warn 58 | unsafe-getters-setters=warn 59 | unnecessary-invariant=warn 60 | signature-verification-failure=warn 61 | deprecated-utility=error 62 | 63 | [strict] 64 | deprecated-type 65 | nonstrict-import 66 | sketchy-null 67 | unclear-type 68 | unsafe-getters-setters 69 | untyped-import 70 | untyped-type-import 71 | 72 | [version] 73 | ^0.122.0 74 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #ifdef FB_SONARKIT_ENABLED 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | static void InitializeFlipper(UIApplication *application) { 16 | FlipperClient *client = [FlipperClient sharedClient]; 17 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 18 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 19 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 20 | [client addPlugin:[FlipperKitReactPlugin new]]; 21 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 22 | [client start]; 23 | } 24 | #endif 25 | 26 | @implementation AppDelegate 27 | 28 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 29 | { 30 | #ifdef FB_SONARKIT_ENABLED 31 | InitializeFlipper(application); 32 | #endif 33 | 34 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 35 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 36 | moduleName:@"example" 37 | initialProperties:nil]; 38 | 39 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 40 | 41 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 42 | UIViewController *rootViewController = [UIViewController new]; 43 | rootViewController.view = rootView; 44 | self.window.rootViewController = rootViewController; 45 | [self.window makeKeyAndVisible]; 46 | return YES; 47 | } 48 | 49 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 50 | { 51 | #if DEBUG 52 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 53 | #else 54 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 55 | #endif 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /android/src/main/java/com/sunmi/trans/TransBean.java: -------------------------------------------------------------------------------- 1 | package com.sunmi.trans; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class TransBean implements Parcelable { 7 | 8 | private byte type = 0; 9 | private String text = ""; 10 | private byte[] data = null; 11 | private int datalength = 0; 12 | 13 | public TransBean(){ 14 | type = 0; 15 | data = null; 16 | text = ""; 17 | datalength = 0; 18 | }; 19 | 20 | public byte getType() { 21 | return type; 22 | } 23 | 24 | public void setType(byte type) { 25 | this.type = type; 26 | } 27 | 28 | public String getText() { 29 | return text; 30 | } 31 | 32 | public void setText(String text) { 33 | this.text = text; 34 | } 35 | 36 | public byte[] getData() { 37 | return data; 38 | } 39 | 40 | public void setData(byte[] data) { 41 | if(data != null){ 42 | datalength = data.length; 43 | this.data = new byte[datalength]; 44 | System.arraycopy(data, 0, this.data, 0, datalength); 45 | } 46 | } 47 | 48 | public TransBean(Parcel source){ 49 | this.type = source.readByte(); 50 | this.datalength = source.readInt(); 51 | this.text = source.readString(); 52 | if(datalength > 0){ 53 | this.data = new byte[datalength]; 54 | source.readByteArray(data); 55 | } 56 | } 57 | 58 | public TransBean(byte type, String text, byte[] data){ 59 | this.type = type; 60 | this.text = text; 61 | if(data != null){ 62 | this.datalength = data.length; 63 | this.data = new byte[datalength]; 64 | System.arraycopy(data, 0, this.data, 0, datalength); 65 | } 66 | } 67 | 68 | @Override 69 | public int describeContents() { 70 | return 0; 71 | } 72 | 73 | @Override 74 | public void writeToParcel(Parcel dest, int flags) { 75 | dest.writeByte(type); 76 | dest.writeInt(datalength); 77 | dest.writeString(text); 78 | if(data != null){ 79 | dest.writeByteArray(data); 80 | } 81 | } 82 | 83 | public static Creator CREATOR = new Creator(){ 84 | 85 | @Override 86 | public TransBean createFromParcel(Parcel source) { 87 | return new TransBean(source); 88 | } 89 | 90 | @Override 91 | public TransBean[] newArray(int size) { 92 | return new TransBean[size]; 93 | } 94 | }; 95 | 96 | } -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.facebook.react.PackageList; 6 | import com.facebook.react.ReactApplication; 7 | import com.sunmi.v2.printer.SunmiV2PrinterPackage; 8 | import com.facebook.react.ReactInstanceManager; 9 | import com.facebook.react.ReactNativeHost; 10 | import com.facebook.react.ReactPackage; 11 | import com.facebook.soloader.SoLoader; 12 | import java.lang.reflect.InvocationTargetException; 13 | import java.util.List; 14 | 15 | public class MainApplication extends Application implements ReactApplication { 16 | 17 | private final ReactNativeHost mReactNativeHost = 18 | new ReactNativeHost(this) { 19 | @Override 20 | public boolean getUseDeveloperSupport() { 21 | return BuildConfig.DEBUG; 22 | } 23 | 24 | @Override 25 | protected List getPackages() { 26 | @SuppressWarnings("UnnecessaryLocalVariable") 27 | List packages = new PackageList(this).getPackages(); 28 | // Packages that cannot be autolinked yet can be added manually here, for example: 29 | // packages.add(new MyReactNativePackage()); 30 | return packages; 31 | } 32 | 33 | @Override 34 | protected String getJSMainModuleName() { 35 | return "index"; 36 | } 37 | }; 38 | 39 | @Override 40 | public ReactNativeHost getReactNativeHost() { 41 | return mReactNativeHost; 42 | } 43 | 44 | @Override 45 | public void onCreate() { 46 | super.onCreate(); 47 | SoLoader.init(this, /* native exopackage */ false); 48 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 49 | } 50 | 51 | /** 52 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like 53 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 54 | * 55 | * @param context 56 | * @param reactInstanceManager 57 | */ 58 | private static void initializeFlipper( 59 | Context context, ReactInstanceManager reactInstanceManager) { 60 | if (BuildConfig.DEBUG) { 61 | try { 62 | /* 63 | We use reflection here to pick up the class that initializes Flipper, 64 | since Flipper library is not available in release mode 65 | */ 66 | Class aClass = Class.forName("com.example.ReactNativeFlipper"); 67 | aClass 68 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 69 | .invoke(null, context, reactInstanceManager); 70 | } catch (ClassNotFoundException e) { 71 | e.printStackTrace(); 72 | } catch (NoSuchMethodException e) { 73 | e.printStackTrace(); 74 | } catch (IllegalAccessException e) { 75 | e.printStackTrace(); 76 | } catch (InvocationTargetException e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # react-native-sunmi-v2-printer 3 | 4 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/suraneti/react-native-sunmi-inner-printer/master/LICENSE) [![npm version](https://badge.fury.io/js/react-native-sunmi-v2-printer.svg)](https://www.npmjs.com/package/react-native-sunmi-v2-printer) 5 | 6 | ## SUNMI V2 Printer for React-Native 7 | 8 | ### Caution: this is not the official project and forked form https://github.com/januslo/react-native-sunmi-inner-printer 9 | 10 | Offical Demos refer: https://github.com/shangmisunmi/SunmiPrinterDemo 11 | 12 | ## Installation 13 | 14 | **Step 1.** 15 | 16 | Install via NPM 17 | 18 | ```bash 19 | npm install react-native-sunmi-v2-printer --save 20 | ``` 21 | 22 | Install via Yarn 23 | 24 | ```bash 25 | yarn add react-native-sunmi-v2-printer 26 | ``` 27 | 28 | Install from source 29 | 30 | ```bash 31 | npm install https://github.com/suraneti/react-native-sunmi-v2-printer.git --save 32 | yarn add https://github.com/suraneti/react-native-sunmi-v2-printer.git --save 33 | ``` 34 | 35 | **Step 2:** (For React-Native < 0.60) 36 | 37 | Links this plugin to your project. 38 | 39 | ```bash 40 | react-native link react-native-sunmi-v2-printer 41 | ``` 42 | 43 | or you may need to link manually 44 | 45 | * modify settings.gradle 46 | 47 | ```javascript 48 | include ':react-native-sunmi-v2-printer' 49 | project(':react-native-sunmi-v2-printer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sunmi-v2-printer/android') 50 | ``` 51 | 52 | * modify app/build.gradle,add dependenceie: 53 | 54 | ```javascript 55 | compile project(':react-native-sunmi-v2-printer') 56 | ``` 57 | 58 | * adds package references to MainPackage.java 59 | 60 | ```java 61 | import com.sunmi.v2.printer.SunmiV2PrinterPackage; 62 | ... 63 | 64 | @Override 65 | protected List getPackages() { 66 | return Arrays.asList( 67 | new MainReactPackage(), 68 | new SunmiV2PrinterPackage() 69 | ); 70 | } 71 | ``` 72 | 73 | **Step 3:** 74 | 75 | Import in React-Native: 76 | 77 | ```javascript 78 | import SunmiV2Printer from 'react-native-sunmi-v2-printer'; 79 | ``` 80 | 81 | ## API 82 | 83 | ### Constants 84 | 85 | | Name | Type| Description | 86 | |:-----:|:-----:|:-----------:| 87 | | Constants | string | Printer's status | 88 | | hasPrinter | boolean | Is printer available | 89 | | printerVersion | string | Printer's version | 90 | | printerSerialNo | string | Printer's serial number | 91 | | printerModal | string | Printer's model | 92 | 93 | ### Printer Status 94 | 95 | | Name | Description | 96 | |:-----:|:-----------:| 97 | | OUT_OF_PAPER_ACTION | Printer of paper | 98 | | ERROR_ACTION | Printing error | 99 | | NORMAL_ACTION | Printing normal | 100 | | COVER_OPEN_ACTION | Printer's cover has open | 101 | | COVER_ERROR_ACTION | Printer's cover is unusal | 102 | | KNIFE_ERROR_1_ACTION | 切刀异常1-卡切刀 | 103 | | KNIFE_ERROR_2_ACTION | 切刀异常2-切刀修复 | 104 | | OVER_HEATING_ACITON | Printer is overheat | 105 | | FIRMWARE_UPDATING_ACITON | Upgrade printer's firmware | 106 | 107 | ### Example 108 | 109 | Please check on `example/` floder 110 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /example/android/app/src/debug/java/com/example/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.example; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin; 21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | public class ReactNativeFlipper { 28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 29 | if (FlipperUtils.shouldEnableFlipper(context)) { 30 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 31 | 32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 33 | client.addPlugin(new ReactFlipperPlugin()); 34 | client.addPlugin(new DatabasesFlipperPlugin(context)); 35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 36 | client.addPlugin(CrashReporterPlugin.getInstance()); 37 | 38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 39 | NetworkingModule.setCustomClientBuilder( 40 | new NetworkingModule.CustomClientBuilder() { 41 | @Override 42 | public void apply(OkHttpClient.Builder builder) { 43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 44 | } 45 | }); 46 | client.addPlugin(networkFlipperPlugin); 47 | client.start(); 48 | 49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 50 | // Hence we run if after all native modules have been initialized 51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 52 | if (reactContext == null) { 53 | reactInstanceManager.addReactInstanceEventListener( 54 | new ReactInstanceManager.ReactInstanceEventListener() { 55 | @Override 56 | public void onReactContextInitialized(ReactContext reactContext) { 57 | reactInstanceManager.removeReactInstanceEventListener(this); 58 | reactContext.runOnNativeModulesQueueThread( 59 | new Runnable() { 60 | @Override 61 | public void run() { 62 | client.addPlugin(new FrescoFlipperPlugin()); 63 | } 64 | }); 65 | } 66 | }); 67 | } else { 68 | client.addPlugin(new FrescoFlipperPlugin()); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /example/ios/example/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /android/src/main/java/woyou/aidlservice/jiuiv5/IWoyouService.aidl: -------------------------------------------------------------------------------- 1 | //P、V系列 2 | 3 | package woyou.aidlservice.jiuiv5; 4 | 5 | import woyou.aidlservice.jiuiv5.ICallback; 6 | import woyou.aidlservice.jiuiv5.ITax; 7 | import android.graphics.Bitmap; 8 | import com.sunmi.trans.TransBean; 9 | 10 | interface IWoyouService 11 | { 12 | /** 13 | * 替换原打印机升级固件接口(void updateFirmware()) 14 | * 现更改为负载包名的数据接口,仅系统调用 15 | * 支持版本:4.0.0以上 16 | */ 17 | boolean postPrintData(String packageName, in byte[] data, int offset, int length); 18 | 19 | /** 20 | * 打印机固件状态 21 | * 返回: 0--未知, A5--bootloader, C3--print 22 | */ 23 | int getFirmwareStatus(); 24 | 25 | /** 26 | * 获取打印服务版本 27 | * 返回: WoyouService服务版本 28 | */ 29 | String getServiceVersion(); 30 | 31 | /** 32 | * 初始化打印机,重置打印机的逻辑程序,但不清空缓存区数据,因此 33 | * 未完成的打印作业将在重置后继续 34 | */ 35 | void printerInit(in ICallback callback); 36 | 37 | /** 38 | * 打印机自检,打印机会打印自检页 39 | */ 40 | void printerSelfChecking(in ICallback callback); 41 | 42 | /** 43 | * 获取打印机板序列号 44 | * 返回:打印机板的序列号 45 | */ 46 | String getPrinterSerialNo(); 47 | 48 | /** 49 | * 获取打印机固件版本号 50 | * 返回:打印机固件版本号 51 | */ 52 | String getPrinterVersion(); 53 | 54 | /** 55 | * 获取打印机型号 56 | * 返回:打印机型号 57 | */ 58 | String getPrinterModal(); 59 | 60 | /** 61 | * 获取打印机上电后的打印长度 62 | * callback onReturnString 中返回 63 | */ 64 | void getPrintedLength(in ICallback callback); 65 | 66 | /** 67 | * 打印机走纸(强制换行,结束之前的打印内容后走纸n行) 68 | * n: 走纸行数 69 | */ 70 | void lineWrap(int n, in ICallback callback); 71 | 72 | /** 73 | * epson指令打印 74 | */ 75 | void sendRAWData(in byte[] data, in ICallback callback); 76 | 77 | /** 78 | * 设置对齐模式,对之后打印有影响,除非初始化 79 | * alignment: 对齐方式 0--居左 , 1--居中, 2--居右 80 | */ 81 | void setAlignment(int alignment, in ICallback callback); 82 | 83 | /** 84 | * 设置打印字体, 暂时仅能系统调用,外部调用无效 85 | */ 86 | void setFontName(String typeface, in ICallback callback); 87 | 88 | /** 89 | * 设置字体大小, 对之后打印有影响,除非初始化 90 | * 注意:字体大小是超出标准国际指令的打印方式, 91 | * 调整字体大小会影响字符宽度,每行字符数量也会随之改变, 92 | * 因此按等宽字体形成的排版可能会错乱 93 | * fontsize: 字体大小 94 | */ 95 | void setFontSize(float fontsize, in ICallback callback); 96 | 97 | /** 98 | * 打印文字,文字宽度满一行自动换行排版,不满一整行不打印除非强制换行 99 | * text: 要打印的文字字符串 100 | */ 101 | void printText(String text, in ICallback callback); 102 | 103 | /** 104 | * 打印指定字体的文本,字体设置只对本次有效 105 | * text: 要打印文字 106 | * typeface: 字体名称(暂时仅能系统调用,外部调用无效) 107 | * fontsize: 字体大小 108 | */ 109 | void printTextWithFont(String text, String typeface, float fontsize, in ICallback callback); 110 | 111 | /** 112 | * 打印表格的一行,可以指定列宽、对齐方式 113 | * colsTextArr: 各列文本字符串数组 114 | * colsWidthArr: 各列宽度数组(以英文字符计算, 每个中文字符占两个英文字符, 每个宽度大于0) 115 | * colsAlign: 各列对齐方式(0居左, 1居中, 2居右) 116 | * 备注: 三个参数的数组长度应该一致, 如果colsText[i]的宽度大于colsWidth[i], 则文本换行 117 | */ 118 | void printColumnsText(in String[] colsTextArr, in int[] colsWidthArr, in int[] colsAlign, in ICallback callback); 119 | 120 | /** 121 | * 打印图片 122 | * bitmap: 最大宽度384像素,超出宽度将显示不全;图片大小长*宽<8M; 123 | */ 124 | void printBitmap(in Bitmap bitmap, in ICallback callback); 125 | 126 | /** 127 | * 打印一维条码 128 | * data: 条码数据 129 | * symbology: 条码类型 130 | * 0 -- UPC-A, 要求12位数字(最后一位校验位必须正确),但受限于打印机的宽度及条码宽度 131 | * 1 -- UPC-E, 要求8位数字(最后一位校验位必须正确),但受限于打印机的宽度及条码宽度 132 | * 2 -- JAN13(EAN13), 要求13位数字(最后一位校验位必须正确),但受限于打印机的宽度及条码宽度 133 | * 3 -- JAN8(EAN8), 要求8位数字(最后一位校验位必须正确),但受限于打印机的宽度及条码宽度 134 | * 4 -- CODE39, 数字英文及8个特殊符号且首尾为*号,但受限于打印机的宽度及条码宽度 135 | * 5 -- ITF, 字符为数字且小于14位,但受限于打印机的宽度及条码宽度 136 | * 6 -- CODABAR, 起始和终止必须为A-D,数据为0-9及6个特殊字符,长度任意但受限于打印机的宽度及条码宽度 137 | * 7 -- CODE93, 字符任意,长度任意但受限于打印机的宽度及条码宽度 138 | * 8 -- CODE128 字符任意,长度任意但受限于打印机的宽度及条码宽度 139 | * height: 条码高度, 取值1到255, 默认162 140 | * width: 条码宽度, 取值2至6, 默认2 141 | * textposition: 文字位置 0--不打印文字, 1--文字在条码上方, 2--文字在条码下方, 3--条码上下方均打印 142 | */ 143 | void printBarCode(String data, int symbology, int height, int width, int textposition, in ICallback callback); 144 | 145 | /** 146 | * 打印二维条码 147 | * data: 二维码数据 148 | * modulesize: 二维码块大小(单位:点, 取值 1 至 16 ) 149 | * errorlevel: 二维码纠错等级(0 至 3), 150 | * 0 -- 纠错级别L ( 7%), 151 | * 1 -- 纠错级别M (15%), 152 | * 2 -- 纠错级别Q (25%), 153 | * 3 -- 纠错级别H (30%) 154 | */ 155 | void printQRCode(String data, int modulesize, int errorlevel, in ICallback callback); 156 | 157 | /** 158 | * 打印文字,文字宽度满一行自动换行排版,不满一整行不打印除非强制换行 159 | * 文字按矢量文字宽度原样输出,即每个字符不等宽 160 | * text: 要打印的文字字符串 161 | */ 162 | void printOriginalText(String text, in ICallback callback); 163 | 164 | /** 165 | * lib包打印专用接口 166 | * transbean:打印任务列表 167 | */ 168 | void commitPrint(in TransBean[] transbean, in ICallback callback); 169 | 170 | /** 171 | * 打印缓冲区内容 172 | */ 173 | void commitPrinterBuffer(); 174 | 175 | /** 176 | * 进入事务模式,所有打印调用将缓存; 177 | * 调用commitPrinterBuffe()、exitPrinterBuffer(true)、commitPrinterBufferWithCallback()、 178 | * exitPrinterBufferWithCallback(true)后才进行打印; 179 | * clean: 如果之前没退出事务模式,是否清除已缓存的缓冲区内容 180 | */ 181 | void enterPrinterBuffer(in boolean clean); 182 | 183 | /** 184 | * 退出缓冲模式 185 | * commit: 是否打印出缓冲区内容 186 | */ 187 | void exitPrinterBuffer(in boolean commit); 188 | 189 | /** 190 | * 发送数控指令 191 | * data:税控命令 192 | */ 193 | void tax(in byte [] data,in ITax callback); 194 | 195 | /** 196 | * 获取打印机头的型号 197 | * callback onReturnString 中返回 198 | */ 199 | void getPrinterFactory(in ICallback callback); 200 | 201 | /** 202 | * 清除打印机缓存数据(仅系统调用,外部调用无效) 203 | */ 204 | void clearBuffer(); 205 | 206 | /** 207 | * 带反馈打印缓冲区内容 208 | */ 209 | void commitPrinterBufferWithCallback(in ICallback callback); 210 | 211 | /** 212 | * 带反馈退出缓冲打印模式 213 | * commit: 是否提交缓冲区内容 214 | */ 215 | void exitPrinterBufferWithCallback(in boolean commit, in ICallback callback); 216 | 217 | /** 218 | * 打印表格的一行,可以指定列宽、对齐方式 219 | * colsTextArr: 各列文本字符串数组 220 | * colsWidthArr: 各列宽度权重即各列所占比例 221 | * colsAlign: 各列对齐方式(0居左, 1居中, 2居右) 222 | * 备注: 三个参数的数组长度应该一致, 如果colsText[i]的宽度大于colsWidth[i], 则文本换行 223 | */ 224 | void printColumnsString(in String[] colsTextArr, in int[] colsWidthArr, in int[] colsAlign, in ICallback callback); 225 | 226 | /** 227 | * 获取打印机的最新状态 228 | * 返回:打印机状态反馈 1正常 2准备中 3通信异常 4缺纸 5过热 505:无打印机 507:更新失败 229 | */ 230 | int updatePrinterState(); 231 | 232 | /** 233 | * 自定义打印图片 234 | * bitmap: 图片bitmap对象(最大宽度384像素,图片超过1M无法打印) 235 | * type: 目前有两种打印方式:0、同printBitmap 1、阈值200的黑白化图片 2、灰度图片 236 | * 支持版本: P1-v3.2.0以上 237 | * P14g-v1.1.6以上 238 | * V1s-v3.1.6以上 239 | * V2-v1.0.0以上 240 | */ 241 | void printBitmapCustom(in Bitmap bitmap, in int type, in ICallback callback); 242 | 243 | /** 244 | * 获取强制启用字体加倍状态 245 | * 返回 0:未启用 1:倍宽 2:倍高 3:倍高倍宽 246 | * 支持版本: P1-v3.2.0以上 247 | * P14g-v1.2.0以上 248 | * V1s-v3.2.0以上 249 | * V2-v1.0.0以上 250 | */ 251 | int getForcedDouble(); 252 | 253 | /** 254 | * 是否强制启用反白样式 255 | * 返回 true:启用 false:未启用 256 | * 支持版本: P1-v3.2.0以上 257 | * P14g-v1.2.0以上 258 | * V1s-v3.2.0以上 259 | * V2-v1.0.0以上 260 | */ 261 | boolean isForcedAntiWhite(); 262 | 263 | /** 264 | * 是否强制启用加粗样式 265 | * 返回 true:启用 false:未启用 266 | * 支持版本: P1-v3.2.0以上 267 | * P14g-v1.2.0以上 268 | * V1s-v3.2.0以上 269 | * V2-v1.0.0以上 270 | */ 271 | boolean isForcedBold(); 272 | 273 | /** 274 | * 是否强制启用下划线样式 275 | * 返回 true:启用 false:未启用 276 | * 支持版本: P1-v3.2.0以上 277 | * P14g-v1.2.0以上 278 | * V1s-v3.2.0以上 279 | * V2-v1.0.0以上 280 | */ 281 | boolean isForcedUnderline(); 282 | 283 | /** 284 | * 获取强制启用行高状态 285 | * 返回 -1:未启用 0~255:强制行高像素高度 286 | * 支持版本: P1-v3.2.0以上 287 | * P14g-v1.2.0以上 288 | * V1s-v3.2.0以上 289 | * V2-v1.0.0以上 290 | */ 291 | int getForcedRowHeight(); 292 | 293 | /** 294 | * 获取当前字体 295 | * 返回 0:商米字体1.0 1:商米字体2.0 296 | * 支持版本: P1-v3.2.0以上 297 | * P14g-v1.2.0以上 298 | * V1s-v3.2.0以上 299 | * V2-v1.0.0以上 300 | */ 301 | int getFontName(); 302 | } -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation. If none specified and 19 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is 20 | * // default. Can be overridden with ENTRY_FILE environment variable. 21 | * entryFile: "index.android.js", 22 | * 23 | * // https://reactnative.dev/docs/performance#enable-the-ram-format 24 | * bundleCommand: "ram-bundle", 25 | * 26 | * // whether to bundle JS and assets in debug mode 27 | * bundleInDebug: false, 28 | * 29 | * // whether to bundle JS and assets in release mode 30 | * bundleInRelease: true, 31 | * 32 | * // whether to bundle JS and assets in another build variant (if configured). 33 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 34 | * // The configuration property can be in the following formats 35 | * // 'bundleIn${productFlavor}${buildType}' 36 | * // 'bundleIn${buildType}' 37 | * // bundleInFreeDebug: true, 38 | * // bundleInPaidRelease: true, 39 | * // bundleInBeta: true, 40 | * 41 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 42 | * // for example: to disable dev mode in the staging build type (if configured) 43 | * devDisabledInStaging: true, 44 | * // The configuration property can be in the following formats 45 | * // 'devDisabledIn${productFlavor}${buildType}' 46 | * // 'devDisabledIn${buildType}' 47 | * 48 | * // the root of your project, i.e. where "package.json" lives 49 | * root: "../../", 50 | * 51 | * // where to put the JS bundle asset in debug mode 52 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 53 | * 54 | * // where to put the JS bundle asset in release mode 55 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 56 | * 57 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 58 | * // require('./image.png')), in debug mode 59 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 60 | * 61 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 62 | * // require('./image.png')), in release mode 63 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 64 | * 65 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 66 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 67 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 68 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 69 | * // for example, you might want to remove it from here. 70 | * inputExcludes: ["android/**", "ios/**"], 71 | * 72 | * // override which node gets called and with what additional arguments 73 | * nodeExecutableAndArgs: ["node"], 74 | * 75 | * // supply additional arguments to the packager 76 | * extraPackagerArgs: [] 77 | * ] 78 | */ 79 | 80 | project.ext.react = [ 81 | enableHermes: false, // clean and rebuild if changing 82 | ] 83 | 84 | apply from: "../../node_modules/react-native/react.gradle" 85 | 86 | /** 87 | * Set this to true to create two separate APKs instead of one: 88 | * - An APK that only works on ARM devices 89 | * - An APK that only works on x86 devices 90 | * The advantage is the size of the APK is reduced by about 4MB. 91 | * Upload all the APKs to the Play Store and people will download 92 | * the correct one based on the CPU architecture of their device. 93 | */ 94 | def enableSeparateBuildPerCPUArchitecture = false 95 | 96 | /** 97 | * Run Proguard to shrink the Java bytecode in release builds. 98 | */ 99 | def enableProguardInReleaseBuilds = false 100 | 101 | /** 102 | * The preferred build flavor of JavaScriptCore. 103 | * 104 | * For example, to use the international variant, you can use: 105 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 106 | * 107 | * The international variant includes ICU i18n library and necessary data 108 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 109 | * give correct results when using with locales other than en-US. Note that 110 | * this variant is about 6MiB larger per architecture than default. 111 | */ 112 | def jscFlavor = 'org.webkit:android-jsc:+' 113 | 114 | /** 115 | * Whether to enable the Hermes VM. 116 | * 117 | * This should be set on project.ext.react and mirrored here. If it is not set 118 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode 119 | * and the benefits of using Hermes will therefore be sharply reduced. 120 | */ 121 | def enableHermes = project.ext.react.get("enableHermes", false); 122 | 123 | android { 124 | compileSdkVersion rootProject.ext.compileSdkVersion 125 | 126 | compileOptions { 127 | sourceCompatibility JavaVersion.VERSION_1_8 128 | targetCompatibility JavaVersion.VERSION_1_8 129 | } 130 | 131 | defaultConfig { 132 | applicationId "com.example" 133 | minSdkVersion rootProject.ext.minSdkVersion 134 | targetSdkVersion rootProject.ext.targetSdkVersion 135 | versionCode 1 136 | versionName "1.0" 137 | } 138 | splits { 139 | abi { 140 | reset() 141 | enable enableSeparateBuildPerCPUArchitecture 142 | universalApk false // If true, also generate a universal APK 143 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" 144 | } 145 | } 146 | signingConfigs { 147 | debug { 148 | storeFile file('debug.keystore') 149 | storePassword 'android' 150 | keyAlias 'androiddebugkey' 151 | keyPassword 'android' 152 | } 153 | } 154 | buildTypes { 155 | debug { 156 | signingConfig signingConfigs.debug 157 | } 158 | release { 159 | // Caution! In production, you need to generate your own keystore file. 160 | // see https://reactnative.dev/docs/signed-apk-android. 161 | signingConfig signingConfigs.debug 162 | minifyEnabled enableProguardInReleaseBuilds 163 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 164 | } 165 | } 166 | 167 | // applicationVariants are e.g. debug, release 168 | applicationVariants.all { variant -> 169 | variant.outputs.each { output -> 170 | // For each separate APK per architecture, set a unique version code as described here: 171 | // https://developer.android.com/studio/build/configure-apk-splits.html 172 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] 173 | def abi = output.getFilter(OutputFile.ABI) 174 | if (abi != null) { // null for the universal-debug, universal-release variants 175 | output.versionCodeOverride = 176 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 177 | } 178 | 179 | } 180 | } 181 | } 182 | 183 | dependencies { 184 | compile project(':react-native-sunmi-v2-printer') 185 | 186 | implementation fileTree(dir: "libs", include: ["*.jar"]) 187 | //noinspection GradleDynamicVersion 188 | implementation "com.facebook.react:react-native:+" // From node_modules 189 | 190 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" 191 | 192 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { 193 | exclude group:'com.facebook.fbjni' 194 | } 195 | 196 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { 197 | exclude group:'com.facebook.flipper' 198 | exclude group:'com.squareup.okhttp3', module:'okhttp' 199 | } 200 | 201 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { 202 | exclude group:'com.facebook.flipper' 203 | } 204 | 205 | if (enableHermes) { 206 | def hermesPath = "../../node_modules/hermes-engine/android/"; 207 | debugImplementation files(hermesPath + "hermes-debug.aar") 208 | releaseImplementation files(hermesPath + "hermes-release.aar") 209 | } else { 210 | implementation jscFlavor 211 | } 212 | } 213 | 214 | // Run this once to be able to run the application with BUCK 215 | // puts all compile dependencies into folder libs for BUCK to use 216 | task copyDownloadableDepsToLibs(type: Copy) { 217 | from configurations.compile 218 | into 'libs' 219 | } 220 | 221 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 222 | -------------------------------------------------------------------------------- /android/src/main/java/com/sunmi/v2/printer/ESCUtil.java: -------------------------------------------------------------------------------- 1 | package com.sunmi.v2.printer; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.util.List; 5 | import java.util.ArrayList; 6 | import java.util.Map; 7 | 8 | import android.graphics.Bitmap; 9 | import android.graphics.BitmapFactory; 10 | 11 | public class ESCUtil { 12 | 13 | public static final byte ESC = 27;// 换码 14 | public static final byte FS = 28;// 文本分隔符 15 | public static final byte GS = 29;// 组分隔符 16 | public static final byte DLE = 16;// 数据连接换码 17 | public static final byte EOT = 4;// 传输结束 18 | public static final byte ENQ = 5;// 询问字符 19 | public static final byte SP = 32;// 空格 20 | public static final byte HT = 9;// 横向列表 21 | public static final byte LF = 10;// 打印并换行(水平定位) 22 | public static final byte CR = 13;// 归位键 23 | public static final byte FF = 12;// 走纸控制(打印并回到标准模式(在页模式下) ) 24 | public static final byte CAN = 24;// 作废(页模式下取消打印数据 ) 25 | 26 | // ------------------------打印机初始化----------------------------- 27 | 28 | private static String hexStr = "0123456789ABCDEF"; 29 | private static String[] binaryArray = { "0000", "0001", "0010", "0011", 30 | "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", 31 | "1100", "1101", "1110", "1111" }; 32 | 33 | private static byte uniteBytes(byte src0, byte src1) { 34 | byte _b0 = Byte.decode(new String(new byte[] { src0 })).byteValue(); 35 | _b0 = (byte) (_b0 << 4); 36 | byte _b1 = Byte.decode(new String(new byte[] { src1 })).byteValue(); 37 | byte ret = (byte) (_b0 | _b1); 38 | byte aret = Byte.decode("0x" + ret).byteValue(); 39 | 40 | return aret; 41 | } 42 | public static String binaryStrToHexString(String binaryStr) { 43 | String hex = ""; 44 | String f4 = binaryStr.substring(0, 4); 45 | String b4 = binaryStr.substring(4, 8); 46 | for (int i = 0; i < binaryArray.length; i++) { 47 | if (f4.equals(binaryArray[i])) 48 | hex += hexStr.substring(i, i + 1); 49 | } 50 | for (int i = 0; i < binaryArray.length; i++) { 51 | if (b4.equals(binaryArray[i])) 52 | hex += hexStr.substring(i, i + 1); 53 | } 54 | 55 | return hex; 56 | } 57 | 58 | public static byte[] HexStringToBinary(String hexString) { 59 | int len = hexString.length() / 2; 60 | byte[] bytes = new byte[len]; 61 | byte high = 0; 62 | byte low = 0; 63 | for (int i = 0; i < len; i++) { 64 | high = (byte) ((hexStr.indexOf(hexString.charAt(2 * i))) << 4); 65 | low = (byte) hexStr.indexOf(hexString.charAt(2 * i + 1)); 66 | bytes[i] = (byte) (high & 0xF0 | low & 0x0F); 67 | } 68 | return bytes; 69 | } 70 | 71 | public static List binaryListToHexStringList(List list) { 72 | List hexList = new ArrayList(); 73 | for (String binaryStr : list) { 74 | StringBuffer sb = new StringBuffer(); 75 | for (int i = 0; i < binaryStr.length(); i += 8) { 76 | String str = binaryStr.substring(i, i + 8); 77 | String hexString = binaryStrToHexString(str); 78 | sb.append(hexString); 79 | } 80 | hexList.add(sb.toString()); 81 | } 82 | return hexList; 83 | 84 | } 85 | public static byte[] sysCopy(List srcArrays) { 86 | int len = 0; 87 | for (byte[] srcArray : srcArrays) { 88 | len += srcArray.length; 89 | } 90 | byte[] destArray = new byte[len]; 91 | int destLen = 0; 92 | for (byte[] srcArray : srcArrays) { 93 | System.arraycopy(srcArray, 0, destArray, destLen, srcArray.length); 94 | destLen += srcArray.length; 95 | } 96 | return destArray; 97 | } 98 | 99 | public static byte[] hexList2Byte(List list) { 100 | List commandList = new ArrayList(); 101 | for (String hexStr : list) { 102 | commandList.add(HexStringToBinary(hexStr)); 103 | } 104 | byte[] bytes = sysCopy(commandList); 105 | return bytes; 106 | } 107 | 108 | /** 109 | * 打印机初始化 110 | * 111 | * @return 112 | */ 113 | public static byte[] init_printer() { 114 | byte[] result = new byte[2]; 115 | result[0] = ESC; 116 | result[1] = 64; 117 | return result; 118 | } 119 | 120 | // ------------------------换行----------------------------- 121 | 122 | /** 123 | * 换行 124 | * 125 | * @param lineNum要换几行 126 | * @return 127 | */ 128 | public static byte[] nextLine(int lineNum) { 129 | byte[] result = new byte[lineNum]; 130 | for (int i = 0; i < lineNum; i++) { 131 | result[i] = LF; 132 | } 133 | 134 | return result; 135 | } 136 | 137 | // ------------------------下划线----------------------------- 138 | 139 | /** 140 | * 绘制下划线(1点宽) 141 | * 142 | * @return 143 | */ 144 | public static byte[] underlineWithOneDotWidthOn() { 145 | byte[] result = new byte[3]; 146 | result[0] = ESC; 147 | result[1] = 45; 148 | result[2] = 1; 149 | return result; 150 | } 151 | 152 | /** 153 | * 绘制下划线(2点宽) 154 | * 155 | * @return 156 | */ 157 | public static byte[] underlineWithTwoDotWidthOn() { 158 | byte[] result = new byte[3]; 159 | result[0] = ESC; 160 | result[1] = 45; 161 | result[2] = 2; 162 | return result; 163 | } 164 | 165 | /** 166 | * 取消绘制下划线 167 | * 168 | * @return 169 | */ 170 | public static byte[] underlineOff() { 171 | byte[] result = new byte[3]; 172 | result[0] = ESC; 173 | result[1] = 45; 174 | result[2] = 0; 175 | return result; 176 | } 177 | 178 | // ------------------------加粗----------------------------- 179 | 180 | /** 181 | * 选择加粗模式 182 | * 183 | * @return 184 | */ 185 | public static byte[] boldOn() { 186 | byte[] result = new byte[3]; 187 | result[0] = ESC; 188 | result[1] = 69; 189 | result[2] = 0xF; 190 | return result; 191 | } 192 | 193 | /** 194 | * 取消加粗模式 195 | * 196 | * @return 197 | */ 198 | public static byte[] boldOff() { 199 | byte[] result = new byte[3]; 200 | result[0] = ESC; 201 | result[1] = 69; 202 | result[2] = 0; 203 | return result; 204 | } 205 | 206 | // ------------------------对齐----------------------------- 207 | 208 | /** 209 | * 左对齐 210 | * 211 | * @return 212 | */ 213 | public static byte[] alignLeft() { 214 | byte[] result = new byte[3]; 215 | result[0] = ESC; 216 | result[1] = 97; 217 | result[2] = 0; 218 | return result; 219 | } 220 | 221 | /** 222 | * 居中对齐 223 | * 224 | * @return 225 | */ 226 | public static byte[] alignCenter() { 227 | byte[] result = new byte[3]; 228 | result[0] = ESC; 229 | result[1] = 97; 230 | result[2] = 1; 231 | return result; 232 | } 233 | 234 | /** 235 | * 右对齐 236 | * 237 | * @return 238 | */ 239 | public static byte[] alignRight() { 240 | byte[] result = new byte[3]; 241 | result[0] = ESC; 242 | result[1] = 97; 243 | result[2] = 2; 244 | return result; 245 | } 246 | 247 | /** 248 | * 水平方向向右移动col列 249 | * 250 | * @param col 251 | * @return 252 | */ 253 | public static byte[] set_HT_position(byte col) { 254 | byte[] result = new byte[4]; 255 | result[0] = ESC; 256 | result[1] = 68; 257 | result[2] = col; 258 | result[3] = 0; 259 | return result; 260 | } 261 | // ------------------------字体变大----------------------------- 262 | 263 | /** 264 | * 字体变大为标准的n倍 265 | * 266 | * @param num 267 | * @return 268 | */ 269 | public static byte[] fontSizeSetBig(int num) { 270 | byte realSize = 0; 271 | switch (num) { 272 | case 1: 273 | realSize = 0; 274 | break; 275 | case 2: 276 | realSize = 17; 277 | break; 278 | case 3: 279 | realSize = 34; 280 | break; 281 | case 4: 282 | realSize = 51; 283 | break; 284 | case 5: 285 | realSize = 68; 286 | break; 287 | case 6: 288 | realSize = 85; 289 | break; 290 | case 7: 291 | realSize = 102; 292 | break; 293 | case 8: 294 | realSize = 119; 295 | break; 296 | } 297 | byte[] result = new byte[3]; 298 | result[0] = 29; 299 | result[1] = 33; 300 | result[2] = realSize; 301 | return result; 302 | } 303 | 304 | // ------------------------字体变小----------------------------- 305 | 306 | /** 307 | * 字体取消倍宽倍高 308 | * 309 | * @param num 310 | * @return 311 | */ 312 | public static byte[] fontSizeSetSmall(int num) { 313 | byte[] result = new byte[3]; 314 | result[0] = ESC; 315 | result[1] = 33; 316 | 317 | return result; 318 | } 319 | 320 | // ------------------------切纸----------------------------- 321 | 322 | /** 323 | * 进纸并全部切割 324 | * 325 | * @return 326 | */ 327 | public static byte[] feedPaperCutAll() { 328 | byte[] result = new byte[4]; 329 | result[0] = GS; 330 | result[1] = 86; 331 | result[2] = 65; 332 | result[3] = 0; 333 | return result; 334 | } 335 | 336 | /** 337 | * 进纸并切割(左边留一点不切) 338 | * 339 | * @return 340 | */ 341 | public static byte[] feedPaperCutPartial() { 342 | byte[] result = new byte[4]; 343 | result[0] = GS; 344 | result[1] = 86; 345 | result[2] = 66; 346 | result[3] = 0; 347 | return result; 348 | } 349 | 350 | // ------------------------切纸----------------------------- 351 | public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) { 352 | byte[] byte_3 = new byte[byte_1.length + byte_2.length]; 353 | System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length); 354 | System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length); 355 | return byte_3; 356 | } 357 | 358 | public static byte[] byteMerger(byte[][] byteList) { 359 | 360 | int length = 0; 361 | for (int i = 0; i < byteList.length; i++) { 362 | length += byteList[i].length; 363 | } 364 | byte[] result = new byte[length]; 365 | 366 | int index = 0; 367 | for (int i = 0; i < byteList.length; i++) { 368 | byte[] nowByte = byteList[i]; 369 | for (int k = 0; k < byteList[i].length; k++) { 370 | result[index] = nowByte[k]; 371 | index++; 372 | } 373 | } 374 | for (int i = 0; i < index; i++) { 375 | // CommonUtils.LogWuwei("", "result[" + i + "] is " + result[i]); 376 | } 377 | return result; 378 | } 379 | 380 | // -------------------- 381 | public static byte[] generateMockData() { 382 | try { 383 | byte[] next2Line = ESCUtil.nextLine(2); 384 | byte[] title = "出餐单(午餐)**万通中心店".getBytes("gb2312"); 385 | 386 | byte[] boldOn = ESCUtil.boldOn(); 387 | byte[] fontSize2Big = ESCUtil.fontSizeSetBig(3); 388 | byte[] center = ESCUtil.alignCenter(); 389 | byte[] Focus = "网 507".getBytes("gb2312"); 390 | byte[] boldOff = ESCUtil.boldOff(); 391 | byte[] fontSize2Small = ESCUtil.fontSizeSetSmall(3); 392 | 393 | byte[] left = ESCUtil.alignLeft(); 394 | byte[] orderSerinum = "订单编号:11234".getBytes("gb2312"); 395 | boldOn = ESCUtil.boldOn(); 396 | byte[] fontSize1Big = ESCUtil.fontSizeSetBig(2); 397 | byte[] FocusOrderContent = "韭菜鸡蛋饺子-小份(单)".getBytes("gb2312"); 398 | boldOff = ESCUtil.boldOff(); 399 | byte[] fontSize1Small = ESCUtil.fontSizeSetSmall(2); 400 | 401 | next2Line = ESCUtil.nextLine(2); 402 | 403 | byte[] priceInfo = "应收:22元 优惠:2.5元 ".getBytes("gb2312"); 404 | byte[] nextLine = ESCUtil.nextLine(1); 405 | 406 | byte[] priceShouldPay = "实收:19.5元".getBytes("gb2312"); 407 | nextLine = ESCUtil.nextLine(1); 408 | 409 | byte[] takeTime = "取餐时间:2015-02-13 12:51:59".getBytes("gb2312"); 410 | nextLine = ESCUtil.nextLine(1); 411 | byte[] setOrderTime = "下单时间:2015-02-13 12:35:15".getBytes("gb2312"); 412 | 413 | byte[] tips_1 = "微信关注\"**\"自助下单每天免1元".getBytes("gb2312"); 414 | nextLine = ESCUtil.nextLine(1); 415 | byte[] tips_2 = "饭后点评再奖5毛".getBytes("gb2312"); 416 | byte[] next4Line = ESCUtil.nextLine(4); 417 | 418 | byte[] breakPartial = ESCUtil.feedPaperCutPartial(); 419 | 420 | byte[][] cmdBytes = { title, nextLine, center, boldOn, fontSize2Big, Focus, boldOff, fontSize2Small, 421 | next2Line, left, orderSerinum, nextLine, center, boldOn, fontSize1Big, FocusOrderContent, boldOff, 422 | fontSize1Small, nextLine, left, next2Line, priceInfo, nextLine, priceShouldPay, next2Line, takeTime, 423 | nextLine, setOrderTime, next2Line, center, tips_1, nextLine, center, tips_2, next4Line, 424 | breakPartial }; 425 | 426 | return ESCUtil.byteMerger(cmdBytes); 427 | 428 | } catch (UnsupportedEncodingException e) { 429 | e.printStackTrace(); 430 | } 431 | return null; 432 | } 433 | 434 | public static byte[] decodeBitmap(byte[] bitmapBytes) { 435 | 436 | Bitmap bmp = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length); 437 | 438 | int zeroCount = bmp.getWidth() % 8; 439 | String zeroStr = ""; 440 | if (zeroCount > 0) { 441 | for (int i = 0; i < (8 - zeroCount); i++) { 442 | zeroStr = zeroStr + "0"; 443 | } 444 | } 445 | 446 | List list = new ArrayList<>(); 447 | for (int i = 0; i < bmp.getHeight(); i++) { 448 | StringBuilder sb = new StringBuilder(); 449 | for (int j = 0; j < bmp.getWidth(); j++) { 450 | int color = bmp.getPixel(j, i); 451 | 452 | int r = (color >> 16) & 0xff; 453 | int g = (color >> 8) & 0xff; 454 | int b = color & 0xff; 455 | 456 | // if color close to white,bit='0', else bit='1' 457 | if (r > 160 && g > 160 && b > 160) 458 | sb.append("0"); 459 | else 460 | sb.append("1"); 461 | } 462 | if (zeroCount > 0) { 463 | sb.append(zeroStr); 464 | } 465 | 466 | list.add(sb.toString()); 467 | } 468 | 469 | List bmpHexList = binaryListToHexStringList(list); 470 | List commandList = new ArrayList<>(); 471 | commandList.addAll(bmpHexList); 472 | 473 | return hexList2Byte(commandList); 474 | } 475 | public static byte[] decodeBitmap2(byte[] bitmapBytes) { 476 | Bitmap bmp = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length); 477 | return draw2PxPoint(bmp); 478 | } 479 | 480 | public static byte[] draw2PxPoint(Bitmap bmp) { 481 | //用来存储转换后的 bitmap 数据。为什么要再加1000,这是为了应对当图片高度无法 482 | //整除24时的情况。比如bitmap 分辨率为 240 * 250,占用 7500 byte, 483 | //但是实际上要存储11行数据,每一行需要 24 * 240 / 8 =720byte 的空间。再加上一些指令存储的开销, 484 | //所以多申请 1000byte 的空间是稳妥的,不然运行时会抛出数组访问越界的异常。 485 | int size = bmp.getWidth() * bmp.getHeight() / 8 + 1000; 486 | byte[] data = new byte[size]; 487 | int k = 0; 488 | //设置行距为0的指令 489 | data[k++] = 0x1B; 490 | data[k++] = 0x33; 491 | data[k++] = 0x00; 492 | // 逐行打印 493 | for (int j = 0; j < bmp.getHeight() / 24f; j++) { 494 | //打印图片的指令 495 | data[k++] = 0x1B; 496 | data[k++] = 0x2A; 497 | data[k++] = 33; 498 | data[k++] = (byte) (bmp.getWidth() % 256); //nL 499 | data[k++] = (byte) (bmp.getWidth() / 256); //nH 500 | //对于每一行,逐列打印 501 | for (int i = 0; i < bmp.getWidth(); i++) { 502 | //每一列24个像素点,分为3个字节存储 503 | for (int m = 0; m < 3; m++) { 504 | //每个字节表示8个像素点,0表示白色,1表示黑色 505 | for (int n = 0; n < 8; n++) { 506 | byte b = px2Byte(i, j * 24 + m * 8 + n, bmp); 507 | data[k] += data[k] + b; 508 | } 509 | k++; 510 | } 511 | } 512 | data[k++] = 10;//换行 513 | } 514 | return data; 515 | } 516 | /** 517 | * 灰度图片黑白化,黑色是1,白色是0 518 | * 519 | * @param x 横坐标 520 | * @param y 纵坐标 521 | * @param bit 位图 522 | * @return 523 | */ 524 | public static byte px2Byte(int x, int y, Bitmap bit) { 525 | if (x < bit.getWidth() && y < bit.getHeight()) { 526 | byte b; 527 | int pixel = bit.getPixel(x, y); 528 | int red = (pixel & 0x00ff0000) >> 16; // 取高两位 529 | int green = (pixel & 0x0000ff00) >> 8; // 取中两位 530 | int blue = pixel & 0x000000ff; // 取低两位 531 | int gray = RGB2Gray(red, green, blue); 532 | if (gray < 128) { 533 | b = 1; 534 | } else { 535 | b = 0; 536 | } 537 | return b; 538 | } 539 | return 0; 540 | } 541 | /** 542 | * 图片灰度的转化 543 | */ 544 | private static int RGB2Gray(int r, int g, int b) { 545 | int gray = (int) (0.29900 * r + 0.58700 * g + 0.11400 * b); //灰度转化公式 546 | return gray; 547 | } 548 | } 549 | 550 | 551 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - boost-for-react-native (1.63.0) 3 | - CocoaAsyncSocket (7.6.4) 4 | - CocoaLibEvent (1.0.0) 5 | - DoubleConversion (1.1.6) 6 | - FBLazyVector (0.63.3) 7 | - FBReactNativeSpec (0.63.3): 8 | - Folly (= 2020.01.13.00) 9 | - RCTRequired (= 0.63.3) 10 | - RCTTypeSafety (= 0.63.3) 11 | - React-Core (= 0.63.3) 12 | - React-jsi (= 0.63.3) 13 | - ReactCommon/turbomodule/core (= 0.63.3) 14 | - Flipper (0.54.0): 15 | - Flipper-Folly (~> 2.2) 16 | - Flipper-RSocket (~> 1.1) 17 | - Flipper-DoubleConversion (1.1.7) 18 | - Flipper-Folly (2.3.0): 19 | - boost-for-react-native 20 | - CocoaLibEvent (~> 1.0) 21 | - Flipper-DoubleConversion 22 | - Flipper-Glog 23 | - OpenSSL-Universal (= 1.0.2.20) 24 | - Flipper-Glog (0.3.6) 25 | - Flipper-PeerTalk (0.0.4) 26 | - Flipper-RSocket (1.1.0): 27 | - Flipper-Folly (~> 2.2) 28 | - FlipperKit (0.54.0): 29 | - FlipperKit/Core (= 0.54.0) 30 | - FlipperKit/Core (0.54.0): 31 | - Flipper (~> 0.54.0) 32 | - FlipperKit/CppBridge 33 | - FlipperKit/FBCxxFollyDynamicConvert 34 | - FlipperKit/FBDefines 35 | - FlipperKit/FKPortForwarding 36 | - FlipperKit/CppBridge (0.54.0): 37 | - Flipper (~> 0.54.0) 38 | - FlipperKit/FBCxxFollyDynamicConvert (0.54.0): 39 | - Flipper-Folly (~> 2.2) 40 | - FlipperKit/FBDefines (0.54.0) 41 | - FlipperKit/FKPortForwarding (0.54.0): 42 | - CocoaAsyncSocket (~> 7.6) 43 | - Flipper-PeerTalk (~> 0.0.4) 44 | - FlipperKit/FlipperKitHighlightOverlay (0.54.0) 45 | - FlipperKit/FlipperKitLayoutPlugin (0.54.0): 46 | - FlipperKit/Core 47 | - FlipperKit/FlipperKitHighlightOverlay 48 | - FlipperKit/FlipperKitLayoutTextSearchable 49 | - YogaKit (~> 1.18) 50 | - FlipperKit/FlipperKitLayoutTextSearchable (0.54.0) 51 | - FlipperKit/FlipperKitNetworkPlugin (0.54.0): 52 | - FlipperKit/Core 53 | - FlipperKit/FlipperKitReactPlugin (0.54.0): 54 | - FlipperKit/Core 55 | - FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0): 56 | - FlipperKit/Core 57 | - FlipperKit/SKIOSNetworkPlugin (0.54.0): 58 | - FlipperKit/Core 59 | - FlipperKit/FlipperKitNetworkPlugin 60 | - Folly (2020.01.13.00): 61 | - boost-for-react-native 62 | - DoubleConversion 63 | - Folly/Default (= 2020.01.13.00) 64 | - glog 65 | - Folly/Default (2020.01.13.00): 66 | - boost-for-react-native 67 | - DoubleConversion 68 | - glog 69 | - glog (0.3.5) 70 | - OpenSSL-Universal (1.0.2.20): 71 | - OpenSSL-Universal/Static (= 1.0.2.20) 72 | - OpenSSL-Universal/Static (1.0.2.20) 73 | - RCTRequired (0.63.3) 74 | - RCTTypeSafety (0.63.3): 75 | - FBLazyVector (= 0.63.3) 76 | - Folly (= 2020.01.13.00) 77 | - RCTRequired (= 0.63.3) 78 | - React-Core (= 0.63.3) 79 | - React (0.63.3): 80 | - React-Core (= 0.63.3) 81 | - React-Core/DevSupport (= 0.63.3) 82 | - React-Core/RCTWebSocket (= 0.63.3) 83 | - React-RCTActionSheet (= 0.63.3) 84 | - React-RCTAnimation (= 0.63.3) 85 | - React-RCTBlob (= 0.63.3) 86 | - React-RCTImage (= 0.63.3) 87 | - React-RCTLinking (= 0.63.3) 88 | - React-RCTNetwork (= 0.63.3) 89 | - React-RCTSettings (= 0.63.3) 90 | - React-RCTText (= 0.63.3) 91 | - React-RCTVibration (= 0.63.3) 92 | - React-callinvoker (0.63.3) 93 | - React-Core (0.63.3): 94 | - Folly (= 2020.01.13.00) 95 | - glog 96 | - React-Core/Default (= 0.63.3) 97 | - React-cxxreact (= 0.63.3) 98 | - React-jsi (= 0.63.3) 99 | - React-jsiexecutor (= 0.63.3) 100 | - Yoga 101 | - React-Core/CoreModulesHeaders (0.63.3): 102 | - Folly (= 2020.01.13.00) 103 | - glog 104 | - React-Core/Default 105 | - React-cxxreact (= 0.63.3) 106 | - React-jsi (= 0.63.3) 107 | - React-jsiexecutor (= 0.63.3) 108 | - Yoga 109 | - React-Core/Default (0.63.3): 110 | - Folly (= 2020.01.13.00) 111 | - glog 112 | - React-cxxreact (= 0.63.3) 113 | - React-jsi (= 0.63.3) 114 | - React-jsiexecutor (= 0.63.3) 115 | - Yoga 116 | - React-Core/DevSupport (0.63.3): 117 | - Folly (= 2020.01.13.00) 118 | - glog 119 | - React-Core/Default (= 0.63.3) 120 | - React-Core/RCTWebSocket (= 0.63.3) 121 | - React-cxxreact (= 0.63.3) 122 | - React-jsi (= 0.63.3) 123 | - React-jsiexecutor (= 0.63.3) 124 | - React-jsinspector (= 0.63.3) 125 | - Yoga 126 | - React-Core/RCTActionSheetHeaders (0.63.3): 127 | - Folly (= 2020.01.13.00) 128 | - glog 129 | - React-Core/Default 130 | - React-cxxreact (= 0.63.3) 131 | - React-jsi (= 0.63.3) 132 | - React-jsiexecutor (= 0.63.3) 133 | - Yoga 134 | - React-Core/RCTAnimationHeaders (0.63.3): 135 | - Folly (= 2020.01.13.00) 136 | - glog 137 | - React-Core/Default 138 | - React-cxxreact (= 0.63.3) 139 | - React-jsi (= 0.63.3) 140 | - React-jsiexecutor (= 0.63.3) 141 | - Yoga 142 | - React-Core/RCTBlobHeaders (0.63.3): 143 | - Folly (= 2020.01.13.00) 144 | - glog 145 | - React-Core/Default 146 | - React-cxxreact (= 0.63.3) 147 | - React-jsi (= 0.63.3) 148 | - React-jsiexecutor (= 0.63.3) 149 | - Yoga 150 | - React-Core/RCTImageHeaders (0.63.3): 151 | - Folly (= 2020.01.13.00) 152 | - glog 153 | - React-Core/Default 154 | - React-cxxreact (= 0.63.3) 155 | - React-jsi (= 0.63.3) 156 | - React-jsiexecutor (= 0.63.3) 157 | - Yoga 158 | - React-Core/RCTLinkingHeaders (0.63.3): 159 | - Folly (= 2020.01.13.00) 160 | - glog 161 | - React-Core/Default 162 | - React-cxxreact (= 0.63.3) 163 | - React-jsi (= 0.63.3) 164 | - React-jsiexecutor (= 0.63.3) 165 | - Yoga 166 | - React-Core/RCTNetworkHeaders (0.63.3): 167 | - Folly (= 2020.01.13.00) 168 | - glog 169 | - React-Core/Default 170 | - React-cxxreact (= 0.63.3) 171 | - React-jsi (= 0.63.3) 172 | - React-jsiexecutor (= 0.63.3) 173 | - Yoga 174 | - React-Core/RCTSettingsHeaders (0.63.3): 175 | - Folly (= 2020.01.13.00) 176 | - glog 177 | - React-Core/Default 178 | - React-cxxreact (= 0.63.3) 179 | - React-jsi (= 0.63.3) 180 | - React-jsiexecutor (= 0.63.3) 181 | - Yoga 182 | - React-Core/RCTTextHeaders (0.63.3): 183 | - Folly (= 2020.01.13.00) 184 | - glog 185 | - React-Core/Default 186 | - React-cxxreact (= 0.63.3) 187 | - React-jsi (= 0.63.3) 188 | - React-jsiexecutor (= 0.63.3) 189 | - Yoga 190 | - React-Core/RCTVibrationHeaders (0.63.3): 191 | - Folly (= 2020.01.13.00) 192 | - glog 193 | - React-Core/Default 194 | - React-cxxreact (= 0.63.3) 195 | - React-jsi (= 0.63.3) 196 | - React-jsiexecutor (= 0.63.3) 197 | - Yoga 198 | - React-Core/RCTWebSocket (0.63.3): 199 | - Folly (= 2020.01.13.00) 200 | - glog 201 | - React-Core/Default (= 0.63.3) 202 | - React-cxxreact (= 0.63.3) 203 | - React-jsi (= 0.63.3) 204 | - React-jsiexecutor (= 0.63.3) 205 | - Yoga 206 | - React-CoreModules (0.63.3): 207 | - FBReactNativeSpec (= 0.63.3) 208 | - Folly (= 2020.01.13.00) 209 | - RCTTypeSafety (= 0.63.3) 210 | - React-Core/CoreModulesHeaders (= 0.63.3) 211 | - React-jsi (= 0.63.3) 212 | - React-RCTImage (= 0.63.3) 213 | - ReactCommon/turbomodule/core (= 0.63.3) 214 | - React-cxxreact (0.63.3): 215 | - boost-for-react-native (= 1.63.0) 216 | - DoubleConversion 217 | - Folly (= 2020.01.13.00) 218 | - glog 219 | - React-callinvoker (= 0.63.3) 220 | - React-jsinspector (= 0.63.3) 221 | - React-jsi (0.63.3): 222 | - boost-for-react-native (= 1.63.0) 223 | - DoubleConversion 224 | - Folly (= 2020.01.13.00) 225 | - glog 226 | - React-jsi/Default (= 0.63.3) 227 | - React-jsi/Default (0.63.3): 228 | - boost-for-react-native (= 1.63.0) 229 | - DoubleConversion 230 | - Folly (= 2020.01.13.00) 231 | - glog 232 | - React-jsiexecutor (0.63.3): 233 | - DoubleConversion 234 | - Folly (= 2020.01.13.00) 235 | - glog 236 | - React-cxxreact (= 0.63.3) 237 | - React-jsi (= 0.63.3) 238 | - React-jsinspector (0.63.3) 239 | - React-RCTActionSheet (0.63.3): 240 | - React-Core/RCTActionSheetHeaders (= 0.63.3) 241 | - React-RCTAnimation (0.63.3): 242 | - FBReactNativeSpec (= 0.63.3) 243 | - Folly (= 2020.01.13.00) 244 | - RCTTypeSafety (= 0.63.3) 245 | - React-Core/RCTAnimationHeaders (= 0.63.3) 246 | - React-jsi (= 0.63.3) 247 | - ReactCommon/turbomodule/core (= 0.63.3) 248 | - React-RCTBlob (0.63.3): 249 | - FBReactNativeSpec (= 0.63.3) 250 | - Folly (= 2020.01.13.00) 251 | - React-Core/RCTBlobHeaders (= 0.63.3) 252 | - React-Core/RCTWebSocket (= 0.63.3) 253 | - React-jsi (= 0.63.3) 254 | - React-RCTNetwork (= 0.63.3) 255 | - ReactCommon/turbomodule/core (= 0.63.3) 256 | - React-RCTImage (0.63.3): 257 | - FBReactNativeSpec (= 0.63.3) 258 | - Folly (= 2020.01.13.00) 259 | - RCTTypeSafety (= 0.63.3) 260 | - React-Core/RCTImageHeaders (= 0.63.3) 261 | - React-jsi (= 0.63.3) 262 | - React-RCTNetwork (= 0.63.3) 263 | - ReactCommon/turbomodule/core (= 0.63.3) 264 | - React-RCTLinking (0.63.3): 265 | - FBReactNativeSpec (= 0.63.3) 266 | - React-Core/RCTLinkingHeaders (= 0.63.3) 267 | - React-jsi (= 0.63.3) 268 | - ReactCommon/turbomodule/core (= 0.63.3) 269 | - React-RCTNetwork (0.63.3): 270 | - FBReactNativeSpec (= 0.63.3) 271 | - Folly (= 2020.01.13.00) 272 | - RCTTypeSafety (= 0.63.3) 273 | - React-Core/RCTNetworkHeaders (= 0.63.3) 274 | - React-jsi (= 0.63.3) 275 | - ReactCommon/turbomodule/core (= 0.63.3) 276 | - React-RCTSettings (0.63.3): 277 | - FBReactNativeSpec (= 0.63.3) 278 | - Folly (= 2020.01.13.00) 279 | - RCTTypeSafety (= 0.63.3) 280 | - React-Core/RCTSettingsHeaders (= 0.63.3) 281 | - React-jsi (= 0.63.3) 282 | - ReactCommon/turbomodule/core (= 0.63.3) 283 | - React-RCTText (0.63.3): 284 | - React-Core/RCTTextHeaders (= 0.63.3) 285 | - React-RCTVibration (0.63.3): 286 | - FBReactNativeSpec (= 0.63.3) 287 | - Folly (= 2020.01.13.00) 288 | - React-Core/RCTVibrationHeaders (= 0.63.3) 289 | - React-jsi (= 0.63.3) 290 | - ReactCommon/turbomodule/core (= 0.63.3) 291 | - ReactCommon/turbomodule/core (0.63.3): 292 | - DoubleConversion 293 | - Folly (= 2020.01.13.00) 294 | - glog 295 | - React-callinvoker (= 0.63.3) 296 | - React-Core (= 0.63.3) 297 | - React-cxxreact (= 0.63.3) 298 | - React-jsi (= 0.63.3) 299 | - Yoga (1.14.0) 300 | - YogaKit (1.18.1): 301 | - Yoga (~> 1.14) 302 | 303 | DEPENDENCIES: 304 | - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) 305 | - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) 306 | - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`) 307 | - Flipper (~> 0.54.0) 308 | - Flipper-DoubleConversion (= 1.1.7) 309 | - Flipper-Folly (~> 2.2) 310 | - Flipper-Glog (= 0.3.6) 311 | - Flipper-PeerTalk (~> 0.0.4) 312 | - Flipper-RSocket (~> 1.1) 313 | - FlipperKit (~> 0.54.0) 314 | - FlipperKit/Core (~> 0.54.0) 315 | - FlipperKit/CppBridge (~> 0.54.0) 316 | - FlipperKit/FBCxxFollyDynamicConvert (~> 0.54.0) 317 | - FlipperKit/FBDefines (~> 0.54.0) 318 | - FlipperKit/FKPortForwarding (~> 0.54.0) 319 | - FlipperKit/FlipperKitHighlightOverlay (~> 0.54.0) 320 | - FlipperKit/FlipperKitLayoutPlugin (~> 0.54.0) 321 | - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.54.0) 322 | - FlipperKit/FlipperKitNetworkPlugin (~> 0.54.0) 323 | - FlipperKit/FlipperKitReactPlugin (~> 0.54.0) 324 | - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.54.0) 325 | - FlipperKit/SKIOSNetworkPlugin (~> 0.54.0) 326 | - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) 327 | - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) 328 | - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) 329 | - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) 330 | - React (from `../node_modules/react-native/`) 331 | - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) 332 | - React-Core (from `../node_modules/react-native/`) 333 | - React-Core/DevSupport (from `../node_modules/react-native/`) 334 | - React-Core/RCTWebSocket (from `../node_modules/react-native/`) 335 | - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) 336 | - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) 337 | - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) 338 | - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) 339 | - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) 340 | - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) 341 | - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) 342 | - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) 343 | - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) 344 | - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) 345 | - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) 346 | - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) 347 | - React-RCTText (from `../node_modules/react-native/Libraries/Text`) 348 | - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) 349 | - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) 350 | - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) 351 | 352 | SPEC REPOS: 353 | trunk: 354 | - boost-for-react-native 355 | - CocoaAsyncSocket 356 | - CocoaLibEvent 357 | - Flipper 358 | - Flipper-DoubleConversion 359 | - Flipper-Folly 360 | - Flipper-Glog 361 | - Flipper-PeerTalk 362 | - Flipper-RSocket 363 | - FlipperKit 364 | - OpenSSL-Universal 365 | - YogaKit 366 | 367 | EXTERNAL SOURCES: 368 | DoubleConversion: 369 | :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" 370 | FBLazyVector: 371 | :path: "../node_modules/react-native/Libraries/FBLazyVector" 372 | FBReactNativeSpec: 373 | :path: "../node_modules/react-native/Libraries/FBReactNativeSpec" 374 | Folly: 375 | :podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec" 376 | glog: 377 | :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" 378 | RCTRequired: 379 | :path: "../node_modules/react-native/Libraries/RCTRequired" 380 | RCTTypeSafety: 381 | :path: "../node_modules/react-native/Libraries/TypeSafety" 382 | React: 383 | :path: "../node_modules/react-native/" 384 | React-callinvoker: 385 | :path: "../node_modules/react-native/ReactCommon/callinvoker" 386 | React-Core: 387 | :path: "../node_modules/react-native/" 388 | React-CoreModules: 389 | :path: "../node_modules/react-native/React/CoreModules" 390 | React-cxxreact: 391 | :path: "../node_modules/react-native/ReactCommon/cxxreact" 392 | React-jsi: 393 | :path: "../node_modules/react-native/ReactCommon/jsi" 394 | React-jsiexecutor: 395 | :path: "../node_modules/react-native/ReactCommon/jsiexecutor" 396 | React-jsinspector: 397 | :path: "../node_modules/react-native/ReactCommon/jsinspector" 398 | React-RCTActionSheet: 399 | :path: "../node_modules/react-native/Libraries/ActionSheetIOS" 400 | React-RCTAnimation: 401 | :path: "../node_modules/react-native/Libraries/NativeAnimation" 402 | React-RCTBlob: 403 | :path: "../node_modules/react-native/Libraries/Blob" 404 | React-RCTImage: 405 | :path: "../node_modules/react-native/Libraries/Image" 406 | React-RCTLinking: 407 | :path: "../node_modules/react-native/Libraries/LinkingIOS" 408 | React-RCTNetwork: 409 | :path: "../node_modules/react-native/Libraries/Network" 410 | React-RCTSettings: 411 | :path: "../node_modules/react-native/Libraries/Settings" 412 | React-RCTText: 413 | :path: "../node_modules/react-native/Libraries/Text" 414 | React-RCTVibration: 415 | :path: "../node_modules/react-native/Libraries/Vibration" 416 | ReactCommon: 417 | :path: "../node_modules/react-native/ReactCommon" 418 | Yoga: 419 | :path: "../node_modules/react-native/ReactCommon/yoga" 420 | 421 | SPEC CHECKSUMS: 422 | boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c 423 | CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845 424 | CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f 425 | DoubleConversion: cde416483dac037923206447da6e1454df403714 426 | FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d 427 | FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f 428 | Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365 429 | Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 430 | Flipper-Folly: e4493b013c02d9347d5e0cb4d128680239f6c78a 431 | Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 432 | Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 433 | Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7 434 | FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d 435 | Folly: b73c3869541e86821df3c387eb0af5f65addfab4 436 | glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3 437 | OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd 438 | RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047 439 | RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab 440 | React: f36e90f3ceb976546e97df3403e37d226f79d0e3 441 | React-callinvoker: 18874f621eb96625df7a24a7dc8d6e07391affcd 442 | React-Core: ac3d816b8e3493970153f4aaf0cff18af0bb95e6 443 | React-CoreModules: 4016d3a4e518bcfc4f5a51252b5a05692ca6f0e1 444 | React-cxxreact: ffc9129013b87cb36cf3f30a86695a3c397b0f99 445 | React-jsi: df07aa95b39c5be3e41199921509bfa929ed2b9d 446 | React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451 447 | React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2 448 | React-RCTActionSheet: 53ea72699698b0b47a6421cb1c8b4ab215a774aa 449 | React-RCTAnimation: 1befece0b5183c22ae01b966f5583f42e69a83c2 450 | React-RCTBlob: 0b284339cbe4b15705a05e2313a51c6d8b51fa40 451 | React-RCTImage: d1756599ebd4dc2cb19d1682fe67c6b976658387 452 | React-RCTLinking: 9af0a51c6d6a4dd1674daadafffc6d03033a6d18 453 | React-RCTNetwork: 332c83929cc5eae0b3bbca4add1d668e1fc18bda 454 | React-RCTSettings: d6953772cfd55f2c68ad72b7ef29efc7ec49f773 455 | React-RCTText: 65a6de06a7389098ce24340d1d3556015c38f746 456 | React-RCTVibration: 8e9fb25724a0805107fc1acc9075e26f814df454 457 | ReactCommon: 4167844018c9ed375cc01a843e9ee564399e53c3 458 | Yoga: 7d13633d129fd179e01b8953d38d47be90db185a 459 | YogaKit: f782866e155069a2cca2517aafea43200b01fd5a 460 | 461 | PODFILE CHECKSUM: 311cf87a4a33d759b7ec994ec3735e03d4ededbf 462 | 463 | COCOAPODS: 1.10.0 464 | -------------------------------------------------------------------------------- /android/src/main/java/com/sunmi/v2/printer/BitmapUtils.java: -------------------------------------------------------------------------------- 1 | package com.sunmi.v2.printer; 2 | 3 | import java.io.Closeable; 4 | import java.io.File; 5 | import java.io.FileNotFoundException; 6 | import java.io.FileOutputStream; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | 10 | import android.content.Context; 11 | import android.content.res.Resources; 12 | import android.database.Cursor; 13 | import android.graphics.Bitmap; 14 | import android.graphics.Bitmap.CompressFormat; 15 | import android.graphics.BitmapFactory; 16 | import android.graphics.Canvas; 17 | import android.graphics.Color; 18 | import android.graphics.Matrix; 19 | import android.graphics.Paint; 20 | import android.graphics.Rect; 21 | import android.graphics.RectF; 22 | import android.net.Uri; 23 | import android.provider.MediaStore.Images.ImageColumns; 24 | import android.util.Log; 25 | 26 | /** 27 | * bitmap操作的工具类 28 | * 29 | * @author longtao.li 30 | * 2012-10-18 31 | * 32 | */ 33 | public class BitmapUtils { 34 | 35 | private static final String TAG = "BitmapUtils"; 36 | private static final int DEFAULT_COMPRESS_QUALITY = 50; 37 | private static final int INDEX_ORIENTATION = 0; 38 | 39 | private static final String[] IMAGE_PROJECTION = new String[] { 40 | ImageColumns.ORIENTATION 41 | }; 42 | 43 | private final Context context; 44 | 45 | public BitmapUtils(Context context) { 46 | this.context = context; 47 | } 48 | 49 | /** 50 | * Creates a mutable bitmap from subset of source bitmap, transformed by the optional matrix. 51 | */ 52 | private static Bitmap createBitmap( 53 | Bitmap source, int x, int y, int width, int height, Matrix m) { 54 | // Re-implement Bitmap createBitmap() to always return a mutable bitmap. 55 | Canvas canvas = new Canvas(); 56 | 57 | Bitmap bitmap; 58 | Paint paint; 59 | if ((m == null) || m.isIdentity()) { 60 | bitmap = Bitmap.createBitmap(width, height, source.getConfig()); 61 | paint = null; 62 | } else { 63 | RectF rect = new RectF(0, 0, width, height); 64 | m.mapRect(rect); 65 | bitmap = Bitmap.createBitmap( 66 | Math.round(rect.width()), Math.round(rect.height()), source.getConfig()); 67 | 68 | canvas.translate(-rect.left, -rect.top); 69 | canvas.concat(m); 70 | 71 | paint = new Paint(Paint.FILTER_BITMAP_FLAG); 72 | if (!m.rectStaysRect()) { 73 | paint.setAntiAlias(true); 74 | } 75 | } 76 | bitmap.setDensity(source.getDensity()); 77 | canvas.setBitmap(bitmap); 78 | 79 | Rect srcBounds = new Rect(x, y, x + width, y + height); 80 | RectF dstBounds = new RectF(0, 0, width, height); 81 | canvas.drawBitmap(source, srcBounds, dstBounds, paint); 82 | return bitmap; 83 | } 84 | 85 | private void closeStream(Closeable stream) { 86 | if (stream != null) { 87 | try { 88 | stream.close(); 89 | } catch (Exception e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | } 94 | 95 | public Rect getBitmapBounds(byte[] data){ 96 | Rect bounds = new Rect(); 97 | try { 98 | BitmapFactory.Options options = new BitmapFactory.Options(); 99 | options.inJustDecodeBounds = true; 100 | BitmapFactory.decodeByteArray(data, 0, data.length, options); 101 | bounds.right = options.outWidth; 102 | bounds.bottom = options.outHeight; 103 | Log.i(TAG, "options.outWidth="+options.outWidth+" , "+"options.outHeight="+options.outHeight); 104 | } catch (Exception e) { 105 | }finally { 106 | } 107 | return bounds; 108 | } 109 | 110 | public Rect getBitmapBounds(Uri uri) { 111 | Rect bounds = new Rect(); 112 | InputStream is = null; 113 | 114 | try { 115 | is = context.getContentResolver().openInputStream(uri); 116 | BitmapFactory.Options options = new BitmapFactory.Options(); 117 | options.inJustDecodeBounds = true; 118 | BitmapFactory.decodeStream(is, null, options); 119 | 120 | bounds.right = options.outWidth; 121 | bounds.bottom = options.outHeight; 122 | Log.i(TAG, "options.outWidth="+options.outWidth+" , "+"options.outHeight="+options.outHeight); 123 | } catch (Exception e) { 124 | e.printStackTrace(); 125 | } finally { 126 | closeStream(is); 127 | } 128 | 129 | return bounds; 130 | } 131 | 132 | public Rect getBitmapBounds(InputStream is, boolean isClose) { 133 | Rect bounds = new Rect(); 134 | 135 | try { 136 | BitmapFactory.Options options = new BitmapFactory.Options(); 137 | options.inJustDecodeBounds = true; 138 | BitmapFactory.decodeStream(is, null, options); 139 | 140 | bounds.right = options.outWidth; 141 | bounds.bottom = options.outHeight; 142 | Log.i(TAG, "options.outWidth="+options.outWidth+" , "+"options.outHeight="+options.outHeight); 143 | } catch (Exception e) { 144 | e.printStackTrace(); 145 | } finally { 146 | if( isClose ) 147 | closeStream(is); 148 | } 149 | 150 | return bounds; 151 | } 152 | 153 | private int getOrientation(Uri uri) { 154 | int orientation = 0; 155 | Cursor cursor = null; 156 | try { 157 | cursor = context.getContentResolver().query(uri, IMAGE_PROJECTION, null, null, null); 158 | if ((cursor != null) && cursor.moveToNext()) { 159 | orientation = cursor.getInt(INDEX_ORIENTATION); 160 | } 161 | } catch (Exception e) { 162 | // Ignore error for no orientation column; just use the default orientation value 0. 163 | } finally { 164 | if (cursor != null) { 165 | cursor.close(); 166 | } 167 | } 168 | return orientation; 169 | } 170 | 171 | /** 172 | * Decodes bitmap (maybe immutable) that keeps aspect-ratio and spans most within the bounds. 173 | */ 174 | public Bitmap decodeBitmapByStream(InputStream is, Rect bounds, int width, int height) { 175 | Log.i(TAG, "width = " + width + " , " + "height = " + height); 176 | Bitmap bitmap = null; 177 | try { 178 | // TODO: Take max pixels allowed into account for calculation to avoid possible OOM. 179 | // Rect bounds = getBitmapBounds(is, false); 180 | int sampleSize = Math.max(bounds.width() / width, bounds.height() / height); 181 | sampleSize = Math.min(sampleSize, 182 | Math.max(bounds.width() / height, bounds.height() / width)); 183 | 184 | BitmapFactory.Options options = new BitmapFactory.Options(); 185 | options.inSampleSize = Math.max(sampleSize, 1); 186 | options.inPreferredConfig = Bitmap.Config.ARGB_8888; 187 | 188 | Log.i(TAG, "sampleSize = " + sampleSize + " , " + "options.inSampleSize = " + options.inSampleSize); 189 | bitmap = BitmapFactory.decodeStream(is, null, options);//!!!!溢出 190 | } catch (Exception e) { 191 | Log.e(TAG, e.getMessage()); 192 | } finally { 193 | closeStream(is); 194 | } 195 | 196 | // Ensure bitmap in 8888 format, good for editing as well as GL compatible. 197 | if ((bitmap != null) && (bitmap.getConfig() != Bitmap.Config.ARGB_8888)) { 198 | Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true); 199 | bitmap.recycle(); 200 | bitmap = copy; 201 | } 202 | 203 | if (bitmap != null) { 204 | // Scale down the sampled bitmap if it's still larger than the desired dimension. 205 | float scale = Math.min((float) width / bitmap.getWidth(), 206 | (float) height / bitmap.getHeight()); 207 | scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(), 208 | (float) width / bitmap.getHeight())); 209 | if (scale < 1) { 210 | Matrix m = new Matrix(); 211 | m.setScale(scale, scale); 212 | Bitmap transformed = createBitmap( 213 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m); 214 | bitmap.recycle(); 215 | return transformed; 216 | } 217 | } 218 | return bitmap; 219 | } 220 | 221 | /** 222 | * Decodes bitmap (maybe immutable) that keeps aspect-ratio and spans most within the bounds. 223 | */ 224 | public Bitmap decodeBitmap(byte[] data, int width, int height){ 225 | Log.i(TAG, "width = " + width + " , " + "height = " + height); 226 | Bitmap bitmap = null; 227 | try { 228 | // TODO: Take max pixels allowed into account for calculation to avoid possible OOM. 229 | Rect bounds = getBitmapBounds(data); 230 | int sampleSize = Math.max(bounds.width() / width, bounds.height() / height); 231 | sampleSize = Math.min(sampleSize, 232 | Math.max(bounds.width() / height, bounds.height() / width)); 233 | 234 | BitmapFactory.Options options = new BitmapFactory.Options(); 235 | options.inSampleSize = Math.max(sampleSize, 1); 236 | options.inPreferredConfig = Bitmap.Config.ARGB_8888; 237 | 238 | Log.i(TAG, "sampleSize = " + sampleSize + " , " + "options.inSampleSize = " + options.inSampleSize); 239 | bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);//!!!!溢出 240 | } catch (Exception e) { 241 | Log.e(TAG, e.getMessage()); 242 | } finally { 243 | data = null; 244 | } 245 | 246 | // Ensure bitmap in 8888 format, good for editing as well as GL compatible. 247 | if ((bitmap != null) && (bitmap.getConfig() != Bitmap.Config.ARGB_8888)) { 248 | Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true); 249 | bitmap.recycle(); 250 | bitmap = copy; 251 | } 252 | 253 | if (bitmap != null) { 254 | // Scale down the sampled bitmap if it's still larger than the desired dimension. 255 | float scale = Math.min((float) width / bitmap.getWidth(), 256 | (float) height / bitmap.getHeight()); 257 | scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(), 258 | (float) width / bitmap.getHeight())); 259 | if (scale < 1) { 260 | Matrix m = new Matrix(); 261 | m.setScale(scale, scale); 262 | Bitmap transformed = createBitmap( 263 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m); 264 | bitmap.recycle(); 265 | return transformed; 266 | } 267 | } 268 | return bitmap; 269 | } 270 | 271 | /** 272 | * Decodes bitmap (maybe immutable) that keeps aspect-ratio and spans most within the bounds. 273 | */ 274 | private Bitmap decodeBitmap(Uri uri, int width, int height) { 275 | Log.i(TAG, "width = " + width + " , " + "height = " + height); 276 | InputStream is = null; 277 | Bitmap bitmap = null; 278 | 279 | try { 280 | // TODO: Take max pixels allowed into account for calculation to avoid possible OOM. 281 | Rect bounds = getBitmapBounds(uri); 282 | int sampleSize = Math.max(bounds.width() / width, bounds.height() / height); 283 | sampleSize = Math.min(sampleSize, 284 | Math.max(bounds.width() / height, bounds.height() / width)); 285 | 286 | BitmapFactory.Options options = new BitmapFactory.Options(); 287 | options.inSampleSize = Math.max(sampleSize, 1); 288 | options.inPreferredConfig = Bitmap.Config.ARGB_8888; 289 | 290 | is = context.getContentResolver().openInputStream(uri); 291 | Log.i(TAG, "sampleSize = " + sampleSize + " , " + "options.inSampleSize = " + options.inSampleSize); 292 | bitmap = BitmapFactory.decodeStream(is, null, options);//!!!!溢出 293 | } catch (Exception e) { 294 | } finally { 295 | closeStream(is); 296 | } 297 | 298 | // Ensure bitmap in 8888 format, good for editing as well as GL compatible. 299 | if ((bitmap != null) && (bitmap.getConfig() != Bitmap.Config.ARGB_8888)) { 300 | Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true); 301 | bitmap.recycle(); 302 | bitmap = copy; 303 | } 304 | 305 | if (bitmap != null) { 306 | // Scale down the sampled bitmap if it's still larger than the desired dimension. 307 | float scale = Math.min((float) width / bitmap.getWidth(), 308 | (float) height / bitmap.getHeight()); 309 | scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(), 310 | (float) width / bitmap.getHeight())); 311 | if (scale < 1) { 312 | Matrix m = new Matrix(); 313 | m.setScale(scale, scale); 314 | Bitmap transformed = createBitmap( 315 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m); 316 | bitmap.recycle(); 317 | return transformed; 318 | } 319 | } 320 | return bitmap; 321 | } 322 | 323 | public Bitmap transform(Bitmap bitmap, int width, int height){ 324 | // Scale down the sampled bitmap if it's still larger than the desired dimension. 325 | float scale = Math.min((float) width / bitmap.getWidth(), 326 | (float) height / bitmap.getHeight()); 327 | scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(), 328 | (float) width / bitmap.getHeight())); 329 | if (scale < 1) { 330 | Matrix m = new Matrix(); 331 | m.setScale(scale, scale); 332 | Bitmap transformed = createBitmap( 333 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m); 334 | bitmap.recycle(); 335 | return transformed; 336 | } 337 | return bitmap; 338 | } 339 | 340 | /** 341 | * Gets decoded bitmap that keeps orientation as well. 342 | */ 343 | public Bitmap getBitmap(Uri uri, int width, int height) { 344 | Bitmap bitmap = decodeBitmap(uri, width, height); 345 | 346 | // Rotate the decoded bitmap according to its orientation if it's necessary. 347 | if (bitmap != null) { 348 | int orientation = getOrientation(uri); 349 | if (orientation != 0) { 350 | Matrix m = new Matrix(); 351 | m.setRotate(orientation); 352 | Bitmap transformed = createBitmap( 353 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m); 354 | bitmap.recycle(); 355 | return transformed; 356 | } 357 | } 358 | return bitmap; 359 | } 360 | 361 | /** 362 | * Saves the bitmap by given directory, filename, and format; if the directory is given null, 363 | * then saves it under the cache directory. 364 | */ 365 | public File saveBitmap( 366 | Bitmap bitmap, String directory, String filename, CompressFormat format) { 367 | 368 | if (directory == null) { 369 | directory = context.getCacheDir().getAbsolutePath(); 370 | } else { 371 | // Check if the given directory exists or try to create it. 372 | File file = new File(directory); 373 | if (!file.isDirectory() && !file.mkdirs()) { 374 | return null; 375 | } 376 | } 377 | 378 | File file = null; 379 | OutputStream os = null; 380 | 381 | try { 382 | filename = (format == CompressFormat.PNG) ? filename + ".png" : filename + ".jpg"; 383 | file = new File(directory, filename); 384 | os = new FileOutputStream(file); 385 | bitmap.compress(format, DEFAULT_COMPRESS_QUALITY, os); 386 | } catch (FileNotFoundException e) { 387 | e.printStackTrace(); 388 | } finally { 389 | closeStream(os); 390 | } 391 | return file; 392 | } 393 | 394 | /** 395 | * 缩放bitmap 396 | * @param bitmap 397 | * @param w 398 | * @param h 399 | * @return 400 | */ 401 | public static Bitmap zoomBitmap(Bitmap bitmap, float w, float h){ 402 | int width = bitmap.getWidth(); 403 | int height = bitmap.getHeight(); 404 | Matrix matrix = new Matrix(); 405 | float scaleW = ((float)w / width); 406 | float scaleH = ((float)h / height); 407 | matrix.postScale(scaleW, scaleH); 408 | Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); 409 | 410 | return newBmp; 411 | } 412 | 413 | public static Bitmap rotateBitmap(Bitmap bitmap, float degrees){ 414 | if (degrees != 0 && bitmap != null) { 415 | Matrix m = new Matrix(); 416 | m.postRotate(degrees); 417 | // m.setRotate(degrees, 418 | // (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2); 419 | try { 420 | bitmap = Bitmap.createBitmap( 421 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true); 422 | // if (bitmap != b2) { 423 | // bitmap.recycle(); //Android开发网再次提示Bitmap操作完应该显示的释放 424 | // bitmap = b2; 425 | // } 426 | } catch (OutOfMemoryError ex) { 427 | // Android建议大家如果出现了内存不足异常,最好return 原始的bitmap对象。. 428 | } 429 | } 430 | return bitmap; 431 | } 432 | 433 | public static Bitmap drawTextToBitmap(Context gContext, int gResId, String gText) { 434 | Log.i(TAG, "drawTextToBitmap = " + gText); 435 | Resources resources = gContext.getResources(); 436 | float scale = resources.getDisplayMetrics().density; 437 | Bitmap bitmap = 438 | BitmapFactory.decodeResource(resources, gResId); 439 | 440 | Bitmap.Config bitmapConfig = 441 | bitmap.getConfig(); 442 | // set default bitmap config if none 443 | if(bitmapConfig == null) { 444 | bitmapConfig = Bitmap.Config.ARGB_8888; 445 | } 446 | // resource bitmaps are imutable, 447 | // so we need to convert it to mutable one 448 | bitmap = bitmap.copy(bitmapConfig, true); 449 | 450 | Canvas canvas = new Canvas(bitmap); 451 | // new antialised Paint 452 | Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 453 | // text color - #3D3D3D 454 | paint.setColor(Color.WHITE); 455 | // text size in pixels 456 | paint.setTextSize((int) (12 * scale)); 457 | // text shadow 458 | // paint.setShadowLayer(1f, 0f, 1f, Color.WHITE); 459 | 460 | // draw text to the Canvas center 461 | Rect bounds = new Rect(); 462 | paint.getTextBounds(gText, 0, gText.length(), bounds); 463 | int x = (bitmap.getWidth() - bounds.width())/2; 464 | int y = (bitmap.getHeight())/2 + (int)scale*2; 465 | 466 | canvas.drawText(gText, x, y, paint); 467 | 468 | canvas.save(); 469 | canvas.restore(); 470 | 471 | return bitmap; 472 | } 473 | 474 | } 475 | -------------------------------------------------------------------------------- /android/src/main/java/com/sunmi/v2/printer/BytesUtil.java: -------------------------------------------------------------------------------- 1 | package com.sunmi.v2.printer; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.util.Hashtable; 5 | 6 | import com.google.zxing.BarcodeFormat; 7 | import com.google.zxing.EncodeHintType; 8 | import com.google.zxing.WriterException; 9 | import com.google.zxing.common.BitMatrix; 10 | import com.google.zxing.qrcode.QRCodeWriter; 11 | 12 | import android.annotation.SuppressLint; 13 | import android.graphics.Bitmap; 14 | 15 | public class BytesUtil { 16 | /** 17 | * 生成间断性黑块数据 18 | * @param w : 打印纸宽度, 单位点 19 | * @return 20 | */ 21 | public static byte[] initBlackBlock(int w){ 22 | int ww = (w + 7)/8 ; 23 | int n = (ww + 11)/12; 24 | int hh = n * 24; 25 | byte[] data = new byte[ hh * ww + 10]; 26 | 27 | data[0] = 0x0A; 28 | data[1] = 0x1D; 29 | data[2] = 0x76; 30 | data[3] = 0x30; 31 | data[4] = 0x00; 32 | 33 | data[5] = (byte)ww;//xL 34 | data[6] = (byte)(ww >> 8);//xH 35 | data[7] = (byte)hh; 36 | data[8] = (byte)(hh >> 8); 37 | 38 | int k = 9; 39 | for(int i=0; i < n; i++){ 40 | for(int j=0; j<24; j++){ 41 | for(int m =0; m> 8);//xH 71 | data[6] = (byte)hh; 72 | data[7] = (byte)(hh >> 8); 73 | 74 | int k = 8; 75 | for(int i=0; i> 8);//xH 101 | data[6] = (byte)hh; 102 | data[7] = (byte)(hh >> 8); 103 | 104 | int k = 8; 105 | byte m =(byte)0xAA; 106 | for(int i=0; i> 8);//xH 137 | data[7] = (byte)hh; 138 | data[8] = (byte)(hh >> 8); 139 | 140 | int k = 9; 141 | int m = 31; 142 | for(int i=0; i> 8); 264 | return returnText; 265 | } 266 | 267 | public static byte[] PrintBarcode(String stBarcode) { 268 | int iLength = stBarcode.length() + 4; 269 | byte[] returnText = new byte[iLength]; 270 | 271 | returnText[0] = 0x1D; 272 | returnText[1] = 'k'; 273 | returnText[2] = 0x45; 274 | returnText[3] = (byte) stBarcode.length(); // 条码长度; 275 | 276 | System.arraycopy(stBarcode.getBytes(), 0, returnText, 4, 277 | stBarcode.getBytes().length); 278 | 279 | return returnText; 280 | } 281 | 282 | public static byte[] CutPaper() { 283 | byte[] returnText = {0x20,0x0A, 0x1D, 0x56, 0x42, 0x00 }; // 切纸; GS V 284 | // 66D 0D 285 | return returnText; 286 | } 287 | 288 | public static byte[] selfCheck(){ 289 | byte[] returnText = {0x1F, 0x1B, 0x1F, 0x53}; 290 | return returnText; 291 | } 292 | 293 | public static byte[] getPrinterStatus(){ 294 | byte[] data = {0x0A,0x10,0x04,0x01}; 295 | return data; 296 | } 297 | 298 | 299 | public static String getHexStringFromBytes(byte[] data){ 300 | if(data == null || data.length <= 0){ 301 | return null; 302 | } 303 | String hexString = "0123456789ABCDEF"; 304 | int size = data.length * 2; 305 | StringBuilder sb = new StringBuilder(size); 306 | for (int i = 0; i < data.length; i++) { 307 | sb.append(hexString.charAt((data[i] & 0xF0) >> 4)); 308 | sb.append(hexString.charAt((data[i] & 0x0F) >> 0)); 309 | } 310 | return sb.toString(); 311 | } 312 | 313 | //一个二维码 314 | /** 315 | * 打印二维码 316 | * @param code: 二维码数据 317 | * @param modulesize: 二维码块大小(单位:点, 取值 1 至 16 ) 318 | * @param errorlevel: 二维码纠错等级(0 至 3) 319 | * 0 -- 纠错级别L ( 7%) 320 | * 1 -- 纠错级别M (15%) 321 | * 2 -- 纠错级别Q (25%) 322 | * 3 -- 纠错级别H (30%) 323 | */ 324 | 325 | public static byte[] getPrintQRCode(String code, int modulesize, int errorlevel){ 326 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 327 | try{ 328 | buffer.write(setQRCodeSize(modulesize)); 329 | buffer.write(setQRCodeErrorLevel(errorlevel)); 330 | buffer.write(getQCodeBytes(code)); 331 | buffer.write(getBytesForPrintQRCode(true)); 332 | }catch(Exception e){ 333 | e.printStackTrace(); 334 | } 335 | return buffer.toByteArray(); 336 | } 337 | 338 | //两个二维码 339 | /** 340 | * 打印二维码 341 | * @param code1: 二维码数据 342 | * @param code2: 二维码数据 343 | * @param modulesize: 二维码块大小(单位:点, 取值 1 至 16 ) 344 | * @param errorlevel: 二维码纠错等级(0 至 3) 345 | * 0 -- 纠错级别L ( 7%) 346 | * 1 -- 纠错级别M (15%) 347 | * 2 -- 纠错级别Q (25%) 348 | * 3 -- 纠错级别H (30%) 349 | */ 350 | 351 | public static byte[] getPrintDoubleQRCode(String code1, String code2, int modulesize, int errorlevel){ 352 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 353 | try{ 354 | buffer.write(setQRCodeSize(modulesize)); 355 | buffer.write(setQRCodeErrorLevel(errorlevel)); 356 | buffer.write(getQCodeBytes(code1)); 357 | buffer.write(getBytesForPrintQRCode(false)); 358 | buffer.write(getQCodeBytes(code2)); 359 | 360 | //加入横向间隔 361 | buffer.write(new byte[]{0x1B, 0x5C, 0x30, 0x00}); 362 | 363 | buffer.write(getBytesForPrintQRCode(true)); 364 | }catch(Exception e){ 365 | e.printStackTrace(); 366 | } 367 | return buffer.toByteArray(); 368 | } 369 | 370 | //一维码 371 | public static byte[] getPrintBarCode(String data, int symbology, 372 | int height, int width, int textposition){ 373 | 374 | if(symbology < 0 || symbology > 8){ 375 | return new byte[]{0x0A}; 376 | } 377 | if(width < 2 || width > 6){ 378 | width = 2; 379 | } 380 | if(textposition <0 || textposition > 3){ 381 | textposition = 0; 382 | } 383 | if(height < 1 || height>255){ 384 | height = 162; 385 | } 386 | 387 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 388 | try{ 389 | buffer.write(new byte[]{0x1D,0x66,0x01,0x1D,0x48,(byte)textposition, 390 | 0x1D,0x77,(byte)width,0x1D,0x68,(byte)height,0x0A}); 391 | 392 | byte[] barcode = data.getBytes(); 393 | 394 | if(symbology == 8){ 395 | buffer.write(new byte[]{0x1D,0x6B,0x49,(byte)(barcode.length+2),0x7B,0x42}); 396 | }else{ 397 | buffer.write(new byte[]{0x1D,0x6B,(byte)(symbology + 0x41),(byte)barcode.length}); 398 | } 399 | 400 | buffer.write(barcode); 401 | 402 | }catch(Exception e){ 403 | e.printStackTrace(); 404 | } 405 | return buffer.toByteArray(); 406 | } 407 | 408 | //////////////////////////////////////////////////////////////////////////////////// 409 | ////////////////////////// private ///////////////////////// 410 | //////////////////////////////////////////////////////////////////////////////////// 411 | 412 | 413 | private static byte[] setQRCodeSize(int modulesize){ 414 | //二维码块大小设置指令 415 | byte[] dtmp = new byte[8]; 416 | dtmp[0] = 0x1D; 417 | dtmp[1] = 0x28; 418 | dtmp[2] = 0x6B; 419 | dtmp[3] = 0x03; 420 | dtmp[4] = 0x00; 421 | dtmp[5] = 0x31; 422 | dtmp[6] = 0x43; 423 | dtmp[7] = (byte)modulesize; 424 | return dtmp; 425 | } 426 | private static byte[] setQRCodeErrorLevel(int errorlevel){ 427 | //二维码纠错等级设置指令 428 | byte[] dtmp = new byte[8]; 429 | dtmp[0] = 0x1D; 430 | dtmp[1] = 0x28; 431 | dtmp[2] = 0x6B; 432 | dtmp[3] = 0x03; 433 | dtmp[4] = 0x00; 434 | dtmp[5] = 0x31; 435 | dtmp[6] = 0x45; 436 | dtmp[7] = (byte)(48+errorlevel); 437 | return dtmp; 438 | } 439 | private static byte[] getBytesForPrintQRCode(boolean single){ 440 | //打印已存入数据的二维码 441 | byte[] dtmp; 442 | if(single){ //同一行只打印一个QRCode, 后面加换行 443 | dtmp = new byte[9]; 444 | dtmp[8] = 0x0A; 445 | }else{ 446 | dtmp = new byte[8]; 447 | } 448 | dtmp[0] = 0x1D; 449 | dtmp[1] = 0x28; 450 | dtmp[2] = 0x6B; 451 | dtmp[3] = 0x03; 452 | dtmp[4] = 0x00; 453 | dtmp[5] = 0x31; 454 | dtmp[6] = 0x51; 455 | dtmp[7] = 0x30; 456 | return dtmp; 457 | } 458 | private static byte[] getQCodeBytes(String code){ 459 | //二维码存入指令 460 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 461 | try{ 462 | byte[] d = getGbk(code); 463 | int len = d.length + 3; 464 | if(len > 7092)len = 7092; 465 | buffer.write((byte)0x1D); 466 | buffer.write((byte)0x28); 467 | buffer.write((byte)0x6B); 468 | buffer.write((byte)len); 469 | buffer.write((byte)(len >> 8)); 470 | buffer.write((byte)0x31); 471 | buffer.write((byte)0x50); 472 | buffer.write((byte)0x30); 473 | for(int i=0; i> 8);//xH 596 | data[6] = 13; //高度13 597 | data[7] = 0; 598 | 599 | int k = 8; 600 | for(int i=0; i < 3 * ww; i++){ 601 | data[k++] = 0; 602 | } 603 | for(int i=0; i < ww; i++){ 604 | data[k++] = kk[type][0]; 605 | } 606 | for(int i=0; i < ww; i++){ 607 | data[k++] = kk[type][1]; 608 | } 609 | for(int i=0; i < ww; i++){ 610 | data[k++] = kk[type][2]; 611 | } 612 | for(int i=0; i < ww; i++){ 613 | data[k++] = kk[type][3]; 614 | } 615 | for(int i=0; i < ww; i++){ 616 | data[k++] = kk[type][4]; 617 | } 618 | for(int i=0; i < ww; i++){ 619 | data[k++] = kk[type][5]; 620 | } 621 | for(int i=0; i < ww; i++){ 622 | data[k++] = kk[type][6]; 623 | } 624 | for(int i=0; i < 3 * ww; i++){ 625 | data[k++] = 0; 626 | } 627 | return data; 628 | } 629 | public static byte[] initLine2(int w){ 630 | int ww = (w + 7)/8; 631 | 632 | byte[] data = new byte[ 12 * ww + 8]; 633 | 634 | data[0] = 0x1D; 635 | data[1] = 0x76; 636 | data[2] = 0x30; 637 | data[3] = 0x00; 638 | 639 | data[4] = (byte)ww;//xL 640 | data[5] = (byte)(ww >> 8);//xH 641 | data[6] = 12; //高度13 642 | data[7] = 0; 643 | 644 | int k = 8; 645 | for(int i=0; i < 5 * ww; i++){ 646 | data[k++] = 0; 647 | } 648 | for(int i=0; i < ww; i++){ 649 | data[k++] = 0x7f; 650 | } 651 | for(int i=0; i < ww; i++){ 652 | data[k++] = 0x7f; 653 | } 654 | for(int i=0; i < 5 * ww; i++){ 655 | data[k++] = 0; 656 | } 657 | return data; 658 | } 659 | 660 | public static Bitmap getLineBitmapFromData(int size, int width){ 661 | int[] pixels = createLineData(size, width); 662 | return getBitmapFromData(pixels, width, size + 6); 663 | } 664 | private static byte getBitMatrixColor(BitMatrix bits, int x, int y){ 665 | int width = bits.getWidth(); 666 | int height = bits.getHeight(); 667 | if( x >= width || y >= height || x < 0 || y < 0)return 0; 668 | if(bits.get(x, y)){ 669 | return 1; 670 | }else{ 671 | return 0; 672 | } 673 | } 674 | 675 | public static byte[] getBytesFromBitMatrix(BitMatrix bits){ 676 | if(bits == null)return null; 677 | 678 | int h = bits.getHeight(); 679 | int w = (bits.getWidth()+7) / 8; 680 | byte[] rv = new byte[h * w + 8]; 681 | 682 | rv[0] = 0x1D; 683 | rv[1] = 0x76; 684 | rv[2] = 0x30; 685 | rv[3] = 0x00; 686 | 687 | rv[4] = (byte)w;//xL 688 | rv[5] = (byte)(w >> 8);//xH 689 | rv[6] = (byte)h; 690 | rv[7] = (byte)(h >> 8); 691 | 692 | int k = 8; 693 | for(int i=0; i hints = new Hashtable(); 708 | hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); 709 | //图像数据转换,使用了矩阵转换 710 | BitMatrix bitMatrix = new QRCodeWriter().encode(data, BarcodeFormat.QR_CODE, size, size, hints); 711 | System.out.println("bitmatrix height:" + bitMatrix.getHeight() + " width:" + bitMatrix.getWidth()); 712 | return getBytesFromBitMatrix(bitMatrix); 713 | } catch (WriterException e) { 714 | // TODO Auto-generated catch block 715 | e.printStackTrace(); 716 | } 717 | return null; 718 | } 719 | 720 | public static byte[] getHorizontalLine(int w, int size, int type){ 721 | int ww = (w + 7)/8; 722 | int hh = size + 6; 723 | 724 | byte kk = (byte)0xff; //实线 725 | 726 | if(type == 0){ 727 | kk = 0x3f; //虚线 728 | } 729 | 730 | byte[] data = new byte[ hh * ww + 8]; 731 | 732 | data[0] = 0x1D; 733 | data[1] = 0x76; 734 | data[2] = 0x30; 735 | data[3] = 0x00; 736 | 737 | data[4] = (byte)ww;//xL 738 | data[5] = (byte)(ww >> 8);//xH 739 | data[6] = (byte)hh; //高度 740 | data[7] = (byte)(hh >> 8); 741 | 742 | int k = 8; 743 | for(int i=0; i < 3 * ww; i++){ 744 | data[k++] = 0; 745 | } 746 | for(int j=0; j < size; j++){ 747 | for(int i=0; i < ww; i++){ 748 | data[k++] = kk; 749 | } 750 | } 751 | for(int i=0; i < 3 * ww; i++){ 752 | data[k++] = 0; 753 | } 754 | return data; 755 | } 756 | } 757 | -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import { 3 | Text, 4 | View, 5 | StyleSheet, 6 | TouchableOpacity, 7 | DeviceEventEmitter, 8 | } from 'react-native'; 9 | 10 | import SunmiV2Printer from 'react-native-sunmi-v2-printer'; 11 | 12 | const App = () => { 13 | const [status, setStatus] = useState(''); 14 | 15 | useEffect(() => { 16 | listener = null; 17 | 18 | try { 19 | listener = DeviceEventEmitter.addListener('PrinterStatus', (action) => { 20 | switch (action) { 21 | case SunmiV2Printer.Constants.NORMAL_ACTION: 22 | setStatus(() => 'printer normal'); 23 | break; 24 | case SunmiV2Printer.Constants.OUT_OF_PAPER_ACTION: 25 | setStatus(() => 'printer out out page'); 26 | break; 27 | case SunmiV2Printer.Constants.COVER_OPEN_ACTION: 28 | setStatus(() => 'printer cover open'); 29 | break; 30 | default: 31 | setStatus(() => 'printer status:' + action); 32 | } 33 | }); 34 | } catch (e) { 35 | console.log(e); 36 | } 37 | 38 | return () => { 39 | if (listener) { 40 | listener.remove(); 41 | } 42 | }; 43 | }, []); 44 | 45 | const print = async () => { 46 | let logo = 47 | 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAQCAwMDAgQDAwMEBAQEBQkGBQUFBQsICAYJDQsNDQ0LDAwOEBQRDg8TDwwMEhgSExUWFxcXDhEZGxkWGhQWFxb/2wBDAQQEBAUFBQoGBgoWDwwPFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhb/wAARCALRBAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD7+oooqQCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKMiis7xTrOneHvD15rer3KW1lYwtNPK54VR/nH40AaNAOa8j+G37Rfw38WaidJuNQl0DVlIH2HWI/s8jZ+6VLcNkc8V61G6SIHRgysMgg5BFADqKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArwr4t69YeNvGl5pt9dmHwL4D/07xJOeI7+6QbktA3cLwzDuSBXa/Hnxbf6LpFr4c8NIZvE/iSQ2mmIv/LuCPnuH9FjXJz64r41/bR8bafoWi2XwM8F3TXFjpji4169V90mo3rH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UAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUmMjB5paKAOI+JPwk+HnjyFl8TeFrC7lwQtyIgsqe4cc5r59+JH7DXh66LXHgjxTd6awB22t8vnIT2zJ94D8K+uaKAPzQ8f/sqfGXwwWePQodat1zmbTZQwA9drYb9K8l1/Qtb0KZo9b0a/05kbawu7Z4wD9WAzX7E1Q1nRtI1eExarpdneoRgrc26SDH/AgaAPx2UgjIII9qK/T/xl+zX8GPEkjTXfgq0t7hhjz7Rmib8gcfpXlnir9hrwRcmR/D/ijV9PZslVuNsyKfwC8UAfCdFfUviL9hvx1ahzovivStRIztE0bQZ9M/exXBa/+yl8btKjZj4Zhvtg+7Y3SyFvpnGaAPF6K6zXPhb8SdFDf2r4G1y1Cfe3WpOMdema5mWyv4jiXT72P2e2kX+YoAhopshER/eZjz03Db/Ok82LGfNTH+8KAH0UzzY/+eif99ClR0d9qMHP91Tk/pQA6ipYrS9l/wBVYXkuf+eds7fyFdX4c+E/xO14qdH8C63dBzwVtio+vzYoA4+ivoLwZ+xz8XtYmRtUi03RrduWee43yL/wAD+te1fDr9h/wdpzxXHi/wAQ32sypy0NuBBC59COSR+VAHwvYW11f3q2djbTXVw/3IYIy7t9FHNe5/B/9k34o+M2iutVtY/DWmsQTNff610PdIxzn/exX3p8Pfhn4C8DWkdv4W8K6dpwjACyJCGk/wC+2y3611tMDxb4G/sx/Db4cCK9+wnXNXjwft+ooG2sO6R8qn617OqqihVAAHAAGAKdRSAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKo6ho+k35JvdMs7gnkmaBXP6ir1FFwOW1D4b/AA9vipvfBHh+4KZ2+bpsTYz1xlaypfgr8JpJWkb4faBuY5O2xQD8gOK76igDgD8EPhIf+afaF/4CLWjYfC74bWUqyWngPw7DIq7Q6aZEGx9dua66igDO0zQtE04g6fpFjalehht0TH5CtGiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKGAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//Z'; 48 | //ensure the base64 string without URI Scheme 49 | let logobase64 = logo.replace('data:image/jpeg;base64,', ''); 50 | let orderList = [ 51 | ['2020-11-25 15:00:00', '', ''], 52 | ['Some item x 1', '', ''], 53 | ['', '', '$100'], 54 | ['2020-11-25 15:00:00', '', ''], 55 | ['Some item x 1', '', ''], 56 | ['', '', '$100'], 57 | ]; 58 | let columnAliment = [0, 1, 2]; 59 | let columnWidth = [25, 1, 5]; 60 | try { 61 | //set aligment: 0-left,1-center,2-right 62 | await SunmiV2Printer.setAlignment(1); 63 | 64 | //图片bitmap对象(最大宽度384像素,超过无法打印并且回调callback异常函数) 65 | await SunmiV2Printer.printBitmap( 66 | logobase64, 67 | 384 /*width*/, 68 | 380 /*height*/, 69 | ); 70 | //SunmiV2Printer.commitPrinterBuffer(); 71 | 72 | await SunmiV2Printer.setFontSize(40); 73 | await SunmiV2Printer.printOriginalText('Title name\n'); 74 | await SunmiV2Printer.setFontSize(50); 75 | await SunmiV2Printer.printOriginalText('Subtitle name\n'); 76 | await SunmiV2Printer.setFontSize(20); 77 | await SunmiV2Printer.setAlignment(0); 78 | await SunmiV2Printer.printOriginalText('Receipt ID: 1234567890\n'); 79 | await SunmiV2Printer.printOriginalText(`Date: 2020-11-25 15:00:00\n`); 80 | await SunmiV2Printer.printOriginalText( 81 | '===============================\n', 82 | ); 83 | await SunmiV2Printer.setFontSize(22); 84 | for (var i in orderList) { 85 | console.log(orderList[i]); 86 | console.log(columnWidth); 87 | console.log(columnAliment); 88 | await SunmiV2Printer.printColumnsText( 89 | orderList[i], 90 | columnWidth, 91 | columnAliment, 92 | ); 93 | } 94 | await SunmiV2Printer.setFontSize(20); 95 | await SunmiV2Printer.printOriginalText( 96 | '===============================\n', 97 | ); 98 | await SunmiV2Printer.setAlignment(2); 99 | await SunmiV2Printer.setFontSize(30); 100 | await SunmiV2Printer.printOriginalText('Total: $200\n'); 101 | await SunmiV2Printer.setFontSize(20); 102 | await SunmiV2Printer.printOriginalText( 103 | '===============================\n', 104 | ); 105 | await SunmiV2Printer.printOriginalText('\n\n'); 106 | } catch (e) { 107 | console.log(e); 108 | } 109 | }; 110 | 111 | return ( 112 | 113 | {`Printer Status: ${status}`} 114 | print()}> 115 | Print 116 | 117 | 118 | ); 119 | }; 120 | 121 | const styles = StyleSheet.create({ 122 | container: { 123 | flex: 1, 124 | justifyContent: 'center', 125 | alignItems: 'center', 126 | }, 127 | button: { 128 | marginTop: 50, 129 | paddingHorizontal: 50, 130 | paddingVertical: 10, 131 | borderRadius: 10, 132 | backgroundColor: 'steelblue', 133 | }, 134 | buttonText: { 135 | color: 'white', 136 | }, 137 | }); 138 | 139 | export default App; 140 | --------------------------------------------------------------------------------