Browse Source

同步dev分支

newdev_shunjiawei
lxm 1 year ago
parent
commit
9d0c33859d
  1. 3
      lib/core/bindings/initial_binding.dart
  2. 6
      lib/core/translations/language/zh_cn.dart
  3. 18
      lib/data/services/navigation_manager.dart
  4. 76
      lib/modules/agent/controllers/agent_controller.dart
  5. 18
      lib/modules/agent/views/message_bubble.dart
  6. 96
      lib/modules/agent/views/message_bubble_navigation.dart
  7. 157
      lib/modules/agent/views/navigation_card.dart
  8. 9
      lib/modules/navigation/bindings/navigation_binding.dart
  9. 5
      lib/modules/navigation/controllers/navigation_controller.dart
  10. 17
      lib/modules/navigation/views/navigation_view.dart
  11. 8
      lib/routes/app_pages.dart
  12. 1
      lib/routes/app_routes.dart
  13. 2
      local_plugins/agent_service/android/build.gradle.kts
  14. 8
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  15. 4
      local_plugins/amap_service/android/build.gradle
  16. 8
      local_plugins/amap_service/android/src/main/AndroidManifest.xml
  17. 77
      local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/AmapServicePlugin.kt
  18. 293
      local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationActivity.kt
  19. 450
      local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationService.kt
  20. 108
      local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationServiceHelper.kt
  21. 34
      local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationServiceStarter.kt
  22. 8
      local_plugins/amap_service/android/src/main/res/drawable/ic_navigation.xml
  23. 10
      local_plugins/amap_service/android/src/main/res/layout/activity_navigation.xml
  24. 20
      local_plugins/amap_service/android/src/main/res/values/colors.xml
  25. 9
      local_plugins/amap_service/android/src/main/res/values/styles.xml
  26. 117
      local_plugins/amap_service/lib/amap_service.dart
  27. 55
      local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/SystemFunctionHandler.kt
  28. 4
      local_plugins/opus/.gradle/buildOutputCleanup/cache.properties
  29. 2
      pubspec.yaml

3
lib/core/bindings/initial_binding.dart

@ -1,4 +1,5 @@
import 'package:deep_voice/data/services/music_manager.dart';
import 'package:deep_voice/data/services/navigation_manager.dart';
import 'package:get/get.dart';
import '../../data/services/speech_factory.dart';
import '../../data/services/chat_history_service.dart';
@ -35,6 +36,8 @@ class InitialBinding extends Bindings {
Get.lazyPut<BleManager>(() => BleManager(), fenix: true);
// MusicManager
Get.lazyPut<MusicManager>(() => MusicManager(), fenix: true);
// NavigationManager
Get.lazyPut<NavigationManager>(() => NavigationManager(), fenix: true);
// 火山AI服务
// Get.lazyPut<VolcanoAIService>(() => VolcanoAIService(), fenix: true);

6
lib/core/translations/language/zh_cn.dart

@ -332,6 +332,12 @@ const Map<String, String> zhCN = {
"你好!我是 Emma,很高兴成为你的私人助理。我可以帮你处理各种任务,比如时间管理、目标规划、信息整理等。有什么我可以帮你的吗?",
"personalAssistantVoiceWelcome": "我在呢!",
// 导航
"driving": "驾车",
"walking": "步行",
"bicycling": "骑行",
"tsransit": "公交",
// 同声传译 Lucy
"interpreterName": "同声传译",
"interpreterDescription":

18
lib/data/services/navigation_manager.dart

@ -1,4 +1,4 @@
// import 'package:amap_service/amap_service.dart';
import 'package:amap_service/amap_service.dart';
import 'package:deep_voice/core/utils/logger.dart';
import 'package:get/get.dart';
@ -7,7 +7,7 @@ class NavigationManager extends GetxService {
static NavigationManager get to => Get.find<NavigationManager>();
// 音乐服务实例
// final AmapService _amapervice = AmapService();
final AmapService _amapervice = AmapService();
/// 初始化
@override
@ -22,8 +22,20 @@ class NavigationManager extends GetxService {
super.onClose();
}
Stream<AmapServiceEvent> events() {
return _amapervice.events;
}
//导航
Future<void> navigation(String org, String dis) async {
Future<void> start(String org, String dis) async {
// await _amapervice.navigation(org, dis);
}
//导航
Future<void> open(
String start,
String end,
String ntype,
) async {
await _amapervice.open(start, end, ntype);
}
}

76
lib/modules/agent/controllers/agent_controller.dart

@ -1,7 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:amap_service/amap_service.dart';
import 'package:deep_voice/data/services/music_manager.dart';
import 'package:deep_voice/data/services/navigation_manager.dart';
import 'package:deep_voice/routes/app_routes.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:agent_service/agent_service.dart';
@ -46,6 +49,7 @@ class AgentController extends GetxController {
//音乐播放管理
final _musiceManager = Get.find<MusicManager>();
final _navigationManager = Get.find<NavigationManager>();
var isPlaying = false.obs;
var currentUrl = "".obs;
@ -73,6 +77,9 @@ class AgentController extends GetxController {
// 流事件订阅
StreamSubscription<AgentServiceEvent>? _eventSubscription;
// 导航流事件订阅
StreamSubscription<AmapServiceEvent>? _amapeventSubscription;
// 用于跟踪AI回复状态
bool _isNewAssistantResponse = true;
String _currentResponseId = '';
@ -419,7 +426,7 @@ class AgentController extends GetxController {
if (meta != null && meta is String && meta.isNotEmpty) {
try {
metaResult = jsonDecode(meta);
metaResult = jsonDecode(meta) as Map<String, dynamic>;
} catch (e) {
Logger.e(TAG, '解析meta数据失败: $e');
}
@ -444,6 +451,16 @@ class AgentController extends GetxController {
);
}
}
if (metaResult.containsKey("card_navigation")) {
final start = metaResult["card_navigation"]["start"] as String;
final end = metaResult["card_navigation"]["end"] as String;
// Logger.i(TAG, 'liwei-------- 打开导航页面: $start $end ');
Future.delayed(Duration(milliseconds: 1500), () {
// Logger.i(TAG, 'liwei-------- 打开导航页面: $start $end ');
// 2秒后执行的代码
openNavigation(start, end, "");
});
}
}
}
break;
@ -451,6 +468,58 @@ class AgentController extends GetxController {
break;
}
});
//后台导航事件同步
_amapeventSubscription = _navigationManager.events().listen((event) {
switch (event.type) {
case AmapServiceEventType.starte:
Logger.i(TAG, '导航通知 started!');
break;
case AmapServiceEventType.stope:
Logger.i(TAG, '导航通知 stoped!');
break;
case AmapServiceEventType.update:
Logger.i(TAG, '导航通知 update! $event');
Message? result;
try {
result = messages.lastWhere(
(msg) =>
!msg.isUser &&
msg.meta?.containsKey("card_navigation") == true,
);
} catch (e) {
result = null;
}
if (result != null) {
int index = messages.indexOf(result);
if (index != -1) {
// ✅ 创建 meta 的深拷贝
final newMeta = Map<String, dynamic>.from(result.meta ?? {});
newMeta["card_navigation"]["navigation"] = event.data;
// ✅ 创建新的 Message 替换原来的
messages[index] = Message(
isUser: result.isUser,
text: result.text,
meta: newMeta,
timestamp: result.timestamp,
);
messages.refresh();
Logger.i(
TAG, '更新导航卡片! ${newMeta["card_navigation"]["navigation"]}');
}
}
break;
case AmapServiceEventType.error:
Logger.i(TAG, '导航通知 error!');
break;
case AmapServiceEventType.unknown:
Logger.i(TAG, '导航通知 unknown!');
break;
}
});
}
// 发送消息(包括所有支持的消息类型)
@ -885,4 +954,9 @@ class AgentController extends GetxController {
AgentService.processTextInput(messages[index].text,
speakResponse: isTtsEnabled.value);
}
//去导航
void openNavigation(String start, String end, String ntype) {
_navigationManager.open(start, end, ntype);
}
}

