Compare commits

...

67 Commits

Author SHA1 Message Date
3365972f83 2.18 瑞兴平板,rk3568_r,存在两个ttyS8、ttyS1,2G内存之前是ttyS8才可以接收,4G内存的ttyS8直接报错,导致打不开,需要缓存ttyS1,改成兼容模式
支持霍尼韦尔eda52服务扫描修改成广播模式,医院那边不要所谓的服务连续扫描
2026-04-16 09:50:41 +08:00
2f8f958a6a 2.17 支持原生相机 支持压缩+水印时间,默认压缩不默认添加时间;支持霍尼韦尔eda52服务扫描 2026-03-18 11:07:58 +08:00
f91790d046 2.6 强制暴露相机接口 2026-03-10 14:39:39 +08:00
038c96fbe0 feat(res): 修改数字输入选择屏幕方向 2026-03-10 14:14:51 +08:00
210d599ce0 feat: 添加文件选择和相机拍照功能
主要功能:
1. 支持WebView中选择文件(任意类型)
2. 支持选择图片(相册+相机)
3. 支持直接调用相机拍照
4. 自动处理相机和存储权限请求

新增文件:
- FileChooserHelper.java - 文件选择和相机功能核心工具类
- CameraHelper.java - 直接调用相机的JavaScript接口
- FileTestActivity.java - 文件选择测试Activity
- file_paths.xml - FileProvider配置文件
- file_test.html - 功能测试页面(ES5语法兼容老版本)
- INTEGRATION_GUIDE.md - 接入指南文档
- FILE_CHOOSER_USAGE.md - 详细使用文档

修改文件:
- AndroidManifest.xml - 添加相机和存储权限,注册FileProvider和FileTestActivity
- MainActivity.java - 集成FileChooserHelper,添加WebChromeClient支持文件选择
- menu_main.xml - 添加"文件测试"菜单项
- strings.xml - 添加相关字符串资源

技术特性:
- 支持Android 9+(API 28+)
- 适配Android 10+分区存储
- 适配Android 13+新媒体权限
- 权限授予后自动重新打开文件选择器
- 使用ES5语法兼容老版本WebView
- 支持FileProvider安全文件共享

使用方式:
在WebView加载的HTML页面中使用标准input标签:
<input type="file" accept="image/*" capture="environment">

版本:2.15
2026-01-23 17:23:47 +08:00
b9897fa0c6 2.14 适配 AIFUU 陈安良:陆军特色中心医院 2025-12-22 16:10:05 +08:00
16281e386d 2.13 取消监听旋转角度,使用系统自带的旋转(根据配置初始化,旋转方向:横、竖、随意)
瑞芯适配器 接入 新的型号,使用的是 ttyS8;而不是ttyS1;并且只有一个接口。
2025-11-26 17:19:27 +08:00
55e624111d 2.12 群创科技接入广播模式 补充 适配类 2025-11-20 10:22:30 +08:00
588f378a40 2.12 群创科技接入广播模式 中山市古镇人民医院 蒋凡 接入广播模式 2025-11-12 09:55:57 +08:00
55cd99687b 2.11 优化了保存配置的时候,重新加载页面方法,去设置初始化屏幕方向,已经init.json中添加了字段备注信息 2025-10-09 11:11:05 +08:00
c3e3d6de61 2.9和2.10一并提交,
2.9:添加了初始化屏幕方向
2.10:添加了init.json,进行初始化ip等,使用mt管理器进行修改,从而实现医院不用配置ip
2025-09-23 09:09:47 +08:00
bc8142c157 修复状态栏引擎、显示的默认值 2025-07-28 14:43:58 +08:00
46b6b43a0f 2.8 编写一个通用的广播模式,action:chaoran.crtech.cn.pda.scan key:barcode;并且写了一个固定的注册码,但是这个注册码只能注册一个小时 2025-06-25 11:04:14 +08:00
d949619023 2.7 海康威视 mv-idp5204 适配广播模式;和之前的idp5102厂家code不一样 2025-06-20 17:23:29 +08:00
1e3c32eb2e 2.6、缓存清除,错误提示,网络不通页面 2025-05-21 15:18:37 +08:00
c5579771d9 2.5 mac地址存储文件(mac获取失败就随机一个);监听返回按键,提示是否退出程序的提示窗口 2025-04-18 09:57:07 +08:00
2d43980458 2.4 适配 qualcomm:mc50 PDA 广播模式 2025-01-16 09:16:05 +08:00
0800e0eaf0 eda56可以作用于eda52 2025-01-14 11:02:20 +08:00
ce30c6cf7b 2.3 适配IOT_Device:sc55g PDA 广播模式 2024-12-31 14:02:52 +08:00
cdc07256ee 2.2 安卓14以上,无法获取mac地址,修改成获取唯一id作为mac地址 2024-12-11 14:15:57 +08:00
47715f0fd6 2.1 注册PDA的信息存储到文件内部,采取mac+固定加密串的MD5加密校验 2024-12-09 15:14:20 +08:00
59c228f36c 1.19 index.html初始化参数 2024-11-19 14:52:11 +08:00
5769b7670b 1.18 瑞兴平板,读取扫描结果,使用同步加锁模式 2024-11-14 16:05:54 +08:00
12c928f059 1.17 霍尼韦尔EDA51、EDA50P,调用扫描枪的方法,在关闭的时候停止调用扫描枪 2024-08-27 13:41:54 +08:00
724e691188 V16版本,idata不默认设置广播模式 2024-08-16 10:31:50 +08:00
e24a8955a2 1.15 系统状态栏根据配置是否显示 2024-08-04 18:03:08 +08:00
ec798944ad 1.14 瑞芯 rk3566_r 添加引用 librockchip.so; 新加一个方法,返回当前PDA的厂家和型号 2024-07-24 10:04:56 +08:00
57094bfbd4 新大陆 pda 广播模式兼容 2024-06-06 16:50:49 +08:00
4af4aac1e5 PDA旋转,不重置activity;采取配置化,是否强制竖屏、强制横屏 2024-05-29 14:06:56 +08:00
23df4da1ff 霍尼韦尔厂家的pda,只有固定的型号走服务,其他的都使用默认的广播模式;兼容eda52 2024-05-07 14:51:31 +08:00
5f9b5cd2e5 bug 修改 2024-04-07 16:41:32 +08:00
3e4af1c2d1 1.9 海康威视 mv-idp5102 适配广播模式 2024-03-11 13:53:23 +08:00
2a13c1a886 jdk11改成jdk8 2024-01-11 17:29:41 +08:00
832b0e3368 霍尼韦尔EDA50p,在返回桌面,点击了新的扫描之后,出现再进入程序(新打开一样)无法扫描,从而导致问题,添加了一个stop2的方法,针对霍尼韦尔eda50p,不进行销毁,在真正销毁的方法关闭服务,还是存在程序返回桌面无法扫描问题,但是退出程序之后,新打开程序可以扫描 2024-01-11 17:27:22 +08:00
bcfb6155d2 暴露一个方法,跳转到index初始化页面 2024-01-09 11:47:38 +08:00
10fdade4da 阿尔卑斯 多测试了一个型号 2023-11-16 10:23:31 +08:00
17a0889e08 瑞星平板扫描,线程读取一半就返回处理,回滚gradle版本和Java版本 2023-11-01 13:11:56 +08:00
483cce8fd5 urovo DT50 Lite pda的支持 2023-09-01 09:51:56 +08:00
46199cda0f 联新 pda的支持 已经 adapter 关闭的判断 2023-08-21 13:35:56 +08:00
3bc08317d0 版本1.3 2023-07-11 12:08:07 +08:00
40c330795c 东集PDA 兼容 2023-07-11 12:01:43 +08:00
0a19e1f587 jdk11、gradle7.3,霍尼维尔EDA56,安卓12 2023-06-13 14:01:07 +08:00
e2a34d76a6 海信PDA兼容 2023-06-12 17:52:13 +08:00
4aa71457fe 霍尼韦尔的监听修改(扫描网站二维码跳出程序,监听失效,调整)、斑马PDA广播模式设置 2023-04-21 15:03:30 +08:00
3613dd6a21 获取APK版本号 2023-04-20 10:40:28 +08:00
26e9ad63f5 IDATA 扫描广播监听修改 2023-04-14 14:14:48 +08:00
2485332d77 霍尼维尔的EDA51扫描适配 2023-04-10 16:17:49 +08:00
27216aa2af 霍尼维尔的EDA50P扫描正常 2023-04-10 15:37:18 +08:00
2c72ce04f6 正式讯飞语音环境 2023-04-10 10:51:36 +08:00
f80dd211f1 音频弹窗提示取消 2022-11-07 14:32:46 +08:00
5199147f58 音频文件每一次播报的时候,都重新加载 2022-11-07 10:16:07 +08:00
d80325553d 网络保存 2022-11-04 15:11:45 +08:00
0f435a565d APK旋转修改 2022-11-04 13:46:57 +08:00
0cb5922557 APK更换icon,成功音频 2022-11-03 17:24:16 +08:00
d63d7ecee3 旋转屏幕,使用的是重力感应自旋转,但是一切手动切换了屏幕的旋转,那么重力旋转就无效了 2022-11-02 09:32:52 +08:00
6786cf2908 删除了打印 2022-11-01 17:19:58 +08:00
a56e76f552 0:表示正竖屏,其他表示倒竖屏 2022-11-01 15:25:48 +08:00
9cad5923d8 使用view的旋转,键盘没有旋转,最后使用pda开启屏幕旋转,调用api实现竖屏旋转(横屏旋转暂时没有解决) 2022-11-01 14:55:18 +08:00
6fb1768a1b 状态栏的设置,最后采取的是隐藏状态栏,透明状态栏需要在页面中调整布局;
旋转使用的是api进行的旋转,不使用css进行旋转
2022-11-01 11:28:44 +08:00
b4e1d2c06f 状态栏的设置,最后采取的是隐藏状态栏,透明状态栏需要在页面中调整布局 2022-10-31 11:51:27 +08:00
f1137bfbf2 正式讯飞语音环境 2022-10-20 14:22:04 +08:00
4bd42b98d5 正式讯飞语音环境 2022-10-20 14:12:18 +08:00
8ce4133e50 正式讯飞语音环境 2021-05-27 18:14:01 +08:00
05b8ddf0fd Merge branch 'master' of http://chaoran.crtech.cn:3000/PDA/pda-web 2021-02-20 14:03:11 +08:00
61a9adab5e Merge branch 'master' of http://chaoran.crtech.cn:3000/PDA/pda-web 2021-02-19 14:12:47 +08:00
50a191f675 Merge branch 'master' of http://chaoran.crtech.cn:3000/PDA/pda-web 2021-02-18 13:42:43 +08:00
bbd9a7e85c 1 2021-02-18 13:42:30 +08:00
67 changed files with 4098 additions and 92 deletions

2
.idea/compiler.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" />
<bytecodeTargetLevel target="11" />
</component>
</project>

2
.idea/encodings.xml generated
View File

@ -7,8 +7,6 @@
<file url="file://$PROJECT_DIR$/app/jni/gen_SerialPort_h.sh" charset="GBK" />
<file url="file://$PROJECT_DIR$/app/jni/rockchip.c" charset="GBK" />
<file url="file://$PROJECT_DIR$/app/libs/armeabi" charset="GBK" />
<file url="file://$PROJECT_DIR$/app/libs/armeabi-v7a" charset="GBK" />
<file url="file://$PROJECT_DIR$/app/libs/armeabi-v7a/libmsc.so" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/app/libs/armeabi/librockchip.so" charset="GBK" />
<file url="file://$PROJECT_DIR$/app/libs/x86" charset="GBK" />
<file url="file://$PROJECT_DIR$/app/src/main/java/chaoran/business/adapter/librockchip.so" charset="GBK" />

View File

@ -31,5 +31,15 @@
<option name="name" value="maven2" />
<option name="url" value="https://maven.aliyun.com/repository/jcenter/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

2
.idea/misc.xml generated
View File

@ -10,7 +10,7 @@
</option>
</component>
<component name="PhpWorkspaceProjectConfiguration" backward_compatibility_performed="true" interpreter_name="PHP-5.6.25" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -3,17 +3,62 @@ plugins {
}
android {
compileSdkVersion 29
buildToolsVersion "30.0.2"
compileSdk 28
buildToolsVersion '30.0.3'
defaultConfig {
applicationId "chaoran.business.pda"
minSdkVersion 22
targetSdkVersion 29
minSdk 28
targetSdk 28
versionCode 1
versionName "1.0"
versionName "2.18"
// 1.0 IDATA广播模式处理
// 1.1 霍尼韦尔的监听修改扫描网站二维码跳出程序监听失效调整、斑马PDA广播模式设置
// 1.2 霍尼韦尔EDA56、jdk11gradle7.3、海信PDA广播支持
// 1.3 东集 pda的支持
// 1.4 联新 pda的支持 已经 adapter 关闭的判断
// 1.5 urovo DT50 Lite pda的支持
// 1.6 瑞星平板扫描,线程读取一半就返回处理
// 1.7 暴露一个方法跳转到index初始化页面
// 1.8 霍尼韦尔EDA50p在返回桌面点击了新的扫描之后出现再进入程序(新打开一样)无法扫描从而导致问题添加了一个stop2的方法针对霍尼韦尔eda50p不进行销毁在真正销毁的方法关闭服务还是存在程序返回桌面无法扫描问题但是退出程序之后新打开程序可以扫描
// 1.9 海康威视 mv-idp5102 适配广播模式
// 1.10 bug 代码判断没有结束
// 1.11 霍尼韦尔EDA52
// 1.12 屏幕旋转采取配置化模式只能竖屏、横屏取消旋转屏幕就重置activity生命周期
// 1.13 新大陆pda 兼容广播模式NLS-NFT10
// 1.14 瑞芯 rk3566_r 添加引用 librockchip.so 新加一个方法返回当前PDA的厂家和型号
// 1.15 系统状态栏根据配置进行设置
// 1.16 idata pda 不设置模式
// 1.17 霍尼韦尔EDA51、EDA50P调用扫描枪的方法在关闭的时候停止调用扫描枪
// 1.18 瑞兴平板,读取扫描结果,使用同步加锁模式
// 1.19 index页面接入初始化数据
// 2.1 注册PDA的信息存储到文件内部采取mac+固定加密串的MD5加密校验
// 2.2 安卓14以上无法获取mac地址修改成获取唯一id作为mac地址
// 2.3 适配IOT_Device:sc55g PDA 广播模式
// 2.4 适配 qualcomm:mc50 PDA 广播模式
// 2.5 mac地址存储文件mac获取失败就随机一个监听返回按键提示是否退出程序的提示窗口
// 2.6 1、打开程序的时候清空缓存为了程序更新的时候不被缓存影响不用手动清除、出现了用户手动清除的时候吧存储空间清除了
// 2、判断是否初始化成功(webview成功)如果已经加载完毕那么apk不在拦截调用js的方法让BS进行处理提示
// 3、根据官方文档将code的判断进行修改添加了一个网络错误的页面进行提示(并且添加了一个重新加载的按钮执行webview重新加载)
// 2.7 海康威视 mv-idp5204 适配广播模式和之前的idp5102厂家code不一样 宿州市立医院-李德
// 2.8 编写一个通用的广播模式actionchaoran.crtech.cn.pda.scan keybarcode
// 2.9 初始化屏幕方向
// 2.10 再次添加了init.json文件进行初始化ip等参数
// 2.11 优化了保存配置的时候重新加载页面方法去设置初始化屏幕方向已经init.json中添加了字段备注信息
// 2.12 群创科技接入广播模式 中山市古镇人民医院 蒋凡
// 2.13 取消监听旋转角度,使用系统自带的旋转(根据配置初始化,旋转方向:横、竖、随意)
// 瑞芯适配器 接入 新的型号,使用的是 ttyS8而不是ttyS1并且只有一个接口。
// 2.14 适配 AIFUU 陈安良:陆军特色中心医院
// 2.15 添加文件选择和相机拍照功能
// 2.16 暴露了一个直接调用相机的方法非http input调用会出现默认的弹出窗口进行选择相机、文件
// 2.17 支持原生相机默认压缩支持霍尼韦尔eda52服务扫描
// 2.18 瑞兴平板rk3568_r存在两个ttyS8、ttyS12G内存之前是ttyS8才可以接收4G内存的ttyS8直接报错导致打不开需要缓存ttyS1改成兼容模式
// 支持霍尼韦尔eda52服务扫描修改成广播模式医院那边不要所谓的服务连续扫描
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters 'armeabi-v7a'
}
}
//签名配置
@ -54,12 +99,22 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
/* repositories {
flatDir {
dirs 'libs'
}
}*/
}
dependencies {
compileOnly 'com.symbol:emdk:9.1.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'com.google.code.gson:gson:2.6.2'
//加载jar包
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.preference:preference:1.1.1'
//implementation fileTree(include: ['*.aar'], dir: 'libs')
// 全屏,没有状态栏
api 'com.readystatesoftware.systembartint:systembartint:1.0.3'
implementation files('libs/armeabi-v7a/librockchip.so')
}

