Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

weicu
lxm 1 year ago
parent
commit
52e9fbf7a4
  1. 12
      .metadata
  2. 7
      android/app/src/debug/AndroidManifest.xml
  3. 5
      android/app/src/main/kotlin/com/yunqiinnovation/deep_voice/MainActivity.kt
  4. 7
      android/app/src/profile/AndroidManifest.xml
  5. 53
      lib/data/services/location_manager.dart
  6. 25
      lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart
  7. 1
      local_plugins/agent_service/android/build.gradle.kts
  8. 15
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  9. 13
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/utils.kt
  10. 2
      local_plugins/amap_service/android/build.gradle
  11. 3
      local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationServiceHelper.kt
  12. 39
      local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift
  13. 11
      local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftBleServicePlugin.swift
  14. 56
      local_plugins/ble_service/lib/ble_service.dart
  15. 33
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt
  16. 4
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/CustomSseClientTransport.kt
  17. 9
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MCPSubClient.kt
  18. 20
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt
  19. 33
      local_plugins/location_service/.gitignore
  20. 33
      local_plugins/location_service/.metadata
  21. 3
      local_plugins/location_service/CHANGELOG.md
  22. 1
      local_plugins/location_service/LICENSE
  23. 15
      local_plugins/location_service/README.md
  24. 4
      local_plugins/location_service/analysis_options.yaml
  25. 9
      local_plugins/location_service/android/.gitignore
  26. 70
      local_plugins/location_service/android/build.gradle
  27. 1
      local_plugins/location_service/android/settings.gradle
  28. 7
      local_plugins/location_service/android/src/main/AndroidManifest.xml
  29. 87
      local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt
  30. 32
      local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationServicePlugin.kt
  31. 38
      local_plugins/location_service/ios/.gitignore
  32. 29
      local_plugins/location_service/ios/location_service.podspec
  33. 32
      local_plugins/location_service/ios/location_service/Package.swift
  34. 19
      local_plugins/location_service/ios/location_service/Sources/location_service/LocationServicePlugin.swift
  35. 14
      local_plugins/location_service/ios/location_service/Sources/location_service/PrivacyInfo.xcprivacy
  36. 49
      local_plugins/location_service/lib/location_service.dart
  37. 72
      local_plugins/location_service/pubspec.yaml
  38. 3
      local_plugins/ota/ios/Classes/BleManager/JLBleManager.h
  39. 89
      local_plugins/ota/ios/Classes/BleManager/JLBleManager.m
  40. 35
      local_plugins/ota/ios/Classes/OtaPlugin.swift
  41. 4
      local_plugins/ota/lib/ota.dart
  42. 5
      local_plugins/ota/lib/ota_method_channel.dart
  43. 4
      local_plugins/ota/lib/ota_platform_interface.dart
  44. 2
      pubspec.yaml

12
.metadata

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
revision: "ea121f8859e4b13e47a8f845e4586164519588bc"
revision: "5c1433509f997e9059d230942bdf83f1b88a179e"
channel: "stable"
project_type: app
@ -13,11 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: ea121f8859e4b13e47a8f845e4586164519588bc
base_revision: ea121f8859e4b13e47a8f845e4586164519588bc
- platform: ios
create_revision: ea121f8859e4b13e47a8f845e4586164519588bc
base_revision: ea121f8859e4b13e47a8f845e4586164519588bc
create_revision: 5c1433509f997e9059d230942bdf83f1b88a179e
base_revision: 5c1433509f997e9059d230942bdf83f1b88a179e
- platform: android
create_revision: 5c1433509f997e9059d230942bdf83f1b88a179e
base_revision: 5c1433509f997e9059d230942bdf83f1b88a179e
# User provided section

7
android/app/src/debug/AndroidManifest.xml

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

5
android/app/src/main/kotlin/com/yunqiinnovation/deep_voice/MainActivity.kt

@ -0,0 +1,5 @@
package com.yunqiinnovation.deep_voice
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

7
android/app/src/profile/AndroidManifest.xml

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

53
lib/data/services/location_manager.dart

@ -0,0 +1,53 @@
import 'package:deep_voice/core/utils/logger.dart';
import 'package:get/get.dart';
import 'package:location_service/location_service.dart';
import 'package:music_service/music_service.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../core/utils/permission_util.dart';
import 'package:flutter/material.dart';
//音乐播放管理
class LocationManager extends GetxService {
static LocationManager get to => Get.find<LocationManager>();
// 音乐服务实例
final LocationService _musicService = LocationService();
/// 初始化
@override
void onInit() {
super.onInit();
requestNotificationPermission();
Logger.info('初始化权限请求');
}
Future<void> requestNotificationPermission() async {
final granted = await PermissionUtil.instance.requestPermission(
// context: context,
permissionType: Permission.notification,
permissionName: '通知',
explanationText: '需要通知权限来显示音乐播放状态。',
permanentDenialText: '您已永久拒绝通知权限,请前往设置开启权限。',
settingsButtonText: '去设置',
cancelButtonText: '取消',
);
if (!granted) {
Logger.warning('通知权限未被授予');
return;
}
Logger.info('通知权限已授予');
}
/// 释放资源
@override
void onClose() {
super.onClose();
}
//定位
Future<Map<String, double>> location() async {
return await _musicService.location();
}
}

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

