Browse Source

ota升级优化版

newdev_shunjiawei
fdp 1 year ago
parent
commit
e5712b60cc
  1. 110
      lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart
  2. 114
      lib/modules/ota_upgrade/views/ota_upgrade_view.dart
  3. 48
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt
  4. 116
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/OTAManager.kt
  5. 388
      local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt
  6. 45
      local_plugins/ota/android/src/main/kotlin/com/example/ota/tool/ota/OTAManager.kt
  7. 145
      local_plugins/ota/lib/ota.dart
  8. 20
      local_plugins/ota/lib/ota_method_channel.dart
  9. 12
      local_plugins/ota/lib/ota_platform_interface.dart
  10. 4
      local_plugins/ota/pubspec.yaml

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

@ -11,18 +11,28 @@ import 'package:ota/ota.dart';
import '../../../data/services/ble_manager.dart';
class OtaUpgradeController extends GetxController {
static const int NOT_STARTED = -1; // 未开始
static const int VALIDATION_FILE = 0; // 验证文件
static const int ONUPGRADE = 1; // 升级中
static const int START_UPGRADING = 2; // 开始升级
static const int UPGRADE_SUCCESS = 3; // 升级成功
static const int UPGRADE_FAILED = 4; // 升级失败
static const int CONNECTION_DISCONNECTION = 5; // 连接断开
static const int CONNECTION_SUCCESS = 6; // 连接成功
static const int DOWNLOADING_UP_FILES = 7; // 下载升级文件
final RxString errorMessage = ''.obs;
final RxBool isFetchingDeviceInfo = false.obs;
final Dio _dio = Dio();
final RxString latestVersion = ''.obs;
final RxString currentVersion = ''.obs;
String updatepackageaddress = '';
final RxDouble downloadProgress = 0.0.obs;
final RxBool isUpdating = false.obs;
final isUpdateAvailable = true.obs;
final isUpdateAvailable = true.obs;
//int currentProgress = 0; // 当前进度值,范围0-100
final RxDouble progress = 0.0.obs;
var jlota = Ota();
final jlota = Ota.instance;
final RxBool isDownloading = false.obs;
// 蓝牙服务
final _bleManager = Get.find<BleManager>();
@override
@ -31,30 +41,57 @@ class OtaUpgradeController extends GetxController {
_getCurrentVersion();
}
@override
void onClose() {
jlota.onOtaClose();
super.onClose();
}
// 添加重试方法
void retryConnection() {
errorMessage.value = '';
_getCurrentVersion();
}
String getStatusMessage(int type) {
switch (type) {
case NOT_STARTED: // -1
return "未开始".tr;
case VALIDATION_FILE: // 0
return "验证文件中".tr;
case ONUPGRADE: // 1
return "升级中".tr;
case START_UPGRADING: // 2
return "开始升级".tr;
case UPGRADE_SUCCESS: // 3
return "升级成功".tr;
case UPGRADE_FAILED: // 4
return "升级失败".tr;
case CONNECTION_DISCONNECTION: // 5
return "连接断开".tr;
case CONNECTION_SUCCESS: // 6
return "连接成功".tr;
case DOWNLOADING_UP_FILES: // 7 (新增)
return "下载升级文件".tr;
default:
return "未知状态".tr; // 更安全的默认值
}
}
// 添加更新方法
Future<void> startUpdate() async {
try {
isUpdating.value = true;
checkUpdate();
// 实现更新逻辑...
} catch (e) {
errorMessage.value = e.toString();
Get.snackbar('更新失败', e.toString());
} finally {
isUpdating.value = false;
}
}
Future<void> _getCurrentVersion() async {
try {
jlota.getPlatformVersion();
// 添加获取设备信息的重试机制
DeviceInfo? deviceInfo = _bleManager.deviceInfo.value;
@ -158,12 +195,18 @@ class OtaUpgradeController extends GetxController {
content: Text('是否立即更新?'),
actions: [
TextButton(
onPressed: () => Get.back(),
onPressed: () {
if (Get.isDialogOpen!) {
Get.back(); // 确保只关闭对话框
}
},
child: Text('取消'),
),
TextButton(
onPressed: () {
Get.back();
if (Get.isDialogOpen!) {
Get.back(); // 确保只关闭对话框
}
startDownload();
},
child: Text('更新'),
@ -175,7 +218,7 @@ class OtaUpgradeController extends GetxController {
// 修改后的startDownload方法
Future<void> startDownload() async {
isUpdating.value = true;
isDownloading.value = true;
int lastIndex = updatepackageaddress.lastIndexOf('/');
String fileName = updatepackageaddress.substring(lastIndex + 1);
String directoryName = "ota_upgrades";
@ -187,8 +230,7 @@ class OtaUpgradeController extends GetxController {
headers: {'Accept-Encoding': 'gzip'},
));
Get.snackbar('升级命令已发送', '开始下载...');
jlota.type.value = DOWNLOADING_UP_FILES;
try {
final Directory? downloadsDir = await getDownloadsDirectory();
if (downloadsDir == null) {
@ -217,13 +259,13 @@ class OtaUpgradeController extends GetxController {
}
} catch (e) {
Get.snackbar('错误', '检查本地文件失败: $e');
isUpdating.value = false;
return;
}
if (existingSameFile) {
Get.snackbar('文件已存在', '跳过下载,直接更新');
isUpdating.value = false;
_installApp(savePath);
return;
}
@ -238,7 +280,7 @@ class OtaUpgradeController extends GetxController {
}
} catch (e) {
Get.snackbar('错误', '删除旧文件失败: $e');
isUpdating.value = false;
//isUpgrade.value = false;
return;
}
@ -249,59 +291,39 @@ class OtaUpgradeController extends GetxController {
followRedirects: true,
maxRedirects: 5,
);
DateTime lastUpdate = DateTime.now();
// 修改下载部分
await dio.download(
updatepackageaddress,
savePath,
options: options,
onReceiveProgress: (received, total) {
final now = DateTime.now();
if (now.difference(lastUpdate).inMilliseconds > 1000) {
lastUpdate = now;
String progressMsg = total != -1
? '${(received / total * 100).toStringAsFixed(1)}%'
: '已下载:${(received / 1024 / 1024).toStringAsFixed(1)}MB';
Get.snackbar(
'升级进度',
progressMsg,
duration: const Duration(seconds: 1),
);
if (total != -1) {
downloadProgress.value = received / total;
}
},
);
File file = File(savePath);
if (await file.exists()) {
Get.snackbar('下载完成', '文件已保存至: $savePath');
} else {
Get.snackbar('错误', '文件未找到,请检查写入权限');
}
isUpdating.value = false;
isDownloading.value = false;
_installApp(savePath);
return;
} on DioException catch (e) {
Get.snackbar('下载失败', '错误: ${e.message}');
isUpdating.value = false;
} on FileSystemException catch (e) {
Get.snackbar('存储失败', '无法写入文件: ${e.message}');
isUpdating.value = false;
} catch (e) {
Get.snackbar('未知错误', e.toString());
isUpdating.value = false;
}
}
// 开始升级
void _installApp(String path) async {
jlota.searchDevices();
// jlota.startUpgrade();
// JlOtaPlugin()
// final File file = File(path);
// if (await file.exists()) {
// // OpenResult result = await OpenFile.open(path);
// // if (result.type != ResultType.done) {
// // Get.snackbar('错误', '安装失败: ${result.message}');
// // }
// }
jlota.searchDevices(path);
return;
// isUpgrade.value = false;
}
}

114
lib/modules/ota_upgrade/views/ota_upgrade_view.dart

@ -101,7 +101,7 @@ class OtaUpgradeView extends GetView<OtaUpgradeController> {
fontSize: 18,
color: isDarkMode ? Colors.grey[300] : Colors.black)),
SizedBox(width: 8), // 改为width
Obx(() => Text('v${controller.currentVersion}',
Obx(() => Text('V ${controller.currentVersion}',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.normal,
@ -117,11 +117,11 @@ class OtaUpgradeView extends GetView<OtaUpgradeController> {
fontSize: 18,
color: isDarkMode ? Colors.grey[300] : Colors.black)),
SizedBox(width: 8), // 改为width
Text('v${controller.latestVersion}',
Obx(() => Text('V ${controller.latestVersion}',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.normal,
color: isDarkMode ? Colors.green[300] : Colors.green)),
color: isDarkMode ? Colors.blue[300] : Colors.blue))),
],
),
],
@ -227,7 +227,7 @@ class OtaUpgradeView extends GetView<OtaUpgradeController> {
);
Widget _buildUpdateButton(bool isDarkMode) {
return controller.isUpdating.value
return controller.jlota.isUpgrade.value
? _buildProgressIndicator(isDarkMode)
: Container(
width: double.infinity,
@ -262,7 +262,7 @@ class OtaUpgradeView extends GetView<OtaUpgradeController> {
if (controller.isUpdateAvailable.value) ...[
SizedBox(height: 8),
Text(
'建议连接WiFi并保持充电状态'.tr,
'取出双耳机,并保证电量均为30%以上的状态'.tr,
style: TextStyle(
fontSize: 14,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
@ -274,61 +274,67 @@ class OtaUpgradeView extends GetView<OtaUpgradeController> {
);
}
Widget _buildProgressIndicator(bool isDarkMode) => Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color:
isDarkMode ? Colors.blue[900]!.withOpacity(0.2) : Colors.blue[50],
borderRadius: BorderRadius.circular(16),
),
Widget _buildProgressIndicator(bool isDarkMode) {
return Obx(() {
final progress = controller.jlota.progress.value;
final type = controller.jlota.type.value;
// // 处理升级失败状态:弹出 snackbar 并显示错误提示
// if (type == OtaUpgradeController.UPGRADE_FAILED ||
// type == OtaUpgradeController.CONNECTION_DISCONNECTION) {
// // 使用延迟确保在 build 完成后执行
// Future.delayed(Duration.zero, () {
// Get.snackbar('升级失败', '重新连接设备后重试');
// });
// return Container(
// width: double.infinity,
// padding: const EdgeInsets.symmetric(vertical: 10),
// child: Text(
// '升级失败,请重新连接设备',
// style: TextStyle(
// fontSize: 18,
// fontWeight: FontWeight.bold,
// color: Colors.red[700],
// ),
// ),
// );
// }
// 正常升级状态
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 10),
child: Column(
children: [
Text('正在下载更新...'.tr,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDarkMode ? Colors.blue[300] : Colors.blue[700])),
SizedBox(height: 16),
//SizedBox(
//width: 240,
Container(
decoration: BoxDecoration(
border: Border.all(
color: isDarkMode ? Colors.blue[400]! : Colors.blue[300]!,
width: 1.5,
),
borderRadius: BorderRadius.circular(12),
Text(
controller.getStatusMessage(type),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDarkMode ? Colors.blue[300] : Colors.blue[700],
),
child: LinearProgressIndicator(
value: controller.downloadProgress.value,
backgroundColor:
isDarkMode ? Colors.grey[800] : Colors.grey[200],
valueColor: AlwaysStoppedAnimation(
isDarkMode ? Colors.blue[300] : Colors.blue),
minHeight: 12,
borderRadius: BorderRadius.circular(10),
),
const SizedBox(height: 16),
LinearProgressIndicator(
value: progress / 100.0, // 转换为 0.0-1.0
backgroundColor: isDarkMode ? Colors.grey[800] : Colors.grey[200],
valueColor: AlwaysStoppedAnimation<Color>(
isDarkMode ? Colors.blue[300]! : Colors.blue,
),
),
const SizedBox(height: 8),
Text(
'${progress.toStringAsFixed(1)}%',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: isDarkMode ? Colors.blue[300] : Colors.blue[700],
),
),
SizedBox(height: 16),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.downloading,
color: isDarkMode ? Colors.blue[300] : Colors.blue,
size: 20,
),
SizedBox(width: 8),
Text(
'${(controller.downloadProgress.value * 100).toStringAsFixed(1)}%',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color:
isDarkMode ? Colors.blue[300] : Colors.blue[700])),
],
)
],
),
);
});
}
}

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