BIN
app/libs/DataCollection.jar Normal file

Binary file not shown.

BIN
app/libs/Msc.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,7 +1,7 @@
<?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="chaoran.business">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@ -10,23 +10,40 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="com.symbol.emdk.permission.EMDK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="com.honeywell.decode.permission.DECODE" />
<uses-permission android:name="android.permission.STOP_SERVICE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:fitsSystemWindows="true"
android:name=".application.InitApplication"
android:theme="@style/Theme.PdaWeb"
android:usesCleartextTraffic="true">
android:usesCleartextTraffic="true"
>
<uses-library android:name="com.symbol.emdk" android:required="false"/>
<activity
android:name=".activity.NetworkSettingActivity"
android:label="@string/title_activity_setting_network" />
<activity
android:name=".activity.VoiceSettingActivity"
android:label="@string/title_activity_setting_voice" />
<activity
android:name=".activity.FileTestActivity"
android:label="@string/title_activity_file_test" />
<activity
android:name=".activity.MainActivity"
android:configChanges="orientation|screenSize"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -34,6 +51,27 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".service.ScanServiceEDA50P" android:exported="true">
<intent-filter>
<action android:name="com.example.chaoran.ScanServiceEDA50P"/>
</intent-filter>
</service>
<service android:name=".service.ScanServiceZEBRA">
<intent-filter>
<action android:name="com.example.chaoran.ScanServiceZEBRA"/>
</intent-filter>
</service>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>

View File

@ -0,0 +1,175 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件选择测试</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; max-width: 600px; margin: 0 auto; }
.section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
h2 { color: #333; margin-top: 0; }
input[type="file"] { display: block; margin: 10px 0; padding: 10px; width: 100%; box-sizing: border-box; }
.preview { margin-top: 15px; max-width: 100%; }
.preview img { max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 5px; }
.info { margin-top: 10px; padding: 10px; background-color: #f5f5f5; border-radius: 5px; font-size: 14px; }
</style>
</head>
<body>
<h1>文件选择和相机测试</h1>
<!-- 测试1选择任意文件 -->
<div class="section">
<h2>1. 选择任意文件</h2>
<input type="file" id="fileInput" onchange="handleFileSelect(this)">
<div id="fileInfo" class="info" style="display:none;"></div>
</div>
<!-- 测试2选择图片支持相机和文件 -->
<div class="section">
<h2>2. 选择图片(支持相机)</h2>
<!-- accept="image/*": 只接受图片, capture="environment": 启用相机 -->
<input type="file" id="imageInput" accept="image/*" capture="environment" onchange="handleImageSelect(this)">
<div id="imageInfo" class="info" style="display:none;"></div>
<div id="imagePreview" class="preview"></div>
</div>
<!-- 测试3直接打开相机 -->
<div class="section">
<h2>3. 仅使用相机拍照</h2>
<!-- 通过JavaScript接口直接调用相机不显示选择器 -->
<button onclick="openCameraDirectly()" style="padding: 10px 20px; font-size: 16px; cursor: pointer;">打开相机</button>
<div id="cameraInfo" class="info" style="display:none;"></div>
<div id="cameraPreview" class="preview"></div>
</div>
<script>
/**
* 处理文件选择 - 显示文件信息
*/
function handleFileSelect(input) {
var file = input.files[0]; // 获取选择的文件
var infoDiv = document.getElementById('fileInfo'); // 获取信息显示区域
if (file) {
infoDiv.style.display = 'block'; // 显示信息区域
infoDiv.innerHTML = '<strong>文件信息:</strong><br>文件名: ' + file.name + '<br>文件大小: ' + formatFileSize(file.size) + '<br>文件类型: ' + (file.type || '未知');
} else {
infoDiv.style.display = 'none'; // 隐藏信息区域
}
}
/**
* 处理图片选择 - 显示信息和预览
*/
function handleImageSelect(input) {
var file = input.files[0]; // 获取选择的文件
var infoDiv = document.getElementById('imageInfo');
var previewDiv = document.getElementById('imagePreview');
if (file) {
infoDiv.style.display = 'block';
infoDiv.innerHTML = '<strong>图片信息:</strong><br>文件名: ' + file.name + '<br>文件大小: ' + formatFileSize(file.size) + '<br>文件类型: ' + file.type;
// 使用FileReader读取图片并显示预览
var reader = new FileReader();
reader.onload = function(e) {
// e.target.result 是图片的Base64数据
previewDiv.innerHTML = '<img src="' + e.target.result + '" alt="图片预览">';
};
reader.readAsDataURL(file); // 读取为DataURL格式
} else {
infoDiv.style.display = 'none';
previewDiv.innerHTML = '';
}
}
/**
* 直接打开相机 - 通过Android接口
*/
function openCameraDirectly() {
// CameraHelper是Android注入的JavaScript接口
if (typeof CameraHelper !== 'undefined') {
CameraHelper.openCamera(); // 调用Android方法打开相机
} else {
alert('相机功能不可用');
}
}
/**
* 相机拍照结果回调 - 由Android调用
* @param uri - 照片URI
* @param path - 照片路径
*/
function onCameraResult(uri, path) {
var infoDiv = document.getElementById('cameraInfo');
var previewDiv = document.getElementById('cameraPreview');
if (uri && path) {
// 拍照成功
infoDiv.style.display = 'block';
infoDiv.innerHTML = '<strong>拍照成功:</strong><br>URI: ' + uri + '<br>路径: ' + path;
previewDiv.innerHTML = '<img src="' + uri + '" alt="照片预览">';
} else {
// 拍照取消
infoDiv.style.display = 'block';
infoDiv.innerHTML = '<strong>拍照已取消</strong>';
previewDiv.innerHTML = '';
}
}
/**
* 格式化文件大小 - 字节转换为KB/MB/GB
*/
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
var k = 1024; // 换算基数
var sizes = ['Bytes', 'KB', 'MB', 'GB']; // 单位数组
var i = Math.floor(Math.log(bytes) / Math.log(k)); // 计算单位索引
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; // 计算并返回
}
</script>
</body>
</html>
<!--
其他地方接入
<input type="file" accept="image/*" capture="environment" onchange="handlePhoto(this)">
// 处理照片选择
function handlePhoto(input) {
var file = input.files[0];
if (file) {
// 读取文件
var reader = new FileReader();
reader.onload = function(e) {
var base64Data = e.target.result;
// 显示预览
document.getElementById('preview').innerHTML =
'<img src="' + base64Data + '" style="max-width: 100%;">';
// 上传到服务器
uploadPhoto(base64Data);
};
reader.readAsDataURL(file);
}
}
// 上传照片
function uploadPhoto(base64Data) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/api/upload', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status === 200) {
console.log('上传成功');
}
};
xhr.send(JSON.stringify({
image: base64Data
}));
}
-->

View File