18
lib/modules/agent/views/message_bubble.dart

@ -1,5 +1,7 @@
import 'dart:convert';
import 'package:common_utils/common_utils.dart';
import 'package:deep_voice/modules/agent/views/message_bubble_navigation.dart';
import 'package:deep_voice/modules/agent/views/navigation_card.dart';
import 'weather_card.dart';
import 'music_card.dart';
import 'travel_card.dart';
@ -290,10 +292,22 @@ class MessageBubble extends GetView<AgentController> {
// 旅游
return buildTravelContent(context, travellist);
} else if (meta!.keys.contains('card_navigation')) {
final start = meta!['card_navigation']['start'] as String;
final end = meta!['card_navigation']['end'] as String;
final destination = meta!['card_navigation']['destination'] as String;
final rawList = meta!['card_navigation']['items'] as List;
final list = rawList.map((e) => Map<String, dynamic>.from(e)).toList();
final rawNavigation = meta!['card_navigation']['navigation'];
final Map<String, dynamic>? navigation =
rawNavigation is Map<String, dynamic> ? rawNavigation : null;
print("导航实时数据 ${rawNavigation}");
// 导航
return navigationCard(context, modes: list);
return navigationCard(context,
start: start,
end: end,
destination: destination,
modes: list,
navigation: navigation);
}
// if (message.contains('播放列表') || message.contains('音乐')) {

96
lib/modules/agent/views/message_bubble_navigation.dart

@ -1,96 +0,0 @@
import 'package:flutter/material.dart';
/// A widget that displays a navigation card with different modes of transportation.
Widget navigationCard(
BuildContext context, {
required List<Map<String, dynamic>> modes,
double height = 260,
}) {
int currentIndex = 0;
return Container(
height: height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.white,
boxShadow: const [
BoxShadow(
blurRadius: 8,
color: Colors.black12,
offset: Offset(0, 4),
),
],
),
child: StatefulBuilder(
builder: (context, setInnerState) {
return Column(
children: [
// 路线图片
ClipRRect(
borderRadius:
const BorderRadius.vertical(top: Radius.circular(16)),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: Image.network(
modes[currentIndex]["image"] as String,
key: ValueKey<String>(modes[currentIndex]["ntype"] as String),
height: 160,
width: double.infinity,
fit: BoxFit.cover,
),
),
),
// 导航方式切换按钮
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(modes.length, (index) {
final isSelected = index == currentIndex;
return GestureDetector(
onTap: () {
setInnerState(() => currentIndex = index);
},
child: SizedBox(
width: 50,
child: Text(
modes[index]["ntype"] as String,
style: TextStyle(
color: isSelected ? Colors.blue : Colors.black87,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
),
);
}),
),
),
// 距离和耗时
Padding(
padding: const EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('距离:${modes[currentIndex]["distance"]}',
style: const TextStyle(fontSize: 12, color: Colors.blue)),
Container(
decoration: BoxDecoration(
shape: BoxShape.circle, // 👈 圆形背景
color: Colors.blue,
),
child: Icon(Icons.turn_left,
color: Colors.white, // 👈 圆形图标
size: 20),
)
],
),
),
],
);
},
),
);
}

157
lib/modules/agent/views/navigation_card.dart

@ -0,0 +1,157 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/agent_controller.dart';
String formatDistance(String distanceStr) {
final distance = double.tryParse(distanceStr);
if (distance == null) return distanceStr; // 如果无法解析,则返回原始字符串
if (distance >= 1000) {
final km = distance / 1000;
return '${km.toStringAsFixed(1)} 公里';
} else {
return '${distance.toStringAsFixed(0)} 米';
}
}
String formatDuration(String secondsStr) {
int totalSeconds = int.tryParse(secondsStr) ?? 0;
Duration duration = Duration(seconds: totalSeconds);
int hours = duration.inHours;
int minutes = duration.inMinutes.remainder(60);
int seconds = duration.inSeconds.remainder(60);
List<String> parts = [];
if (hours > 0) parts.add('$hours小时');
if (minutes > 0) parts.add('$minutes分钟');
if (seconds > 0 || parts.isEmpty) parts.add('$seconds秒');
return parts.join('');
}
//导航卡片
Widget navigationCard(
BuildContext context, {
required String start,
required String end,
required String destination,
required List<Map<String, dynamic>> modes,
Map<String, dynamic>? navigation,
double height = 300,
}) {
int currentIndex = 0;
final controller = Get.find<AgentController>(); // 获取控制器
return Container(
height: height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.white,
boxShadow: const [
BoxShadow(
blurRadius: 8,
color: Colors.black12,
offset: Offset(0, 4),
),
],
),
child: StatefulBuilder(
builder: (context, setInnerState) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 路线图片
ClipRRect(
borderRadius:
const BorderRadius.vertical(top: Radius.circular(16)),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: Image.network(
modes[currentIndex]["image"] as String,
key: ValueKey<String>(modes[currentIndex]["ntype"] as String),
height: 160,
width: double.infinity,
fit: BoxFit.cover,
),
),
),
// 导航方式切换按钮
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(modes.length, (index) {
final isSelected = index == currentIndex;
return GestureDetector(
onTap: modes[index]['enable'] as bool
? () {
setInnerState(() => currentIndex = index);
}
: null,
child: SizedBox(
width: 50,
child: Text(
(modes[index]["ntype"] as String).tr,
textAlign: TextAlign.center,
style: TextStyle(
color: modes[index]['enable']
? isSelected
? Colors.blue
: Colors.black87
: Colors.grey,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
),
);
}),
),
),
// 距离和耗时
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('$destination',
style: const TextStyle(
fontSize: 12, color: Colors.black)),
Text(
'${formatDistance(modes[currentIndex]["distance"])}-约${formatDuration(modes[currentIndex]["duration"])}',
style: const TextStyle(
fontSize: 15, color: Colors.blue)),
if (navigation != null)
Text(
"导航中... ${navigation["distanceText"]}进入${navigation["nextRoad"]}",
style: TextStyle(
fontSize: 12,
color:
const Color.fromARGB(255, 164, 164, 164)))
],
),
CircleAvatar(
backgroundColor: Colors.blue,
radius: 18,
child: IconButton(
icon: const Icon(Icons.turn_left,
color: Colors.white, size: 20),
onPressed: () {
controller.openNavigation(start, end, "");
},
),
)
],
),
),
],
);
},
),
);
}

9
lib/modules/navigation/bindings/navigation_binding.dart

@ -0,0 +1,9 @@
import 'package:get/get.dart';
import '../controllers/navigation_controller.dart';
class NavigationBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<NavigationController>(() => NavigationController());
}
}

5
lib/modules/navigation/controllers/navigation_controller.dart

@ -0,0 +1,5 @@
import 'package:get/get.dart';
class NavigationController extends GetxController {
static NavigationController get to => Get.find(); //单例访问模式
}

17
lib/modules/navigation/views/navigation_view.dart

@ -0,0 +1,17 @@
import 'package:deep_voice/modules/navigation/controllers/navigation_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class NavigationView extends GetView<NavigationController> {
const NavigationView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('地图展示')),
body: Stack(
children: [],
),
);
}
}

8
lib/routes/app_pages.dart

