46 changed files with 3613 additions and 220 deletions
@ -1,213 +0,0 @@ |
|||||
{ |
|
||||
"originHash" : "fb7ecff82f4ff109531189491cc6cc2299a7357c61874f58bae1e0f7b7d9e8d0", |
|
||||
"pins" : [ |
|
||||
{ |
|
||||
"identity" : "abseil-cpp-binary", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/abseil-cpp-binary.git", |
|
||||
"state" : { |
|
||||
"revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", |
|
||||
"version" : "1.2024072200.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "app-check", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/app-check.git", |
|
||||
"state" : { |
|
||||
"revision" : "61b85103a1aeed8218f17c794687781505fbbef5", |
|
||||
"version" : "11.2.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "appauth-ios", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/openid/AppAuth-iOS.git", |
|
||||
"state" : { |
|
||||
"revision" : "2781038865a80e2c425a1da12cc1327bcd56501f", |
|
||||
"version" : "1.7.6" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "dkcamera", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/zhangao0086/DKCamera", |
|
||||
"state" : { |
|
||||
"branch" : "master", |
|
||||
"revision" : "5c691d11014b910aff69f960475d70e65d9dcc96" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "dkimagepickercontroller", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/zhangao0086/DKImagePickerController", |
|
||||
"state" : { |
|
||||
"branch" : "4.3.9", |
|
||||
"revision" : "0bdfeacefa308545adde07bef86e349186335915" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "dkphotogallery", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/zhangao0086/DKPhotoGallery", |
|
||||
"state" : { |
|
||||
"branch" : "master", |
|
||||
"revision" : "311c1bc7a94f1538f82773a79c84374b12a2ef3d" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "firebase-ios-sdk", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/firebase/firebase-ios-sdk", |
|
||||
"state" : { |
|
||||
"revision" : "3663b1aa6c7a1bed67ee80fd09dc6d0f9c3bb660", |
|
||||
"version" : "11.13.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "flutterfire", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/firebase/flutterfire", |
|
||||
"state" : { |
|
||||
"revision" : "a80a123386fd4904cad6938673020a7bcf31b2f2", |
|
||||
"version" : "3.13.0-firebase-core-swift" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "googleappmeasurement", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/GoogleAppMeasurement.git", |
|
||||
"state" : { |
|
||||
"revision" : "543071966b3fb6613a2fc5c6e7112d1e998184a7", |
|
||||
"version" : "11.13.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "googledatatransport", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/GoogleDataTransport.git", |
|
||||
"state" : { |
|
||||
"revision" : "617af071af9aa1d6a091d59a202910ac482128f9", |
|
||||
"version" : "10.1.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "googlesignin-ios", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/GoogleSignIn-iOS.git", |
|
||||
"state" : { |
|
||||
"revision" : "a7965d134c5d3567026c523e0a8a583f73b62b0d", |
|
||||
"version" : "7.1.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "googleutilities", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/GoogleUtilities.git", |
|
||||
"state" : { |
|
||||
"revision" : "60da361632d0de02786f709bdc0c4df340f7613e", |
|
||||
"version" : "8.1.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "grpc-binary", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/grpc-binary.git", |
|
||||
"state" : { |
|
||||
"revision" : "cc0001a0cf963aa40501d9c2b181e7fc9fd8ec71", |
|
||||
"version" : "1.69.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "gtm-session-fetcher", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/gtm-session-fetcher.git", |
|
||||
"state" : { |
|
||||
"revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", |
|
||||
"version" : "3.5.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "gtmappauth", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/GTMAppAuth.git", |
|
||||
"state" : { |
|
||||
"revision" : "5d7d66f647400952b1758b230e019b07c0b4b22a", |
|
||||
"version" : "4.1.1" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "interop-ios-for-google-sdks", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/interop-ios-for-google-sdks.git", |
|
||||
"state" : { |
|
||||
"revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", |
|
||||
"version" : "101.0.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "leveldb", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/firebase/leveldb.git", |
|
||||
"state" : { |
|
||||
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", |
|
||||
"version" : "1.22.5" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "nanopb", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/firebase/nanopb.git", |
|
||||
"state" : { |
|
||||
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", |
|
||||
"version" : "2.30910.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "promises", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/google/promises.git", |
|
||||
"state" : { |
|
||||
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", |
|
||||
"version" : "2.4.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "sdwebimage", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/SDWebImage/SDWebImage", |
|
||||
"state" : { |
|
||||
"revision" : "cac9a55a3ae92478a2c95042dcc8d9695d2129ca", |
|
||||
"version" : "5.21.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "swift-protobuf", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/apple/swift-protobuf.git", |
|
||||
"state" : { |
|
||||
"revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", |
|
||||
"version" : "1.29.0" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "swiftygif", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/kirualex/SwiftyGif.git", |
|
||||
"state" : { |
|
||||
"revision" : "4430cbc148baa3907651d40562d96325426f409a", |
|
||||
"version" : "5.4.5" |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
"identity" : "tocropviewcontroller", |
|
||||
"kind" : "remoteSourceControl", |
|
||||
"location" : "https://github.com/TimOliver/TOCropViewController", |
|
||||
"state" : { |
|
||||
"revision" : "a634cb7cdfd580006e79a6e74e64417fe9e9783b", |
|
||||
"version" : "2.7.4" |
|
||||
} |
|
||||
} |
|
||||
], |
|
||||
"version" : 3 |
|
||||
} |
|
||||
@ -1,6 +1,5 @@ |
|||||
class NWApi { |
class NWApi { |
||||
// static const baseApi = "https://www.deapsound.com"; |
// static const baseApi = "https://www.deapsound.com"; |
||||
//测试服务器链接地址,经常修改变动,开发使用 |
|
||||
static const baseApi = "https://api.deapsound.com"; |
static const baseApi = "https://api.deapsound.com"; |
||||
// static const baseApi = "https://api.ideapsound.com"; |
//static const baseApi = "https://api.ideapsound.com"; |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,33 @@ |
|||||
|
# Miscellaneous |
||||
|
*.class |
||||
|
*.log |
||||
|
*.pyc |
||||
|
*.swp |
||||
|
.DS_Store |
||||
|
.atom/ |
||||
|
.build/ |
||||
|
.buildlog/ |
||||
|
.history |
||||
|
.svn/ |
||||
|
.swiftpm/ |
||||
|
migrate_working_dir/ |
||||
|
|
||||
|
# IntelliJ related |
||||
|
*.iml |
||||
|
*.ipr |
||||
|
*.iws |
||||
|
.idea/ |
||||
|
|
||||
|
# The .vscode folder contains launch configuration and tasks you configure in |
||||
|
# VS Code which you may wish to be included in version control, so this line |
||||
|
# is commented out by default. |
||||
|
#.vscode/ |
||||
|
|
||||
|
# Flutter/Dart/Pub related |
||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. |
||||
|
/pubspec.lock |
||||
|
**/doc/api/ |
||||
|
.dart_tool/ |
||||
|
.flutter-plugins |
||||
|
.flutter-plugins-dependencies |
||||
|
build/ |
||||
@ -0,0 +1,33 @@ |
|||||
|
# This file tracks properties of this Flutter project. |
||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc. |
||||
|
# |
||||
|
# This file should be version controlled and should not be manually edited. |
||||
|
|
||||
|
version: |
||||
|
revision: "ea121f8859e4b13e47a8f845e4586164519588bc" |
||||
|
channel: "stable" |
||||
|
|
||||
|
project_type: plugin |
||||
|
|
||||
|
# Tracks metadata for the flutter migrate command |
||||
|
migration: |
||||
|
platforms: |
||||
|
- platform: root |
||||
|
create_revision: ea121f8859e4b13e47a8f845e4586164519588bc |
||||
|
base_revision: ea121f8859e4b13e47a8f845e4586164519588bc |
||||
|
- platform: android |
||||
|
create_revision: ea121f8859e4b13e47a8f845e4586164519588bc |
||||
|
base_revision: ea121f8859e4b13e47a8f845e4586164519588bc |
||||
|
- platform: ios |
||||
|
create_revision: ea121f8859e4b13e47a8f845e4586164519588bc |
||||
|
base_revision: ea121f8859e4b13e47a8f845e4586164519588bc |
||||
|
|
||||
|
# User provided section |
||||
|
|
||||
|
# List of Local paths (relative to this file) that should be |
||||
|
# ignored by the migrate tool. |
||||
|
# |
||||
|
# Files that are not part of the templates will be ignored by default. |
||||
|
unmanaged_files: |
||||
|
- 'lib/main.dart' |
||||
|
- 'ios/Runner.xcodeproj/project.pbxproj' |
||||
@ -0,0 +1,3 @@ |
|||||
|
## 0.0.1 |
||||
|
|
||||
|
* TODO: Describe initial release. |
||||
@ -0,0 +1 @@ |
|||||
|
TODO: Add your license here. |
||||
@ -0,0 +1,15 @@ |
|||||
|
# ota |
||||
|
|
||||
|
A new Flutter project. |
||||
|
|
||||
|
## Getting Started |
||||
|
|
||||
|
This project is a starting point for a Flutter |
||||
|
[plug-in package](https://flutter.dev/to/develop-plugins), |
||||
|
a specialized package that includes platform-specific implementation code for |
||||
|
Android and/or iOS. |
||||
|
|
||||
|
For help getting started with Flutter development, view the |
||||
|
[online documentation](https://docs.flutter.dev), which offers tutorials, |
||||
|
samples, guidance on mobile development, and a full API reference. |
||||
|
|
||||
@ -0,0 +1,4 @@ |
|||||
|
include: package:flutter_lints/flutter.yaml |
||||
|
|
||||
|
# Additional information about this file can be found at |
||||
|
# https://dart.dev/guides/language/analysis-options |
||||
@ -0,0 +1,9 @@ |
|||||
|
*.iml |
||||
|
.gradle |
||||
|
/local.properties |
||||
|
/.idea/workspace.xml |
||||
|
/.idea/libraries |
||||
|
.DS_Store |
||||
|
/build |
||||
|
/captures |
||||
|
.cxx |
||||
@ -0,0 +1,68 @@ |
|||||
|
group = "com.example.ota" |
||||
|
version = "1.0-SNAPSHOT" |
||||
|
|
||||
|
buildscript { |
||||
|
ext.kotlin_version = "1.8.22" |
||||
|
repositories { |
||||
|
google() |
||||
|
mavenCentral() |
||||
|
} |
||||
|
|
||||
|
dependencies { |
||||
|
classpath("com.android.tools.build:gradle:8.7.0") |
||||
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
allprojects { |
||||
|
repositories { |
||||
|
google() |
||||
|
mavenCentral() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
apply plugin: "com.android.library" |
||||
|
apply plugin: "kotlin-android" |
||||
|
|
||||
|
android { |
||||
|
namespace = "com.example.ota" |
||||
|
|
||||
|
compileSdk = 35 |
||||
|
|
||||
|
compileOptions { |
||||
|
sourceCompatibility = JavaVersion.VERSION_11 |
||||
|
targetCompatibility = JavaVersion.VERSION_11 |
||||
|
} |
||||
|
|
||||
|
kotlinOptions { |
||||
|
jvmTarget = JavaVersion.VERSION_11 |
||||
|
} |
||||
|
|
||||
|
sourceSets { |
||||
|
main.java.srcDirs += "src/main/kotlin" |
||||
|
test.java.srcDirs += "src/test/kotlin" |
||||
|
} |
||||
|
|
||||
|
defaultConfig { |
||||
|
minSdk = 21 |
||||
|
} |
||||
|
|
||||
|
dependencies { |
||||
|
implementation("com.jieli.jl_bt_ota:jl_bt_ota:1.9.2") |
||||
|
testImplementation("org.jetbrains.kotlin:kotlin-test") |
||||
|
testImplementation("org.mockito:mockito-core:5.0.0") |
||||
|
add("compileOnly", project(":ble_service")) |
||||
|
} |
||||
|
|
||||
|
testOptions { |
||||
|
unitTests.all { |
||||
|
useJUnitPlatform() |
||||
|
|
||||
|
testLogging { |
||||
|
events "passed", "skipped", "failed", "standardOut", "standardError" |
||||
|
outputs.upToDateWhen {false} |
||||
|
showStandardStreams = true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1 @@ |
|||||
|
rootProject.name = 'ota' |
||||
@ -0,0 +1,4 @@ |
|||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
package="com.example.ota"> |
||||
|
|
||||
|
</manifest> |
||||
@ -0,0 +1,26 @@ |
|||||
|
package com.jieli.otasdk.model |
||||
|
|
||||
|
import android.bluetooth.BluetoothDevice |
||||
|
|
||||
|
/** |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2022/9/9 |
||||
|
* @email zhongzhuocheng@zh-jieli.com |
||||
|
* @desc 设备连接状态 |
||||
|
*/ |
||||
|
class DeviceConnection(val device: BluetoothDevice?, var state: Int) { |
||||
|
|
||||
|
override fun hashCode(): Int { |
||||
|
return device.hashCode() |
||||
|
} |
||||
|
|
||||
|
override fun equals(other: Any?): Boolean { |
||||
|
if (other !is DeviceConnection) return false |
||||
|
return device == other.device |
||||
|
} |
||||
|
|
||||
|
override fun toString(): String { |
||||
|
return "DeviceConnection(device=$device, state=$state)" |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,148 @@ |
|||||
|
package com.example.ota |
||||
|
|
||||
|
import io.flutter.embedding.engine.plugins.FlutterPlugin |
||||
|
import io.flutter.plugin.common.MethodCall |
||||
|
import io.flutter.plugin.common.MethodChannel |
||||
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler |
||||
|
import io.flutter.plugin.common.MethodChannel.Result |
||||
|
|
||||
|
import android.bluetooth.BluetoothDevice |
||||
|
import android.content.Context |
||||
|
import com.jieli.otasdk.tool.ota.OTAManager |
||||
|
import com.jieli.jl_bt_ota.constant.ErrorCode |
||||
|
import com.jieli.jl_bt_ota.constant.StateCode |
||||
|
import com.jieli.jl_bt_ota.interfaces.BtEventCallback |
||||
|
import com.jieli.jl_bt_ota.interfaces.IUpgradeCallback |
||||
|
import com.jieli.jl_bt_ota.model.base.BaseError |
||||
|
import com.jieli.jl_bt_ota.model.response.TargetInfoResponse |
||||
|
import com.jieli.jl_bt_ota.util.BluetoothUtil |
||||
|
import com.jieli.jl_bt_ota.util.CHexConver |
||||
|
import com.jieli.jl_bt_ota.util.JL_Log |
||||
|
import com.jieli.otasdk.tool.bluetooth.BluetoothHelper |
||||
|
import androidx.lifecycle.MutableLiveData |
||||
|
import com.jieli.otasdk.model.DeviceConnection |
||||
|
import android.util.Log |
||||
|
import androidx.annotation.NonNull |
||||
|
import com.jieli.otasdk.tool.config.ConfigHelper |
||||
|
import com.yunqiinnovation.ble_service.BleService |
||||
|
class CustomUpdateCallback( |
||||
|
|
||||
|
val device: BluetoothDevice? |
||||
|
) : IUpgradeCallback { |
||||
|
companion object { |
||||
|
private const val TAG = "JlOtaPlugin" |
||||
|
private const val CHANNEL_NAME = "com.yunqiinnovation.jl_ota" |
||||
|
private const val EVENT_CHANNEL_NAME = "com.yunqiinnovation.jl_ota/events" |
||||
|
|
||||
|
} |
||||
|
|
||||
|
override fun onStartOTA() { |
||||
|
Log.d(TAG, "onStartOTA开始升级") |
||||
|
// viewModel.otaStateMLD.value = OTAStart(device) |
||||
|
} |
||||
|
|
||||
|
override fun onNeedReconnect(addr: String?, isNewReconnectWay: Boolean) { |
||||
|
Log.d(TAG, "onNeedReconnect需要重新链接") |
||||
|
|
||||
|
} |
||||
|
|
||||
|
override fun onProgress(type: Int, progress: Float) { |
||||
|
Log.d(TAG, "onProgress正在进展") |
||||
|
//viewModel.otaStateMLD.value = OTAWorking(device, type, progress) |
||||
|
} |
||||
|
|
||||
|
override fun onStopOTA() { |
||||
|
Log.d(TAG, "onStartOTA停止升级") |
||||
|
|
||||
|
} |
||||
|
|
||||
|
override fun onCancelOTA() { |
||||
|
Log.d(TAG, "onCancelOTA取消升级") |
||||
|
|
||||
|
} |
||||
|
|
||||
|
override fun onError(error: BaseError?) { |
||||
|
val errorMessage = error?.message ?: "Unknown error" |
||||
|
Log.e(TAG, "升级失败: ${error?.message ?: "未知错误"}") |
||||
|
|
||||
|
// 如果需要打印完整错误对象信息(假设BaseError有重写toString()) |
||||
|
Log.d(TAG, "完整错误对象: ${error ?: "null"}") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** OtaPlugin */ |
||||
|
class OtaPlugin: FlutterPlugin, MethodCallHandler { |
||||
|
/// The MethodChannel that will the communication between Flutter and native Android |
||||
|
/// |
||||
|
/// This local reference serves to register the plugin with the Flutter Engine and unregister it |
||||
|
/// when the Flutter Engine is detached from the Activity |
||||
|
private lateinit var channel : MethodChannel |
||||
|
private lateinit var context: Context |
||||
|
|
||||
|
private lateinit var bluetoothHelper : BluetoothHelper |
||||
|
val otaConnectionMLD = MutableLiveData<DeviceConnection>() |
||||
|
val mandatoryUpgradeMLD = MutableLiveData<BluetoothDevice>() |
||||
|
private lateinit var otaManager : OTAManager |
||||
|
|
||||
|
private val bluetoothCallback = object : BtEventCallback() { |
||||
|
|
||||
|
override fun onConnection(device: BluetoothDevice?, status: Int) { |
||||
|
if ((status == StateCode.CONNECTION_FAILED || status == StateCode.CONNECTION_DISCONNECT) && bluetoothHelper.getConnectedDevice() != null) { |
||||
|
bluetoothHelper.disconnectDevice(bluetoothHelper.getConnectedDevice()) |
||||
|
} |
||||
|
otaConnectionMLD.value = DeviceConnection(device, status) |
||||
|
} |
||||
|
|
||||
|
override fun onMandatoryUpgrade(device: BluetoothDevice?) { |
||||
|
mandatoryUpgradeMLD.postValue(device!!) |
||||
|
} |
||||
|
} |
||||
|
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { |
||||
|
context = flutterPluginBinding.applicationContext |
||||
|
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "ota") |
||||
|
channel.setMethodCallHandler(this) |
||||
|
|
||||
|
Log.d("TAG", "onAttachedToEngine") |
||||
|
} |
||||
|
|
||||
|
override fun onMethodCall(call: MethodCall, result: Result) { |
||||
|
if (call.method == "getPlatformVersion") { |
||||
|
Log.d("TAG", "Connection") |
||||
|
// otaManager.configHelper |
||||
|
// 确保先初始化 ConfigHelper |
||||
|
ConfigHelper.initialize(context) |
||||
|
otaManager = OTAManager(context) |
||||
|
bluetoothHelper = BluetoothHelper(context) |
||||
|
// 如果需要额外的初始化逻辑,请在这里添加 |
||||
|
otaManager.registerBluetoothCallback(bluetoothCallback) |
||||
|
|
||||
|
// otaManager.registerBluetoothCallback(bluetoothCallback) |
||||
|
result.success("Android ${android.os.Build.VERSION.RELEASE}") |
||||
|
}else if (call.method == "searchDevices") { |
||||
|
Log.d("TAG", "searchDevices") |
||||
|
// otaManager.configHelper |
||||
|
BleService.disconnect() |
||||
|
// 确保先初始化 ConfigHelper |
||||
|
otaManager.bluetoothHelper.startScan(BleService.bluetoothGatt?.device) |
||||
|
|
||||
|
// otaManager.registerBluetoothCallback(bluetoothCallback) |
||||
|
result.success("Android ${android.os.Build.VERSION.RELEASE}") |
||||
|
} else if (call.method == "startUpgrade") { |
||||
|
Log.d("TAG", "startUpgrade") |
||||
|
var filePath = "/storage/emulated/0/Download/JieLiOTA/upgrade/update - v0.1.0_添加杰理OTA,优化主从切换拾音丢失问题.ufw" |
||||
|
otaManager.bluetoothOption.firmwareFilePath = filePath |
||||
|
otaManager.startOTA( |
||||
|
CustomUpdateCallback( |
||||
|
otaManager.bluetoothHelper.getConnectedDevice() |
||||
|
) |
||||
|
) |
||||
|
result.success("Android ${android.os.Build.VERSION.RELEASE}") |
||||
|
} else { |
||||
|
result.notImplemented() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { |
||||
|
channel.setMethodCallHandler(null) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,91 @@ |
|||||
|
package com.jieli.otasdk.tool.bluetooth |
||||
|
|
||||
|
import android.bluetooth.BluetoothDevice |
||||
|
import android.os.Handler |
||||
|
import android.os.Looper |
||||
|
import com.jieli.otasdk.tool.ota.ble.model.BleScanInfo |
||||
|
import java.util.* |
||||
|
|
||||
|
/** |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2022/9/14 |
||||
|
* @email zhongzhuocheng@zh-jieli.com |
||||
|
* @desc 蓝牙事件回调辅助类 |
||||
|
*/ |
||||
|
class BTEventCbHelper : OnBTEventCallback() { |
||||
|
private val callbacks = mutableListOf<OnBTEventCallback>() |
||||
|
private val uiHandler = Handler(Looper.getMainLooper()) |
||||
|
|
||||
|
fun registerCallback(callback: OnBTEventCallback) { |
||||
|
if (callbacks.contains(callback)) return |
||||
|
callbacks.add(callback) |
||||
|
} |
||||
|
|
||||
|
fun unregisterCallback(callback: OnBTEventCallback) { |
||||
|
if (callbacks.isEmpty()) return |
||||
|
callbacks.remove(callback) |
||||
|
} |
||||
|
|
||||
|
fun release() { |
||||
|
callbacks.clear() |
||||
|
uiHandler.removeCallbacksAndMessages(null) |
||||
|
} |
||||
|
|
||||
|
override fun onAdapterChange(bEnabled: Boolean) { |
||||
|
callbackEvent(object : CallbackImpl<OnBTEventCallback> { |
||||
|
override fun onCallback(callback: OnBTEventCallback) { |
||||
|
callback.onAdapterChange(bEnabled) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
override fun onDiscoveryChange(bStart: Boolean, scanType: Int) { |
||||
|
callbackEvent(object : CallbackImpl<OnBTEventCallback> { |
||||
|
override fun onCallback(callback: OnBTEventCallback) { |
||||
|
callback.onDiscoveryChange(bStart, scanType) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
override fun onDiscovery(device: BluetoothDevice?, bleScanMessage: BleScanInfo?) { |
||||
|
callbackEvent(object : CallbackImpl<OnBTEventCallback> { |
||||
|
override fun onCallback(callback: OnBTEventCallback) { |
||||
|
callback.onDiscovery(device, bleScanMessage) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
override fun onDeviceConnection(device: BluetoothDevice?, way: Int, status: Int) { |
||||
|
callbackEvent(object : CallbackImpl<OnBTEventCallback> { |
||||
|
override fun onCallback(callback: OnBTEventCallback) { |
||||
|
callback.onDeviceConnection(device, way, status) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
override fun onReceiveData(device: BluetoothDevice?, way: Int, uuid: UUID?, data: ByteArray?) { |
||||
|
callbackEvent(object : CallbackImpl<OnBTEventCallback> { |
||||
|
override fun onCallback(callback: OnBTEventCallback) { |
||||
|
callback.onReceiveData(device, way, uuid, data) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
override fun onBleMtuChange(device: BluetoothDevice?, mtu: Int, status: Int) { |
||||
|
callbackEvent(object : CallbackImpl<OnBTEventCallback> { |
||||
|
override fun onCallback(callback: OnBTEventCallback) { |
||||
|
callback.onBleMtuChange(device, mtu, status) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
private fun callbackEvent(impl: CallbackImpl<OnBTEventCallback>?) { |
||||
|
if (null == impl) return |
||||
|
val runnable = CallbackRunnable(callbacks, impl) |
||||
|
if (Thread.currentThread().id == Looper.getMainLooper().thread.id) { |
||||
|
runnable.run() |
||||
|
} else { |
||||
|
uiHandler.post(runnable) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,204 @@ |
|||||
|
package com.jieli.otasdk.tool.bluetooth |
||||
|
|
||||
|
import android.bluetooth.BluetoothDevice |
||||
|
import android.bluetooth.BluetoothGatt |
||||
|
import android.bluetooth.BluetoothProfile |
||||
|
import android.content.Context |
||||
|
import com.jieli.jl_bt_ota.util.BluetoothUtil |
||||
|
import com.jieli.jl_bt_ota.util.CHexConver |
||||
|
import com.jieli.otasdk.tool.config.ConfigHelper |
||||
|
import com.jieli.otasdk.tool.ota.ble.BleManager |
||||
|
import com.jieli.otasdk.tool.ota.ble.interfaces.BleEventCallback |
||||
|
import com.jieli.otasdk.tool.ota.ble.model.BleScanInfo |
||||
|
import com.jieli.otasdk.tool.bluetooth.OtaConstant |
||||
|
import java.util.* |
||||
|
import android.util.Log |
||||
|
/** |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2022/9/14 |
||||
|
* @email zhongzhuocheng@zh-jieli.com |
||||
|
* @desc 蓝牙操作辅助类 |
||||
|
*/ |
||||
|
class BluetoothHelper(private val context: Context) { |
||||
|
|
||||
|
private val configHelper = ConfigHelper.getInstance() |
||||
|
private val bleManager = BleManager.getInstance(context)!! |
||||
|
private val btEventCbHelper = BTEventCbHelper() |
||||
|
|
||||
|
companion object { |
||||
|
|
||||
|
const val TAG: String = "BluetoothHelper" |
||||
|
|
||||
|
@Volatile |
||||
|
private var instance: BluetoothHelper? = null |
||||
|
|
||||
|
fun getInstance(context: Context): BluetoothHelper = instance ?: synchronized(this) { |
||||
|
instance ?: BluetoothHelper(context).also { instance = it } |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private val bleEventCallback = object : BleEventCallback() { |
||||
|
|
||||
|
override fun onAdapterChange(bEnabled: Boolean) { |
||||
|
btEventCbHelper.onAdapterChange(bEnabled) |
||||
|
} |
||||
|
|
||||
|
override fun onDiscoveryBleChange(bStart: Boolean) { |
||||
|
btEventCbHelper.onDiscoveryChange(bStart, OtaConstant.PROTOCOL_BLE) |
||||
|
} |
||||
|
|
||||
|
override fun onDiscoveryBle(device: BluetoothDevice?, bleScanMessage: BleScanInfo?) { |
||||
|
btEventCbHelper.onDiscovery(device, bleScanMessage) |
||||
|
} |
||||
|
|
||||
|
override fun onBleConnection(device: BluetoothDevice?, status: Int) { |
||||
|
btEventCbHelper.onDeviceConnection(device, OtaConstant.PROTOCOL_BLE, status) |
||||
|
} |
||||
|
|
||||
|
override fun onBleDataNotification( |
||||
|
device: BluetoothDevice?, |
||||
|
serviceUuid: UUID?, |
||||
|
characteristicsUuid: UUID?, |
||||
|
data: ByteArray? |
||||
|
) { |
||||
|
btEventCbHelper.onReceiveData( |
||||
|
device, |
||||
|
OtaConstant.PROTOCOL_BLE, |
||||
|
characteristicsUuid, |
||||
|
data |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
override fun onBleDataBlockChanged(device: BluetoothDevice?, block: Int, status: Int) { |
||||
|
btEventCbHelper.onBleMtuChange(device, block, status) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
init { |
||||
|
// if (configHelper.isBleWay()) { |
||||
|
bleManager.registerBleEventCallback(bleEventCallback) |
||||
|
// } else { |
||||
|
|
||||
|
// } |
||||
|
} |
||||
|
|
||||
|
fun destroy() { |
||||
|
bleManager.unregisterBleEventCallback(bleEventCallback) |
||||
|
bleManager.destroy() |
||||
|
|
||||
|
btEventCbHelper.release() |
||||
|
instance = null |
||||
|
} |
||||
|
|
||||
|
fun registerCallback(callback: OnBTEventCallback) { |
||||
|
btEventCbHelper.registerCallback(callback) |
||||
|
} |
||||
|
|
||||
|
fun unregisterCallback(callback: OnBTEventCallback) { |
||||
|
btEventCbHelper.unregisterCallback(callback) |
||||
|
} |
||||
|
|
||||
|
fun isConnected(): Boolean { |
||||
|
return getConnectedDevice() != null |
||||
|
} |
||||
|
|
||||
|
fun isDeviceConnected(device: BluetoothDevice?): Boolean { |
||||
|
return BluetoothUtil.deviceEquals(getConnectedDevice(), device) |
||||
|
} |
||||
|
|
||||
|
fun isScanning(): Boolean = |
||||
|
bleManager.isBleScanning |
||||
|
|
||||
|
|
||||
|
fun isConnecting(): Boolean = |
||||
|
bleManager.isBleScanning |
||||
|
|
||||
|
|
||||
|
fun getConnectedDevice(): BluetoothDevice? { |
||||
|
|
||||
|
return bleManager.connectedBtDevice |
||||
|
|
||||
|
} |
||||
|
|
||||
|
fun getConnectedGatt(): BluetoothGatt? { |
||||
|
return if (configHelper.isBleWay()) { |
||||
|
bleManager.getConnectedBtGatt(getConnectedDevice()) |
||||
|
} else { |
||||
|
null |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fun getBleMtu(): Int { |
||||
|
return if (configHelper.isBleWay()) { |
||||
|
return bleManager.getBleMtu(getConnectedDevice()) |
||||
|
} else { |
||||
|
20 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fun startScan(device: BluetoothDevice?) { |
||||
|
Log.i(TAG, ", startScan" + device); |
||||
|
|
||||
|
bleManager.startLeScan(device) |
||||
|
|
||||
|
} |
||||
|
|
||||
|
fun stopScan() { |
||||
|
|
||||
|
bleManager.stopLeScan() |
||||
|
|
||||
|
} |
||||
|
|
||||
|
fun connectDevice(device: BluetoothDevice?): Boolean = |
||||
|
|
||||
|
bleManager.connectBleDevice(device) |
||||
|
|
||||
|
|
||||
|
fun disconnectDevice(device: BluetoothDevice?) { |
||||
|
|
||||
|
bleManager.disconnectBleDevice(device) |
||||
|
|
||||
|
} |
||||
|
|
||||
|
fun connectBleDevice(device: BluetoothDevice?): Boolean = |
||||
|
bleManager.connectBleDevice(device) |
||||
|
|
||||
|
fun reconnectDevice(address: String?, isUseNewAdv: Boolean) { |
||||
|
if (configHelper.isBleWay()) { |
||||
|
bleManager.reconnectDevice(address, isUseNewAdv) |
||||
|
} else { |
||||
|
//TODO:需要增加SPP自定义回连方式 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fun writeDataToDevice(bluetoothDevice: BluetoothDevice?, byteArray: ByteArray?): Boolean { |
||||
|
if (null == bluetoothDevice || null == byteArray || byteArray.isEmpty()) return false |
||||
|
if (bleManager.connectedBtDevice != null) {//目前连接的设备是ble |
||||
|
bleManager.writeDataByBleAsync( |
||||
|
bluetoothDevice, |
||||
|
BleManager.BLE_UUID_SERVICE, |
||||
|
BleManager.BLE_UUID_WRITE, |
||||
|
byteArray |
||||
|
) { device, serviceUUID, characteristicUUID, result, data -> |
||||
|
Log.i( |
||||
|
TAG, |
||||
|
"-writeDataByBleAsync- device:${ |
||||
|
printDeviceInfo(device) |
||||
|
}, result = $result,\ndata:[${ |
||||
|
CHexConver.byte2HexStr(data) |
||||
|
}]" |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
private fun printDeviceInfo(device: BluetoothDevice?): String? { |
||||
|
//return AppUtil.printBtDeviceInfo(device) |
||||
|
//注释掉 |
||||
|
return "返回设备信息" |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
package com.jieli.otasdk.tool.bluetooth |
||||
|
|
||||
|
/** |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2022/9/14 |
||||
|
* @email zhongzhuocheng@zh-jieli.com |
||||
|
* @desc 回调实现类 |
||||
|
*/ |
||||
|
interface CallbackImpl<T> { |
||||
|
|
||||
|
fun onCallback(callback: T) |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
package com.jieli.otasdk.tool.bluetooth |
||||
|
|
||||
|
/** |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2022/9/14 |
||||
|
* @email zhongzhuocheng@zh-jieli.com |
||||
|
* @desc 回调处理类 |
||||
|
*/ |
||||
|
class CallbackRunnable<T> constructor( |
||||
|
private val callbacks: MutableList<T>, |
||||
|
private val impl: CallbackImpl<T>? |
||||
|
) : |
||||
|
Runnable { |
||||
|
|
||||
|
override fun run() { |
||||
|
if (callbacks.isEmpty() || impl == null) return |
||||
|
val temp = mutableListOf<T>() |
||||
|
temp.addAll(callbacks) |
||||
|
temp.forEach { |
||||
|
impl.onCallback(it) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
package com.jieli.otasdk.tool.bluetooth |
||||
|
|
||||
|
import android.bluetooth.BluetoothDevice |
||||
|
import com.jieli.otasdk.tool.ota.ble.model.BleScanInfo |
||||
|
import java.util.* |
||||
|
|
||||
|
/** |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2022/9/14 |
||||
|
* @email zhongzhuocheng@zh-jieli.com |
||||
|
* @desc 蓝牙事件回调 |
||||
|
*/ |
||||
|
open class OnBTEventCallback { |
||||
|
|
||||
|
open fun onAdapterChange(bEnabled: Boolean) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
open fun onDiscoveryChange(bStart: Boolean, scanType: Int) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
open fun onDiscovery(device: BluetoothDevice?, bleScanMessage: BleScanInfo?) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
open fun onDeviceConnection(device: BluetoothDevice?, way: Int, status: Int) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
open fun onReceiveData(device: BluetoothDevice?, way: Int, uuid: UUID?, data: ByteArray?) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
open fun onBleMtuChange(device: BluetoothDevice?, mtu: Int, status: Int) { |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
package com.jieli.otasdk.tool.bluetooth |
||||
|
|
||||
|
import java.util.* |
||||
|
|
||||
|
/** |
||||
|
* 常量声明 |
||||
|
* |
||||
|
* @author zqjasonZhong |
||||
|
* @date 2019/12/30 |
||||
|
*/ |
||||
|
class OtaConstant { |
||||
|
|
||||
|
companion object { |
||||
|
|
||||
|
val UUID_A2DP: UUID = UUID.fromString("0000110b-0000-1000-8000-00805f9b34fb") |
||||
|
val UUID_SPP: UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb") |
||||
|
|
||||
|
//Ble协议 |
||||
|
const val PROTOCOL_BLE = 0 |
||||
|
//Spp协议 |
||||
|
const val PROTOCOL_SPP = 1 |
||||
|
|
||||
|
const val CURRENT_PROTOCOL = PROTOCOL_BLE |
||||
|
|
||||
|
//是否使用设备认证 |
||||
|
const val IS_NEED_DEVICE_AUTH = true |
||||
|
|
||||
|
//是否HID设备连接 |
||||
|
const val HID_DEVICE_WAY = false |
||||
|
|
||||
|
//是否需要自定义连接方式 |
||||
|
const val NEED_CUSTOM_RECONNECT_WAY = false |
||||
|
|
||||
|
//是否使用SPP多通道连接 |
||||
|
const val USE_SPP_MULTIPLE_CHANNEL = false |
||||
|
|
||||
|
//是否使用自动化测试 |
||||
|
const val AUTO_TEST_OTA = false |
||||
|
//自动化测试次数 |
||||
|
const val AUTO_TEST_COUNT = 30 |
||||
|
|
||||
|
//是否自动化测试时允许容错 |
||||
|
const val AUTO_FAULT_TOLERANT = false |
||||
|
//容错次数 |
||||
|
const val AUTO_FAULT_TOLERANT_COUNT = 1 |
||||
|
|
||||
|
//dir |
||||
|
const val DIR_ROOT = "JieLiOTA" |
||||
|
const val DIR_UPGRADE = "upgrade" |
||||
|
const val DIR_LOGCAT = "logcat" |
||||
|
|
||||
|
const val SCAN_TIMEOUT = 16 * 1000L |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,187 @@ |
|||||
|
package com.jieli.otasdk.tool.config |
||||
|
|
||||
|
import android.annotation.SuppressLint |
||||
|
import android.content.Context |
||||
|
import androidx.annotation.IntRange |
||||
|
import com.jieli.jl_bt_ota.constant.BluetoothConstant |
||||
|
import com.jieli.otasdk.tool.bluetooth.OtaConstant |
||||
|
|
||||
|
/** |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2022/9/8 |
||||
|
* @email zhongzhuocheng@zh-jieli.com |
||||
|
* @desc 配置辅助类 |
||||
|
*/ |
||||
|
open class ConfigHelper private constructor(context: Context) { |
||||
|
|
||||
|
companion object { |
||||
|
//download文件夹 |
||||
|
private const val KEY_DOWNLOAD_URI = "download_uri" |
||||
|
|
||||
|
//通讯方式 |
||||
|
private const val KEY_COMMUNICATION_WAY = "communication_way" |
||||
|
|
||||
|
//是否使用设备认证 |
||||
|
private const val KEY_IS_USE_DEVICE_AUTH = "is_use_device_auth" |
||||
|
|
||||
|
//是否HID设备 |
||||
|
private const val KEY_IS_HID_DEVICE = "is_hid_device" |
||||
|
|
||||
|
//是否使用自定义回连方式 |
||||
|
private const val KEY_USE_CUSTOM_RECONNECT_WAY = "use_custom_reconnect_way" |
||||
|
|
||||
|
//BLE的MTU请求值 |
||||
|
private const val KEY_BLE_MTU_VALUE = "ble_mtu_value" |
||||
|
|
||||
|
//是否启用SPP多通道 |
||||
|
private const val KEY_SPP_MULTIPLE_CHANNEL = "spp_multiple_channel" |
||||
|
|
||||
|
//自定义SPP通道 |
||||
|
private const val KEY_SPP_CUSTOM_UUID = "spp_custom_uuid" |
||||
|
|
||||
|
//是否自动测试OTA |
||||
|
private const val KEY_AUTO_TEST_OTA = "auto_test_ota" |
||||
|
|
||||
|
//自动化测试次数 |
||||
|
private const val KEY_AUTO_TEST_COUNT = "auto_test_count" |
||||
|
|
||||
|
//是否自动测试OTA时,允许容错 |
||||
|
private const val KEY_FAULT_TOLERANT = "fault_tolerant" |
||||
|
|
||||
|
//容错次数 |
||||
|
private const val KEY_FAULT_TOLERANT_COUNT = "fault_tolerant_count" |
||||
|
//扫描过滤参数 |
||||
|
private const val KEY_SCAN_FILTER_STRING = "scan_filter_string" |
||||
|
|
||||
|
//开发者模式 |
||||
|
private const val KEY_DEVELOP_MODE = "develop_mode" |
||||
|
|
||||
|
// 广播音箱模式 |
||||
|
private const val KEY_BROADCAST_BOX = "broadcast_box_switch" |
||||
|
@Volatile |
||||
|
private var instance: ConfigHelper? = null |
||||
|
|
||||
|
// 双重校验锁初始化 |
||||
|
fun initialize(context: Context) = instance ?: synchronized(this) { |
||||
|
instance ?: ConfigHelper(context.applicationContext).also { |
||||
|
instance = it |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 安全获取实例方法 |
||||
|
fun getInstance(): ConfigHelper = instance ?: throw IllegalStateException( |
||||
|
"Call ConfigHelper.initialize() first" |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
private val preferences = context.getSharedPreferences("ota_config_data", Context.MODE_PRIVATE) |
||||
|
|
||||
|
fun isBleWay(): Boolean = preferences.getInt( |
||||
|
KEY_COMMUNICATION_WAY, |
||||
|
OtaConstant.CURRENT_PROTOCOL |
||||
|
) == OtaConstant.PROTOCOL_BLE |
||||
|
|
||||
|
fun setBleWay(isBle: Boolean) { |
||||
|
val way = if (isBle) OtaConstant.PROTOCOL_BLE else OtaConstant.PROTOCOL_SPP |
||||
|
preferences.edit().putInt(KEY_COMMUNICATION_WAY, way).apply() |
||||
|
} |
||||
|
|
||||
|
fun isUseDeviceAuth(): Boolean = |
||||
|
preferences.getBoolean(KEY_IS_USE_DEVICE_AUTH, OtaConstant.IS_NEED_DEVICE_AUTH) |
||||
|
|
||||
|
fun setUseDeviceAuth(isAuth: Boolean) { |
||||
|
preferences.edit().putBoolean(KEY_IS_USE_DEVICE_AUTH, isAuth).apply() |
||||
|
} |
||||
|
|
||||
|
fun isHidDevice(): Boolean = |
||||
|
preferences.getBoolean(KEY_IS_HID_DEVICE, OtaConstant.HID_DEVICE_WAY) |
||||
|
|
||||
|
fun setHidDevice(isHid: Boolean) { |
||||
|
preferences.edit().putBoolean(KEY_IS_HID_DEVICE, isHid).apply() |
||||
|
} |
||||
|
|
||||
|
fun isUseCustomReConnectWay(): Boolean = preferences.getBoolean( |
||||
|
KEY_USE_CUSTOM_RECONNECT_WAY, |
||||
|
OtaConstant.NEED_CUSTOM_RECONNECT_WAY |
||||
|
) |
||||
|
|
||||
|
fun setUseCustomReConnectWay(isCustom: Boolean) { |
||||
|
preferences.edit().putBoolean(KEY_USE_CUSTOM_RECONNECT_WAY, isCustom).apply() |
||||
|
} |
||||
|
|
||||
|
fun getBleRequestMtu(): Int = |
||||
|
preferences.getInt(KEY_BLE_MTU_VALUE, 509) |
||||
|
|
||||
|
fun setBleRequestMtu(@IntRange(from = 20, to = 509) mtu: Int) { |
||||
|
preferences.edit().putInt(KEY_BLE_MTU_VALUE, mtu).apply() |
||||
|
} |
||||
|
|
||||
|
fun isUseMultiSppChannel(): Boolean = preferences.getBoolean( |
||||
|
KEY_SPP_MULTIPLE_CHANNEL, |
||||
|
OtaConstant.USE_SPP_MULTIPLE_CHANNEL |
||||
|
) |
||||
|
|
||||
|
fun setUseMultiSppChannel(isUseMulti: Boolean) { |
||||
|
preferences.edit().putBoolean(KEY_SPP_MULTIPLE_CHANNEL, isUseMulti).apply() |
||||
|
} |
||||
|
|
||||
|
fun getCustomSppChannel(): String? = |
||||
|
preferences.getString(KEY_SPP_CUSTOM_UUID, OtaConstant.UUID_SPP.toString()) |
||||
|
|
||||
|
fun setCustomSppChannel(uuid: String?) { |
||||
|
preferences.edit().putString(KEY_SPP_CUSTOM_UUID, uuid).apply() |
||||
|
} |
||||
|
|
||||
|
fun isAutoTest(): Boolean = preferences.getBoolean(KEY_AUTO_TEST_OTA, OtaConstant.AUTO_TEST_OTA) |
||||
|
|
||||
|
fun setAutoTest(isAutoTest: Boolean) { |
||||
|
preferences.edit().putBoolean(KEY_AUTO_TEST_OTA, isAutoTest).apply() |
||||
|
} |
||||
|
|
||||
|
fun getAutoTestCount(): Int = preferences.getInt( |
||||
|
KEY_AUTO_TEST_COUNT, |
||||
|
OtaConstant.AUTO_TEST_COUNT |
||||
|
) |
||||
|
|
||||
|
fun setAutoTestCount(count: Int) { |
||||
|
if (!isAutoTest()) return |
||||
|
preferences.edit().putInt(KEY_AUTO_TEST_COUNT, count).apply() |
||||
|
} |
||||
|
|
||||
|
fun isFaultTolerant(): Boolean = preferences.getBoolean(KEY_FAULT_TOLERANT, OtaConstant.AUTO_FAULT_TOLERANT) |
||||
|
|
||||
|
fun setFaultTolerant(isFaultTolerant: Boolean) { |
||||
|
preferences.edit().putBoolean(KEY_FAULT_TOLERANT, isFaultTolerant).apply() |
||||
|
} |
||||
|
|
||||
|
fun getFaultTolerantCount(): Int = preferences.getInt( |
||||
|
KEY_FAULT_TOLERANT_COUNT, |
||||
|
OtaConstant.AUTO_FAULT_TOLERANT_COUNT |
||||
|
) |
||||
|
|
||||
|
fun setFaultTolerantCount(count: Int) { |
||||
|
if (!isFaultTolerant()) return |
||||
|
preferences.edit().putInt(KEY_FAULT_TOLERANT_COUNT, count).apply() |
||||
|
} |
||||
|
|
||||
|
fun getScanFilter(): String? = |
||||
|
preferences.getString(KEY_SCAN_FILTER_STRING, "") |
||||
|
|
||||
|
fun setScanFilter(scanFilter: String?) { |
||||
|
preferences.edit().putString(KEY_SCAN_FILTER_STRING, scanFilter).apply() |
||||
|
} |
||||
|
|
||||
|
fun isDevelopMode(): Boolean = |
||||
|
preferences.getBoolean(KEY_DEVELOP_MODE, false) |
||||
|
|
||||
|
fun setDevelopMode(developMode: Boolean) { |
||||
|
preferences.edit().putBoolean(KEY_DEVELOP_MODE, developMode).apply() |
||||
|
} |
||||
|
|
||||
|
fun isEnableBroadcastBox(): Boolean = |
||||
|
preferences.getBoolean(KEY_BROADCAST_BOX, false) |
||||
|
|
||||
|
fun enableBroadcastBox(enable: Boolean) { |
||||
|
preferences.edit().putBoolean(KEY_BROADCAST_BOX, enable).apply() |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,149 @@ |
|||||
|
package com.jieli.otasdk.tool.ota |
||||
|
|
||||
|
import android.bluetooth.BluetoothDevice |
||||
|
import android.bluetooth.BluetoothGatt |
||||
|
import android.content.Context |
||||
|
import com.jieli.jl_bt_ota.constant.BluetoothConstant |
||||
|
import com.jieli.jl_bt_ota.constant.StateCode |
||||
|
import com.jieli.jl_bt_ota.impl.BluetoothOTAManager |
||||
|
import com.jieli.jl_bt_ota.impl.RcspAuth |
||||
|
import com.jieli.jl_bt_ota.model.BluetoothOTAConfigure |
||||
|
import com.jieli.jl_bt_ota.util.BluetoothUtil |
||||
|
import com.jieli.jl_bt_ota.util.CHexConver |
||||
|
import com.jieli.otasdk.tool.bluetooth.BluetoothHelper |
||||
|
import com.jieli.otasdk.tool.bluetooth.OnBTEventCallback |
||||
|
import com.jieli.otasdk.tool.config.ConfigHelper |
||||
|
import com.jieli.otasdk.tool.bluetooth.OtaConstant |
||||
|
import java.util.* |
||||
|
import android.util.Log |
||||
|
/** |
||||
|
* 用于RCSP的第三方SDK接入OTA流程 |
||||
|
*/ |
||||
|
class OTAManager(context: Context) : BluetoothOTAManager(context) { |
||||
|
|
||||
|
val configHelper = ConfigHelper.getInstance() |
||||
|
val bluetoothHelper = BluetoothHelper.getInstance(context) |
||||
|
|
||||
|
val btEventCallback = object : OnBTEventCallback() { |
||||
|
|
||||
|
override fun onDeviceConnection(device: BluetoothDevice?, way: Int, status: Int) { |
||||
|
super.onDeviceConnection(device, way, status) |
||||
|
// val connectionState = AppUtil.changeConnectStatus(status) |
||||
|
// Log.i( |
||||
|
// TAG, "onConnection >>> device : ${printDeviceInfo(device)}, way = $way" + |
||||
|
// " status :$status, change status : $connectionState" |
||||
|
// ) |
||||
|
// onBtDeviceConnection(device, connectionState) |
||||
|
//注释掉 |
||||
|
Log.i( |
||||
|
TAG, "onConnection >>> device : ${device}, way = $way" + |
||||
|
" status :$status, change status : 3" |
||||
|
) |
||||
|
onBtDeviceConnection(device, 1) |
||||
|
} |
||||
|
|
||||
|
override fun onReceiveData( |
||||
|
device: BluetoothDevice?, |
||||
|
way: Int, |
||||
|
uuid: UUID?, |
||||
|
data: ByteArray? |
||||
|
) { |
||||
|
super.onReceiveData(device, way, uuid, data) |
||||
|
Log.d( |
||||
|
TAG, "onReceiveData >>> ${printDeviceInfo(device)}, way = $way,\n" + |
||||
|
"uuid = $uuid, data :${CHexConver.byte2HexStr(data)} " |
||||
|
) |
||||
|
// if (way == OtaConstant.PROTOCOL_SPP && SppManager.UUID_SPP != uuid) { |
||||
|
// Log.d( |
||||
|
// TAG, "onReceiveData >>> skip spec" |
||||
|
// ) |
||||
|
// return |
||||
|
// } |
||||
|
onReceiveDeviceData(device, data) |
||||
|
} |
||||
|
|
||||
|
override fun onBleMtuChange(device: BluetoothDevice?, mtu: Int, status: Int) { |
||||
|
super.onBleMtuChange(device, mtu, status) |
||||
|
onMtuChanged(bluetoothHelper.getConnectedGatt(), mtu, status) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
init { |
||||
|
// ConfigHelper.initialize(context) |
||||
|
val bluetoothOption = BluetoothOTAConfigure() |
||||
|
//选择通讯方式 |
||||
|
bluetoothOption.priority = |
||||
|
BluetoothOTAConfigure.PREFER_BLE |
||||
|
|
||||
|
//是否需要自定义回连方式(默认不需要,如需要自定义回连方式,需要客户自行实现) |
||||
|
bluetoothOption.isUseReconnect = |
||||
|
(configHelper.isUseCustomReConnectWay() && configHelper.isHidDevice()) |
||||
|
Log.d( |
||||
|
TAG, "是否启用设备认证流程(与固件工程师确认) >>> ${configHelper.isUseDeviceAuth()} >>> ${bluetoothOption.isUseReconnect} " |
||||
|
) |
||||
|
//是否启用设备认证流程(与固件工程师确认) |
||||
|
bluetoothOption.isUseAuthDevice = configHelper.isUseDeviceAuth() |
||||
|
//设置BLE的MTU |
||||
|
bluetoothOption.mtu = BluetoothConstant.BLE_MTU_MIN |
||||
|
//是否需要改变BLE的MTU |
||||
|
bluetoothOption.isNeedChangeMtu = false |
||||
|
//是否启用杰理服务器(暂时不支持) |
||||
|
bluetoothOption.isUseJLServer = false |
||||
|
|
||||
|
//配置OTA参数 |
||||
|
configure(bluetoothOption) |
||||
|
RcspAuth.setAuthTimeout(5000) |
||||
|
bluetoothHelper.registerCallback(btEventCallback) |
||||
|
if (bluetoothHelper.isConnected()) { |
||||
|
onBtDeviceConnection(bluetoothHelper.getConnectedDevice(), StateCode.CONNECTION_OK) |
||||
|
if (configHelper.isBleWay()) { |
||||
|
onMtuChanged( |
||||
|
bluetoothHelper.getConnectedGatt(), |
||||
|
bluetoothHelper.getBleMtu() + 3, |
||||
|
BluetoothGatt.GATT_SUCCESS |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
override fun getConnectedDevice(): BluetoothDevice? = bluetoothHelper.getConnectedDevice() |
||||
|
|
||||
|
override fun getConnectedBluetoothGatt(): BluetoothGatt? = bluetoothHelper.getConnectedGatt() |
||||
|
|
||||
|
override fun connectBluetoothDevice(bluetoothDevice: BluetoothDevice?) { |
||||
|
//仅仅作为回连设备,回连设备现在仅支持BLE |
||||
|
val result: Boolean = bluetoothHelper.connectBleDevice(bluetoothDevice) |
||||
|
if (!result) { |
||||
|
onBtDeviceConnection(bluetoothDevice, StateCode.CONNECTION_FAILED) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
override fun disconnectBluetoothDevice(bluetoothDevice: BluetoothDevice?) { |
||||
|
bluetoothHelper.disconnectDevice(bluetoothDevice) |
||||
|
} |
||||
|
|
||||
|
override fun sendDataToDevice(bluetoothDevice: BluetoothDevice?, bytes: ByteArray?): Boolean { |
||||
|
Log.d( |
||||
|
TAG, |
||||
|
"sendDataToDevice : device = ${printDeviceInfo(bluetoothDevice)}\n" + |
||||
|
"data = [${CHexConver.byte2HexStr(bytes)}]" |
||||
|
) |
||||
|
return bluetoothHelper.writeDataToDevice(bluetoothDevice, bytes) |
||||
|
} |
||||
|
|
||||
|
override fun release() { |
||||
|
super.release() |
||||
|
bluetoothHelper.unregisterCallback(btEventCallback) |
||||
|
} |
||||
|
|
||||
|
fun setReconnectAddr(addr: String?) { |
||||
|
// setReconnectAddress(addr) |
||||
|
} |
||||
|
|
||||
|
private fun printDeviceInfo(device: BluetoothDevice?): String? { |
||||
|
return BluetoothUtil.printBtDeviceInfo(context, device) |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,175 @@ |
|||||
|
package com.jieli.otasdk.tool.ota.ble; |
||||
|
|
||||
|
import android.bluetooth.BluetoothDevice; |
||||
|
import android.bluetooth.BluetoothGattService; |
||||
|
import android.os.Handler; |
||||
|
import android.os.Looper; |
||||
|
|
||||
|
import com.jieli.otasdk.tool.ota.ble.interfaces.BleEventCallback; |
||||
|
import com.jieli.otasdk.tool.ota.ble.model.BleScanInfo; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
/** |
||||
|
* Ble事件回调管理器 |
||||
|
* |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2020/12/24 |
||||
|
*/ |
||||
|
class BleEventCallbackManager extends BleEventCallback { |
||||
|
private final ArrayList<BleEventCallback> mCallbacks = new ArrayList<>(); |
||||
|
private final Handler mHandler = new Handler(Looper.getMainLooper()); |
||||
|
|
||||
|
public void registerBleEventCallback(BleEventCallback callback) { |
||||
|
if (callback != null && !mCallbacks.contains(callback)) { |
||||
|
mCallbacks.add(callback); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void unregisterBleEventCallback(BleEventCallback callback) { |
||||
|
if (callback != null && !mCallbacks.isEmpty()) { |
||||
|
mCallbacks.remove(callback); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void release() { |
||||
|
mCallbacks.clear(); |
||||
|
mHandler.removeCallbacksAndMessages(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onAdapterChange(final boolean bEnabled) { |
||||
|
callbackBleEvent(new BleEventCallbackImpl() { |
||||
|
@Override |
||||
|
public void onCallback(BleEventCallback callback) { |
||||
|
callback.onAdapterChange(bEnabled); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onDiscoveryBleChange(final boolean bStart) { |
||||
|
callbackBleEvent(new BleEventCallbackImpl() { |
||||
|
@Override |
||||
|
public void onCallback(BleEventCallback callback) { |
||||
|
callback.onDiscoveryBleChange(bStart); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onDiscoveryBle(final BluetoothDevice device, final BleScanInfo bleScanMessage) { |
||||
|
callbackBleEvent(new BleEventCallbackImpl() { |
||||
|
@Override |
||||
|
public void onCallback(BleEventCallback callback) { |
||||
|
callback.onDiscoveryBle(device, bleScanMessage); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleConnection(final BluetoothDevice device, final int status) { |
||||
|
callbackBleEvent(new BleEventCallbackImpl() { |
||||
|
@Override |
||||
|
public void onCallback(BleEventCallback callback) { |
||||
|
callback.onBleConnection(device, status); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleServiceDiscovery(final BluetoothDevice device, final int status, final List<BluetoothGattService> services) { |
||||
|
callbackBleEvent(new BleEventCallbackImpl() { |
||||
|
@Override |
||||
|
public void onCallback(BleEventCallback callback) { |
||||
|
callback.onBleServiceDiscovery(device, status, services); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleNotificationStatus(final BluetoothDevice device, final UUID serviceUuid, final UUID characteristicUuid, final int status) { |
||||
|
callbackBleEvent(new BleEventCallbackImpl() { |
||||
|
@Override |
||||
|
public void onCallback(BleEventCallback callback) { |
||||
|
callback.onBleNotificationStatus(device, serviceUuid, characteristicUuid, status); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleDataBlockChanged(final BluetoothDevice device, final int block, final int status) { |
||||
|
callbackBleEvent(new BleEventCallbackImpl() { |
||||
|
@Override |
||||
|
public void onCallback(BleEventCallback callback) { |
||||
|
callback.onBleDataBlockChanged(device, block, status); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleDataNotification(final BluetoothDevice device, final UUID serviceUuid, final UUID characteristicsUuid, final byte[] data) { |
||||
|
callbackBleEvent(new BleEventCallbackImpl() { |
||||
|
@Override |
||||
|
public void onCallback(BleEventCallback callback) { |
||||
|
callback.onBleDataNotification(device, serviceUuid, characteristicsUuid, data); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleWriteStatus(final BluetoothDevice device, final UUID serviceUuid, final UUID characteristicsUuid, final byte[] data, final int status) { |
||||
|
callbackBleEvent(new BleEventCallbackImpl() { |
||||
|
@Override |
||||
|
public void onCallback(BleEventCallback callback) { |
||||
|
callback.onBleWriteStatus(device, serviceUuid, characteristicsUuid, data, status); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onConnectionUpdated(final BluetoothDevice device, final int interval, final int latency, final int timeout, final int status) { |
||||
|
callbackBleEvent(new BleEventCallbackImpl() { |
||||
|
@Override |
||||
|
public void onCallback(BleEventCallback callback) { |
||||
|
callback.onConnectionUpdated(device, interval, latency, timeout, status); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private void callbackBleEvent(BleEventCallbackImpl impl) { |
||||
|
if (null == impl) return; |
||||
|
OnBleEventRunnable runnable = new OnBleEventRunnable(impl); |
||||
|
if (Thread.currentThread().getId() == Looper.getMainLooper().getThread().getId()) { |
||||
|
runnable.run(); |
||||
|
} else { |
||||
|
mHandler.post(runnable); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static abstract class BleEventCallbackImpl { |
||||
|
|
||||
|
public abstract void onCallback(BleEventCallback callback); |
||||
|
} |
||||
|
|
||||
|
private class OnBleEventRunnable implements Runnable { |
||||
|
private final BleEventCallbackImpl mImpl; |
||||
|
|
||||
|
public OnBleEventRunnable(BleEventCallbackImpl impl) { |
||||
|
mImpl = impl; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void run() { |
||||
|
if (!mCallbacks.isEmpty() && mImpl != null) { |
||||
|
for (BleEventCallback callback : new ArrayList<>(mCallbacks)) { |
||||
|
if (callback != null) { |
||||
|
mImpl.onCallback(callback); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,295 @@ |
|||||
|
package com.jieli.otasdk.tool.ota.ble; |
||||
|
|
||||
|
import android.bluetooth.BluetoothGatt; |
||||
|
|
||||
|
import com.jieli.jl_bt_ota.util.JL_Log; |
||||
|
import com.jieli.otasdk.tool.ota.ble.interfaces.IBleOp; |
||||
|
import com.jieli.otasdk.tool.ota.ble.interfaces.OnThreadStateListener; |
||||
|
import com.jieli.otasdk.tool.ota.ble.interfaces.OnWriteDataCallback; |
||||
|
|
||||
|
import java.util.Arrays; |
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.LinkedBlockingQueue; |
||||
|
|
||||
|
/** |
||||
|
* 发送BLE数据线程 |
||||
|
* |
||||
|
* @author zqjasonZhong |
||||
|
* @date 2019/10/8 |
||||
|
*/ |
||||
|
public class SendBleDataThread extends Thread { |
||||
|
private final static String TAG = "SendBleDataThread"; |
||||
|
private final LinkedBlockingQueue<BleSendTask> mQueue = new LinkedBlockingQueue<>(); |
||||
|
private volatile boolean isDataSend = false; |
||||
|
private volatile boolean isThreadWaiting = false; |
||||
|
private volatile boolean isWaitingForCallback = false; |
||||
|
private volatile int retryNum = 0; |
||||
|
private final IBleOp mBleManager; |
||||
|
private final OnThreadStateListener mListener; |
||||
|
|
||||
|
private BleSendTask mCurrentTask; |
||||
|
|
||||
|
public SendBleDataThread(IBleOp manager, OnThreadStateListener listener) { |
||||
|
super(TAG); |
||||
|
mBleManager = manager; |
||||
|
mListener = listener; |
||||
|
} |
||||
|
|
||||
|
public boolean isRunning() { |
||||
|
return isDataSend; |
||||
|
} |
||||
|
|
||||
|
public boolean addSendTask(BluetoothGatt gatt, UUID serviceUUID, UUID characteristicUUID, byte[] data, OnWriteDataCallback callback) { |
||||
|
if (null == mBleManager || null == gatt || null == serviceUUID || null == characteristicUUID || null == data || data.length == 0) { |
||||
|
return false; |
||||
|
} |
||||
|
int mtu = mBleManager.getBleMtu(); |
||||
|
JL_Log.d(TAG, "addSendTask : " + mtu); |
||||
|
int dataLen = data.length; |
||||
|
int blockCount = dataLen / mtu; |
||||
|
boolean ret = false; |
||||
|
for (int i = 0; i < blockCount; i++) { |
||||
|
byte[] mBlockData = new byte[mtu]; |
||||
|
System.arraycopy(data, i * mtu, mBlockData, 0, mBlockData.length); |
||||
|
ret = addSendData(gatt, serviceUUID, characteristicUUID, mBlockData, callback); |
||||
|
} |
||||
|
|
||||
|
if (0 != dataLen % mtu) { |
||||
|
JL_Log.d(TAG, "addSendTask : " + mtu); |
||||
|
byte[] noBlockData = new byte[dataLen % mtu]; |
||||
|
System.arraycopy(data, dataLen - dataLen % mtu, noBlockData, 0, noBlockData.length); |
||||
|
ret = addSendData(gatt, serviceUUID, characteristicUUID, noBlockData, callback); |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
public void wakeupSendThread(BleSendTask sendTask) { |
||||
|
if (null == sendTask || mCurrentTask != null && mCurrentTask.equals(sendTask)) { |
||||
|
if (sendTask != null) { |
||||
|
sendTask.setCallback(mCurrentTask.getCallback()); |
||||
|
mCurrentTask = sendTask; |
||||
|
} |
||||
|
synchronized (mQueue) { |
||||
|
if (isThreadWaiting) { |
||||
|
if (isWaitingForCallback) { |
||||
|
mQueue.notifyAll(); |
||||
|
} else { |
||||
|
mQueue.notify(); |
||||
|
} |
||||
|
} else if (isWaitingForCallback) { |
||||
|
mQueue.notify(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private boolean addSendData(BluetoothGatt gatt, UUID serviceUUID, UUID characteristicUUID, byte[] data, OnWriteDataCallback callback) { |
||||
|
boolean ret = false; |
||||
|
if (isDataSend) { |
||||
|
BleSendTask sendTask = new BleSendTask(gatt, serviceUUID, characteristicUUID, data, callback); |
||||
|
try { |
||||
|
mQueue.put(sendTask); |
||||
|
ret = true; |
||||
|
} catch (InterruptedException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
if (ret && isThreadWaiting && !isWaitingForCallback) { |
||||
|
isThreadWaiting = false; |
||||
|
synchronized (mQueue) { |
||||
|
mQueue.notify(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public synchronized void start() { |
||||
|
isDataSend = true; |
||||
|
super.start(); |
||||
|
} |
||||
|
|
||||
|
public synchronized void stopThread() { |
||||
|
isDataSend = false; |
||||
|
wakeupSendThread(null); |
||||
|
} |
||||
|
|
||||
|
private void callbackResult(BleSendTask task, boolean result) { |
||||
|
if (task != null && task.getCallback() != null) { |
||||
|
if (task.getBleGatt() == null) return; |
||||
|
task.getCallback().onBleResult(task.getBleGatt().getDevice(), task.getServiceUUID(), |
||||
|
task.getCharacteristicUUID(), result, task.getData()); |
||||
|
} else { |
||||
|
JL_Log.i(TAG, "getCallback is null."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void run() { |
||||
|
JL_Log.d(TAG, "send ble data thread is started."); |
||||
|
if (mListener != null) { |
||||
|
mListener.onStart(getId(), getName()); |
||||
|
} |
||||
|
if (mBleManager != null) { |
||||
|
synchronized (mQueue) { |
||||
|
while (isDataSend) { |
||||
|
mCurrentTask = null; |
||||
|
isThreadWaiting = false; |
||||
|
isWaitingForCallback = false; |
||||
|
if (mQueue.isEmpty()) { |
||||
|
isThreadWaiting = true; |
||||
|
JL_Log.d(TAG, "queue is empty, so waiting for data"); |
||||
|
try { |
||||
|
mQueue.wait(); |
||||
|
} catch (InterruptedException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} else { |
||||
|
mCurrentTask = mQueue.peek(); |
||||
|
if (mCurrentTask != null) { |
||||
|
isWaitingForCallback = mBleManager.writeDataByBle(mCurrentTask.mGatt, mCurrentTask.getServiceUUID(), |
||||
|
mCurrentTask.getCharacteristicUUID(), mCurrentTask.getData()); |
||||
|
if (isWaitingForCallback) { |
||||
|
try { |
||||
|
mQueue.wait(BleManager.SEND_DATA_MAX_TIMEOUT); |
||||
|
} catch (InterruptedException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} else { |
||||
|
mCurrentTask.setStatus(-1); |
||||
|
} |
||||
|
JL_Log.d(TAG, "data send ret :" + mCurrentTask.getStatus()); |
||||
|
if (mCurrentTask.getStatus() != BluetoothGatt.GATT_SUCCESS) { //发送失败
|
||||
|
retryNum++; |
||||
|
if (retryNum >= 3) { //重发次数超过限制
|
||||
|
callbackResult(mCurrentTask, false); |
||||
|
mQueue.clear(); |
||||
|
} else { |
||||
|
if (mCurrentTask.getStatus() != -1) { |
||||
|
mCurrentTask.setStatus(-1); |
||||
|
try { |
||||
|
sleep(10); |
||||
|
} catch (InterruptedException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
} else { //发送成功
|
||||
|
callbackResult(mCurrentTask, true); |
||||
|
} |
||||
|
} |
||||
|
retryNum = 0; |
||||
|
if (!mQueue.isEmpty()) mQueue.poll(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
isWaitingForCallback = false; |
||||
|
isThreadWaiting = false; |
||||
|
mQueue.clear(); |
||||
|
if (mListener != null) { |
||||
|
mListener.onEnd(getId(), getName()); |
||||
|
} |
||||
|
JL_Log.d(TAG, "send ble data thread exit."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static class BleSendTask { |
||||
|
private BluetoothGatt mGatt; |
||||
|
private UUID serviceUUID; |
||||
|
private UUID characteristicUUID; |
||||
|
private byte[] data; |
||||
|
private int status = -1; |
||||
|
private OnWriteDataCallback mCallback; |
||||
|
|
||||
|
public BleSendTask(BluetoothGatt gatt, UUID serviceUUID, UUID characteristicUUID, byte[] data, OnWriteDataCallback callback) { |
||||
|
this.mGatt = gatt; |
||||
|
this.serviceUUID = serviceUUID; |
||||
|
this.characteristicUUID = characteristicUUID; |
||||
|
this.data = data; |
||||
|
this.mCallback = callback; |
||||
|
} |
||||
|
|
||||
|
public BluetoothGatt getBleGatt() { |
||||
|
return mGatt; |
||||
|
} |
||||
|
|
||||
|
public void setDevice(BluetoothGatt gatt) { |
||||
|
mGatt = gatt; |
||||
|
} |
||||
|
|
||||
|
public UUID getServiceUUID() { |
||||
|
return serviceUUID; |
||||
|
} |
||||
|
|
||||
|
public void setServiceUUID(UUID serviceUUID) { |
||||
|
this.serviceUUID = serviceUUID; |
||||
|
} |
||||
|
|
||||
|
public UUID getCharacteristicUUID() { |
||||
|
return characteristicUUID; |
||||
|
} |
||||
|
|
||||
|
public void setCharacteristicUUID(UUID characteristicUUID) { |
||||
|
this.characteristicUUID = characteristicUUID; |
||||
|
} |
||||
|
|
||||
|
public byte[] getData() { |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public void setData(byte[] data) { |
||||
|
this.data = data; |
||||
|
} |
||||
|
|
||||
|
public OnWriteDataCallback getCallback() { |
||||
|
return mCallback; |
||||
|
} |
||||
|
|
||||
|
public void setCallback(OnWriteDataCallback callback) { |
||||
|
mCallback = callback; |
||||
|
} |
||||
|
|
||||
|
public int getStatus() { |
||||
|
return status; |
||||
|
} |
||||
|
|
||||
|
public void setStatus(int status) { |
||||
|
this.status = status; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "BleSendTask{" + |
||||
|
"mGatt=" + mGatt + |
||||
|
", serviceUUID=" + serviceUUID + |
||||
|
", characteristicUUID=" + characteristicUUID + |
||||
|
", data=" + Arrays.toString(data) + |
||||
|
", status=" + status + |
||||
|
", mCallback=" + mCallback + |
||||
|
'}'; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int hashCode() { |
||||
|
if (mGatt != null && serviceUUID != null && characteristicUUID != null) { |
||||
|
return mGatt.hashCode() + serviceUUID.hashCode() + characteristicUUID.hashCode(); |
||||
|
} |
||||
|
return super.hashCode(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean equals(Object obj) { |
||||
|
if (obj instanceof BleSendTask) { |
||||
|
BleSendTask other = (BleSendTask) obj; |
||||
|
if (mGatt != null && serviceUUID != null && characteristicUUID != null) { |
||||
|
return mGatt.equals(other.getBleGatt()) && serviceUUID.equals(other.getServiceUUID()) |
||||
|
&& characteristicUUID.equals(other.getCharacteristicUUID()); |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
package com.jieli.otasdk.tool.ota.ble.interfaces; |
||||
|
|
||||
|
import android.bluetooth.BluetoothDevice; |
||||
|
import android.bluetooth.BluetoothGattService; |
||||
|
|
||||
|
import com.jieli.otasdk.tool.ota.ble.model.BleScanInfo; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
/** |
||||
|
* Ble事件回调抽象类 |
||||
|
* |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2020/7/16 |
||||
|
*/ |
||||
|
public abstract class BleEventCallback implements IBleEventCallback { |
||||
|
|
||||
|
@Override |
||||
|
public void onAdapterChange(boolean bEnabled) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onDiscoveryBleChange(boolean bStart) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onDiscoveryBle(BluetoothDevice device, BleScanInfo bleScanMessage) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleConnection(BluetoothDevice device, int status) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleServiceDiscovery(BluetoothDevice device, int status, List<BluetoothGattService> services) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleNotificationStatus(BluetoothDevice device, UUID serviceUuid, UUID characteristicUuid, int status) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleDataBlockChanged(BluetoothDevice device, int block, int status) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleDataNotification(BluetoothDevice device, UUID serviceUuid, UUID characteristicsUuid, byte[] data) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBleWriteStatus(BluetoothDevice device, UUID serviceUuid, UUID characteristicsUuid, byte[] data, int status) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onConnectionUpdated(BluetoothDevice device, int interval, int latency, int timeout, int status) { |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,108 @@ |
|||||
|
package com.jieli.otasdk.tool.ota.ble.interfaces; |
||||
|
|
||||
|
import android.bluetooth.BluetoothDevice; |
||||
|
import android.bluetooth.BluetoothGattService; |
||||
|
|
||||
|
import com.jieli.otasdk.tool.ota.ble.model.BleScanInfo; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
/** |
||||
|
* Ble事件回调 |
||||
|
* |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2020/7/16 |
||||
|
*/ |
||||
|
public interface IBleEventCallback { |
||||
|
|
||||
|
/** |
||||
|
* 蓝牙适配器开关回调 |
||||
|
* |
||||
|
* @param bEnabled 开关 |
||||
|
*/ |
||||
|
void onAdapterChange(boolean bEnabled); |
||||
|
|
||||
|
/** |
||||
|
* 搜索蓝牙设备的状态回调 |
||||
|
* |
||||
|
* @param bStart 搜索状态 |
||||
|
*/ |
||||
|
void onDiscoveryBleChange(boolean bStart); |
||||
|
|
||||
|
/** |
||||
|
* 发现蓝牙设备的回调 |
||||
|
* |
||||
|
* @param device 蓝牙设备对象 |
||||
|
* @param bleScanInfo BLE扫描数据 |
||||
|
*/ |
||||
|
void onDiscoveryBle(BluetoothDevice device, BleScanInfo bleScanInfo); |
||||
|
|
||||
|
/** |
||||
|
* BLE连接状态回调 |
||||
|
* |
||||
|
* @param device 蓝牙设备对象 |
||||
|
* @param status 连接状态 |
||||
|
*/ |
||||
|
void onBleConnection(BluetoothDevice device, int status); |
||||
|
|
||||
|
/** |
||||
|
* BLE服务发现回调 |
||||
|
* |
||||
|
* @param device 蓝牙设备对象 |
||||
|
* @param status 状态 |
||||
|
* @param services 服务列表 |
||||
|
*/ |
||||
|
void onBleServiceDiscovery(BluetoothDevice device, int status, List<BluetoothGattService> services); |
||||
|
|
||||
|
/** |
||||
|
* BLE特征状态回调 |
||||
|
* |
||||
|
* @param device 蓝牙设备对象 |
||||
|
* @param serviceUuid 服务UUID |
||||
|
* @param characteristicUuid 特征值UUID |
||||
|
* @param status 状态 |
||||
|
*/ |
||||
|
void onBleNotificationStatus(BluetoothDevice device, UUID serviceUuid, UUID characteristicUuid, int status); |
||||
|
|
||||
|
/** |
||||
|
* BLE MTU改变回调 |
||||
|
* |
||||
|
* @param device 蓝牙设备对象 |
||||
|
* @param block 协商后的MTU |
||||
|
* @param status 状态 |
||||
|
*/ |
||||
|
void onBleDataBlockChanged(BluetoothDevice device, int block, int status); |
||||
|
|
||||
|
/** |
||||
|
* BLE数据回调 |
||||
|
* |
||||
|
* @param device 蓝牙设备对象 |
||||
|
* @param serviceUuid 服务UUID |
||||
|
* @param characteristicsUuid 特征值UUID |
||||
|
* @param data 数据 |
||||
|
*/ |
||||
|
void onBleDataNotification(BluetoothDevice device, UUID serviceUuid, UUID characteristicsUuid, byte[] data); |
||||
|
|
||||
|
/** |
||||
|
* BLE 写数据回调 |
||||
|
* |
||||
|
* @param device 蓝牙设备对象 |
||||
|
* @param serviceUuid 服务UUID |
||||
|
* @param characteristicsUuid 特征值UUID |
||||
|
* @param data 数据 |
||||
|
* @param status 状态 |
||||
|
*/ |
||||
|
void onBleWriteStatus(BluetoothDevice device, UUID serviceUuid, UUID characteristicsUuid, byte[] data, int status); |
||||
|
|
||||
|
/** |
||||
|
* BLE 连接参数回调 |
||||
|
* |
||||
|
* @param device 蓝牙设备对象 |
||||
|
* @param interval 发数间隔 |
||||
|
* @param latency 延时参数 |
||||
|
* @param timeout 超时时间 |
||||
|
* @param status 状态 |
||||
|
*/ |
||||
|
void onConnectionUpdated(BluetoothDevice device, int interval, int latency, int timeout, int status); |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
package com.jieli.otasdk.tool.ota.ble.interfaces; |
||||
|
|
||||
|
import android.bluetooth.BluetoothGatt; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
/** |
||||
|
* @author zqjasonZhong |
||||
|
* @since 2020/12/24 |
||||
|
*/ |
||||
|
public interface IBleOp { |
||||
|
|
||||
|
int getBleMtu(); |
||||
|
|
||||
|
boolean writeDataByBle(BluetoothGatt gatt, UUID serviceUUID, UUID characteristicUUID, byte[] data); |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
package com.jieli.otasdk.tool.ota.ble.interfaces; |
||||
|
|
||||
|
/** |
||||
|
* 线程生命周期监听器 |
||||
|
* |
||||
|
* @author zqjasonZhong |
||||
|
* @date 2019/9/20 |
||||
|
*/ |
||||
|
public interface OnThreadStateListener { |
||||
|
|
||||
|
void onStart(long id, String name); |
||||
|
|
||||
|
void onEnd(long id, String name); |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
package com.jieli.otasdk.tool.ota.ble.interfaces; |
||||
|
|
||||
|
import android.bluetooth.BluetoothDevice; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
/** |
||||
|
* 写数据的结果回调 |
||||
|
* |
||||
|
* @author zqjasonZhong |
||||
|
* @date 2019/9/25 |
||||
|
*/ |
||||
|
public interface OnWriteDataCallback { |
||||
|
|
||||
|
/** |
||||
|
* 回调BLE发送结果 |
||||
|
* |
||||
|
* @param device 蓝牙设备 |
||||
|
* @param serviceUUID 服务UUID |
||||
|
* @param characteristicUUID 特征值UUID |
||||
|
* @param result 发送结果 |
||||
|
* @param data 发送数据 |
||||
|
*/ |
||||
|
void onBleResult(BluetoothDevice device, UUID serviceUUID, UUID characteristicUUID, boolean result, byte[] data); |
||||
|
} |
||||
@ -0,0 +1,155 @@ |
|||||
|
package com.jieli.otasdk.tool.ota.ble.model; |
||||
|
|
||||
|
import android.annotation.SuppressLint; |
||||
|
import android.bluetooth.BluetoothGatt; |
||||
|
import android.bluetooth.BluetoothGattCharacteristic; |
||||
|
import android.bluetooth.BluetoothGattService; |
||||
|
import android.content.Context; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
|
||||
|
import com.jieli.jl_bt_ota.constant.BluetoothConstant; |
||||
|
import com.jieli.jl_bt_ota.util.CHexConver; |
||||
|
|
||||
|
import com.jieli.otasdk.tool.ota.ble.SendBleDataThread; |
||||
|
import com.jieli.otasdk.tool.ota.ble.interfaces.IBleOp; |
||||
|
import com.jieli.otasdk.tool.ota.ble.interfaces.OnThreadStateListener; |
||||
|
import com.jieli.otasdk.tool.ota.ble.interfaces.OnWriteDataCallback; |
||||
|
//import com.jieli.otasdk.util.AppUtil;
|
||||
|
|
||||
|
import java.util.UUID; |
||||
|
import android.util.Log; |
||||
|
/** |
||||
|
* @author zqjasonZhong |
||||
|
* @email zhongzhuocheng@zh-jieli.com |
||||
|
* @desc BLE设备 |
||||
|
* @since 2022/12/5 |
||||
|
*/ |
||||
|
public class BleDevice { |
||||
|
private final String tag = "BleManager"; |
||||
|
private final Context context; //上下文
|
||||
|
private final BluetoothGatt gatt; //BleGatt控制对象
|
||||
|
private int mtu = BluetoothConstant.BLE_MTU_MIN; //MTU
|
||||
|
private long connectedTime; //连接的时间戳
|
||||
|
|
||||
|
private SendBleDataThread sendDataThread; //发送数据线程
|
||||
|
|
||||
|
public BleDevice(@NonNull Context context, @NonNull BluetoothGatt gatt) { |
||||
|
this.context = context; |
||||
|
this.gatt = gatt; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
public BluetoothGatt getGatt() { |
||||
|
return gatt; |
||||
|
} |
||||
|
|
||||
|
public int getMtu() { |
||||
|
if (mtu > 128) { |
||||
|
mtu -= 6; |
||||
|
} |
||||
|
return mtu; |
||||
|
} |
||||
|
|
||||
|
public void setMtu(int mtu) { |
||||
|
this.mtu = mtu; |
||||
|
} |
||||
|
|
||||
|
public long getConnectedTime() { |
||||
|
return connectedTime; |
||||
|
} |
||||
|
|
||||
|
public void setConnectedTime(long connectedTime) { |
||||
|
this.connectedTime = connectedTime; |
||||
|
} |
||||
|
|
||||
|
public void startSendDataThread() { |
||||
|
if (sendDataThread == null || !sendDataThread.isRunning()) { |
||||
|
sendDataThread = new SendBleDataThread(new IBleOp() { |
||||
|
@Override |
||||
|
public int getBleMtu() { |
||||
|
return getMtu(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean writeDataByBle(BluetoothGatt gatt, UUID serviceUUID, UUID characteristicUUID, byte[] data) { |
||||
|
Log.d(tag, "writeDataByBle " + gatt +"serviceUUID "+ serviceUUID + "characteristicUUID "+ characteristicUUID + data ); |
||||
|
return writeDataToDeviceByBle(gatt, serviceUUID, characteristicUUID, data); |
||||
|
} |
||||
|
}, new OnThreadStateListener() { |
||||
|
@Override |
||||
|
public void onStart(long id, String name) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onEnd(long id, String name) { |
||||
|
if (sendDataThread != null && sendDataThread.getId() == id) { |
||||
|
sendDataThread = null; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
sendDataThread.start(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void stopSendDataThread() { |
||||
|
if (sendDataThread != null) { |
||||
|
sendDataThread.stopThread(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void wakeupSendThread(SendBleDataThread.BleSendTask task) { |
||||
|
if (sendDataThread != null && task != null && gatt.equals(task.getBleGatt())) { |
||||
|
sendDataThread.wakeupSendThread(task); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public boolean addSendTask(UUID serviceUUID, UUID characteristicUUID, byte[] data, OnWriteDataCallback callback) { |
||||
|
boolean ret = false; |
||||
|
if (sendDataThread != null && sendDataThread.isRunning()) { |
||||
|
ret = sendDataThread.addSendTask(gatt, serviceUUID, characteristicUUID, data, callback); |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
@SuppressLint("MissingPermission") |
||||
|
private boolean writeDataToDeviceByBle(BluetoothGatt gatt, UUID serviceUUID, UUID characteristicUUID, byte[] data) { |
||||
|
if (gatt == null || null == serviceUUID || null == characteristicUUID || null == data || data.length == 0 |
||||
|
// || !AppUtil.checkHasConnectPermission(this.context)
|
||||
|
) { |
||||
|
Log.d(tag, "writeDataByBle : param is invalid."); |
||||
|
return false; |
||||
|
} |
||||
|
BluetoothGattService gattService = gatt.getService(serviceUUID); |
||||
|
if (null == gattService) { |
||||
|
Log.d(tag, "writeDataByBle : service is null."); |
||||
|
return false; |
||||
|
} |
||||
|
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(characteristicUUID); |
||||
|
if (null == gattCharacteristic) { |
||||
|
Log.d(tag, "writeDataByBle : characteristic is null"); |
||||
|
return false; |
||||
|
} |
||||
|
boolean ret = false; |
||||
|
try { |
||||
|
gattCharacteristic.setValue(data); |
||||
|
ret = gatt.writeCharacteristic(gattCharacteristic); |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
Log.d(tag, "writeDataByBle : send ret : " + ret + ", data = " + CHexConver.byte2HexStr(data)); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "BleDevice{" + |
||||
|
"context=" + context + |
||||
|
", gatt=" + gatt + |
||||
|
", mtu=" + mtu + |
||||
|
", connectedTime=" + connectedTime + |
||||
|
", sendDataThread=" + sendDataThread + |
||||
|
'}'; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
package com.jieli.otasdk.tool.ota.ble.model; |
||||
|
|
||||
|
|
||||
|
import android.os.Parcel; |
||||
|
import android.os.Parcelable; |
||||
|
|
||||
|
import com.jieli.jl_bt_ota.util.CHexConver; |
||||
|
|
||||
|
/** |
||||
|
* BLE 设备扫描信息 |
||||
|
* <p> |
||||
|
* Created by zqjasonzhong on 2018/10/17. |
||||
|
*/ |
||||
|
|
||||
|
public class BleScanInfo implements Parcelable { |
||||
|
/** |
||||
|
* 原始数据 |
||||
|
*/ |
||||
|
private byte[] rawData; |
||||
|
/** |
||||
|
* 信号强度 |
||||
|
*/ |
||||
|
private int rssi; |
||||
|
/** |
||||
|
* 是否允许连接 |
||||
|
* <p>说明: 默认允许连接,特殊情况不允许</p> |
||||
|
*/ |
||||
|
private boolean isEnableConnect = true; |
||||
|
|
||||
|
public BleScanInfo() { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected BleScanInfo(Parcel in) { |
||||
|
rawData = in.createByteArray(); |
||||
|
rssi = in.readInt(); |
||||
|
isEnableConnect = in.readByte() != 0; |
||||
|
} |
||||
|
|
||||
|
public static final Creator<BleScanInfo> CREATOR = new Creator<BleScanInfo>() { |
||||
|
@Override |
||||
|
public BleScanInfo createFromParcel(Parcel in) { |
||||
|
return new BleScanInfo(in); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public BleScanInfo[] newArray(int size) { |
||||
|
return new BleScanInfo[size]; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
public byte[] getRawData() { |
||||
|
return rawData; |
||||
|
} |
||||
|
|
||||
|
public BleScanInfo setRawData(byte[] rawData) { |
||||
|
this.rawData = rawData; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public int getRssi() { |
||||
|
return rssi; |
||||
|
} |
||||
|
|
||||
|
public BleScanInfo setRssi(int rssi) { |
||||
|
this.rssi = rssi; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public boolean isEnableConnect() { |
||||
|
return isEnableConnect; |
||||
|
} |
||||
|
|
||||
|
public BleScanInfo setEnableConnect(boolean enableConnect) { |
||||
|
isEnableConnect = enableConnect; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int describeContents() { |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void writeToParcel(Parcel dest, int flags) { |
||||
|
dest.writeByteArray(rawData); |
||||
|
dest.writeInt(rssi); |
||||
|
dest.writeByte((byte) (isEnableConnect ? 1 : 0)); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "BleScanMessage{" + |
||||
|
"rawData=" + CHexConver.byte2HexStr(rawData) + |
||||
|
", rssi=" + rssi + |
||||
|
", isEnableConnect=" + isEnableConnect + |
||||
|
'}'; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
.idea/ |
||||
|
.vagrant/ |
||||
|
.sconsign.dblite |
||||
|
.svn/ |
||||
|
|
||||
|
.DS_Store |
||||
|
*.swp |
||||
|
profile |
||||
|
|
||||
|
DerivedData/ |
||||
|
build/ |
||||
|
GeneratedPluginRegistrant.h |
||||
|
GeneratedPluginRegistrant.m |
||||
|
|
||||
|
.generated/ |
||||
|
|
||||
|
*.pbxuser |
||||
|
*.mode1v3 |
||||
|
*.mode2v3 |
||||
|
*.perspectivev3 |
||||
|
|
||||
|
!default.pbxuser |
||||
|
!default.mode1v3 |
||||
|
!default.mode2v3 |
||||
|
!default.perspectivev3 |
||||
|
|
||||
|
xcuserdata |
||||
|
|
||||
|
*.moved-aside |
||||
|
|
||||
|
*.pyc |
||||
|
*sync/ |
||||
|
Icon? |
||||
|
.tags* |
||||
|
|
||||
|
/Flutter/Generated.xcconfig |
||||
|
/Flutter/ephemeral/ |
||||
|
/Flutter/flutter_export_environment.sh |
||||
@ -0,0 +1,19 @@ |
|||||
|
import Flutter |
||||
|
import UIKit |
||||
|
|
||||
|
public class OtaPlugin: NSObject, FlutterPlugin { |
||||
|
public static func register(with registrar: FlutterPluginRegistrar) { |
||||
|
let channel = FlutterMethodChannel(name: "ota", binaryMessenger: registrar.messenger()) |
||||
|
let instance = OtaPlugin() |
||||
|
registrar.addMethodCallDelegate(instance, channel: channel) |
||||
|
} |
||||
|
|
||||
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
||||
|
switch call.method { |
||||
|
case "getPlatformVersion": |
||||
|
result("iOS " + UIDevice.current.systemVersion) |
||||
|
default: |
||||
|
result(FlutterMethodNotImplemented) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
|
<plist version="1.0"> |
||||
|
<dict> |
||||
|
<key>NSPrivacyTrackingDomains</key> |
||||
|
<array/> |
||||
|
<key>NSPrivacyAccessedAPITypes</key> |
||||
|
<array/> |
||||
|
<key>NSPrivacyCollectedDataTypes</key> |
||||
|
<array/> |
||||
|
<key>NSPrivacyTracking</key> |
||||
|
<false/> |
||||
|
</dict> |
||||
|
</plist> |
||||
@ -0,0 +1,29 @@ |
|||||
|
# |
||||
|
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. |
||||
|
# Run `pod lib lint ota.podspec` to validate before publishing. |
||||
|
# |
||||
|
Pod::Spec.new do |s| |
||||
|
s.name = 'ota' |
||||
|
s.version = '0.0.1' |
||||
|
s.summary = 'A new Flutter project.' |
||||
|
s.description = <<-DESC |
||||
|
A new Flutter project. |
||||
|
DESC |
||||
|
s.homepage = 'http://example.com' |
||||
|
s.license = { :file => '../LICENSE' } |
||||
|
s.author = { 'Your Company' => 'email@example.com' } |
||||
|
s.source = { :path => '.' } |
||||
|
s.source_files = 'Classes/**/*' |
||||
|
s.dependency 'Flutter' |
||||
|
s.platform = :ios, '12.0' |
||||
|
|
||||
|
# Flutter.framework does not contain a i386 slice. |
||||
|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } |
||||
|
s.swift_version = '5.0' |
||||
|
|
||||
|
# If your plugin requires a privacy manifest, for example if it uses any |
||||
|
# required reason APIs, update the PrivacyInfo.xcprivacy file to describe your |
||||
|
# plugin's privacy impact, and then uncomment this line. For more information, |
||||
|
# see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files |
||||
|
# s.resource_bundles = {'ota_privacy' => ['Resources/PrivacyInfo.xcprivacy']} |
||||
|
end |
||||
@ -0,0 +1,19 @@ |
|||||
|
import 'ota_platform_interface.dart'; |
||||
|
|
||||
|
class Ota { |
||||
|
Future<String?> getPlatformVersion() { |
||||
|
return OtaPlatform.instance.getPlatformVersion(); |
||||
|
} |
||||
|
|
||||
|
Future<String?> searchDevices() { |
||||
|
return OtaPlatform.instance.searchDevices(); |
||||
|
} |
||||
|
|
||||
|
Future<String?> connectDevice() { |
||||
|
return OtaPlatform.instance.connectDevice(); |
||||
|
} |
||||
|
|
||||
|
Future<String?> startUpgrade() { |
||||
|
return OtaPlatform.instance.startUpgrade(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
import 'package:flutter/foundation.dart'; |
||||
|
import 'package:flutter/services.dart'; |
||||
|
|
||||
|
import 'ota_platform_interface.dart'; |
||||
|
|
||||
|
/// An implementation of [OtaPlatform] that uses method channels. |
||||
|
class MethodChannelOta extends OtaPlatform { |
||||
|
/// The method channel used to interact with the native platform. |
||||
|
@visibleForTesting |
||||
|
final methodChannel = const MethodChannel('ota'); |
||||
|
|
||||
|
@override |
||||
|
Future<String?> getPlatformVersion() async { |
||||
|
final version = await methodChannel.invokeMethod<String>( |
||||
|
'getPlatformVersion', |
||||
|
); |
||||
|
return version; |
||||
|
} |
||||
|
|
||||
|
Future<String?> searchDevices() async { |
||||
|
final version = await methodChannel.invokeMethod<String>('searchDevices'); |
||||
|
return version; |
||||
|
} |
||||
|
|
||||
|
Future<String?> connectDevice() async { |
||||
|
final version = await methodChannel.invokeMethod<String>('connectDevice'); |
||||
|
return version; |
||||
|
} |
||||
|
|
||||
|
Future<String?> startUpgrade() async { |
||||
|
final version = await methodChannel.invokeMethod<String>('startUpgrade'); |
||||
|
return version; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart'; |
||||
|
|
||||
|
import 'ota_method_channel.dart'; |
||||
|
|
||||
|
abstract class OtaPlatform extends PlatformInterface { |
||||
|
/// Constructs a OtaPlatform. |
||||
|
OtaPlatform() : super(token: _token); |
||||
|
|
||||
|
static final Object _token = Object(); |
||||
|
|
||||
|
static OtaPlatform _instance = MethodChannelOta(); |
||||
|
|
||||
|
/// The default instance of [OtaPlatform] to use. |
||||
|
/// |
||||
|
/// Defaults to [MethodChannelOta]. |
||||
|
static OtaPlatform get instance => _instance; |
||||
|
|
||||
|
/// Platform-specific implementations should set this with their own |
||||
|
/// platform-specific class that extends [OtaPlatform] when |
||||
|
/// they register themselves. |
||||
|
static set instance(OtaPlatform instance) { |
||||
|
PlatformInterface.verifyToken(instance, _token); |
||||
|
_instance = instance; |
||||
|
} |
||||
|
|
||||
|
Future<String?> getPlatformVersion() { |
||||
|
throw UnimplementedError('platformVersion() has not been implemented.'); |
||||
|
} |
||||
|
|
||||
|
Future<String?> searchDevices() { |
||||
|
throw UnimplementedError('platformVersion() has not been implemented.'); |
||||
|
} |
||||
|
|
||||
|
Future<String?> connectDevice() { |
||||
|
throw UnimplementedError('platformVersion() has not been implemented.'); |
||||
|
} |
||||
|
|
||||
|
Future<String?> startUpgrade() { |
||||
|
throw UnimplementedError('platformVersion() has not been implemented.'); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
name: ota |
||||
|
description: "A new Flutter project." |
||||
|
version: 0.0.1 |
||||
|
homepage: |
||||
|
|
||||
|
environment: |
||||
|
sdk: ^3.7.2 |
||||
|
flutter: '>=3.3.0' |
||||
|
|
||||
|
dependencies: |
||||
|
flutter: |
||||
|
sdk: flutter |
||||
|
plugin_platform_interface: ^2.0.2 |
||||
|
|
||||
|
dev_dependencies: |
||||
|
flutter_test: |
||||
|
sdk: flutter |
||||
|
flutter_lints: ^5.0.0 |
||||
|
|
||||
|
# For information on the generic Dart part of this file, see the |
||||
|
# following page: https://dart.dev/tools/pub/pubspec |
||||
|
|
||||
|
# The following section is specific to Flutter packages. |
||||
|
flutter: |
||||
|
# This section identifies this Flutter project as a plugin project. |
||||
|
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) |
||||
|
# which should be registered in the plugin registry. This is required for |
||||
|
# using method channels. |
||||
|
# The Android 'package' specifies package in which the registered class is. |
||||
|
# This is required for using method channels on Android. |
||||
|
# The 'ffiPlugin' specifies that native code should be built and bundled. |
||||
|
# This is required for using `dart:ffi`. |
||||
|
# All these are used by the tooling to maintain consistency when |
||||
|
# adding or updating assets for this project. |
||||
|
plugin: |
||||
|
platforms: |
||||
|
android: |
||||
|
package: com.example.ota |
||||
|
pluginClass: OtaPlugin |
||||
|
ios: |
||||
|
pluginClass: OtaPlugin |
||||
|
|
||||
|
# To add assets to your plugin package, add an assets section, like this: |
||||
|
# assets: |
||||
|
# - images/a_dot_burr.jpeg |
||||
|
# - images/a_dot_ham.jpeg |
||||
|
# |
||||
|
# For details regarding assets in packages, see |
||||
|
# https://flutter.dev/to/asset-from-package |
||||
|
# |
||||
|
# An image asset can refer to one or more resolution-specific "variants", see |
||||
|
# https://flutter.dev/to/resolution-aware-images |
||||
|
|
||||
|
# To add custom fonts to your plugin package, add a fonts section here, |
||||
|
# in this "flutter" section. Each entry in this list should have a |
||||
|
# "family" key with the font family name, and a "fonts" key with a |
||||
|
# list giving the asset and other descriptors for the font. For |
||||
|
# example: |
||||
|
# fonts: |
||||
|
# - family: Schyler |
||||
|
# fonts: |
||||
|
# - asset: fonts/Schyler-Regular.ttf |
||||
|
# - asset: fonts/Schyler-Italic.ttf |
||||
|
# style: italic |
||||
|
# - family: Trajan Pro |
||||
|
# fonts: |
||||
|
# - asset: fonts/TrajanPro.ttf |
||||
|
# - asset: fonts/TrajanPro_Bold.ttf |
||||
|
# weight: 700 |
||||
|
# |
||||
|
# For details regarding fonts in packages, see |
||||
|
# https://flutter.dev/to/font-from-package |
||||
Loading…
Reference in new issue