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 { |
|||
// 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"; |
|||
} |
|||
|
|||
@ -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