@ -96,21 +96,29 @@
outline: -webkit-focus-ring-color auto 0px;
}
</style>
<script>
function saveSetting(){
var data=JSON.stringify({
"address":document.getElementById('address').value,
"port":document.getElementById('port').value,
"path":document.getElementById('path').value
});
window.NetworkSettingEngine.save(data);
window.View.reload();
.radio-group {
display: flex;
padding: 10px;
}
.radio-group>span {
display: inline-block;
width: 110px;
min-width: 110px;
}
.radio-group .radio-group-box {
display: flex;
flex-wrap: wrap; /* 允许换行 */
gap: 10px 20px; /* 垂直和水平间距 */
width: 100%;
}
.radio-group .radio-group-box .radio-item {
width: calc(50% - 10px);
}
.radio-group .radio-group-box .radio-item.full {
width: 100%;
}
</script>
</style>
</head>
@ -130,6 +138,56 @@
<label>访问子路径</label>
<input id="path" name="path" placeholder="访问子路径">
</div>
<div class="radio-group">
<span>屏幕方向</span>
<div class="radio-group-box">
<label class="radio-item">
<input type="radio" name="screen_rotation" value="5" checked>
正竖
</label>
<label class="radio-item">
<input type="radio" name="screen_rotation" value="6" checked>
倒竖
</label>
<label class="radio-item full">
<input type="radio" name="screen_rotation" value="1" checked>
竖屏(系统可能不支持)
</label>
<label class="radio-item">
<input type="radio" name="screen_rotation" value="7">
正横
</label>
<label class="radio-item">
<input type="radio" name="screen_rotation" value="8">
倒横
</label>
<label class="radio-item full">
<input type="radio" name="screen_rotation" value="2">
横屏
</label>
<label class="radio-item">
<input type="radio" name="screen_rotation" value="3">
横竖
</label>
<label class="radio-item">
<input type="radio" name="screen_rotation" value="4">
禁止
</label>
</div>
</div>
<div class="radio-group">
<span>隐藏状态栏</span>
<div class="radio-group-box">
<label class="radio-item">
<input type="radio" name="hide_bar" value="1" checked>
隐藏
</label>
<label class="radio-item">
<input type="radio" name="hide_bar" value="2">
显示
</label>
</div>
</div>
</div>
<div class="operation">
<button style="background: #1989fa;" onclick="saveSetting()">保存</button>
@ -138,4 +196,35 @@
</div>
</div>
</body>
<script>
function init() {
var dstr = window.NetworkSettingEngine.getStringSetting();
if (dstr.length > 0) {
var d = JSON.parse(dstr);
document.getElementById('address').value = d.address;
document.getElementById('port').value = d.port;
document.getElementById('path').value = d.path;
document.querySelector('input[name="screen_rotation"][value="'+d.screen_rotation+'"]').checked = true;
document.querySelector('input[name="hide_bar"][value="'+d.hide_bar+'"]').checked = true;
console.log("d_data_end_=========================");
}
}
init();
function saveSetting(){
var data=JSON.stringify({
"address":document.getElementById('address').value,
"port":document.getElementById('port').value,
"path":document.getElementById('path').value,
"screen_rotation":document.querySelector('input[name="screen_rotation"]:checked').value,
"hide_bar":document.querySelector('input[name="hide_bar"]:checked').value
});
window.NetworkSettingEngine.saveSetting(data);
window.View.reload();
}
</script>
</html>

View File

@ -0,0 +1,14 @@
<!-- 手动添加了一个网络错误提示 -->
<html lang="zh-CN">
<body>
<p>网络错误,无法连接到服务器,请检查网络</p>
<button onclick="reLoadWebView()">重新加载</button>
</body>
<script>
function reLoadWebView(){
window.View.reload();
}
</script>
</html>

View File

@ -0,0 +1,8 @@
{
"备注": "下面所有的字段删除前面的_就是配置的字段在没有手动配置的情况下会读取字段中的信息从而实现默认配置",
"_ip":"ip地址",
"_port":"端口号",
"_path":"访问路径",
"_screen_rotation":"屏幕方向5正竖;6倒竖;7正横;8倒横;1竖;2横;3横竖;4禁止",
"_hide_bar":"状态拦1隐藏、2显示"
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -10,11 +10,25 @@ package chaoran.business;
public enum BrandEnum {
//枚举名即为valueOf()
AIFUU("AIFUU", "AIFUU"),
HORIEMTECH("群创科技", "Horiemtech"),
QUALCOMM("qualcomm", "qualcomm"),
IOT_DEVICE("新大陆", "iot_device"),
NEW_LAND("新大陆", "newland"),
HISENSE("海信", "hisense"),
UROVO("DT50 Lite", "urovo"),
LACHESIS("联新", "lachesis"),
UROBO("优博讯", "urobo"),
ROCKCHIP("瑞芯微电子", "rockchip"),
TEST("测试设备", "test"),
IDATA("匿名设备", "idata"),
ALPS("阿尔卑斯", "alps");
HONEY_WELL_EDA50P("霍尼维尔EDA50P", "mobiwire"),
HONEY_WELL_EDA51("霍尼维尔EDA51", "honeywell"),
ZEBRA_TECHNOLOGIES("斑马TP26CK", "zebra technologies"),
SEUIC("东集", "seuic"),
ALPS("阿尔卑斯", "alps"),
HKWS("海康威视", "mv-idp5102"),
HKWS2("海康威视", "droi"); // mv-idp5204 宿州市立医院
private String name;
private String code;
@ -35,7 +49,7 @@ public enum BrandEnum {
code = new String();
}
for (BrandEnum brandEnum : values()) {
if (brandEnum.code.equals(code.toLowerCase())) {
if (brandEnum.code.equalsIgnoreCase(code)) {
return brandEnum;
}
}

View File

@ -0,0 +1,113 @@
package chaoran.business.activity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import chaoran.business.R;
import chaoran.business.utils.FileChooserHelper;
import chaoran.business.utils.CameraHelper;
/**
* 文件选择测试Activity
*/
public class FileTestActivity extends AppCompatActivity {
private WebView webView;
private FileChooserHelper fileChooserHelper;
private CameraHelper cameraHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_test);
fileChooserHelper = new FileChooserHelper(this);
webView = findViewById(R.id.webViewFileTest);
cameraHelper = new CameraHelper(this, webView);
initWebView();
// 加载测试页面
webView.loadUrl("file:///android_asset/demo/file_test.html");
}
@SuppressLint("SetJavaScriptEnabled")
private void initWebView() {
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
// 添加JavaScript接口
webView.addJavascriptInterface(cameraHelper, "CameraHelper");
webView.setWebViewClient(new WebViewClient());
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
// 检查是否接受图片
String[] acceptTypes = fileChooserParams.getAcceptTypes();
boolean acceptImage = false;
boolean acceptCamera = false;
boolean cameraOnly = false;
if (acceptTypes != null && acceptTypes.length > 0) {
for (String type : acceptTypes) {
if (type.contains("image")) {
acceptImage = true;
break;
}
}
}
// 检查capture模式
if (fileChooserParams.isCaptureEnabled()) {
acceptCamera = true;
// 注意Android WebView中capture="user"或capture="environment"都会返回true
// 我们通过简单的方式判断如果是图片且启用capture默认显示选择器
// 只有在特定情况下才直接打开相机
// 这里我们不设置cameraOnly=true让所有capture都显示选择器
// 如果需要仅相机模式可以通过其他方式如URL参数来控制
}
fileChooserHelper.openFileChooser(filePathCallback, acceptImage, acceptCamera, cameraOnly);
return true;
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (!fileChooserHelper.onActivityResult(requestCode, resultCode, data)) {
cameraHelper.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
fileChooserHelper.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
}

View File

@ -1,15 +1,33 @@
package chaoran.business.activity;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Vibrator;
import android.util.JsonReader;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.webkit.*;
import android.widget.ProgressBar;
import android.widget.Toast;
@ -17,13 +35,31 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import chaoran.business.BrandEnum;
import chaoran.business.R;
import chaoran.business.adapter.*;
import chaoran.business.engine.entity.NetworkSetting;
import chaoran.business.engine.impl.NetworkSettingEngine;
import chaoran.business.engine.SettingEngine;
import chaoran.business.engine.impl.TekVoiceEngine;
import chaoran.business.engine.VoiceEngine;
import chaoran.business.service.ScanServiceEDA50P;
import chaoran.business.service.ScanServiceZEBRA;
import chaoran.business.utils.DataCleanManager;
import chaoran.business.utils.LocalAddressUtil;
import chaoran.business.utils.StatusBarUtil;
import chaoran.business.utils.FileChooserHelper;
/**
* 流程:联网认证设备型号,验证通过,查找设备品牌进行调用驱动操作
@ -34,25 +70,81 @@ import chaoran.business.engine.VoiceEngine;
*/
public class MainActivity extends AppCompatActivity implements ResultListener{
public static int SCREEN_ROTATION = 3; // 屏幕旋转的设置
public static int hideBar = 0; // 屏幕旋转的设置
private WebView webView;
private Adapter adapter;
private VoiceEngine voiceEngine;
private SettingEngine settingEngine;
private ProgressBar progressBar;
private ActionBar actionBar;
private FileChooserHelper fileChooserHelper;
private LocalAddressUtil localAddressUtil;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
DataCleanManager.clearAllCache(this); // 清空缓存
}catch (Exception e) {
e.printStackTrace();
}
initView();
initData();
//SCREEN_ORIENTATION_USER不能旋转1800、90、270都可以旋转
//SCREEN_ORIENTATION_FULL_SENSOR 都可以旋转,但是在不打开旋转的情况下,一样可以旋转
//SCREEN_ORIENTATION_FULL_USER 关闭旋转则不会旋转了
// 取消监听屏幕方向使用用户的等出现了问题再来调整之前有一款pda系统旋转有bug
// this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
if (SCREEN_ROTATION == 1) {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);//竖
}else if (SCREEN_ROTATION == 2) {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);//横
}else if (SCREEN_ROTATION == 3){
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);//随便
}else if (SCREEN_ROTATION == 5) { // 正竖
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}else if (SCREEN_ROTATION == 6) {// 倒竖
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
}else if (SCREEN_ROTATION == 7) {// 正横
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
}else if (SCREEN_ROTATION == 8) {// 倒横
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}else {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); // 禁止
}
// myOrientationDetector = new MyOrientationDetector(this, this);
// myOrientationDetector.enable();
}
public static String readInitFile(Context context) {
StringBuffer init = new StringBuffer();
try {
AssetManager assetManager = context.getAssets();
// 打开文件输入流
InputStream inputStream = assetManager.open("init.json");
// 使用BufferedReader进行逐行读取
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
init.append(line);
}
reader.close();
} catch (IOException e) {
Log.e("AssetError", "Failed to read asset file: init.json", e);
}
return init.toString();
}
private void initData() {
BrandEnum brand = BrandEnum.code(Build.MANUFACTURER);
Toast.makeText(this, Build.MANUFACTURER, Toast.LENGTH_LONG).show();
Log.i("brand===",brand.toString());
switch (brand) {
case UROBO:
adapter = new UroBoAdapter(this, this);
@ -66,22 +158,78 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
case ALPS:
adapter = new AlpsAdapter(this, this);
break;
case SEUIC:
adapter = new SeuicAdapter(this, this);
break;
case HONEY_WELL_EDA50P:
// 51、56都是同一个code
case HONEY_WELL_EDA51:
if (
"eda50p".equals(Build.MODEL.toLowerCase())
|| "eda51".equals(Build.MODEL.toLowerCase())
// || "eda52".equals(Build.MODEL.toLowerCase()) // todo
|| "tc26".equals(Build.MODEL.toLowerCase())
) {
// 走服务模式
adapter = new HoneywellAdapter(this, this);
}else {
// 走广播模式
adapter = new HoneywellEda56Adapter(this, this);
}
break;
case ZEBRA_TECHNOLOGIES:
adapter = new ZebraAdapter(this, this);
break;
case HISENSE:
adapter = new HisenseAdapter(this, this);
break;
case LACHESIS:
adapter = new LachesisAdapter(this, this);
break;
case UROVO:
adapter = new UrovoAdapter(this, this);
break;
case HKWS:
case HKWS2:
adapter = new HkwsAdapter(this, this);
break;
case NEW_LAND:
adapter = new NewlandAdapter(this, this);
break;
case IOT_DEVICE:
adapter = new IOT_DeviceAdapter(this, this);
break;
case QUALCOMM:
adapter = new QualcommAdapter(this, this);
break;
case HORIEMTECH:
adapter = new HoriemtechAdapter(this, this);
break;
case AIFUU:
adapter = new AifuuAdapter(this, this);
break;
default:
adapter = new DefaultAdapter(this, this);
}
if (null != adapter) {
adapter.start();
}
}
@SuppressLint("JavascriptInterface")
private void initView() {
actionBar = getSupportActionBar();
voiceEngine = new TekVoiceEngine(this);
settingEngine = new NetworkSettingEngine(this);
fileChooserHelper = new FileChooserHelper(this);
webView = findViewById(R.id.webView);
progressBar = findViewById(R.id.loading);
webView.setWebViewClient(disposeView());
webView.setWebChromeClient(createWebChromeClient());
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
//设置接口进行windows暴露
settings.setDomStorageEnabled(true);
//语音引擎
@ -90,7 +238,57 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
webView.addJavascriptInterface(settingEngine, "NetworkSettingEngine");
//重新加载页面
webView.addJavascriptInterface(this, "View");
localAddressUtil = new LocalAddressUtil(this, this, webView);
webView.addJavascriptInterface(localAddressUtil, "Localpda");
webView.loadUrl(url());
// StatusBarUtil.transparencyBar( this); // 设置全部透明需要在页面设置一个参数进行布局的样式跳转不同的手机端状态栏高度不一样apk设置状态栏高度无效这个是安卓9的一个bug所以在此采取隐藏状态栏
if (hideBar == 1) {
StatusBarUtil.hideStatusBar( this); // 设置全部透明
}
}
private boolean isPageFinished = false; // 新增标志位
/**
* 创建WebChromeClient以支持文件选择
*/
private WebChromeClient createWebChromeClient() {
return new WebChromeClient() {
// For Android 5.0+
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
// 检查是否接受图片
String[] acceptTypes = fileChooserParams.getAcceptTypes();
boolean acceptImage = false;
boolean acceptCamera = false;
boolean cameraOnly = false;
if (acceptTypes != null && acceptTypes.length > 0) {
for (String type : acceptTypes) {
if (type.contains("image")) {
acceptImage = true;
break;
}
}
}
// 检查capture模式
if (fileChooserParams.isCaptureEnabled()) {
acceptCamera = true;
// 注意Android WebView中capture="user"或capture="environment"都会返回true
// 我们通过简单的方式判断如果是图片且启用capture默认显示选择器
// 只有在特定情况下才直接打开相机
// 这里我们不设置cameraOnly=true让所有capture都显示选择器
// 如果需要仅相机模式可以通过其他方式如URL参数来控制
}
fileChooserHelper.openFileChooser(filePathCallback, acceptImage, acceptCamera, cameraOnly);
return true;
}
};
}
//配置客户端
@ -101,6 +299,7 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressBar.setVisibility(View.VISIBLE);
isPageFinished = false;
}
//页面加载完成时
@ -112,33 +311,31 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
actionBar.show();
super.onPageFinished(view, url);
progressBar.setVisibility(View.INVISIBLE);
isPageFinished = true;
}
//网络发生错误时,先展示错误界面,然后关闭加载条
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
switch (errorCode) {
case 404:
webView.loadUrl("file:///android_asset/error/404.html");
break;
case 500:
webView.loadUrl("file:///android_asset/error/500.html");
break;
default:
webView.loadUrl("file:///android_asset/error/index.html");
}
setHtml(webView, errorCode);
actionBar.show();
actionBar.setTitle(R.string.title_activity_main);
super.onReceivedError(view, errorCode, description, failingUrl);
progressBar.setVisibility(View.INVISIBLE);
}
//安卓6.0以上发生错误时回调
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
switch (error.getErrorCode()) {
private void setHtml(WebView view, int errorCode) {
switch (errorCode) {
// https://www.apiref.com/android-zh/android/webkit/WebViewClient.html根据这个code进行的设置
// -数的都是有
case -2:
case -6:
case -8:
webView.loadUrl("file:///android_asset/error/netError.html");
break;
case 404:
case -14:
case -12:
webView.loadUrl("file:///android_asset/error/404.html");
break;
case 500:
@ -148,10 +345,27 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
webView.loadUrl("file:///android_asset/error/index.html");
}
}
private void handleReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
if (isPageFinished) {
// 页面已加载完成,交由 JS 处理错误
String jsError = String.format("javascript:handleWebError(%d, '%s')", error.getErrorCode(), error.getDescription());
view.loadUrl(jsError);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setHtml(webView, error.getErrorCode());
}
actionBar.show();
actionBar.setTitle(R.string.title_activity_main);
super.onReceivedError(view, request, error);
}
progressBar.setVisibility(View.INVISIBLE);
super.onReceivedError(view, request, error);
}
//安卓6.0以上发生错误时回调
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
handleReceivedError(view, request, error);
}
};
}
@ -160,9 +374,24 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
@Override
protected void onResume() {
//再次唤醒该页面时,重新加载页面和语音配置
webView.loadUrl(url());
voiceEngine.reload();
//webView.loadUrl(url());
// voiceEngine.reload();
super.onResume();
if (adapter != null) {
adapter.stop2();
adapter.start();
}
// int rotate = 1;
// if (startDirection == 1) {
// rotate = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; // 正竖屏
// }else if (startDirection == 2) {
// rotate = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; // 倒竖屏
// }else if (startDirection == 3) {
// rotate = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; // 正横屏
// }else if (startDirection == 4) {
// rotate = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; // 倒横屏
// }
// this.setRequestedOrientation(rotate);
}
@Override
@ -170,11 +399,31 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
public void result(String result) {
runOnUiThread(() -> webView.loadUrl("javascript:render(\'" + result + "\')"));
}
@Override
protected void onDestroy() {
if (adapter != null) {
adapter.stop();
}
super.onDestroy();
if (diPlayer != null) {
diPlayer.stop();
diPlayer.release();
diPlayer = null;
}
if (duPlayer != null) {
duPlayer.stop();
duPlayer.release();
duPlayer = null;
}
if (dingPlayer != null) {
dingPlayer.stop();
dingPlayer.release();
dingPlayer = null;
}
if (vibrator != null) {
vibrator.cancel();
}
completionListener = null;
}
@Override
@ -192,6 +441,9 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
case R.id.action_setting_voice:
startActivity(new Intent(this, VoiceSettingActivity.class));
break;
case R.id.action_file_test:
startActivity(new Intent(this, FileTestActivity.class));
break;
}
return super.onOptionsItemSelected(item);
}
@ -200,12 +452,42 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
private String url() {
SharedPreferences spf = this.getSharedPreferences("crtech", Context.MODE_PRIVATE);
Integer port = spf.getInt("port", -1);
Map<String, Object> map = null;
if (port == -1) {
try {
String init = readInitFile(this);
// String init = null;
if (init != null && init.length() > 0) {
Gson gson = new Gson();
map = gson.fromJson(init, new TypeToken<Map<String, Object>>(){}.getType());
// 说明采取了配置并且存在ip才认为是一个合法的json配置
if (map.containsKey("ip") && map.get("ip").toString().length() > 0) {
NetworkSetting initData = new NetworkSetting();
initData.setAddress(map.get("ip").toString());
initData.setPort(Integer.parseInt(map.get("port").toString()));
initData.setPath(map.get("path").toString());
initData.setScreen_rotation(Integer.parseInt(map.getOrDefault("screen_rotation", "3").toString()));
initData.setHide_bar(Integer.parseInt(map.getOrDefault("hide_bar", "0").toString()));
settingEngine.saveSetting(initData);
spf = this.getSharedPreferences("crtech", Context.MODE_PRIVATE);
port = initData.getPort();
}else {
map = null;
}
}
}catch (Exception e) {
Log.e("MainActivity", "get init file error");
return "file:///android_asset/demo/index.html";
}
if (map == null) {
return "file:///android_asset/demo/index.html";
}
}
String address = spf.getString("address", "").replaceAll(" ", "");
String path = spf.getString("path", "").replaceAll(" ", "");
String link = address.concat(":").concat(String.valueOf(port)).concat(path);
SCREEN_ROTATION = spf.getInt("screen_rotation", 3);
hideBar = spf.getInt("hide_bar", 0);
return link.startsWith("http://") ? link : "http://".concat(link);
}
@ -215,4 +497,107 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
webView.loadUrl(url());
});
}
private MediaPlayer diPlayer = null;
private MediaPlayer duPlayer = null;
private MediaPlayer dingPlayer = null;
private String ttsNr;
public Vibrator vibrator;
@JavascriptInterface
public void play(String msg, String type, int isZd) throws IOException {
ttsNr = msg;
if ("1".equals(type)) {
if (diPlayer == null) {
diPlayer = MediaPlayer.create(this, R.raw.didi);
diPlayer.setOnCompletionListener(completionListener);
}
diPlayer.start();
} else if ("2".equals(type)) {
if (duPlayer == null) {
duPlayer = MediaPlayer.create(this, R.raw.dudu);
duPlayer.setOnCompletionListener(completionListener);
}
duPlayer.start();
} else if ("10".equals(type)) {
if (dingPlayer == null) {
dingPlayer = MediaPlayer.create(this, R.raw.ding);
dingPlayer.setOnCompletionListener(completionListener);
}
dingPlayer.start();
} else {
//isZd = 1;
if (voiceEngine != null) {
voiceEngine.startSpeaking(msg);
}
}
if (isZd == 1) {
if (vibrator == null) {
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
}
vibrator.vibrate(2000);
}
}
private MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
// @Override
public void onCompletion(MediaPlayer mp) {
if (ttsNr != null) {
if (voiceEngine != null) {
voiceEngine.startSpeaking(ttsNr);
}
ttsNr = null;
}
}
};
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public void openIndex() throws IOException {
runOnUiThread(() -> {
webView.loadUrl("file:///android_asset/demo/index.html");
});
}
@SuppressLint("WrongConstant")
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
@Override
public void onBackPressed() {
// 创建退出确认对话框
new AlertDialog.Builder(this)
.setTitle("提示")
.setMessage("是否退出程序?")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish(); // 关闭当前 Activity
// 如果需要彻底退出应用(适用于多任务场景),可以添加:
// System.exit(0);
}
})
.setNegativeButton("取消", null)
.show();
// 不调用 super.onBackPressed(),避免直接退出
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (!fileChooserHelper.onActivityResult(requestCode, resultCode, data)
&& (localAddressUtil == null || !localAddressUtil.onActivityResult(requestCode, resultCode, data))) {
// 如果不是文件选择器的结果,可以在这里处理其他结果
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (!fileChooserHelper.onRequestPermissionsResult(requestCode, permissions, grantResults)
&& (localAddressUtil == null || !localAddressUtil.onRequestPermissionsResult(requestCode, permissions, grantResults))) {
// 如果不是文件选择器的权限请求,可以在这里处理其他权限请求
}
}
}