@ -1,3 +1,4 @@
import 'package:deep_voice/modules/navigation/views/navigation_view.dart';
import 'package:get/get.dart';
import '../modules/feedback/bindings/feedback_bindings.dart';
import '../modules/feedback/views/feedback_view.dart';
@ -189,5 +190,12 @@ abstract class AppPages {
transition: Transition.rightToLeft,
transitionDuration: Duration(milliseconds: 250),
),
GetPage(
name: Routes.navigation,
page: () => const NavigationView(),
binding: AgentBinding(),
transition: Transition.rightToLeft,
transitionDuration: Duration(milliseconds: 250),
),
];
}

1
lib/routes/app_routes.dart

@ -44,4 +44,5 @@ abstract class Routes {
// 支持服务
static const feedback = '/feedback'; // 用户反馈
static const agent = '/agent'; // 在线客服
static const navigation = '/navigation'; // 实时导航
}

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

@ -47,6 +47,6 @@ dependencies {
add("compileOnly", project(":chat_storage"))
add("compileOnly", project(":ble_service"))
add("compileOnly", project(":music_service"))
// add("compileOnly", project(":amap_service"))
add("compileOnly", project(":amap_service"))
add("compileOnly", project(":speech"))
}

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

@ -7,16 +7,13 @@ import com.yunqiinnovation.azure_speech.AzureAsrHelper
import com.yunqiinnovation.open_ai_service.OpenAIService
import com.yunqiinnovation.chat_storage.ChatStorageHelper
import com.yunqiinnovation.music_service.MusicServiceStarter
import com.yunqiinnovation.amap_service.NavigationServiceHelper
import android.util.Log
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.*
import java.util.Collections
import kotlin.coroutines.CoroutineContext
import com.yunqiinnovation.ble_service.BleService
import android.media.MediaPlayer
import com.yunqiinnovation.agent_service.R
import android.os.Handler
import android.os.Looper
import com.deep_voice.speech.tts.TtsEvent
import com.deep_voice.speech.tts.TtsEventListener
import com.deep_voice.speech.tts.TtsEventType
@ -1196,7 +1193,6 @@ object AgentService : CoroutineScope {
/**
* 处理导航
* 将ai返回的音乐连接 在后台启动播放
*
* @param musicUrl 图片文件路径
* @param name 歌曲名称,默认为空
* @param singer 歌手,默认为false
@ -1206,7 +1202,7 @@ object AgentService : CoroutineScope {
// 如果 playlist 中有有效的歌曲,开始播放
if (!start.isNullOrEmpty() && !end.isNullOrEmpty()) {
Log.i(TAG, "启动导航服务 ${start} ${end}")
// NavigationServiceStarter.startServiceWithCommand(context, command = "start", startpos = start, endpos = end)
NavigationServiceHelper.startNavigation(context,"start", startpos = start, endpos = end)
} else {
Log.i(TAG, "启动导航服务失败")
}

4
local_plugins/amap_service/android/build.gradle

@ -65,3 +65,7 @@ android {
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.7.0'
}

8
local_plugins/amap_service/android/src/main/AndroidManifest.xml

@ -39,6 +39,14 @@
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden|screenSize|navigation" />
<service android:name="com.amap.api.location.APSService"/>
<activity
android:name=".NavigationActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:configChanges="orientation|keyboardHidden|screenSize|navigation"
android:screenOrientation="portrait" />
<service
android:name=".NavigationService"
android:exported="true"

77
local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/AmapServicePlugin.kt

@ -1,26 +1,37 @@
package com.yunqiinnovation.amap_service
import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.core.content.ContextCompat
// import com.yunqiinnovation.deepvoice_utils.FileLogger
import com.yunqiinnovation.amap_service.NavigationServiceStarter
import com.amap.api.navi.AMapNaviView
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 kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import org.json.JSONObject
/** MusicServicePlugin */
class AmapServicePlugin: FlutterPlugin, MethodCallHandler {
private val TAG = "MusicServicePlugin"
class AmapServicePlugin: FlutterPlugin, MethodCallHandler,EventChannel.StreamHandler, NavigationListener {
private val TAG = "AmapServicePlugin"
private lateinit var context: Context
private lateinit var channel : MethodChannel
private var activity: Activity? = null;
private var aMapNaviView: AMapNaviView? = null
private lateinit var eventChannel: EventChannel
private var eventSink: EventChannel.EventSink? = null
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.yunqiinnovation.music_service")
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.yunqiinnovation.amap_service")
channel.setMethodCallHandler(this)
eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "com.yunqiinnovation.amap_service/events")
eventChannel.setStreamHandler(this)
NavigationServiceHelper.init(context)
NavigationServiceHelper.addListener(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
@ -31,24 +42,60 @@ class AmapServicePlugin: FlutterPlugin, MethodCallHandler {
val endpos = call.argument<String>("endpos")
val ntype = call.argument<String>("ntype")
if (startpos != null && endpos != null && ntype != null) {
NavigationServiceStarter.startServiceWithCommand(context, "start", startpos, endpos, ntype)
NavigationServiceHelper.startNavigation(context,"start",startpos,endpos,ntype)
result.success(null)
} else {
result.error("INVALID_ARGUMENT", "Missing 'song' map", null)
}
}
"open" -> {
try {
val startpos = call.argument<String>("startpos")
val endpos = call.argument<String>("endpos")
val ntype = call.argument<String>("ntype")
NavigationServiceHelper.openNavigationUI(startpos,endpos,ntype)
}catch (e: Exception){
result.error("INVALID_ARGUMENT", "Try Exception ", e)
}
}
"stop" -> {
val songs = call.argument<List<Map<String, String>>>("songs")
if (songs != null && songs.isNotEmpty()) {
NavigationServiceStarter.startServiceWithCommand(context, "stop")
NavigationServiceHelper.startNavigation(context,"stop")
result.success(null)
} else {
result.error("INVALID_ARGUMENT", "Missing or empty 'songs' list", null)
}
}
else -> result.notImplemented()
else -> {
result.notImplemented()
}
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {}
/**
* 实现AgentServiceListener接口
* 接收来自AgentService的事件,并转发到Flutter
*/
override fun onEvent(eventName: String, data: Map<String, Any>) {
// 将事件转换为JSON字符串
val eventData = JSONObject().apply {
put("event", eventName)
put("data", JSONObject(data))
}
// 在主线程发送事件到Flutter
MainScope().launch {
eventSink?.success(eventData.toString())
}
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
// TODO("Not yet implemented")
this.eventSink = events
}
override fun onCancel(arguments: Any?) {
// TODO("Not yet implemented")
this.eventSink = null
}
}

293
local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationActivity.kt

@ -0,0 +1,293 @@
package com.yunqiinnovation.amap_service
import android.content.Context
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.amap.api.navi.AMapNavi
import com.amap.api.navi.AMapNaviListener
import com.amap.api.navi.AMapNaviView
import com.amap.api.navi.AMapNaviViewListener
import com.amap.api.navi.enums.PathPlanningStrategy
import com.amap.api.navi.model.AMapCalcRouteResult
import com.amap.api.navi.model.AMapLaneInfo
import com.amap.api.navi.model.AMapModelCross
import com.amap.api.navi.model.AMapNaviCameraInfo
import com.amap.api.navi.model.AMapNaviCross
import com.amap.api.navi.model.AMapNaviLocation
import com.amap.api.navi.model.AMapNaviRouteNotifyData
import com.amap.api.navi.model.AMapNaviTrafficFacilityInfo
import com.amap.api.navi.model.AMapServiceAreaInfo
import com.amap.api.navi.model.AimLessModeCongestionInfo
import com.amap.api.navi.model.AimLessModeStat
import com.amap.api.navi.model.NaviInfo
import com.amap.api.navi.model.NaviLatLng
import com.yunqiinnovation.amap_service.NavigationService.Companion.sharedNavi
class NavigationActivity : AppCompatActivity(), AMapNaviListener, AMapNaviViewListener {
private lateinit var naviView: AMapNaviView
private var navi: AMapNavi? = null
private lateinit var context: Context
private var startpos:String = "";
private var endpos:String = "";
private var ntype:String = "";
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_navigation)
context = this
// 初始化导航视图
naviView = findViewById(R.id.navi_view)
naviView.onCreate(savedInstanceState)
naviView.setAMapNaviViewListener(this)
startpos = intent.getStringExtra("startpos").toString()
endpos = intent.getStringExtra("endpos").toString()
ntype = intent.getStringExtra("ntype").toString()
Log.i("NavigationActivity", "onCreate $startpos $endpos $ntype")
navi = NavigationService.sharedNavi
if (navi == null) {
navi = AMapNavi.getInstance(context).apply {
setUseInnerVoice(true)
setBroadcastMode(1)
}
}
navi?.addAMapNaviListener(this);
// 绑定当前 activity 到导航状态
}
override fun onResume() {
super.onResume()
naviView.onResume()
}
override fun onPause() {
super.onPause()
naviView.onPause()
}
override fun onDestroy() {
navi?.stopNavi()
navi?.removeAMapNaviListener(this);
AMapNavi.destroy()
NavigationServiceHelper.startNavigation(context,"stop")
naviView.onDestroy()
super.onDestroy()
Log.i("NavigationActivity", "liwei------------------- onDestroy!")
}
override fun onInitNaviFailure() {
// TODO("Not yet implemented")
}
override fun onInitNaviSuccess() {
Log.i("NavigationActivity", "onInitNaviSuccess!")
// 起点
val startParts: List<String> = startpos.split(",")
val startLng = startParts[0].toDouble()
val startLat = startParts[1].toDouble()
// 终点
val endParts: List<String> = endpos.split(",")
val endLng = endParts[0].toDouble()
val endLat = endParts[1].toDouble()
val startList: MutableList<NaviLatLng> = ArrayList()
val endList: MutableList<NaviLatLng> = ArrayList()
startList.add(NaviLatLng(startLat, startLng))
endList.add(NaviLatLng(endLat, endLng))
val success = navi?.calculateDriveRoute(startList, endList, null, PathPlanningStrategy.DRIVING_MULTIPLE_ROUTES_DEFAULT);
if (!success!!) {
Log.e("NavigationActivity", "路径规划请求失败")
}
// TODO("Not yet implemented")
}
override fun onStartNavi(p0: Int) {
// TODO("Not yet implemented")
}
override fun onTrafficStatusUpdate() {
// TODO("Not yet implemented")
}
override fun onLocationChange(p0: AMapNaviLocation?) {
// TODO("Not yet implemented")
}
override fun onGetNavigationText(p0: Int, p1: String?) {
// TODO("Not yet implemented")
}
override fun onGetNavigationText(p0: String?) {
// TODO("Not yet implemented")
}
override fun onEndEmulatorNavi() {
// TODO("Not yet implemented")
}
override fun onArriveDestination() {
// TODO("Not yet implemented")
}
override fun onCalculateRouteFailure(p0: Int) {
// TODO("Not yet implemented")
}
override fun onCalculateRouteFailure(p0: AMapCalcRouteResult?) {
// TODO("Not yet implemented")
}
override fun onReCalculateRouteForYaw() {
// TODO("Not yet implemented")
}
override fun onReCalculateRouteForTrafficJam() {
// TODO("Not yet implemented")
}
override fun onArrivedWayPoint(p0: Int) {
// TODO("Not yet implemented")
}
override fun onGpsOpenStatus(p0: Boolean) {
// TODO("Not yet implemented")
}
override fun onNaviInfoUpdate(p0: NaviInfo?) {
// TODO("Not yet implemented")
}
override fun updateCameraInfo(p0: Array<out AMapNaviCameraInfo>?) {
// TODO("Not yet implemented")
}
override fun updateIntervalCameraInfo(
p0: AMapNaviCameraInfo?,
p1: AMapNaviCameraInfo?,
p2: Int
) {
// TODO("Not yet implemented")
}
override fun onServiceAreaUpdate(p0: Array<out AMapServiceAreaInfo>?) {
// TODO("Not yet implemented")
}
override fun showCross(p0: AMapNaviCross?) {
// TODO("Not yet implemented")
}
override fun hideCross() {
// TODO("Not yet implemented")
}
override fun showModeCross(p0: AMapModelCross?) {
// TODO("Not yet implemented")
}
override fun hideModeCross() {
// TODO("Not yet implemented")
}
override fun showLaneInfo(p0: Array<out AMapLaneInfo>?, p1: ByteArray?, p2: ByteArray?) {
// TODO("Not yet implemented")
}
override fun showLaneInfo(p0: AMapLaneInfo?) {
// TODO("Not yet implemented")
}
override fun hideLaneInfo() {
// TODO("Not yet implemented")
}
override fun onCalculateRouteSuccess(p0: IntArray?) {
navi?.startNavi(1);
// TODO("Not yet implemented")
}
override fun onCalculateRouteSuccess(p0: AMapCalcRouteResult?) {
// TODO("Not yet implemented")
}
override fun notifyParallelRoad(p0: Int) {
// TODO("Not yet implemented")
}
override fun OnUpdateTrafficFacility(p0: Array<out AMapNaviTrafficFacilityInfo>?) {
// TODO("Not yet implemented")
}
override fun OnUpdateTrafficFacility(p0: AMapNaviTrafficFacilityInfo?) {
// TODO("Not yet implemented")
}
override fun updateAimlessModeStatistics(p0: AimLessModeStat?) {
// TODO("Not yet implemented")
}
override fun updateAimlessModeCongestionInfo(p0: AimLessModeCongestionInfo?) {
// TODO("Not yet implemented")
}
override fun onPlayRing(p0: Int) {
// TODO("Not yet implemented")
}
override fun onNaviRouteNotify(p0: AMapNaviRouteNotifyData?) {
// TODO("Not yet implemented")
}
override fun onGpsSignalWeak(p0: Boolean) {
// TODO("Not yet implemented")
}
//UI事件----------------------------------------------------------------------------------------------
override fun onNaviSetting() {
// TODO("Not yet implemented")
}
override fun onNaviCancel() {
finishAndRemoveTask()
// TODO("Not yet implemented")
}
override fun onNaviBackClick(): Boolean {
// TODO("Not yet implemented")
return false
}
override fun onNaviMapMode(p0: Int) {
// TODO("Not yet implemented")
}
override fun onNaviTurnClick() {
// TODO("Not yet implemented")
}
override fun onNextRoadClick() {
// TODO("Not yet implemented")
}
override fun onScanViewButtonClick() {
// TODO("Not yet implemented")
}
override fun onLockMap(p0: Boolean) {
// TODO("Not yet implemented")
}
override fun onNaviViewLoaded() {
// TODO("Not yet implemented")
}
override fun onMapTypeChanged(p0: Int) {
// TODO("Not yet implemented")
}
override fun onNaviViewShowMode(p0: Int) {
// TODO("Not yet implemented")
}
}