@ -30,7 +30,6 @@ import com.jieli.jl_audio_decode.callback.OnStateCallback
import com.jieli.jl_audio_decode.opus.OpusManager
import com.jieli.jl_audio_decode.exceptions.OpusException
import com.yunqiinnovation.ble_service.BleCompanionManager
import com.yunqiinnovation.ble_service.OTAManager
import com.jieli.jl_bt_ota.model.BluetoothOTAConfigure;
import com.jieli.jl_bt_ota.constant.ErrorCode;
import com.jieli.jl_bt_ota.constant.StateCode;
@ -89,7 +88,8 @@ object BleService {
private lateinit var scanHandler: Handler
private val scanResults = ArrayList<ScanResult>()
private var isScanning = false
var isota = false
// 是否是ota模式
var isEnterOta = false
// 连接状态
val connectionState = MutableLiveData(BleConst.STATE_DISCONNECTED)
@ -354,9 +354,10 @@ object BleService {
return
}
Log.i(TAG, "检测到已配对设备,MAC: $pairedMac,尝试连接")
if(!isota)
if(!isEnterOta)
{
Log.i(TAG, "检测到已配对设备,MAC: $pairedMac,尝试连接")
// 调用现有的 connect 方法
connect(pairedMac)
}
@ -430,44 +431,7 @@ object BleService {
audioChar = null
}
}
// fun startOTA() {
// try {
// otaManager?.startOTA(CustomUpdateCallback(context, null))
// Log.i(TAG, "OTA 升级已启动")
// } catch (e: IllegalStateException) {
// Log.e(TAG, "OTA 启动失败: ${e.message}")
// disconnectGatt()
// }
// }
// // 配置OTA
// fun configureOTA() {
// try {
// val firmwarePath =
// context.getExternalFilesDir("downloads/ota_upgrades")?.path + "/1.ufw"
// val file = File(firmwarePath)
// if (!file.exists()) {
// // throw IOException("Firmware file not found")
// Log.i(TAG, "Firmware file not found")
// }
// val bluetoothOption = BluetoothOTAConfigure().apply {
// priority = BluetoothOTAConfigure.PREFER_BLE
// isUseReconnect = true
// isUseAuthDevice = true
// mtu = 512
// isNeedChangeMtu = false // 确保MTU请求生效
// isUseJLServer = false
// firmwareFilePath = firmwarePath
// }
// otaManager?.configure(bluetoothOption) ?: run {
// Log.e(TAG, "OTA Manager未初始化")
// }
// } catch (e: Exception) {
// Log.e(TAG, "OTA配置失败: ${e.message}")
// // 处理错误
// }
// }
/**
* 更新连接状态并通知回调

116
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/OTAManager.kt

@ -1,116 +0,0 @@
package com.yunqiinnovation.ble_service
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.EventChannel
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.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothProfile
import android.content.Context
import com.jieli.jl_bt_ota.constant.ErrorCode;
import com.jieli.jl_bt_ota.constant.StateCode;
import com.jieli.jl_bt_ota.impl.BluetoothOTAManager;
import com.jieli.jl_bt_ota.interfaces.BtEventCallback;
import com.jieli.jl_bt_ota.interfaces.IActionCallback;
import com.jieli.jl_bt_ota.interfaces.IUpgradeCallback;
import com.jieli.jl_bt_ota.model.BluetoothOTAConfigure;
import com.jieli.jl_bt_ota.model.base.BaseError;
import com.jieli.jl_bt_ota.model.response.TargetInfoResponse;
import android.util.Log
import java.util.*
class CustomUpdateCallback(
val context: Context,
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"}")
}
}
class OTAManager(context: Context) : BluetoothOTAManager(context) {
// 定义回调接口
interface DataCallback {
fun onDataReceived(device: BluetoothDevice?, data: ByteArray?)
}
// 保存回调的实例
private var dataCallback: DataCallback? = null
// 设置回调的方法
fun setDataCallback(callback: DataCallback) {
this.dataCallback = callback
}
override fun getConnectedDevice(): BluetoothDevice? {
// 这里可以添加空实现的注释说明
Log.i(TAG, "ota数据发送getConnectedDevice")
return null
}
override fun getConnectedBluetoothGatt(): BluetoothGatt? {
// 这里可以添加空实现的注释说明
Log.i(TAG, "ota数据发送getConnectedBluetoothGatt")
return null
}
override fun connectBluetoothDevice(bluetoothDevice: BluetoothDevice?) {
// 这里可以添加空实现的注释说明
Log.i(TAG, "ota数据发送connectBluetoothDevice")
}
override fun disconnectBluetoothDevice(bluetoothDevice: BluetoothDevice?) {
// 这里可以添加空实现的注释说明
Log.i(TAG, "ota数据发送disconnectBluetoothDevice")
}
/** 数据发送方法(协议无关) */
override fun sendDataToDevice(
bluetoothDevice: BluetoothDevice?,
bytes: ByteArray?
): Boolean {
Log.i(TAG, "ota数据发送${bluetoothDevice},${bytes?.size}")
// 触发回调,将数据回传给调用者
dataCallback?.onDataReceived(bluetoothDevice, bytes)
// context.onReceiveDeviceData(device, data);
return true
}
}

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