@ -1,3 +1,5 @@
import 'package:ble_service/ble_service.dart';
import 'package:classic_bluetooth/classic_bluetooth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:path/path.dart';
@ -30,6 +32,8 @@ class OtaUpgradeController extends GetxController {
// 当前设备固件版本号
final RxString currentVersion = ''.obs;
String _address = '';
// 更新包地址,用于下载更新文件
String updatepackageaddress = '';
@ -50,6 +54,7 @@ class OtaUpgradeController extends GetxController {
// 蓝牙管理服务,用于获取设备信息和通信
final _bleManager = Get.find<BleManager>();
final BleService _bleService = BleService();
final maxRetryAttempts = 3; // 最大重试次数
int currentRetryCount = 0;
@ -58,6 +63,7 @@ class OtaUpgradeController extends GetxController {
final RxDouble progress = 0.0.obs; // 控制器自己的可观察属性
final RxInt type = OtaEvent.NOT_STARTED.obs;
final RxBool isUpgrade = false.obs;
@override
void onInit() {
super.onInit();
@ -67,6 +73,7 @@ class OtaUpgradeController extends GetxController {
@override
void onClose() {
jlota.onOtaClose();
_bleService.setEnterOta(false);
isUpgrade.value = false;
super.onClose();
}
@ -156,7 +163,7 @@ class OtaUpgradeController extends GetxController {
final firstPart = firmwareParts.sublist(0, 3).join('.');
final secondPart = firmwareParts.sublist(3).join('.');
print("firstPart${firstPart},secondPart=${secondPart}");
// 判断两部分是否一致
// 判断两部分是否一致
if (firstPart != secondPart) {
throw "固件版本格式错误:${deviceInfo.firmwareVersion}";
}
@ -164,6 +171,19 @@ class OtaUpgradeController extends GetxController {
// 正常流程
currentVersion.value = firstPart;
print('getequipmentinfo,id: ${deviceInfo.firmwareProductId}');
final connectedDevices = await ClassicBluetooth.getConnectedDevices();
if (connectedDevices.isNotEmpty) {
_address = connectedDevices.first.address;
}
await _bleService.setEnterOta(true);
await _bleService.disconnect();
await Future.delayed(const Duration(seconds: 2));
jlota.init();
// API请求
final response = await Api.getequipmentinfo({
"id": deviceInfo.firmwareProductId,
@ -368,7 +388,7 @@ class OtaUpgradeController extends GetxController {
// 开始升级
void startUpgrading(String path) async {
jlota.startUpgrading(address: 'E9:09:05:E0:18:7A', filePath: path);
jlota.startUpgrading(address: _address, filePath: path);
return;
// isUpgrade.value = false;
}
@ -402,6 +422,7 @@ class OtaUpgradeController extends GetxController {
if (event.event == 'onError') {
type.value = OtaEvent.UPGRADE_FAILED;
isUpgrade.value = false;
Get.snackbar('升级失败', '请检查设备连接或升级包');
}
if (event.event == 'NOCONNECTION') {
type.value = OtaEvent.CONNECTION_DISCONNECTION;

1
local_plugins/agent_service/android/build.gradle.kts

@ -49,4 +49,5 @@ dependencies {
add("compileOnly", project(":amap_service"))
add("compileOnly", project(":speech"))
add("compileOnly", project(":chat_api"))
add("compileOnly", project(":location_service"))
}

15
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt

@ -9,6 +9,7 @@ import com.yunqiinnovation.chat_api.StreamCallback
import com.yunqiinnovation.chat_storage.ChatStorageHelper
import com.yunqiinnovation.music_service.MusicServiceStarter
import com.yunqiinnovation.amap_service.NavigationServiceHelper
import com.yunqiinnovation.location_service.LocationService
import android.util.Log
import kotlinx.coroutines.*
import java.util.Collections
@ -125,8 +126,8 @@ object AgentService : CoroutineScope {
this.chatApiService = ChatApiService(context)
this.chatStorageHelper = ChatStorageHelper(context)
this.audioPlayer = AudioPlayer(context)
//初始化定位服务
LocationService.initialize(appContext)
// 设置系统提示词
val customSystemPrompt = config["systemPrompt"] as? String
if (!customSystemPrompt.isNullOrBlank()) {
@ -621,8 +622,14 @@ object AgentService : CoroutineScope {
// 先添加系统提示词
if (systemPrompt.isNotEmpty()) {
messagesWithSystemPrompt.put(createSystemMessage(systemPrompt))
// Log.d(TAG, "系统提示词: ${systemPrompt}")
val location = LocationService.getCachedLocation()?: mapOf(
"user_location" to "定位未开启",
"user_lat" to "定位未开启",
"user_lng" to "定位未开启",
)
val _systemPrompt = Utils.fillTemplate(systemPrompt,location)
messagesWithSystemPrompt.put(createSystemMessage(_systemPrompt))
Log.d(TAG, "liwei--------- 系统提示词: ${_systemPrompt}")
}
// 再添加历史消息

13
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/utils.kt

@ -141,4 +141,15 @@ object Utils {
false
}
}
}
/**
* 替换字符串 变量
* */
fun fillTemplate(tpl: String, data: Map<String, String>): String {
var result = tpl
data.forEach { (k, v) ->
result = result.replace("{$k}", v)
}
return result
}
}

2
local_plugins/amap_service/android/build.gradle

@ -48,7 +48,7 @@ android {
}
dependencies {
implementation 'com.amap.api:navi-3dmap:latest.integration'
implementation('com.amap.api:navi-3dmap:latest.integration')
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.mockito:mockito-core:5.0.0")
}

3
local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationServiceHelper.kt

@ -105,4 +105,7 @@ object NavigationServiceHelper {
}
}
}

39
local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift

@ -43,6 +43,10 @@ public class BleService: NSObject {
private var audioCharacteristic: CBCharacteristic?
private var isScanning = false
// 是否是ota模式
var isEnterOta = false
private var callbacks = [Callback]()
private var connectionState = BleConst.STATE_DISCONNECTED
@ -212,6 +216,9 @@ public class BleService: NSObject {
/// 统一的连接方法,支持恢复和重连
private func connectPeripheral(_ peripheral: CBPeripheral) {
if isEnterOta {
return
}
guard let manager = centralManager else {
return
}
@ -592,19 +599,14 @@ public class BleService: NSObject {
]
if !responseData.isEmpty {
if let versionStr = String(data: responseData, encoding: .utf8) {
resultMap["version"] = versionStr
os_log("接收到目标版本号: %@", log: logger, type: .info, versionStr)
} else {
// 如果无法解析为字符串,以点分隔的数字表示版本
let versionNums = responseData
.map { $0 }
.filter { $0 != 0xFF } // 过滤掉无效字节
.map { String($0) }
.joined(separator: ".")
resultMap["version"] = versionNums
os_log("接收到目标版本号(数字格式): %@", log: logger, type: .info, versionNums)
}
// 如果无法解析为字符串,以点分隔的数字表示版本
let versionNums = responseData
.map { $0 }
.filter { $0 != 0xFF } // 过滤掉无效字节
.map { String($0) }
.joined(separator: ".")
resultMap["version"] = versionNums
os_log("接收到目标版本号(数字格式): %@", log: logger, type: .info, versionNums)
}
notifyDeviceInfoReceived(infoType: Int(cmd), infoData: resultMap)
@ -616,13 +618,8 @@ public class BleService: NSObject {
"rawData": responseData
]
if responseData.count >= 2 {
let productId = UInt16(responseData[0]) << 8 | UInt16(responseData[1])
resultMap["productId"] = productId
os_log("接收到产品ID: %d", log: logger, type: .info, productId)
} else if !responseData.isEmpty {
// 单字节产品ID
let productId = UInt16(responseData[0])
if !responseData.isEmpty {
let productId = responseData.map { String($0 & 0xFF) }.joined(separator: ".")
resultMap["productId"] = productId
os_log("接收到产品ID: %d", log: logger, type: .info, productId)
}
@ -1263,4 +1260,4 @@ extension BleService: CBPeripheralDelegate {
os_log("✅ 描述符写入成功: %{public}@", log: logger, type: .debug, descriptor.uuid.uuidString)
}
}
}
}

11
local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftBleServicePlugin.swift

@ -150,6 +150,15 @@ public class SwiftBleServicePlugin: NSObject, FlutterPlugin {
case "isBleConnected":
result(BleService.shared.isConnected())
case "setEnterOta":
let arguments = call.arguments as? [String: Any]
BleService.shared.isEnterOta = (arguments?["enterOta"] as? Bool) ?? false
result("")
case "disconnect":
BleService.shared.disconnect()
result("")
case "registerBackgroundScan", "unregisterBackgroundScan":
// iOS上禁用,直接返回成功以避免Flutter端报错
@ -380,4 +389,4 @@ class BleStatusStreamHandler: NSObject, FlutterStreamHandler {
plugin?.statusEventSink = nil
return nil
}
}
}

56
local_plugins/ble_service/lib/ble_service.dart

@ -183,34 +183,35 @@ class BleService {
return false;
}
}
//========================================================================
// 以下是Companion Device相关功能
//========================================================================
/// 检查设备是否支持Companion Device Manager
///
///
/// 返回设备是否支持Companion Device Manager API
/// 仅在Android 8.0及以上版本可用
Future<bool> isCompanionSupported() async {
try {
final result = await _methodChannel.invokeMethod<bool>('isCompanionSupported');
final result =
await _methodChannel.invokeMethod<bool>('isCompanionSupported');
return result ?? false;
} catch (e) {
print('检查Companion设备支持失败: $e');
return false;
}
}
/// 启动设备关联流程
///
///
/// 弹出系统设备选择对话框,用户选择后将建立持久化关联
/// 关联成功后,应用将获得后台扫描和启动前台服务的特权
///
///
/// [deviceNamePattern] 设备名称正则表达式,例如"MyHeadset.*"
/// [serviceUuid] 可选的服务UUID
/// [macAddress] 可选的MAC地址
///
///
/// 返回是否成功启动关联流程
Future<bool> startDeviceAssociation({
String? deviceNamePattern,
@ -232,13 +233,14 @@ class BleService {
return false;
}
}
/// 获取已关联的设备MAC地址
///
///
/// 返回已关联的设备MAC地址,如果没有则返回null
Future<String?> getPairedMacAddress() async {
try {
final result = await _methodChannel.invokeMethod<String>('getPairedMacAddress');
final result =
await _methodChannel.invokeMethod<String>('getPairedMacAddress');
return result;
} catch (e) {
print('获取配对MAC地址失败: $e');
@ -246,9 +248,8 @@ class BleService {
}
}
/// 检查BLE连接状态
///
///
/// 返回BLE是否已连接
Future<bool> isBleConnected() async {
try {
@ -260,11 +261,31 @@ class BleService {
}
}
/// 设置是否是ota模式
Future<void> setEnterOta(bool enterOta) async {
try {
await _methodChannel.invokeMethod<String>('setEnterOta', {
'enterOta': enterOta,
});
} catch (e) {
print('设置是否是ota模式失败: $e');
}
}
/// 断开连接
Future<void> disconnect() async {
try {
await _methodChannel.invokeMethod<String>('disconnect');
} catch (e) {
print('断开连接失败: $e');
}
}
/// 注册BLE后台扫描
///
/// 注册后,即使应用被杀死,系统也会在检测到设备广播时唤醒应用
/// 这是确保设备可以在应用后台时被自动连接的关键功能
///
///
/// [macAddress] 可选的设备MAC地址,如果不提供则使用已关联的设备
Future<bool> registerBackgroundScan({String? macAddress}) async {
try {
@ -290,13 +311,14 @@ class BleService {
return false;
}
}
/// 清除所有设备关联
///
///
/// 用于重置设备关联状态,通常在用户主动解除关联或调试时使用
Future<bool> clearAssociations() async {
try {
final result = await _methodChannel.invokeMethod<bool>('clearAssociations');
final result =
await _methodChannel.invokeMethod<bool>('clearAssociations');
return result ?? false;
} catch (e) {
print('清除设备关联失败: $e');

33
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt

@ -308,7 +308,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
Log.e("ChatApiService", "消息列表为空")
throw IllegalArgumentException("消息列表不能为空")
}
// Log.e("ChatApiService", "AI解析工具列表: ${tools}")
val chatsFlow = try {
val chatCompletionRequest = ChatCompletionRequest(
model = ModelId(currentModel),
@ -333,7 +334,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
chatsFlow.collect { result ->
if (isCanceled) return@collect
val choice = result.choices?.firstOrNull()
val choice = result.choices.firstOrNull()
if (choice == null) {
Log.e("ChatApiService", "choice为null")
return@collect
@ -353,37 +354,34 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
// 收集工具调用信息
delta.toolCalls?.forEach { toolCall ->
val index = toolCall.index
// 创建或获取现有的工具调用信息
val toolCallInfo = toolCalls.getOrPut(index) { ToolCallInfo() }
// 安全处理工具调用ID
try {
toolCall.id?.let { id ->
toolCallInfo.id = id.toString()
toolCall.id?.let { i ->
toolCallInfo.id = i.toString()
}
} catch (e: Exception) {
Log.e("ChatApiService", "处理工具调用ID异常: ${e.message}")
}
// 安全处理函数信息
try {
toolCall.function?.let { function ->
try {
function.name?.let { name ->
function.name.let { name ->
toolCallInfo.name = name
}
} catch (e: Exception) {
Log.e("ChatApiService", "处理工具调用函数名称异常: ${e.message}")
Log.e("ChatApiService", "处理工具调用函数名称异常! function:${toolCall.function} err:${e.message}")
}
try {
function.arguments?.let { args ->
function.arguments.let { args ->
toolCallInfo.arguments += args
}
} catch (e: Exception) {
Log.e("ChatApiService", "处理工具调用参数异常: ${e.message}")
}
Log.e("ChatApiService", "AI发起工具调用:${toolCallInfo.name} ${toolCallInfo.arguments}")
}
} catch (e: Exception) {
Log.e("ChatApiService", "处理工具调用函数信息异常: ${e.message}")
@ -417,11 +415,9 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
Log.e("ChatApiService", "查找有效工具调用异常: ${e.message}")
null
}
if (firstToolCall == null) {
return false
}
// 创建函数调用字典
val functionCall = try {
mapOf(
@ -433,10 +429,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
Log.e("ChatApiService", "创建函数调用字典异常: ${e.message}")
return false
}
// 通知上层工具调用事件
streamCallback?.onFunctionCall(convertMapToJsonObject(functionCall))
// 在后台队列处理工具调用
launch {
try {
@ -444,14 +438,13 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
// 通过MCP客户端处理工具调用
val functionName = firstToolCall.name
val argumentsJson = firstToolCall.arguments
val result = if (_mcpClient?.hasToolWithName(functionName) == true) {
// 解析参数
val arguments = _mcpClient?.parseJsonArguments(argumentsJson) ?: emptyMap()
// 调用MCP工具
val toolResult = _mcpClient?.callTool(functionName, arguments)
Log.e("ChatApiService", "AI调用工具结果: $functionName}:$arguments} toolResult:${toolResult}")
// 处理结果
if (toolResult != null) {
if (toolResult["isError"] == true) {
@ -478,14 +471,12 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
// 工具不存在
mapOf("context" to "Tool not found: $functionName")
}
if (!isCanceled) {
// 处理结果
streamCallback?.onFunctionCallResult(
convertMapToJsonObject(functionCall),
convertMapToJsonObject(result)
)
// 将结果发送回OpenAI继续对话
sendFunctionCallResultInternal(
messages = currentMessages,
@ -697,6 +688,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
description = description,
parameters = parameters
)
Log.e("ChatApiService", "AI携带工具:${name}:${parameters}")
tools.add(tool)
} catch (e: Exception) {
@ -855,12 +847,11 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
nameOrNull = name,
argumentsOrNull = arguments
)
val toolCall = ToolCall.Function(
id = ToolId(id),
function = functionCall
)
toolCallsList.add(toolCall)
}
}

4
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/CustomSseClientTransport.kt

@ -107,7 +107,9 @@ class CustomSseClientTransport(
"open" -> {
// SSE连接已打开
}
"ping" -> {
// 心跳
}
"endpoint" -> {
try {
val eventData = event.data ?: ""

9
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MCPSubClient.kt

@ -77,11 +77,11 @@ class MCPSubClient(
}
// 连接到服务器
client?.connect(transport)
client.connect(transport)
// 获取可用工具列表
try {
val toolsResult = client?.listTools()
val toolsResult = client.listTools()
if (toolsResult != null) {
availableTools.clear()
val filtered = toolsResult.tools.filter { tool ->
@ -129,7 +129,7 @@ class MCPSubClient(
"properties" to emptyMap<String, Any>(),
"required" to emptyList<String>()
)
Log.w(TAG, "解析工具数据: ${tool.name} ${parametersMap}")
val toolMap = mapOf(
"type" to "function",
"function" to mapOf(
@ -229,7 +229,7 @@ class MCPSubClient(
val required = mutableListOf<String>()
// 处理properties
inputSchema.properties?.let { propsJsonObject ->
inputSchema.properties.let { propsJsonObject ->
for ((key, value) in propsJsonObject) {
when (value) {
is JsonPrimitive -> {
@ -260,7 +260,6 @@ class MCPSubClient(
"properties" to properties,
"required" to required
)
return result
}

20
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt

@ -127,16 +127,16 @@ class SystemFunctionHandler(private val context: Context? = null) {
)
// 注册获取当前位置函数
client.registerLocalFunction(
name = "get_current_location",
description = "获取当前地理位置",
parameters = mapOf(
"type" to "object",
"properties" to emptyMap<String, Any>(),
"required" to emptyList<String>()
),
handler = GetCurrentLocationHandler(context)
)
// client.registerLocalFunction(
// name = "get_current_location",
// description = "获取当前地理位置",
// parameters = mapOf(
// "type" to "object",
// "properties" to emptyMap<String, Any>(),
// "required" to emptyList<String>()
// ),
// handler = GetCurrentLocationHandler(context)
// )
// 注册媒体播放功能
client.registerLocalFunction(

33
local_plugins/location_service/.gitignore

@ -0,0 +1,33 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
build/

33
local_plugins/location_service/.metadata

@ -0,0 +1,33 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "5c1433509f997e9059d230942bdf83f1b88a179e"
channel: "stable"
project_type: plugin
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 5c1433509f997e9059d230942bdf83f1b88a179e
base_revision: 5c1433509f997e9059d230942bdf83f1b88a179e
- platform: android
create_revision: 5c1433509f997e9059d230942bdf83f1b88a179e
base_revision: 5c1433509f997e9059d230942bdf83f1b88a179e
- platform: ios
create_revision: 5c1433509f997e9059d230942bdf83f1b88a179e
base_revision: 5c1433509f997e9059d230942bdf83f1b88a179e
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

3
local_plugins/location_service/CHANGELOG.md

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

1
local_plugins/location_service/LICENSE

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

15
local_plugins/location_service/README.md

@ -0,0 +1,15 @@
# location_service
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter
[plug-in package](https://flutter.dev/to/develop-plugins),
a specialized package that includes platform-specific implementation code for
Android and/or iOS.
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

4
local_plugins/location_service/analysis_options.yaml

@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

9
local_plugins/location_service/android/.gitignore

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

70
local_plugins/location_service/android/build.gradle

@ -0,0 +1,70 @@
group = "com.yunqiinnovation.location_service"
version = "1.0-SNAPSHOT"
buildscript {
ext.kotlin_version = "2.1.0"
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:8.7.3")
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.yunqiinnovation.location_service"
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 应该写在这里(android {} 外面)
dependencies {
implementation('com.amap.api:navi-3dmap:latest.integration')
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.mockito:mockito-core:5.0.0")
}
testOptions {
unitTests.all {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen {false}
showStandardStreams = true
}
}
}
}

1
local_plugins/location_service/android/settings.gradle

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

7
local_plugins/location_service/android/src/main/AndroidManifest.xml

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yunqiinnovation.location_service">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>

87
local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt

@ -0,0 +1,87 @@
package com.yunqiinnovation.location_service
import android.content.Context
import android.location.Location
import android.util.Log
import com.amap.api.location.*
import io.flutter.plugin.common.MethodChannel
object LocationService {
private const val TAG = "LocationService"
private var locationClient: AMapLocationClient? = null
private var currentLocation: AMapLocation? = null
private var initialized = false
fun initialize(context: Context) {
if (initialized) return
Log.e(TAG, "liwei--------- 初始化高德定位服务 1")
try {
AMapLocationClient.updatePrivacyShow(context, true, true)
AMapLocationClient.updatePrivacyAgree(context, true)
Log.e(TAG, "liwei--------- 初始化高德定位服务 2")
locationClient = AMapLocationClient(context.applicationContext)
val option = AMapLocationClientOption().apply {
locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy
isOnceLocation = false
interval = 5000L
}
Log.e(TAG, "liwei--------- 初始化高德定位服务 3")
locationClient?.setLocationOption(option)
Log.e(TAG, "liwei--------- 初始化高德定位服务 4")
locationClient?.setLocationListener { location ->
if (location != null && location.errorCode == 0) {
currentLocation = location
Log.d(TAG, "liwei---------定位更新: ${location.latitude}, ${location.longitude}")
} else {
Log.w(TAG, "liwei---------定位失败: ${location?.errorCode} - ${location?.errorInfo}")
}
}
Log.e(TAG, "liwei--------- 初始化高德定位服务 5")
locationClient?.startLocation()
Log.e(TAG, "liwei--------- 初始化高德定位服务 6")
initialized = true
} catch (e: Exception) {
Log.e(TAG, "高德定位初始化失败: ${e.message}", e)
}
}
fun getCachedLocation(): Map<String, String>? {
// // 定位成功,获取经纬度
// val latitude = location.latitude
// val longitude = location.longitude
//
// // 获取结构化地址
// val province = location.province // 省
// val city = location.city // 市
// val district = location.district // 区县
// val street = location.street // 街道
// val streetNum = location.streetNum // 门牌号
// val adCode = location.adCode // 行政区划代码
// val address = location.address // 详细地址拼接字符串
return currentLocation?.let {
mapOf(
"user_location" to it.address,
"user_lat" to it.latitude.toString(),
"user_lng" to it.longitude.toString(),
)
}
}
fun getCurrentLocation(result: MethodChannel.Result) {
val cached = getCachedLocation()
if (cached != null) {
result.success(cached)
} else {
result.error("NO_LOCATION", "当前位置还未获取", null)
}
}
fun stop() {
locationClient?.stopLocation()
locationClient?.onDestroy()
locationClient = null
currentLocation = null
initialized = false
}
}

32
local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationServicePlugin.kt

@ -0,0 +1,32 @@
package com.yunqiinnovation.location_service
import android.content.Context
import android.util.Log
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
/** LocationServicePlugin */
class LocationServicePlugin: FlutterPlugin, MethodCallHandler {
private val TAG = "LocationServicePlugin"
private lateinit var context: Context
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.yunqiinnovation.location_service")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
Log.d(TAG, "Method call: ${call.method}")
when (call.method) {
"location" -> LocationService.getCurrentLocation(result)
else -> result.notImplemented()
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {}
}

38
local_plugins/location_service/ios/.gitignore

@ -0,0 +1,38 @@
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/ephemeral/
/Flutter/flutter_export_environment.sh

29
local_plugins/location_service/ios/location_service.podspec

@ -0,0 +1,29 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint location_service.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'location_service'
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 = 'location_service/Sources/location_service/**/*'
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 = {'location_service_privacy' => ['location_service/Sources/location_service/PrivacyInfo.xcprivacy']}
end

32
local_plugins/location_service/ios/location_service/Package.swift

@ -0,0 +1,32 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "location_service",
platforms: [
.iOS("12.0")
],
products: [
.library(name: "location-service", targets: ["location_service"])
],
dependencies: [],
targets: [
.target(
name: "location_service",
dependencies: [],
resources: [
// 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 these lines. For more information, see
// https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
// .process("PrivacyInfo.xcprivacy"),
// If you have other resources that need to be bundled with your plugin, refer to
// the following instructions to add them:
// https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package
]
)
]
)

19
local_plugins/location_service/ios/location_service/Sources/location_service/LocationServicePlugin.swift

@ -0,0 +1,19 @@
import Flutter
import UIKit
public class LocationServicePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "location_service", binaryMessenger: registrar.messenger())
let instance = LocationServicePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("iOS " + UIDevice.current.systemVersion)
default:
result(FlutterMethodNotImplemented)
}
}
}

14
local_plugins/location_service/ios/location_service/Sources/location_service/PrivacyInfo.xcprivacy

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>

49
local_plugins/location_service/lib/location_service.dart

@ -0,0 +1,49 @@
import 'package:flutter/services.dart';
class LocationService {
static final LocationService _instance = LocationService._internal();
/// 获取单例
factory LocationService() => _instance;
// 方法通道
final MethodChannel _methodChannel = MethodChannel(
'com.yunqiinnovation.location_service',
);
// 事件通道 - 简化为只使用数据和状态通道
late final EventChannel _dataEventChannel;
late final EventChannel _statusEventChannel;
// 事件流
late final Stream<dynamic> _dataStream;
late final Stream<dynamic> _statusStream;
// 获取事件流
Stream<dynamic> get onDataReceived => _dataStream;
// 状态流直接传递原始数据
Stream<dynamic> get onStatusUpdated => _statusStream;
// 内部构造函数
LocationService._internal() {
_dataEventChannel = const EventChannel(
'com.yunqiinnovation.music_service/data_events',
);
_statusEventChannel = const EventChannel(
'com.yunqiinnovation.music_service/status_events',
);
// 初始化事件流
_dataStream = _dataEventChannel.receiveBroadcastStream();
_statusStream = _statusEventChannel.receiveBroadcastStream();
}
Future<Map<String, double>> location() async {
final result = await _methodChannel.invokeMethod<Map>('location');
return {
'latitude': result?['longitude'] ?? 0.0,
'longitude': result?['longitude'] ?? 0.0,
};
}
}

72
local_plugins/location_service/pubspec.yaml

@ -0,0 +1,72 @@
name: location_service
description: "A new Flutter project."
version: 0.0.1
homepage:
environment:
sdk: ^3.8.1
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.yunqiinnovation.location_service
pluginClass: LocationServicePlugin
ios:
pluginClass: LocationServicePlugin
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/to/asset-from-package
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/to/font-from-package

3
local_plugins/ota/ios/Classes/BleManager/JLBleManager.h

@ -15,6 +15,7 @@
#import <JL_OTALib/JL_OTALib.h>
#import <JL_HashPair/JL_HashPair.h>
#import <JL_AdvParse/JL_AdvParse.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@ -63,6 +64,8 @@ extern NSString *kFLT_BLE_DISCONNECTED; //断开连接
@property (strong, nonatomic) JLBleEntity *__nullable currentEntity;
@property (nonatomic, copy, nullable) FlutterEventSink flutterEventSink;
/**
* 单例
*/

89
local_plugins/ota/ios/Classes/BleManager/JLBleManager.m

@ -206,8 +206,17 @@ NSString *FLT_BLE_RCSP_R = @"AE02"; //命令“读”通道
if (ble_name.length == 0) return;
kJLLog(JLLOG_DEBUG, @"发现 ----> NAME:%@ RSSI:%@ AD:%@", ble_name,RSSI,ble_AD);
[self addPeripheral:peripheral RSSI:RSSI Name:ble_name Info:ble_AD];
if (ble_AD.length >= 29) {
const uint8_t *bytes = ble_AD.bytes;
NSMutableString *macStr = [NSMutableString string];
for (int i = 7; i <= 12; i++) {
uint8_t macByte = bytes[i] ^ 0x5A;
[macStr appendFormat:@"%02X", macByte];
if (i < 12) [macStr appendString:@":"];
}
[self addPeripheral:peripheral Info:macStr];
}
[[NSNotificationCenter defaultCenter] postNotificationName:kFLT_BLE_FOUND object:_blePeripheralArr userInfo:nil];
@ -217,11 +226,11 @@ NSString *FLT_BLE_RCSP_R = @"AE02"; //命令“读”通道
}
}
- (void)addPeripheral:(CBPeripheral*)peripheral RSSI:(NSNumber *)rssi Name:(NSString*)name Info:(NSData*)blead{
// if ([name isEqualToString:@"DeapSound"]) {
// [self stopScanBLE];
// [self connectBLE:peripheral];
// }
- (void)addPeripheral:(CBPeripheral*)peripheral Info:(NSString*)macStr {
if (self.lastAddress && [macStr isEqualToString:self.lastAddress]) {
[self stopScanBLE];
[self connectBLE:peripheral];
}
}
#pragma mark 设备连接回调
@ -349,13 +358,7 @@ NSString *FLT_BLE_RCSP_R = @"AE02"; //命令“读”通道
if (needForcedUpgrade) {
kJLLog(JLLOG_DEBUG, @"设备需要强制升级,请到升级界面选择ota升级文件进行升级!");
}
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *otaFilePath = [documentsPath stringByAppendingPathComponent:@"update030.ufw"];
if ([[NSFileManager defaultManager] fileExistsAtPath:otaFilePath]) {
[self otaFuncWithFilePath:otaFilePath];
} else {
kJLLog(JLLOG_DEBUG, @"OTA文件不存在: %@", otaFilePath);
}
[self otaFuncWithFilePath:self.lastFilePath];
}];
});
}
@ -396,6 +399,15 @@ NSString *FLT_BLE_RCSP_R = @"AE02"; //命令“读”通道
*/
- (void)otaFuncWithFilePath:(NSString *)otaFilePath {
kJLLog(JLLOG_DEBUG, @"current otaFilePath ---> %@", otaFilePath);
if (self.flutterEventSink) {
self.flutterEventSink(@{
@"event": @"onStartOTA",
@"type": @0,
@"progress": @0.0,
@"code": @8,
@"message": @"开始升级"
});
}
self.selectedOtaFilePath = otaFilePath;
NSData *otaData = [[NSData alloc] initWithContentsOfFile:otaFilePath];
@ -425,6 +437,55 @@ NSString *FLT_BLE_RCSP_R = @"AE02"; //命令“读”通道
-(void)otaUpgradeResult:(JL_OTAResult)result Progress:(float)progress{
//TODO: 设备升级过程回调,包括进度状态
kJLLog(JLLOG_DEBUG, @"otaUpgradeResult ---> %u", result);
if (result == JL_OTAResultPreparing) {
if (self.flutterEventSink) {
self.flutterEventSink(@{
@"event": @"onProgress",
@"type": @0,
@"progress": @(progress * 100),
@"code": @(result),
@"message": @"开始升级"
});
}
} else if (result == JL_OTAResultUpgrading) {
if (self.flutterEventSink) {
self.flutterEventSink(@{
@"event": @"onProgress",
@"type": @1,
@"progress": @(progress * 100),
@"code": @(result),
@"message": @"开始升级"
});
}
} else if (result == JL_OTAResultPrepared) {
} else if (result == JL_OTAResultReconnect) {
} else if (result == JL_OTAResultReconnectWithMacAddr) {
} else if (result == JL_OTAResultSuccess) {
if (self.flutterEventSink) {
self.flutterEventSink(@{
@"event": @"onStopOTA",
@"type": @0,
@"progress": @0.0,
@"code": @(result),
@"message": @"升级成功"
});
}
} else if (result == JL_OTAResultReboot) {
} else {
if (self.flutterEventSink) {
self.flutterEventSink(@{
@"event": @"onError",
@"type": @0,
@"progress": @0.0,
@"code": @(result),
@"message": @"升级失败"
});
}
}
}
-(void)otaDataSend:(NSData *)data{

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

@ -1,18 +1,24 @@
import Flutter
public class OtaPlugin: NSObject, FlutterPlugin {
let userManager = JLBleManager.sharedInstance()
public class OtaPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
var userManager: JLBleManager?
var eventSink: FlutterEventSink?
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "ota", binaryMessenger: registrar.messenger())
let eventChannel = FlutterEventChannel(name: "com.example.ota/events", binaryMessenger: registrar.messenger())
let instance = OtaPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
eventChannel.setStreamHandler(instance)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? [String: Any]
switch call.method {
case "init":
userManager = JLBleManager.sharedInstance()
result("")
case "startUpgrading":
let address = arguments?["address"] as? String
let filePath = arguments?["filePath"] as? String
@ -26,14 +32,31 @@ public class OtaPlugin: NSObject, FlutterPlugin {
}
}
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
eventSink = events
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
eventSink = nil
userManager?.flutterEventSink = nil
return nil
}
private func startUpgrading(address: String?, filePath: String?) {
if userManager.mBleManagerState == CBManagerState.poweredOn {
userManager.startScanBLE(address, filePath: filePath)
if userManager?.mBleManagerState == CBManagerState.poweredOn {
userManager?.flutterEventSink = eventSink
userManager?.startScanBLE(address, filePath: filePath)
DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) {
self.userManager.stopScanBLE()
self.userManager?.stopScanBLE()
}
}
}
private func onOtaClose() {}
private func onOtaClose() {
userManager?.stopScanBLE()
userManager?.otaFuncCancel{code in}
userManager?.disconnectBLE()
userManager = nil
}
}

4
local_plugins/ota/lib/ota.dart

@ -132,6 +132,10 @@ class Ota {
// _isEventListenerSet = true;
// }
Future<void> init() {
return OtaPlatform.instance.init();
}
Future<void> startUpgrading({String? address, String? filePath}) {
return OtaPlatform.instance.startUpgrading(
address: address,

5
local_plugins/ota/lib/ota_method_channel.dart

@ -9,6 +9,11 @@ class MethodChannelOta extends OtaPlatform {
@visibleForTesting
final methodChannel = const MethodChannel('ota');
@override
Future<void> init() async {
await methodChannel.invokeMethod<String>('init');
}
@override
Future<void> startUpgrading({String? address, String? filePath}) async {
await methodChannel.invokeMethod<String>('startUpgrading', {

4
local_plugins/ota/lib/ota_platform_interface.dart

@ -23,6 +23,10 @@ abstract class OtaPlatform extends PlatformInterface {
_instance = instance;
}
Future<void> init() {
throw UnimplementedError('init() has not been implemented.');
}
Future<void> startUpgrading({String? address, String? filePath}) {
throw UnimplementedError('startUpgrading() has not been implemented.');
}

2
pubspec.yaml

@ -91,6 +91,8 @@ dependencies:
path: local_plugins/music_service
amap_service:
path: local_plugins/amap_service
location_service:
path: local_plugins/location_service
realtime:
path: local_plugins/realtime

Loading…
Cancel
Save