View File

@ -19,7 +19,7 @@ import chaoran.business.R;
public class NetworkSettingActivity extends AppCompatActivity {
private EditText address, path, port;
private EditText address, path, port, screen_rotation, hide_bar;
private Button save, cancel;
@Override
@ -35,12 +35,16 @@ public class NetworkSettingActivity extends AppCompatActivity {
address = findViewById(R.id.address);
path = findViewById(R.id.path);
port = findViewById(R.id.port);
screen_rotation = findViewById(R.id.screen_rotation);
hide_bar = findViewById(R.id.hide_bar);
save = findViewById(R.id.save);
cancel = findViewById(R.id.cancel);
SharedPreferences sharedPreferences = this.getSharedPreferences("crtech", Context.MODE_PRIVATE);
address.setText(sharedPreferences.getString("address", ""));
path.setText(sharedPreferences.getString("path", ""));
port.setText(String.valueOf(sharedPreferences.getInt("port", -1)));
screen_rotation.setText(String.valueOf(sharedPreferences.getInt("screen_rotation", 3)));
hide_bar.setText(String.valueOf(sharedPreferences.getInt("hide_bar", 0)));
cancel.setOnClickListener((e) -> this.finish());
save.setOnClickListener((e) -> saveSetting());
}
@ -50,6 +54,8 @@ public class NetworkSettingActivity extends AppCompatActivity {
editor.putString("address", address.getText().toString().trim());
editor.putString("path", path.getText().toString().trim());
editor.putInt("port", Integer.parseInt(port.getText().toString().trim()));
editor.putInt("screen_rotation", Integer.parseInt(screen_rotation.getText().toString().trim()));
editor.putInt("hide_bar", Integer.parseInt(hide_bar.getText().toString().trim()));
editor.commit();
this.finish();
}

View File

@ -29,4 +29,8 @@ public interface Adapter{
*/
public void stop();
public default void stop2() {
stop();
}
}

View File

@ -0,0 +1,54 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
/**
* AIFUU 陆军特色中心医院 陈安良
*/
public class AifuuAdapter implements Adapter {
private Context context;
private Strategy strategy;
private ResultListener resultListener;
public AifuuAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("code"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("com.kte.scan.result");
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -20,7 +20,7 @@ import chaoran.business.strategy.Strategy;
/**
* 阿尔卑斯适配器
* CR-5W适用
* CR-5W适用Android Handheld Terminal
*/
public class AlpsAdapter implements Adapter {
private Context context;

View File

@ -0,0 +1,51 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
public class DefaultAdapter implements Adapter {
private Context context;
private Strategy strategy;
private ResultListener resultListener;
public DefaultAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new DefaultAdapter.Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("barcode"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("chaoran.crtech.cn.pda.scan");
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -0,0 +1,53 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
public class HisenseAdapter implements Adapter {
private Context context;
private ResultListener resultListener;
private Strategy strategy;
public HisenseAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("msg"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("chaoran");
filter.setPriority(2);
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -0,0 +1,53 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
public class HkwsAdapter implements Adapter {
private Context context;
private ResultListener resultListener;
private Strategy strategy;
public HkwsAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("barcode"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.ACTION_SCAN_OUTPUT");
filter.setPriority(2);
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -0,0 +1,118 @@
package chaoran.business.adapter;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import java.util.List;
import chaoran.business.activity.ResultListener;
import chaoran.business.service.ScanServiceEDA50P;
import chaoran.business.service.ScanServiceZEBRA;
import chaoran.business.strategy.Strategy;
public class HoneywellAdapter implements Adapter {
private Context context;
private Strategy strategy;
private ResultListener resultListener;
private Intent intent = null;
public HoneywellAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
openContinueScan();
}
@Override
public void stop2() {
}
@Override
public void stop() {
new ScanServiceEDA50P().onDestroy();
strategy.exclusiveStrategy();
this.stopContinueScan();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("data"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("com.honeywell.scan.broadcast");
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
public void stopContinueScan() {
Class clazz = null;
if("eda50p".equals(Build.MODEL.toLowerCase())){ // 扫描正常
intent = new Intent(context, ScanServiceEDA50P.class);
clazz = ScanServiceEDA50P.class;
}else if("eda51".equals(Build.MODEL.toLowerCase())){ // 扫描正常
intent = new Intent(context, ScanServiceEDA50P.class);
clazz = ScanServiceEDA50P.class;
}
else if("tc26".equals(Build.MODEL.toLowerCase())){
intent = new Intent(context, ScanServiceZEBRA.class);
clazz = ScanServiceZEBRA.class;
}
if (clazz != null && isServiceRunning(clazz)) {
context.stopService(intent);
}
}
private boolean isServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> runningServices = manager.getRunningServices(Integer.MAX_VALUE);
for (ActivityManager.RunningServiceInfo service : runningServices) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
public void openContinueScan(){
if("eda50p".equals(Build.MODEL.toLowerCase()) || "eda51".equals(Build.MODEL.toLowerCase()) || "eda52".equals(Build.MODEL.toLowerCase())){ // 扫描正常
intent = new Intent(context, ScanServiceEDA50P.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.getApplicationContext().startForegroundService(intent);
}else {
context.getApplicationContext().startService(intent);
}
}
else if("tc26".equals(Build.MODEL.toLowerCase())){
intent = new Intent(context, ScanServiceZEBRA.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.getApplicationContext().startForegroundService(intent);
}else {
context.getApplicationContext().startService(intent);
}
}
}
}

View File

@ -0,0 +1,57 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
/**
* 23-6-13 霍尼韦尔EDA56使用
* 25-1-14 兰陵县人民医院 测试可以使用(唐圆圆)
*/
public class HoneywellEda56Adapter implements Adapter {
private Context context;
private ResultListener resultListener;
private Strategy strategy;
public HoneywellEda56Adapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new HoneywellEda56Adapter.Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("data"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("chaoran");
filter.setPriority(2);
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -0,0 +1,61 @@
package chaoran.business.adapter;
/*
**********************************************
* DATE PERSON REASON
* 2021-02-03 FXY Created
**********************************************
*/
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
/**
* 群创科技
*/
public class HoriemtechAdapter implements Adapter {
private Context context;
private Strategy strategy;
private ResultListener resultListener;
public HoriemtechAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("value"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("mmi.scan.mode.notify");
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -1,11 +1,5 @@
package chaoran.business.adapter;
/*
**********************************************
* DATE PERSON REASON
* 2021-02-02 FXY Created
**********************************************
*/
import android.content.BroadcastReceiver;
import android.content.Context;
@ -45,7 +39,7 @@ public class IDataAdapter implements Adapter {
public Receiver() {
scannerInerface = new ScannerInerface(context);
scannerInerface.setOutputMode(1);
// scannerInerface.setOutputMode(1);
}
@Override
@ -55,7 +49,7 @@ public class IDataAdapter implements Adapter {
@Override
public void executeStrategy(ResultListener resultListener) {
scannerInerface.open();
// scannerInerface.open();
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.SCANRESULT");
context.registerReceiver(this, filter);
@ -64,7 +58,7 @@ public class IDataAdapter implements Adapter {
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
scannerInerface.close();
// scannerInerface.close();
}
}
}

View File

@ -0,0 +1,56 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
/**
* 适配 sc55g
*/
public class IOT_DeviceAdapter implements Adapter {
private Context context;
private ResultListener resultListener;
private Strategy strategy;
public IOT_DeviceAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("message"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("com.speedata.showdecodedata");
filter.setPriority(2);
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -0,0 +1,53 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
public class LachesisAdapter implements Adapter {
private Context context;
private ResultListener resultListener;
private Strategy strategy;
public LachesisAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("data"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("chaoran");
filter.setPriority(2);
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -0,0 +1,53 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
public class NewlandAdapter implements Adapter {
private Context context;
private ResultListener resultListener;
private Strategy strategy;
public NewlandAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("SCAN_BARCODE1"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("nlscan.action.SCANNER_RESULT");
filter.setPriority(2);
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -0,0 +1,56 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
/**
* 适配 mc50
*/
public class QualcommAdapter implements Adapter {
private Context context;
private ResultListener resultListener;
private Strategy strategy;
public QualcommAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("data"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("com.scanner.broadcast");
filter.setPriority(2);
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -9,10 +9,8 @@ package chaoran.business.adapter;
import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Vibrator;
import chaoran.business.R;
import android.os.Build;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
@ -29,6 +27,8 @@ public class RockChipAdapter implements Adapter {
private Context context;
public static final String[] barcode = {""};
@Override
public void start() {
strategy.executeStrategy(resultListener);
@ -42,7 +42,11 @@ public class RockChipAdapter implements Adapter {
public RockChipAdapter(Context context, ResultListener resultListener) {
this.resultListener = resultListener;
this.context = context;
this.strategy = new Reader(new File("/dev/ttyS1"), 115200, 0);
String fileName = "/dev/ttyS1";
if (Build.MODEL.equalsIgnoreCase("rk3568_r")) {
fileName = "/dev/ttyS8";
}
this.strategy = new Reader(new File(fileName), 115200, 0, fileName);
}
public class Reader implements Strategy {
@ -50,7 +54,7 @@ public class RockChipAdapter implements Adapter {
private FileInputStream mFileInputStream;
private boolean running = true;
public Reader(File device, int baudrate, int flags) {
public Reader(File device, int baudrate, int flags, String fileName) {
if (!device.canRead() || !device.canWrite()) {
try {
@ -69,6 +73,10 @@ public class RockChipAdapter implements Adapter {
}
mFd = open(device.getAbsolutePath(), baudrate, flags);
if (mFd == null) {
if (!"/dev/ttyS1".equals(fileName)) {
strategy = new Reader(new File("/dev/ttyS1"), 115200, 0, "/dev/ttyS1");
return;
}
System.out.println("获取文件描述符失败!");
}
mFileInputStream = new FileInputStream(mFd);
@ -76,11 +84,26 @@ public class RockChipAdapter implements Adapter {
@Override
public void executeStrategy(ResultListener resultListener) {
running = true;
new Thread(() -> {
while (running) {
String data = data();
if (!data.equals("")) {
resultListener.result(data);
synchronized (RockChipAdapter.class) {
barcode[0] = barcode[0] + data;
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (RockChipAdapter.class) {
if (!"".equals(barcode[0])) {
// Log.i("listen", "Thread result data: \t\t" + barcode[0] + "\t\t" + System.currentTimeMillis());
resultListener.result(barcode[0]);
barcode[0] = "";
}
}
}
}).start();
@ -88,22 +111,27 @@ public class RockChipAdapter implements Adapter {
@Override
public void exclusiveStrategy() {
running = false;
close();
// running = false;
// close();
}
private String data() {
if (mFileInputStream == null) return "";
if (mFileInputStream == null) {
return "";
}
synchronized (RockChipAdapter.class) {
try {
int size = mFileInputStream.available();
byte[] buffer = new byte[size];
size = mFileInputStream.read(buffer);
if (size > 0) {
return new String(buffer);
String data = new String(buffer);
return data;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return "";
}
}

View File

@ -0,0 +1,61 @@
package chaoran.business.adapter;
/*
**********************************************
* DATE PERSON REASON
* 2021-02-03 FXY Created
**********************************************
*/
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
/**
* 东集 pda
*/
public class SeuicAdapter implements Adapter {
private Context context;
private Strategy strategy;
private ResultListener resultListener;
public SeuicAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("scannerdata"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("com.android.server.scannerservice.broadcast");
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -0,0 +1,53 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
public class UrovoAdapter implements Adapter {
private Context context;
private ResultListener resultListener;
private Strategy strategy;
public UrovoAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("barcode_string"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.ACTION_DECODE_DATA");
filter.setPriority(2);
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -0,0 +1,53 @@
package chaoran.business.adapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import chaoran.business.activity.ResultListener;
import chaoran.business.strategy.Strategy;
public class ZebraAdapter implements Adapter {
private Context context;
private ResultListener resultListener;
private Strategy strategy;
public ZebraAdapter(Context context, ResultListener resultListener) {
this.context = context;
this.resultListener = resultListener;
strategy = new ZebraAdapter.Receiver();
}
@Override
public void start() {
strategy.executeStrategy(resultListener);
}
@Override
public void stop() {
strategy.exclusiveStrategy();
}
public class Receiver extends BroadcastReceiver implements Strategy {
@Override
public void onReceive(Context context, Intent intent) {
resultListener.result(intent.getStringExtra("com.motorolasolutions.emdk.datawedge.data_string"));
}
@Override
public void executeStrategy(ResultListener resultListener) {
IntentFilter filter = new IntentFilter();
filter.addAction("chaoran");
filter.setPriority(2);
context.registerReceiver(this, filter);
}
@Override
public void exclusiveStrategy() {
context.unregisterReceiver(this);
}
}
}

View File

@ -12,6 +12,10 @@ public class NetworkSetting extends Setting {
private Integer port;
private String path;
private Integer screen_rotation;
private Integer hide_bar;
public String getAddress() {
return address;
}
@ -35,4 +39,21 @@ public class NetworkSetting extends Setting {
public void setPath(String path) {
this.path = path;
}
public Integer getScreen_rotation() {
return screen_rotation;
}
public void setScreen_rotation(Integer screen_rotation) {
this.screen_rotation = screen_rotation;
}
public Integer getHide_bar() {
return hide_bar;
}
public void setHide_bar(Integer hide_bar) {
this.hide_bar = hide_bar;
}
}

View File

@ -11,6 +11,8 @@ package chaoran.business.engine.impl;
import android.content.Context;
import android.content.SharedPreferences;
import android.webkit.JavascriptInterface;
import chaoran.business.activity.MainActivity;
import chaoran.business.engine.SettingEngine;
import chaoran.business.engine.entity.NetworkSetting;
import chaoran.business.engine.entity.Setting;
@ -35,12 +37,15 @@ public class NetworkSettingEngine implements SettingEngine {
public Setting getSetting() {
SharedPreferences spf = context.getSharedPreferences("crtech", Context.MODE_PRIVATE);
Integer port = spf.getInt("port", -1);
Integer screen_rotation = spf.getInt("screen_rotation", 3);
String address = spf.getString("address", "").replaceAll(" ", "");
String path = spf.getString("path", "").replaceAll(" ", "");
NetworkSetting networkSetting = new NetworkSetting();
networkSetting.setAddress(address);
networkSetting.setPort(port);
networkSetting.setPath(path);
networkSetting.setScreen_rotation(screen_rotation);
networkSetting.setHide_bar(spf.getInt("hide_bar", 0));
return networkSetting;
}
@ -51,7 +56,12 @@ public class NetworkSettingEngine implements SettingEngine {
editor.putString("address", networkSetting.getAddress());
editor.putString("path", networkSetting.getPath());
editor.putInt("port", networkSetting.getPort());
editor.putInt("screen_rotation", networkSetting.getScreen_rotation());
editor.putInt("hide_bar", networkSetting.getHide_bar());
editor.commit();
MainActivity.SCREEN_ROTATION = networkSetting.getScreen_rotation();
MainActivity.hideBar = networkSetting.getHide_bar();
// MainActivity.startDirection = networkSetting.getStart_direction();
return true;
}

View File

@ -109,22 +109,22 @@ public class TekVoiceEngine extends OfflineVoiceEngine {
String info) {
// 合成进度
mPercentForBuffering = percent;
showTip(String.format(context.getString(R.string.tts_toast_format),
mPercentForBuffering, mPercentForPlaying));
// showTip(String.format(context.getString(R.string.tts_toast_format),
// mPercentForBuffering, mPercentForPlaying));
}
@Override
public void onSpeakProgress(int percent, int beginPos, int endPos) {
// 播放进度
mPercentForPlaying = percent;
showTip(String.format(context.getString(R.string.tts_toast_format),
mPercentForBuffering, mPercentForPlaying));
// showTip(String.format(context.getString(R.string.tts_toast_format),
// mPercentForBuffering, mPercentForPlaying));
}
@Override
public void onCompleted(SpeechError error) {
if (error == null) {
showTip("播放完成");
// showTip("播放完成");
} else if (error != null) {
showTip(error.getPlainDescription(true));
}

View File

@ -0,0 +1,261 @@
package chaoran.business.service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import com.honeywell.aidc.AidcManager;
import com.honeywell.aidc.BarcodeDeviceConnectionEvent;
import com.honeywell.aidc.BarcodeFailureEvent;
import com.honeywell.aidc.BarcodeReadEvent;
import com.honeywell.aidc.BarcodeReader;
import com.honeywell.aidc.BarcodeReaderInfo;
import com.honeywell.aidc.ScannerNotClaimedException;
import com.honeywell.aidc.ScannerUnavailableException;
import com.honeywell.aidc.TriggerStateChangeEvent;
import com.honeywell.aidc.UnsupportedPropertyException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ScanServiceEDA50P extends Service
implements BarcodeReader.BarcodeListener, BarcodeReader.TriggerListener, AidcManager.BarcodeDeviceListener{
private final String TAG = "TestScanService";
private NotificationManager notificationManager;
private String notificationId = "channelId";
private String notificationName = "channelName";
private String _Suffix = "\n";
private AidcManager mAidcManager;
private BarcodeReader mBarcodeReader;
private BarcodeReader mInternalScannerReader;
private boolean mKeyPressed = false;
private boolean isContinue = true;
//必须要实现的方法
@Override
public IBinder onBind(Intent intent) {
// Log.i(TAG, "onBind方法被调用!");
return null;
}
//Service被创建时调用
@Override
public void onCreate() {
// Log.i(TAG, "onCreate方法被调用!");
super.onCreate();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { //适配9.0service
// if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { //适配9.0service
channel = new NotificationChannel(notificationId, notificationName, NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
Notification notification = new Notification.Builder(getApplicationContext(), notificationId).build();
//startForeground(1,getNotification());
startForeground(1, notification);
}
AidcManager.create(this, new MyCreatedCallback());
}
//Service被启动时调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand方法被调用!");
return super.onStartCommand(intent, flags, startId);
}
//Service被关闭之前回调
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy方法被调用!");
super.onDestroy();
if (mBarcodeReader != null) {
mBarcodeReader.release();
}
if (this.mInternalScannerReader != null) {
this.mInternalScannerReader.removeBarcodeListener(this);
this.mInternalScannerReader.removeTriggerListener(this);
this.mInternalScannerReader.close();
this.mInternalScannerReader = null;
Log.d(TAG, "Close internal scanner");
}
if (this.mAidcManager != null) {
this.mAidcManager.removeBarcodeDeviceListener(this);
this.mAidcManager.close();
}
}
class MyCreatedCallback implements AidcManager.CreatedCallback {
MyCreatedCallback() {
}
@Override
public void onCreated(AidcManager aidcManager) {
Log.d(TAG, "MyCreatedCallback onCreate !!!");
mAidcManager = aidcManager;
mAidcManager.addBarcodeDeviceListener(ScanServiceEDA50P.this);
initAllBarcodeReaderAndSetDefault();
}
}
void initAllBarcodeReaderAndSetDefault() {
List<BarcodeReaderInfo> readerList = mAidcManager.listBarcodeDevices();
Log.d(TAG, "initAllBarcodeReaderAndSetDefault readerList = "+readerList);
mInternalScannerReader = null;
for (BarcodeReaderInfo reader : readerList) {
if ("dcs.scanner.imager".equals(reader.getName())) {
mInternalScannerReader = initBarcodeReader(mInternalScannerReader, reader.getName());
}
}
Log.d(TAG, "initAllBarcodeReaderAndSetDefault mInternalScannerReader = "+mInternalScannerReader);
if (mInternalScannerReader != null) {
mBarcodeReader = mInternalScannerReader;
}
else {
Log.d(TAG, "No reader find");
}
if (mBarcodeReader != null) {
try {
mBarcodeReader.addBarcodeListener(this);
mBarcodeReader.addTriggerListener(this);
}
catch (Throwable e2) {
e2.printStackTrace();
}
try {
mBarcodeReader.setProperty(BarcodeReader.PROPERTY_NOTIFICATION_GOOD_READ_ENABLED, true);
mBarcodeReader.setProperty(BarcodeReader.PROPERTY_EAN_13_CHECK_DIGIT_TRANSMIT_ENABLED, true);
} catch (UnsupportedPropertyException e) {
e.printStackTrace();
}
}
}
BarcodeReader initBarcodeReader(BarcodeReader mReader, String mReaderName) {
if (mReader == null) {
try {
if (mReaderName == null) {
mReader = mAidcManager.createBarcodeReader();
} else {
mReader = mAidcManager.createBarcodeReader(mReaderName);
}
}
catch (Exception e) {
Log.e(TAG, "error", e);
}
try {
mReader.claim();
Log.d(TAG, "Call DCS interface claim() " + mReaderName);
} catch (ScannerUnavailableException e) {
e.printStackTrace();
}
try {
mReader.setProperty(BarcodeReader.PROPERTY_TRIGGER_CONTROL_MODE, BarcodeReader.TRIGGER_CONTROL_MODE_CLIENT_CONTROL);
mReader.setProperty(BarcodeReader.PROPERTY_DATA_PROCESSOR_LAUNCH_EZ_CONFIG, false);
mReader.setProperty(BarcodeReader.PROPERTY_TRIGGER_AUTO_MODE_TIMEOUT, 300);
} catch (UnsupportedPropertyException e2) {
e2.printStackTrace();
}
}
return mReader;
}
public void onBarcodeDeviceConnectionEvent(BarcodeDeviceConnectionEvent event) {
Log.d(TAG, event.getBarcodeReaderInfo() + " Connection status: " + event.getConnectionStatus());
}
public void onBarcodeEvent(final BarcodeReadEvent event) {
String barcodeDate = new String(event.getBarcodeData().getBytes(event.getCharset()));
Intent intent2 = new Intent("com.honeywell.scan.broadcast");
intent2.putExtra("data",barcodeDate);
sendBroadcast(intent2);
if(isContinue) {
executorService.execute(new MyThread(mBarcodeReader));
}
}
ExecutorService executorService = Executors.newSingleThreadExecutor();
class MyThread implements Runnable{
private BarcodeReader mBarcodeReader;
public MyThread(BarcodeReader mBarcodeReader){
this.mBarcodeReader = mBarcodeReader;
}
@Override
public void run() {
{
try {
Thread.sleep(1000);
mBarcodeReader.decode(true);
} catch (ScannerNotClaimedException e) {
Log.e(TAG, "catch ScannerNotClaimedException",e);
e.printStackTrace();
} catch (ScannerUnavailableException e2) {
Log.e(TAG, "catch ScannerUnavailableException",e2);
e2.printStackTrace();
} catch (Exception e3) {
e3.printStackTrace();
}
}
}
}
public void onFailureEvent(final BarcodeFailureEvent event) {
//Log.d(TAG, "Enter onFailureEvent ===> " + event.getTimestamp());
if(isContinue) {
try {
Thread.sleep(200);
} catch (Exception ex) {
ex.printStackTrace();
}
// doScan(true);
}
}
public void onTriggerEvent(TriggerStateChangeEvent event) {
if (event.getState()) {
if (!mKeyPressed) {
mKeyPressed = true;
doScan(true);
}else{
mKeyPressed = false;
doScan(false);
}
} else {
//mKeyPressed = false;
// doScan(false);
}
// Log.d(TAG, "OnTriggerEvent status: " + event.getState());
}
void doScan(boolean do_scan) {
try {
if (do_scan) {
// Log.d(TAG, "Start a new Scan!");
} else {
// Log.d(TAG, "Cancel last Scan!");
}
mBarcodeReader.decode(do_scan);
} catch (ScannerNotClaimedException e) {
Log.e(TAG, "catch ScannerNotClaimedException",e);
e.printStackTrace();
} catch (ScannerUnavailableException e2) {
Log.e(TAG, "catch ScannerUnavailableException",e2);
e2.printStackTrace();
} catch (Exception e3) {
e3.printStackTrace();
}
}
}

View File

@ -0,0 +1,333 @@
package chaoran.business.service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;
import com.symbol.emdk.EMDKManager;
import com.symbol.emdk.EMDKResults;
import com.symbol.emdk.barcode.BarcodeManager;
import com.symbol.emdk.barcode.ScanDataCollection;
import com.symbol.emdk.barcode.Scanner;
import com.symbol.emdk.barcode.ScannerConfig;
import com.symbol.emdk.barcode.ScannerException;
import com.symbol.emdk.barcode.ScannerResults;
import com.symbol.emdk.barcode.StatusData;
import java.util.ArrayList;
public class ScanServiceZEBRA extends Service implements EMDKManager.EMDKListener, Scanner.DataListener, Scanner.StatusListener {
private final String TAG = "ScanService";
private NotificationManager notificationManager;
private String notificationId = "ScanServiceId";
private String notificationName = "ScanServiceName";
private EMDKManager emdkManager = null;
private BarcodeManager barcodeManager = null;
private Scanner scanner = null;
private String statusString = "";
private boolean bSoftTriggerSelected = true;
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind方法被调用!");
return null;
}
//Service被创建时调用
@Override
public void onCreate() {
Log.i(TAG, "onCreate方法被调用!");
super.onCreate();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { //适配8.0service
channel = new NotificationChannel(notificationId, notificationName, NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
Notification notification = new Notification.Builder(getApplicationContext(), notificationId).build();
//startForeground(1,getNotification());
startForeground(1, notification);
}
EMDKResults results = EMDKManager.getEMDKManager(getApplicationContext(), this);
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.pda.scan.trigger");
registerReceiver(myCodeReceiver, intentFilter);
}
private MyCodeReceiver myCodeReceiver = new MyCodeReceiver();
public class MyCodeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int keyCode = intent.getIntExtra("data",0);
if(keyCode == 103 || keyCode == 10036){
if(bSoftTriggerSelected){
bSoftTriggerSelected = false;
}else{
bSoftTriggerSelected = true;
cancelRead();
}
}
}
}
/* private Notification getNotification() {
Notification.Builder builder = new Notification.Builder(this)
.setContentTitle("连续扫描服务")
.setContentText("正在运行");
builder.setChannelId(notificationId);
Notification notification = builder.build();
return notification;
}*/
//Service被启动时调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand方法被调用!");
//setconfig();
initScanner();
return super.onStartCommand(intent, flags, startId);
}
public void setconfig(){
Intent i = new Intent();
i.setAction("com.symbol.datawedge.api.ACTION");
i.putExtra("com.symbol.datawedge.api.SET_DEFAULT_PROFILE", "myProfile");
sendBroadcast(i);
}
@Override
public void onOpened(EMDKManager emdkManager) {
updateStatus("EMDK open success!");
this.emdkManager = emdkManager;
// Acquire the barcode manager resources
initBarcodeManager();
// Enumerate scanner devices
// enumerateScannerDevices();
initScanner();
}
@Override
public void onClosed() {
// Release all the resources
if (emdkManager != null) {
emdkManager.release();
emdkManager = null;
}
updateStatus("EMDK closed unexpectedly! Please close and restart the application.");
}
@Override
public void onDestroy() {
super.onDestroy();
// Release all the resources
if (emdkManager != null) {
emdkManager.release();
emdkManager = null;
}
}
@Override
public void onData(ScanDataCollection scanDataCollection) {
if ((scanDataCollection != null) && (scanDataCollection.getResult() == ScannerResults.SUCCESS)) {
ArrayList<ScanDataCollection.ScanData> scanData = scanDataCollection.getScanData();
for (ScanDataCollection.ScanData data : scanData) {
updateData(data.getData());
}
}
}
@Override
public void onStatus(StatusData statusData) {
StatusData.ScannerStates state = statusData.getState();
switch (state) {
case IDLE:
statusString = statusData.getFriendlyName() + " is enabled and idle...";
updateStatus(statusString);
// set trigger type
if(bSoftTriggerSelected) {
scanner.triggerType = Scanner.TriggerType.SOFT_ALWAYS;
setDecoders();
} else {
scanner.triggerType = Scanner.TriggerType.HARD;
cancelRead();
}
// submit read
if (!scanner.isReadPending()) {
try {
Thread.sleep(1000);
Log.i("jqtest:", "scan");
scanner.read();
} catch (Exception e) {
e.printStackTrace();
updateStatus(e.getMessage());
try {
deInitScanner();
Thread.sleep(200);
}catch (Exception e1){
e1.printStackTrace();
}
initScanner();
}
}
break;
case WAITING:
statusString = "Scanner is waiting for trigger press...";
updateStatus(statusString);
break;
case SCANNING:
statusString = "Scanning...";
updateStatus(statusString);
break;
case DISABLED:
statusString = statusData.getFriendlyName() + " is disabled.";
updateStatus(statusString);
break;
case ERROR:
statusString = "An error has occurred.";
updateStatus(statusString);
break;
default:
break;
}
}
private void initScanner() {
if (scanner == null) {
if (barcodeManager != null) {
scanner = barcodeManager.getDevice(BarcodeManager.DeviceIdentifier.DEFAULT);
} else {
updateStatus("Failed to get the specified scanner device! Please close and restart the application.");
return;
}
if (scanner != null) {
scanner.addDataListener(this);
scanner.addStatusListener(this);
try {
scanner.enable();
} catch (ScannerException e) {
updateStatus(e.getMessage());
deInitScanner();
}
} else {
updateStatus("Failed to initialize the scanner device.");
}
}
}
private void deInitScanner() {
if (scanner != null) {
try {
scanner.disable();
} catch (Exception e) {
updateStatus(e.getMessage());
}
try {
scanner.removeDataListener(this);
scanner.removeStatusListener(this);
} catch (Exception e) {
updateStatus(e.getMessage());
}
try {
scanner.release();
} catch (Exception e) {
updateStatus(e.getMessage());
}
scanner = null;
}
}
private void initBarcodeManager() {
barcodeManager = (BarcodeManager) emdkManager.getInstance(EMDKManager.FEATURE_TYPE.BARCODE);
}
private void deInitBarcodeManager() {
if (emdkManager != null) {
emdkManager.release(EMDKManager.FEATURE_TYPE.BARCODE);
}
}
/* private void enumerateScannerDevices() {
if (barcodeManager != null) {
List<ScannerInfo> deviceList = barcodeManager.getSupportedDevicesInfo();
if ((deviceList != null) && (deviceList.size() != 0)) {
Iterator<ScannerInfo> it = deviceList.iterator();
while (it.hasNext()) {
ScannerInfo scnInfo = it.next();
if (scnInfo.isDefaultScanner()) {
scannerInfo = scnInfo;
Log.i("scannerInfo", scannerInfo.getFriendlyName());
break;
}
}
}
}
}*/
private void setDecoders() {
if (scanner != null) {
try {
ScannerConfig config = scanner.getConfig();
// Set EAN8
config.decoderParams.ean8.enabled = true;
// Set EAN13
config.decoderParams.ean13.enabled = true;
// Set Code39
config.decoderParams.code39.enabled = true;
//Set Code128
config.decoderParams.code128.enabled = true;
// config.readerParams.readerSpecific.imagerSpecific.aimType = ScannerConfig.AimType.PRESENTATION;
scanner.setConfig(config);
} catch (ScannerException e) {
}
}
}
private void cancelRead() {
if (scanner != null) {
if (scanner.isReadPending()) {
try {
scanner.cancelRead();
} catch (ScannerException e) {
updateStatus(e.getMessage());
}
}
}
}
private void updateStatus(final String status) {
//Log.i("status",status);
// runOnUiThread(new Runnable() {
// @Override
// public void run() {
// textViewStatus.setText("" + status);
// }
// });
}
private void updateData(final String result) {
new Thread() {
@Override
public void run() {
super.run();
Intent intent2 = new Intent("com.pda.scan.result");
intent2.putExtra("data", result);
sendBroadcast(intent2);
Log.i(TAG, result);
}
}.start();
}
}

View File

@ -0,0 +1,97 @@
package chaoran.business.utils;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* 相机辅助类 - 通过JavaScript接口直接调用相机
*/
public class CameraHelper {
private static final int REQUEST_CODE_CAMERA_DIRECT = 1003;
private Activity activity;
private WebView webView;
private Uri cameraPhotoUri;
private String currentPhotoPath;
public CameraHelper(Activity activity, WebView webView) {
this.activity = activity;
this.webView = webView;
}
/**
* 直接打开相机拍照
*/
@JavascriptInterface
public void openCamera() {
activity.runOnUiThread(() -> {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (cameraIntent.resolveActivity(activity.getPackageManager()) != null) {
File photoFile = createImageFile();
if (photoFile != null) {
currentPhotoPath = photoFile.getAbsolutePath();
cameraPhotoUri = FileProvider.getUriForFile(
activity,
activity.getPackageName() + ".fileprovider",
photoFile
);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraPhotoUri);
cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
activity.startActivityForResult(cameraIntent, REQUEST_CODE_CAMERA_DIRECT);
}
}
});
}
/**
* 创建图片文件
*/
private File createImageFile() {
try {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
if (storageDir != null && !storageDir.exists()) {
storageDir.mkdirs();
}
return File.createTempFile(imageFileName, ".jpg", storageDir);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* 处理Activity结果
*/
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_CAMERA_DIRECT) {
if (resultCode == Activity.RESULT_OK && cameraPhotoUri != null) {
// 通知JavaScript照片已拍摄
String jsCode = "javascript:onCameraResult('" + cameraPhotoUri.toString() + "', '" + currentPhotoPath + "')";
webView.post(() -> webView.loadUrl(jsCode));
} else {
// 取消拍照
String jsCode = "javascript:onCameraResult(null, null)";
webView.post(() -> webView.loadUrl(jsCode));
}
cameraPhotoUri = null;
currentPhotoPath = null;
return true;
}
return false;
}
}

View File

@ -0,0 +1,89 @@
package chaoran.business.utils;
import android.content.Context;
import android.os.Environment;
import java.io.File;
import java.math.BigDecimal;
public class DataCleanManager {
/**
* 获取缓存总大小
*/
public static String getTotalCacheSize(Context context) throws Exception {
long cacheSize = getFolderSize(context.getCacheDir());
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
cacheSize += getFolderSize(context.getExternalCacheDir());
}
return getFormatSize(cacheSize);
}
/**
* 清空所有缓存
*/
public static void clearAllCache(Context context) {
deleteDir(context.getCacheDir());
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
deleteDir(context.getExternalCacheDir());
}
}
private static boolean deleteDir(File dir) {
if (dir != null && dir.isDirectory()) {
String[] children = dir.list();
for (String child : children) {
boolean success = deleteDir(new File(dir, child));
if (!success) {
return false;
}
}
}
return dir.delete();
}
/**
* 获取文件夹大小
*/
private static long getFolderSize(File file) throws Exception {
long size = 0;
try {
File[] fileList = file.listFiles();
for (File aFileList : fileList) {
if (aFileList.isDirectory()) {
size += getFolderSize(aFileList);
} else {
size += aFileList.length();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return size;
}
/**
* 格式化文件大小
*/
private static String getFormatSize(double size) {
double kiloByte = size / 1024;
if (kiloByte < 1) {
return size + "Byte";
}
double megaByte = kiloByte / 1024;
if (megaByte < 1) {
BigDecimal result1 = new BigDecimal(Double.toString(kiloByte));
return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB";
}
double gigaByte = megaByte / 1024;
if (gigaByte < 1) {
BigDecimal result2 = new BigDecimal(Double.toString(megaByte));
return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB";
}
double teraBytes = gigaByte / 1024;
if (teraBytes < 1) {
BigDecimal result3 = new BigDecimal(Double.toString(gigaByte));
return result3.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "GB";
}
BigDecimal result4 = new BigDecimal(teraBytes);
return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB";
}
}

View File

@ -0,0 +1,378 @@
package chaoran.business.utils;
import android.Manifest;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.Base64;
import android.util.Log;
import android.webkit.ValueCallback;
import android.widget.Toast;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* 文件选择和相机拍照辅助类
*/
public class FileChooserHelper {
private static final String TAG = "FileChooserHelper";
private static final int REQUEST_CODE_CAMERA = 1001;
private static final int REQUEST_CODE_FILE_CHOOSER = 1002;
private static final int REQUEST_CODE_PERMISSION_CAMERA = 2001;
private static final int REQUEST_CODE_PERMISSION_STORAGE = 2002;
private Activity activity;
private ValueCallback<Uri[]> filePathCallback;
private Uri cameraPhotoUri;
private boolean pendingAcceptImage = false;
private boolean pendingAcceptCamera = false;
private boolean pendingCameraOnly = false;
public FileChooserHelper(Activity activity) {
this.activity = activity;
}
/**
* 打开文件选择器(支持相机和图片选择)
*/
public void openFileChooser(ValueCallback<Uri[]> callback, boolean acceptImage, boolean acceptCamera) {
openFileChooser(callback, acceptImage, acceptCamera, false);
}
/**
* 打开文件选择器(支持相机和图片选择)
* @param callback 文件选择回调
* @param acceptImage 是否只接受图片
* @param acceptCamera 是否支持相机
* @param cameraOnly 是否仅使用相机(不显示文件选择器)
*/
public void openFileChooser(ValueCallback<Uri[]> callback, boolean acceptImage, boolean acceptCamera, boolean cameraOnly) {
this.filePathCallback = callback;
this.pendingAcceptImage = acceptImage;
this.pendingAcceptCamera = acceptCamera;
this.pendingCameraOnly = cameraOnly;
// 检查权限
if (acceptCamera && !checkCameraPermission()) {
requestCameraPermission();
return;
}
if (!cameraOnly && !checkStoragePermission()) {
requestStoragePermission();
return;
}
// 创建选择器
showFileChooser();
}
/**
* 显示文件选择器
*/
private void showFileChooser() {
Intent chooserIntent = createChooserIntent(pendingAcceptImage, pendingAcceptCamera);
if (chooserIntent != null) {
try {
activity.startActivityForResult(chooserIntent, REQUEST_CODE_FILE_CHOOSER);
} catch (Exception e) {
Log.e(TAG, "打开文件选择器失败", e);
Toast.makeText(activity, "打开文件选择器失败", Toast.LENGTH_SHORT).show();
if (filePathCallback != null) {
filePathCallback.onReceiveValue(null);
filePathCallback = null;
}
}
}
}
/**
* 创建选择器Intent
*/
private Intent createChooserIntent(boolean acceptImage, boolean acceptCamera) {
// 如果只需要相机直接返回相机Intent
if (acceptCamera && pendingCameraOnly) {
return createCameraIntent();
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
if (acceptImage) {
contentSelectionIntent.setType("image/*");
} else {
contentSelectionIntent.setType("*/*");
}
Intent[] intentArray;
if (acceptCamera) {
Intent cameraIntent = createCameraIntent();
if (cameraIntent != null) {
intentArray = new Intent[]{cameraIntent};
} else {
intentArray = new Intent[0];
}
} else {
intentArray = new Intent[0];
}
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "选择文件");
return chooserIntent;
}
/**
* 创建相机Intent
*/
private Intent createCameraIntent() {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (cameraIntent.resolveActivity(activity.getPackageManager()) != null) {
File photoFile = createImageFile();
if (photoFile != null) {
cameraPhotoUri = FileProvider.getUriForFile(
activity,
activity.getPackageName() + ".fileprovider",
photoFile
);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraPhotoUri);
cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
return cameraIntent;
}
}
return null;
}
/**
* 创建图片文件
*/
private File createImageFile() {
try {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
if (storageDir != null && !storageDir.exists()) {
storageDir.mkdirs();
}
return File.createTempFile(imageFileName, ".jpg", storageDir);
} catch (IOException e) {
Log.e(TAG, "创建图片文件失败", e);
return null;
}
}
/**
* 处理Activity结果
*/
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_FILE_CHOOSER) {
if (filePathCallback == null) {
return true;
}
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (data != null && data.getData() != null) {
// 从文件选择器选择的文件
results = new Uri[]{data.getData()};
} else if (cameraPhotoUri != null) {
// 从相机拍摄的照片
results = new Uri[]{cameraPhotoUri};
}
}
filePathCallback.onReceiveValue(results);
filePathCallback = null;
cameraPhotoUri = null;
return true;
}
return false;
}
/**
* 处理权限请求结果
*/
public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_CODE_PERMISSION_CAMERA) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(activity, "相机权限已授予", Toast.LENGTH_SHORT).show();
// 权限授予后,检查存储权限
if (!checkStoragePermission()) {
requestStoragePermission();
} else {
// 权限都已授予,重新打开文件选择器
showFileChooser();
}
} else {
Toast.makeText(activity, "相机权限被拒绝", Toast.LENGTH_SHORT).show();
if (filePathCallback != null) {
filePathCallback.onReceiveValue(null);
filePathCallback = null;
}
}
return true;
} else if (requestCode == REQUEST_CODE_PERMISSION_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(activity, "存储权限已授予", Toast.LENGTH_SHORT).show();
// 权限授予后,重新打开文件选择器
showFileChooser();
} else {
Toast.makeText(activity, "存储权限被拒绝", Toast.LENGTH_SHORT).show();
if (filePathCallback != null) {
filePathCallback.onReceiveValue(null);
filePathCallback = null;
}
}
return true;
}
return false;
}
/**
* 检查相机权限
*/
private boolean checkCameraPermission() {
return ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}
/**
* 请求相机权限
*/
private void requestCameraPermission() {
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.CAMERA},
REQUEST_CODE_PERMISSION_CAMERA);
}
/**
* 检查存储权限
* Android 10+ 使用分区存储不需要READ_EXTERNAL_STORAGE权限来访问通过文件选择器选择的文件
*/
private boolean checkStoragePermission() {
if (Build.VERSION.SDK_INT >= 33) { // Android 13 (TIRAMISU)
// Android 13+ 使用新的媒体权限
return ContextCompat.checkSelfPermission(activity, "android.permission.READ_MEDIA_IMAGES")
== PackageManager.PERMISSION_GRANTED;
} else if (Build.VERSION.SDK_INT >= 29) { // Android 10 (Q)
// Android 10-12 使用分区存储,通过文件选择器不需要权限
return true;
} else {
// Android 9 需要存储权限
return ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED;
}
}
/**
* 请求存储权限
*/
private void requestStoragePermission() {
if (Build.VERSION.SDK_INT >= 33) { // Android 13 (TIRAMISU)
// Android 13+ 请求新的媒体权限
ActivityCompat.requestPermissions(activity,
new String[]{"android.permission.READ_MEDIA_IMAGES"},
REQUEST_CODE_PERMISSION_STORAGE);
} else if (Build.VERSION.SDK_INT >= 29) { // Android 10 (Q)
// Android 10-12 不需要请求权限
Toast.makeText(activity, "存储权限已授予", Toast.LENGTH_SHORT).show();
} else {
// Android 9 请求存储权限
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_CODE_PERMISSION_STORAGE);
}
}
/**
* 获取文件名
*/
public static String getFileName(Context context, Uri uri) {
String result = null;
if (uri.getScheme().equals("content")) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (nameIndex >= 0) {
result = cursor.getString(nameIndex);
}
}
} catch (Exception e) {
Log.e(TAG, "获取文件名失败", e);
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}
/**
* 将文件转换为Base64字符串
*/
public static String fileToBase64(Context context, Uri uri) {
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
if (inputStream == null) {
return null;
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
inputStream.close();
byte[] fileBytes = byteArrayOutputStream.toByteArray();
return Base64.encodeToString(fileBytes, Base64.NO_WRAP);
} catch (Exception e) {
Log.e(TAG, "文件转Base64失败", e);
return null;
}
}
/**
* 获取文件大小
*/
public static long getFileSize(Context context, Uri uri) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
if (sizeIndex >= 0) {
return cursor.getLong(sizeIndex);
}
}
} catch (Exception e) {
Log.e(TAG, "获取文件大小失败", e);
}
return 0;
}
}

View File

@ -0,0 +1,725 @@
package chaoran.business.utils;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.media.ExifInterface;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Random;
import chaoran.business.BuildConfig;
public class LocalAddressUtil {
public final static String SSO_KEY = "!~CROP@CRTECH@PDA~!";
private Context context;
private Activity activity;
private View view;
private int[] heights;
private static final String TAG = "LocalAddressUtil";
private static final int REQUEST_CODE_NATIVE_CAMERA = 31002;
private static final int REQUEST_CODE_PERMISSION_CAMERA = 31001;
private static final String DEFAULT_CAMERA_CALLBACK = "onNativeCameraResult";
// Base64 settings for camera callback
private static final int CAMERA_IMAGE_DECODE_MAX_DIMENSION = 3200;
private static final int CAMERA_OUTPUT_JPEG_QUALITY = 90;
private static final int CAMERA_OUTPUT_JPEG_MIN_QUALITY = 55;
private static final int CAMERA_OUTPUT_BASE64_MAX_BYTES = 500 * 1024;
private static final int CAMERA_OUTPUT_IMAGE_MAX_BYTES = CAMERA_OUTPUT_BASE64_MAX_BYTES / 4 * 3;
private static final String CAMERA_RAW_DATA_URL_PREFIX = "data:image/jpeg;base64,";
private static final String CAMERA_WATERMARK_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
private Uri nativeCameraPhotoUri;
private String pendingCameraCallback = null;
private boolean pendingCameraCompress = true;
private boolean pendingCameraWatermark = false;
public LocalAddressUtil(Context context, Activity activity, View view) {
this.context = context;
this.activity = activity;
this.view = view;
this.heights = StatusBarUtil.getStatusBarHeight(context);
}
/**
* 直接调用原生相机默认回调window.onNativeCameraResult(base64OrDataUrl, error)
*/
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public void openNativeCamera() {
openNativeCamera(DEFAULT_CAMERA_CALLBACK, true, false);
}
/**
* 直接调用原生相机自定义回调函数名window[callback](base64OrDataUrl, error)
* @param callback JS 回调函数名(不需要传 window. 前缀)
*/
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public void openNativeCamera(String callback) {
openNativeCamera(callback, true, false);
}
/**
* 直接调用原生相机自定义回调函数名window[callback](base64OrDataUrl, error)
* @param callback JS 回调函数名(不需要传 window. 前缀)
* @param needCompress 是否压缩,默认压缩到约 500KB base64
* @param addWatermark 是否添加拍照时间水印
*/
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public void openNativeCamera(String callback, boolean needCompress, boolean addWatermark) {
this.pendingCameraCallback = (callback == null || callback.trim().isEmpty())
? DEFAULT_CAMERA_CALLBACK
: callback.trim();
this.pendingCameraCompress = needCompress;
this.pendingCameraWatermark = addWatermark;
if (!checkCameraPermission()) {
ActivityCompat.requestPermissions(activity,
new String[]{android.Manifest.permission.CAMERA},
REQUEST_CODE_PERMISSION_CAMERA);
return;
}
Intent cameraIntent = createNativeCameraIntent();
if (cameraIntent == null) {
dispatchCameraResultToJs(null, "create_intent_failed");
return;
}
try {
activity.startActivityForResult(cameraIntent, REQUEST_CODE_NATIVE_CAMERA);
} catch (Exception e) {
Log.e(TAG, "启动相机失败", e);
dispatchCameraResultToJs(null, "start_failed");
}
}
/**
* 由宿主 Activity 转发调用
*/
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != REQUEST_CODE_NATIVE_CAMERA) {
return false;
}
if (resultCode == Activity.RESULT_OK) {
String dataUrl = buildCameraPhotoDataUrl(nativeCameraPhotoUri, pendingCameraCompress, pendingCameraWatermark);
if (dataUrl == null || dataUrl.trim().isEmpty()) {
dispatchCameraResultToJs(null, "encode_failed");
} else {
dispatchCameraResultToJs(dataUrl, null);
}
} else {
dispatchCameraResultToJs(null, "cancelled");
}
nativeCameraPhotoUri = null;
pendingCameraCompress = true;
pendingCameraWatermark = false;
return true;
}
/**
* 由宿主 Activity 转发调用
*/
public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode != REQUEST_CODE_PERMISSION_CAMERA) {
return false;
}
boolean granted = grantResults != null && grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (granted) {
openNativeCamera(pendingCameraCallback, pendingCameraCompress, pendingCameraWatermark);
} else {
dispatchCameraResultToJs(null, "permission_denied");
}
return true;
}
private boolean checkCameraPermission() {
return ContextCompat.checkSelfPermission(activity, android.Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}
private Intent createNativeCameraIntent() {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (cameraIntent.resolveActivity(activity.getPackageManager()) == null) {
return null;
}
File photoFile = createImageFile();
if (photoFile == null) {
return null;
}
nativeCameraPhotoUri = FileProvider.getUriForFile(
activity,
activity.getPackageName() + ".fileprovider",
photoFile
);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, nativeCameraPhotoUri);
cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
return cameraIntent;
}
private File createImageFile() {
try {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
if (storageDir != null && !storageDir.exists()) {
//noinspection ResultOfMethodCallIgnored
storageDir.mkdirs();
}
return File.createTempFile(imageFileName, ".jpg", storageDir);
} catch (IOException e) {
Log.e(TAG, "创建图片文件失败", e);
return null;
}
}
/**
* 将拍照结果转为 dataUrlbase64
*/
private String buildCameraPhotoDataUrl(Uri uri, boolean needCompress, boolean addWatermark) {
if (uri == null) {
return null;
}
byte[] rawBytes;
try {
rawBytes = readBytesFromUri(uri);
if (rawBytes == null || rawBytes.length == 0) {
return null;
}
} catch (Exception e) {
Log.e(TAG, "读取原图失败", e);
return null;
}
if (!addWatermark && (!needCompress || rawBytes.length <= CAMERA_OUTPUT_IMAGE_MAX_BYTES)) {
String base64 = Base64.encodeToString(rawBytes, Base64.NO_WRAP);
String dataUrl = CAMERA_RAW_DATA_URL_PREFIX + base64;
Log.i(TAG, "camera_base64_size rawBytes=" + rawBytes.length
+ " rawKb=" + String.format(Locale.getDefault(), "%.2f", rawBytes.length / 1024f)
+ " resultBytes=" + rawBytes.length
+ " resultKb=" + String.format(Locale.getDefault(), "%.2f", rawBytes.length / 1024f)
+ " base64Chars=" + base64.length()
+ " base64Kb=" + String.format(Locale.getDefault(), "%.2f", base64.length() / 1024f)
+ " dataUrlChars=" + dataUrl.length()
+ " needCompress=" + needCompress
+ " addWatermark=" + addWatermark
+ " outputWidth=raw"
+ " outputHeight=raw"
+ " outputFormat=jpeg_raw");
return dataUrl;
}
InputStream inputStream = null;
try {
inputStream = context.getContentResolver().openInputStream(uri);
if (inputStream == null) {
return null;
}
BitmapFactory.Options options = new BitmapFactory.Options();
if (needCompress) {
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, options);
safeClose(inputStream);
inputStream = context.getContentResolver().openInputStream(uri);
if (inputStream == null) {
return null;
}
options.inSampleSize = calculateInSampleSize(options.outWidth, options.outHeight, CAMERA_IMAGE_DECODE_MAX_DIMENSION);
options.inJustDecodeBounds = false;
}
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
if (bitmap == null) {
return null;
}
int rotateDegree = readImageRotateDegree(uri);
if (rotateDegree != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(rotateDegree);
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
if (rotatedBitmap != bitmap) {
bitmap.recycle();
bitmap = rotatedBitmap;
}
}
if (addWatermark) {
Bitmap.Config config = bitmap.getConfig() != null ? bitmap.getConfig() : Bitmap.Config.ARGB_8888;
if (!bitmap.isMutable()) {
Bitmap watermarkBitmap = bitmap.copy(config, true);
if (watermarkBitmap == null) {
return null;
}
if (watermarkBitmap != bitmap) {
bitmap.recycle();
bitmap = watermarkBitmap;
}
}
Canvas canvas = new Canvas(bitmap);
String watermark = new SimpleDateFormat(CAMERA_WATERMARK_TIME_PATTERN, Locale.getDefault()).format(new Date());
float textSize = Math.max(28f, bitmap.getWidth() / 16f);
float padding = Math.max(18f, textSize * 0.45f);
Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(textSize);
textPaint.setFakeBoldText(true);
textPaint.setShadowLayer(4f, 0f, 1f, 0x66000000);
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
Rect textBounds = new Rect();
textPaint.getTextBounds(watermark, 0, watermark.length(), textBounds);
float left = padding;
float bottom = bitmap.getHeight() - padding;
float top = bottom - (fontMetrics.descent - fontMetrics.ascent) - padding * 1.6f;
float right = left + textBounds.width() + padding * 2f;
Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgPaint.setColor(0xB3000000);
canvas.drawRoundRect(left, top, right, bottom, padding, padding, bgPaint);
Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
borderPaint.setColor(0x66FFFFFF);
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(Math.max(2f, textSize / 14f));
canvas.drawRoundRect(left, top, right, bottom, padding, padding, borderPaint);
float textY = bottom - padding - fontMetrics.descent;
canvas.drawText(watermark, left + padding, textY, textPaint);
}
byte[] bytes;
int quality = CAMERA_OUTPUT_JPEG_QUALITY;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
bytes = baos.toByteArray();
int outputWidth = bitmap.getWidth();
int outputHeight = bitmap.getHeight();
if (needCompress && bytes.length > CAMERA_OUTPUT_IMAGE_MAX_BYTES) {
int guard = 0;
while (bytes.length > CAMERA_OUTPUT_IMAGE_MAX_BYTES && guard < 12) {
guard++;
while (bytes.length > CAMERA_OUTPUT_IMAGE_MAX_BYTES && quality > CAMERA_OUTPUT_JPEG_MIN_QUALITY) {
quality = Math.max(CAMERA_OUTPUT_JPEG_MIN_QUALITY, quality - 5);
baos.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
bytes = baos.toByteArray();
}
if (bytes.length <= CAMERA_OUTPUT_IMAGE_MAX_BYTES) {
break;
}
int nextWidth = Math.max(1, Math.round(bitmap.getWidth() * 0.85f));
int nextHeight = Math.max(1, Math.round(bitmap.getHeight() * 0.85f));
if (nextWidth == bitmap.getWidth() && nextHeight == bitmap.getHeight()) {
break;
}
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, nextWidth, nextHeight, true);
if (scaledBitmap != bitmap) {
bitmap.recycle();
bitmap = scaledBitmap;
}
outputWidth = bitmap.getWidth();
outputHeight = bitmap.getHeight();
quality = CAMERA_OUTPUT_JPEG_QUALITY;
baos.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
bytes = baos.toByteArray();
}
}
bitmap.recycle();
if (bytes == null || bytes.length == 0) {
return null;
}
String base64 = Base64.encodeToString(bytes, Base64.NO_WRAP);
String dataUrl = CAMERA_RAW_DATA_URL_PREFIX + base64;
Log.i(TAG, "camera_base64_size rawBytes=" + rawBytes.length
+ " rawKb=" + String.format(Locale.getDefault(), "%.2f", rawBytes.length / 1024f)
+ " resultBytes=" + bytes.length
+ " resultKb=" + String.format(Locale.getDefault(), "%.2f", bytes.length / 1024f)
+ " base64Chars=" + base64.length()
+ " base64Kb=" + String.format(Locale.getDefault(), "%.2f", base64.length() / 1024f)
+ " dataUrlChars=" + dataUrl.length()
+ " needCompress=" + needCompress
+ " addWatermark=" + addWatermark
+ " outputWidth=" + outputWidth
+ " outputHeight=" + outputHeight
+ " jpegQuality=" + quality
+ " targetBytes=" + (needCompress ? CAMERA_OUTPUT_IMAGE_MAX_BYTES : -1)
+ " outputFormat=jpeg");
return dataUrl;
} catch (Exception e) {
Log.e(TAG, "图片转base64失败", e);
return null;
} finally {
safeClose(inputStream);
}
}
private byte[] readBytesFromUri(Uri uri) throws IOException {
InputStream inputStream = null;
try {
inputStream = context.getContentResolver().openInputStream(uri);
if (inputStream == null) {
return null;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int len;
while ((len = inputStream.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
} finally {
safeClose(inputStream);
}
}
private int readImageRotateDegree(Uri uri) {
InputStream inputStream = null;
try {
inputStream = context.getContentResolver().openInputStream(uri);
if (inputStream == null) {
return 0;
}
ExifInterface exifInterface = new ExifInterface(inputStream);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
return 90;
}
if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
return 180;
}
if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
return 270;
}
} catch (Exception e) {
Log.w(TAG, "读取图片方向失败", e);
} finally {
safeClose(inputStream);
}
return 0;
}
private int calculateInSampleSize(int srcW, int srcH, int maxDim) {
if (srcW <= 0 || srcH <= 0 || maxDim <= 0) {
return 1;
}
int maxSrc = Math.max(srcW, srcH);
int inSampleSize = 1;
while (maxSrc / inSampleSize > maxDim) {
inSampleSize *= 2;
}
return Math.max(1, inSampleSize);
}
private void safeClose(InputStream is) {
try {
if (is != null) {
is.close();
}
} catch (Exception ignored) {
}
}
private void dispatchCameraResultToJs(String dataUrl, String error) {
if (!(view instanceof WebView)) {
return;
}
WebView webView = (WebView) view;
String cb = (pendingCameraCallback == null || pendingCameraCallback.trim().isEmpty())
? DEFAULT_CAMERA_CALLBACK
: pendingCameraCallback.trim();
String js = "try{" +
"var cb=window[" + JSONObject.quote(cb) + "];" +
"if(typeof cb==='function'){cb(" + JSONObject.quote(dataUrl) + "," + JSONObject.quote(error) + ");}" +
"}catch(e){}";
webView.post(() -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, null);
} else {
webView.loadUrl("javascript:" + js);
}
});
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String getIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
// if(!intf.getDisplayName().equals("eth0")) continue;
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException ex) {
Log.e("address: local", ex.toString());
}
return null;
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String getMacAddress(){//可以兼容安卓7以下
SharedPreferences spf = context.getSharedPreferences("chaoran_mac", Context.MODE_PRIVATE);
String saveMac = spf.getString("chaoran_mac", "");
if (!"".equalsIgnoreCase(saveMac)) {
return saveMac;
}
SharedPreferences.Editor editor = spf.edit();
String androidMac = getAndroidMac();
editor.putString("chaoran_mac", androidMac);
editor.apply();
return androidMac;
}
/**
* 获取安卓的mac
* @return
*/
private String getAndroidMac() {
if (Build.VERSION.SDK_INT >= 34) { // Android 14 is code-named Tiramisu
try {
return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
}catch (Exception e) {
e.printStackTrace();
return generateRandomMacAddress();
}
}
String macAddress = null;
StringBuffer buf = new StringBuffer();
NetworkInterface networkInterface = null;
try {
networkInterface = NetworkInterface.getByName("eth1");
if (networkInterface == null) {
networkInterface = NetworkInterface.getByName("wlan0");
}
if (networkInterface == null) {
return generateRandomMacAddress();
}
byte[] addr = networkInterface.getHardwareAddress();
for (byte b : addr) {
buf.append(String.format("%02X:", b));
}
if (buf.length() > 0) {
buf.deleteCharAt(buf.length() - 1);
}
macAddress = buf.toString();
} catch (Exception e) {
e.printStackTrace();
macAddress = generateRandomMacAddress();
}
if (macAddress.length() < 4) {
macAddress = generateRandomMacAddress();
}
return macAddress;
}
private static String generateRandomMacAddress() {
Random rand = new Random();
byte[] macBytes = new byte[6];
rand.nextBytes(macBytes);
macBytes[0] = (byte) (macBytes[0] & 0xfe | 0x02);
StringBuilder macAddress = new StringBuilder();
for (int i = 0; i < macBytes.length; i++) {
macAddress.append(String.format("%02X%s", macBytes[i], (i < macBytes.length - 1) ? ":" : ""));
}
return macAddress.toString();
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String getInfo(int type){
String info;
switch (type) {
case 1: info = Build.BRAND;break;//品牌
case 2: info = Build.MODEL;break;//型号
case 3: info = Build.MANUFACTURER;break;//厂商
case 4: info = Build.DEVICE;break;//设备名
case 5: info = Build.ID;break;//设备硬件id
// case 6: info = Build.SERIAL;break;//序列号,可能获取不到
default: info = "";break;
}
return info;
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String getHeight(int type) {
String info = "";
switch (type) {
case 1 : info = String.valueOf(this.heights[0]); break;
case 2 : info = String.valueOf(this.heights[1]); break;
}
return info;
}
public void test(){
Log.e("test", "MANUFACTURER=" + Build.MANUFACTURER);
Log.e("test", "BRAND=" + Build.BRAND);
Log.e("test", "MODEL=" + Build.MODEL);
Log.e("test", "VERSION.RELEASE=" + Build.VERSION.RELEASE);
Log.e("test", "VERSION.SDK_INT=" + Build.VERSION.SDK_INT);
Log.e("test", "DEVICE=" + Build.DEVICE);
Log.e("test", "HOST=" + Build.HOST);
Log.e("test", "ID=" + Build.ID);
Log.e("test", "TIME=" + Build.TIME);
Log.e("test", "TYPE=" + Build.TYPE);
Log.e("test", "PRODUCT=" + Build.PRODUCT);
Log.e("test", "BOARD=" + Build.BOARD);
Log.e("test", "DISPLAY=" + Build.DISPLAY);
Log.e("test", "FINGERPRINT=" + Build.FINGERPRINT);
Log.e("test", "HARDWARE=" + Build.HARDWARE);
Log.e("test", "BOOTLOADER=" + Build.BOOTLOADER);
Log.e("test", "TAGS=" + Build.TAGS);
Log.e("test", "UNKNOWN=" + Build.UNKNOWN);
Log.e("test", "USER=" + Build.USER);
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public void rotateScreen() {
int angle = ((WindowManager)activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
switch (angle) {
case Surface.ROTATION_0:
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
break;
case Surface.ROTATION_90:
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
break;
case Surface.ROTATION_180:
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
break;
case Surface.ROTATION_270:
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
break;
default:
break;
}
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String registerMac(String applyMac) {
try {
String mac = this.getMacAddress();
String md5Hash = MD5.md5(mac + SSO_KEY);
if ("Crtech!register@PDA#APK".equals(applyMac) || (md5Hash != null && md5Hash.equals(applyMac))) {
// 将允许注册适配的mac写入文件中
SharedPreferences.Editor editor = context.getSharedPreferences("CrtechPdaConfig", Context.MODE_PRIVATE).edit();
editor.putString("checkMac", "success");
if ("Crtech!register@PDA#APK".equals(applyMac)) {
editor.putString("checkMacTime", String.valueOf(System.currentTimeMillis()));
editor.putString("checkMacType", "1");
}else {
editor.putString("checkMacType", "0");
}
editor.commit();
return "success";
}
}catch (Exception ignored) {
}
return "error";
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String checkMacRegister() {
SharedPreferences sharedPreferences = context.getSharedPreferences("CrtechPdaConfig", Context.MODE_PRIVATE);
String checkMac = sharedPreferences.getString("checkMac", "error");
if (!"success".equals(checkMac)) {
return "error";
}
if ("1".equals(sharedPreferences.getString("checkMacType", "0"))) {
// 判断是否是强行注册的,强行注册的只允许使用一个小时;一但判断到,就取消注册
Long aLong = Long.valueOf(sharedPreferences.getString("checkMacTime", String.valueOf(System.currentTimeMillis())));
if ((aLong + 1 * 60 * 60 * 1000) < System.currentTimeMillis()) {
SharedPreferences.Editor edit = sharedPreferences.edit();
edit.putString("checkMac", "error");
edit.commit();
return "error";
}
}
return "success";
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String getApkVersion() {
return BuildConfig.VERSION_NAME;
}
/**
* 暴露PDA的厂家和型号
* @return String 格式:厂家:型号
*/
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String getPdaInfo() {
return Build.MANUFACTURER + "" + Build.MODEL.toLowerCase();
}
}

View File

@ -0,0 +1,34 @@
package chaoran.business.utils;
import java.security.MessageDigest;
// 从框架的MD5加密类拷贝过来的
public class MD5 {
private static final String key = "aa";
private static final char[] hexDigits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
public static final String md5(String s) {
return s == null ? null : md5(s.getBytes());
}
public static final String md5(byte[] bytes) {
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(bytes);
byte[] md = mdTemp.digest();
int j = md.length;
char[] str = new char[j * 2];
int k = 0;
for(int i = 0; i < j; ++i) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 15];
str[k++] = hexDigits[byte0 & 15];
}
return new String(str);
} catch (Exception var8) {
return null;
}
}
}

View File

@ -0,0 +1,217 @@
package chaoran.business.utils;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import com.readystatesoftware.systembartint.SystemBarTintManager;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 状态栏工具类
*/
public class StatusBarUtil {
/**
* 修改状态栏为全透明
*
* @param activity
*/
@TargetApi(19)
public static void transparencyBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window = activity.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
/**
* 获取屏幕的高度和状态栏高度
* @param context
* @return 第一个是屏幕,第二个是状态栏
*/
public static int[] getStatusBarHeight(Context context) {
int[] result = new int[]{0,0};
int resourceld = context.getResources().getIdentifier(
"status_bar_height", "dimen", "android"
);
if (resourceld > 0) {
result[0] = context.getResources().getDisplayMetrics().heightPixels;
result[1] = context.getResources().getDimensionPixelSize(resourceld);
}
return result;
}
//隐藏状态栏
public static void hideStatusBar(Activity activity){
if (Build.VERSION.SDK_INT >= 21) {
activity.getWindow()
.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
/**
* 修改状态栏颜色支持4.4以上版本
*
* @param activity
* @param colorId
*/
public static void setStatusBarColor(Activity activity, int colorId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.setStatusBarColor(activity.getResources().getColor(colorId));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//使用SystemBarTint库使4.4版本状态栏变色,需要先将状态栏设置为透明
transparencyBar(activity);
SystemBarTintManager tintManager = new SystemBarTintManager(activity);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(colorId);
}
}
/**
* 状态栏亮色模式,设置状态栏黑色文字、图标,
* 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android
*
* @param activity
* @return 1:MIUUI 2:Flyme 3:android6.0
*/
public static int StatusBarLightMode(Activity activity) {
int result = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (MIUISetStatusBarLightMode(activity, true)) {
result = 1;
} else if (FlymeSetStatusBarLightMode(activity.getWindow(), true)) {
result = 2;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
result = 3;
}
}
return result;
}
/**
* 已知系统类型时,设置状态栏黑色文字、图标。
* 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android
*
* @param activity
* @param type 1:MIUUI 2:Flyme 3:android6.0
*/
public static void StatusBarLightMode(Activity activity, int type) {
if (type == 1) {
MIUISetStatusBarLightMode(activity, true);
} else if (type == 2) {
FlymeSetStatusBarLightMode(activity.getWindow(), true);
} else if (type == 3) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
/**
* 状态栏暗色模式清除MIUI、flyme或6.0以上版本状态栏黑色文字、图标
*/
public static void StatusBarDarkMode(Activity activity, int type) {
if (type == 1) {
MIUISetStatusBarLightMode(activity, false);
} else if (type == 2) {
FlymeSetStatusBarLightMode(activity.getWindow(), false);
} else if (type == 3) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
/**
* 设置状态栏图标为深色和魅族特定的文字风格
* 可以用来判断是否为Flyme用户
*
* @param window 需要设置的窗口
* @param dark 是否把状态栏文字及图标颜色设置为深色
* @return boolean 成功执行返回true
*/
public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) {
boolean result = false;
if (window != null) {
try {
WindowManager.LayoutParams lp = window.getAttributes();
Field darkFlag = WindowManager.LayoutParams.class
.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
Field meizuFlags = WindowManager.LayoutParams.class
.getDeclaredField("meizuFlags");
darkFlag.setAccessible(true);
meizuFlags.setAccessible(true);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
if (dark) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
result = true;
} catch (Exception e) {
}
}
return result;
}
/**
* 需要MIUIV6以上
*
* @param activity
* @param dark 是否把状态栏文字及图标颜色设置为深色
* @return boolean 成功执行返回true
*/
public static boolean MIUISetStatusBarLightMode(Activity activity, boolean dark) {
boolean result = false;
Window window = activity.getWindow();
if (window != null) {
Class clazz = window.getClass();
try {
int darkModeFlag = 0;
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
if (dark) {
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体
} else {
extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体
}
result = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//开发版 7.7.13 及以后版本采用了系统API旧方法无效但不会报错所以两个方式都要加上
if (dark) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
} catch (Exception e) {
}
}
return result;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webViewFileTest"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>

View File

@ -7,6 +7,7 @@
android:id="@+id/table">
<TableRow>
<TextView />
@ -42,6 +43,7 @@
android:minWidth="150dp"
android:id="@+id/port"
android:singleLine="true"
android:inputType="number"
android:hint="介于1024-65535之间的数字" />
<TextView />
@ -68,6 +70,56 @@
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="隐藏状态栏:" />
<EditText
android:id="@+id/hide_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="1:隐藏0显示"
android:minWidth="150dp"
android:inputType="number"
android:digits="01"
android:singleLine="true" />
<TextView />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="屏幕方向:" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="150dp"
android:id="@+id/screen_rotation"
android:singleLine="true"
android:inputType="number"
android:digits="12345678"
android:hint="5正竖;6倒竖;7正横;8倒横;1竖;2横;3横竖;4禁止" />
<TextView />
</TableRow>
<TableRow>
<TextView />

View File

@ -113,7 +113,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:max="200"
android:min="100"
android:min="0"
android:id="@+id/voice_speed" />
<TextView />

View File

@ -14,4 +14,10 @@
android:orderInCategory="100"
app:showAsAction="never" />
<item
android:id="@+id/action_file_test"
android:title="@string/action_file_test"
android:orderInCategory="100"
app:showAsAction="never" />
</menu>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

View File

@ -4,10 +4,12 @@
<string name="title_activity_setting_network">网络设置界面</string>
<string name="action_setting_voice">语音设置</string>
<string name="title_activity_setting_voice">语音设置界面</string>
<string name="action_file_test">文件测试</string>
<string name="title_activity_file_test">文件选择测试</string>
<string name="title_activity_main">主页</string>
<!-- 讯飞离线语音appid-->
<string name="app_id">601c9ec6</string>
<string name="app_id">602e1727</string>
<!-- 讯飞离线语音弹出框格式-->
<string name="tts_toast_format" formatted="false">缓冲进度为%d%%,播放进度为%d%%</string>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
<external-cache-path name="external_cache" path="." />
<cache-path name="cache" path="." />
<files-path name="files" path="." />
</paths>

View File

@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.0"
classpath 'com.android.tools.build:gradle:4.1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files