@ -32,46 +32,114 @@ import android.os.Looper
import java.io.File
import java.io.FileOutputStream
import java.io.RandomAccessFile
class CustomUpdateCallback(
import io.flutter.plugin.common.EventChannel
val device: BluetoothDevice?
class CustomUpdateCallback(
val device: BluetoothDevice?,
private val streamSink: EventChannel.EventSink?
) : 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"
private const val CHANNEL_NAME = "com.example.ota"
private const val EVENT_CHANNEL_NAME = "com.example.ota/events"
}
override fun onStartOTA() {
Log.d(TAG, "onStartOTA开始升级")
// viewModel.otaStateMLD.value = OTAStart(device)
if (streamSink != null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
streamSink.success(
mapOf(
"event" to "onStartOTA"
)
)
}
}
}
override fun onNeedReconnect(addr: String?, isNewReconnectWay: Boolean) {
Log.d(TAG, "onNeedReconnect需要重新链接")
if (streamSink != null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
streamSink.success(
mapOf(
"event" to "onNeedReconnect",
"addr" to addr,
"isNewReconnectWay" to isNewReconnectWay
)
)
}
}
}
override fun onProgress(type: Int, progress: Float) {
Log.d(TAG, "onProgress正在进展")
//viewModel.otaStateMLD.value = OTAWorking(device, type, progress)
if (streamSink != null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
streamSink.success(
mapOf(
"event" to "onProgress",
"type" to type,
"progress" to progress
)
)
} else {
// Handler(Looper.getMainLooper()).post {
// streamSink.success(
// mapOf(
// "event" to "onDecodeStream"
// //"data" to data
// )
// )
// }
}
}
}
override fun onStopOTA() {
Log.d(TAG, "onStartOTA停止升级")
if (streamSink != null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
streamSink.success(
mapOf(
"event" to "onStopOTA",
)
)
}
}
BleService.isEnterOta = false
}
override fun onCancelOTA() {
Log.d(TAG, "onCancelOTA取消升级")
if (streamSink != null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
streamSink.success(
mapOf(
"event" to "onCancelOTA",
)
)
}
}
}
override fun onError(error: BaseError?) {
val errorMessage = error?.message ?: "Unknown error"
Log.e(TAG, "升级失败: ${error?.message ?: "未知错误"}")
Log.d(TAG, "升级失败: ${error?.message ?: "未知错误"}")
if (streamSink != null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
Log.d(TAG, "升级失败:error?.message ${error?.message}未知错误 error?.code${error?.code}")
streamSink.success(
mapOf(
"event" to "onError",
"code" to (error?.code ?: 255), // 确保值是正确的类型
"message" to (error?.message ?: "Unknown error") // 确保值是正确的类型
)
)
}
}
// 如果需要打印完整错误对象信息(假设BaseError有重写toString())
Log.d(TAG, "完整错误对象: ${error ?: "null"}")
}
@ -79,123 +147,215 @@ import java.io.RandomAccessFile
/** OtaPlugin */
class OtaPlugin : BleService.Callback, 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 device1: BluetoothDevice
private lateinit var bluetoothHelper : BluetoothHelper
val otaConnectionMLD = MutableLiveData<DeviceConnection>()
val mandatoryUpgradeMLD = MutableLiveData<BluetoothDevice>()
private lateinit var otaManager : OTAManager
private val bluetoothCallback = object : BtEventCallback() {
/// 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 otaDevice: BluetoothDevice
private lateinit var bluetoothHelper: BluetoothHelper
private lateinit var otaManager: OTAManager
private lateinit var filePath: String//升级文件路径
private var isDisconnect = false//是否主动断开
private var streamSink: EventChannel.EventSink? = null
private lateinit var eventChannel: EventChannel
// 是否已注册BLE回调
private var isRegisteredToBle = false
// 添加主线程Handler用于确保在主线程回调
private val mainHandler = Handler(Looper.getMainLooper())
companion object {
private const val TAG = "OtaPlugin"
private const val CHANNEL_NAME = "com.example.ota"
private const val EVENT_CHANNEL_NAME = "com.example.ota/events"
}
// 在主线程上安全发送事件
private fun sendEventOnMainThread(event: Map<String, Any?>) {
if (streamSink == null) return
if (Looper.myLooper() == Looper.getMainLooper()) {
// 如果已经在主线程,直接发送
streamSink?.success(event)
} else {
// 否则切换到主线程
mainHandler.post {
streamSink?.success(event)
}
}
}
// 在主线程上安全返回结果
private fun sendResultOnMainThread(result: Result, value: Any?) {
if (Looper.myLooper() == Looper.getMainLooper()) {
// 如果已经在主线程,直接返回
result.success(value)
} else {
// 否则切换到主线程
mainHandler.post {
result.success(value)
}
}
}
// 在主线程上安全返回错误
private fun sendErrorOnMainThread(
result: Result,
errorCode: String,
errorMessage: String,
errorDetails: Any?
) {
if (Looper.myLooper() == Looper.getMainLooper()) {
// 如果已经在主线程,直接返回
result.error(errorCode, errorMessage, errorDetails)
} else {
// 否则切换到主线程
mainHandler.post {
result.error(errorCode, errorMessage, errorDetails)
}
}
}
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())
}
otaDevice = device!!
// sendEventOnMainThread(
// mapOf(
// "event" to "NOCONNECTION"
// )
// )
// 更新连接状态的 LiveData,用于通知 UI 或其他逻辑
// otaConnectionMLD.value = DeviceConnection(device, status)
Log.d("bluetoothCallback", "onConnection,status${status}")
if (status == StateCode.CONNECTION_OK) {
Log.d("TAG", "startUpgrade")
sendEventOnMainThread(
mapOf(
"event" to "CONNECTION_SUCCESS"
)
)
Log.d("TAG", "startUpgrade,filePath=${filePath}")
otaManager.bluetoothOption.firmwareFilePath = filePath
otaManager.startOTA(
CustomUpdateCallback(
otaManager.bluetoothHelper.getConnectedDevice(), streamSink
)
)
}
// 更新连接状态的 LiveData,用于通知 UI 或其他逻辑
otaConnectionMLD.value = DeviceConnection(device, status)
Log.d("bluetoothCallback", "onConnection,status${status}")
if( status == StateCode.CONNECTION_OK) {
Log.d("TAG", "startUpgrade")
val directoryName = "ota_upgrades"
val fileName = "1.ufw"
// 创建WAV输出文件路径
val filePath = context.getExternalFilesDir(null)?.absolutePath + "/downloads/ota_upgrades/$fileName"
Log.d("TAG", "startUpgrade,filePath=${filePath}")
otaManager.bluetoothOption.firmwareFilePath = filePath
otaManager.startOTA(
CustomUpdateCallback(
otaManager.bluetoothHelper.getConnectedDevice()
)
)
}
}
override fun onMandatoryUpgrade(device: BluetoothDevice?) {
mandatoryUpgradeMLD.postValue(device!!)
Log.d("bluetoothCallback", "onMandatoryUpgrade")
// mandatoryUpgradeMLD.postValue(device!!)
Log.d("bluetoothCallback", "onMandatoryUpgrade")
}
}
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
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
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "ota")
channel.setMethodCallHandler(this)
eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, EVENT_CHANNEL_NAME)
eventChannel.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
streamSink = events
}
override fun onCancel(arguments: Any?) {
streamSink = null
}
})
// 初始化 otaManager 和 bluetoothHelper
ConfigHelper.initialize(context)
otaManager = OTAManager(context)
bluetoothHelper = BluetoothHelper(context)
// 如果需要额外的初始化逻辑,请在这里添加
otaManager.registerBluetoothCallback(bluetoothCallback)
BleService.addCallback(this)
// otaManager.registerBluetoothCallback(bluetoothCallback)
result.success("Android ${android.os.Build.VERSION.RELEASE}")
}else if (call.method == "searchDevices") {
Log.d("TAG", "searchDevices")
BleService.isota=true
device1 = BleService.bluetoothGatt?.device!!
BleService.disconnect()
} else if (call.method == "startUpgrade") {
Log.d("TAG", "startUpgrade")
val directoryName = "ota_upgrades"
val fileName = "1.ufw"
// 创建WAV输出文件路径
val filePath = context.getExternalFilesDir(null)?.absolutePath + "/downloads/ota_upgrades/$fileName"
Log.d("TAG", "startUpgrade,filePath=${filePath}")
//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)
}
override fun onScanResult(devices: List<Map<String, Any>>) {
otaManager = OTAManager(context)
bluetoothHelper = BluetoothHelper(context)
// 如果需要额外的初始化逻辑,请在这里添加
otaManager.registerBluetoothCallback(bluetoothCallback)
BleService.addCallback(this)
isRegisteredToBle = true
Log.i("TAG", "onAttachedToEngine")
}
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "searchDevices") {
Log.d("TAG", "searchDevices")
// 接收传入的参数
val arguments = call.arguments as? Map<String, Any>
filePath = (arguments?.get("filePath") as? String).toString()
if (filePath != null) {
Log.d("TAG", "Received filePath: $filePath")
// 在这里使用 filePath 参数,例如传递给其他方法或类
} else {
Log.d("TAG", "filePath 参数为空")
}
BleService.isEnterOta = true
val bluetoothGatt = BleService.bluetoothGatt
if (bluetoothGatt?.device != null) {
isDisconnect = true
otaDevice = bluetoothGatt.device
BleService.disconnect()
} else {
Log.e("TAG", "BluetoothGatt 或 device 为 null,无法执行操作")
sendEventOnMainThread(
mapOf(
"event" to "NOCONNECTION"
)
)
BleService.isEnterOta = false
}
} else if (call.method == "onOtaClose") {
BleService.isEnterOta = false
isDisconnect = false
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
eventChannel.setStreamHandler(null)
// 取消BLE回调注册
if (isRegisteredToBle) {
BleService.removeCallback(this)
isRegisteredToBle = false
}
isDisconnect = false
// 释放其他资源
streamSink = null
BleService.isEnterOta = false
Log.d(TAG, "OtaPlugin 已释放资源")
}
override fun onScanResult(devices: List<Map<String, Any>>) {
// 不处理扫描结果
}
override fun onConnectionStateChanged(state: Int) {
// // 可选:处理连接状态变化
// if (state == BleConst.STATE_CONNECTED) {
// Log.d(TAG, "Ble设备已连接,准备好语音交互")
// } else if (state == BleConst.STATE_DISCONNECTED) {
Log.d("TAG", "Ble设备已断开,onConnectionStateChanged")
// // 设备断开连接,停止语音服务
// // stopListening()
// }
if (state == BleConst.STATE_DISCONNECTED) {
Handler(Looper.getMainLooper()).postDelayed({
otaManager.bluetoothHelper.startScan(device1)
}, 2000) // 2000 毫秒 = 2 秒
}
if (state == BleConst.STATE_DISCONNECTED && isDisconnect) {
Log.d("TAG", "Ble设备已断开,onConnectionStateChanged")
Handler(Looper.getMainLooper()).postDelayed({
otaManager.bluetoothHelper.startScan(otaDevice)
}, 2000) // 2000 毫秒 = 2 秒
}
}
override fun onAudioDataReceived(data: ByteArray) {
@ -208,12 +368,12 @@ private lateinit var bluetoothHelper : BluetoothHelper
* 在收到唤醒信号时启动语音识别
*/
override fun onWakeupSignalReceived() {
}
override fun onDeviceInfoReceived(infoType: Int, infoData: Map<String, Any>) {
// 不处理设备信息
}
}

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

