Browse Source

加入ota升级demo代码

weicu
fdp 1 year ago
parent
commit
9eb8204e98
  1. 213
      ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
  2. 3
      lib/data/services/network/nw_api.dart
  3. 7
      lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart
  4. 85
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt
  5. 2
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt
  6. 33
      local_plugins/ota/.gitignore
  7. 33
      local_plugins/ota/.metadata
  8. 3
      local_plugins/ota/CHANGELOG.md
  9. 1
      local_plugins/ota/LICENSE
  10. 15
      local_plugins/ota/README.md
  11. 4
      local_plugins/ota/analysis_options.yaml
  12. 9
      local_plugins/ota/android/.gitignore
  13. 68
      local_plugins/ota/android/build.gradle
  14. BIN
      local_plugins/ota/android/libs/jl_bt_ota_V1.9.2-release.aar
  15. 1
      local_plugins/ota/android/settings.gradle
  16. 4
      local_plugins/ota/android/src/main/AndroidManifest.xml
  17. 26
      local_plugins/ota/android/src/main/kotlin/com/example/ota/DeviceConnection.kt
  18. 148
      local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt
  19. 91
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/BTEventCbHelper.kt
  20. 204
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/BluetoothHelper.kt
  21. 12
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/CallbackImpl.kt
  22. 23
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/CallbackRunnable.kt
  23. 38
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/OnBTEventCallback.kt
  24. 54
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/OtaConstant.kt
  25. 187
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/config/ConfigHelper.kt
  26. 149
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/OTAManager.kt
  27. 175
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/BleEventCallbackManager.java
  28. 1194
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/BleManager.java
  29. 295
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/SendBleDataThread.java
  30. 68
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/interfaces/BleEventCallback.java
  31. 108
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/interfaces/IBleEventCallback.java
  32. 16
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/interfaces/IBleOp.java
  33. 14
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/interfaces/OnThreadStateListener.java
  34. 25
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/interfaces/OnWriteDataCallback.java
  35. 155
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/model/BleDevice.java
  36. 99
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/model/BleScanInfo.java
  37. 38
      local_plugins/ota/ios/.gitignore
  38. 0
      local_plugins/ota/ios/Assets/.gitkeep
  39. 19
      local_plugins/ota/ios/Classes/OtaPlugin.swift
  40. 14
      local_plugins/ota/ios/Resources/PrivacyInfo.xcprivacy
  41. 29
      local_plugins/ota/ios/ota.podspec
  42. 19
      local_plugins/ota/lib/ota.dart
  43. 34
      local_plugins/ota/lib/ota_method_channel.dart
  44. 41
      local_plugins/ota/lib/ota_platform_interface.dart
  45. 72
      local_plugins/ota/pubspec.yaml
  46. 3
      pubspec.yaml

213
ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved

@ -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
}

3
lib/data/services/network/nw_api.dart

@ -1,6 +1,5 @@
class NWApi {
// static const baseApi = "https://www.deapsound.com";
//测试服务器链接地址,经常修改变动,开发使用
static const baseApi = "https://api.deapsound.com";
// static const baseApi = "https://api.ideapsound.com";
//static const baseApi = "https://api.ideapsound.com";
}

7
lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart

