@@ -4,10 +4,10 @@ | |||
<selectionStates> | |||
<SelectionState runConfigName="app"> | |||
<option name="selectionMode" value="DROPDOWN" /> | |||
<DropdownSelection timestamp="2024-09-14T07:16:28.864494800Z"> | |||
<DropdownSelection timestamp="2024-09-29T02:30:16.584496100Z"> | |||
<Target type="DEFAULT_BOOT"> | |||
<handle> | |||
<DeviceId pluginId="Default" identifier="serial=192.168.1.108:5555;connection=54d7fd03" /> | |||
<DeviceId pluginId="Default" identifier="serial=192.168.1.6:5555;connection=26244323" /> | |||
</handle> | |||
</Target> | |||
</DropdownSelection> | |||
@@ -0,0 +1,329 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="direct_access_persist.xml"> | |||
<option name="deviceSelectionList"> | |||
<list> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="27" /> | |||
<option name="brand" value="DOCOMO" /> | |||
<option name="codename" value="F01L" /> | |||
<option name="id" value="F01L" /> | |||
<option name="manufacturer" value="FUJITSU" /> | |||
<option name="name" value="F-01L" /> | |||
<option name="screenDensity" value="360" /> | |||
<option name="screenX" value="720" /> | |||
<option name="screenY" value="1280" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="28" /> | |||
<option name="brand" value="DOCOMO" /> | |||
<option name="codename" value="SH-01L" /> | |||
<option name="id" value="SH-01L" /> | |||
<option name="manufacturer" value="SHARP" /> | |||
<option name="name" value="AQUOS sense2 SH-01L" /> | |||
<option name="screenDensity" value="480" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2160" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="Lenovo" /> | |||
<option name="codename" value="TB370FU" /> | |||
<option name="id" value="TB370FU" /> | |||
<option name="manufacturer" value="Lenovo" /> | |||
<option name="name" value="Tab P12" /> | |||
<option name="screenDensity" value="340" /> | |||
<option name="screenX" value="1840" /> | |||
<option name="screenY" value="2944" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="31" /> | |||
<option name="brand" value="samsung" /> | |||
<option name="codename" value="a51" /> | |||
<option name="id" value="a51" /> | |||
<option name="manufacturer" value="Samsung" /> | |||
<option name="name" value="Galaxy A51" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2400" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="akita" /> | |||
<option name="id" value="akita" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 8a" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2400" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="33" /> | |||
<option name="brand" value="samsung" /> | |||
<option name="codename" value="b0q" /> | |||
<option name="id" value="b0q" /> | |||
<option name="manufacturer" value="Samsung" /> | |||
<option name="name" value="Galaxy S22 Ultra" /> | |||
<option name="screenDensity" value="600" /> | |||
<option name="screenX" value="1440" /> | |||
<option name="screenY" value="3088" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="32" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="bluejay" /> | |||
<option name="id" value="bluejay" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 6a" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2400" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="caiman" /> | |||
<option name="id" value="caiman" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 9 Pro" /> | |||
<option name="screenDensity" value="360" /> | |||
<option name="screenX" value="960" /> | |||
<option name="screenY" value="2142" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="comet" /> | |||
<option name="id" value="comet" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 9 Pro Fold" /> | |||
<option name="screenDensity" value="390" /> | |||
<option name="screenX" value="2076" /> | |||
<option name="screenY" value="2152" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="29" /> | |||
<option name="brand" value="samsung" /> | |||
<option name="codename" value="crownqlteue" /> | |||
<option name="id" value="crownqlteue" /> | |||
<option name="manufacturer" value="Samsung" /> | |||
<option name="name" value="Galaxy Note9" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="2220" /> | |||
<option name="screenY" value="1080" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="samsung" /> | |||
<option name="codename" value="dm3q" /> | |||
<option name="id" value="dm3q" /> | |||
<option name="manufacturer" value="Samsung" /> | |||
<option name="name" value="Galaxy S23 Ultra" /> | |||
<option name="screenDensity" value="600" /> | |||
<option name="screenX" value="1440" /> | |||
<option name="screenY" value="3088" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="samsung" /> | |||
<option name="codename" value="e1q" /> | |||
<option name="id" value="e1q" /> | |||
<option name="manufacturer" value="Samsung" /> | |||
<option name="name" value="Galaxy S24" /> | |||
<option name="screenDensity" value="480" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2340" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="33" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="felix" /> | |||
<option name="id" value="felix" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel Fold" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="2208" /> | |||
<option name="screenY" value="1840" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="felix" /> | |||
<option name="id" value="felix" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel Fold" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="2208" /> | |||
<option name="screenY" value="1840" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="33" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="felix_camera" /> | |||
<option name="id" value="felix_camera" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel Fold (Camera-enabled)" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="2208" /> | |||
<option name="screenY" value="1840" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="33" /> | |||
<option name="brand" value="samsung" /> | |||
<option name="codename" value="gts8uwifi" /> | |||
<option name="id" value="gts8uwifi" /> | |||
<option name="manufacturer" value="Samsung" /> | |||
<option name="name" value="Galaxy Tab S8 Ultra" /> | |||
<option name="screenDensity" value="320" /> | |||
<option name="screenX" value="1848" /> | |||
<option name="screenY" value="2960" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="husky" /> | |||
<option name="id" value="husky" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 8 Pro" /> | |||
<option name="screenDensity" value="390" /> | |||
<option name="screenX" value="1008" /> | |||
<option name="screenY" value="2244" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="30" /> | |||
<option name="brand" value="motorola" /> | |||
<option name="codename" value="java" /> | |||
<option name="id" value="java" /> | |||
<option name="manufacturer" value="Motorola" /> | |||
<option name="name" value="G20" /> | |||
<option name="screenDensity" value="280" /> | |||
<option name="screenX" value="720" /> | |||
<option name="screenY" value="1600" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="komodo" /> | |||
<option name="id" value="komodo" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 9 Pro XL" /> | |||
<option name="screenDensity" value="360" /> | |||
<option name="screenX" value="1008" /> | |||
<option name="screenY" value="2244" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="33" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="lynx" /> | |||
<option name="id" value="lynx" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 7a" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2400" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="31" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="oriole" /> | |||
<option name="id" value="oriole" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 6" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2400" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="33" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="panther" /> | |||
<option name="id" value="panther" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 7" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2400" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="samsung" /> | |||
<option name="codename" value="q5q" /> | |||
<option name="id" value="q5q" /> | |||
<option name="manufacturer" value="Samsung" /> | |||
<option name="name" value="Galaxy Z Fold5" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="1812" /> | |||
<option name="screenY" value="2176" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="30" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="r11" /> | |||
<option name="id" value="r11" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel Watch" /> | |||
<option name="screenDensity" value="320" /> | |||
<option name="screenX" value="384" /> | |||
<option name="screenY" value="384" /> | |||
<option name="type" value="WEAR_OS" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="30" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="redfin" /> | |||
<option name="id" value="redfin" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 5" /> | |||
<option name="screenDensity" value="440" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2340" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="shiba" /> | |||
<option name="id" value="shiba" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 8" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2400" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="33" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="tangorpro" /> | |||
<option name="id" value="tangorpro" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel Tablet" /> | |||
<option name="screenDensity" value="320" /> | |||
<option name="screenX" value="1600" /> | |||
<option name="screenY" value="2560" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="34" /> | |||
<option name="brand" value="google" /> | |||
<option name="codename" value="tokay" /> | |||
<option name="id" value="tokay" /> | |||
<option name="manufacturer" value="Google" /> | |||
<option name="name" value="Pixel 9" /> | |||
<option name="screenDensity" value="420" /> | |||
<option name="screenX" value="1080" /> | |||
<option name="screenY" value="2424" /> | |||
</PersistentDeviceSelectionData> | |||
<PersistentDeviceSelectionData> | |||
<option name="api" value="29" /> | |||
<option name="brand" value="samsung" /> | |||
<option name="codename" value="x1q" /> | |||
<option name="id" value="x1q" /> | |||
<option name="manufacturer" value="Samsung" /> | |||
<option name="name" value="Galaxy S20" /> | |||
<option name="screenDensity" value="480" /> | |||
<option name="screenX" value="1440" /> | |||
<option name="screenY" value="3200" /> | |||
</PersistentDeviceSelectionData> | |||
</list> | |||
</option> | |||
</component> | |||
</project> |
@@ -19,6 +19,9 @@ android { | |||
versionName rootProject.ext.versionName | |||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' | |||
multiDexEnabled true | |||
ndk { | |||
abiFilters 'armeabi','armeabi-v7a', 'x86', 'arm64-v8a','mips', 'x86_64' | |||
} | |||
} | |||
signingConfigs { | |||
release { | |||
@@ -59,6 +62,8 @@ android { | |||
sourceSets { | |||
main { | |||
jni.srcDirs = []//禁止gradle 自动编译,使用已经编译好的So库 | |||
jniLibs.srcDirs = ['src/main/jniLibs', 'libs']//指向要使用的库文件//的路径,前边的是自己项目的,后边的是第三方的so | |||
res.srcDirs = [ | |||
'src/main/res/layout/datatab', | |||
'src/main/res/layout/item', | |||
@@ -74,6 +79,7 @@ android { | |||
buildFeatures { | |||
viewBinding = true | |||
} | |||
ndkVersion '28.0.12433566 rc1' | |||
applicationVariants.all { variant -> | |||
variant.outputs.all { | |||
outputFileName = "boluo-xiaochao-v${defaultConfig.versionCode}-${releaseTime()}"+"-${variant.name}.apk" | |||
@@ -83,7 +89,6 @@ android { | |||
dependencies { | |||
implementation fileTree(include: ['*.jar'], dir: 'libs') | |||
//测试相关 | |||
testImplementation 'junit:junit:4.13-beta-2' | |||
androidTestImplementation 'androidx.test.ext:junit:1.1.3' | |||
@@ -194,4 +199,13 @@ dependencies { | |||
//日志工具 可定位代码行数 | |||
api 'com.apkfuns.logutils:library:1.7.5' | |||
//retrofit2网络请求 | |||
api 'com.squareup.retrofit2:converter-gson:2.5.0' | |||
api "com.squareup.retrofit2:retrofit:2.5.0" | |||
api "com.squareup.retrofit2:adapter-rxjava2:2.5.0" | |||
api "com.squareup.retrofit2:converter-scalars:2.5.0" | |||
api 'com.squareup.okhttp3:logging-interceptor:3.10.0' | |||
//调试okhttp请求(建议debug使用) | |||
api 'com.localebro:okhttpprofiler:1.0.8' | |||
} |
@@ -13,7 +13,7 @@ | |||
"attributes": [], | |||
"versionCode": 107, | |||
"versionName": "1.0.7", | |||
"outputFile": "boluo-xiaochao-v107-202409251528-release.apk" | |||
"outputFile": "boluo-xiaochao-v107-202410071053-release.apk" | |||
} | |||
], | |||
"elementType": "File" |
@@ -5,6 +5,7 @@ | |||
<uses-sdk tools:overrideLibrary="com.aliyun.iot.breeze.biz,com.aliyun.iot.breeze.ota, com.aliyun.iot.breeze.sdk,com.aliyun.iot.ble,com.aliyun.alink.linksdk.tmp" /> | |||
<!-- 网络权限 --> | |||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> | |||
<uses-permission android:name="android.permission.INTERNET" /> <!-- 网络连接 --> | |||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 获取网络连接状态 --> | |||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 写存储的权限 --> | |||
@@ -21,6 +22,20 @@ | |||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> | |||
<uses-permission android:name="android.permission.WAKE_LOCK"/> | |||
<uses-feature | |||
android:name="android.hardware.bluetooth_le" | |||
android:required="true" /> | |||
<uses-feature android:name="android.hardware.location.gps" /> | |||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> | |||
<uses-permission android:name="android.permission.BLUETOOTH" /> | |||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> | |||
<!--Android 6.0及后续版本扫描蓝牙,需要定位权限(进入GPS设置,可以看到蓝牙定位)--> | |||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> | |||
<uses-permission android:name="com.android.example.USB_PERMISSION" /> | |||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> | |||
<application | |||
android:vmSafeMode="true" | |||
android:name=".MainApplication" | |||
@@ -33,11 +48,21 @@ | |||
android:requestLegacyExternalStorage="true" | |||
android:roundIcon="@mipmap/ico" | |||
android:supportsRtl="true" | |||
android:extractNativeLibs="true" | |||
android:theme="@style/AppTheme" | |||
android:debuggable="true" | |||
tools:ignore="GoogleAppIndexingWarning" | |||
tools:node="merge" | |||
tools:replace="android:icon"> | |||
<activity | |||
android:name=".serial.ComAssistantActivity" | |||
android:exported="true" /> | |||
<activity | |||
android:name=".ble.BleServiceActivity" | |||
android:exported="true" /> | |||
<activity | |||
android:name=".ble.BleClientActivity" | |||
android:exported="true" /> | |||
<activity | |||
android:name=".modules.home.fragment.from.Cpxz1Activity" | |||
android:exported="false" /> | |||
@@ -0,0 +1,46 @@ | |||
package com.bonait.bnframework.api; | |||
import com.bonait.bnframework.common.API.APIResultT; | |||
import com.bonait.bnframework.common.model.GoodsData; | |||
import retrofit2.Call; | |||
import retrofit2.http.GET; | |||
import retrofit2.http.Url; | |||
/** | |||
* 接口服务 | |||
*/ | |||
public interface Api { | |||
// /** | |||
// * 上报日志文件 | |||
// * | |||
// * @param url | |||
// * @param account 账号 | |||
// * @param device 设备 | |||
// * @param faultCode 故障代码 | |||
// * @param sceneCode 场景code | |||
// * @param serialNo 序列号 | |||
// * @param file 文件 | |||
// * @return | |||
// */ | |||
// @Multipart | |||
// @POST | |||
// Call<BaseResp<String>> upLoadLogFile(@Url String url, | |||
// @Query("account") String account, | |||
// @Query("device") String device, | |||
// @Query("faultCode") String faultCode, | |||
// @Query("sceneCode") String sceneCode, | |||
// @Query("serialNo") String serialNo, | |||
// @Part MultipartBody.Part file); | |||
/** | |||
* 获取搜索关键字 | |||
* | |||
* @param url | |||
* @return | |||
*/ | |||
@GET | |||
Call<APIResultT<GoodsData>> getGoodsData(@Url String url); | |||
} |
@@ -0,0 +1,117 @@ | |||
package com.bonait.bnframework.api; | |||
import java.net.SocketTimeoutException; | |||
import retrofit2.Call; | |||
import retrofit2.Callback; | |||
import retrofit2.Response; | |||
/** | |||
* @author Nov | |||
* @date 19.11.25 | |||
*/ | |||
public class CommonResponse<T> implements Callback<T> { | |||
/** | |||
* 服务器请求失败 | |||
*/ | |||
public static final int APP_NET_ERROR = 99999; | |||
/** | |||
* 服务器请求成功,但是数据返回为空 | |||
*/ | |||
public static final int APP_DATA_NULL = 99998; | |||
/** | |||
* 服务器请求成功,但是数据返回错误 | |||
*/ | |||
public static final int APP_DATA_ERROR = 99997; | |||
/** | |||
* 回调 | |||
*/ | |||
private IHttpCallBack callback; | |||
public CommonResponse(IHttpCallBack callback) { | |||
this.callback = callback; | |||
} | |||
@Override | |||
public void onResponse(Call<T> call, Response<T> response) { | |||
T data = response.body(); | |||
if (null == response) { | |||
callError("response=null", APP_DATA_ERROR, "response =null"); | |||
return; | |||
} | |||
if (null == data) { | |||
callError("data=null", APP_DATA_ERROR, response.message()); | |||
return; | |||
} | |||
callSuccess(data); | |||
} | |||
@Override | |||
public void onFailure(Call<T> call, Throwable t) { | |||
//LogUtils.d("接口出错....."+t.getMessage() + " "+call.request().url()); | |||
callFail(APP_NET_ERROR, t); | |||
} | |||
/** | |||
* 回调失败 | |||
* | |||
* @param code | |||
* @param e | |||
*/ | |||
private void callFail(final int code, final Throwable e) { | |||
if (null == callback) { | |||
return; | |||
} | |||
if (e instanceof SocketTimeoutException) { | |||
callback.onFailed(code, e, "请求超时"); | |||
} else { | |||
callback.onFailed(code, e, "接口请求错误"); | |||
} | |||
e.printStackTrace(); | |||
reportErrorInfo(code + "", e.getMessage()); | |||
} | |||
/** | |||
* 回调错误 | |||
* | |||
* @param code | |||
*/ | |||
private void callError(String type, final int code, String msg) { | |||
if (null == callback) { | |||
return; | |||
} | |||
//LogUtils.d("请求数据处理错误....."); | |||
callback.onError(code); | |||
reportErrorInfo(code + "", msg); | |||
} | |||
/** | |||
* 回调成功 | |||
* | |||
* @param t | |||
*/ | |||
private void callSuccess(final Object t) { | |||
if (null == callback) { | |||
return; | |||
} | |||
callback.onSuccess(t); | |||
} | |||
/** | |||
* 上报错误信息 | |||
* | |||
* @param code | |||
* @param description | |||
*/ | |||
private void reportErrorInfo(String code, String description) { | |||
} | |||
} |
@@ -0,0 +1,70 @@ | |||
package com.bonait.bnframework.api; | |||
import com.bonait.bnframework.BuildConfig; | |||
import com.bonait.bnframework.api.interceptor.ConnectRetryInterceptor; | |||
import com.bonait.bnframework.api.interceptor.RequestInterceptor; | |||
import com.bonait.bnframework.api.interceptor.StbInfoInterceptor; | |||
import com.google.gson.GsonBuilder; | |||
import com.localebro.okhttpprofiler.OkHttpProfilerInterceptor; | |||
import java.lang.reflect.Modifier; | |||
import java.util.concurrent.TimeUnit; | |||
import okhttp3.ConnectionPool; | |||
import okhttp3.OkHttpClient; | |||
import okhttp3.logging.HttpLoggingInterceptor; | |||
import retrofit2.Retrofit; | |||
import retrofit2.converter.gson.GsonConverterFactory; | |||
/** | |||
* Http网络请求 | |||
*/ | |||
public class HttpUtil { | |||
private static final String JSON_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; | |||
private static Retrofit mRetrofit; | |||
private HttpUtil() { | |||
throw new AssertionError(); | |||
} | |||
public static void init(String baseUrl) { | |||
Retrofit.Builder builder = new Retrofit.Builder() | |||
.baseUrl(baseUrl) | |||
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder() | |||
.excludeFieldsWithModifiers(Modifier.PROTECTED | Modifier.STATIC) | |||
.setDateFormat(JSON_DATE_FORMAT) | |||
.setPrettyPrinting() | |||
.create())); | |||
OkHttpClient.Builder builderOk = new OkHttpClient.Builder(); | |||
builderOk.connectTimeout(10, TimeUnit.SECONDS) | |||
.readTimeout(10, TimeUnit.SECONDS) | |||
.writeTimeout(10, TimeUnit.SECONDS); | |||
//过多的打印会导致盒子io异常 | |||
builderOk.addInterceptor(new RequestInterceptor()); | |||
// if (Config.isShowHttpLog) { | |||
builderOk.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)); | |||
// } else { | |||
// builderOk.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)); | |||
// } | |||
if (BuildConfig.DEBUG) { | |||
builderOk.addInterceptor(new OkHttpProfilerInterceptor()); | |||
} | |||
builderOk.addInterceptor(new ConnectRetryInterceptor(3)); | |||
// builderOk.addInterceptor(new StbInfoInterceptor()); | |||
// builderOk.addInterceptor(new AccountSNInterceptor()); | |||
OkHttpClient okHttpClient = builderOk.connectionPool(new ConnectionPool()).build(); | |||
builder.client(okHttpClient); | |||
mRetrofit = builder.build(); | |||
} | |||
public static <T> T getService(Class<T> t) { | |||
if (mRetrofit == null) { | |||
throw new IllegalStateException("HttpUtil is not init."); | |||
} | |||
return mRetrofit.create(t); | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
package com.bonait.bnframework.api; | |||
/** | |||
* @author Nov | |||
* @date 19.11.25 | |||
*/ | |||
public interface IHttpCallBack<T> { | |||
/** | |||
* 请求成功 | |||
* | |||
* @param t 成功返回参数 | |||
*/ | |||
public void onSuccess(T t); | |||
/** | |||
* 请求成功,但是返回数据处理中发现异常 | |||
* | |||
* @param failCode 失败原因Code 非法请求:ServerUtil.CODE_FAIL_ILLEGAL;其它失败:ServerUtil.CODE_FAIL; 等 | |||
*/ | |||
public void onError(int failCode); | |||
/** | |||
* Http请求失败 | |||
* | |||
* @param throwable 异常,可能为空 | |||
* @param message 其它错误信息 | |||
*/ | |||
public void onFailed(int failCode, Throwable throwable, String message); | |||
} |
@@ -0,0 +1,56 @@ | |||
package com.bonait.bnframework.api; | |||
import com.bonait.bnframework.common.constant.ConfigName; | |||
import com.bonait.bnframework.common.model.GoodsData; | |||
import com.bonait.bnframework.common.model.upload.UploadRes; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import okhttp3.MediaType; | |||
import okhttp3.MultipartBody; | |||
import okhttp3.RequestBody; | |||
import retrofit2.Call; | |||
public class ServerManager { | |||
/** | |||
* 热搜 | |||
* | |||
* @param callback | |||
*/ | |||
public static void getGoodsData(IHttpCallBack<GoodsData> callback) { | |||
String url = "https://cfv.black-pa.com/kitchbase"+"/api/goods/Getdevicegoods?deviceId="+ ConfigName.getInstance().DeviceAutoKey; | |||
Call call = HttpUtil.getService(Api.class).getGoodsData(url); | |||
comEnqueue(call, callback); | |||
} | |||
/** | |||
* 获取消息数据 | |||
* | |||
* @param device 设备 | |||
* @param faultCode 故障代码 | |||
* @param sceneCode 场景code | |||
* @param file 文件 | |||
* @param callback | |||
*/ | |||
public static void upLoadLogFile(String device, String faultCode, String sceneCode, MultipartBody.Part file, IHttpCallBack<UploadRes<String>> callback) { | |||
// String url = "/logfile/upload"; | |||
// Call<UploadRes<String>> call = HttpUtil.getService(Api.class).upLoadLogFile(url, itvName, device, faultCode, sceneCode, serialNo, file); | |||
// CommonResponse<UploadRes<String>> response = new CommonResponse<>(callback); | |||
// call.enqueue(response); | |||
} | |||
/** | |||
* >>>>>>> .merge_file_a00276 | |||
* cms同步请求 | |||
* | |||
* @param call | |||
* @param callBack | |||
*/ | |||
private static void comEnqueue(Call call, IHttpCallBack callBack) { | |||
CommonResponse hrh = new CommonResponse(callBack); | |||
call.enqueue(hrh); | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
//package com.amt.module_common.net.interceptor | |||
// | |||
//import okhttp3.* | |||
// | |||
///** | |||
// * @author Nov | |||
// * @date 23.11.23 | |||
// */ | |||
//class AccountSNInterceptor : Interceptor { | |||
// override fun intercept(chain: Interceptor.Chain): Response { | |||
// val request = addAccountSN(chain.request()) | |||
// return chain.proceed(request) | |||
// } | |||
// | |||
// /** | |||
// * 添加公共字段 | |||
// */ | |||
// private fun addAccountSN(request: Request): Request { | |||
// val oldRequest: Request = request //旧连接LoginActivity | |||
// var newRequest: Request //添加公共参数后的新连接 | |||
// val method = oldRequest.method() | |||
// if (method.equals("GET")) { | |||
// var httpUrl = oldRequest.url() | |||
// httpUrl = httpUrl.newBuilder() | |||
//// .addEncodedQueryParameter("publicParamsSN", LocalDataManager.sn()) | |||
// .build() | |||
// newRequest = oldRequest.newBuilder().url(httpUrl).build() | |||
// } else if (method.equals("POST")) { | |||
// val oldBody = oldRequest.body() | |||
// newRequest = oldRequest | |||
// oldBody?.let { | |||
// newRequest = if (it is FormBody) { | |||
// val newBody = FormBody.Builder() | |||
//// newBody.addEncoded("publicParamsAccount", LocalDataManager.userId()) | |||
// if (it.contentLength() > 0) { | |||
// val formBody1 = it | |||
// for (i in 0 until formBody1.size()) { | |||
// newBody.addEncoded(formBody1.encodedName(i), formBody1.encodedValue(i)) | |||
// } | |||
// } | |||
// oldRequest.newBuilder().post(newBody.build()).build() | |||
// } else { | |||
// oldRequest | |||
// } | |||
// } | |||
// } else { | |||
// newRequest = oldRequest | |||
// } | |||
// return newRequest | |||
// } | |||
//} |
@@ -0,0 +1,71 @@ | |||
package com.bonait.bnframework.api.interceptor; | |||
import org.greenrobot.eventbus.EventBus; | |||
import org.jetbrains.annotations.NotNull; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import okhttp3.Interceptor; | |||
import okhttp3.Request; | |||
import okhttp3.Response; | |||
/** | |||
* 网络请求重试拦截器 | |||
* | |||
* @author: wx | |||
* @date: 2022/12/21 | |||
*/ | |||
public class ConnectRetryInterceptor implements Interceptor { | |||
// 最大重试次数 | |||
private int maxRetry; | |||
//失败连续请求时间 | |||
private List<Long> failTimeList = new ArrayList<>(); | |||
public ConnectRetryInterceptor(int maxRetry) { | |||
this.maxRetry = maxRetry; | |||
} | |||
@NotNull | |||
@Override | |||
public Response intercept(@NotNull Chain chain) throws IOException { | |||
Request request = chain.request(); | |||
Response response = chain.proceed(request); | |||
int retryNum = 0; | |||
if (!response.isSuccessful()) { | |||
dealFail(); | |||
while (retryNum < maxRetry) { | |||
retryNum++; | |||
response = chain.proceed(request); | |||
} | |||
} else { | |||
if (failTimeList.size() > 0) { | |||
failTimeList.clear(); | |||
} | |||
} | |||
return response; | |||
} | |||
private void dealFail() { | |||
long curTimeMillis = System.currentTimeMillis(); | |||
if (failTimeList.size() > 0) { | |||
Iterator<Long> iterator = failTimeList.iterator(); | |||
while (iterator.hasNext()) { | |||
Long timeMillis = iterator.next(); | |||
//超过一分钟的移除 | |||
if (curTimeMillis - timeMillis >= 60 * 1000) { | |||
iterator.remove(); | |||
} | |||
} | |||
//连续3次请求错误 | |||
// if(failTimeList.size() >=3){ | |||
// EventBus.getDefault().post(new EventBusMessage(EventBusType.FETCH_FAULT_DETECT,0)); | |||
// } | |||
} else { | |||
failTimeList.add(curTimeMillis); | |||
} | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.bonait.bnframework.api.interceptor; | |||
import java.io.IOException; | |||
import okhttp3.Interceptor; | |||
import okhttp3.Request; | |||
import okhttp3.Response; | |||
/** | |||
* 请求拦截器,修改请求header | |||
* | |||
* @author: wx | |||
* @date: 2022/12/21 | |||
*/ | |||
public class RequestInterceptor implements Interceptor { | |||
@Override | |||
public Response intercept(Chain chain) throws IOException { | |||
Request request = chain.request().newBuilder().addHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8").build(); | |||
return chain.proceed(request); | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
package com.bonait.bnframework.api.interceptor; | |||
import android.text.TextUtils; | |||
import android.util.Base64; | |||
import com.google.gson.JsonObject; | |||
import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import okhttp3.Interceptor; | |||
import okhttp3.Request; | |||
import okhttp3.Response; | |||
/** | |||
* 系统属性 | |||
* | |||
* @author: wx | |||
* @date: 2023/2/23 | |||
*/ | |||
public class StbInfoInterceptor implements Interceptor { | |||
@Override | |||
public Response intercept(Chain chain) throws IOException { | |||
JsonObject jb = new JsonObject(); | |||
// try { | |||
// if (DataSaveUtil.Companion.getInstance().decodeBoolean(SpConfig.SP_KEY_USER_IS_LOGIN)) { | |||
// jb.addProperty("account", LocalDataManager.INSTANCE.userId()); | |||
// }else { | |||
// jb.addProperty("account", ""); | |||
// } | |||
// jb.addProperty("areaCode", LocalDataManager.INSTANCE.areaId()); | |||
// jb.addProperty("groupCode", LocalDataManager.INSTANCE.userGroup()); | |||
// jb.addProperty("liveGroup", LocalDataManager.INSTANCE.liveGroup()); | |||
// jb.addProperty("manufacturer", LocalDataManager.INSTANCE.manufacturer()); | |||
// jb.addProperty("model", LocalDataManager.INSTANCE.model()); | |||
// jb.addProperty("mac", LocalDataManager.INSTANCE.mac()); | |||
// jb.addProperty("softVersion", LocalDataManager.INSTANCE.softVersion()); | |||
// jb.addProperty("hardVersion", LocalDataManager.INSTANCE.hardware()); | |||
// jb.addProperty("apkVersion", AppUtil.INSTANCE.getVersionCode()); | |||
// LogUtil.d("stbInfo:" + jb); | |||
// } catch (Exception e) { | |||
// e.printStackTrace(); | |||
// } | |||
byte[] datas = jb.toString().getBytes("UTF-8"); | |||
String base64String = Base64.encodeToString(datas, Base64.DEFAULT); | |||
String base64String1 = encodeHeadInfo(base64String); | |||
Request request = chain.request().newBuilder() | |||
// .addHeader("stbInfo", base64String1) | |||
.build(); | |||
return chain.proceed(request); | |||
} | |||
/** | |||
* 过滤特殊符号 | |||
* | |||
* @param headInfo | |||
* @return | |||
*/ | |||
private String encodeHeadInfo(String headInfo) { | |||
StringBuffer stringBuffer = new StringBuffer(); | |||
for (int i = 0, length = headInfo.length(); i < length; i++) { | |||
char c = headInfo.charAt(i); | |||
if (c <= '\u001f' || c >= '\u007f') { | |||
stringBuffer.append(String.format("", (int) c)); | |||
} else { | |||
stringBuffer.append(c); | |||
} | |||
} | |||
return stringBuffer.toString(); | |||
} | |||
} |
@@ -0,0 +1,430 @@ | |||
package com.bonait.bnframework.ble; | |||
import android.Manifest; | |||
import android.annotation.SuppressLint; | |||
import android.bluetooth.BluetoothDevice; | |||
import android.bluetooth.BluetoothGatt; | |||
import android.bluetooth.BluetoothGattCallback; | |||
import android.bluetooth.BluetoothGattCharacteristic; | |||
import android.bluetooth.BluetoothGattDescriptor; | |||
import android.bluetooth.BluetoothGattService; | |||
import android.bluetooth.BluetoothProfile; | |||
import android.content.Intent; | |||
import android.content.pm.PackageManager; | |||
import android.os.Build; | |||
import android.os.Bundle; | |||
import android.util.Base64; | |||
import android.util.Log; | |||
import android.view.View; | |||
import android.widget.EditText; | |||
import android.widget.TextView; | |||
import android.widget.Toast; | |||
import androidx.annotation.Nullable; | |||
import androidx.appcompat.app.AppCompatActivity; | |||
import androidx.core.app.ActivityCompat; | |||
import androidx.recyclerview.widget.LinearLayoutManager; | |||
import androidx.recyclerview.widget.RecyclerView; | |||
import com.bonait.bnframework.R; | |||
import java.lang.reflect.Method; | |||
import java.util.Arrays; | |||
import java.util.LinkedList; | |||
import java.util.Queue; | |||
import java.util.UUID; | |||
/** | |||
* BLE客户端 | |||
*/ | |||
@SuppressLint("MissingPermission") | |||
public class BleClientActivity extends AppCompatActivity { | |||
private static final String TAG = BleClientActivity.class.getSimpleName(); | |||
private EditText mWriteET; | |||
private TextView mTips; | |||
private BleDevAdapter mBleDevAdapter; | |||
private BluetoothGatt mBluetoothGatt; | |||
private boolean isConnected = false; | |||
private static final int bleSize = 20; | |||
private static Queue<String> queue = new LinkedList<String>(); | |||
public static final UUID UUID_SERVICE = UUID.fromString("7377647A-0000-1000-8000-00805F9B34FB"); //全部自定义. | |||
public static final UUID UUID_DESC_NOTITY = UUID.fromString("7377647A-0000-1000-adc5-89df72c789ec"); | |||
public static final UUID UUID_CHAR_WRITE = UUID.fromString("7377647A-0000-1000-7264-00805F9B34FB"); | |||
public static final UUID UUID_CHAR_READ_NOTIFY = UUID.fromString("7377647A-0000-1000-6e66-00805F9B34FB"); | |||
// 与服务端连接的Callback | |||
public BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() { | |||
@Override | |||
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { | |||
BluetoothDevice dev = gatt.getDevice(); | |||
Log.i(TAG, String.format("onConnectionStateChange:%s,%s,%s,%s", dev.getName(), dev.getAddress(), status, newState)); | |||
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) { | |||
isConnected = true; | |||
gatt.discoverServices(); //启动服务发现 | |||
} else { | |||
isConnected = false; | |||
closeConn(); | |||
} | |||
logTv(String.format(status == 0 ? (newState == 2 ? "与[%s]连接成功" : "与[%s]连接断开") : ("与[%s]连接出错,错误码:" + status), dev)); | |||
} | |||
@Override | |||
public void onServicesDiscovered(BluetoothGatt gatt, int status) { | |||
Log.i(TAG, String.format("onServicesDiscovered:%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), status)); | |||
if (status == BluetoothGatt.GATT_SUCCESS) { //BLE服务发现成功 | |||
// 遍历获取BLE服务Services/Characteristics/Descriptors的全部UUID | |||
for (BluetoothGattService service : gatt.getServices()) { | |||
StringBuilder allUUIDs = new StringBuilder("UUIDs={\nS=" + service.getUuid().toString()); | |||
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { | |||
allUUIDs.append(",\nC=").append(characteristic.getUuid()); | |||
for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) | |||
allUUIDs.append(",\nD=").append(descriptor.getUuid()); | |||
} | |||
allUUIDs.append("}"); | |||
Log.i(TAG, "onServicesDiscovered:" + allUUIDs.toString()); | |||
logTv("发现服务" + allUUIDs); | |||
} | |||
} | |||
} | |||
@Override | |||
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { | |||
UUID uuid = characteristic.getUuid(); | |||
String valueStr = new String(characteristic.getValue()); | |||
Log.e(TAG, String.format("onCharacteristicRead:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status)); | |||
logTv("读取Characteristic[" + uuid + "]:\n" + valueStr); | |||
} | |||
@Override | |||
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { | |||
UUID uuid = characteristic.getUuid(); | |||
String valueStr = new String(characteristic.getValue()); | |||
Log.e(TAG, String.format("onCharacteristicWrite:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status)); | |||
logTv("写入Characteristic[" + uuid + "]:\n" + valueStr); | |||
if(queue.size()>0){ | |||
bleNotifyDevice(Base64.decode(queue.poll(), Base64.NO_WRAP)); | |||
} | |||
} | |||
@Override | |||
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { | |||
UUID uuid = characteristic.getUuid(); | |||
String valueStr = new String(characteristic.getValue()); | |||
Log.e(TAG, String.format("onCharacteristicChanged:%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr)); | |||
logTv("通知Characteristic[" + uuid + "]:\n" + valueStr); | |||
} | |||
@Override | |||
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { | |||
UUID uuid = descriptor.getUuid(); | |||
String valueStr = Arrays.toString(descriptor.getValue()); | |||
Log.i(TAG, String.format("onDescriptorRead:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status)); | |||
logTv("读取Descriptor[" + uuid + "]:\n" + valueStr); | |||
} | |||
@Override | |||
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { | |||
UUID uuid = descriptor.getUuid(); | |||
String valueStr = Arrays.toString(descriptor.getValue()); | |||
Log.e(TAG, String.format("onDescriptorWrite:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status)); | |||
logTv("写入Descriptor[" + uuid + "]:\n" + valueStr); | |||
} | |||
}; | |||
@Override | |||
protected void onCreate(Bundle savedInstanceState) { | |||
super.onCreate(savedInstanceState); | |||
setContentView(R.layout.activity_bleclient); | |||
RecyclerView rv = findViewById(R.id.rv_ble); | |||
mWriteET = findViewById(R.id.et_write); | |||
mTips = findViewById(R.id.tv_tips); | |||
rv.setLayoutManager(new LinearLayoutManager(this)); | |||
mBleDevAdapter = new BleDevAdapter(new BleDevAdapter.Listener() { | |||
@SuppressLint("NewApi") | |||
@Override | |||
public void onItemClick(BluetoothDevice dev) { | |||
closeConn(); | |||
mBluetoothGatt = dev.connectGatt(BleClientActivity.this, false, mBluetoothGattCallback,BluetoothDevice.TRANSPORT_LE); | |||
//autoConnect 这意味着 是否直接连接到远程设备(false)或在远程设备可用时立即自动连接(true) | |||
//以下方案还是无效 | |||
//mBluetoothGatt = dev.connectGatt(BleClientActivity.this, true, mBluetoothGattCallback,TRANSPORT_LE); // 连接蓝牙设备 | |||
//bluetoothDevice.connectGatt(MainActivity.this, true, gattCallback);总是连接失败,提示status返回133,用了各种方法都不行, | |||
// 后台一查才发现6.0及以上系统的手机要使用bluetoothDevice.connectGatt(MainActivity.this,true, gattCallback, TRANSPORT_LE), | |||
// 其中TRANSPORT_LE参数是设置传输层模式。传输层模式有三种TRANSPORT_AUTO 、TRANSPORT_BREDR 和TRANSPORT_LE。 | |||
// 如果不传默认TRANSPORT_AUTO,6.0系统及以上需要使用TRANSPORT_LE这种传输模式, | |||
// 具体为啥,我也不知道,我猜是因为Android6.0及以上系统重新定义了蓝牙BLE的传输模式必须使用TRANSPORT_LE这种方式吧。 | |||
//发起蓝牙Gatt连接 BluetoothDevice.connectGatt(Context context, boolean autoConnect, | |||
//BluetoothGattCallback callback),这里有一个参数autoConnect,如果为 true | |||
//的话,系统就会发起一个后台连接,等到系统发现了一个设备,就会自动连上,通常这个过程是非常慢的。为 | |||
//false 的话,就会直接连接,通常会比较快。同样,BluetoothGatt.connect()只能发起一个后台连接,不是直 | |||
//接连接,所以连接时设置autoConnect参数设置为false,如果想实现重连功能的话,自己去手动实现吧,实在不 | |||
//想手动写 | |||
logTv(String.format("与[%s]开始连接............", dev)); | |||
} | |||
}); | |||
rv.setAdapter(mBleDevAdapter); | |||
checkPermissions(); | |||
} | |||
@Override | |||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { | |||
super.onActivityResult(requestCode, resultCode, data); | |||
checkPermissions(); | |||
} | |||
private void checkPermissions(){ | |||
boolean isPermission = true; | |||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | |||
String[] permissions = { | |||
Manifest.permission.ACCESS_COARSE_LOCATION, | |||
Manifest.permission.ACCESS_FINE_LOCATION}; | |||
for (String str : permissions) { | |||
if (checkSelfPermission(str) != PackageManager.PERMISSION_GRANTED) { | |||
Log.i("tag"," requestPermissions "+str); | |||
ActivityCompat.requestPermissions(this,permissions, 111); | |||
isPermission = false; | |||
break; | |||
}else { | |||
Log.i("tag"," permissions true "+str); | |||
} | |||
} | |||
} | |||
if(isPermission){ | |||
} | |||
} | |||
@Override | |||
protected void onDestroy() { | |||
super.onDestroy(); | |||
closeConn(); | |||
} | |||
/** | |||
* 清理本地的BluetoothGatt 的缓存,以保证在蓝牙连接设备的时候,设备的服务、特征是最新的 | |||
* @param gatt | |||
* @return | |||
*/ | |||
public boolean refreshDeviceCache(BluetoothGatt gatt) { | |||
if(null != gatt){ | |||
try { | |||
BluetoothGatt localBluetoothGatt = gatt; | |||
Method localMethod = localBluetoothGatt.getClass().getMethod( "refresh", new Class[0]); | |||
if (localMethod != null) { | |||
boolean bool = ((Boolean) localMethod.invoke( | |||
localBluetoothGatt, new Object[0])).booleanValue(); | |||
return bool; | |||
} | |||
} catch (Exception localException) { | |||
localException.printStackTrace(); | |||
} | |||
} | |||
return false; | |||
} | |||
// BLE中心设备连接外围设备的数量有限(大概2~7个),在建立新连接之前必须释放旧连接资源,否则容易出现连接错误133 | |||
private void closeConn() { | |||
if (mBluetoothGatt != null) { | |||
refreshDeviceCache(mBluetoothGatt); | |||
mBluetoothGatt.disconnect(); | |||
mBluetoothGatt.close(); | |||
} | |||
} | |||
// 扫描BLE | |||
public void reScan(View view) { | |||
if (mBleDevAdapter.isScanning) | |||
Toast.makeText(this, "正在扫描", Toast.LENGTH_SHORT).show(); | |||
else | |||
mBleDevAdapter.reScan(); | |||
} | |||
// 清除Log | |||
public void cleanLog(View view) { | |||
mTips.setText(""); | |||
} | |||
// 注意:连续频繁读写数据容易失败,读写操作间隔最好200ms以上,或等待上次回调完成后再进行下次读写操作! | |||
// 读取数据成功会回调->onCharacteristicChanged() | |||
public void read(View view) { | |||
BluetoothGattService service = getGattService(UUID_SERVICE); | |||
if (service != null) { | |||
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_CHAR_READ_NOTIFY);//通过UUID获取可读的Characteristic | |||
mBluetoothGatt.readCharacteristic(characteristic); | |||
} | |||
} | |||
// 注意:连续频繁读写数据容易失败,读写操作间隔最好200ms以上,或等待上次回调完成后再进行下次读写操作! | |||
// 写入数据成功会回调->onCharacteristicWrite() | |||
public void write(View view) { | |||
BluetoothGattService service = getGattService(UUID_SERVICE); | |||
if (service != null) { | |||
String text = mWriteET.getText().toString(); | |||
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_CHAR_WRITE);//通过UUID获取可写的Characteristic | |||
characteristic.setValue(text.getBytes()); //单次最多20个字节 | |||
mBluetoothGatt.writeCharacteristic(characteristic); | |||
} | |||
} | |||
// 设置通知Characteristic变化会回调->onCharacteristicChanged() | |||
public void setNotify(View view) { | |||
BluetoothGattService service = getGattService(UUID_SERVICE); | |||
if (service != null) { | |||
// 设置Characteristic通知 | |||
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_CHAR_READ_NOTIFY);//通过UUID获取可通知的Characteristic | |||
mBluetoothGatt.setCharacteristicNotification(characteristic, true); | |||
// 向Characteristic的Descriptor属性写入通知开关,使蓝牙设备主动向手机发送数据 | |||
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID_DESC_NOTITY); | |||
// descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);//和通知类似,但服务端不主动发数据,只指示客户端读取数据 | |||
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); | |||
mBluetoothGatt.writeDescriptor(descriptor); | |||
} | |||
} | |||
// 获取Gatt服务 | |||
private BluetoothGattService getGattService(UUID uuid) { | |||
if (!isConnected) { | |||
Toast.makeText(this, "没有连接", Toast.LENGTH_SHORT).show(); | |||
return null; | |||
} | |||
BluetoothGattService service = mBluetoothGatt.getService(uuid); | |||
if (service == null) | |||
Toast.makeText(this, "没有找到服务UUID", Toast.LENGTH_SHORT).show(); | |||
return service; | |||
} | |||
// 输出日志 | |||
private void logTv(final String msg) { | |||
if (isDestroyed()) | |||
return; | |||
runOnUiThread(new Runnable() { | |||
@Override | |||
public void run() { | |||
mTips.append(msg + "\n\n"); | |||
} | |||
}); | |||
} | |||
public void writeCmd(View view){ | |||
BluetoothGattService service = getGattService(UUID_SERVICE); | |||
if (service != null) { | |||
String text = "01010100002D22222222222222222222222222"; | |||
byte[] write_cmd = text.getBytes(); | |||
//byte[] write_cmd = new byte[] {(byte)0x01,0x01, 0x01, 0x00,0x00,(byte)0x2D}; | |||
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_CHAR_WRITE);//通过UUID获取可写的Characteristic | |||
characteristic.setValue(write_cmd); //单次最多20个字节 | |||
mBluetoothGatt.writeCharacteristic(characteristic); | |||
} | |||
} | |||
public static byte[] hexStrToByteArray(String str) { | |||
if (str == null) { | |||
return null; | |||
} | |||
if (str.length() == 0) { | |||
return new byte[0]; | |||
} | |||
byte[] byteArray = new byte[str.length() / 2]; | |||
for (int i = 0; i < byteArray.length; i++) { | |||
String subStr = str.substring(2 * i, 2 * i + 2); | |||
byteArray[i] = ((byte) Integer.parseInt(subStr, 16)); | |||
} | |||
return byteArray; | |||
} | |||
public void writeLongCmd(View view){ | |||
//String text = "AA010101010000"; | |||
String text = "AA01010101000F0102030405060708090A0B0C0D0E0F"; | |||
String crc = "123"; | |||
String message = text + crc; | |||
byte[] write_cmd = hexStrToByteArray(message); | |||
//byte[] write_cmd = new byte[] {(byte)0xaa,0x01,0x01,0x01,0x01,0x00,0x00,(byte)0x2D}; | |||
// 1、定义通讯协议,如下(这里只是个举例,可以项目扩展) | |||
// 协议号(2个字节) 消息号(1个字节) 功能(1个字节) 子功能(1个字节) 数据长度(2个字节) Data(N个字节) CRC校验(1个字节) | |||
// AA01 01 01 01 0000 2D | |||
// 消息号(1个字节) 功能(1个字节) 子功能(1个字节) 数据长度(2个字节) 数据内容(N个字节) CRC校验(1个字节) | |||
// 01 01 01 0000 -- 2D | |||
//2、封装通用发送数据接口(拆包) | |||
//该接口根据会发送数据内容按最大字节数拆分(一般20字节)放入队列,拆分完后,依次从队列里取出发送 | |||
//3、封装通用接收数据接口(组包) | |||
//该接口根据从接收的数据按协议里的定义解析数据长度判读是否完整包,不是的话把每条消息累加起来 | |||
//4、解析完整的数据包,进行业务逻辑处理 | |||
//5、协议还可以加密解密,需要注意的选算法参数的时候,加密后的长度最好跟原数据长度一致,这样不会影响拆包组包 | |||
logTv("写入长指令(大于20字节):\n" + message); | |||
bleNotifyDeviceByQueue(false,write_cmd); | |||
} | |||
private void bleNotifyDeviceByQueue(boolean isNeedEncrypt, byte[] data){ | |||
Log.e(TAG, "bleNotifyAppByQueue isNeedEncrypt=" + isNeedEncrypt); | |||
boolean isNeedSend = true; | |||
if (queue.size() > 0) { | |||
isNeedSend = false; | |||
} | |||
int section = data.length / bleSize; | |||
int remain = data.length % bleSize; | |||
//Log.e(TAG, "queue.size=" + queue.size() + ",data.length=" + data.length + ",section=" + section + ",remain=" + remain); | |||
if (section > 1 || (section == 1 && remain > 0)) { | |||
for (int i = 0; i < section; i++) { | |||
byte b_data[] = new byte[bleSize]; | |||
System.arraycopy(data, i * bleSize, b_data, 0, bleSize); | |||
queue.add(Base64.encodeToString(b_data, Base64.NO_WRAP)); | |||
} | |||
if (remain > 0) { | |||
byte b_data[] = new byte[remain]; | |||
System.arraycopy(data, section * bleSize, b_data, 0, remain); | |||
queue.add(Base64.encodeToString(b_data, Base64.NO_WRAP)); | |||
} | |||
} else { | |||
byte b_data[] = new byte[data.length]; | |||
System.arraycopy(data, 0, b_data, 0, data.length); | |||
queue.add(Base64.encodeToString(b_data, Base64.NO_WRAP)); | |||
} | |||
if (isNeedSend) { | |||
bleNotifyDevice(Base64.decode(queue.poll(), Base64.NO_WRAP)); | |||
} | |||
} | |||
private void bleNotifyDevice(byte[] data) { | |||
BluetoothGattService service = getGattService(UUID_SERVICE); | |||
if (service != null) { | |||
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_CHAR_WRITE);//通过UUID获取可写的Characteristic | |||
characteristic.setValue(data); //单次最多20个字节 | |||
mBluetoothGatt.writeCharacteristic(characteristic); | |||
} | |||
} | |||
} |
@@ -0,0 +1,186 @@ | |||
package com.bonait.bnframework.ble; | |||
import android.annotation.SuppressLint; | |||
import android.bluetooth.BluetoothAdapter; | |||
import android.bluetooth.BluetoothClass; | |||
import android.bluetooth.BluetoothDevice; | |||
import android.bluetooth.le.BluetoothLeScanner; | |||
import android.bluetooth.le.ScanCallback; | |||
import android.bluetooth.le.ScanResult; | |||
import android.graphics.Color; | |||
import android.os.Handler; | |||
import android.util.Log; | |||
import android.view.LayoutInflater; | |||
import android.view.View; | |||
import android.view.ViewGroup; | |||
import android.widget.TextView; | |||
import androidx.annotation.NonNull; | |||
import androidx.recyclerview.widget.RecyclerView; | |||
import com.bonait.bnframework.R; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Objects; | |||
@SuppressLint("MissingPermission") | |||
public class BleDevAdapter extends RecyclerView.Adapter<BleDevAdapter.VH> { | |||
private static final String TAG = BleDevAdapter.class.getSimpleName(); | |||
private static final String DeviceName = "62F";//要搜索的设备蓝牙名称高亮显示 | |||
private final Listener mListener; | |||
private final Handler mHandler = new Handler(); | |||
private final List<BleDev> mDevices = new ArrayList<>(); | |||
public boolean isScanning; | |||
@SuppressLint("NewApi") | |||
private final ScanCallback mScanCallback = new ScanCallback() {// 扫描Callback | |||
@Override | |||
public void onScanResult(int callbackType, ScanResult result) { | |||
BluetoothDevice device = result.getDevice(); | |||
BleDev dev = new BleDev(device, result); | |||
Log.i(TAG, "onScan: device=" + device+";dev="+dev); | |||
if (!mDevices.contains(dev) &&device!=null&& device.getName()!=null) {//&& device.getName().contains("62F") && device.getName()!=null | |||
String deviceName = device.getName();// 获取蓝牙设备名字 | |||
String deviceAddress = device.getAddress();// 获取蓝牙设备mac地址 | |||
int deviceBondState = device.getBondState();// 蓝牙绑定状态 | |||
if(device.getName()!=null){ | |||
mDevices.add(0,dev); | |||
}else { | |||
mDevices.add(dev); | |||
} | |||
/*if(deviceName!=null && !deviceName.toLowerCase().contains("mi")){ | |||
mDevices.add(dev); | |||
}*/ | |||
BluetoothClass bluetoothClass = device.getBluetoothClass(); | |||
final int deviceClass = bluetoothClass.getDeviceClass(); //设备类型(音频、手机、电脑、音箱等等) | |||
final int majorDeviceClass = bluetoothClass.getMajorDeviceClass();//具体的设备类型(例如音频设备又分为音箱、耳机、麦克风等等) | |||
Log.e(TAG,"onScanResult ======= deviceName:"+deviceName+",deviceAddress="+deviceAddress+",deviceBondState="+deviceBondState | |||
+",deviceClass="+Integer.toHexString(deviceClass)+",majorDeviceClass="+Integer.toHexString(majorDeviceClass)); | |||
notifyDataSetChanged(); | |||
Log.i(TAG, "onScanResult: " + result); // result.getScanRecord() 获取BLE广播数据 | |||
} | |||
} | |||
}; | |||
BleDevAdapter(Listener listener) { | |||
mListener = listener; | |||
scanBle(); | |||
} | |||
// 重新扫描 | |||
public void reScan() { | |||
mDevices.clear(); | |||
notifyDataSetChanged(); | |||
scanBle(); | |||
} | |||
// 扫描BLE蓝牙(不会扫描经典蓝牙) | |||
@SuppressLint("NewApi") | |||
private void scanBle() { | |||
isScanning = true; | |||
Log.e(TAG,"scanBle"); | |||
// BluetoothAdapter bluetoothAdapter = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE).getDefaultAdapter(); | |||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); | |||
final BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); | |||
// Android5.0新增的扫描API,扫描返回的结果更友好,比如BLE广播数据以前是byte[] scanRecord,而新API帮我们解析成ScanRecord类 | |||
bluetoothLeScanner.startScan(mScanCallback); | |||
mHandler.postDelayed(new Runnable() { | |||
@Override | |||
public void run() { | |||
bluetoothLeScanner.stopScan(mScanCallback); //停止扫描 | |||
isScanning = false; | |||
} | |||
}, 15000); | |||
} | |||
@NonNull | |||
@Override | |||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | |||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_dev, parent, false); | |||
return new VH(view); | |||
} | |||
@SuppressLint("NewApi") | |||
@Override | |||
public void onBindViewHolder(@NonNull final VH holder, int position) { | |||
BleDev dev = mDevices.get(position); | |||
String name = dev.dev.getName(); | |||
String address = dev.dev.getAddress(); | |||
holder.name.setText(String.format("%s, %s, Rssi=%s", name, address, dev.scanResult.getRssi())); | |||
holder.address.setText(String.format("广播数据{%s}", dev.scanResult.getScanRecord())); | |||
if(holder.name.getText().toString().toLowerCase().contains(DeviceName)){ | |||
Log.e(TAG,"c="+name); | |||
holder.name.setBackgroundColor(Color.RED); | |||
}else { | |||
holder.name.setBackgroundColor(0); | |||
} | |||
} | |||
@Override | |||
public int getItemCount() { | |||
return mDevices.size(); | |||
} | |||
class VH extends RecyclerView.ViewHolder implements View.OnClickListener { | |||
final TextView name; | |||
final TextView address; | |||
VH(final View itemView) { | |||
super(itemView); | |||
itemView.setOnClickListener(this); | |||
name = itemView.findViewById(R.id.name); | |||
address = itemView.findViewById(R.id.address); | |||
} | |||
@Override | |||
public void onClick(View v) { | |||
int pos = getAdapterPosition(); | |||
Log.d(TAG, "onClick, getAdapterPosition=" + pos); | |||
if (pos >= 0 && pos < mDevices.size()) | |||
mListener.onItemClick(mDevices.get(pos).dev); | |||
} | |||
} | |||
public interface Listener { | |||
void onItemClick(BluetoothDevice dev); | |||
} | |||
public static class BleDev { | |||
public BluetoothDevice dev; | |||
ScanResult scanResult; | |||
BleDev(BluetoothDevice device, ScanResult result) { | |||
dev = device; | |||
scanResult = result; | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) return true; | |||
if (o == null || getClass() != o.getClass()) return false; | |||
BleDev bleDev = (BleDev) o; | |||
return Objects.equals(dev, bleDev.dev); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return Objects.hash(dev); | |||
} | |||
} | |||
} |
@@ -0,0 +1,297 @@ | |||
package com.bonait.bnframework.ble; | |||
import android.annotation.SuppressLint; | |||
import android.bluetooth.BluetoothAdapter; | |||
import android.bluetooth.BluetoothDevice; | |||
import android.bluetooth.BluetoothGatt; | |||
import android.bluetooth.BluetoothGattCharacteristic; | |||
import android.bluetooth.BluetoothGattDescriptor; | |||
import android.bluetooth.BluetoothGattServer; | |||
import android.bluetooth.BluetoothGattServerCallback; | |||
import android.bluetooth.BluetoothGattService; | |||
import android.bluetooth.BluetoothManager; | |||
import android.bluetooth.le.AdvertiseCallback; | |||
import android.bluetooth.le.AdvertiseData; | |||
import android.bluetooth.le.AdvertiseSettings; | |||
import android.bluetooth.le.BluetoothLeAdvertiser; | |||
import android.content.Context; | |||
import android.os.Bundle; | |||
import android.os.Handler; | |||
import android.os.Looper; | |||
import android.os.Message; | |||
import android.os.ParcelUuid; | |||
import android.util.Log; | |||
import android.view.View; | |||
import androidx.annotation.NonNull; | |||
import androidx.annotation.Nullable; | |||
import androidx.appcompat.app.AppCompatActivity; | |||
import com.bonait.bnframework.databinding.ActivityServiceBinding; | |||
import java.util.UUID; | |||
/** | |||
* @author liupeng | |||
* @description: | |||
* @date :2024/2/5 10:02 | |||
*/ | |||
@SuppressLint("MissingPermission") | |||
public class BleServiceActivity extends AppCompatActivity { | |||
private final String TAG = "MainActivity==>"; | |||
private ActivityServiceBinding bindingView; | |||
private Handler handler = new Handler(Looper.getMainLooper()){ | |||
@SuppressLint("HandlerLeak") | |||
@Override | |||
public void handleMessage(@NonNull Message msg) { | |||
super.handleMessage(msg); | |||
} | |||
}; | |||
@Override | |||
protected void onCreate(@Nullable Bundle savedInstanceState) { | |||
super.onCreate(savedInstanceState); | |||
bindingView = ActivityServiceBinding.inflate(getLayoutInflater()); | |||
setContentView(bindingView.getRoot()); | |||
bindingView.btn1.setOnClickListener(new View.OnClickListener() { | |||
@Override | |||
public void onClick(View v) { | |||
if(!open){ | |||
openBlueTooth(); | |||
startBLEServer(); | |||
open = true; | |||
} | |||
} | |||
}); | |||
} | |||
@Override | |||
protected void onPause() { | |||
super.onPause(); | |||
finish(); | |||
} | |||
@Override | |||
protected void onDestroy() { | |||
super.onDestroy(); | |||
closeBlueTooth(); | |||
stopBleServer(); | |||
} | |||
private boolean open = false; | |||
@SuppressLint("MissingPermission") | |||
public void openBlueTooth(){ | |||
// 打开蓝牙(提示对话框) | |||
// Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); | |||
// discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); | |||
// startActivity(discoverableIntent); | |||
// 打开蓝牙(静默,无提示) | |||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); | |||
bluetoothAdapter.enable();//需要BLUETOOTH_ADMIN权限 | |||
} | |||
@SuppressLint("MissingPermission") | |||
public void closeBlueTooth(){ | |||
// 关闭蓝牙 | |||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); | |||
bluetoothAdapter.disable(); | |||
} | |||
public static final UUID UUID_SERVICE = UUID.fromString("7377647A-0000-1000-8000-00805F9B34FB"); //全部自定义. | |||
public static final UUID UUID_DESC_NOTITY = UUID.fromString("7377647A-0000-1000-adc5-89df72c789ec"); | |||
public static final UUID UUID_CHAR_WRITE = UUID.fromString("7377647A-0000-1000-7264-00805F9B34FB"); | |||
public static final UUID UUID_CHAR_READ_NOTIFY = UUID.fromString("7377647A-0000-1000-6e66-00805F9B34FB"); | |||
private BluetoothLeAdvertiser mBluetoothLeAdvertiser; // BLE广播 | |||
private BluetoothGattServer mBluetoothGattServer; // BLE服务端 | |||
private BluetoothGatt bluetoothGatt; | |||
// BLE广播Callback | |||
@SuppressLint("NewApi") | |||
private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() { | |||
@Override | |||
public void onStartSuccess(AdvertiseSettings settingsInEffect) { | |||
Log.d(TAG,"BLE广播开启成功"); | |||
handler.post(new Runnable() { | |||
@Override | |||
public void run() { | |||
bindingView.tvState.setText("蓝牙已开启"); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void onStartFailure(int errorCode) { | |||
Log.e(TAG,"BLE广播开启失败,错误码:" + errorCode); | |||
handler.post(new Runnable() { | |||
@Override | |||
public void run() { | |||
bindingView.tvState.setText("蓝牙开启失败"); | |||
} | |||
}); | |||
} | |||
}; | |||
// private final BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() { | |||
// @Override | |||
// public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { | |||
// super.onMtuChanged(gatt, mtu, status); | |||
// Log.d("onMtuChanged"," onMtuChanged"); | |||
// if (BluetoothGatt.GATT_SUCCESS == status) { | |||
// Log.d("BleService", "onMtuChanged success MTU = " + mtu); | |||
// }else { | |||
// Log.d("BleService", "onMtuChanged fail "); | |||
// } | |||
// } | |||
// }; | |||
// private boolean requestMtu2(){ | |||
// if (bluetoothGatt != null) { | |||
// // 25 *6 +3 =153 | |||
// return bluetoothGatt.requestMtu(517); | |||
// } | |||
// return false; | |||
// } | |||
/* | |||
* 蓝牙连接,通信 | |||
* */ | |||
// BLE服务端Callback | |||
private final BluetoothGattServerCallback mBluetoothGattServerCallback = new BluetoothGattServerCallback() { | |||
@SuppressLint("MissingPermission") | |||
@Override | |||
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { | |||
// bluetoothGatt = device.connectGatt(getApplication(), true, mBluetoothGattCallback); | |||
// Log.i("requestMTUU=",bluetoothGatt.requestMtu(25)+""); | |||
// Log.i("requestMtu2=",requestMtu2()+""); | |||
// onMtuChanged(device,517); | |||
Log.i(TAG, String.format("onConnectionStateChange:%s,%s,%s,%s", device.getName(), device.getAddress(), status, newState)); | |||
String stateStr = String.format(status == 0 ? (newState == 2 ? "与[%s]连接成功" : "与[%s]连接断开") : ("与[%s]连接出错,错误码:" + status), device); | |||
Log.d(TAG,stateStr); | |||
handler.post(new Runnable() { | |||
@Override | |||
public void run() { | |||
bindingView.tvState.setText(stateStr+""); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void onServiceAdded(int status, BluetoothGattService service) { | |||
Log.i(TAG, String.format("onServiceAdded:%s,%s", status, service.getUuid())); | |||
Log.d(TAG,String.format(status == 0 ? "添加服务[%s]成功" : "添加服务[%s]失败,错误码:" + status, service.getUuid())); | |||
} | |||
@SuppressLint("MissingPermission") | |||
@Override | |||
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) { | |||
Log.i(TAG, String.format("onCharacteristicReadRequest:%s,%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, offset, characteristic.getUuid())); | |||
// mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,offset,new byte[]{1, 2,3,4} );// 响应客户端 | |||
String test = " 盒子信息 ******* /// 99"; | |||
byte[] responseBytes = test.getBytes(); | |||
Log.i(TAG,"responseBytes.length=="+responseBytes.length); | |||
if(responseBytes.length>offset){ | |||
int restLen = responseBytes.length-offset; | |||
byte[] bytesCopy = new byte[restLen>22?22:restLen]; | |||
System.arraycopy(responseBytes,offset,bytesCopy,0,restLen>22?22:restLen); | |||
mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,offset,bytesCopy );// 响应客户端 | |||
Log.d(TAG,"客户端读取Characteristic[" + characteristic.getUuid() + "]:\n" + bytesCopy.toString()); | |||
}else{ | |||
mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,offset,responseBytes );// 响应客户端 | |||
} | |||
} | |||
@SuppressLint("MissingPermission") | |||
@Override | |||
public void onCharacteristicWriteRequest(final BluetoothDevice device, int requestId, | |||
final BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) { | |||
// 获取客户端发过来的数据 | |||
String requestStr = new String(requestBytes); | |||
Log.i(TAG, String.format("onCharacteristicWriteRequest:%s,%s,%s,%s,%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, characteristic.getUuid(), | |||
preparedWrite, responseNeeded, offset, "requestStr="+requestStr)); | |||
mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, new byte[]{1,2,1});// 响应客户端 | |||
handler.post(new Runnable() { | |||
@Override | |||
public void run() { | |||
bindingView.tv.setText("收到信息="+requestStr); | |||
} | |||
}); | |||
} | |||
@SuppressLint("MissingPermission") | |||
@Override | |||
public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { | |||
Log.i(TAG, String.format("onExecuteWrite:%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, execute)); | |||
} | |||
@SuppressLint("MissingPermission") | |||
@Override | |||
public void onNotificationSent(BluetoothDevice device, int status) { | |||
Log.i(TAG, String.format("onNotificationSent:%s,%s,%s", device.getName(), device.getAddress(), status)); | |||
} | |||
@SuppressLint("MissingPermission") | |||
@Override | |||
public void onMtuChanged(BluetoothDevice device, int mtu) { | |||
super.onMtuChanged(device, 512); | |||
Log.i(TAG, String.format("onMtuChanged:%s,%s,%s", device.getName(), device.getAddress(), mtu)); | |||
} | |||
}; | |||
BluetoothGattCharacteristic characteristicRead; | |||
@SuppressLint({"NewApi", "MissingPermission"}) | |||
public void startBLEServer(){ | |||
BluetoothManager bluetoothManager = (BluetoothManager) getApplication().getSystemService(Context.BLUETOOTH_SERVICE); | |||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); | |||
bluetoothAdapter.setName("62F"); | |||
// ============启动BLE蓝牙广播(广告) ================================================================================= | |||
Log.i(TAG,"启动BLE蓝牙广播(广告)"); | |||
//广播设置(必须) | |||
AdvertiseSettings settings = new AdvertiseSettings.Builder() | |||
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) //广播模式: 低功耗,平衡,低延迟 | |||
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) //发射功率级别: 极低,低,中,高 | |||
.setConnectable(true) //能否连接,广播分为可连接广播和不可连接广播 | |||
.setTimeout(0) | |||
.build(); | |||
//广播数据(必须,广播启动就会发送) | |||
AdvertiseData advertiseData = new AdvertiseData.Builder() | |||
.setIncludeDeviceName(true) //包含蓝牙名称 | |||
.setIncludeTxPowerLevel(true) //包含发射功率级别 | |||
.addManufacturerData(0x09, "sw".getBytes()) //设备厂商数据,自定义 | |||
.build(); | |||
//扫描响应数据(可选,当客户端扫描时才发送) | |||
AdvertiseData scanResponse = new AdvertiseData.Builder() | |||
.addManufacturerData(2, new byte[]{66, 66,31,45, (byte) 0xac}) //设备厂商数据,自定义 | |||
.addServiceUuid(new ParcelUuid(UUID_SERVICE)) //服务UUID | |||
// .addServiceData(new ParcelUuid(UUID_SERVICE), new byte[]{2}) //服务数据,自定义 | |||
.build(); | |||
mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser(); | |||
mBluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponse, mAdvertiseCallback); | |||
// 注意:必须要开启可连接的BLE广播,其它设备才能发现并连接BLE服务端! | |||
// =============启动BLE蓝牙服务端===================================================================================== | |||
BluetoothGattService service = new BluetoothGattService(UUID_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY); | |||
//添加可读+通知characteristic | |||
characteristicRead = new BluetoothGattCharacteristic(UUID_CHAR_READ_NOTIFY, | |||
BluetoothGattCharacteristic.PROPERTY_NOTIFY|BluetoothGattCharacteristic.PROPERTY_READ|BluetoothGattCharacteristic.PROPERTY_WRITE, | |||
BluetoothGattCharacteristic.PERMISSION_READ|BluetoothGattCharacteristic.PERMISSION_WRITE); | |||
characteristicRead.addDescriptor(new BluetoothGattDescriptor(UUID_DESC_NOTITY, BluetoothGattCharacteristic.PERMISSION_WRITE | BluetoothGattCharacteristic.PERMISSION_READ)); | |||
service.addCharacteristic(characteristicRead); | |||
//添加可写characteristic | |||
BluetoothGattCharacteristic characteristicWrite = new BluetoothGattCharacteristic(UUID_CHAR_WRITE, | |||
BluetoothGattCharacteristic.PROPERTY_NOTIFY|BluetoothGattCharacteristic.PROPERTY_READ|BluetoothGattCharacteristic.PROPERTY_WRITE | |||
, BluetoothGattCharacteristic.PERMISSION_WRITE|BluetoothGattCharacteristic.PERMISSION_READ); | |||
service.addCharacteristic(characteristicWrite); | |||
if (bluetoothManager != null) | |||
mBluetoothGattServer = bluetoothManager.openGattServer(getApplication(), mBluetoothGattServerCallback); | |||
mBluetoothGattServer.addService(service); | |||
} | |||
@SuppressLint("NewApi") | |||
public void stopBleServer(){ | |||
if (mBluetoothLeAdvertiser != null) | |||
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback); | |||
if (mBluetoothGattServer != null) | |||
mBluetoothGattServer.close(); | |||
} | |||
} |
@@ -18,6 +18,7 @@ import com.apkfuns.logutils.LogUtils; | |||
import com.bonait.bnframework.BuildConfig; | |||
import com.bonait.bnframework.MainApplication; | |||
import com.bonait.bnframework.R; | |||
import com.bonait.bnframework.ble.BleClientActivity; | |||
import com.bonait.bnframework.business.MainInit; | |||
import com.bonait.bnframework.common.base.BaseActivity; | |||
import com.bonait.bnframework.common.constant.ConfigName; | |||
@@ -26,6 +27,7 @@ import com.bonait.bnframework.common.helper.CrashHandler; | |||
import com.bonait.bnframework.common.utils.ScreenUtils; | |||
import com.bonait.bnframework.manager.ActivityLifecycleManager; | |||
import com.bonait.bnframework.modules.home.activity.BottomNavigation2Activity; | |||
import com.bonait.bnframework.serial.ComAssistantActivity; | |||
import com.bonait.bnframework.test.TestActivity; | |||
import com.bonait.bnframework.ui.util.DisplayManager; | |||
import com.lzy.okgo.OkGo; | |||
@@ -147,7 +149,9 @@ public class WelcomeActivity extends BaseActivity { | |||
*/ | |||
public void PermissionCheck() { | |||
String[] params = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}; | |||
String[] params = {Manifest.permission.WRITE_EXTERNAL_STORAGE, | |||
Manifest.permission.RECORD_AUDIO, | |||
Manifest.permission.CAMERA}; | |||
//String[] params = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; | |||
//判断是否获取权限 | |||
if (EasyPermissions.hasPermissions(this, params)) { | |||
@@ -254,6 +258,7 @@ public class WelcomeActivity extends BaseActivity { | |||
ConfigName.getInstance().IsPortraitScreen=ScreenUtils.IsPortraitScreen(this); | |||
// 跳转到登录页面 | |||
// Intent intent = new Intent(WelcomeActivity.this, ComAssistantActivity.class); | |||
Intent intent = new Intent(WelcomeActivity.this, LoginActivity.class); | |||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); | |||
startActivity(intent); | |||
@@ -0,0 +1,111 @@ | |||
package com.bonait.bnframework.serial; | |||
import java.io.Serializable; | |||
/** | |||
* @author benjaminwan | |||
*用于保存界面数据 | |||
*/ | |||
public class AssistBean implements Serializable{ | |||
private static final long serialVersionUID = -5620661009186692227L; | |||
private boolean isTxt=true; | |||
private String SendTxtA="COMA",SendTxtB="COMB",SendTxtC="COMC",SendTxtD="COMD"; | |||
private String SendHexA="AA",SendHexB="BB",SendHexC="CC",SendHexD="DD"; | |||
public String sTimeA="500"; | |||
public String sTimeB="500"; | |||
public String sTimeC="500"; | |||
public String sTimeD="500"; | |||
public boolean isTxt() | |||
{ | |||
return isTxt; | |||
} | |||
public void setTxtMode(boolean isTxt) | |||
{ | |||
this.isTxt = isTxt; | |||
} | |||
public String getSendA() | |||
{ | |||
if (isTxt) | |||
{ | |||
return SendTxtA; | |||
} else | |||
{ | |||
return SendHexA; | |||
} | |||
} | |||
public String getSendB() | |||
{ | |||
if (isTxt) | |||
{ | |||
return SendTxtB; | |||
} else | |||
{ | |||
return SendHexB; | |||
} | |||
} | |||
public String getSendC() | |||
{ | |||
if (isTxt) | |||
{ | |||
return SendTxtC; | |||
} else | |||
{ | |||
return SendHexC; | |||
} | |||
} | |||
public String getSendD() | |||
{ | |||
if (isTxt) | |||
{ | |||
return SendTxtD; | |||
} else | |||
{ | |||
return SendHexD; | |||
} | |||
} | |||
public void setSendA(String sendA) | |||
{ | |||
if (isTxt) | |||
{ | |||
SendTxtA = sendA; | |||
} else | |||
{ | |||
SendHexA = sendA; | |||
} | |||
} | |||
public void setSendB(String sendB) | |||
{ | |||
if (isTxt) | |||
{ | |||
SendTxtB = sendB; | |||
} else | |||
{ | |||
SendHexB = sendB; | |||
} | |||
} | |||
public void setSendC(String sendC) | |||
{ | |||
if (isTxt) | |||
{ | |||
SendTxtC = sendC; | |||
} else | |||
{ | |||
SendHexC = sendC; | |||
} | |||
} | |||
public void setSendD(String sendD) | |||
{ | |||
if (isTxt) | |||
{ | |||
SendTxtD = sendD; | |||
} else | |||
{ | |||
SendHexD = sendD; | |||
} | |||
} | |||
} |
@@ -0,0 +1,737 @@ | |||
package com.bonait.bnframework.serial; | |||
import android.app.Activity; | |||
import android.content.Context; | |||
import android.content.SharedPreferences; | |||
import android.content.pm.PackageInfo; | |||
import android.content.pm.PackageManager; | |||
import android.content.pm.PackageManager.NameNotFoundException; | |||
import android.content.res.Configuration; | |||
import android.os.Bundle; | |||
import android.text.InputType; | |||
import android.text.method.KeyListener; | |||
import android.text.method.NumberKeyListener; | |||
import android.text.method.TextKeyListener; | |||
import android.text.method.TextKeyListener.Capitalize; | |||
import android.util.Base64; | |||
import android.view.KeyEvent; | |||
import android.view.View; | |||
import android.widget.AdapterView; | |||
import android.widget.ArrayAdapter; | |||
import android.widget.Button; | |||
import android.widget.CheckBox; | |||
import android.widget.CompoundButton; | |||
import android.widget.EditText; | |||
import android.widget.RadioButton; | |||
import android.widget.Spinner; | |||
import android.widget.TextView; | |||
import android.widget.Toast; | |||
import android.widget.ToggleButton; | |||
import androidx.appcompat.app.AppCompatActivity; | |||
import com.apkfuns.logutils.LogUtils; | |||
import com.bonait.bnframework.R; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.ObjectInputStream; | |||
import java.io.ObjectOutputStream; | |||
import java.security.InvalidParameterException; | |||
import java.util.ArrayList; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Queue; | |||
/** | |||
* serialport api和jni取自http://code.google.com/p/android-serialport-api/ | |||
* | |||
* @author benjaminwan | |||
* 串口助手,支持4串口同时读写 | |||
* 程序载入时自动搜索串口设备 | |||
* n,8,1,没得选 | |||
* | |||
* 本页面是参考页面 | |||
* http://code1.okbase.net/codefile/ComAssistantActivity.java_2014121928629_9.htm | |||
*/ | |||
public class ComAssistantActivity extends AppCompatActivity { | |||
EditText editTextRecDisp, editTextLines, editTextCOMA, editTextCOMB, editTextCOMC, editTextCOMD; | |||
EditText editTextTimeCOMA, editTextTimeCOMB, editTextTimeCOMC, editTextTimeCOMD; | |||
CheckBox checkBoxAutoClear, checkBoxAutoCOMA, checkBoxAutoCOMB, checkBoxAutoCOMC, checkBoxAutoCOMD; | |||
Button ButtonClear, ButtonSendCOMA, ButtonSendCOMB, ButtonSendCOMC, ButtonSendCOMD; | |||
ToggleButton toggleButtonCOMA, toggleButtonCOMB, toggleButtonCOMC, toggleButtonCOMD; | |||
Spinner SpinnerCOMA, SpinnerCOMB, SpinnerCOMC, SpinnerCOMD; | |||
Spinner SpinnerBaudRateCOMA, SpinnerBaudRateCOMB, SpinnerBaudRateCOMC, SpinnerBaudRateCOMD; | |||
RadioButton radioButtonTxt, radioButtonHex; | |||
SerialControl ComA, ComB, ComC, ComD;//4个串口 | |||
DispQueueThread DispQueue;//刷新显示线程 | |||
SerialPortFinder mSerialPortFinder;//串口设备搜索 | |||
AssistBean AssistData;//用于界面数据序列化和反序列化 | |||
int iRecLines = 0;//接收区行数 | |||
/** | |||
* Called when the activity is first created. | |||
*/ | |||
@Override | |||
public void onCreate(Bundle savedInstanceState) { | |||
super.onCreate(savedInstanceState); | |||
setContentView(R.layout.activity_serial_test); | |||
ComA = new SerialControl(); | |||
ComB = new SerialControl(); | |||
ComC = new SerialControl(); | |||
ComD = new SerialControl(); | |||
DispQueue = new DispQueueThread(); | |||
DispQueue.start(); | |||
AssistData = getAssistData(); | |||
setControls(); | |||
} | |||
@Override | |||
public void onDestroy() { | |||
saveAssistData(AssistData); | |||
CloseComPort(ComA); | |||
CloseComPort(ComB); | |||
CloseComPort(ComC); | |||
CloseComPort(ComD); | |||
super.onDestroy(); | |||
} | |||
@Override | |||
public void onConfigurationChanged(Configuration newConfig) { | |||
super.onConfigurationChanged(newConfig); | |||
CloseComPort(ComA); | |||
CloseComPort(ComB); | |||
CloseComPort(ComC); | |||
CloseComPort(ComD); | |||
setContentView(R.layout.activity_serial_test); | |||
setControls(); | |||
} | |||
//---------------------------------------------------- | |||
private void setControls() { | |||
String appName = getString(R.string.app_name); | |||
try { | |||
PackageInfo pinfo = getPackageManager().getPackageInfo("com.mstxx.kaoqin", PackageManager.GET_CONFIGURATIONS); | |||
String versionName = pinfo.versionName; | |||
// String versionCode = String.valueOf(pinfo.versionCode); | |||
setTitle(appName + " V" + versionName); | |||
} catch (NameNotFoundException e) { | |||
e.printStackTrace(); | |||
} | |||
editTextRecDisp = (EditText) findViewById(R.id.editTextRecDisp); | |||
editTextLines = (EditText) findViewById(R.id.editTextLines); | |||
editTextCOMA = (EditText) findViewById(R.id.editTextCOMA); | |||
editTextCOMB = (EditText) findViewById(R.id.editTextCOMB); | |||
editTextCOMC = (EditText) findViewById(R.id.editTextCOMC); | |||
editTextCOMD = (EditText) findViewById(R.id.editTextCOMD); | |||
editTextTimeCOMA = (EditText) findViewById(R.id.editTextTimeCOMA); | |||
editTextTimeCOMB = (EditText) findViewById(R.id.editTextTimeCOMB); | |||
editTextTimeCOMC = (EditText) findViewById(R.id.editTextTimeCOMC); | |||
editTextTimeCOMD = (EditText) findViewById(R.id.editTextTimeCOMD); | |||
checkBoxAutoClear = (CheckBox) findViewById(R.id.checkBoxAutoClear); | |||
checkBoxAutoCOMA = (CheckBox) findViewById(R.id.checkBoxAutoCOMA); | |||
checkBoxAutoCOMB = (CheckBox) findViewById(R.id.checkBoxAutoCOMB); | |||
checkBoxAutoCOMC = (CheckBox) findViewById(R.id.checkBoxAutoCOMC); | |||
checkBoxAutoCOMD = (CheckBox) findViewById(R.id.checkBoxAutoCOMD); | |||
ButtonClear = (Button) findViewById(R.id.ButtonClear); | |||
ButtonSendCOMA = (Button) findViewById(R.id.ButtonSendCOMA); | |||
ButtonSendCOMB = (Button) findViewById(R.id.ButtonSendCOMB); | |||
ButtonSendCOMC = (Button) findViewById(R.id.ButtonSendCOMC); | |||
ButtonSendCOMD = (Button) findViewById(R.id.ButtonSendCOMD); | |||
toggleButtonCOMA = (ToggleButton) findViewById(R.id.toggleButtonCOMA); | |||
toggleButtonCOMB = (ToggleButton) findViewById(R.id.ToggleButtonCOMB); | |||
toggleButtonCOMC = (ToggleButton) findViewById(R.id.ToggleButtonCOMC); | |||
toggleButtonCOMD = (ToggleButton) findViewById(R.id.ToggleButtonCOMD); | |||
SpinnerCOMA = (Spinner) findViewById(R.id.SpinnerCOMA); | |||
SpinnerCOMB = (Spinner) findViewById(R.id.SpinnerCOMB); | |||
SpinnerCOMC = (Spinner) findViewById(R.id.SpinnerCOMC); | |||
SpinnerCOMD = (Spinner) findViewById(R.id.SpinnerCOMD); | |||
SpinnerBaudRateCOMA = (Spinner) findViewById(R.id.SpinnerBaudRateCOMA); | |||
SpinnerBaudRateCOMB = (Spinner) findViewById(R.id.SpinnerBaudRateCOMB); | |||
SpinnerBaudRateCOMC = (Spinner) findViewById(R.id.SpinnerBaudRateCOMC); | |||
SpinnerBaudRateCOMD = (Spinner) findViewById(R.id.SpinnerBaudRateCOMD); | |||
radioButtonTxt = (RadioButton) findViewById(R.id.radioButtonTxt); | |||
radioButtonHex = (RadioButton) findViewById(R.id.radioButtonHex); | |||
editTextCOMA.setOnEditorActionListener(new EditorActionEvent()); | |||
editTextCOMB.setOnEditorActionListener(new EditorActionEvent()); | |||
editTextCOMC.setOnEditorActionListener(new EditorActionEvent()); | |||
editTextCOMD.setOnEditorActionListener(new EditorActionEvent()); | |||
editTextTimeCOMA.setOnEditorActionListener(new EditorActionEvent()); | |||
editTextTimeCOMB.setOnEditorActionListener(new EditorActionEvent()); | |||
editTextTimeCOMC.setOnEditorActionListener(new EditorActionEvent()); | |||
editTextTimeCOMD.setOnEditorActionListener(new EditorActionEvent()); | |||
editTextCOMA.setOnFocusChangeListener(new FocusChangeEvent()); | |||
editTextCOMB.setOnFocusChangeListener(new FocusChangeEvent()); | |||
editTextCOMC.setOnFocusChangeListener(new FocusChangeEvent()); | |||
editTextCOMD.setOnFocusChangeListener(new FocusChangeEvent()); | |||
editTextTimeCOMA.setOnFocusChangeListener(new FocusChangeEvent()); | |||
editTextTimeCOMB.setOnFocusChangeListener(new FocusChangeEvent()); | |||
editTextTimeCOMC.setOnFocusChangeListener(new FocusChangeEvent()); | |||
editTextTimeCOMD.setOnFocusChangeListener(new FocusChangeEvent()); | |||
radioButtonTxt.setOnClickListener(new radioButtonClickEvent()); | |||
radioButtonHex.setOnClickListener(new radioButtonClickEvent()); | |||
ButtonClear.setOnClickListener(new ButtonClickEvent()); | |||
ButtonSendCOMA.setOnClickListener(new ButtonClickEvent()); | |||
ButtonSendCOMB.setOnClickListener(new ButtonClickEvent()); | |||
ButtonSendCOMC.setOnClickListener(new ButtonClickEvent()); | |||
ButtonSendCOMD.setOnClickListener(new ButtonClickEvent()); | |||
toggleButtonCOMA.setOnCheckedChangeListener(new ToggleButtonCheckedChangeEvent()); | |||
toggleButtonCOMB.setOnCheckedChangeListener(new ToggleButtonCheckedChangeEvent()); | |||
toggleButtonCOMC.setOnCheckedChangeListener(new ToggleButtonCheckedChangeEvent()); | |||
toggleButtonCOMD.setOnCheckedChangeListener(new ToggleButtonCheckedChangeEvent()); | |||
checkBoxAutoCOMA.setOnCheckedChangeListener(new CheckBoxChangeEvent()); | |||
checkBoxAutoCOMB.setOnCheckedChangeListener(new CheckBoxChangeEvent()); | |||
checkBoxAutoCOMC.setOnCheckedChangeListener(new CheckBoxChangeEvent()); | |||
checkBoxAutoCOMD.setOnCheckedChangeListener(new CheckBoxChangeEvent()); | |||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, | |||
R.array.baudrates_value, android.R.layout.simple_spinner_item); | |||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); | |||
SpinnerBaudRateCOMA.setAdapter(adapter); | |||
SpinnerBaudRateCOMB.setAdapter(adapter); | |||
SpinnerBaudRateCOMC.setAdapter(adapter); | |||
SpinnerBaudRateCOMD.setAdapter(adapter); | |||
SpinnerBaudRateCOMA.setSelection(12); | |||
SpinnerBaudRateCOMB.setSelection(12); | |||
SpinnerBaudRateCOMC.setSelection(12); | |||
SpinnerBaudRateCOMD.setSelection(12); | |||
mSerialPortFinder = new SerialPortFinder(); | |||
String[] entryValues = mSerialPortFinder.getAllDevicesPath(); | |||
List<String> allDevices = new ArrayList<String>(); | |||
for (int i = 0; i < entryValues.length; i++) { | |||
allDevices.add(entryValues[i]); | |||
} | |||
allDevices.add("/dev/ttyCOM0"); | |||
ArrayAdapter<String> aspnDevices = new ArrayAdapter<String>(this, | |||
android.R.layout.simple_spinner_item, allDevices); | |||
aspnDevices.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); | |||
SpinnerCOMA.setAdapter(aspnDevices); | |||
SpinnerCOMB.setAdapter(aspnDevices); | |||
SpinnerCOMC.setAdapter(aspnDevices); | |||
SpinnerCOMD.setAdapter(aspnDevices); | |||
if (allDevices.size() > 0) { | |||
SpinnerCOMA.setSelection(0); | |||
} | |||
if (allDevices.size() > 1) { | |||
SpinnerCOMB.setSelection(1); | |||
} | |||
if (allDevices.size() > 2) { | |||
SpinnerCOMC.setSelection(2); | |||
} | |||
if (allDevices.size() > 3) { | |||
SpinnerCOMD.setSelection(3); | |||
} | |||
SpinnerCOMA.setOnItemSelectedListener(new ItemSelectedEvent()); | |||
SpinnerCOMB.setOnItemSelectedListener(new ItemSelectedEvent()); | |||
SpinnerCOMC.setOnItemSelectedListener(new ItemSelectedEvent()); | |||
SpinnerCOMD.setOnItemSelectedListener(new ItemSelectedEvent()); | |||
SpinnerBaudRateCOMA.setOnItemSelectedListener(new ItemSelectedEvent()); | |||
SpinnerBaudRateCOMB.setOnItemSelectedListener(new ItemSelectedEvent()); | |||
SpinnerBaudRateCOMC.setOnItemSelectedListener(new ItemSelectedEvent()); | |||
SpinnerBaudRateCOMD.setOnItemSelectedListener(new ItemSelectedEvent()); | |||
DispAssistData(AssistData); | |||
} | |||
//----------------------------------------------------串口号或波特率变化时,关闭打开的串口 | |||
class ItemSelectedEvent implements Spinner.OnItemSelectedListener { | |||
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { | |||
if ((arg0 == SpinnerCOMA) || (arg0 == SpinnerBaudRateCOMA)) { | |||
CloseComPort(ComA); | |||
checkBoxAutoCOMA.setChecked(false); | |||
toggleButtonCOMA.setChecked(false); | |||
} else if ((arg0 == SpinnerCOMB) || (arg0 == SpinnerBaudRateCOMB)) { | |||
CloseComPort(ComB); | |||
checkBoxAutoCOMA.setChecked(false); | |||
toggleButtonCOMB.setChecked(false); | |||
} else if ((arg0 == SpinnerCOMC) || (arg0 == SpinnerBaudRateCOMC)) { | |||
CloseComPort(ComC); | |||
checkBoxAutoCOMA.setChecked(false); | |||
toggleButtonCOMC.setChecked(false); | |||
} else if ((arg0 == SpinnerCOMD) || (arg0 == SpinnerBaudRateCOMD)) { | |||
CloseComPort(ComD); | |||
checkBoxAutoCOMA.setChecked(false); | |||
toggleButtonCOMD.setChecked(false); | |||
} | |||
} | |||
public void onNothingSelected(AdapterView<?> arg0) { | |||
} | |||
} | |||
//----------------------------------------------------编辑框焦点转移事件 | |||
class FocusChangeEvent implements EditText.OnFocusChangeListener { | |||
public void onFocusChange(View v, boolean hasFocus) { | |||
if (v == editTextCOMA) { | |||
setSendData(editTextCOMA); | |||
} else if (v == editTextCOMB) { | |||
setSendData(editTextCOMB); | |||
} else if (v == editTextCOMC) { | |||
setSendData(editTextCOMC); | |||
} else if (v == editTextCOMD) { | |||
setSendData(editTextCOMD); | |||
} else if (v == editTextTimeCOMA) { | |||
setDelayTime(editTextTimeCOMA); | |||
} else if (v == editTextTimeCOMB) { | |||
setDelayTime(editTextTimeCOMB); | |||
} else if (v == editTextTimeCOMC) { | |||
setDelayTime(editTextTimeCOMC); | |||
} else if (v == editTextTimeCOMD) { | |||
setDelayTime(editTextTimeCOMD); | |||
} | |||
} | |||
} | |||
//----------------------------------------------------编辑框完成事件 | |||
class EditorActionEvent implements EditText.OnEditorActionListener { | |||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { | |||
if (v == editTextCOMA) { | |||
setSendData(editTextCOMA); | |||
} else if (v == editTextCOMB) { | |||
setSendData(editTextCOMB); | |||
} else if (v == editTextCOMC) { | |||
setSendData(editTextCOMC); | |||
} else if (v == editTextCOMD) { | |||
setSendData(editTextCOMD); | |||
} else if (v == editTextTimeCOMA) { | |||
setDelayTime(editTextTimeCOMA); | |||
} else if (v == editTextTimeCOMB) { | |||
setDelayTime(editTextTimeCOMB); | |||
} else if (v == editTextTimeCOMC) { | |||
setDelayTime(editTextTimeCOMC); | |||
} else if (v == editTextTimeCOMD) { | |||
setDelayTime(editTextTimeCOMD); | |||
} | |||
return false; | |||
} | |||
} | |||
//----------------------------------------------------Txt、Hex模式选择 | |||
class radioButtonClickEvent implements RadioButton.OnClickListener { | |||
public void onClick(View v) { | |||
if (v == radioButtonTxt) { | |||
KeyListener TxtkeyListener = new TextKeyListener(Capitalize.NONE, false); | |||
editTextCOMA.setKeyListener(TxtkeyListener); | |||
editTextCOMB.setKeyListener(TxtkeyListener); | |||
editTextCOMC.setKeyListener(TxtkeyListener); | |||
editTextCOMD.setKeyListener(TxtkeyListener); | |||
AssistData.setTxtMode(true); | |||
} else if (v == radioButtonHex) { | |||
KeyListener HexkeyListener = new NumberKeyListener() { | |||
public int getInputType() { | |||
return InputType.TYPE_CLASS_TEXT; | |||
} | |||
@Override | |||
protected char[] getAcceptedChars() { | |||
return new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', | |||
'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F'}; | |||
} | |||
}; | |||
editTextCOMA.setKeyListener(HexkeyListener); | |||
editTextCOMB.setKeyListener(HexkeyListener); | |||
editTextCOMC.setKeyListener(HexkeyListener); | |||
editTextCOMD.setKeyListener(HexkeyListener); | |||
AssistData.setTxtMode(false); | |||
} | |||
editTextCOMA.setText(AssistData.getSendA()); | |||
editTextCOMB.setText(AssistData.getSendB()); | |||
editTextCOMC.setText(AssistData.getSendC()); | |||
editTextCOMD.setText(AssistData.getSendD()); | |||
setSendData(editTextCOMA); | |||
setSendData(editTextCOMB); | |||
setSendData(editTextCOMC); | |||
setSendData(editTextCOMD); | |||
} | |||
} | |||
//----------------------------------------------------自动发送 | |||
class CheckBoxChangeEvent implements CheckBox.OnCheckedChangeListener { | |||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { | |||
if (buttonView == checkBoxAutoCOMA) { | |||
if (!toggleButtonCOMA.isChecked() && isChecked) { | |||
buttonView.setChecked(false); | |||
return; | |||
} | |||
SetLoopData(ComA, editTextCOMA.getText().toString()); | |||
SetAutoSend(ComA, isChecked); | |||
} else if (buttonView == checkBoxAutoCOMB) { | |||
if (!toggleButtonCOMB.isChecked() && isChecked) { | |||
buttonView.setChecked(false); | |||
return; | |||
} | |||
SetLoopData(ComB, editTextCOMB.getText().toString()); | |||
SetAutoSend(ComB, isChecked); | |||
} else if (buttonView == checkBoxAutoCOMC) { | |||
if (!toggleButtonCOMC.isChecked() && isChecked) { | |||
buttonView.setChecked(false); | |||
return; | |||
} | |||
SetLoopData(ComC, editTextCOMC.getText().toString()); | |||
SetAutoSend(ComC, isChecked); | |||
} else if (buttonView == checkBoxAutoCOMD) { | |||
if (!toggleButtonCOMD.isChecked() && isChecked) { | |||
buttonView.setChecked(false); | |||
return; | |||
} | |||
SetLoopData(ComD, editTextCOMD.getText().toString()); | |||
SetAutoSend(ComD, isChecked); | |||
} | |||
} | |||
} | |||
//----------------------------------------------------清除按钮、发送按钮 | |||
class ButtonClickEvent implements View.OnClickListener { | |||
public void onClick(View v) { | |||
if (v == ButtonClear) { | |||
editTextRecDisp.setText(""); | |||
} else if (v == ButtonSendCOMA) { | |||
sendPortData(ComA, editTextCOMA.getText().toString()); | |||
} else if (v == ButtonSendCOMB) { | |||
sendPortData(ComB, editTextCOMB.getText().toString()); | |||
} else if (v == ButtonSendCOMC) { | |||
sendPortData(ComC, editTextCOMC.getText().toString()); | |||
} else if (v == ButtonSendCOMD) { | |||
sendPortData(ComD, editTextCOMD.getText().toString()); | |||
} | |||
} | |||
} | |||
//----------------------------------------------------打开关闭串口 | |||
class ToggleButtonCheckedChangeEvent implements ToggleButton.OnCheckedChangeListener { | |||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { | |||
if (buttonView == toggleButtonCOMA) { | |||
if (isChecked) { | |||
if (toggleButtonCOMB.isChecked() && SpinnerCOMA.getSelectedItemPosition() == SpinnerCOMB.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMA.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else if (toggleButtonCOMC.isChecked() && SpinnerCOMA.getSelectedItemPosition() == SpinnerCOMC.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMA.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else if (toggleButtonCOMD.isChecked() && SpinnerCOMA.getSelectedItemPosition() == SpinnerCOMD.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMA.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else { | |||
// ComA=new SerialControl("/dev/s3c2410_serial0", "9600"); | |||
ComA.setPort(SpinnerCOMA.getSelectedItem().toString()); | |||
LogUtils.d("COMA = " + SpinnerCOMA.getSelectedItem().toString()); | |||
ComA.setBaudRate(SpinnerBaudRateCOMA.getSelectedItem().toString()); | |||
LogUtils.d("COMA = " + SpinnerBaudRateCOMA.getSelectedItem().toString()); | |||
OpenComPort(ComA); | |||
} | |||
} else { | |||
CloseComPort(ComA); | |||
checkBoxAutoCOMA.setChecked(false); | |||
} | |||
} else if (buttonView == toggleButtonCOMB) { | |||
if (isChecked) { | |||
if (toggleButtonCOMA.isChecked() && SpinnerCOMB.getSelectedItemPosition() == SpinnerCOMA.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMB.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else if (toggleButtonCOMC.isChecked() && SpinnerCOMB.getSelectedItemPosition() == SpinnerCOMC.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMB.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else if (toggleButtonCOMD.isChecked() && SpinnerCOMB.getSelectedItemPosition() == SpinnerCOMD.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMB.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else { | |||
// ComB=new SerialControl("/dev/s3c2410_serial1", "9600"); | |||
ComB.setPort(SpinnerCOMB.getSelectedItem().toString()); | |||
ComB.setBaudRate(SpinnerBaudRateCOMB.getSelectedItem().toString()); | |||
OpenComPort(ComB); | |||
} | |||
} else { | |||
CloseComPort(ComB); | |||
checkBoxAutoCOMB.setChecked(false); | |||
} | |||
} else if (buttonView == toggleButtonCOMC) { | |||
if (isChecked) { | |||
if (toggleButtonCOMA.isChecked() && SpinnerCOMC.getSelectedItemPosition() == SpinnerCOMA.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMC.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else if (toggleButtonCOMB.isChecked() && SpinnerCOMC.getSelectedItemPosition() == SpinnerCOMB.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMC.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else if (toggleButtonCOMD.isChecked() && SpinnerCOMC.getSelectedItemPosition() == SpinnerCOMD.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMC.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else { | |||
// ComC=new SerialControl("/dev/s3c2410_serial2", "9600"); | |||
ComC.setPort(SpinnerCOMC.getSelectedItem().toString()); | |||
ComC.setBaudRate(SpinnerBaudRateCOMC.getSelectedItem().toString()); | |||
OpenComPort(ComC); | |||
} | |||
} else { | |||
CloseComPort(ComC); | |||
checkBoxAutoCOMC.setChecked(false); | |||
} | |||
} else if (buttonView == toggleButtonCOMD) { | |||
if (isChecked) { | |||
if (toggleButtonCOMA.isChecked() && SpinnerCOMD.getSelectedItemPosition() == SpinnerCOMA.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMD.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else if (toggleButtonCOMB.isChecked() && SpinnerCOMD.getSelectedItemPosition() == SpinnerCOMB.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMD.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else if (toggleButtonCOMC.isChecked() && SpinnerCOMD.getSelectedItemPosition() == SpinnerCOMC.getSelectedItemPosition()) { | |||
ShowMessage("串口" + SpinnerCOMD.getSelectedItem().toString() + "已打开"); | |||
buttonView.setChecked(false); | |||
} else { | |||
// ComD=new SerialControl("/dev/s3c2410_serial3", "9600"); | |||
ComD.setPort(SpinnerCOMD.getSelectedItem().toString()); | |||
ComD.setBaudRate(SpinnerBaudRateCOMD.getSelectedItem().toString()); | |||
OpenComPort(ComD); | |||
} | |||
} else { | |||
CloseComPort(ComD); | |||
checkBoxAutoCOMD.setChecked(false); | |||
} | |||
} | |||
} | |||
} | |||
//----------------------------------------------------串口控制类 | |||
private class SerialControl extends SerialHelper { | |||
// public SerialControl(String sPort, String sBaudRate){ | |||
// super(sPort, sBaudRate); | |||
// } | |||
public SerialControl() { | |||
} | |||
@Override | |||
protected void onDataReceived(final ComBean ComRecData) { | |||
//数据接收量大或接收时弹出软键盘,界面会卡顿,可能和6410的显示性能有关 | |||
//直接刷新显示,接收数据量大时,卡顿明显,但接收与显示同步。 | |||
//用线程定时刷新显示可以获得较流畅的显示效果,但是接收数据速度快于显示速度时,显示会滞后。 | |||
//最终效果差不多-_-,线程定时刷新稍好一些。 | |||
DispQueue.AddQueue(ComRecData);//线程定时刷新显示(推荐) | |||
/* | |||
runOnUiThread(new Runnable()//直接刷新显示 | |||
{ | |||
public void run() | |||
{ | |||
DispRecData(ComRecData); | |||
} | |||
});*/ | |||
} | |||
} | |||
//----------------------------------------------------刷新显示线程 | |||
private class DispQueueThread extends Thread { | |||
private Queue<ComBean> QueueList = new LinkedList<ComBean>(); | |||
@Override | |||
public void run() { | |||
super.run(); | |||
while (!isInterrupted()) { | |||
final ComBean ComData; | |||
while ((ComData = QueueList.poll()) != null) { | |||
runOnUiThread(new Runnable() { | |||
public void run() { | |||
DispRecData(ComData); | |||
} | |||
}); | |||
try { | |||
Thread.sleep(100);//显示性能高的话,可以把此数值调小。 | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
public synchronized void AddQueue(ComBean ComData) { | |||
QueueList.add(ComData); | |||
} | |||
} | |||
//----------------------------------------------------刷新界面数据 | |||
private void DispAssistData(AssistBean AssistData) { | |||
editTextCOMA.setText(AssistData.getSendA()); | |||
editTextCOMB.setText(AssistData.getSendB()); | |||
editTextCOMC.setText(AssistData.getSendC()); | |||
editTextCOMD.setText(AssistData.getSendD()); | |||
setSendData(editTextCOMA); | |||
setSendData(editTextCOMB); | |||
setSendData(editTextCOMC); | |||
setSendData(editTextCOMD); | |||
if (AssistData.isTxt()) { | |||
radioButtonTxt.setChecked(true); | |||
} else { | |||
radioButtonHex.setChecked(true); | |||
} | |||
editTextTimeCOMA.setText(AssistData.sTimeA); | |||
editTextTimeCOMB.setText(AssistData.sTimeB); | |||
editTextTimeCOMC.setText(AssistData.sTimeC); | |||
editTextTimeCOMD.setText(AssistData.sTimeD); | |||
setDelayTime(editTextTimeCOMA); | |||
setDelayTime(editTextTimeCOMB); | |||
setDelayTime(editTextTimeCOMC); | |||
setDelayTime(editTextTimeCOMD); | |||
} | |||
//----------------------------------------------------保存、获取界面数据 | |||
private void saveAssistData(AssistBean AssistData) { | |||
AssistData.sTimeA = editTextTimeCOMA.getText().toString(); | |||
AssistData.sTimeB = editTextTimeCOMB.getText().toString(); | |||
AssistData.sTimeC = editTextTimeCOMC.getText().toString(); | |||
AssistData.sTimeD = editTextTimeCOMD.getText().toString(); | |||
SharedPreferences msharedPreferences = getSharedPreferences("ComAssistant", Context.MODE_PRIVATE); | |||
try { | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
ObjectOutputStream oos = new ObjectOutputStream(baos); | |||
oos.writeObject(AssistData); | |||
String sBase64 = new String(Base64.encode(baos.toByteArray(), 0)); | |||
SharedPreferences.Editor editor = msharedPreferences.edit(); | |||
editor.putString("AssistData", sBase64); | |||
editor.commit(); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
//---------------------------------------------------- | |||
private AssistBean getAssistData() { | |||
SharedPreferences msharedPreferences = getSharedPreferences("ComAssistant", Context.MODE_PRIVATE); | |||
AssistBean AssistData = new AssistBean(); | |||
try { | |||
String personBase64 = msharedPreferences.getString("AssistData", ""); | |||
byte[] base64Bytes = Base64.decode(personBase64.getBytes(), 0); | |||
ByteArrayInputStream bais = new ByteArrayInputStream(base64Bytes); | |||
ObjectInputStream ois = new ObjectInputStream(bais); | |||
AssistData = (AssistBean) ois.readObject(); | |||
return AssistData; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
return AssistData; | |||
} | |||
//----------------------------------------------------设置自动发送延时 | |||
private void setDelayTime(TextView v) { | |||
if (v == editTextTimeCOMA) { | |||
AssistData.sTimeA = v.getText().toString(); | |||
SetiDelayTime(ComA, v.getText().toString()); | |||
} else if (v == editTextTimeCOMB) { | |||
AssistData.sTimeB = v.getText().toString(); | |||
SetiDelayTime(ComB, v.getText().toString()); | |||
} else if (v == editTextTimeCOMC) { | |||
AssistData.sTimeC = v.getText().toString(); | |||
SetiDelayTime(ComC, v.getText().toString()); | |||
} else if (v == editTextTimeCOMD) { | |||
AssistData.sTimeD = v.getText().toString(); | |||
SetiDelayTime(ComD, v.getText().toString()); | |||
} | |||
} | |||
//----------------------------------------------------设置自动发送数据 | |||
private void setSendData(TextView v) { | |||
if (v == editTextCOMA) { | |||
AssistData.setSendA(v.getText().toString()); | |||
SetLoopData(ComA, v.getText().toString()); | |||
} else if (v == editTextCOMB) { | |||
AssistData.setSendB(v.getText().toString()); | |||
SetLoopData(ComB, v.getText().toString()); | |||
} else if (v == editTextCOMC) { | |||
AssistData.setSendC(v.getText().toString()); | |||
SetLoopData(ComC, v.getText().toString()); | |||
} else if (v == editTextCOMD) { | |||
AssistData.setSendD(v.getText().toString()); | |||
SetLoopData(ComD, v.getText().toString()); | |||
} | |||
} | |||
//----------------------------------------------------设置自动发送延时 | |||
private void SetiDelayTime(SerialHelper ComPort, String sTime) { | |||
ComPort.setiDelay(Integer.parseInt(sTime)); | |||
} | |||
//----------------------------------------------------设置自动发送数据 | |||
private void SetLoopData(SerialHelper ComPort, String sLoopData) { | |||
if (radioButtonTxt.isChecked()) { | |||
ComPort.setTxtLoopData(sLoopData); | |||
} else if (radioButtonHex.isChecked()) { | |||
ComPort.setHexLoopData(sLoopData); | |||
} | |||
} | |||
//----------------------------------------------------显示接收数据 | |||
private void DispRecData(ComBean ComRecData) { | |||
StringBuilder sMsg = new StringBuilder(); | |||
sMsg.append(ComRecData.sRecTime); | |||
sMsg.append("["); | |||
sMsg.append(ComRecData.sComPort); | |||
sMsg.append("]"); | |||
if (radioButtonTxt.isChecked()) { | |||
sMsg.append("[Txt] "); | |||
sMsg.append(new String(ComRecData.bRec)); | |||
} else if (radioButtonHex.isChecked()) { | |||
sMsg.append("[Hex] "); | |||
sMsg.append(MyFunc.ByteArrToHex(ComRecData.bRec)); | |||
} | |||
sMsg.append("\r\n"); | |||
editTextRecDisp.append(sMsg); | |||
LogUtils.d("sMsg = " + sMsg); | |||
iRecLines++; | |||
editTextLines.setText(String.valueOf(iRecLines)); | |||
if ((iRecLines > 500) && (checkBoxAutoClear.isChecked()))//达到500项自动清除 | |||
{ | |||
editTextRecDisp.setText(""); | |||
editTextLines.setText("0"); | |||
iRecLines = 0; | |||
} | |||
} | |||
//----------------------------------------------------设置自动发送模式开关 | |||
private void SetAutoSend(SerialHelper ComPort, boolean isAutoSend) { | |||
if (isAutoSend) { | |||
ComPort.startSend(); | |||
} else { | |||
ComPort.stopSend(); | |||
} | |||
} | |||
//----------------------------------------------------串口发送 | |||
private void sendPortData(SerialHelper ComPort, String sOut) { | |||
if (ComPort != null && ComPort.isOpen()) { | |||
if (radioButtonTxt.isChecked()) { | |||
ComPort.sendTxt(sOut); | |||
} else if (radioButtonHex.isChecked()) { | |||
ComPort.sendHex(sOut); | |||
} | |||
} | |||
} | |||
//----------------------------------------------------关闭串口 | |||
private void CloseComPort(SerialHelper ComPort) { | |||
if (ComPort != null) { | |||
ComPort.stopSend(); | |||
ComPort.close(); | |||
} | |||
} | |||
//----------------------------------------------------打开串口 | |||
private void OpenComPort(SerialHelper ComPort) { | |||
try { | |||
ComPort.open(); | |||
} catch (SecurityException e) { | |||
ShowMessage("打开串口失败:没有串口读/写权限!"); | |||
} catch (IOException e) { | |||
ShowMessage("打开串口失败:未知错误!"); | |||
} catch (InvalidParameterException e) { | |||
ShowMessage("打开串口失败:参数错误!"); | |||
} | |||
} | |||
//------------------------------------------显示消息 | |||
private void ShowMessage(String sMsg) { | |||
Toast.makeText(this, sMsg, Toast.LENGTH_SHORT).show(); | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
package com.bonait.bnframework.serial; | |||
import java.text.SimpleDateFormat; | |||
/** | |||
* @author benjaminwan | |||
*串口数据 | |||
*/ | |||
public class ComBean { | |||
public byte[] bRec=null; | |||
public String sRecTime=""; | |||
public String sComPort=""; | |||
public ComBean(String sPort, byte[] buffer, int size){ | |||
sComPort=sPort; | |||
bRec=new byte[size]; | |||
for (int i = 0; i < size; i++) | |||
{ | |||
bRec[i]=buffer[i]; | |||
} | |||
SimpleDateFormat sDateFormat = new SimpleDateFormat("hh:mm:ss"); | |||
sRecTime = sDateFormat.format(new java.util.Date()); | |||
} | |||
} |
@@ -0,0 +1,83 @@ | |||
package com.bonait.bnframework.serial; | |||
import com.apkfuns.logutils.LogUtils; | |||
/** | |||
* @author benjaminwan | |||
*数据转换工具 | |||
*/ | |||
public class MyFunc { | |||
//------------------------------------------------------- | |||
// 判断奇数或偶数,位运算,最后一位是1则为奇数,为0是偶数 | |||
static public int isOdd(int num) | |||
{ | |||
return num & 0x1; | |||
} | |||
//------------------------------------------------------- | |||
static public int HexToInt(String inHex)//Hex字符串转int | |||
{ | |||
return Integer.parseInt(inHex, 16); | |||
} | |||
//------------------------------------------------------- | |||
static public byte HexToByte(String inHex)//Hex字符串转byte | |||
{ | |||
return (byte)Integer.parseInt(inHex,16); | |||
} | |||
//------------------------------------------------------- | |||
static public String Byte2Hex(Byte inByte)//1字节转2个Hex字符 | |||
{ | |||
return String.format("%02x", inByte).toUpperCase(); | |||
} | |||
//------------------------------------------------------- | |||
static public String ByteArrToHex(byte[] inBytArr)//字节数组转转hex字符串 | |||
{ | |||
StringBuilder strBuilder=new StringBuilder(); | |||
int j=inBytArr.length; | |||
for (int i = 0; i < j; i++) | |||
{ | |||
strBuilder.append(Byte2Hex(inBytArr[i])); | |||
strBuilder.append(""); | |||
} | |||
String result = strBuilder.toString(); | |||
for (int i = 0; i < 10; i++) { | |||
result = result.replace("3"+i,i+""); | |||
} | |||
LogUtils.d("srt = "+strBuilder.toString()); | |||
LogUtils.d("result = "+result); | |||
return result.replace("0D0A",""); | |||
} | |||
//------------------------------------------------------- | |||
static public String ByteArrToHex(byte[] inBytArr,int offset,int byteCount)//字节数组转转hex字符串,可选长度 | |||
{ | |||
StringBuilder strBuilder=new StringBuilder(); | |||
int j=byteCount; | |||
for (int i = offset; i < j; i++) | |||
{ | |||
strBuilder.append(Byte2Hex(inBytArr[i])); | |||
} | |||
return strBuilder.toString(); | |||
} | |||
//------------------------------------------------------- | |||
//转hex字符串转字节数组 | |||
static public byte[] HexToByteArr(String inHex)//hex字符串转字节数组 | |||
{ | |||
int hexlen = inHex.length(); | |||
byte[] result; | |||
if (isOdd(hexlen)==1) | |||
{//奇数 | |||
hexlen++; | |||
result = new byte[(hexlen/2)]; | |||
inHex="0"+inHex; | |||
}else {//偶数 | |||
result = new byte[(hexlen/2)]; | |||
} | |||
int j=0; | |||
for (int i = 0; i < hexlen; i+=2) | |||
{ | |||
result[j]=HexToByte(inHex.substring(i,i+2)); | |||
j++; | |||
} | |||
return result; | |||
} | |||
} |
@@ -0,0 +1,243 @@ | |||
package com.bonait.bnframework.serial; | |||
import com.apkfuns.logutils.LogUtils; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.security.InvalidParameterException; | |||
/** | |||
* @author benjaminwan | |||
*串口辅助工具类 | |||
*/ | |||
public abstract class SerialHelper { | |||
private SerialPort mSerialPort; | |||
private OutputStream mOutputStream; | |||
private InputStream mInputStream; | |||
private ReadThread mReadThread; | |||
private SendThread mSendThread; | |||
private String sPort="/dev/s3c2410_serial0"; | |||
private int iBaudRate=9600; | |||
private boolean _isOpen=false; | |||
private byte[] _bLoopData=new byte[]{0x30}; | |||
private int iDelay=500; | |||
//---------------------------------------------------- | |||
public SerialHelper(String sPort, int iBaudRate){ | |||
this.sPort = sPort; | |||
this.iBaudRate=iBaudRate; | |||
} | |||
public SerialHelper(){ | |||
this("/dev/ttyCOM4",9600); | |||
} | |||
public SerialHelper(String sPort){ | |||
this(sPort,9600); | |||
} | |||
public SerialHelper(String sPort, String sBaudRate){ | |||
this(sPort,Integer.parseInt(sBaudRate)); | |||
} | |||
//---------------------------------------------------- | |||
public void open() throws SecurityException, IOException,InvalidParameterException{ | |||
mSerialPort = new SerialPort(new File(sPort), iBaudRate, 0); | |||
mOutputStream = mSerialPort.getOutputStream(); | |||
mInputStream = mSerialPort.getInputStream(); | |||
mReadThread = new ReadThread(); | |||
mReadThread.start(); | |||
mSendThread = new SendThread(); | |||
mSendThread.setSuspendFlag(); | |||
mSendThread.start(); | |||
_isOpen=true; | |||
} | |||
//---------------------------------------------------- | |||
public void close(){ | |||
if (mReadThread != null) | |||
mReadThread.interrupt(); | |||
if (mSerialPort != null) { | |||
mSerialPort.close(); | |||
mSerialPort = null; | |||
} | |||
_isOpen=false; | |||
} | |||
//---------------------------------------------------- | |||
public void send(byte[] bOutArray){ | |||
try | |||
{ | |||
mOutputStream.write(bOutArray); | |||
} catch (IOException e) | |||
{ | |||
e.printStackTrace(); | |||
} | |||
} | |||
//---------------------------------------------------- | |||
public void sendHex(String sHex){ | |||
byte[] bOutArray = MyFunc.HexToByteArr(sHex); | |||
send(bOutArray); | |||
} | |||
//---------------------------------------------------- | |||
public void sendTxt(String sTxt){ | |||
byte[] bOutArray =sTxt.getBytes(); | |||
send(bOutArray); | |||
LogUtils.d("sTXT= "+sTxt); | |||
} | |||
//---------------------------------------------------- | |||
private class ReadThread extends Thread { | |||
@Override | |||
public void run() { | |||
super.run(); | |||
while(!isInterrupted()) { | |||
try | |||
{ | |||
if (mInputStream == null) return; | |||
byte[] buffer=new byte[512]; | |||
int size = mInputStream.read(buffer); | |||
if (size > 0){ | |||
ComBean ComRecData = new ComBean(sPort,buffer,size); | |||
onDataReceived(ComRecData); | |||
} | |||
try | |||
{ | |||
Thread.sleep(50);//延时50ms | |||
} catch (InterruptedException e) | |||
{ | |||
e.printStackTrace(); | |||
} | |||
} catch (Throwable e) | |||
{ | |||
e.printStackTrace(); | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
//---------------------------------------------------- | |||
private class SendThread extends Thread{ | |||
public boolean suspendFlag = true;// 控制线程的执行 | |||
@Override | |||
public void run() { | |||
super.run(); | |||
while(!isInterrupted()) { | |||
synchronized (this) | |||
{ | |||
while (suspendFlag) | |||
{ | |||
try | |||
{ | |||
wait(); | |||
} catch (InterruptedException e) | |||
{ | |||
e.printStackTrace(); | |||
} | |||
} | |||
} | |||
send(getbLoopData()); | |||
try | |||
{ | |||
Thread.sleep(iDelay); | |||
} catch (InterruptedException e) | |||
{ | |||
e.printStackTrace(); | |||
} | |||
} | |||
} | |||
//线程暂停 | |||
public void setSuspendFlag() { | |||
this.suspendFlag = true; | |||
} | |||
//唤醒线程 | |||
public synchronized void setResume() { | |||
this.suspendFlag = false; | |||
notify(); | |||
} | |||
} | |||
//---------------------------------------------------- | |||
public int getBaudRate() | |||
{ | |||
return iBaudRate; | |||
} | |||
public boolean setBaudRate(int iBaud) | |||
{ | |||
if (_isOpen) | |||
{ | |||
return false; | |||
} else | |||
{ | |||
iBaudRate = iBaud; | |||
return true; | |||
} | |||
} | |||
public boolean setBaudRate(String sBaud) | |||
{ | |||
int iBaud = Integer.parseInt(sBaud); | |||
return setBaudRate(iBaud); | |||
} | |||
//---------------------------------------------------- | |||
public String getPort() | |||
{ | |||
return sPort; | |||
} | |||
public boolean setPort(String sPort) | |||
{ | |||
if (_isOpen) | |||
{ | |||
return false; | |||
} else | |||
{ | |||
this.sPort = sPort; | |||
return true; | |||
} | |||
} | |||
//---------------------------------------------------- | |||
public boolean isOpen() | |||
{ | |||
return _isOpen; | |||
} | |||
//---------------------------------------------------- | |||
public byte[] getbLoopData() | |||
{ | |||
return _bLoopData; | |||
} | |||
//---------------------------------------------------- | |||
public void setbLoopData(byte[] bLoopData) | |||
{ | |||
this._bLoopData = bLoopData; | |||
} | |||
//---------------------------------------------------- | |||
public void setTxtLoopData(String sTxt){ | |||
this._bLoopData = sTxt.getBytes(); | |||
} | |||
//---------------------------------------------------- | |||
public void setHexLoopData(String sHex){ | |||
this._bLoopData = MyFunc.HexToByteArr(sHex); | |||
} | |||
//---------------------------------------------------- | |||
public int getiDelay() | |||
{ | |||
return iDelay; | |||
} | |||
//---------------------------------------------------- | |||
public void setiDelay(int iDelay) | |||
{ | |||
this.iDelay = iDelay; | |||
} | |||
//---------------------------------------------------- | |||
public void startSend() | |||
{ | |||
if (mSendThread != null) | |||
{ | |||
mSendThread.setResume(); | |||
} | |||
} | |||
//---------------------------------------------------- | |||
public void stopSend() | |||
{ | |||
if (mSendThread != null) | |||
{ | |||
mSendThread.setSuspendFlag(); | |||
} | |||
} | |||
//---------------------------------------------------- | |||
protected abstract void onDataReceived(ComBean ComRecData); | |||
} |
@@ -0,0 +1,86 @@ | |||
/* | |||
* Copyright 2009 Cedric Priscal | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.bonait.bnframework.serial; | |||
import com.apkfuns.logutils.LogUtils; | |||
import java.io.File; | |||
import java.io.FileDescriptor; | |||
import java.io.FileInputStream; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
public class SerialPort { | |||
private static final String TAG = "SerialPort"; | |||
/* | |||
* Do not remove or rename the field mFd: it is used by native method close(); | |||
*/ | |||
private FileDescriptor mFd; | |||
private FileInputStream mFileInputStream; | |||
private FileOutputStream mFileOutputStream; | |||
public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { | |||
/* Check access permission */ | |||
if (!device.canRead() || !device.canWrite()) { | |||
try { | |||
/* Missing read/write permission, trying to chmod the file */ | |||
Process su; | |||
su = Runtime.getRuntime().exec("/system/xbin/su"); | |||
String cmd = "chmod 777 " + device.getAbsolutePath() + "\n" | |||
+ "exit\n"; | |||
su.getOutputStream().write(cmd.getBytes()); | |||
if ((su.waitFor() != 0) || !device.canRead() | |||
|| !device.canWrite()) { | |||
throw new SecurityException(); | |||
} | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
throw new SecurityException(); | |||
} | |||
} | |||
mFd = open(device.getAbsolutePath(), baudrate, flags); | |||
if (mFd == null) { | |||
LogUtils.d(TAG+ "native open returns null"); | |||
throw new IOException(); | |||
} | |||
mFileInputStream = new FileInputStream(mFd); | |||
mFileOutputStream = new FileOutputStream(mFd); | |||
} | |||
// Getters and setters | |||
public InputStream getInputStream() { | |||
return mFileInputStream; | |||
} | |||
public OutputStream getOutputStream() { | |||
return mFileOutputStream; | |||
} | |||
// JNI | |||
private native static FileDescriptor open(String path, int baudrate, int flags); | |||
public native void close(); | |||
static { | |||
System.loadLibrary("serial_port"); | |||
} | |||
} |
@@ -0,0 +1,124 @@ | |||
/* | |||
* Copyright 2009 Cedric Priscal | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.bonait.bnframework.serial; | |||
import android.util.Log; | |||
import com.apkfuns.logutils.LogUtils; | |||
import java.io.File; | |||
import java.io.FileReader; | |||
import java.io.IOException; | |||
import java.io.LineNumberReader; | |||
import java.util.Iterator; | |||
import java.util.Vector; | |||
public class SerialPortFinder { | |||
public class Driver { | |||
public Driver(String name, String root) { | |||
mDriverName = name; | |||
mDeviceRoot = root; | |||
} | |||
private String mDriverName; | |||
private String mDeviceRoot; | |||
Vector<File> mDevices = null; | |||
public Vector<File> getDevices() { | |||
if (mDevices == null) { | |||
mDevices = new Vector<File>(); | |||
File dev = new File("/dev"); | |||
File[] files = dev.listFiles(); | |||
int i; | |||
for (i=0; i<files.length; i++) { | |||
if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { | |||
Log.d(TAG, "Found new device: " + files[i]); | |||
mDevices.add(files[i]); | |||
} | |||
} | |||
} | |||
return mDevices; | |||
} | |||
public String getName() { | |||
return mDriverName; | |||
} | |||
} | |||
private static final String TAG = "SerialPort"; | |||
private Vector<Driver> mDrivers = null; | |||
Vector<Driver> getDrivers() throws IOException { | |||
if (mDrivers == null) { | |||
mDrivers = new Vector<Driver>(); | |||
LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); | |||
String l; | |||
while((l = r.readLine()) != null) { | |||
// Issue 3: | |||
// Since driver name may contain spaces, we do not extract driver name with split() | |||
String drivername = l.substring(0, 0x15).trim(); | |||
String[] w = l.split(" +"); | |||
if ((w.length >= 5) && (w[w.length-1].equals("serial"))) { | |||
LogUtils.d(TAG+ "Found new driver " + drivername + " on " + w[w.length-4]); | |||
mDrivers.add(new Driver(drivername, w[w.length-4])); | |||
} | |||
} | |||
r.close(); | |||
} | |||
return mDrivers; | |||
} | |||
public String[] getAllDevices() { | |||
Vector<String> devices = new Vector<String>(); | |||
// Parse each driver | |||
Iterator<Driver> itdriv; | |||
try { | |||
itdriv = getDrivers().iterator(); | |||
while(itdriv.hasNext()) { | |||
Driver driver = itdriv.next(); | |||
Iterator<File> itdev = driver.getDevices().iterator(); | |||
while(itdev.hasNext()) { | |||
String device = itdev.next().getName(); | |||
String value = String.format("%s (%s)", device, driver.getName()); | |||
devices.add(value); | |||
} | |||
} | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
return devices.toArray(new String[devices.size()]); | |||
} | |||
public String[] getAllDevicesPath() { | |||
Vector<String> devices = new Vector<String>(); | |||
// Parse each driver | |||
Iterator<Driver> itdriv; | |||
try { | |||
itdriv = getDrivers().iterator(); | |||
while(itdriv.hasNext()) { | |||
Driver driver = itdriv.next(); | |||
Iterator<File> itdev = driver.getDevices().iterator(); | |||
while(itdev.hasNext()) { | |||
String device = itdev.next().getAbsolutePath(); | |||
devices.add(device); | |||
} | |||
} | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
return devices.toArray(new String[devices.size()]); | |||
} | |||
} |
@@ -10,6 +10,8 @@ import androidx.lifecycle.ViewModel; | |||
import com.alibaba.fastjson.TypeReference; | |||
import com.apkfuns.logutils.LogUtils; | |||
import com.bonait.bnframework.api.IHttpCallBack; | |||
import com.bonait.bnframework.api.ServerManager; | |||
import com.bonait.bnframework.business.RecordManager; | |||
import com.bonait.bnframework.common.API.APIHelper; | |||
import com.bonait.bnframework.common.API.APIResultT; | |||
@@ -0,0 +1,7 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<shape xmlns:android="http://schemas.android.com/apk/res/android"> | |||
<solid android:color="#666" /> | |||
<size | |||
android:width="0.1dp" | |||
android:height="0.1dp" /> | |||
</shape> |
@@ -0,0 +1,14 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> | |||
<!--<item android:state_focused="true"> | |||
<color android:color="#600f" /> | |||
</item>--> | |||
<!--<item android:state_selected="true"> | |||
<color android:color="#6666" /> | |||
</item>--> | |||
<item android:state_pressed="true"> | |||
<color android:color="@color/colorAccent" /> | |||
</item> | |||
</selector> |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<shape xmlns:android="http://schemas.android.com/apk/res/android"> | |||
<stroke | |||
android:width="1dp" | |||
android:color="@color/colorPrimaryDark" /> | |||
</shape> |
@@ -0,0 +1,122 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<androidx.constraintlayout.widget.ConstraintLayout | |||
xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:app="http://schemas.android.com/apk/res-auto" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:padding="2dp" | |||
> | |||
<Button | |||
android:id="@+id/btn_scan" | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:onClick="reScan" | |||
android:text="@string/reScan" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintEnd_toStartOf="@+id/btn_clean_log" | |||
app:layout_constraintTop_toTopOf="parent" | |||
app:layout_constraintBottom_toTopOf="@+id/rv_ble"/> | |||
<Button | |||
android:id="@+id/btn_clean_log" | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:onClick="cleanLog" | |||
android:text="@string/clean_log" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toEndOf="@+id/btn_scan" | |||
app:layout_constraintTop_toTopOf="parent" | |||
app:layout_constraintBottom_toTopOf="@+id/rv_ble" /> | |||
<androidx.recyclerview.widget.RecyclerView | |||
android:id="@+id/rv_ble" | |||
android:layout_width="match_parent" | |||
android:layout_height="0dp" | |||
android:background="@drawable/stroke" | |||
android:padding="4dp" | |||
app:layout_constraintHeight_percent="0.4" | |||
app:layout_constraintTop_toBottomOf="@+id/btn_scan" /> | |||
<Button | |||
android:id="@+id/btn_read" | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:onClick="read" | |||
android:text="@string/read" | |||
app:layout_constraintEnd_toStartOf="@+id/btn_notify" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@id/rv_ble" /> | |||
<Button | |||
android:id="@+id/btn_notify" | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:onClick="setNotify" | |||
android:text="@string/setNotify" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toEndOf="@+id/btn_read" | |||
app:layout_constraintTop_toBottomOf="@+id/rv_ble" /> | |||
<EditText | |||
android:id="@+id/et_write" | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:hint="@string/max_byte" | |||
android:inputType="none" | |||
android:maxLength="20" | |||
app:layout_constraintEnd_toStartOf="@+id/btn_write" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/btn_read"/> | |||
<Button | |||
android:id="@+id/btn_write" | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:onClick="write" | |||
android:text="@string/write" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toEndOf="@+id/et_write" | |||
app:layout_constraintTop_toBottomOf="@+id/btn_notify" /> | |||
<Button | |||
android:id="@+id/btn_write_cmd" | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:onClick="writeCmd" | |||
android:text="@string/write_cmd" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintEnd_toStartOf="@+id/btn_write_long_cmd" | |||
app:layout_constraintTop_toBottomOf="@+id/btn_write" /> | |||
<Button | |||
android:id="@+id/btn_write_long_cmd" | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:onClick="writeLongCmd" | |||
android:textSize="10sp" | |||
android:text="@string/write_long_cmd" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toEndOf="@+id/btn_write_cmd" | |||
app:layout_constraintTop_toBottomOf="@+id/btn_write" /> | |||
<ScrollView | |||
android:id="@+id/sv" | |||
android:layout_width="0dp" | |||
android:layout_height="0dp" | |||
android:background="@drawable/stroke" | |||
android:padding="2dp" | |||
android:scrollbars="none" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/btn_write_cmd"> | |||
<TextView | |||
android:id="@+id/tv_tips" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" /> | |||
</ScrollView> | |||
</androidx.constraintlayout.widget.ConstraintLayout> |
@@ -0,0 +1,451 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:layout_width="fill_parent" | |||
android:layout_height="fill_parent" | |||
android:orientation="vertical" > | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutRecDisp" | |||
android:layout_width="match_parent" | |||
android:layout_height="fill_parent" | |||
android:layout_weight="1" > | |||
<EditText | |||
android:id="@+id/editTextRecDisp" | |||
android:layout_width="fill_parent" | |||
android:layout_height="match_parent" | |||
android:clickable="false" | |||
android:gravity="top" | |||
android:longClickable="false" android:layout_weight="1" android:textSize="14sp" android:editable="false"/> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutRecTool" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:orientation="vertical" > | |||
<Button | |||
android:id="@+id/ButtonClear" | |||
android:layout_width="fill_parent" | |||
android:layout_height="wrap_content" | |||
android:text="清除"/> | |||
<RadioGroup | |||
android:id="@+id/radioGroupOption" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" > | |||
<RadioButton | |||
android:id="@+id/radioButtonTxt" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:checked="true" | |||
android:text="Txt" /> | |||
<RadioButton | |||
android:id="@+id/radioButtonHex" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="Hex" /> | |||
</RadioGroup> | |||
<EditText | |||
android:id="@+id/editTextLines" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:clickable="false" | |||
android:editable="false" | |||
android:enabled="false" | |||
android:imeOptions="actionNone" | |||
android:inputType="number" | |||
android:longClickable="false" | |||
android:text="0" > | |||
<requestFocus /> | |||
</EditText> | |||
<CheckBox | |||
android:id="@+id/checkBoxAutoClear" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:checked="true" | |||
android:text="自清" /> | |||
</LinearLayout> | |||
</LinearLayout> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutCOMAB" | |||
android:layout_width="match_parent" | |||
android:layout_height="fill_parent" | |||
android:layout_weight="1" > | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutCOMA" | |||
android:layout_width="fill_parent" | |||
android:layout_height="match_parent" | |||
android:layout_weight="1" | |||
android:orientation="vertical" > | |||
<EditText | |||
android:id="@+id/editTextCOMA" | |||
android:layout_width="match_parent" | |||
android:layout_height="fill_parent" | |||
android:gravity="top" | |||
android:imeOptions="actionDone" android:layout_weight="1" android:singleLine="true"/> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutTooLCOMA" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:orientation="vertical"> | |||
<Spinner | |||
android:id="@+id/SpinnerCOMA" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" /> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutOption1COMA" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:gravity="center_vertical" > | |||
<Spinner | |||
android:id="@+id/SpinnerBaudRateCOMA" | |||
android:layout_width="fill_parent" | |||
android:layout_height="wrap_content" | |||
android:layout_weight="1" /> | |||
<ToggleButton | |||
android:id="@+id/toggleButtonCOMA" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="ToggleButton" /> | |||
</LinearLayout> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutOption2COMA" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:gravity="center_vertical" > | |||
<EditText | |||
android:id="@+id/editTextTimeCOMA" | |||
android:layout_width="fill_parent" | |||
android:layout_height="wrap_content" | |||
android:inputType="number" android:text="500" android:layout_weight="1"/> | |||
<TextView | |||
android:id="@+id/textView1" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="ms" /> | |||
<CheckBox | |||
android:id="@+id/checkBoxAutoCOMA" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="自动" /> | |||
<Button | |||
android:id="@+id/ButtonSendCOMA" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="发送"/> | |||
</LinearLayout> | |||
</LinearLayout> | |||
</LinearLayout> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutCOMB" | |||
android:layout_width="fill_parent" | |||
android:layout_height="match_parent" | |||
android:layout_weight="1" | |||
android:orientation="vertical" > | |||
<EditText | |||
android:id="@+id/editTextCOMB" | |||
android:layout_width="match_parent" | |||
android:layout_height="fill_parent" | |||
android:layout_weight="1" android:gravity="top" android:imeOptions="actionDone" android:singleLine="true"/> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutTooLCOMB" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:orientation="vertical" > | |||
<Spinner | |||
android:id="@+id/SpinnerCOMB" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" /> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutOption1COMB" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:gravity="center_vertical" > | |||
<Spinner | |||
android:id="@+id/SpinnerBaudRateCOMB" | |||
android:layout_width="fill_parent" | |||
android:layout_height="wrap_content" | |||
android:layout_weight="1" /> | |||
<ToggleButton | |||
android:id="@+id/ToggleButtonCOMB" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="ToggleButton" /> | |||
</LinearLayout> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutOption2COMB" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:gravity="center_vertical" > | |||
<EditText | |||
android:id="@+id/editTextTimeCOMB" | |||
android:layout_width="fill_parent" | |||
android:layout_height="wrap_content" | |||
android:layout_weight="1" | |||
android:inputType="number" android:text="500"/> | |||
<TextView | |||
android:id="@+id/TextView01" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="ms" /> | |||
<CheckBox | |||
android:id="@+id/checkBoxAutoCOMB" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="自动" /> | |||
<Button | |||
android:id="@+id/ButtonSendCOMB" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="发送"/> | |||
</LinearLayout> | |||
</LinearLayout> | |||
</LinearLayout> | |||
</LinearLayout> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutCOMCD" | |||
android:layout_width="match_parent" | |||
android:layout_height="fill_parent" | |||
android:layout_weight="1" > | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutCOMC" | |||
android:layout_width="fill_parent" | |||
android:layout_height="match_parent" | |||
android:layout_weight="1" | |||
android:orientation="vertical" > | |||
<EditText | |||
android:id="@+id/editTextCOMC" | |||
android:layout_width="match_parent" | |||
android:layout_height="fill_parent" | |||
android:gravity="top" | |||
android:imeOptions="actionDone" android:layout_weight="1" android:singleLine="true"/> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutTooLCOMC" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:orientation="vertical" > | |||
<Spinner | |||
android:id="@+id/SpinnerCOMC" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" /> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutOption1COMC" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:gravity="center_vertical" > | |||
<Spinner | |||
android:id="@+id/SpinnerBaudRateCOMC" | |||
android:layout_width="fill_parent" | |||
android:layout_height="wrap_content" | |||
android:layout_weight="1" /> | |||
<ToggleButton | |||
android:id="@+id/ToggleButtonCOMC" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="ToggleButton" /> | |||
</LinearLayout> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutOption2COMC" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:gravity="center_vertical" > | |||
<EditText | |||
android:id="@+id/editTextTimeCOMC" | |||
android:layout_width="fill_parent" | |||
android:layout_height="wrap_content" | |||
android:layout_weight="1" | |||
android:inputType="number" | |||
android:text="500" /> | |||
<TextView | |||
android:id="@+id/TextView02" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="ms" /> | |||
<CheckBox | |||
android:id="@+id/checkBoxAutoCOMC" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="自动" /> | |||
<Button | |||
android:id="@+id/ButtonSendCOMC" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="发送" /> | |||
</LinearLayout> | |||
</LinearLayout> | |||
</LinearLayout> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutCOMD" | |||
android:layout_width="fill_parent" | |||
android:layout_height="match_parent" | |||
android:layout_weight="1" | |||
android:orientation="vertical" > | |||
<EditText | |||
android:id="@+id/editTextCOMD" | |||
android:layout_width="match_parent" | |||
android:layout_height="fill_parent" | |||
android:layout_weight="1" | |||
android:gravity="top" | |||
android:imeOptions="actionDone" android:singleLine="true"/> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutTooLCOMD" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:orientation="vertical" > | |||
<Spinner | |||
android:id="@+id/SpinnerCOMD" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" /> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutOption1COMD" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:gravity="center_vertical" > | |||
<Spinner | |||
android:id="@+id/SpinnerBaudRateCOMD" | |||
android:layout_width="fill_parent" | |||
android:layout_height="wrap_content" | |||
android:layout_weight="1" /> | |||
<ToggleButton | |||
android:id="@+id/ToggleButtonCOMD" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="ToggleButton" /> | |||
</LinearLayout> | |||
<LinearLayout | |||
android:id="@+id/LinearLayoutOption2COMD" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:gravity="center_vertical" > | |||
<EditText | |||
android:id="@+id/editTextTimeCOMD" | |||
android:layout_width="fill_parent" | |||
android:layout_height="wrap_content" | |||
android:layout_weight="1" | |||
android:inputType="number" | |||
android:text="500" /> | |||
<TextView | |||
android:id="@+id/TextView03" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="ms" /> | |||
<CheckBox | |||
android:id="@+id/checkBoxAutoCOMD" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="自动" /> | |||
<Button | |||
android:id="@+id/ButtonSendCOMD" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="发送" /> | |||
</LinearLayout> | |||
</LinearLayout> | |||
</LinearLayout> | |||
</LinearLayout> | |||
</LinearLayout> |
@@ -0,0 +1,49 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
xmlns:tools="http://schemas.android.com/tools"> | |||
<Button | |||
android:id="@+id/btn1" | |||
android:layout_width="200dp" | |||
android:layout_height="100dp" | |||
android:layout_gravity="center_horizontal" | |||
android:background="@drawable/bg_round15_yellow_btn" | |||
android:text="开启蓝牙" | |||
android:layout_marginTop="150dp" | |||
android:textSize="36sp" | |||
android:textColor="@color/white" | |||
> | |||
<requestFocus/> | |||
</Button> | |||
<TextView | |||
android:id="@+id/tv_state" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginTop="50dp" | |||
android:textColor="@color/black" | |||
tools:text="开启" | |||
android:layout_marginLeft="300dp" | |||
android:textStyle="bold" | |||
android:textSize="36sp" | |||
/> | |||
<TextView | |||
android:id="@+id/tv" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_gravity="center" | |||
android:textColor="@color/black" | |||
android:textSize="36sp" | |||
tools:text="asasasaaaaaaaaaaaaaa" | |||
/> | |||
<TextView | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_gravity="center" | |||
android:textColor="@color/black" | |||
android:textSize="36sp" | |||
android:text="a" | |||
/> | |||
</FrameLayout> |
@@ -0,0 +1,22 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:app="http://schemas.android.com/apk/res-auto" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:layout_margin="2dp" | |||
android:background="@drawable/sel_item" | |||
android:divider="@drawable/divider" | |||
android:orientation="vertical" | |||
android:showDividers="end"> | |||
<TextView | |||
android:id="@+id/name" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content"/> | |||
<TextView | |||
android:id="@+id/address" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
app:layout_constraintStart_toEndOf="@+id/name" /> | |||
</LinearLayout> |
@@ -0,0 +1,69 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<resources> | |||
<string-array name="baudrates_name"> | |||
<item>50</item> | |||
<item>75</item> | |||
<item>110</item> | |||
<item>134</item> | |||
<item>150</item> | |||
<item>200</item> | |||
<item>300</item> | |||
<item>600</item> | |||
<item>1200</item> | |||
<item>1800</item> | |||
<item>2400</item> | |||
<item>4800</item> | |||
<item>9600</item> | |||
<item>19200</item> | |||
<item>38400</item> | |||
<item>57600</item> | |||
<item>115200</item> | |||
<item>230400</item> | |||
<item>460800</item> | |||
<item>500000</item> | |||
<item>576000</item> | |||
<item>921600</item> | |||
<item>1000000</item> | |||
<item>1152000</item> | |||
<item>1500000</item> | |||
<item>2000000</item> | |||
<item>2500000</item> | |||
<item>3000000</item> | |||
<item>3500000</item> | |||
<item>4000000</item> | |||
</string-array> | |||
<string-array name="baudrates_value"> | |||
<item>50</item> | |||
<item>75</item> | |||
<item>110</item> | |||
<item>134</item> | |||
<item>150</item> | |||
<item>200</item> | |||
<item>300</item> | |||
<item>600</item> | |||
<item>1200</item> | |||
<item>1800</item> | |||
<item>2400</item> | |||
<item>4800</item> | |||
<item>9600</item> | |||
<item>19200</item> | |||
<item>38400</item> | |||
<item>57600</item> | |||
<item>115200</item> | |||
<item>230400</item> | |||
<item>460800</item> | |||
<item>500000</item> | |||
<item>576000</item> | |||
<item>921600</item> | |||
<item>1000000</item> | |||
<item>1152000</item> | |||
<item>1500000</item> | |||
<item>2000000</item> | |||
<item>2500000</item> | |||
<item>3000000</item> | |||
<item>3500000</item> | |||
<item>4000000</item> | |||
</string-array> | |||
</resources> |
@@ -198,4 +198,18 @@ | |||
<string name="top_cleaninfo">在此输入您的用量</string> | |||
<string name="text_calibrationinfo">标定前需要排空管道</string> | |||
<string name="top_autotest">模块自动测试</string> | |||
<string name="ble_client">BLE客户端</string> | |||
<string name="ble_server">BLE服务端</string> | |||
<string name="ble">低功耗蓝牙(BLE)</string> | |||
<string name="reScan">重新扫描</string> | |||
<string name="clean_log">清空日志</string> | |||
<string name="read">读取</string> | |||
<string name="setNotify">设置通知</string> | |||
<string name="max_byte">最多20字节</string> | |||
<string name="write">写入</string> | |||
<string name="re_conn">重连</string> | |||
<string name="serial_port">串口调试</string> | |||
<string name="write_cmd">写入协议内容</string> | |||
<string name="write_long_cmd">写入长(大于20字节)内容</string> | |||
</resources> |