@ -16,33 +16,37 @@ 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 configHelper = ConfigHelper.getInstance()
val bluetoothHelper = BluetoothHelper.getInstance(context)
/**
* 转换成OTA库的连接状态
*
* @param status 连接状态
* @return 库的连接状态
*/
fun changeConnectStatus(status: Int): Int {
var changeStatus = StateCode.CONNECTION_DISCONNECT
when (status) {
2 -> {
changeStatus = StateCode.CONNECTION_OK
}
1 -> {
changeStatus = StateCode.CONNECTION_CONNECTING
}
fun changeConnectStatus(status: Int): Int {
var changeStatus = StateCode.CONNECTION_DISCONNECT
when (status) {
2 -> {
changeStatus = StateCode.CONNECTION_OK
}
1 -> {
changeStatus = StateCode.CONNECTION_CONNECTING
}
return changeStatus
}
val btEventCallback = object : OnBTEventCallback() {
return changeStatus
}
val btEventCallback = object : OnBTEventCallback() {
override fun onDeviceConnection(device: BluetoothDevice?, way: Int, status: Int) {
super.onDeviceConnection(device, way, status)
@ -57,7 +61,7 @@ class OTAManager(context: Context) : BluetoothOTAManager(context) {
TAG, "onConnection >>> device : ${device}, way = $way" +
" status :$status, change status : $connectionState"
)
onBtDeviceConnection(device, connectionState)
onBtDeviceConnection(device, connectionState)
}
override fun onReceiveData(
@ -88,18 +92,19 @@ class OTAManager(context: Context) : BluetoothOTAManager(context) {
}
init {
// ConfigHelper.initialize(context)
// ConfigHelper.initialize(context)
val bluetoothOption = BluetoothOTAConfigure()
//选择通讯方式
bluetoothOption.priority =
BluetoothOTAConfigure.PREFER_BLE
//是否需要自定义回连方式(默认不需要,如需要自定义回连方式,需要客户自行实现)
bluetoothOption.isUseReconnect =
(configHelper.isUseCustomReConnectWay() && configHelper.isHidDevice())
Log.d(
TAG, "是否启用设备认证流程(与固件工程师确认) >>> ${configHelper.isUseDeviceAuth()} >>> ${bluetoothOption.isUseReconnect} "
)
Log.d(
TAG,
"是否启用设备认证流程(与固件工程师确认) >>> ${configHelper.isUseDeviceAuth()} >>> ${bluetoothOption.isUseReconnect} "
)
//是否启用设备认证流程(与固件工程师确认)
bluetoothOption.isUseAuthDevice = configHelper.isUseDeviceAuth()
//设置BLE的MTU

145
local_plugins/ota/lib/ota.dart

@ -1,19 +1,148 @@
import 'ota_platform_interface.dart';
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
class OtaEvent {
static final int NOT_STARTED = -1; // 未开始
static final int VALIDATION_FILE = 0; // 验证文件
static final int ONUPGRADE = 1; // 升级中
static final int START_UPGRADING = 2; // 开始升级
static final int UPGRADE_SUCCESS = 3; // 升级成功
static final int UPGRADE_FAILED = 4; // 升级失败
static final int CONNECTION_DISCONNECTION = 5; // 连接断开
static final int CONNECTION_SUCCESS = 6; // 连接成功
/// 事件类型
final String event;
/// 输出升级状态
final int? type;
/// 输出升级进度
final double? progress;
/// 错误码 (仅在发生错误时有效)
final int? code;
/// 错误信息 (仅在发生错误时有效)
final String? message;
/// 构造函数
OtaEvent({
required this.event,
this.type,
this.progress,
this.code,
this.message,
});
/// 从Map构造对象
factory OtaEvent.fromMap(Map<dynamic, dynamic> map) {
return OtaEvent(
event: map['event'],
type: map['type'],
progress: map['progress'],
code: map['code'],
message: map['message'],
);
}
}
class Ota {
Future<String?> getPlatformVersion() {
return OtaPlatform.instance.getPlatformVersion();
// 单例实例
static final Ota _instance = Ota._internal();
// 私有构造函数
Ota._internal() {
// 初始化时设置事件监听
_setupEventListener();
}
Future<String?> searchDevices() {
return OtaPlatform.instance.searchDevices();
// 公共访问点
static Ota get instance => _instance;
static const EventChannel _eventChannel = EventChannel(
'com.example.ota/events',
);
final RxDouble progress = 0.0.obs;
final RxInt type = OtaEvent.NOT_STARTED.obs;
final RxBool isUpgrade = false.obs;
/// 事件流
Stream<OtaEvent>? _eventStream;
bool _isEventListenerSet = false; // 防止重复监听
/// 获取事件流
Stream<OtaEvent> get eventStream {
_eventStream ??= _eventChannel.receiveBroadcastStream().map((event) {
if (event is Map) {
return OtaEvent.fromMap(event);
}
throw PlatformException(code: 'INVALID_EVENT', message: '无效的事件数据');
});
return _eventStream!;
}
/// 释放资源
void onClose() {
onOtaClose();
// 停止事件监听
_eventStream = null;
_isEventListenerSet = false;
// 清理 Rx 对象
progress.close();
type.close();
isUpgrade.value = false;
print("Ota 对象资源已释放");
}
/// 内部设置事件监听(只执行一次)
void _setupEventListener() {
if (_isEventListenerSet) return;
eventStream.listen((event) {
if (event.event == 'onProgress' &&
event.type != null &&
event.progress != null) {
print("flutter 收到数据${event.progress}type=${type.value}");
progress.value = event.progress!;
type.value = event.type!;
}
if (event.event == 'onStartOTA') {
type.value = OtaEvent.START_UPGRADING;
isUpgrade.value = true; // 开始升级
}
if (event.event == 'onStopOTA') {
type.value = OtaEvent.UPGRADE_SUCCESS;
isUpgrade.value = false;
}
if (event.event == 'onError' &&
event.code != null &&
event.message != null) {
print("flutter onError${event.code}type=${event.message}");
type.value = OtaEvent.UPGRADE_FAILED;
isUpgrade.value = false;
}
if (event.event == 'NOCONNECTION') {
type.value = OtaEvent.CONNECTION_DISCONNECTION;
isUpgrade.value = false;
}
if (event.event == 'CONNECTION_SUCCESS') {
type.value = OtaEvent.CONNECTION_SUCCESS;
}
});
_isEventListenerSet = true;
}
Future<String?> connectDevice() {
return OtaPlatform.instance.connectDevice();
Future<String?> searchDevices(String? filePath) {
return OtaPlatform.instance.searchDevices(filePath);
}
Future<String?> startUpgrade() {
return OtaPlatform.instance.startUpgrade();
Future<String?> onOtaClose() {
return OtaPlatform.instance.onOtaClose();
}
}

20
local_plugins/ota/lib/ota_method_channel.dart

@ -10,25 +10,17 @@ class MethodChannelOta extends OtaPlatform {
final methodChannel = const MethodChannel('ota');
@override
Future<String?> getPlatformVersion() async {
Future<String?> searchDevices(String? filePath) async {
final version = await methodChannel.invokeMethod<String>(
'getPlatformVersion',
'searchDevices',
{'filePath': filePath}, // 传递参数
);
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');
@override
Future<String?> onOtaClose() async {
final version = await methodChannel.invokeMethod<String>('onOtaClose');
return version;
}
}

12
local_plugins/ota/lib/ota_platform_interface.dart

@ -23,19 +23,11 @@ abstract class OtaPlatform extends PlatformInterface {
_instance = instance;
}
Future<String?> getPlatformVersion() {
Future<String?> searchDevices(String? filePath) {
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() {
Future<String?> onOtaClose() {
throw UnimplementedError('platformVersion() has not been implemented.');
}
}

4
local_plugins/ota/pubspec.yaml

@ -11,12 +11,12 @@ dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
get: ^4.6.5
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

Loading…
Cancel
Save