From 4f97c1685065ed6a6bca8a8b09ceac410032aec6 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Fri, 13 Jun 2025 19:24:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0ai=E6=B5=8B=E8=AF=95=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .metadata | 12 +-- android/app/src/debug/AndroidManifest.xml | 7 ++ .../deep_voice/MainActivity.kt | 5 ++ android/app/src/profile/AndroidManifest.xml | 7 ++ lib/data/services/location_manager.dart | 53 +++++++++++ .../agent_service/android/build.gradle.kts | 1 + .../agent_service/AgentService.kt | 15 +++- .../yunqiinnovation/agent_service/utils.kt | 13 ++- .../amap_service/android/build.gradle | 2 +- .../amap_service/NavigationServiceHelper.kt | 3 + .../chat_api/ChatApiService.kt | 11 +-- .../yunqiinnovation/chat_api/MCPSubClient.kt | 7 +- .../chat_api/SystemFunctionHandler.kt | 20 ++--- local_plugins/location_service/.gitignore | 33 +++++++ local_plugins/location_service/.metadata | 33 +++++++ local_plugins/location_service/CHANGELOG.md | 3 + local_plugins/location_service/LICENSE | 1 + local_plugins/location_service/README.md | 15 ++++ .../location_service/analysis_options.yaml | 4 + .../location_service/android/.gitignore | 9 ++ .../location_service/android/build.gradle | 70 +++++++++++++++ .../location_service/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 7 ++ .../location_service/LocationService.kt | 87 +++++++++++++++++++ .../location_service/LocationServicePlugin.kt | 32 +++++++ local_plugins/location_service/ios/.gitignore | 38 ++++++++ .../ios/location_service.podspec | 29 +++++++ .../ios/location_service/Package.swift | 32 +++++++ .../LocationServicePlugin.swift | 19 ++++ .../location_service/PrivacyInfo.xcprivacy | 14 +++ .../lib/location_service.dart | 49 +++++++++++ local_plugins/location_service/pubspec.yaml | 72 +++++++++++++++ pubspec.yaml | 2 + 33 files changed, 673 insertions(+), 33 deletions(-) create mode 100644 android/app/src/debug/AndroidManifest.xml create mode 100644 android/app/src/main/kotlin/com/yunqiinnovation/deep_voice/MainActivity.kt create mode 100644 android/app/src/profile/AndroidManifest.xml create mode 100644 lib/data/services/location_manager.dart create mode 100644 local_plugins/location_service/.gitignore create mode 100644 local_plugins/location_service/.metadata create mode 100644 local_plugins/location_service/CHANGELOG.md create mode 100644 local_plugins/location_service/LICENSE create mode 100644 local_plugins/location_service/README.md create mode 100644 local_plugins/location_service/analysis_options.yaml create mode 100644 local_plugins/location_service/android/.gitignore create mode 100644 local_plugins/location_service/android/build.gradle create mode 100644 local_plugins/location_service/android/settings.gradle create mode 100644 local_plugins/location_service/android/src/main/AndroidManifest.xml create mode 100644 local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt create mode 100644 local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationServicePlugin.kt create mode 100644 local_plugins/location_service/ios/.gitignore create mode 100644 local_plugins/location_service/ios/location_service.podspec create mode 100644 local_plugins/location_service/ios/location_service/Package.swift create mode 100644 local_plugins/location_service/ios/location_service/Sources/location_service/LocationServicePlugin.swift create mode 100644 local_plugins/location_service/ios/location_service/Sources/location_service/PrivacyInfo.xcprivacy create mode 100644 local_plugins/location_service/lib/location_service.dart create mode 100644 local_plugins/location_service/pubspec.yaml diff --git a/.metadata b/.metadata index 5c982c8d4..0d012fee6 100644 --- a/.metadata +++ b/.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 diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..399f6981d --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/kotlin/com/yunqiinnovation/deep_voice/MainActivity.kt b/android/app/src/main/kotlin/com/yunqiinnovation/deep_voice/MainActivity.kt new file mode 100644 index 000000000..ac31828f0 --- /dev/null +++ b/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() diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..399f6981d --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/lib/data/services/location_manager.dart b/lib/data/services/location_manager.dart new file mode 100644 index 000000000..bf9839d83 --- /dev/null +++ b/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(); + + // 音乐服务实例 + final LocationService _musicService = LocationService(); + + /// 初始化 + @override + void onInit() { + super.onInit(); + requestNotificationPermission(); + Logger.info('初始化权限请求'); + } + + Future 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> location() async { + return await _musicService.location(); + } +} diff --git a/local_plugins/agent_service/android/build.gradle.kts b/local_plugins/agent_service/android/build.gradle.kts index 1fd600fb7..9d8dea1bb 100644 --- a/local_plugins/agent_service/android/build.gradle.kts +++ b/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")) } \ No newline at end of file diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index c3854b568..ac3f5d9eb 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/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}") } // 再添加历史消息 diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/utils.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/utils.kt index 2b5457952..f9b3df85a 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/utils.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/utils.kt @@ -141,4 +141,15 @@ object Utils { false } } -} \ No newline at end of file + + /** + * 替换字符串 变量 + * */ + fun fillTemplate(tpl: String, data: Map): String { + var result = tpl + data.forEach { (k, v) -> + result = result.replace("{$k}", v) + } + return result + } +} \ No newline at end of file diff --git a/local_plugins/amap_service/android/build.gradle b/local_plugins/amap_service/android/build.gradle index 3f126ab46..9bb3ba7fb 100644 --- a/local_plugins/amap_service/android/build.gradle +++ b/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") } diff --git a/local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationServiceHelper.kt b/local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationServiceHelper.kt index 500e45bf7..5b7eced04 100644 --- a/local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationServiceHelper.kt +++ b/local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationServiceHelper.kt @@ -105,4 +105,7 @@ object NavigationServiceHelper { } } + + + } \ No newline at end of file diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt index 150321d7e..bad3aa1a2 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt +++ b/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), @@ -352,13 +353,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor // 收集工具调用信息 delta.toolCalls?.forEach { toolCall -> - val id = toolCall.id ?: run { - Log.e("ChatApiService", "toolCall.id 为空,跳过本次处理") - return@forEach - } - Log.e("ChatApiService", "AI发起工具调用: ${toolCall.id} ${toolCall.index} ${toolCall.function?.name} ${toolCall.function?.arguments}") val index = toolCall.index - // 创建或获取现有的工具调用信息 val toolCallInfo = toolCalls.getOrPut(index) { ToolCallInfo() } // 安全处理工具调用ID @@ -386,6 +381,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor } catch (e: Exception) { Log.e("ChatApiService", "处理工具调用参数异常: ${e.message}") } + Log.e("ChatApiService", "AI发起工具调用:${toolCallInfo.name} ${toolCallInfo.arguments}") } } catch (e: Exception) { Log.e("ChatApiService", "处理工具调用函数信息异常: ${e.message}") @@ -692,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) { diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MCPSubClient.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MCPSubClient.kt index a332689e2..b7b3adbe2 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MCPSubClient.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MCPSubClient.kt @@ -101,7 +101,7 @@ class MCPSubClient( retryCount = 0 currentReconnectDelay = initialReconnectDelay // 启动心跳检测 -// startHeartbeat() + startHeartbeat() true } catch (e: Exception) { @@ -129,7 +129,7 @@ class MCPSubClient( "properties" to emptyMap(), "required" to emptyList() ) - + Log.w(TAG, "解析工具数据: ${tool.name} ${parametersMap}") val toolMap = mapOf( "type" to "function", "function" to mapOf( @@ -229,7 +229,7 @@ class MCPSubClient( val required = mutableListOf() // 处理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 } diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt index 93a019bcb..058cb0876 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt +++ b/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(), - "required" to emptyList() - ), - handler = GetCurrentLocationHandler(context) - ) +// client.registerLocalFunction( +// name = "get_current_location", +// description = "获取当前地理位置", +// parameters = mapOf( +// "type" to "object", +// "properties" to emptyMap(), +// "required" to emptyList() +// ), +// handler = GetCurrentLocationHandler(context) +// ) // 注册媒体播放功能 client.registerLocalFunction( diff --git a/local_plugins/location_service/.gitignore b/local_plugins/location_service/.gitignore new file mode 100644 index 000000000..e7d347d9d --- /dev/null +++ b/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/ diff --git a/local_plugins/location_service/.metadata b/local_plugins/location_service/.metadata new file mode 100644 index 000000000..709f51d7e --- /dev/null +++ b/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' diff --git a/local_plugins/location_service/CHANGELOG.md b/local_plugins/location_service/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/local_plugins/location_service/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/local_plugins/location_service/LICENSE b/local_plugins/location_service/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/local_plugins/location_service/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/local_plugins/location_service/README.md b/local_plugins/location_service/README.md new file mode 100644 index 000000000..ba86601cc --- /dev/null +++ b/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. + diff --git a/local_plugins/location_service/analysis_options.yaml b/local_plugins/location_service/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/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 diff --git a/local_plugins/location_service/android/.gitignore b/local_plugins/location_service/android/.gitignore new file mode 100644 index 000000000..161bdcdaf --- /dev/null +++ b/local_plugins/location_service/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/local_plugins/location_service/android/build.gradle b/local_plugins/location_service/android/build.gradle new file mode 100644 index 000000000..63c9f82d9 --- /dev/null +++ b/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 + } + } + } +} + diff --git a/local_plugins/location_service/android/settings.gradle b/local_plugins/location_service/android/settings.gradle new file mode 100644 index 000000000..e165e2838 --- /dev/null +++ b/local_plugins/location_service/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'location_service' diff --git a/local_plugins/location_service/android/src/main/AndroidManifest.xml b/local_plugins/location_service/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..3cbeb31e4 --- /dev/null +++ b/local_plugins/location_service/android/src/main/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt b/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt new file mode 100644 index 000000000..e36eaa569 --- /dev/null +++ b/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? { +// // 定位成功,获取经纬度 +// 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 + } +} diff --git a/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationServicePlugin.kt b/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationServicePlugin.kt new file mode 100644 index 000000000..b34b48f24 --- /dev/null +++ b/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) {} +} diff --git a/local_plugins/location_service/ios/.gitignore b/local_plugins/location_service/ios/.gitignore new file mode 100644 index 000000000..034771fc9 --- /dev/null +++ b/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 diff --git a/local_plugins/location_service/ios/location_service.podspec b/local_plugins/location_service/ios/location_service.podspec new file mode 100644 index 000000000..b5409ce22 --- /dev/null +++ b/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 diff --git a/local_plugins/location_service/ios/location_service/Package.swift b/local_plugins/location_service/ios/location_service/Package.swift new file mode 100644 index 000000000..0b0dd0fbd --- /dev/null +++ b/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 + ] + ) + ] +) diff --git a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationServicePlugin.swift b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationServicePlugin.swift new file mode 100644 index 000000000..1b6f10f28 --- /dev/null +++ b/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) + } + } +} diff --git a/local_plugins/location_service/ios/location_service/Sources/location_service/PrivacyInfo.xcprivacy b/local_plugins/location_service/ios/location_service/Sources/location_service/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..a34b7e2e6 --- /dev/null +++ b/local_plugins/location_service/ios/location_service/Sources/location_service/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyTrackingDomains + + NSPrivacyAccessedAPITypes + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/local_plugins/location_service/lib/location_service.dart b/local_plugins/location_service/lib/location_service.dart new file mode 100644 index 000000000..c662819c6 --- /dev/null +++ b/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 _dataStream; + late final Stream _statusStream; + + // 获取事件流 + Stream get onDataReceived => _dataStream; + + // 状态流直接传递原始数据 + Stream 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> location() async { + final result = await _methodChannel.invokeMethod('location'); + return { + 'latitude': result?['longitude'] ?? 0.0, + 'longitude': result?['longitude'] ?? 0.0, + }; + } +} diff --git a/local_plugins/location_service/pubspec.yaml b/local_plugins/location_service/pubspec.yaml new file mode 100644 index 000000000..b575ce3b7 --- /dev/null +++ b/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 diff --git a/pubspec.yaml b/pubspec.yaml index fe2bd0ce1..6844d9e38 100644 --- a/pubspec.yaml +++ b/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