@ -8,6 +8,7 @@ import 'dart:io';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:jl_ota/jl_ota.dart';
import 'package:ota/ota.dart';
import '../../../data/services/ble_manager.dart';
class OtaUpgradeController extends GetxController {
@ -22,7 +23,7 @@ class OtaUpgradeController extends GetxController {
final isUpdateAvailable = true.obs;
final RxDouble progress = 0.0.obs;
final JlOta jla = JlOta();
var jlota = Ota();
// 蓝牙服务
final _bleManager = Get.find<BleManager>();
@override
@ -54,6 +55,7 @@ class OtaUpgradeController extends GetxController {
Future<void> _getCurrentVersion() async {
try {
jlota.getPlatformVersion();
// 添加获取设备信息的重试机制
DeviceInfo? deviceInfo = _bleManager.deviceInfo.value;
@ -292,7 +294,8 @@ class OtaUpgradeController extends GetxController {
// 开始升级
void _installApp(String path) async {
jla.getPlatformVersion();
jlota.searchDevices();
jlota.startUpgrade();
// JlOtaPlugin()
// final File file = File(path);
// if (await file.exists()) {

85
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt

@ -24,6 +24,10 @@ import com.microsoft.cognitiveservices.speech.audio.AudioStreamFormat
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
import java.io.File
import java.io.FileOutputStream
import java.io.RandomAccessFile
/**
* Azure语音识别辅助类,支持麦克风和外部音频源的单次和连续语音识别
*
@ -528,7 +532,12 @@ class AzureAsrHelper(private val context: Context) {
private inner class MicrophoneStream : PullAudioInputStreamCallback() {
private var audioRecord: AudioRecord? = null
private var echoCanceler: AcousticEchoCanceler? = null
private var currentAudioFile: File? = null
private var fos: FileOutputStream? = null
// 用于存储音频数据的缓冲区
private val dataBuffer = mutableListOf<ByteArray>()
private var totalBytesWritten = 0
// 音频配置
private val sampleRate = 16000
private val channelConfig = AudioFormat.CHANNEL_IN_MONO
@ -546,6 +555,20 @@ class AzureAsrHelper(private val context: Context) {
*/
private fun initMicrophone() {
try {
// 创建新的音频文件
val timestamp = System.currentTimeMillis()
val filePath = context.getExternalFilesDir(null)?.absolutePath + "/recorded_audio_$timestamp.wav"
currentAudioFile = File(filePath)
currentAudioFile?.createNewFile()
// 仅写入初始文件头
fos = FileOutputStream(currentAudioFile).apply {
write(generateWavHeader(0)) // 初始0长度
close()
}
// 追加音频数据到文件
fos = FileOutputStream(currentAudioFile, true)
// 创建录音对象
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
val format = AudioFormat.Builder()
@ -616,18 +639,80 @@ class AzureAsrHelper(private val context: Context) {
Log.e(tag, "读取音频数据失败: $result")
return 0
}
// 将音频数据保存成wav格式的音频文件
saveAudioDataToWav(buffer, result)
return result
} catch (e: Exception) {
Log.e(tag, "读取音频异常: ${e.message}")
return 0
}
}
/**
* 保存音频数据到 WAV 文件
*/
private fun saveAudioDataToWav(buffer: ByteArray, length: Int) {
// 复制数据到新数组
val copy = ByteArray(length)
System.arraycopy(buffer, 0, copy, 0, length)
// 添加到缓冲区
dataBuffer.add(copy)
totalBytesWritten += length
// 写入文件
try {
fos?.write(copy, 0, length)
} catch (e: Exception) {
Log.e(tag, "写入音频数据失败: ${e.message}")
}
}
// 更新文件头生成(修正RIFF长度计算)
private fun generateWavHeader(dataLength: Int): ByteArray {
val totalLength = 36 + dataLength // RIFF块总长度 = 头部36字节 + 音频数据
val byteRate = sampleRate * 2 * 1 // 采样率 * 字节/样本 * 通道数
return byteArrayOf(
'R'.code.toByte(), 'I'.code.toByte(), 'F'.code.toByte(), 'F'.code.toByte(),
(totalLength and 0xFF).toByte(), ((totalLength shr 8) and 0xFF).toByte(),
((totalLength shr 16) and 0xFF).toByte(), ((totalLength shr 24) and 0xFF).toByte(),
'W'.code.toByte(), 'A'.code.toByte(), 'V'.code.toByte(), 'E'.code.toByte(),
'f'.code.toByte(), 'm'.code.toByte(), 't'.code.toByte(), ' '.code.toByte(),
16, 0, 0, 0, // PCM头长度
1, 0, // PCM格式
1, 0, // 单声道
(sampleRate and 0xFF).toByte(), ((sampleRate shr 8) and 0xFF).toByte(), 0, 0, // 采样率
(byteRate and 0xFF).toByte(), ((byteRate shr 8) and 0xFF).toByte(), 0, 0, // 字节率
2, 0, // 块对齐 (通道数 * 样本位数/8)
16, 0, // 样本位数
'd'.code.toByte(), 'a'.code.toByte(), 't'.code.toByte(), 'a'.code.toByte(),
(dataLength and 0xFF).toByte(), ((dataLength shr 8) and 0xFF).toByte(),
((dataLength shr 16) and 0xFF).toByte(), ((dataLength shr 24) and 0xFF).toByte()
)
}
/**
* 关闭音频资源
*/
override fun close() {
releaseAudioResources()
try {
// 关闭文件流
fos?.close()
// 更新WAV文件头
currentAudioFile?.let { file ->
RandomAccessFile(file, "rw").use { raf ->
raf.seek(0)
raf.write(generateWavHeader(totalBytesWritten))
}
Log.d(tag, "音频文件保存完成: ${file.absolutePath}")
}
} catch (e: Exception) {
Log.e(tag, "更新WAV文件头失败: ${e.message}")
} finally {
fos = null
currentAudioFile = null
}
}
/**

2
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

@ -80,7 +80,7 @@ object BleService {
private var bluetoothLeScanner: BluetoothLeScanner? = null
// GATT连接相关
private var bluetoothGatt: BluetoothGatt? = null
var bluetoothGatt: BluetoothGatt? = null
private var notifyChar: BluetoothGattCharacteristic? = null
private var writeChar: BluetoothGattCharacteristic? = null
private var audioChar: BluetoothGattCharacteristic? = null

33
local_plugins/ota/.gitignore

@ -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/

33
local_plugins/ota/.metadata

@ -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'

3
local_plugins/ota/CHANGELOG.md

@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
local_plugins/ota/LICENSE

@ -0,0 +1 @@
TODO: Add your license here.

15
local_plugins/ota/README.md

@ -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.

4
local_plugins/ota/analysis_options.yaml

@ -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

9
local_plugins/ota/android/.gitignore

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx

68
local_plugins/ota/android/build.gradle

@ -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
}
}
}
}

BIN
local_plugins/ota/android/libs/jl_bt_ota_V1.9.2-release.aar

Binary file not shown.

1
local_plugins/ota/android/settings.gradle

@ -0,0 +1 @@
rootProject.name = 'ota'

4
local_plugins/ota/android/src/main/AndroidManifest.xml

@ -0,0 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.ota">
</manifest>

26
local_plugins/ota/android/src/main/kotlin/com/example/ota/DeviceConnection.kt

@ -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)"
}
}

148
local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt

@ -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)
}
}

91
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/BTEventCbHelper.kt

@ -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)
}
}
}

204
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/BluetoothHelper.kt

@ -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 "返回设备信息"
}
}

12
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/CallbackImpl.kt

@ -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)
}

23
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/CallbackRunnable.kt

@ -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)
}
}
}

38
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/OnBTEventCallback.kt

@ -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) {
}
}

54
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/bluetooth/OtaConstant.kt

@ -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
}
}

187
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/config/ConfigHelper.kt

@ -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()
}
}

149
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/OTAManager.kt

@ -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)
}
}

175
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/BleEventCallbackManager.java

@ -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);
}
}
}
}
}
}

1194
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/BleManager.java

File diff suppressed because it is too large

295
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/SendBleDataThread.java

@ -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;
}
}
}

68
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/interfaces/BleEventCallback.java

@ -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) {
}
}

108
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/interfaces/IBleEventCallback.java

@ -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);
}

16
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/interfaces/IBleOp.java

@ -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);
}

14
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/interfaces/OnThreadStateListener.java

@ -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);
}

25
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/interfaces/OnWriteDataCallback.java

@ -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);
}

155
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/model/BleDevice.java

@ -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 +
'}';
}
}

99
local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/ble/model/BleScanInfo.java

@ -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 +
'}';
}
}

38
local_plugins/ota/ios/.gitignore

@ -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
local_plugins/ota/ios/Assets/.gitkeep

19
local_plugins/ota/ios/Classes/OtaPlugin.swift

@ -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)
}
}
}

14
local_plugins/ota/ios/Resources/PrivacyInfo.xcprivacy

@ -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>

29
local_plugins/ota/ios/ota.podspec

@ -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

19
local_plugins/ota/lib/ota.dart

@ -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();
}
}

34
local_plugins/ota/lib/ota_method_channel.dart

@ -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;
}
}

41
local_plugins/ota/lib/ota_platform_interface.dart

@ -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.');
}
}

72
local_plugins/ota/pubspec.yaml

@ -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

3
pubspec.yaml

@ -75,7 +75,8 @@ dependencies:
path: local_plugins/chat_storage
spotify_sdk:
path: local_plugins/spotify_sdk
ota:
path: local_plugins/ota
jl_ota:
path: local_plugins/jl_ota
ble_service:

Loading…
Cancel
Save