450
local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationService.kt

@ -5,13 +5,15 @@ import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.os.Build
import android.os.IBinder
import android.util.Base64
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.amap.api.navi.AMapNavi
import com.amap.api.navi.AMapNaviListener
import com.amap.api.navi.enums.NaviType
import com.amap.api.navi.enums.PathPlanningStrategy
import com.amap.api.navi.model.AMapCalcRouteResult
import com.amap.api.navi.model.AMapLaneInfo
@ -26,11 +28,12 @@ import com.amap.api.navi.model.AimLessModeCongestionInfo
import com.amap.api.navi.model.AimLessModeStat
import com.amap.api.navi.model.NaviInfo
import com.amap.api.navi.model.NaviLatLng
import java.io.ByteArrayOutputStream
import java.io.FileNotFoundException
import java.util.concurrent.atomic.AtomicBoolean
class NavigationService: Service() {
class NavigationService: Service(),AMapNaviListener {
companion object {
private const val TAG = "NavigationService"
@ -42,46 +45,57 @@ class NavigationService: Service() {
private var startpos: String = ""
private var endpos: String = ""
private var naviType: String? = null
private var playlist = mutableListOf<Map<String,String>>()
private var currentIndex = 0
private var lastNotificationUpdateTime = 0L
private val notificationUpdateInterval = 1000L
var instance: NavigationService? = null
var sharedNavi: AMapNavi? = null
fun isServiceRunning() = isRunning.get()
}
private lateinit var aMapNavi: AMapNavi
override fun onCreate() {
super.onCreate()
instance = this
startForegroundNotification()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
val command = intent.getStringExtra("command") ?: return START_NOT_STICKY
when (command) {
"start" -> {
"start" ->{
try {
startpos = intent.getStringExtra("startpos").toString()
endpos = intent.getStringExtra("endpos").toString()
naviType = intent.getStringExtra("naviType");
Log.w(TAG, "开始导航 startpos:$startpos endpos:$endpos naviType:$naviType")
// 初始化导航
aMapNavi = AMapNavi.getInstance(applicationContext)
aMapNavi.setUseInnerVoice(true)
aMapNavi.setBroadcastMode(1)
aMapNavi.addAMapNaviListener(naviListener)
if (startpos.isEmpty() || endpos.isEmpty()) {
Log.e(TAG, "Missing start or end position")
return START_NOT_STICKY
}
initializeAMapNavi()
}catch (e: FileNotFoundException){
Log.w("NavigationService", "Custom voice bin not found: ${e.message}")
}
}
"stop" -> {
aMapNavi.stopNavi();
stopNavigation()
return START_NOT_STICKY
}
}
return START_STICKY
}
private fun initializeAMapNavi() {
try {
sharedNavi = AMapNavi.getInstance(applicationContext).apply {
setUseInnerVoice(true)
setBroadcastMode(1)
addAMapNaviListener(this@NavigationService)
}
} catch (e: Exception) {
Log.e(TAG, "初始化AMapNavi失败: ${e.message}", e)
}
}
private fun startForegroundNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
@ -106,209 +120,283 @@ class NavigationService: Service() {
startForeground(NOTIFICATION_ID, notification)
}
// 实现监听器,处理导航播报等
private val naviListener = object : AMapNaviListener {
override fun onInitNaviFailure() {
Log.e(TAG, "onInitNaviFailure!")
// TODO("Not yet implemented")
}
private fun updateNotification(contentText: String, iconBitmap: Bitmap?) {
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("导航进行中")
.setContentText(contentText)
.setSmallIcon(R.drawable.ic_navigation) // 通知栏图标(小)
.setOnlyAlertOnce(true)
.setOngoing(true)
override fun onInitNaviSuccess() {
Log.e(TAG, "onInitNaviSuccess!")
// 起点
val startParts: List<String> = startpos.split(",")
val startLng = startParts[0].toDouble()
val startLat = startParts[1].toDouble()
// 终点
val endParts: List<String> = endpos.split(",")
val endLng = endParts[0].toDouble()
val endLat = endParts[1].toDouble()
val startList: MutableList<NaviLatLng> = ArrayList()
val endList: MutableList<NaviLatLng> = ArrayList()
startList.add(NaviLatLng(startLat, startLng))
endList.add(NaviLatLng(endLat, endLng))
val success = aMapNavi.calculateDriveRoute(startList, endList, null, PathPlanningStrategy.DRIVING_MULTIPLE_ROUTES_DEFAULT);
if (!success) {
Log.e(TAG, "路径规划请求失败")
}
if (iconBitmap != null) {
val tinted = tintBitmap(iconBitmap, ContextCompat.getColor(this, R.color.teal_200))
builder.setLargeIcon(tinted)
}
override fun onStartNavi(p0: Int) {
Log.e(TAG, "onStartNavi!")
// TODO("Not yet implemented")
}
val notification = builder.build()
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(NOTIFICATION_ID, notification)
}
private fun tintBitmap(source: Bitmap, tintColor: Int): Bitmap {
val result = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888)
val canvas = android.graphics.Canvas(result)
val paint = android.graphics.Paint()
paint.colorFilter = android.graphics.PorterDuffColorFilter(tintColor, android.graphics.PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(source, 0f, 0f, paint)
return result
}
fun bitmapToBase64(bitmap: Bitmap?): String? {
if (bitmap != null) return null;
val outputStream = ByteArrayOutputStream()
bitmap?.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
val byteArray = outputStream.toByteArray()
return Base64.encodeToString(byteArray, Base64.NO_WRAP)
}
override fun onTrafficStatusUpdate() {
Log.e(TAG, "onTrafficStatusUpdate!")
// TODO("Not yet implemented")
private fun stopNavigation() {
if (sharedNavi != null) {
sharedNavi?.stopNavi()
sharedNavi?.removeAMapNaviListener(this)
sharedNavi = null
}
isRunning.set(false)
stopSelf()
}
override fun onLocationChange(location: AMapNaviLocation) {
Log.e(TAG, "onLocationChange! ${location}")
// 可通过广播发送位置信息到 Flutter
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onGetNavigationText(p0: Int, p1: String?) {
Log.e(TAG, "onGetNavigationText! $p0 $p1")
// TODO("Not yet implemented")
}
override fun onGetNavigationText(p0: String?) {
Log.e(TAG, "onGetNavigationText! $p0")
// TODO("Not yet implemented")
}
override fun onInitNaviFailure() {
Log.e(TAG, "onInitNaviFailure!")
// TODO("Not yet implemented")
}
override fun onEndEmulatorNavi() {
Log.e(TAG, "onEndEmulatorNavi! ")
// TODO("Not yet implemented")
}
override fun onInitNaviSuccess() {
Log.e(TAG, "onInitNaviSuccess!")
// 起点
val startParts: List<String> = startpos.split(",")
val startLng = startParts[0].toDouble()
val startLat = startParts[1].toDouble()
// 终点
val endParts: List<String> = endpos.split(",")
val endLng = endParts[0].toDouble()
val endLat = endParts[1].toDouble()
val startList: MutableList<NaviLatLng> = ArrayList()
val endList: MutableList<NaviLatLng> = ArrayList()
startList.add(NaviLatLng(startLat, startLng))
endList.add(NaviLatLng(endLat, endLng))
val success = sharedNavi?.calculateDriveRoute(startList, endList, null, PathPlanningStrategy.DRIVING_MULTIPLE_ROUTES_DEFAULT);
if (!success!!) {
Log.e(TAG, "路径规划请求失败")
}
// TODO("Not yet implemented")
}
override fun onArriveDestination() {
Log.e(TAG, "onArriveDestination! ")
// TODO("Not yet implemented")
}
override fun onStartNavi(p0: Int) {
Log.e(TAG, "onStartNavi! $p0")
// TODO("Not yet implemented")
}
override fun onCalculateRouteFailure(p0: Int) {
Log.e(TAG, "路径规划失败, 错误码: $p0")
// TODO: 可添加反馈机制给 Flutter 层或用户提示
}
override fun onTrafficStatusUpdate() {
Log.e(TAG, "onTrafficStatusUpdate!")
// TODO("Not yet implemented")
}
override fun onCalculateRouteFailure(p0: AMapCalcRouteResult?) {
Log.e(TAG, "onCalculateRouteFailure! ${p0}")
// TODO("Not yet implemented")
}
override fun onLocationChange(p0: AMapNaviLocation?) {
Log.e(TAG, "onLocationChange! $p0")
// TODO("Not yet implemented")
}
override fun onReCalculateRouteForYaw() {
Log.e(TAG, "onReCalculateRouteForYaw!")
// TODO("Not yet implemented")
}
override fun onGetNavigationText(p0: Int, p1: String?) {
Log.e(TAG, "onGetNavigationText! $p0 $p1")
// TODO("Not yet implemented")
}
override fun onReCalculateRouteForTrafficJam() {
Log.e(TAG, "onReCalculateRouteForTrafficJam!")
// TODO("Not yet implemented")
}
override fun onGetNavigationText(p0: String?) {
Log.e(TAG, "onGetNavigationText! $p0")
// TODO("Not yet implemented")
}
override fun onArrivedWayPoint(p0: Int) {
Log.e(TAG, "onArrivedWayPoint!")
// TODO("Not yet implemented")
}
override fun onEndEmulatorNavi() {
Log.e(TAG, "onEndEmulatorNavi!")
stopNavigation()
// TODO("Not yet implemented")
}
override fun onGpsOpenStatus(p0: Boolean) {
Log.e(TAG, "onGpsOpenStatus!")
// TODO("Not yet implemented")
}
override fun onArriveDestination() {
Log.e(TAG, "onArriveDestination!")
stopNavigation()
// TODO("Not yet implemented")
}
override fun onNaviInfoUpdate(p0: NaviInfo?) {
Log.e(TAG, "onNaviInfoUpdate! $p0")
// TODO("Not yet implemented")
}
override fun onCalculateRouteFailure(p0: Int) {
Log.e(TAG, "onCalculateRouteFailure! $p0")
// TODO("Not yet implemented")
}
override fun updateCameraInfo(p0: Array<out AMapNaviCameraInfo>?) {
Log.e(TAG, "updateCameraInfo! $p0")
// TODO("Not yet implemented")
}
override fun onCalculateRouteFailure(p0: AMapCalcRouteResult?) {
Log.e(TAG, "onCalculateRouteFailure! $p0")
// TODO("Not yet implemented")
}
override fun updateIntervalCameraInfo(
p0: AMapNaviCameraInfo?,
p1: AMapNaviCameraInfo?,
p2: Int
) {
Log.e(TAG, "updateIntervalCameraInfo! $p0")
// TODO("Not yet implemented")
}
override fun onReCalculateRouteForYaw() {
Log.e(TAG, "onReCalculateRouteForYaw!")
// TODO("Not yet implemented")
}
override fun onServiceAreaUpdate(p0: Array<out AMapServiceAreaInfo>?) {
Log.e(TAG, "onServiceAreaUpdate! $p0")
// TODO("Not yet implemented")
}
override fun onReCalculateRouteForTrafficJam() {
Log.e(TAG, "onReCalculateRouteForTrafficJam!")
// TODO("Not yet implemented")
}
override fun showCross(p0: AMapNaviCross?) {
Log.e(TAG, "showCross! $p0")
// TODO("Not yet implemented")
}
override fun onArrivedWayPoint(p0: Int) {
Log.e(TAG, "onArrivedWayPoint! $p0")
// TODO("Not yet implemented")
}
override fun hideCross() {
Log.e(TAG, "hideCross!")
// TODO("Not yet implemented")
}
override fun onGpsOpenStatus(p0: Boolean) {
Log.e(TAG, "onGpsOpenStatus! $p0")
// TODO("Not yet implemented")
}
override fun showModeCross(p0: AMapModelCross?) {
Log.e(TAG, "showModeCross! $p0")
// TODO("Not yet implemented")
}
override fun onNaviInfoUpdate(naviInfo: NaviInfo?) {
Log.e(TAG, "onNaviInfoUpdate! $naviInfo")
if (naviInfo == null) return
val currentTime = System.currentTimeMillis()
if (currentTime - lastNotificationUpdateTime < notificationUpdateInterval) {
// 距离上次更新不足1秒,跳过这次更新
return
}
val directionBitmap = naviInfo.iconBitmap
val currentRoad = naviInfo.currentRoadName
val nextRoad = naviInfo.nextRoadName
val remainingDistance = naviInfo.getCurStepRetainDistance()
val remainingTime = naviInfo.getPathRetainTime()
val distanceText = if (remainingDistance < 1000) {
"${remainingDistance} 米"
} else {
String.format("%.1f 千米", remainingDistance / 1000.0)
}
val contentText = "$distanceText \n进入:$nextRoad"
// val directionBitmapBase64 = bitmapToBase64(directionBitmap)
// val naviInfoMap: Map<String, Any> = mapOf(
// "currentRoad" to currentRoad,
// "nextRoad" to nextRoad,
// "distanceText" to distanceText,
// "directionIcon" to (directionBitmapBase64 ?: "")
// )
// NavigationServiceHelper.sendEvent("amap_update",naviInfoMap)
updateNotification(contentText, directionBitmap)
Log.e(TAG, "onNaviInfoUpdate! $contentText")
// TODO("Not yet implemented")
}
override fun hideModeCross() {
Log.e(TAG, "hideModeCross!")
// TODO("Not yet implemented")
}
override fun updateCameraInfo(p0: Array<out AMapNaviCameraInfo>?) {
Log.e(TAG, "updateCameraInfo! $p0")
// TODO("Not yet implemented")
}
override fun showLaneInfo(p0: Array<out AMapLaneInfo>?, p1: ByteArray?, p2: ByteArray?) {
Log.e(TAG, "showLaneInfo! $p0")
// TODO("Not yet implemented")
}
override fun updateIntervalCameraInfo(
p0: AMapNaviCameraInfo?,
p1: AMapNaviCameraInfo?,
p2: Int
) {
Log.e(TAG, "updateIntervalCameraInfo! $p0 $p1 $p2")
// TODO("Not yet implemented")
}
override fun showLaneInfo(p0: AMapLaneInfo?) {
Log.e(TAG, "showLaneInfo! $p0")
// TODO("Not yet implemented")
}
override fun onServiceAreaUpdate(p0: Array<out AMapServiceAreaInfo>?) {
Log.e(TAG, "onServiceAreaUpdate! $p0")
// TODO("Not yet implemented")
}
override fun hideLaneInfo() {
Log.e(TAG, "hideLaneInfo!")
// TODO("Not yet implemented")
}
override fun showCross(p0: AMapNaviCross?) {
Log.e(TAG, "showCross!")
// TODO("Not yet implemented")
}
override fun onCalculateRouteSuccess(ids: IntArray?) {
Log.e(TAG, "onCalculateRouteSuccess! $ids")
aMapNavi.startNavi(NaviType.EMULATOR)
}
override fun hideCross() {
Log.e(TAG, "hideCross!")
// TODO("Not yet implemented")
}
override fun onCalculateRouteSuccess(p0: AMapCalcRouteResult?) {
Log.e(TAG, "onCalculateRouteSuccess! $p0")
// TODO("Not yet implemented")
}
override fun showModeCross(p0: AMapModelCross?) {
Log.e(TAG, "showModeCross! $p0")
// TODO("Not yet implemented")
}
override fun notifyParallelRoad(p0: Int) {
Log.d(TAG, "是否在拥挤平行路段: $p0")
// TODO("Not yet implemented")
}
override fun hideModeCross() {
Log.e(TAG, "hideModeCross!")
// TODO("Not yet implemented")
}
override fun OnUpdateTrafficFacility(p0: Array<out AMapNaviTrafficFacilityInfo>?) {
Log.d(TAG, "OnUpdateTrafficFacility: $p0")
// TODO("Not yet implemented")
}
override fun showLaneInfo(p0: Array<out AMapLaneInfo>?, p1: ByteArray?, p2: ByteArray?) {
Log.e(TAG, "showLaneInfo! $p0 $p1 $p2")
// TODO("Not yet implemented")
}
override fun OnUpdateTrafficFacility(p0: AMapNaviTrafficFacilityInfo?) {
Log.d(TAG, "OnUpdateTrafficFacility: $p0")
// TODO("Not yet implemented")
}
override fun showLaneInfo(p0: AMapLaneInfo?) {
Log.e(TAG, "showLaneInfo! $p0")
// TODO("Not yet implemented")
}
override fun updateAimlessModeStatistics(p0: AimLessModeStat?) {
// TODO("Not yet implemented")
Log.d(TAG, "updateAimlessModeStatistics: $p0")
}
override fun hideLaneInfo() {
Log.e(TAG, "hideLaneInfo!")
// TODO("Not yet implemented")
}
override fun updateAimlessModeCongestionInfo(p0: AimLessModeCongestionInfo?) {
// TODO("Not yet implemented")
Log.d(TAG, "updateAimlessModeCongestionInfo: $p0")
}
override fun onCalculateRouteSuccess(p0: IntArray?) {
Log.e(TAG, "onCalculateRouteSuccess! $p0")
sharedNavi?.startNavi(1);
// TODO("Not yet implemented")
}
override fun onPlayRing(p0: Int) {
Log.d(TAG, "onPlayRing: $p0")
// TODO("Not yet implemented")
}
override fun onCalculateRouteSuccess(p0: AMapCalcRouteResult?) {
sharedNavi?.startNavi(2);
Log.e(TAG, "onCalculateRouteSuccess! $p0")
// TODO("Not yet implemented")
}
override fun onNaviRouteNotify(p0: AMapNaviRouteNotifyData?) {
Log.w(TAG, "GPS信号弱")
// TODO("Not yet implemented")
}
override fun notifyParallelRoad(p0: Int) {
Log.e(TAG, "notifyParallelRoad! $p0")
// TODO("Not yet implemented")
}
override fun onGpsSignalWeak(p0: Boolean) {
Log.w(TAG, "GPS信号弱")
// TODO("Not yet implemented")
}
override fun OnUpdateTrafficFacility(p0: Array<out AMapNaviTrafficFacilityInfo>?) {
Log.e(TAG, "OnUpdateTrafficFacility! $p0")
// TODO("Not yet implemented")
}
override fun onBind(intent: Intent?): IBinder? = null
override fun OnUpdateTrafficFacility(p0: AMapNaviTrafficFacilityInfo?) {
Log.e(TAG, "OnUpdateTrafficFacility! $p0")
// TODO("Not yet implemented")
}
override fun updateAimlessModeStatistics(p0: AimLessModeStat?) {
Log.e(TAG, "updateAimlessModeStatistics! $p0")
// TODO("Not yet implemented")
}
override fun updateAimlessModeCongestionInfo(p0: AimLessModeCongestionInfo?) {
Log.e(TAG, "updateAimlessModeCongestionInfo! $p0")
// TODO("Not yet implemented")
}
override fun onPlayRing(p0: Int) {
Log.e(TAG, "onPlayRing! $p0")
// TODO("Not yet implemented")
}
override fun onNaviRouteNotify(p0: AMapNaviRouteNotifyData?) {
Log.e(TAG, "onNaviRouteNotify! $p0")
// TODO("Not yet implemented")
}
override fun onGpsSignalWeak(p0: Boolean) {
Log.e(TAG, "onGpsSignalWeak! $p0")
// TODO("Not yet implemented")
}
}

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

@ -0,0 +1,108 @@
package com.yunqiinnovation.amap_service
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.content.ContextCompat
import java.util.Collections
/**
* 代理服务事件监听接口
*/
interface NavigationListener {
/**
* 当有事件发生时调用此方法
*
* @param eventName 事件名称
* @param data 事件数据
*/
fun onEvent(eventName: String, data: Map<String, Any>)
}
object NavigationServiceHelper {
private val TAG = "NavigationServiceStarter"
private lateinit var context: Context
private val listeners = Collections.synchronizedSet(mutableSetOf<NavigationListener>())
fun init(appContext: Context) {
context = appContext
}
//启动导航
fun startNavigation(context: Context,command:String, startpos: String? = null, endpos: String? = null, ntype: String? = null) {
Log.i(TAG, "liwei------------------- startNavigation: $command $startpos $endpos $ntype")
val intent = Intent(context, NavigationService::class.java).apply {
putExtra("command", command)
startpos?.let { putExtra("startpos", it) }
endpos?.let { putExtra("endpos", it) }
ntype?.let { putExtra("ntype", it) }
}
ContextCompat.startForegroundService(context, intent)
// context.startForegroundService(intent)
}
//打开导航页面
fun openNavigationUI(startpos: String? = null, endpos: String? = null, ntype: String? = null) {
Log.i(TAG, "liwei------------------- openNavigationUI: $startpos $endpos $ntype")
val intent = Intent(context, NavigationActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra("startpos", startpos)
putExtra("endpos", endpos)
putExtra("ntype", ntype)
}
context.startActivity(intent)
}
/**
* 添加事件监听器
* @param listener 要添加的监听器
*/
fun addListener(listener: NavigationListener) {
listeners.add(listener)
}
/**
* 移除事件监听器
* @param listener 要移除的监听器
*/
fun removeListener(listener: NavigationListener) {
listeners.remove(listener)
}
/**
* 移除所有事件监听器
*/
fun clearListeners() {
listeners.clear()
}
/**
* 发送事件
*/
fun sendEvent(eventName: String, data: Map<String, Any>) {
// 检查当前是否在主线程
if (Looper.myLooper() == Looper.getMainLooper()) {
// 已在主线程,直接发送
listeners.forEach { listener ->
try {
listener.onEvent(eventName, data)
} catch (e: Exception) {
// FileLogger.e(NavigationService.TAG, "向监听器发送事件失败", e)
}
}
} else {
// 不在主线程,切换到主线程
Handler(Looper.getMainLooper()).post {
listeners.forEach { listener ->
try {
listener.onEvent(eventName, data)
} catch (e: Exception) {
// FileLogger.e(NavigationService.TAG, "向监听器发送事件失败", e)
}
}
}
}
}
}

34
local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationServiceStarter.kt

@ -1,34 +0,0 @@
package com.yunqiinnovation.amap_service
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.core.content.ContextCompat
object NavigationServiceStarter {
private val TAG = "NavigationServiceStarter"
//播放单曲
fun startServiceWithCommand(context: Context, command: String, startpos: String? = null, endpos: String? = null, ntype: String? = null) {
Log.i(TAG, "liwei------------------- startServiceWithCommand: $command $startpos $endpos $ntype")
val intent = Intent(context, NavigationService::class.java).apply {
putExtra("command", command)
startpos?.let {
putExtra("startpos",it) // 只有非 null 时才 putExtra
}
endpos?.let {
putExtra("endpos", it) // 只有非 null 时才 putExtra
}
ntype?.let {
putExtra("ntype",it) // 只有非 null 时才 putExtra
}
}
val success = try {
ContextCompat.startForegroundService(context, intent)
true
} catch (e: Exception) {
Log.e(TAG, "liwei------------------- Failed to start service", e)
false
}
Log.d(TAG, "liwei------------------- Service start success: $success")
}
}

8
local_plugins/amap_service/android/src/main/res/drawable/ic_navigation.xml

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFF"
android:pathData="M12,2L3,21h9v-6h6v6h3L12,2z" />
android:fillColor="#000000"
android:pathData="M12,2C8.13,2 5,5.13 5,9C5,11.74 7.19,14.74 9.93,16.07L12,21L14.07,16.07C16.81,14.74 19,11.74 19,9C19,5.13 15.87,2 12,2ZM12,12C10.34,12 9,10.66 9,9C9,7.34 10.34,6 12,6C13.66,6 15,7.34 15,9C15,10.66 13.66,12 12,12Z"/>
</vector>

10
local_plugins/amap_service/android/src/main/res/layout/activity_navigation.xml

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.amap.api.navi.AMapNaviView
android:id="@+id/navi_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>

20
local_plugins/amap_service/android/src/main/res/values/colors.xml

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 主色调 -->
<color name="purple_500">#6200EE</color>
<color name="purple_700">#3700B3</color>
<color name="teal_200">#03DAC5</color>
<color name="teal_700">#018786</color>
<color name="black">#000000</color>
<color name="white">#FFFFFF</color>
<!-- 额外颜色 -->
<color name="red_500">#F44336</color>
<color name="green_500">#4CAF50</color>
<color name="blue_500">#2196F3</color>
<color name="colorPrimary">#6200EE</color>
<color name="colorPrimaryDark">#3700B3</color>
<color name="colorAccent">#03DAC5</color>
</resources>

9
local_plugins/amap_service/android/src/main/res/values/styles.xml

@ -0,0 +1,9 @@
<resources>
<!-- 基础应用主题 -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- 自定义主题属性 -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

117
local_plugins/amap_service/lib/amap_service.dart

@ -1,5 +1,38 @@
import 'dart:convert';
import 'package:flutter/services.dart';
/// 代理服务事件类型
enum AmapServiceEventType {
/// 导航启动
starte,
///导航结束
stope,
update,
/// 错误
error,
/// 未知事件
unknown,
}
/// 代理服务事件数据
class AmapServiceEvent {
/// 事件类型
final AmapServiceEventType type;
/// 事件数据
final Map<String, dynamic> data;
AmapServiceEvent({required this.type, required this.data});
@override
String toString() => 'AmapServiceEvent(type: $type, data: $data)';
}
class AmapService {
static final AmapService _instance = AmapService._internal();
@ -12,48 +45,70 @@ class AmapService {
);
// 事件通道 - 简化为只使用数据和状态通道
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;
late final EventChannel _eventChannel;
static Stream<AmapServiceEvent>? _eventStream;
// 内部构造函数
AmapService._internal() {
_dataEventChannel = const EventChannel(
'com.yunqiinnovation.amap_service/data_events',
);
_statusEventChannel = const EventChannel(
'com.yunqiinnovation.amap_service/status_events',
_eventChannel = const EventChannel(
'com.yunqiinnovation.amap_service/events',
);
}
/// 获取事件流
Stream<AmapServiceEvent> get events {
_eventStream ??= _eventChannel.receiveBroadcastStream().map((event) {
try {
final Map<String, dynamic> eventMap = jsonDecode(event);
final String eventName = eventMap['event'];
final Map<String, dynamic> eventData = eventMap['data'];
// 初始化事件流
_dataStream = _dataEventChannel.receiveBroadcastStream();
_statusStream = _statusEventChannel.receiveBroadcastStream();
// 将事件名称转换为枚举
final AmapServiceEventType type = _stringToEventType(eventName);
return AmapServiceEvent(type: type, data: eventData);
} catch (e) {
return AmapServiceEvent(
type: AmapServiceEventType.unknown,
data: {'error': e.toString(), 'rawEvent': event},
);
}
});
return _eventStream!;
}
// 初始化SDK
Future<void> initSdk({String? iosKey, String? androidKey}) async {}
// 定位初始化
Future<void> initLocation() async {}
/// 将字符串事件名转换为枚举类型
static AmapServiceEventType _stringToEventType(String eventName) {
switch (eventName) {
case 'amap_starte':
return AmapServiceEventType.starte;
case 'amap_stope':
return AmapServiceEventType.stope;
case 'amap_update':
return AmapServiceEventType.update;
case 'amap_error':
return AmapServiceEventType.error;
default:
return AmapServiceEventType.unknown;
}
}
// 开始定位
Future<void> startLocation() async {}
Future<void> start() async {
await _methodChannel.invokeMethod('start');
}
// 监听定位返回位置
Stream<dynamic> locationStream() {
return _dataStream;
Future<void> open(String start, String end, String ntype) async {
await _methodChannel.invokeMethod('open', {
'startpos': start,
"endpos": end,
"ntype": ntype,
});
}
// 停止定位
Future<void> stopLocation() async {}
Future<void> stop() async {
await _methodChannel.invokeMethod('stop');
}
// 销毁
Future<void> dispose() async {}

55
local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/SystemFunctionHandler.kt

@ -29,6 +29,7 @@ import android.content.ContentUris
import androidx.core.content.ContextCompat
import android.app.ActivityManager
import android.app.Application
import android.content.ComponentName
import android.os.Process
/**
* 系统功能处理器 - 负责调用系统API执行各种功能以及注册系统功能
@ -77,7 +78,9 @@ class SystemFunctionHandler(private val context: Context) {
// 注册打开录音机功能
registerOpenRecorderFunction(client)
// 注册关闭导航服务功能
registerStopNavigationFunction(client)
Log.d(TAG, "所有系统功能注册完成")
} catch (e: Exception) {
Log.e(TAG, "注册系统功能失败: ${e.message}", e)
@ -443,7 +446,29 @@ class SystemFunctionHandler(private val context: Context) {
)
Log.d(TAG, "已注册打开录音机功能")
}
/**
* 注册获取当前时间函数
*/
private fun registerStopNavigationFunction(client: MCPClient) {
client.registerLocalFunction(
"stop_navigation",
"结束当前导航服务",
JSONObject("""
{
"type": "object",
"properties": {},
"required": []
}
"""),
object : FunctionHandler {
override suspend fun handle(arguments: Map<String, Any>): String {
val result = stopNavigation()
return "{\"result\": \"${result.message}\"}"
}
}
)
Log.d(TAG, "已注册获取当前时间功能")
}
/**
* 控制媒体播放
* @param action 操作类型:"play", "pause", "previous", "next"
@ -1270,7 +1295,31 @@ class SystemFunctionHandler(private val context: Context) {
false
}
}
/**
* 停止当前导航
* @return 操作结果
*/
fun stopNavigation():Result{
Log.d(TAG, "结束当前导航服务")
try {
// val intent = Intent()
// intent.component = ComponentName("com.example.plugin", "com.example.plugin.MyBackgroundService")
// stopService(intent)
return Result(
success = true,
message = "当前导航服务已结束"
)
} catch (e: Exception) {
Log.e(TAG, "结束当前导航服务失败: ${e.message}", e)
return Result(
success = false,
message = "结束当前导航服务失败: ${e.message}"
)
}
}
/**
* 操作结果数据类
*/

4
local_plugins/opus/.gradle/buildOutputCleanup/cache.properties

@ -1,2 +1,2 @@
#Sun May 18 21:39:38 IST 2025
gradle.version=8.10
#Wed May 21 01:03:35 CST 2025
gradle.version=8.9

2
pubspec.yaml

@ -85,6 +85,8 @@ dependencies:
path: local_plugins/agent_service
music_service:
path: local_plugins/music_service
amap_service:
path: local_plugins/amap_service
dev_dependencies:

Loading…
Cancel
Save