Commit 240ca20c authored by 张圣龙's avatar 张圣龙

Merge branch 'master' of https://git.zmcms.cn/wangwei/widget

parents 5310ed4d a74d216d
......@@ -9,3 +9,4 @@
/.idea
/.gradle
/fetch/build
\ No newline at end of file
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 29
......@@ -26,6 +25,16 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
configurations.all {
resolutionStrategy {
force 'androidx.core:core-ktx:1.6.0'
//force 'com.github.bumptech.glide:compiler:4.2.0'
}
}
buildFeatures {
viewBinding true
}
packagingOptions {
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libmarsxlog.so'
......@@ -38,24 +47,31 @@ dependencies {
implementation 'com.hyman:flowlayout-lib:1.1.2'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation project(path: ':KeyBoardView')
implementation project(path: ':log')
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation project(path: ':widgetlib')
// implementation 'com.qmai.android.sdk:widgetlib:1.1.2-SNAPSHOT'
implementation project(path: ':tools')
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.github.sebdomdev:android-drawer-sheet:1.0'
implementation 'com.lxj:xpopup:2.0.6-rc1'
implementation 'com.google.android.material:material:1.3.0-alpha01'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation project(path: ':printer')
// implementation 'com.android.tools.build:gradle:3.1.1'
// implementation 'com.android.tools.lint:lint-gradle:26.1.1'
implementation 'com.guolindev.permissionx:permissionx:1.5.1'
implementation 'com.eclipsesource.j2v8:j2v8:6.2.0@aar'
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.qimai.android.widget">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".DrawerActivity"/>
android:theme="@style/AppTheme"
tools:ignore="AllowBackup">
<activity
android:name=".LogActivity"
android:exported="false" />
<activity android:name=".DrawerActivity" />
<activity android:name=".GridDividerActivity" />
<activity android:name=".TipActivity" />
<activity android:name=".CommomPopActivity" />
<activity android:name=".TopBarActivity" />
<activity android:name="com.qimai.android.widget.MainActivity">
</activity>
<activity android:name="BtListActivity"/>
<activity android:name=".MainActivityPtinter">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".UsbActivity"/>
<activity android:name=".BtListActivity" />
<activity android:name=".MainActivityPtinter"></activity>
<activity android:name=".UsbActivity" />
</application>
</manifest>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
function print1(template1,data){
var html = template1
// let encoding = require('./encoding')
// let TextEncoder = encoding.TextEncoder
// let template = require('./art-template')
// android.printString(html)
var render = template.render(html, JSON.parse(data));
// var htmlparser2 = require('./htmlparser2')
var byteArray = [27, 64, 10]
var uint8array = new TextEncoder('gbk', { NONSTANDARD_allowLegacyEncoding: true })
var handler = new htmlparser2.DomHandler(function(error, dom) {
dom.forEach(item => {
if (item.name) {
var children = item.children
if (children && children.length == 1 && children[0].type == 'text') {
// console.log(uint8array.encode('值编号:SDJL1293780123') + '')
byteArray = byteArray.concat(Array.from(uint8array.encode(children[0].data)))
byteArray = byteArray.concat([10])
}
if (children.length > 1) {
children.forEach(child => {
var childChildren = child.children
if (childChildren && childChildren.length == 1 && childChildren[0].type == 'text') {
var childStyle = child.attribs.style
if (childStyle) {
var styleObject = tools.normalizeStyle(childStyle)
if (styleObject['text-align']) {
console.log(tools.fontAlign(styleObject['text-align']))
byteArray = byteArray.concat(tools.fontAlign(styleObject['text-align']))
}
}
byteArray = byteArray.concat(Array.from(uint8array.encode(childChildren[0].data)))
byteArray = byteArray.concat([10])
}
})
}
}
})
byteArray = byteArray.concat([10, 10, 10, 10])
android.print12(byteArray)
// console.log(byteArray + '')
})
var parser = new htmlparser2.Parser(handler)
// var parser = new htmlparser2.Parser({
// onopentag(name, attributes) {
// console.log(name)
// console.log(attributes)
// // html = html + '<' + name + '>'
// // if (name === 'span') {
// // name = 'p'
// // }
// },
// // ontext(text) {
// // console.log("-->", text);
// // // html = html + '{{value}}'
// // },
// // onclosetag(tagname) {
// // // if (tagname === "script") {
// // // console.log("That's it?!");
// // // }
// // // html = html + '</' + tagname + '>'
// // console.log(tagname, '结尾')
// // },
// });
parser.write(render);
parser.end();
// function transform() {
// return render
// }
// console.log(transform())
// let encoding = require('./encoding')
// let TextEncoder = encoding.TextEncoder
// console.log(TextEncoder)
var uint8array = new TextEncoder(
'gbk', { NONSTANDARD_allowLegacyEncoding: true }).encode('值编号:SDJL1293780123');
// function encodeArrayOfStrings(strings) {
// var encoder, encoded, len, i, bytes, view, offset;
// encoder = new TextEncoder();
// encoded = [];
// len = Uint16Array.BYTES_PER_ELEMENT;
// for (i = 0; i < strings.length; i += 1) {
// len += Uint16Array.BYTES_PER_ELEMENT;
// encoded[i] = new TextEncoder().encode(strings[i]);
// len += encoded[i].byteLength;
// }
// bytes = new Uint16Array(len);
// view = new DataView(bytes.buffer);
// offset = 0;
// view.setUint32(offset, strings.length);
// offset += Uint16Array.BYTES_PER_ELEMENT;
// for (i = 0; i < encoded.length; i += 1) {
// len = encoded[i].byteLength;
// view.setUint32(offset, len);
// offset += Uint16Array.BYTES_PER_ELEMENT;
// bytes.set(encoded[i], offset);
// offset += len;
// }
// return bytes;
// }
// var aa = encodeArrayOfStrings('hello world')
return
}
\ No newline at end of file
var INITBYTE = [0x1b, 0x40];
var LEFT_ALIGIN = [0x1b, 97, 0];
var CENTER_ALIGIN = [0x1b, 97, 1];
var RIGHT_ALIGIN = [0x1b, 97, 2];
var FONT_BOLD = [0x1b, 0x45, 0x01];
var FONT_NORMAL = [0x1b, 0x45, 0x00];
var FONT_TYPE_0 = [0x1d, 0x21, 0x00];
var FONT_TYPE_1 = [0x1d, 0x21, 0x10];
var FONT_TYPE_2 = [0x1d, 0x21, 0x01];
var FONT_TYPE_3 = [0x1d, 0x21, 17];
var PRINT_AND_FEED = [10];
var dataArray = [];
var hockeyTeam = {
dataArray1 : [],
templateJson: "",
dataJson: "",
getPrintData:function(){
writeValue("企迈测试");
return stringToByte("企迈测试");
},
};
function print1(){
var arrays2 =
[27, 64, 10, 27, 51, 20, 27, 97, 1, 29, 33, 17, 27, 69, 0, 83, 97, 109, 112, 108,
101, 10, 10, 27, 51, 20, 27, 97, 0, 29, 33, 0, 27, 69, 0, 50, 51, 51, 51, 51, 10,
10, 27, 51, 20, 27, 97, 0, 29, 33, 0, 27, 69, 0, 50, 51, 51, 51, 51, 10, 10, 27,
51, 20, 27, 97, 0, 29, 33, 0, 27, 69, 0, 50, 51, 50, 51, 51, 51, 10, 10, 27, 51,
20, 27, 97, 0, 29, 33, 0, 27, 69, 0, 50, 50, 51, 51, 51, 51, 10, 10, 27, 51, 20,
27, 97, 0, 29, 33, 0, 27, 69, 0, 49, 50, 49, 51, 51, 51, 51, 10, 10, 27, 51, 20,
27, 97, 1, 29, 33, 17, 27, 69, 0, 45, 45, 45, -46, -47, -42, -89, -72, -74, 45, 45, 45, 10,
10, 27, 51, 20, 27, 97, 0, 29, 33, 0, 27, 69, 0, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 10, 10, 27, 51, 20, 27, 97, 0, 29, 33, 1, 27,
69, 0, -78, -30, -54, -44, -55, -52, -58, -73, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 49, 32, 32, 32, 32, 32, 32, 49, 50, 46, 48, 48, 10, 10, 27, 51, 20, 27, 97, 0, 29, 33, 1, 27,
69, 0, -78, -30, -54, -44, -55, -52, -58, -73, 50, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, -95, -66, 42, 50, -95, -65, 32, 32, 32, 32, 32, 49, 53, 46, 48, 48, 10, 10, 27, 51, 20, 27, 97, 0, 29, 33, 0, 27, 69, 0, 46,
46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 10, 10, 27, 51, 20, 27, 97, 0, 29, 33, 0, 27, 69, 0, -74, -87, -75, -91, -70, -59, 58, 70,
68, 49, 50, 52, 81, 52, 52, 51, 52, 50, 50, 10, 10, 27, 51, 20, 27, 97, 0, 29, 33, 0, 27, 69, 0, -48, -69, -48, -69, -69, -35, -71, -53,
-93, -84, -69, -74, -45, -83, -49, -62, -76, -50, -71, -30, -63, -39, 10, 10, 10, 10, 10];
dataArray = write([0x1b, 0x40]);
return dataArray;
}
function stringToByte(str) {
var bytes = new Array();
var len, c;
len = str.length;
for(var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if(c >= 0x010000 && c <= 0x10FF FF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
}
return bytes;
}
function write(value) {
dataArray = dataArray.concat(value);
return dataArray;
}
function writeValue(value){
return write(stringToByte(value));
}
function writeAlign(align) {
switch (align) {
case 0:
write(LEFT_ALIGIN);
break;
case 1:
write(CENTER_ALIGIN);
break;
case 2 :
write(RIGHT_ALIGIN);
break;
}
return dataArray;
}
function writePrintAndLineFeed() {
return write(PRINT_AND_FEED);
}
function writeNewLine(num) {
for(var i=0;i<num;i++) {
write([10]);
}
return dataArray;
}
function writelnLineText(value,aligin) {
writePrintAndLineFeed();
writeAlign(aligin);
writeValue(value);
writeNewLine(1);
return dataArray;
}
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.tools = factory());
}(this, (function () { 'use strict';
var tools = {
// 文字大小
fontSize(size) {
var array = []
switch(size) {
case 'normal':
array = [29, 33, 0]
break
case 'doubleHeight':
array = [29, 33, 1]
break
case 'doubleWidth':
array = [29, 33, 16]
break
case 'double':
array = [29, 33, 17]
break
}
return array
},
// 文字加粗
fontBold() {
return [27, 69, 1]
},
// 文字对齐方式
fontAlign(align) {
var array = []
switch(align) {
case 'left':
array = [27, 97, 48]
break
case 'center':
array = [27, 97, 49]
break
case 'right':
array = [27, 97, 50]
break
}
return array
},
normalizeStyle(styleString) {
let styleArray = styleString.split(';')
let styleObject = {}
styleArray.forEach(item => {
if (item) {
var styleItem = item.split(':')
styleObject[styleItem[0]] = styleItem[1]
}
})
return styleObject
}
}
return tools
})))
\ No newline at end of file
......@@ -3,7 +3,7 @@ package com.qimai.android.widget
import android.app.Application
import zs.qimai.com.printer2.utils.PrintManagerUtils
class App: Application() {
class App : Application() {
override fun onCreate() {
super.onCreate()
PrintManagerUtils.getInstance().init(this)
......
......@@ -71,7 +71,7 @@ class BtListActivity : AppCompatActivity(), View.OnClickListener {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_bt_list)
Log.d(TAG, "onCreate: model= ${Build.MODEL} brand = ${Build.BRAND} ")
PrintManagerUtils.getInstance().getSearchBlueToothList(object : BlueToothSearchCallBack {
PrintManagerUtils.getInstance().getSearchBlueToothList(this,object : BlueToothSearchCallBack {
override fun onRefuseGrantPermission() {
}
......
package com.qimai.android.widget
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.qimai.android.widget.databinding.ActivityLogBinding
import com.qimai.log.QLog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class LogActivity : AppCompatActivity() {
private lateinit var bind: ActivityLogBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bind = ActivityLogBinding.inflate(layoutInflater)
setContentView(bind.root)
init()
}
private fun init() {
bind.btnLog.setOnClickListener { initLog() }
bind.btnWrite.setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) {
QLog.write("123456", QLog.D)
withContext(Dispatchers.Main) {
QLog.write("123456789", QLog.E)
}
}
}
}
/**
* 初始化LogUp
*/
private fun initLog() {
QLog.init(true)
}
}
\ No newline at end of file
......@@ -9,7 +9,6 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.qimai.android.widgetlib.popup.QmTipOverlayDrawable
import com.qimai.android.widgetlib.toast.HolderContext
import com.qimai.log.QmLog
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
......@@ -17,11 +16,7 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
HolderContext.getInstance().register {
this
}
QmLog.d("test1111","111111")
HolderContext.getInstance().register { this }
findViewById<Button>(R.id.commonPop).setOnClickListener {
// ToastUtils.getInstance().showCenter("测试测试")
......@@ -47,22 +42,22 @@ class MainActivity : AppCompatActivity() {
findViewById<TextView>(R.id.drawer).setOnClickListener {
startActivity(Intent(MainActivity@ this, CommomPopActivity::class.java))
}
findViewById<Button>(R.id.tip).setOnClickListener {
startActivity(Intent(MainActivity@ this, TipActivity::class.java))
}
findViewById<Button>(R.id.topbar).setOnClickListener {
startActivity(Intent(MainActivity@ this, TopBarActivity::class.java))
}
findViewById<Button>(R.id.divide).setOnClickListener {
startActivity(Intent(MainActivity@ this, GridDividerActivity::class.java))
}
findViewById<Button>(R.id.btn_printer).setOnClickListener {
startActivity(Intent(this, MainActivityPtinter::class.java))
}
findViewById<Button>(R.id.btn_log).setOnClickListener {
startActivity(Intent(this, LogActivity::class.java))
}
keyboard.btnText = "结账"
keyboard.invalidate()
}
}
package com.qimai.android.widget;
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.hardware.usb.UsbDevice
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.util.Log
import androidx.annotation.RequiresApi
import com.qimai.android.widget.R
import kotlinx.android.synthetic.main.activity_main.*
import androidx.appcompat.app.AppCompatActivity
import com.eclipsesource.v8.JavaVoidCallback
import com.eclipsesource.v8.V8
import com.eclipsesource.v8.V8Array
import com.eclipsesource.v8.V8Object
import kotlinx.android.synthetic.main.activity_main_printer.*
import kotlinx.coroutines.*
import zs.qimai.com.printer2.callback.*
......@@ -21,8 +26,11 @@ import zs.qimai.com.printer2.manager.DeviceManager.Companion.USB
import zs.qimai.com.printer2.manager.DeviceManagerUtils
import zs.qimai.com.printer2.manager.UsbDeviceManager
import zs.qimai.com.printer2.utils.PrintManagerUtils
import java.io.*
import java.util.*
import kotlin.collections.ArrayList
class MainActivityPtinter : AppCompatActivity(), PrintConnOrDisCallBack {
class MainActivityPtinter : AppCompatActivity(), PrintConnOrDisCallBack, JavaVoidCallback {
private var list = ArrayList<DeviceManager>()
......@@ -39,18 +47,84 @@ class MainActivityPtinter : AppCompatActivity(), PrintConnOrDisCallBack {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "onCreate: brand= ${Build.BRAND} model= ${Build.MODEL}")
setContentView(R.layout.activity_main_printer)
DeviceManagerUtils.getInstance().addConnectStatusCallBack(this)
initUsbPrinter()
val value = Utils.getSN()
Log.d(TAG, "onCreate: value= $value")
tv_Log_up.setOnClickListener {
startActivity(Intent(this, LogActivity::class.java))
}
tv_connect_bt.setOnClickListener {
startActivity(Intent(this, BtListActivity::class.java))
}
tv_print.setOnClickListener {
//PrintExecutor().doPrint(TestPrintTemplate())
// PrintExecutor().doPrint(LabelTemplete())
Log.d(TAG, "download start: ")
GlobalScope.launch(Dispatchers.IO){
val bitmap : Drawable? = Glide.with(this@MainActivityPtinter)
.load("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white_fe6da1ec.png")
.into(500,500)
.get()
Log.d(TAG, "onCreate: drawale bitmap = ${bitmap==null}")
val futureTask = Glide.with(this@MainActivityPtinter)
.load("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white_fe6da1ec.png")
.downloadOnly(500,500)
val file = futureTask.get()
Log.d(TAG, "download finish: file= ${file.absoluteFile}")
}
return@setOnClickListener
/* val file =
File("${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/test.js")
val bos = ByteArrayOutputStream()
val b = ByteArray(1024)
val `is`: InputStream = FileInputStream(file)
var len: Int
while (`is`!!.read(b).also { len = it } != -1) {
bos.write(b, 0, len)
}
val json = bos.toString()
Log.d(TAG, "onCreate: json= ${json}")
val value = "企迈测试".toByteArray(charset("gbk"))
Log.d(TAG, "onCreate value: value= ${Arrays.toString(value)}")*/
val v8 = V8.createV8Runtime()
// v8.executeJSFunction("print")
// val v8Array = v8.executeArrayScript("print1") as V8Array
// val v8Array = v8.executeScript(readFile()) as V8Array
// val byteArray = v8Array.getBytes(0,v8Array.length())
//Log.d(TAG, "onCreate: byteArray = ${Arrays.toString(byteArray)}")
v8.executeScript(readFile("htmlparser2.js"))
v8.executeScript(readFile("art-template.js"))
v8.executeScript(readFile("encoding-indexes.js"))
v8.executeScript(readFile("encoding.js"))
v8.executeScript(readFile("tools.js"))
v8.executeScript(readFile("operation.js"))
val android = Console()
val v8Object = V8Object(v8)
v8.add("android", v8Object)
v8Object.registerJavaMethod(
android,
"printString",
"printString",
arrayOf(String::class.java)
)
v8Object.registerJavaMethod(android, "print12", "print12", arrayOf(V8Array::class.java))
v8.executeJSFunction("print1")
//val v8Array =
// v8.executeJSFunction("print1")
/*as V8Array
val byteArray = v8Array.getBytes(0,v8Array.length())
val task = object : PrintExecutor() {
override fun covertEscData(): ByteArray? {
return TestPrintTemplate().getPrintData("122")
......@@ -69,7 +143,10 @@ class MainActivityPtinter : AppCompatActivity(), PrintConnOrDisCallBack {
Log.d(TAG, "printFailed: msg= $msg")
}
}
task.execute()
//task.execute()
task.goToPrint(DeviceManagerUtils.getInstance().lists[0], byteArray)*/
}
tv_connect_usb.setOnClickListener {
startActivity(Intent(this, UsbActivity::class.java))
......@@ -91,7 +168,7 @@ class MainActivityPtinter : AppCompatActivity(), PrintConnOrDisCallBack {
}
}
tv_connect_wifi.setOnClickListener {
PrintManagerUtils.getInstance().wifiConnect("192.168.10.200",
PrintManagerUtils.getInstance().wifiConnect("192.168.31.200",
object : OnConnectCallBack {
override fun onConnectStart() {
Log.d(TAG, "onConnectStart: ")
......@@ -128,7 +205,6 @@ class MainActivityPtinter : AppCompatActivity(), PrintConnOrDisCallBack {
}
}
private fun initUsbPrinter() {
DeviceManagerUtils.getInstance().addConnectStatusCallBack(object : PrintConnOrDisCallBack {
override fun onConectPrint(device: DeviceManager) {
......@@ -200,4 +276,106 @@ class MainActivityPtinter : AppCompatActivity(), PrintConnOrDisCallBack {
DeviceManagerUtils.getInstance().removeConnectStatusCallBack(this)
}
fun readFile(fileNme: String): String {
var result = "没有"
var in1: InputStream? = null
try {
in1 = getAssets().open(fileNme)
} catch (e: IOException) {
e.printStackTrace()
}
val reader = BufferedReader(InputStreamReader(in1))
val sb = StringBuilder()
var line: String? = null
while ((reader.readLine().also { line = it }) != null) {
sb.append(line).append("\n")
}
return sb.toString()
}
override fun invoke(p0: V8Object?, p1: V8Array?) {
TODO("Not yet implemented")
}
fun print12(value: V8Array) {
val value1 = value.getBytes(0, value.length())
Log.d(TAG, "print: value1= ${Arrays.toString(value1)}")
val task = object : PrintExecutor() {
override fun covertEscData(): ByteArray? {
return TestPrintTemplate().getPrintData("122")
}
override fun convertTscData(): ByteArray? {
return LabelTemplete().getPrintData("Test")
}
}
task.mPrintCallBack = object : PrintCallBack {
override fun printSuccess(deviceManager: DeviceManager) {
Log.d(TAG, "printSuccess: deviceManager = $deviceManager")
}
override fun printFailed(msg: String?) {
Log.d(TAG, "printFailed: msg= $msg")
}
}
//task.execute()
task.goToPrint(DeviceManagerUtils.getInstance().lists[0], value1)
value1.forEach {
Log.d(TAG, "test123: 111 value= ${it}")
}
"hello world".toByteArray().forEach {
Log.d(TAG, "print: it= $it")
}
}
class Console {
fun printString(text: String) {
Log.d("TAG", "printString: text= ${text}")
}
fun print12(value: V8Array) {
val value1 = value.getBytes(0, value.length())
Log.d("TAG", "print: value1= ${Arrays.toString(value1)}")
val task = object : PrintExecutor() {
override fun covertEscData(): ByteArray? {
return TestPrintTemplate().getPrintData("122")
}
override fun convertTscData(): ByteArray? {
return LabelTemplete().getPrintData("Test")
}
}
task.mPrintCallBack = object : PrintCallBack {
override fun printSuccess(deviceManager: DeviceManager) {
Log.d("TAG", "printSuccess: deviceManager = $deviceManager")
}
override fun printFailed(msg: String?) {
Log.d("TAG", "printFailed: msg= $msg")
}
}
//task.execute()
task.goToPrint(DeviceManagerUtils.getInstance().lists[0], value1)
value1.forEach {
Log.d("TAG", "test123: 111 value= ${it}")
}
"hello world".toByteArray().forEach {
Log.d("TAG", "print: it= $it")
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_log"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="初始化Log"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_write"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="写日志"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_log" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -41,6 +41,16 @@
android:text="drawer"
android:layout_width="match_parent"
android:layout_height="50dp" />
<Button
android:id="@+id/btn_printer"
android:text="打印"
android:layout_width="match_parent"
android:layout_height="50dp" />
<Button
android:id="@+id/btn_log"
android:text="日志"
android:layout_width="match_parent"
android:layout_height="50dp" />
<TextView
android:layout_width="match_parent"
......
......@@ -4,8 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="zs.qimai.com.test.MainActivity">
android:orientation="vertical">
<TextView
android:id="@+id/tv_connect_bt"
......@@ -80,4 +79,20 @@
android:textColor="#000"
/>
<ImageView
android:layout_gravity="center"
android:id="@+id/iv_image"
android:layout_width="100dp"
android:layout_height="100dp"/>
<TextView
android:id="@+id/tv_Log_up"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="日志操作"
android:textColor="#000"
android:textSize="22sp" />
</LinearLayout>
\ No newline at end of file
......@@ -21,7 +21,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath "com.whl:gradle-publish-plugin:0.1.16-SNAPSHOT"
......
......@@ -63,7 +63,7 @@ repositories {
mavenCentral()
}
group 'com.qmai.android.fetch'
version '1.2.57-SNAPSHOT'
version '1.2.58-SNAPSHOT'
gradlePublish {
......
......@@ -137,7 +137,8 @@ class OkHttpConfig private constructor(builder: Builder) {
setHostnameVerifier()
addInterceptors()
setTimeout()
setDebugConfig()
//http log显示信息由外部调用,内部不提供
// setDebugConfig()
return OkHttpConfig(this)
}
......
File mode changed from 100755 to 100644
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: "com.whl.gradle-publish-plugin"
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 29
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
minSdkVersion 16
targetSdkVersion 29
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
......@@ -36,41 +34,33 @@ android {
jniLibs.srcDirs = ['libs']
}
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation files('libs/commons-net-3.7.jar')
implementation files('libs/commons-net-3.7-sources.jar')
implementation files('libs/commons-net-examples-3.7.jar')
testImplementation 'junit:junit:4.+'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
//日志读写
implementation 'com.dianping.android.sdk:logan:1.2.4'
//系统级别浮框
implementation "com.github.princekin-f:EasyFloat:2.0.3"
implementation project(path: ':widgetlib')
//test
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
api 'com.tencent.mars:mars-xlog:1.2.4'
// For Kotlin use kapt instead of annotationProcessor (注意这个注释)
kapt "androidx.room:room-compiler:$lifecycle_version"
implementation "androidx.room:room-runtime:$lifecycle_version"
kapt "androidx.room:room-compiler:$lifecycle_version"
implementation "androidx.room:room-ktx:$lifecycle_version"
api "androidx.lifecycle:lifecycle-extensions:2.2.0"
api "androidx.lifecycle:lifecycle-runtime:2.2.0"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.2.0"
kapt "androidx.lifecycle:lifecycle-compiler:2.2.0"
api "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
api "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
}
group 'com.qmai.android.log'
version '1.0.1.21-SNAPSHOT'
version '1.2-SNAPSHOT'
gradlePublish {
......
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.qimai.log">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.INTERNET" />
<provider
android:name="com.qimai.log.InitContentProvider"
android:authorities="${applicationId}.log.InitContentProvider1"
android:enabled="true"
android:exported="false"/>
<service android:name="com.qimai.log.UploadService"/>
<service android:name=".service.ScheduledExecutionService"/>
<application>
<activity
android:name=".ui.LogUpActivity"
android:exported="false" />
</application>
</manifest>
\ No newline at end of file
package com.qimai.log;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.channels.FileChannel;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class DBCopyUtils {
private static final String TAG = "DBCopyUtils";
/* public static void copyLogToFTP(String tag) {
File file = null;
final String SDCARD = Environment.getExternalStorageDirectory().getAbsolutePath();
final String logPath = LogInit.Companion.getInstance().mApplication.getFilesDir().toString() + "/release/xlog";
file = new File(logPath);
try {
// File logFile =
if (!file.exists()) {
return;
}
File[] fileList = file.listFiles();
if (fileList != null && fileList.length > 0) {
for (int i = 0; i < fileList.length; i++) {
file = fileList[i];
Log.d(TAG, "copylogToFTP: file = " + file.getAbsolutePath().toString());
final FileInputStream in = new FileInputStream(file);
final File finalFile = file;
new Thread(new Runnable() {
@Override
public void run() {
boolean isUpload = FtpUtil.uploadFile("119.29.25.121", 221, "dataftp"
, "Data2019@#%", "/", tag, finalFile.getName(), in);
Log.d(TAG, "copyDataBaseToSD: isUpload= " + isUpload + "DeviceUtils.getDeviceSN()= " + tag);
if (isUpload) {
finalFile.delete();
}
}
}).start();
}
} else {
return;
}
} catch (Exception e) {
Log.d(TAG, "copyDataBaseToSD: failed= " + e.getMessage());
//LogUtils.e(TAG, "copy dataBase to SD error.");
e.printStackTrace();
} finally {
}
}*/
/**
* @param file 代表文件不是目录
**/
public static void copyLogToFTP(File file,String tag) {
FileInputStream in = null;
try {
// File logFile =
if (!file.exists()) {
return;
}
in = new FileInputStream(file);
Log.d(TAG, "copyLogToFTP: thread= " + Thread.currentThread().getName());
boolean isUpload = FtpUtil.uploadFile("119.29.25.121", 221, "dataftp"
, "Data2019@#%",
"/", tag, file.getName(), in);
Log.d(TAG, "copyLogToFTP: isUpload= " + isUpload);
/*File []fileList = file.listFiles();
if (fileList!=null&&fileList.length>0){
for (int i = 0; i <fileList.length;i++) {
file = fileList[i];
Log.d(TAG, "copylogToFTP: file = "+file.getAbsolutePath().toString());
FileInputStream in = new FileInputStream(file);
File finalFile = file;
new Thread(new Runnable() {
@Override
public void run() {
boolean isUpload = FtpUtil.uploadFile("119.29.25.121",221,"dataftp"
,"Data2018@#%",
"/",DeviceUtils.getDeviceSN(), finalFile.getName(),in);
Log.d(TAG, "copyDataBaseToSD: isUpload= "+isUpload+"DeviceUtils.getDeviceSN()= "+(DeviceUtils.getDeviceSN()));
if (isUpload){
finalFile.delete();
}
}
}).start();
}
}
else{
return;
}
}
catch (Exception e) {
Log.d(TAG, "copyDataBaseToSD: failed= "+e.getMessage());
//LogUtils.e(TAG, "copy dataBase to SD error.");
e.printStackTrace();
}finally{
}
*/
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.qimai.log
import android.util.Log
import com.qimai.log.QmLog.getConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.IOException
import java.util.*
object FtpUpload {
fun upload(config: UploadConfig, uploadCallBack: UploadCallBack? = null) {
GlobalScope.launch(Dispatchers.Main){
uploadCallBack?.onLoading()
val logFiles = getLogFiles(config)
if (logFiles.isNullOrEmpty()) {
uploadCallBack?.onError("暂无可上传日志文件")
} else {
try {
for (i in logFiles.indices) {
val tempFile = logFiles[i]
if (tempFile != null) {
uploadFile(config.tag,tempFile)
}
}
uploadCallBack?.onSuccess()
} catch (e: FileNotFoundException) {
uploadCallBack?.onError(e.toString())
}catch (e: IOException) {
uploadCallBack?.onError(e.toString())
}
}
}
}
suspend fun uploadFile(tag: String, file: File) = withContext(Dispatchers.IO){
var `in`: FileInputStream? = null
try {
// File logFile =
if (!file.exists()) {
return@withContext
}
`in` = FileInputStream(file)
val isUpload = FtpUtil.uploadFile(
"119.29.25.121", 221, "dataftp", "Data2019@#%",
"/", tag, file.getName(), `in`
)
} catch (e: FileNotFoundException) {
throw e
} finally {
if (`in` != null) {
try {
`in`.close()
} catch (e: IOException) {
throw e
}
}
}
}
}
private fun getLogFiles(config: UploadConfig): Array<File?>? {
//DBCopyUtils.copyLogToFTP();
//int requestNum = receiverBean.getNum();
var file: File? = null
// final String logPath = SDCARD + "/ptcy/log";
val logPath: String
logPath = if (BuildConfig.DEBUG) {
getConfig().logDebugPath
} else {
getConfig().logReleasePath
}
file = File(logPath)
if (!file.exists()) {
return null
}
//file.lastModified();
val files: Array<File?>? = convertTimeDesc(file.listFiles())
if (files == null || files.size == 0) {
return null
}
return files
// int maxFile = files.length;
// int endNum = Math.min(maxFile,num);
/*for (i in files.indices) {
val tempFile = files[i]
DBCopyUtils.copyLogToFTP(tempFile, config.tag)
}*/
}
private fun convertTimeDesc(files: Array<File>): Array<File?>? {
if (files == null || files.size == 0) {
return null
}
val endNum = files.size
val filesAll = arrayOfNulls<File>(20)
val longFileTreeMap = TreeMap<Long, File>()
for (i in files.indices) {
val temp = files[i].lastModified()
if (files[i].exists()) {
longFileTreeMap[temp] = files[i]
}
}
val navigableSet = longFileTreeMap.descendingKeySet()
val iterator: Iterator<Long> = navigableSet.iterator()
var index = 0
while (iterator.hasNext()) {
val file = longFileTreeMap[iterator.next()]
// Log.d(UploadService.TAG, "convertTimeDesc: file name= " + file!!.name)
filesAll[index] = file
index++
if (index >= endNum) {
break
}
}
return filesAll
}
fun copyLogToFTP(file: File, tag: String?) {
var `in`: FileInputStream? = null
try {
// File logFile =
if (!file.exists()) {
return
}
`in` = FileInputStream(file)
val isUpload = FtpUtil.uploadFile(
"119.29.25.121", 221, "dataftp", "Data2019@#%",
"/", tag, file.name, `in`
)
} catch (e: FileNotFoundException) {
e.printStackTrace()
} finally {
if (`in` != null) {
try {
`in`.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
package com.qimai.log;
import android.util.Log;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
public class FtpUtil {
private static final String TAG = "FtpUtil";
/**
* Description: 向FTP服务器上传文件
*
* @param host FTP服务器hostname
* @param port FTP服务器端口
* @param username FTP登录账号
* @param password FTP登录密码
* @param basePath FTP服务器基础目录
* @param filePath FTP服务器文件存放路径。例如分日期存放:/2017/08/06。文件的路径为basePath+filePath
* @param filename 上传到FTP服务器上的文件名
* @param input 输入流
* @return 成功返回true,否则返回false
*/
public static boolean uploadFile(String host, int port, String username, String password, String basePath,
String filePath, String filename, InputStream input)
{
Log.d(TAG, "uploadFile: thread= "+Thread.currentThread().getName());
boolean result = false;
FTPClient ftp = new FTPClient();
try
{
int reply;
ftp.connect(host, port);// 连接FTP服务器
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
ftp.login(username, password);// 登录
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply))
{
ftp.disconnect();
return result;
}
// 切换到上传目录
String onepath=new String((basePath + filePath).getBytes("GBK"),"iso-8859-1");
if (!ftp.changeWorkingDirectory(onepath))
{
ftp.makeDirectory(onepath);
ftp.changeWorkingDirectory(onepath);
/* Log.d(TAG, "uploadFile: 切换到上传目录");
// 如果目录不存在创建目录
String[] dirs = filePath.split("/");
String tempPath = basePath;*/
/*for (String dir : dirs)
{
if (null == dir || "".equals(dir))
continue;
tempPath += "/" + dir;
if (!ftp.changeWorkingDirectory(tempPath))
{
if (!ftp.makeDirectory(tempPath))
{
return result;
}
else
{
ftp.changeWorkingDirectory(tempPath);
}
}
}*/
}
// 设置上传文件的类型为二进制类型
ftp.setFileType(FTP.BINARY_FILE_TYPE);
// 上传文件
ftp.enterLocalPassiveMode();
if (!ftp.storeFile(new String(filename.getBytes("GBK"),"iso-8859-1"), input)) {
Log.d(TAG, "uploadFile: upload fail");
return result;
}
Log.d(TAG, "uploadFile: 上传成功");
input.close();
ftp.logout();
result = true;
}
catch (IOException e)
{
e.printStackTrace();
Log.d(TAG, "uploadFile: e= "+e.getMessage());
}
finally
{
if (ftp.isConnected())
{
try
{
ftp.disconnect();
}
catch (IOException ioe)
{
}
}
}
return result;
}
/**
* Description: 向FTP服务器上传文件
*
* @param host FTP服务器hostname
* @param port FTP服务器端口
* @param username FTP登录账号
* @param password FTP登录密码
* @param basePath FTP服务器基础目录
* @param filePath FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath
* @param filename 上传到FTP服务器上的文件名
* @param input 输入流
* @return 成功返回true,否则返回false
*/
public static boolean uploadFile1(String host, int port, String username, String password, String basePath,
String filePath, String filename, InputStream input) {
boolean result = false;
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(host, port);// 连接FTP服务器
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
ftp.login(username, password);// 登录
ftp.setBufferSize(1024);
ftp.setControlEncoding("UTF-8");
ftp.enterLocalPassiveMode();
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return result;
}
//切换到上传目录
if (!ftp.changeWorkingDirectory(basePath + filePath)) {
//如果目录不存在创建目录
//String[] dirs = filePath.split("/");
String tempPath = basePath;
/* for (String dir : dirs) {
if (null == dir || "".equals(dir)) continue;
tempPath += "/" + dir;
if (!ftp.changeWorkingDirectory(tempPath)) {
if (!ftp.makeDirectory(tempPath)) {
return result;
} else {
ftp.changeWorkingDirectory(tempPath);
}
}
}*/
ftp.changeWorkingDirectory(tempPath);
}
//创建目录
if (!ftp.changeWorkingDirectory(filePath)){
ftp.makeDirectory(filePath);
}
//设置上传文件的类型为二进制类型
ftp.setFileType(FTP.BINARY_FILE_TYPE);
//上传文件
if (!ftp.storeFile(filePath+"/"+filename, input)) {
return result;
}
input.close();
ftp.logout();
result = true;
} catch (IOException e) {
Log.d(TAG, "uploadFile: e= "+e.getMessage());
e.printStackTrace();
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}
}
package com.qimai.log
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
class InitContentProvider : ContentProvider() {
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
throw UnsupportedOperationException("Not yet implemented")
}
override fun getType(uri: Uri): String? {
throw UnsupportedOperationException("Not yet implemented")
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
throw UnsupportedOperationException("Not yet implemented")
}
override fun onCreate(): Boolean {
context?.let { LogInit.getInstance().init(it) }
return true
}
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?
): Cursor? {
throw UnsupportedOperationException("Not yet implemented")
}
override fun update(
uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String>?
): Int {
throw UnsupportedOperationException("Not yet implemented")
}
}
\ No newline at end of file
package com.qimai.log
import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Environment
import com.qimai.log.service.ScheduledExecutionService
import com.tencent.mars.xlog.Log
import com.tencent.mars.xlog.Xlog
class LogInit private constructor() {
private lateinit var mLogConfig: QmLogConfig
@JvmOverloads
fun init(application: Context) {
init(QmLogConfig(application))
}
@JvmOverloads
fun init(
config: QmLogConfig
) {
this.mLogConfig = config
//addObserver(config.app)
System.loadLibrary("c++_shared")
System.loadLibrary("marsxlog")
var logPath = ""
if (BuildConfig.DEBUG) {
logPath = config.logDebugPath
} else {
logPath = config.logReleasePath
}
val logConfig = Xlog.XLogConfig()
logConfig.mode = Xlog.AppednerModeAsync
logConfig.logdir = logPath
logConfig.nameprefix = "log"
logConfig.pubkey = ""
logConfig.compressmode = Xlog.ZLIB_MODE
logConfig.compresslevel = 0
logConfig.cachedays = 3
logConfig.cachedir = ""
// if (BuildConfig.DEBUG) {
logConfig.level = Xlog.LEVEL_DEBUG
Xlog.setConsoleLogOpen(true)
// } else {
// logConfig.level = Xlog.LEVEL_ALL
// Xlog.setConsoleLogOpen(true)
//}
Xlog.appenderOpen(logConfig)
Log.setLogImp(Xlog())
//检查是否有过期的通知数据
// val intent = Intent(getConfig().app, ScheduledExecutionService::class.java)
// getConfig().app.startService(intent)
}
private fun addObserver(mApplication: Context) {
if (mApplication is Application) {
mApplication.registerActivityLifecycleCallbacks(object :
Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStopped(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
Log.appenderClose()
Log.appenderFlush(false)
}
})
}
}
companion object {
private val mInstance: LogInit = LogInit()
fun getInstance(): LogInit {
return mInstance
}
}
fun getConfig() = mLogConfig
}
\ No newline at end of file
package com.qimai.log;/*
* Copyright (c) 2018-present, 美团点评
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class LoganParser {
private static final String ALGORITHM = "AES";
private static final String ALGORITHM_TYPE = "AES/CBC/NoPadding";
private Cipher mDecryptCipher;
private byte[] mEncryptKey16; //128位ase加密Key
private byte[] mEncryptIv16; //128位aes加密IV
public LoganParser(byte[] encryptKey16, byte[] encryptIv16) {
mEncryptKey16 = encryptKey16;
mEncryptIv16 = encryptIv16;
initEncrypt();
}
private void initEncrypt() {
SecretKeySpec secretKeySpec = new SecretKeySpec(mEncryptKey16, ALGORITHM);
try {
mDecryptCipher = Cipher.getInstance(ALGORITHM_TYPE);
mDecryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(mEncryptIv16));
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
public void parse(InputStream is, OutputStream os) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int t = 0;
try {
while ((t = is.read(buffer)) >= 0) {
bos.write(buffer, 0, t);
bos.flush();
}
byte[] content = bos.toByteArray();
for (int i = 0; i < content.length; i++) {
byte start = content[i];
if (start == '\1') {
i++;
int length = (content[i] & 0xFF) << 24 |
(content[i + 1] & 0xFF) << 16 |
(content[i + 2] & 0xFF) << 8 |
(content[i + 3] & 0xFF);
i += 3;
int type;
if (length > 0) {
int temp = i + length + 1;
if (content.length - i - 1 == length) { //异常
type = 0;
} else if (content.length - i - 1 > length && '\0' == content[temp]) {
type = 1;
} else if (content.length - i - 1 > length && '\1' == content[temp]) { //异常
type = 2;
} else {
i -= 4;
continue;
}
byte[] dest = new byte[length];
System.arraycopy(content, i + 1, dest, 0, length);
ByteArrayOutputStream uncompressBytesArray = new ByteArrayOutputStream();
InflaterInputStream inflaterOs = null;
byte[] uncompressByte;
try {
uncompressBytesArray.reset();
inflaterOs = new GZIPInputStream(new CipherInputStream(new ByteArrayInputStream(dest), mDecryptCipher));
int e = 0;
while ((e = inflaterOs.read(buffer)) >= 0) {
uncompressBytesArray.write(buffer, 0, e);
}
} catch (Exception e) {
e.printStackTrace();
}
uncompressByte = uncompressBytesArray.toByteArray();
uncompressBytesArray.reset();
os.write(uncompressByte);
if (inflaterOs != null)
inflaterOs.close();
i += length;
if (type == 1) {
i++;
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.qimai.log
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
\ No newline at end of file
package com.qimai.log
import com.blankj.utilcode.util.*
import com.dianping.logan.Logan
import com.dianping.logan.LoganConfig
import com.qimai.log.bean.LogUpBean
import com.qimai.log.ui.LogUpFloatView
import kotlinx.coroutines.*
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream
/**
* 上传日志相关操作
*/
object QLog {
//region ------ 日志类型 ------
//调试信息
const val D: Int = 2
//提示性的消息
const val I: Int = 3
//错误信息
const val E: Int = 4
//警告信息
const val W: Int = 5
//endregion
//ase加密Key
private const val encryptKey16 = "0123456789012345"
//aes加密IV
private const val encryptIv16 = "0123456789012345"
// 到天的日期格式
private const val formatDay = "yyyy-MM-dd"
//每天的毫秒数
private const val millisDay = 86_400_000
//零点字符串
private const val zero = "00:00:00"
//log 文件路径
private var filePath: String? = null
/**
* 初始化
*/
fun init(debug: Boolean = false) {
filePath =
"${Utils.getApp().getExternalFilesDir(null)?.absolutePath}${File.separator}Logan_v1"
val config: LoganConfig = LoganConfig.Builder()
.setCachePath(Utils.getApp().applicationContext.filesDir.absolutePath)
.setEncryptKey16(encryptKey16.toByteArray())
.setEncryptIV16(encryptIv16.toByteArray())
.setPath(filePath)
.build()
Logan.init(config)
Logan.setDebug(debug)
Logan.setOnLoganProtocolStatus { cmd, code ->
LogUtils.d("clogan > cmd : $cmd | code : $code")
}
GlobalScope.launch(Dispatchers.IO) {
delay(1_000)
withContext(Dispatchers.Main) {
LogUpFloatView.show()
}
}
}
/**
* 写入日志
* @param log String - 日志内容
* @param tag Int - 日志类型
* @param now Boolean - 是否立即写入
*/
fun write(log: String, tag: Int = D, now: Boolean = false) {
Logan.w(log, tag)
if (now) Logan.f()
}
/**
* 立即写入
*/
fun f() = Logan.f()
/**
* 获取日志文件保存路径
*/
fun getLogFilePath(): String? = filePath
/**
* 按日期获取日志文件,日期格式 yyyy-MM-dd
*/
fun getLogFile(dateStr: String): File? {
val timeStamp = TimeUtils.string2Millis("$dateStr $zero")
return FileUtils.listFilesInDir(filePath).find { it.name == "$timeStamp" }
}
/**
* 获取一段时间内所有日志文件
*
* @param startDate String 开始时间,格式为:yyyy-MM-dd
* @param endDate String 结束时间,格式为:yyyy-MM-dd
* @return MutableList<File> 文件列表
*/
fun getLogFileByPeriod(startDate: String, endDate: String): MutableList<File> {
val dateList = getPeriodDateList(startDate, endDate)
return FileUtils.listFilesInDirWithFilter(filePath) {
dateList.any { a -> "${TimeUtils.string2Millis("$a $zero")}" == it.name }
}
}
/**
*获取所有日志文件
*/
fun getAllLogFile(): MutableList<File> = FileUtils.listFilesInDir(filePath)
/**
* 按日期获取日志文件内容,日期格式 yyyy-MM-dd
*/
fun getLogFileContent(dateStr: String): String {
val file = getLogFile(dateStr)
return if (file != null) getLogFileContent(file) else ""
}
/**
* 获取一天内一段时间内的日志内容
*
* @param startDate String 开始时间,格式为 yyyy-MM-dd HH:mm:ss
* @param endDate String 结束时间,格式为 yyyy-MM-dd HH:mm:ss
* @return List<LogUpBean> 日志内容
*/
fun getLogListByPeriod(startDate: String, endDate: String): List<LogUpBean?> {
//开始时间戳
val startMillis: Long = TimeUtils.string2Millis(startDate)
//结束时间戳
val endMillis: Long = TimeUtils.string2Millis(endDate)
//当天时间字符串,用于查找文件,格式:yyyy-MM-dd
val dateStr = TimeUtils.millis2String(startMillis, formatDay)
return getLogFileContent(dateStr)
.trim()
.split("\n")
.map { GsonUtils.fromJson(it, LogUpBean::class.java) ?: null }
.filter { it?.l in startMillis..endMillis }
.sortedByDescending { it?.l }
}
/**
* 获取一天内一段时间内的日志内容
*
* @param startDate String 开始时间,格式为 yyyy-MM-dd HH:mm:ss
* @param endDate String 结束时间,格式为 yyyy-MM-dd HH:mm:ss
* @return String 日志内容
*/
fun getLogContentByPeriod(startDate: String, endDate: String): String {
val list = getLogListByPeriod(startDate, endDate)
return GsonUtils.toJson(list)
}
/**
* 获取所有文件内容
*/
fun getAllLogFileContent(): String {
return getAllLogFile()
.fold("") { total, next ->
val nextContent = getLogFileContent(next)
return if (total.isEmpty()) nextContent else "${total}\n${nextContent}"
}
}
/**
* 解析日志文件内容
*/
fun getLogFileContent(file: File): String {
val uncompressBytesArray = ByteArrayOutputStream()
val loganParser = LoganParser(encryptKey16.toByteArray(), encryptIv16.toByteArray())
loganParser.parse(FileInputStream(file), uncompressBytesArray)
return String(uncompressBytesArray.toByteArray())
}
/**
* 获取一段时间内时间列表
*
* @param startDate String 开始时间,格式为 yyyy-MM-dd
* @param endDate String 结束时间,格式为 yyyy-MM-dd
* @return MutableList<String> 时间列表 [yyyy-MM-dd,yyyy-MM-dd]
*/
private fun getPeriodDateList(startDate: String, endDate: String): MutableList<String> {
val start: Long = TimeUtils.string2Millis("$startDate $zero")
var end: Long = TimeUtils.string2Millis("$endDate $zero")
val dateList = mutableListOf<String>()
while (end >= start) {
dateList.add(TimeUtils.millis2String(end, formatDay))
end -= millisDay
}
return dateList
}
/**
* 按名称删除文件
*/
fun deleteLogByName(name: String) {
FileUtils.delete("$filePath/$name")
}
/**
* 按日期删除日志
* @param dateStr String
*/
fun deleteLogByDate(dateStr: String) {
getLogFile(dateStr)?.run { FileUtils.delete(this) }
}
/**
* 获取所有日志文件信息
*/
fun getAllFileInfo(): Map<String, Long> {
return Logan.getAllFilesInfo()
}
/**
* 清空所有日志文件
*/
fun clear() {
FileUtils.deleteAllInDir(filePath)
}
}
\ No newline at end of file
package com.qimai.log
import android.content.Intent
import com.tencent.mars.xlog.Log
object QmLog {
fun d(tag: String, message: String) {
Log.d(tag, message)
}
fun e(tag: String, message: String) {
Log.e(tag, message)
}
fun v(tag: String, message: String) {
Log.v(tag, message)
}
fun w(tag: String, message: String) {
Log.w(tag, message)
}
fun i(tag: String, message: String) {
Log.i(tag, message)
}
fun flush() {
Log.appenderClose()
}
/**
* 上传log
*/
fun uploadLog(config: UploadConfig) {
//刷新下
Log.appenderFlush(false)
val intent = Intent(getConfig().app, UploadService::class.java)
intent.putExtra("upload", config)
getConfig().app.startService(intent)
}
fun uploadLog(config: UploadConfig,callBack: UploadCallBack) {
//刷新下
Log.appenderFlush(false)
FtpUpload.upload(config,callBack)
}
fun getConfig(): QmLogConfig {
val config: QmLogConfig? = LogInit.getInstance().getConfig()
if (config == null) {
throw IllegalArgumentException("please call LogInit.init() first")
} else {
return config
}
}
//上传log
/***
* filePath 保存logo的路径
*/
fun uploadXog(config: (filePath: String) -> Unit) {
//刷新下
Log.appenderFlush(false)
// final String logPath = SDCARD + "/ptcy/log";
val logPath: String = if (BuildConfig.DEBUG) {
getConfig().logDebugPath
} else {
getConfig().logReleasePath
}
config(logPath)
// val intent = Intent(getConfig().app, UploadService::class.java)
// getConfig().app.startService(intent)
}
}
\ No newline at end of file
package com.qimai.log
import android.content.Context
/**
* 基本参数配置
*/
class QmLogConfig(val app: Context, var logDebugPath: String = app.filesDir.toString() + "/debug/xlog",
var logReleasePath: String = app.filesDir.toString() + "/release/xlog",
var expiredDays: Int = 10) {
}
\ No newline at end of file
package com.qimai.log
interface UploadCallBack {
fun onLoading()
fun onError(e:String)
fun onSuccess()
}
\ No newline at end of file
package com.qimai.log
import android.os.Parcelable
/**
* @tag 文件名字
*/
class UploadConfig(var tag: String): java.io.Serializable {
}
\ No newline at end of file
package com.qimai.log;
import android.app.IntentService;
import android.content.Intent;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.util.Iterator;
import java.util.NavigableSet;
import java.util.TreeMap;
import androidx.annotation.Nullable;
/**
* Created by wanglei on 18-7-16.
* <p>
* 根据通知去上传日志
*/
public class UploadService extends IntentService {
public UploadService() {
super("upload");
}
private static final String TAG = "UploadService";
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public UploadService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.d(TAG, "onHandleIntent: ");
// updateOldOrder();
try {
UploadConfig receiverBean = (UploadConfig) intent.getSerializableExtra("upload");
if (receiverBean != null) {
uploadLog(receiverBean);
}
} catch (Exception e) {
}
}
private void uploadLog(UploadConfig config) {
//DBCopyUtils.copyLogToFTP();
//int requestNum = receiverBean.getNum();
File file = null;
// final String logPath = SDCARD + "/ptcy/log";
final String logPath;
if (BuildConfig.DEBUG) {
logPath = QmLog.INSTANCE.getConfig().getLogDebugPath();
} else {
logPath = QmLog.INSTANCE.getConfig().getLogReleasePath();
}
file = new File(logPath);
if (!file.exists()) {
return;
}
//file.lastModified();
File[] files = convertTimeDesc(file.listFiles());
if (files == null || files.length == 0) {
return;
}
// int maxFile = files.length;
// int endNum = Math.min(maxFile,num);
for (int i = 0; i < files.length; i++) {
File tempFile = files[i];
DBCopyUtils.copyLogToFTP(tempFile,config.getTag());
}
}
private File[] convertTimeDesc(File[] files) {
if (files == null || files.length == 0) {
return null;
}
int endNum = files.length;
File[] filesAll = new File[20];
TreeMap<Long, File> longFileTreeMap = new TreeMap<>();
for (int i = 0; i < files.length; i++) {
Long temp = files[i].lastModified();
if (files[i].exists()) {
longFileTreeMap.put(temp, files[i]);
}
}
NavigableSet<Long> navigableSet = longFileTreeMap.descendingKeySet();
Iterator<Long> iterator = navigableSet.iterator();
int index = 0;
while (iterator.hasNext()) {
File file = longFileTreeMap.get(iterator.next());
Log.d(TAG, "convertTimeDesc: file name= " + file.getName());
filesAll[index] = file;
index++;
if (index >= endNum) {
break;
}
}
return filesAll;
}
}
package com.qimai.log
import android.view.View
/**
* Created by chuxiaoshan on 2021/11/15 20:25
*
* Description:
*/
/**
* 扩展属性参数:用于标记是否可点击
*/
private var <T : View> T.viewClickable: Boolean
get() = if (getTag(1 + 2 shl 24) != null) getTag(1 + 2 shl 24) as Boolean else true
set(value) {
setTag(1 + 2 shl 24, value)
}
/**
* 设置点击监听, 并自定义防抖时间
*
* @receiver T View
* @param time Long 防抖间隔时长 默认300
* @param action Function1<[@kotlin.ParameterName] View, Unit> 点击事件
*/
fun <T : View> T.click(time: Long = 300, action: (T: View) -> Unit) {
setOnClickListener {
if (viewClickable) {
viewClickable = false
action(it)
postDelayed({ viewClickable = true }, time)
}
}
}
\ No newline at end of file
package com.qimai.log.bean
/**
* Created by chuxiaoshan on 2021/11/11 14:46
*
* Description:
*/
data class LogUpBean(
//{
// "c": "hello world!!!!",
// "f": 312,
// "l": 1636363201246,
// "n": "main",
// "i": 2,
// "m": true
//}
//日志内容
var c: String? = null,
//日志类型
var f: Int,
//日志记录时间
var l: Long,
//:线程名称
var n: String? = null,
// 线程id
var i: Int,
//是否是主线程
var m: Boolean
)
\ No newline at end of file
package com.qimai.log.database
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/**
* 获取数据库操作类
*/
class PushDataBaseVm(application: Application) : AndroidViewModel(application) {
private val mDataBse: PushNotifiInfoDao by lazy {
PushNotifiDataBase.getDatabase(application).pushInfoDao()
}
fun insertPushInfo(notifiInfo: PushNotifiInfo) {
viewModelScope.launch(Dispatchers.IO) {
mDataBse.insert(notifiInfo)
}
}
fun updatePushInfo(msg_id: String, status: Boolean, errorMsg: String? = null) {
viewModelScope.launch(Dispatchers.IO) {
mDataBse.updateStatus(msg_id, status, errorMsg)
}
}
}
\ No newline at end of file
package com.qimai.log.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
/**
*
*/
@Database(entities = [PushNotifiInfo::class], version = 1, exportSchema = false)
abstract class PushNotifiDataBase : RoomDatabase() {
abstract fun pushInfoDao(): PushNotifiInfoDao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
@Volatile
private var INSTANCE: PushNotifiDataBase? = null
fun getDatabase(context: Context): PushNotifiDataBase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
PushNotifiDataBase::class.java,
"push_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
\ No newline at end of file
package com.qimai.log.database
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "push_info")
data class PushNotifiInfo @JvmOverloads
constructor(
@PrimaryKey @ColumnInfo(name = "msg_id") val msg_id: String, //消息id
@ColumnInfo(name = "status") var status: Boolean, // 状态
@ColumnInfo(name = "error_msg") var errorMsg: String? = null, //错误原因
@ColumnInfo(name = "push_type") val pushType: String, //用的什么推送
@ColumnInfo(name = "rawText") var rawText: String? = null, //原始信息
@ColumnInfo(name = "time") var time: String? = null, //格式化时间
@ColumnInfo(name = "device_id") var deviceId: String? = null, //推送的devicedId
@ColumnInfo(name = "model") var model: String? = null, //手机型号
@ColumnInfo(name = "app_version") var version: String? = null, //app版本
@ColumnInfo(name = "time_stamp") var timeStamp: Long? = null //时间戳
) {
}
\ No newline at end of file
package com.qimai.log.database
import androidx.room.*
import java.sql.Timestamp
/**
* 数据库操作类
*/
@Dao
interface PushNotifiInfoDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(word: PushNotifiInfo)
@Query("update push_info set status= :status ,error_msg = :errorMsg where msg_id=:msg_id")
suspend fun updateStatus(msg_id: String, status: Boolean, errorMsg: String? = null)
//删除过期数据
@Query("delete from push_info where time_stamp < :timestamp")
fun deleteExpiredItems(timestamp: Long)
}
\ No newline at end of file
package com.qimai.log.service
import android.app.IntentService
import android.content.Intent
import com.qimai.log.QmLog
import com.qimai.log.database.PushNotifiDataBase
import com.qimai.log.database.PushNotifiInfoDao
import com.qimai.log.QmLogConfig
/**
* 定时执行任务
*/
class ScheduledExecutionService(name: String = "schedule") : IntentService(name) {
private val mDataBse: PushNotifiInfoDao by lazy {
PushNotifiDataBase.getDatabase(application).pushInfoDao()
}
override fun onHandleIntent(intent: Intent?) {
val totalTime: Long = (QmLog.getConfig().expiredDays * 24 * 60 * 60 * 1000).toLong()
mDataBse.deleteExpiredItems((System.currentTimeMillis() - totalTime))
}
}
\ No newline at end of file
package com.qimai.log.ui
import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.blankj.utilcode.util.*
import com.qimai.log.QLog
import com.qimai.log.databinding.ActivityLogUpBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class LogUpActivity : AppCompatActivity() {
private lateinit var bind: ActivityLogUpBinding
private val mAdapter by lazy { LogUpAdapter() }
override fun onCreate(savedInstanceState: Bundle?) {
BarUtils.setStatusBarColor(this, Color.WHITE)
BarUtils.setStatusBarLightMode(this, true)
super.onCreate(savedInstanceState)
bind = ActivityLogUpBinding.inflate(layoutInflater)
setContentView(bind.root)
init()
}
@SuppressLint("SetTextI18n")
private fun init() {
val startDate = TimeUtils.getNowString(TimeUtils.getSafeDateFormat("yyyy-MM-dd 00:00:00"))
val endDate = TimeUtils.getNowString(TimeUtils.getSafeDateFormat("yyyy-MM-dd HH:mm:ss"))
bind.etDate.setText("$startDate -- $endDate")
bind.rv.layoutManager = LinearLayoutManager(this)
bind.rv.adapter = mAdapter
bind.btnSearch.setOnClickListener { getLogData() }
getLogData()
}
private fun getLogData() {
lifecycleScope.launch(Dispatchers.IO) {
val timeStr = bind.etDate.text.toString()
val timeSplit = timeStr.split(" -- ")
val startTime = timeSplit[0]
val endTime = timeSplit[1]
QLog.f()
val list = QLog.getLogListByPeriod(startTime, endTime)
withContext(Dispatchers.Main) {
if (list.isNullOrEmpty()) {
ToastUtils.showShort("未查询到日志")
return@withContext
}
KeyboardUtils.hideSoftInput(bind.btnSearch)
bind.etDate.clearFocus()
mAdapter.setNewData(list)
}
}
}
}
\ No newline at end of file
package com.qimai.log.ui
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.ClipboardUtils
import com.blankj.utilcode.util.TimeUtils
import com.blankj.utilcode.util.ToastUtils
import com.qimai.log.R
import com.qimai.log.bean.LogUpBean
/**
* Created by chuxiaoshan on 2021/11/15 14:42
*
* Description: 日志渲染adapter
*/
class LogUpAdapter(
private var dataSet: List<LogUpBean?> = listOf()
) : RecyclerView.Adapter<LogUpAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_log, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = dataSet[position]
val content = "时间:${TimeUtils.millis2String(item?.l ?: 0, "yyyy-MM-dd HH:mm:ss")}\n" +
"类型:${item?.f}\n" +
"线程:${item?.i} ${item?.n}\n" +
"内容:\n${item?.c}\n"
holder.tvContent.text = content
holder.btnCopy.setOnClickListener {
ClipboardUtils.copyText(content)
ToastUtils.showShort("内容已复制到粘贴板")
}
}
override fun getItemCount() = dataSet.size
@SuppressLint("NotifyDataSetChanged")
fun setNewData(data: List<LogUpBean?>) {
if (dataSet === data) return
dataSet = data
notifyDataSetChanged()
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val tvContent: TextView = view.findViewById(R.id.tv_content)
val btnCopy: TextView = view.findViewById(R.id.btn_copy)
}
}
\ No newline at end of file
package com.qimai.log.ui
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Build
import androidx.constraintlayout.widget.ConstraintLayout
import com.blankj.utilcode.util.*
import com.lzf.easyfloat.EasyFloat
import com.lzf.easyfloat.enums.ShowPattern
import com.lzf.easyfloat.enums.SidePattern
import com.qimai.log.R
import com.qimai.log.click
/**
* Created by chuxiaoshan on 2021/5/27 14:10
*
* Description: 浮框
*/
object LogUpFloatView {
//标识浮框的tag
private const val FLOAT_VIEW_TAG = "tag_float_view"
/**
* 浮框权限判断与申请
*/
private fun requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!PermissionUtils.isGrantedDrawOverlays()) {
PermissionUtils.requestDrawOverlays(object :
PermissionUtils.SimpleCallback {
override fun onGranted() {
LogUtils.d("---1111--->onGranted")
initFloat()
}
override fun onDenied() {
LogUtils.d("---1111--->onDenied")
ToastUtils.showShort("浮框权限未打开")
}
})
} else {
initFloat()
LogUtils.d("---1111--->浮框权限本来就是打开的")
}
} else {
LogUtils.d("---1111--->低版本")
initFloat()
}
}
private fun initFloat() {
try {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
EasyFloat.dismiss(FLOAT_VIEW_TAG)
}
EasyFloat.with(Utils.getApp())
.setTag(FLOAT_VIEW_TAG)
.setShowPattern(ShowPattern.FOREGROUND)
.setLocation(
ScreenUtils.getScreenWidth() - ConvertUtils.dp2px(58f),
ScreenUtils.getScreenHeight() - ConvertUtils.dp2px(200f)
)
.setSidePattern(SidePattern.RESULT_HORIZONTAL)
.setImmersionStatusBar(true)
.setFilter(LogUpActivity::class.java)
.setLayout(R.layout.float_logup) {
it.findViewById<ConstraintLayout>(R.id.cl_float_view).click {
Utils.getApp().startActivity(Intent(
Utils.getApp(),
LogUpActivity::class.java
).apply {
flags = FLAG_ACTIVITY_NEW_TASK
})
}
}.show()
} catch (e: Exception) {
LogUtils.e("---e--->${e.printStackTrace()}")
}
}
/**
* 初始化并显示全局浮框
*/
fun show() {
requestPermission()
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffee58" />
<corners android:radius="100dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<EditText
android:id="@+id/et_date"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="16dp"
android:layout_marginLeft="16dp"
android:text="2021-11-11 15:21:56 -- 2021-11-11 15:34:56"
android:textSize="14sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/btn_search"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:text="查询"
app:layout_constraintBottom_toBottomOf="@+id/et_date"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/et_date" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_date"
tools:listitem="@layout/item_log" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/cl_float_view"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="4dp"
android:background="@drawable/circle_log"
android:clickable="true"
android:clipToPadding="false"
android:elevation="2dp"
android:focusable="true">
<TextView
android:id="@+id/tv_clear_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="LogUp"
android:textColor="#666666"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@color/white"
android:elevation="2dp">
<TextView
android:id="@+id/tv_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="16dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="时间:2021-11-15 12:34:56\n类型:错误\n线程:123 Thread-5\n内容:\ngheioirnerinewrpewrgheioirnerinewrpewrgheioirnerinewrpewrioirnerinewrpewrioirnerinewrpewrioirnerinewrpewrioirnerinewrpewrioirnerinewrpewrioirnerinewrpewrioirnerinewrpewrioirnerinewrpewrioirnerinewrpewrioirnerinewrpewrioirnerinewrpewr" />
<TextView
android:id="@+id/btn_copy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp"
android:text="复制"
android:textColor="#FF9919"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
......@@ -51,7 +51,7 @@ repositories {
}
group 'com.qmai.android.print'
version '1.1.45-SNAPSHOT'
version '1.1.75-SNAPSHOT'
gradlePublish {
......
package zs.qimai.com.printer2
import zs.qimai.com.printer2.manager.DeviceManager
/**
* @author: leiwang
* @time: 2021/12/9 5:15 下午
* @desc:
*/
data class PrintDeviceStatus(
val type: Int,
val deviceManager: DeviceManager?,
val transferLink: String? = null,
var others: String? = null
) {
fun getTypeDesc(): String {
return when (type) {
CONNECT_SUCCESS -> {
"打印机连接成功"
}
CONNECT_FAILED -> {
"打印机连接失败"
}
PRINT_ERROR -> {
"打印错误"
}
PRINT_CLOSED -> {
"打印机断开"
}
BLUETOOTH_CLOSED_RECEIVER -> {
"广播通知蓝牙打印机断开"
}
USER_CANCEL_CONNECTED -> "用户主动取消连接"
PRINT_WRITE ->{
"打印下发指令"
}
else -> {
""
}
}
}
companion object {
const val CONNECT_SUCCESS = 1
const val CONNECT_FAILED = 2
//打印异常
const val PRINT_ERROR = 3
const val PRINT_CLOSED = 4
//广播收到蓝牙打印机断开
const val BLUETOOTH_CLOSED_RECEIVER = 5
//用户主动取消连接
const val USER_CANCEL_CONNECTED = 6
//设备重复添加
const val PRINT_REPEAT_ADD = 7
//下发指令
const val PRINT_WRITE = 8
}
}
\ No newline at end of file
package zs.qimai.com.printer2.callback
import zs.qimai.com.printer2.PrintDeviceStatus
/**
* @author: leiwang
* @time: 2021/12/9 3:22 下午
* @desc:
*/
interface PrintStatusChangeCallBack {
fun onChange(printDeviceStatus: PrintDeviceStatus)
}
\ No newline at end of file
......@@ -73,7 +73,7 @@ open abstract class PrintExecutor {
}
}
private fun goToPrint(it: DeviceManager, data: ByteArray) = GlobalScope.async(Dispatchers.IO) {
fun goToPrint(it: DeviceManager, data: ByteArray) = GlobalScope.async(Dispatchers.IO) {
it.writeData(data)
}
}
\ No newline at end of file
......@@ -5,7 +5,12 @@ import android.app.Activity
import android.bluetooth.BluetoothAdapter
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import com.tbruyelle.rxpermissions2.RxPermissions
import zs.qimai.com.printer2.receiverManager.RequestBlueToothListReceiverManager
import zs.qimai.com.printer2.callback.BlueToothSearchCallBack
......@@ -13,16 +18,30 @@ import zs.qimai.com.printer2.callback.BlueToothSearchCallBack
/****
* 没有界面的Fragment 管理获取定位权限,获取搜索到的蓝牙列表
* **/
class BlueToothListFragment : Fragment() {
class BlueToothListFragment : Fragment(), LifecycleObserver {
private val TAG = "BlueToothListFragment"
var activity: Activity? = null
private var mOuterLifecycleOwner: LifecycleOwner? = null
var callback: BlueToothSearchCallBack? = null
override fun onAttach(context: Context?) {
super.onAttach(context)
activity = context as Activity?
}
fun getBlueToothList(callBack1: BlueToothSearchCallBack? = null) {
private var mRequestBlueToothListReceiverManager: RequestBlueToothListReceiverManager? = null
//监听生命周期
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestory() {
}
fun getBlueToothList(
lifecycleOwner: LifecycleOwner,
callBack1: BlueToothSearchCallBack? = null
) {
mOuterLifecycleOwner = lifecycleOwner
mOuterLifecycleOwner!!.lifecycle.addObserver(this)
this.callback = callBack1
this.callback?.onSearchStart()
//先检查权限 蓝牙搜索必须要这些权限
......@@ -47,10 +66,15 @@ class BlueToothListFragment : Fragment() {
}
/**
* 获取列表
* 获取蓝牙列表
* **/
private fun requestBlueList() {
RequestBlueToothListReceiverManager(this).blueToothSearchCallBack = this.callback
mRequestBlueToothListReceiverManager = RequestBlueToothListReceiverManager(this)
mOuterLifecycleOwner?.apply {
mRequestBlueToothListReceiverManager?.blueToothSearchCallBack =
this@BlueToothListFragment.callback
}
}
/***
......@@ -82,8 +106,16 @@ class BlueToothListFragment : Fragment() {
}
override fun onDestroy() {
callback = null
super.onDestroy()
activity = null
callback = null
}
//取消蓝牙搜索
fun cancelSearch() {
callback = null
mRequestBlueToothListReceiverManager?.cancelSearch()
mRequestBlueToothListReceiverManager = null
}
}
\ No newline at end of file
......@@ -4,6 +4,7 @@ import android.util.Log
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import zs.qimai.com.printer2.callback.BlueToothSearchCallBack
/***
......@@ -12,25 +13,32 @@ import zs.qimai.com.printer2.callback.BlueToothSearchCallBack
class BlueToothLists {
private val TAG = "BlueToothLists"
var mBlueToothListFragment: BlueToothListFragment? = null
private var mOuterLifecycleOwner: LifecycleOwner? = null
constructor(fragmentActivity: FragmentActivity) {
mOuterLifecycleOwner = fragmentActivity
mBlueToothListFragment = getFragment(fragmentActivity)
}
constructor(fragment: Fragment) {
mOuterLifecycleOwner = fragment.viewLifecycleOwner
mBlueToothListFragment = getFragment(fragment)
}
private fun getFragment(fragment: Fragment): BlueToothListFragment? {
private fun getFragment(fragment: Fragment): BlueToothListFragment {
mOuterLifecycleOwner = fragment
return findFragment(fragment.childFragmentManager)
}
private fun getFragment(fragmentActivity: FragmentActivity): BlueToothListFragment? {
private fun getFragment(fragmentActivity: FragmentActivity): BlueToothListFragment {
return findFragment(fragmentActivity.supportFragmentManager)
}
private fun findFragment(supportFragmentManager: FragmentManager?): BlueToothListFragment? {
private fun findFragment(supportFragmentManager: FragmentManager?): BlueToothListFragment {
var blueToothListFragment: BlueToothListFragment? = null
blueToothListFragment =
......@@ -41,7 +49,7 @@ class BlueToothLists {
}
if (!blueToothListFragment.isAdded) {
supportFragmentManager?.beginTransaction()?.add(blueToothListFragment, TAG)
?.commitNow()
?.commitNowAllowingStateLoss()
}
return blueToothListFragment
}
......@@ -50,6 +58,12 @@ class BlueToothLists {
* 获取搜索到的蓝牙列表
* ***/
fun getBlueToothList(callBack: BlueToothSearchCallBack? = null) {
mBlueToothListFragment?.getBlueToothList(callBack)
mOuterLifecycleOwner?.let { mBlueToothListFragment?.getBlueToothList(it, callBack) }
}
//取消蓝牙列表搜索
fun cancelSearchBlueToothList() {
mBlueToothListFragment?.cancelSearch()
}
}
\ No newline at end of file
package zs.qimai.com.printer2.manager
import android.util.Log
import zs.qimai.com.printer2.PrintDeviceStatus
import java.io.InputStream
import java.io.OutputStream
import java.lang.StringBuilder
/****
* 设备基类
......@@ -16,24 +19,33 @@ abstract class DeviceManager {
var mPrintMode: Int? = null
abstract var mType: Int
var name: String? = null
@Deprecated("后续用 mPrintSize 代替打印杯贴大小")
var maxLineChars: Int = 80
var mPrintSize:Int = -1
var mPrintSize: Int = -1
companion object {
//小票模式
val ESC = 1
//标签模式
val TSC = 2
//蓝牙
val BT = 1
//usb
val USB = 2
//wifi(网线)
const val WIFI = 3
//支持小票尺寸
const val TSC_40_30 = 10
const val TSC_40_40 = 11
const val TSC_60_40 = 12
const val TSC_40_60 = 13
}
//连接
......@@ -57,5 +69,75 @@ abstract class DeviceManager {
return deviceId.hashCode()
}
override fun toString(): String {
val str =
"打印机名称: $name\n 打印机连接方式: ${
when (mType) {
BT -> "蓝牙"
WIFI -> "网口"
USB -> "USB"
else -> "未知"
}
} \n 打印机模式: ${if (mPrintMode == ESC) "小票打印机" else "杯贴打印机"}\n deviceId: $deviceId\n address: $address\n 票据大小: $mPrintSize"
return str
}
fun connectSuccessNotify() {
//回调通知
val printDeviceStatus = PrintDeviceStatus(
PrintDeviceStatus.CONNECT_SUCCESS,
this,
Log.getStackTraceString(Throwable())
)
DeviceManagerUtils.getInstance()
.notifyPrintStatusChangeCallBack(printDeviceStatus)
}
fun connectFailedNotify(msg: String?) {
//回调通知
val printDeviceStatus = PrintDeviceStatus(
PrintDeviceStatus.CONNECT_FAILED,
this, Log.getStackTraceString(Throwable()),
"错误原因: $msg"
)
DeviceManagerUtils.getInstance()
.notifyPrintStatusChangeCallBack(printDeviceStatus)
}
//打印异常
fun printErrorNotify(msg: String?) {
//回调通知
val printDeviceStatus = PrintDeviceStatus(
PrintDeviceStatus.PRINT_ERROR,
this, Log.getStackTraceString(Throwable()),
"错误原因: $msg"
)
DeviceManagerUtils.getInstance()
.notifyPrintStatusChangeCallBack(printDeviceStatus)
}
//打印机关闭
fun printCloseNotify(msg: String) {
//回调通知
val printDeviceStatus = PrintDeviceStatus(
PrintDeviceStatus.PRINT_CLOSED,
this, Log.getStackTraceString(Throwable()),
"错误原因: $msg"
)
DeviceManagerUtils.getInstance()
.notifyPrintStatusChangeCallBack(printDeviceStatus)
}
fun printWriteNotify(msg: String?) {
//回调通知
val printDeviceStatus = PrintDeviceStatus(
PrintDeviceStatus.PRINT_WRITE,
this, Log.getStackTraceString(Throwable()),
"msg: $msg"
)
DeviceManagerUtils.getInstance()
.notifyPrintStatusChangeCallBack(printDeviceStatus)
}
}
\ No newline at end of file
......@@ -3,9 +3,15 @@ package zs.qimai.com.printer2.manager
import android.bluetooth.BluetoothDevice
import android.hardware.usb.UsbDevice
import android.util.Log
import zs.qimai.com.printer2.PrintDeviceStatus
import zs.qimai.com.printer2.PrintDeviceStatus.Companion.CONNECT_FAILED
import zs.qimai.com.printer2.PrintDeviceStatus.Companion.PRINT_CLOSED
import zs.qimai.com.printer2.PrintDeviceStatus.Companion.PRINT_REPEAT_ADD
import zs.qimai.com.printer2.callback.PrintConnOrDisCallBack
import zs.qimai.com.printer2.callback.PrintStatusChangeCallBack
import zs.qimai.com.printer2.manager.DeviceManager.Companion.BT
import zs.qimai.com.printer2.manager.DeviceManager.Companion.USB
import zs.qimai.com.printer2.utils.PrintManagerUtils
import java.util.concurrent.CopyOnWriteArrayList
/*****
......@@ -16,12 +22,24 @@ import java.util.concurrent.CopyOnWriteArrayList
class DeviceManagerUtils {
private val TAG = "DeviceManagerUtils"
private var mCallBacklist: CopyOnWriteArrayList<PrintConnOrDisCallBack> = CopyOnWriteArrayList()
private var mCallBackList: CopyOnWriteArrayList<PrintConnOrDisCallBack> = CopyOnWriteArrayList()
//打印机状态监听
private var mPrintStatusChangeCallBackList: CopyOnWriteArrayList<PrintStatusChangeCallBack> =
CopyOnWriteArrayList()
var lists: CopyOnWriteArrayList<DeviceManager> = CopyOnWriteArrayList()
internal fun addDevice(deviceManager: DeviceManager?) {
deviceManager?.let {
//避免重复添加
if (lists.contains(it)) {
val printDeviceStatus = PrintDeviceStatus(
PRINT_REPEAT_ADD, it, Log.getStackTraceString(
Throwable()
)
)
DeviceManagerUtils.getInstance().notifyPrintStatusChangeCallBack(printDeviceStatus)
return
}
lists.add(deviceManager)
notifyaddObserver(it)
}
......@@ -45,11 +63,18 @@ class DeviceManagerUtils {
// lists.remove(deviceManager)
}
internal fun removeUsbDevice(device: UsbDevice) {
private fun removeUsbDevice(device: UsbDevice) {
lists.forEach {
if (it is UsbDeviceManager) {
if (it.usbDevice == device) {
//回调通知
val printDeviceStatus = PrintDeviceStatus(
PRINT_CLOSED, it,
Log.getStackTraceString(Throwable())
)
getInstance().notifyPrintStatusChangeCallBack(printDeviceStatus)
//closePort()会清除所有配置项,并调用removeDevice()
it.closePort()
return@forEach
......@@ -58,7 +83,6 @@ class DeviceManagerUtils {
}
}
}
}
......@@ -99,7 +123,7 @@ class DeviceManagerUtils {
}
internal fun removeUsbDevice(address: String) {
var device = getUsbDevice(address)
val device = getUsbDevice(address)
device?.closePort()
}
......@@ -140,7 +164,7 @@ class DeviceManagerUtils {
* 添加状态监听
* **/
fun addConnectStatusCallBack(callBack: PrintConnOrDisCallBack) {
mCallBacklist.add(callBack)
mCallBackList.add(callBack)
}
/****
......@@ -148,7 +172,37 @@ class DeviceManagerUtils {
* **/
fun removeConnectStatusCallBack(callBack: PrintConnOrDisCallBack) {
mCallBacklist.remove(callBack)
mCallBackList.remove(callBack)
}
/***
* 添加状态监听
* **/
fun addPrintStatusCallBack(callBack: PrintStatusChangeCallBack) {
mPrintStatusChangeCallBackList.add(callBack)
}
/****
* 移除状态监听
* **/
fun removePrintStatusCallBack(callBack: PrintStatusChangeCallBack) {
mPrintStatusChangeCallBackList.remove(callBack)
}
fun notifyPrintDeviceStatusOnDisConnectObserver(
printDeviceStatus: PrintDeviceStatus
) {
mPrintStatusChangeCallBackList.forEach {
it.onChange(printDeviceStatus)
}
}
fun notifyPrintStatusChangeCallBack(printDeviceStatus: PrintDeviceStatus) {
mPrintStatusChangeCallBackList.forEach {
it.onChange(printDeviceStatus)
}
}
......@@ -157,7 +211,7 @@ class DeviceManagerUtils {
* ***/
private fun notifyRemoveObserver(deviceManager: DeviceManager) {
Log.d(TAG, "notifyRemoveObserver: ${Log.getStackTraceString(Throwable())}")
mCallBacklist?.forEach {
mCallBackList.forEach {
it.onDisPrint(deviceManager)
}
}
......@@ -167,7 +221,7 @@ class DeviceManagerUtils {
* ***/
private fun notifyaddObserver(deviceManager: DeviceManager) {
// Log.d(TAG, "notifyaddObserver: ${Log.getStackTraceString(Throwable())}")
mCallBacklist?.forEach {
mCallBackList.forEach {
it.onConectPrint(deviceManager)
}
}
......@@ -297,11 +351,30 @@ class DeviceManagerUtils {
return null
}
val device = lists.find { it.deviceId == deviceId }
return if (device != null) (device as DeviceManager) else null
return device
}
/***
* 通过广播获取新的设备
*/
fun receiveNewUsbDevice(
address: String,
usbDevice: UsbDevice
) {
//如果是设置为自动连接 则自动去连接
if (PrintManagerUtils.getInstance().detachUsbDeviceAutoConn) {
PrintManagerUtils.getInstance().usbConnect(address, usbDevice)
} else {
//不做处理
}
}
/***
* 通过广播监听到usb设备移除
*/
fun receiveDeviceDetach(device: UsbDevice) {
removeUsbDevice(device)
}
}
\ No newline at end of file
......@@ -2,6 +2,10 @@ package zs.qimai.com.printer2.manager
import android.hardware.usb.*
import android.util.Log
import zs.qimai.com.printer2.PrintDeviceStatus
import zs.qimai.com.printer2.PrintDeviceStatus.Companion.CONNECT_SUCCESS
import zs.qimai.com.printer2.PrintDeviceStatus.Companion.PRINT_CLOSED
import zs.qimai.com.printer2.PrintDeviceStatus.Companion.PRINT_ERROR
import zs.qimai.com.printer2.callback.UsbPrintConnCallBack
import zs.qimai.com.printer2.printStatus.PrintStatusCallBack
import zs.qimai.com.printer2.printStatus.PrinterStatusUtils
......@@ -59,11 +63,16 @@ class UsbDeviceManager(override var mType: Int = USB) : DeviceManager() {
}
DeviceManagerUtils.getInstance().addDevice(this@UsbDeviceManager)
mUsbPrintConnCallBack?.onConnSucess(this@UsbDeviceManager)
//回调通知
connectSuccessNotify()
}
}
}.queryStatus()
} else {
connectFailedNotify("连接失败")
mUsbPrintConnCallBack?.onConnFailed(USB_CONN_FAILED, "连接失败")
}
......@@ -72,6 +81,8 @@ class UsbDeviceManager(override var mType: Int = USB) : DeviceManager() {
}
override fun closePort() {
//回调通知
printCloseNotify("")
mStatus = false
mUsbPrintConnCallBack = null
DeviceManagerUtils.getInstance().removeDevice(this@UsbDeviceManager)
......@@ -131,7 +142,8 @@ class UsbDeviceManager(override var mType: Int = USB) : DeviceManager() {
Log.d(TAG, "send success")
}
} catch (var7: Exception) {
Log.d(TAG, "Exception occured while sending data immediately: " + var7.message)
//打印异常通知
printErrorNotify(var7.message)
}
}
......@@ -149,9 +161,9 @@ class UsbDeviceManager(override var mType: Int = USB) : DeviceManager() {
return address?.hashCode() ?: 0 + usbDevice.hashCode()
}*/
override fun toString(): String {
return "UsbDeviceManager(mType=$mType, TAG='$TAG', usbDevice=$usbDevice, usbManager=$usbManager, mmConnection=$mmConnection, mmIntf=$mmIntf, mmEndIn=$mmEndIn, mmEndOut=$mmEndOut, mUsbPrintConnCallBack=$mUsbPrintConnCallBack)"
}
/* override fun toString(): String {
return "usb打印机: UsbDeviceManager(mType=$mType, TAG='$TAG', usbDevice=$usbDevice, usbManager=$usbManager, mmConnection=$mmConnection, mmIntf=$mmIntf, mmEndIn=$mmEndIn, mmEndOut=$mmEndOut, mUsbPrintConnCallBack=$mUsbPrintConnCallBack)" + super.toString()
}*/
override fun equals(other: Any?): Boolean {
if (this === other) return true
......@@ -165,6 +177,7 @@ class UsbDeviceManager(override var mType: Int = USB) : DeviceManager() {
override fun hashCode(): Int {
return deviceId.hashCode()
}
protected fun convertVectorByteToBytes(data: Vector<Byte>): ByteArray? {
val sendData = ByteArray(data.size)
if (data.size > 0) {
......
......@@ -49,7 +49,10 @@ class WifiDeviceManager(var ip: String, var port: Int = 9100) : DeviceManager()
DeviceManagerUtils.getInstance().addDevice(this@WifiDeviceManager)
Log.d(TAG, "searchResult: WifiDeviceManager success")
mConnectCallBack?.onConnectSuccess(this@WifiDeviceManager)
timeDetachStatus()
//ip连接的很奇怪,长时间不操作,会自动化断开 https://www.jianshu.com/p/0f44f9e3f604 按这个教程,每次打印完断开端口连接,打印的时候重新连接在打印
mPort.closePort()
// timeDetachStatus()
connectSuccessNotify()
}
}
}.queryStatus()
......@@ -58,10 +61,10 @@ class WifiDeviceManager(var ip: String, var port: Int = 9100) : DeviceManager()
DeviceManagerUtils.getInstance().removeDevice(this@WifiDeviceManager)
mConnectCallBack?.onConnectError(0, "连接失败")
}
connectFailedNotify("连接失败")
}
}
}
override fun closePort() {
......@@ -85,11 +88,23 @@ class WifiDeviceManager(var ip: String, var port: Int = 9100) : DeviceManager()
}
override fun writeData(bytes: ByteArray) {
//每次打印先连接端口,打印失败后在断开
try {
val isOpenPort = mPort.openPort()
if (isOpenPort) {
mOutPutStream = mPort.outputStream
mInPutStream = mPort.inputStream
this@WifiDeviceManager.deviceId = ip
mOutPutStream?.write(bytes)
mOutPutStream?.flush()
mPort.closePort()
}
} catch (e: IOException) {
printErrorNotify(e.message)
Log.d(TAG, "writeData: e=$e")
} catch (e: SocketException) {
printErrorNotify(e.message)
Log.d(TAG, "writeData: e = $e")
}
}
......
......@@ -7,6 +7,9 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import zs.qimai.com.printer2.PrintDeviceStatus
import zs.qimai.com.printer2.PrintDeviceStatus.Companion.BLUETOOTH_CLOSED_RECEIVER
import zs.qimai.com.printer2.manager.BlueDeviceManager
import zs.qimai.com.printer2.manager.DeviceManagerUtils
class BlueToothStatusReceiver : BroadcastReceiver() {
......@@ -20,10 +23,26 @@ class BlueToothStatusReceiver : BroadcastReceiver() {
}
BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
var bluetoothDevice = intent.getParcelableExtra<BluetoothDevice?>(EXTRA_DEVICE)
bluetoothDevice?.let {
val bluetoothDevice = intent.getParcelableExtra<BluetoothDevice?>(EXTRA_DEVICE)
bluetoothDevice?.let { bluetoothDevice ->
//如果检测到设备已经断开连接,就从保存的集合中删除
DeviceManagerUtils.getInstance().removeBtDevice(it)
var deviceManager = DeviceManagerUtils.getInstance()
.getDevice { it.deviceId == bluetoothDevice.address }
if (deviceManager == null) {
deviceManager = BlueDeviceManager().apply {
address = bluetoothDevice.address
name = bluetoothDevice.name
}
}
val printDeviceStatus = PrintDeviceStatus(
BLUETOOTH_CLOSED_RECEIVER, deviceManager, Log.getStackTraceString(
Throwable()
)
)
DeviceManagerUtils.getInstance()
.notifyPrintStatusChangeCallBack(printDeviceStatus)
DeviceManagerUtils.getInstance().removeBtDevice(bluetoothDevice)
}
}
BluetoothAdapter.ACTION_STATE_CHANGED -> {
......
......@@ -9,6 +9,10 @@ import android.util.Log
import zs.qimai.com.printer2.utils.PrintManagerUtils
import zs.qimai.com.printer2.manager.DeviceManagerUtils
/***
* usb探测监听广播
*/
class UsbDetachReceiver : BroadcastReceiver() {
private val TAG = "UsbDetachReceiver"
override fun onReceive(context: Context, intent: Intent) {
......@@ -17,11 +21,12 @@ class UsbDetachReceiver : BroadcastReceiver() {
//如果存在移除usb设备,则需要去连接设备管理中移除
if (intent.action == UsbManager.ACTION_USB_DEVICE_DETACHED) {
val mUsbDevice = intent.getParcelableExtra<UsbDevice?>(UsbManager.EXTRA_DEVICE)
Log.d(TAG, "onReceive: ACTION_USB_DEVICE_DETACHED mUsbDevice = ${mUsbDevice}")
Log.d(TAG, "onReceive: ACTION_USB_DEVICE_DETACHED mUsbDevice = $mUsbDevice")
//判断是打印机类型
mUsbDevice?.let {
if (mUsbDevice?.interfaceCount != 0 && mUsbDevice?.getInterface(0)?.interfaceClass == 7) {
DeviceManagerUtils.getInstance().removeUsbDevice(it)
if (mUsbDevice.interfaceCount != 0 && mUsbDevice.getInterface(0).interfaceClass == 7) {
//DeviceManagerUtils.getInstance().removeUsbDevice(it)
DeviceManagerUtils.getInstance().receiveDeviceDetach(it)
}
}
......@@ -29,9 +34,12 @@ class UsbDetachReceiver : BroadcastReceiver() {
if (intent.action == UsbManager.ACTION_USB_DEVICE_ATTACHED) {
val mUsbDevice = intent.getParcelableExtra<UsbDevice?>(UsbManager.EXTRA_DEVICE)
//判断是打印机类型
if (mUsbDevice?.interfaceCount != 0 && mUsbDevice?.getInterface(0)?.interfaceClass == 7 && PrintManagerUtils.getInstance().detachUsbDeviceAutoConn) {
PrintManagerUtils.getInstance().usbConnect(mUsbDevice?.deviceName!!, mUsbDevice)
if (mUsbDevice?.interfaceCount != 0 && mUsbDevice?.getInterface(0)?.interfaceClass == 7) {
DeviceManagerUtils.getInstance().receiveNewUsbDevice(mUsbDevice.deviceName, mUsbDevice)
}
/* if (mUsbDevice?.interfaceCount != 0 && mUsbDevice?.getInterface(0)?.interfaceClass == 7 && PrintManagerUtils.getInstance().detachUsbDeviceAutoConn) {
PrintManagerUtils.getInstance().usbConnect(mUsbDevice.deviceName, mUsbDevice)
}*/
}
}
}
......@@ -11,6 +11,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import zs.qimai.com.printer2.callback.BlueToothSearchCallBack
......@@ -22,6 +23,7 @@ class RequestBlueToothListReceiverManager : LifecycleObserver {
var activity: FragmentActivity? = null
var receiver = Receiver()
var blueToothSearchCallBack: BlueToothSearchCallBack? = null
private var mOuterLifecycleOwner: LifecycleOwner? = null
var intentFilter = IntentFilter(BluetoothDevice.ACTION_FOUND)
var bluetoothAdapter: BluetoothAdapter? = null
......@@ -29,9 +31,13 @@ class RequestBlueToothListReceiverManager : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestory() {
Log.d(TAG, "onDestory: ")
cancelSearch()
}
fun cancelSearch() {
blueToothSearchCallBack = null
this.activity?.unregisterReceiver(receiver)
this.activity = null
blueToothSearchCallBack = null
bluetoothAdapter?.cancelDiscovery()
bluetoothAdapter = null
}
......@@ -42,19 +48,24 @@ class RequestBlueToothListReceiverManager : LifecycleObserver {
init()
}
constructor(fragment: Fragment) {
this.activity = fragment.activity
constructor(outerLifecycleOwner: LifecycleOwner) {
this.mOuterLifecycleOwner = outerLifecycleOwner
if (mOuterLifecycleOwner is FragmentActivity) {
this.activity = mOuterLifecycleOwner as FragmentActivity
} else if (mOuterLifecycleOwner is Fragment) {
this.activity = (mOuterLifecycleOwner as Fragment).requireActivity()
}
Log.d(TAG, "activity2= :$activity ")
init()
}
private fun init() {
this.activity?.lifecycle?.addObserver(this)
this.mOuterLifecycleOwner?.lifecycle?.addObserver(this)
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
this?.activity?.registerReceiver(receiver, intentFilter)
this.activity?.registerReceiver(receiver, intentFilter)
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
bluetoothAdapter?.cancelDiscovery()
bluetoothAdapter?.startDiscovery()
......@@ -69,7 +80,13 @@ class RequestBlueToothListReceiverManager : LifecycleObserver {
intent?.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
bluetoothDevice?.let {
if (!it.name.isNullOrEmpty()) {
Log.d(
TAG,
"onReceive:bluetoothDevice.address= ${bluetoothDevice.address} name= ${bluetoothDevice.name}"
)
if (BluetoothAdapter.checkBluetoothAddress(bluetoothDevice.address)) {
blueToothSearchCallBack?.onBlueToothFound(bluetoothDevice)
}
/*Log.d(
TAG,
"onReceive: bluetoothDevice name= " + bluetoothDevice!!.name + " address= "
......@@ -95,4 +112,6 @@ class RequestBlueToothListReceiverManager : LifecycleObserver {
}
}
}
\ No newline at end of file
......@@ -10,6 +10,8 @@ import android.hardware.usb.UsbManager
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import zs.qimai.com.printer2.callback.*
import zs.qimai.com.printer2.fragment.BlueToothLists
import zs.qimai.com.printer2.lifecycle.ActivityManagers
......@@ -19,6 +21,7 @@ import zs.qimai.com.printer2.manager.DeviceManagerUtils
import zs.qimai.com.printer2.manager.UsbDeviceManager
import zs.qimai.com.printer2.manager.WifiDeviceManager
import zs.qimai.com.printer2.receiverManager.UsbRqPermissionReceiverManager
import androidx.core.content.ContextCompat.getSystemService
/****
......@@ -47,13 +50,31 @@ class PrintManagerUtils {
throw RuntimeException("请在Application 先调用init()")
}
}
//获取蓝牙列表
fun getSearchBlueToothList(
fragmentActivity: FragmentActivity,
callBack: BlueToothSearchCallBack? = null
) {
checkIsInit()
BlueToothLists(fragmentActivity).getBlueToothList(callBack)
?: callBack?.searchFailed(ACTIVITY_NOT_FOUND, "获取不到Activity")
}
fun getSearchBlueToothList(callBack: BlueToothSearchCallBack? = null) {
//获取蓝牙列表
fun getSearchBlueToothList(fragment: Fragment, callBack: BlueToothSearchCallBack? = null) {
checkIsInit()
var topActivity = ActivityManagers.getInstance().getTopActivity()
topActivity?.let {
BlueToothLists(it).getBlueToothList(callBack)
} ?: callBack?.searchFailed(ACTIVITY_NOT_FOUND, "获取不到Activity")
BlueToothLists(fragment).getBlueToothList(callBack)
?: callBack?.searchFailed(ACTIVITY_NOT_FOUND, "获取不到Activity")
}
//取消蓝牙搜索
fun cancelSearchToothList(fragmentActivity: FragmentActivity) {
BlueToothLists(fragmentActivity).cancelSearchBlueToothList()
}
//取消蓝牙搜索
fun cancelSearchToothList(fragment: Fragment) {
BlueToothLists(fragment).cancelSearchBlueToothList()
}
//蓝牙连接
......@@ -63,6 +84,8 @@ class PrintManagerUtils {
address: String,
mOnBtConnectCallBack: OnBtConnectCallBack? = null
) {
Log.d(TAG, "btConnect: ip= $address ${Log.getStackTraceString(Throwable())}")
checkIsInit()
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
bluetoothAdapter?.cancelDiscovery()
......@@ -110,12 +133,31 @@ class PrintManagerUtils {
}
}
//usb根据地址进行连接
fun usbConnect(address: String, mUsbPrintConnCallBack: UsbPrintConnCallBack? = null) {
//生成usbDevice
ActivityManagers.getInstance().getTopActivity()?.apply {
val manager = this.getSystemService(Context.USB_SERVICE) as UsbManager
val list = manager.deviceList
val device: UsbDevice? = list[address]
if (device != null) {
usbConnect(address, device, mUsbPrintConnCallBack)
} else {
mUsbPrintConnCallBack?.onConnFailed(USB_DEVICE_INFO_NOT_FOUND, "获取不到该设备")
}
}
}
@JvmOverloads
fun usbConnect(
address: String,
usbDevice: UsbDevice,
mUsbPrintConnCallBack: UsbPrintConnCallBack? = null
) {
Log.d(TAG, "usbConnect: ip= ${address} ${Log.getStackTraceString(Throwable())}")
checkIsInit()
//连接开始
mUsbPrintConnCallBack?.onConnStart()
......@@ -250,6 +292,7 @@ class PrintManagerUtils {
//网口连接
fun wifiConnect(ip: String, callback: OnConnectCallBack? = null, port: Int = 9100) {
Log.d(TAG, "wifiConnect: ip= ${ip} ${Log.getStackTraceString(Throwable())}")
val status = DeviceManagerUtils.getInstance().isContainerDevice {
it is WifiDeviceManager && it.ip == ip
}
......@@ -281,4 +324,24 @@ class PrintManagerUtils {
btConnect("00:11:22:33:44:55", onBtConnectCallBack)
}
}
/**
* 获取已经插入的usb打印机设备
*/
fun getAccessUsbList(): Map<String, UsbDevice>? {
val manager = application!!.getSystemService(Context.USB_SERVICE) as UsbManager?
val deviceMap = manager?.deviceList
if (!deviceMap.isNullOrEmpty()) {
val deviceList = HashMap<String, UsbDevice>()
deviceMap.forEach {
if (it.value.interfaceCount != 0 && it.value.getInterface(0).interfaceClass == 7) {
deviceList[it.key] = it.value
}
}
return deviceList
}
return null
}
}
\ No newline at end of file
function print1(){
android.printString('hello orld');
var html = `<p>储值编号:{{orderNo}}</p><p>储值时间:{{createTime}}</p><p>操作人员:{{operator}}</p><hr /> <p>活动名称{{ruleName}}</p> <p><span style="text-align:left;width:50%;">支付方式:</span> <span style="text-align:right;width:50%;">{{payDesc}}</span></p> <p> <span style="text-align:left;width:50%;">储值金额:</span><span style="text-align:right;width:50%;">{{rechargeAmount}}</span></p> <p> <span style="text-align:left;font-size:24px;width:50%;">账户余额: </span><span style="text-align:right;font-size:36px;width:50%;">{{balance}} </span></p> <hr /> <p>会员:{{buyerName}}</p> <p>电话:{{mobile}}</p><br /><p style="text-align:center">扫码关注店铺公众号</p><p style="text-align:center">本次储值赠送白金卡,5张10元优惠券</p>`
// let encoding = require('./encoding')
// let TextEncoder = encoding.TextEncoder
// let template = require('./art-template')
var render = template.render(html, {
orderNo: 'SDJL1293780123',
createTime: '2020-10-09 23:23:23',
operator: '王雷',
ruleName: '限时折扣活动',
payDesc: '微信',
rechargeAmount: '¥12.22',
balance: '¥100.22',
buyerName: '小雷',
mobile: '19899999999'
});
// var htmlparser2 = require('./htmlparser2')
var byteArray = [27, 64, 10]
var uint8array = new TextEncoder('gbk', { NONSTANDARD_allowLegacyEncoding: true })
var handler = new htmlparser2.DomHandler(function(error, dom) {
dom.forEach(item => {
if (item.name) {
var children = item.children
if (children.length == 1 && children[0].type == 'text') {
// console.log(uint8array.encode('值编号:SDJL1293780123') + '')
byteArray = byteArray.concat(Array.from(uint8array.encode(children[0].data)))
}
}
})
android.print12(byteArray)
// console.log(byteArray + '')
})
var parser = new htmlparser2.Parser(handler)
// var parser = new htmlparser2.Parser({
// onopentag(name, attributes) {
// console.log(name)
// console.log(attributes)
// // html = html + '<' + name + '>'
// // if (name === 'span') {
// // name = 'p'
// // }
// },
// // ontext(text) {
// // console.log("-->", text);
// // // html = html + '{{value}}'
// // },
// // onclosetag(tagname) {
// // // if (tagname === "script") {
// // // console.log("That's it?!");
// // // }
// // // html = html + '</' + tagname + '>'
// // console.log(tagname, '结尾')
// // },
// });
parser.write(render);
parser.end();
// function transform() {
// return render
// }
// console.log(transform())
// let encoding = require('./encoding')
// let TextEncoder = encoding.TextEncoder
// console.log(TextEncoder)
var uint8array = new TextEncoder(
'gbk', { NONSTANDARD_allowLegacyEncoding: true }).encode('值编号:SDJL1293780123');
// function encodeArrayOfStrings(strings) {
// var encoder, encoded, len, i, bytes, view, offset;
// encoder = new TextEncoder();
// encoded = [];
// len = Uint16Array.BYTES_PER_ELEMENT;
// for (i = 0; i < strings.length; i += 1) {
// len += Uint16Array.BYTES_PER_ELEMENT;
// encoded[i] = new TextEncoder().encode(strings[i]);
// len += encoded[i].byteLength;
// }
// bytes = new Uint16Array(len);
// view = new DataView(bytes.buffer);
// offset = 0;
// view.setUint32(offset, strings.length);
// offset += Uint16Array.BYTES_PER_ELEMENT;
// for (i = 0; i < encoded.length; i += 1) {
// len = encoded[i].byteLength;
// view.setUint32(offset, len);
// offset += Uint16Array.BYTES_PER_ELEMENT;
// bytes.set(encoded[i], offset);
// offset += len;
// }
// return bytes;
// }
// var aa = encodeArrayOfStrings('hello world')
}
\ No newline at end of file
......@@ -22,7 +22,7 @@ android {
}
group 'com.qmai.android.sdk'
version '2.1.3-SNAPSHOT'
version '2.1.5-SNAPSHOT'
gradlePublish {
......
......@@ -19,7 +19,7 @@
For example:
<version value="1" />
-->
<version value="2" />
<version value="3" />
<!--
Define your models in the list with mapping tag, LitePal will
......@@ -34,6 +34,7 @@
<list>
<mapping class="com.zhimai.websocket.bean.MessageSqlBean" />
<mapping class="com.zhimai.websocket.bean.SocketOrderInfoBean"/>
<mapping class="com.zhimai.websocket.bean.SocketCheckMessage"/>
</list>
<!--
......
package com.zhimai.websocket.bean
import org.litepal.crud.LitePalSupport
/**
* @author: 高英祥
* @github:
* @time: 2021/9/13 15:02
* @desc:
* @doc:
*/
class SocketCheckMessage : LitePalSupport() {
//消息唯一id
var messageId:String? = null
//订单号
var orderNo:String? = null
//设备id
var deviceId:String? = null
//设备的tag
var tag:String? = null
//门店id
var mutableId:String? = null
//门店名称
var mutableName:String? = null
//店铺名称:
var shopName:String? = null
//店铺id
var shopId:String? = null
//当前版本
var version:String? = null
//订单类容
var orderDetails:String? = null
//订单时间
var orderInTime:String? = null
//打印机状态
var printerState:String? = null
//是否打印
var isPrint:String? = null
//是否播报
var isPlay:String? = null
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment