Browse Source

上传导航服务

newdev_shunjiawei
liwei1dao 1 year ago
parent
commit
9b8e8a9abc
  1. 2
      android/build.gradle.kts
  2. 4
      lib/modules/chat/controllers/chat_controller.dart
  3. 24
      lib/modules/splash/controllers/splash_controller.dart
  4. 1
      local_plugins/agent_service/android/build.gradle.kts
  5. 5
      local_plugins/agent_service/android/settings.gradle.kts
  6. 29
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  7. 2
      local_plugins/agent_service/pubspec.yaml
  8. 1
      local_plugins/amap_flutter/android/settings.gradle
  9. 3
      local_plugins/amap_flutter/android/src/main/AndroidManifest.xml
  10. 169
      local_plugins/amap_flutter/android/src/main/java/com/example/amap_flutter/AmapFlutterPlugin.java
  11. 36
      local_plugins/amap_flutter/lib/amap_flutter.dart
  12. 45
      local_plugins/amap_flutter/lib/amap_flutter_method_channel.dart
  13. 49
      local_plugins/amap_flutter/lib/amap_flutter_platform_interface.dart
  14. 0
      local_plugins/amap_service/.gitignore
  15. 14
      local_plugins/amap_service/.metadata
  16. 0
      local_plugins/amap_service/CHANGELOG.md
  17. 0
      local_plugins/amap_service/LICENSE
  18. 2
      local_plugins/amap_service/README.md
  19. 0
      local_plugins/amap_service/analysis_options.yaml
  20. 0
      local_plugins/amap_service/android/.gitignore
  21. 27
      local_plugins/amap_service/android/build.gradle
  22. 1
      local_plugins/amap_service/android/settings.gradle
  23. 48
      local_plugins/amap_service/android/src/main/AndroidManifest.xml
  24. 54
      local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/AmapServicePlugin.kt
  25. 314
      local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationService.kt
  26. 34
      local_plugins/amap_service/android/src/main/kotlin/com/yunqiinnovation/amap_service/NavigationServiceStarter.kt
  27. 9
      local_plugins/amap_service/android/src/main/res/drawable/ic_navigation.xml
  28. 0
      local_plugins/amap_service/ios/.gitignore
  29. 0
      local_plugins/amap_service/ios/Assets/.gitkeep
  30. 6
      local_plugins/amap_service/ios/Classes/AmapServicePlugin.swift
  31. 0
      local_plugins/amap_service/ios/Resources/PrivacyInfo.xcprivacy
  32. 6
      local_plugins/amap_service/ios/amap_service.podspec
  33. 60
      local_plugins/amap_service/lib/amap_service.dart
  34. 10
      local_plugins/amap_service/pubspec.yaml
  35. 1
      local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicService.kt
  36. 6
      pubspec.yaml

2
android/build.gradle.kts

@ -35,7 +35,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.10")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.24")
// 其他依赖...
}
}

4
lib/modules/chat/controllers/chat_controller.dart

@ -1,6 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'package:amap_flutter/amap_flutter.dart';
import 'package:amap_service/amap_service.dart';
import '../../../data/services/spotify_service.dart';
import 'package:get/get.dart';
import 'package:flutter/widgets.dart';
@ -32,7 +32,7 @@ class ChatController extends GetxController {
// 添加ScrollController用于控制列表滚动
final ScrollController scrollController = ScrollController();
final _amapFlutter = AmapFlutter();
final _amapFlutter = AmapService();
StreamSubscription? _locationStream;
String _latLng = '';
String _city = '深圳市';

24
lib/modules/splash/controllers/splash_controller.dart

@ -1,5 +1,5 @@
import 'dart:async';
import 'package:amap_flutter/amap_flutter.dart';
// import 'package:amap_flutter/amap_flutter.dart';
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:deep_voice/data/models/appconfig_model.dart';
import 'package:flutter/material.dart';
@ -32,21 +32,21 @@ class SplashController extends GetxController {
WidgetsBinding.instance.addPostFrameCallback((_) {
_startLoadingAnimation();
_loadAppResources();
_initMapSDK();
// _initMapSDK();
});
}
// 初始化地图SDK
void _initMapSDK() {
try {
AmapFlutter().initSdk(
iosKey: "a4a1394fe817c2f86a424b897b4a9af4",
androidKey: "e5fdc9605eabdeb5626f18f5721f343d",
);
} catch (e) {
print('地图SDK初始化失败: $e');
}
}
// void _initMapSDK() {
// try {
// AmapFlutter().initSdk(
// iosKey: "a4a1394fe817c2f86a424b897b4a9af4",
// androidKey: "e5fdc9605eabdeb5626f18f5721f343d",
// );
// } catch (e) {
// print('地图SDK初始化失败: $e');
// }
// }
// 进度条动画(持续2秒)
void _startLoadingAnimation() {

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

@ -49,4 +49,5 @@ dependencies {
add("compileOnly", project(":chat_storage"))
add("compileOnly", project(":ble_service"))
add("compileOnly", project(":music_service"))
add("compileOnly", project(":amap_service"))
}

5
local_plugins/agent_service/android/settings.gradle.kts

@ -6,10 +6,11 @@ include(":open_ai_service")
include(":deepvoice_utils")
include(":chat_storage")
include(":music_service")
include(":amap_service")
// 设置项目路径
project(":azure_speech").projectDir = file("../../azure_speech/android")
project(":open_ai_service").projectDir = file("../../open_ai_service/android")
project(":deepvoice_utils").projectDir = file("../../deepvoice_utils/android")
project(":chat_storage").projectDir = file("../../chat_storage/android")
project(":music_service").projectDir = file("../../music_service/android")
project(":music_service").projectDir = file("../../music_service/android")
project(":amap_service").projectDir = file("../../amap_service/android")

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

@ -8,6 +8,7 @@ import com.yunqiinnovation.azure_speech.AzureTtsHelper
import com.yunqiinnovation.open_ai_service.OpenAIService
import com.yunqiinnovation.chat_storage.ChatStorageHelper
import com.yunqiinnovation.music_service.MusicServiceStarter
import com.yunqiinnovation.amap_service.NavigationServiceStarter
import com.yunqiinnovation.deepvoice_utils.FileLogger
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.*
@ -671,7 +672,7 @@ object AgentService : CoroutineScope {
"result" to functionCallResult.toString(),
))
FileLogger.d(TAG, "mcp调用结果: $functionCallResult")
metadata = autoPlayMusic(functionCallResult);
metadata = autoHandleFcunCallResult(functionCallResult);
}
}
)
@ -1019,7 +1020,7 @@ object AgentService : CoroutineScope {
/*
* 自动播放音乐
* */
fun autoPlayMusic(functionCallResult: JSONObject): String{
fun autoHandleFcunCallResult(functionCallResult: JSONObject): String{
val metaStr = functionCallResult.optString("meta")
if (metaStr.isNotEmpty()) {
val meta = JSONObject(metaStr)
@ -1056,6 +1057,11 @@ object AgentService : CoroutineScope {
} else {
FileLogger.w(TAG, "card_musiclist.musics 不是一个数组")
}
}else if (meta.has("card_navigation")) { //导航服务
val cardNavigation = meta.getJSONObject("card_navigation")
val start = cardNavigation.optString("start", "")
val end = cardNavigation.optString("end", "")
processNavigation(start,end);
}
return metaStr;
}
@ -1096,4 +1102,23 @@ object AgentService : CoroutineScope {
FileLogger.i(TAG, "音乐列表为空,未启动播放服务")
}
}
/**
* 处理导航
* 将ai返回的音乐连接 在后台启动播放
*
* @param musicUrl 图片文件路径
* @param name 歌曲名称,默认为空
* @param singer 歌手,默认为false
* @return 是否成功开始处理
*/
fun processNavigation(start:String,end:String){
// 如果 playlist 中有有效的歌曲,开始播放
if (!start.isNullOrEmpty() && !end.isNullOrEmpty()) {
FileLogger.i(TAG, "启动导航服务 ${start} ${end}")
NavigationServiceStarter.startServiceWithCommand(context, command = "start", startpos = start, endpos = end)
} else {
FileLogger.i(TAG, "启动导航服务失败")
}
}
}

2
local_plugins/agent_service/pubspec.yaml

@ -21,6 +21,8 @@ dependencies:
path: ../chat_storage
music_service:
path: ../music_service
amap_service:
path: ../amap_service
get: ^4.6.5
dev_dependencies:

1
local_plugins/amap_flutter/android/settings.gradle

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

3
local_plugins/amap_flutter/android/src/main/AndroidManifest.xml

@ -1,3 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.amap_flutter">
</manifest>

169
local_plugins/amap_flutter/android/src/main/java/com/example/amap_flutter/AmapFlutterPlugin.java

@ -1,169 +0,0 @@
package com.example.amap_flutter;
import android.content.Context;
import androidx.annotation.NonNull;
import android.util.Log;
import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import java.util.HashMap;
import java.util.Map;
/** AmapFlutterPlugin */
public class AmapFlutterPlugin implements FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private MethodChannel methodChannel;
private EventChannel eventChannel;
private Context applicationContext;
private AMapLocationClient mLocationClient;
private EventChannel.EventSink eventSink;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
applicationContext = flutterPluginBinding.getApplicationContext();
methodChannel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "amap_flutter");
methodChannel.setMethodCallHandler(this);
// 初始化EventChannel
eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "amap_flutter/events");
eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
eventSink = events;
}
@Override
public void onCancel(Object arguments) {
eventSink = null;
}
});
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
switch (call.method) {
case "setApiKey":
setApiKey(call, result);
break;
case "initLocation":
initLocation(result);
break;
case "startLocation":
startLocation(result);
break;
case "stopLocation":
stopLocation(result);
break;
case "dispose":
dispose(result);
break;
default:
result.notImplemented();
}
}
private void setApiKey(MethodCall call, Result result) {
try {
String apiKey = call.argument("androidKey");
AMapLocationClient.updatePrivacyShow(applicationContext, true, true);
AMapLocationClient.updatePrivacyAgree(applicationContext, true);
result.success(null);
} catch (Exception e) {
result.error("INIT_ERROR", "初始化失败", e.getMessage());
}
}
private void initLocation(Result result) {
try {
mLocationClient = new AMapLocationClient(applicationContext);
// 配置定位参数
AMapLocationClientOption option = new AMapLocationClientOption();
option.setOnceLocation(true);
option.setNeedAddress(true);
mLocationClient.setLocationOption(option);
mLocationClient.setLocationListener(mLocationListener);
result.success(null);
} catch (Exception e) {
result.error("INIT_ERROR", "初始化失败", e.getMessage());
}
}
private void startLocation(Result result) {
mLocationClient.startLocation();
result.success(null);
}
private void stopLocation(Result result) {
destroyLocationClient();
result.success(null);
}
private void dispose(Result result) {
try {
destroyLocationClient();
result.success(null);
} catch (Exception e) {
result.error("DISPOSE_ERROR", "资源释放失败", e.getMessage());
}
}
private void destroyLocationClient() {
if (mLocationClient != null) {
try {
// 停止定位
mLocationClient.stopLocation();
// 销毁定位客户端
mLocationClient.onDestroy();
mLocationClient = null;
// 关闭事件通道
if (eventSink != null) {
eventSink.endOfStream();
eventSink = null;
}
} catch (Exception e) {
Log.e("AmapFlutter", "销毁定位客户端失败", e);
}
}
}
AMapLocationListener mLocationListener = new AMapLocationListener() {
@Override
public void onLocationChanged(AMapLocation amapLocation) {
if (eventSink == null) return;
if (amapLocation != null) {
if (amapLocation.getErrorCode() == 0) {
Map<String, Object> locationData = new HashMap<>();
locationData.put("latitude", amapLocation.getLatitude());
locationData.put("longitude", amapLocation.getLongitude());
locationData.put("city", amapLocation.getCity());
locationData.put("address", amapLocation.getAddress());
eventSink.success(locationData);
}
}
}
};
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
methodChannel.setMethodCallHandler(null);
eventChannel.setStreamHandler(null);
destroyLocationClient();
}
}

36
local_plugins/amap_flutter/lib/amap_flutter.dart

@ -1,36 +0,0 @@
import 'amap_flutter_platform_interface.dart';
class AmapFlutter {
// 初始化SDK
Future<void> initSdk({String? iosKey, String? androidKey}) async {
await AmapFlutterPlatform.instance.setApiKey(
iosKey: iosKey,
androidKey: androidKey,
);
}
// 定位初始化
Future<void> initLocation() async {
return AmapFlutterPlatform.instance.initLocation();
}
// 开始定位
Future<void> startLocation() async {
return AmapFlutterPlatform.instance.startLocation();
}
// 监听定位返回位置
Stream<dynamic> locationStream() {
return AmapFlutterPlatform.instance.locationStream();
}
// 停止定位
Future<void> stopLocation() async {
await AmapFlutterPlatform.instance.stopLocation();
}
// 销毁
Future<void> dispose() async {
await AmapFlutterPlatform.instance.dispose();
}
}

45
local_plugins/amap_flutter/lib/amap_flutter_method_channel.dart

@ -1,45 +0,0 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'amap_flutter_platform_interface.dart';
/// An implementation of [AmapFlutterPlatform] that uses method channels.
class MethodChannelAmapFlutter extends AmapFlutterPlatform {
/// The method channel used to interact with the native platform.
@visibleForTesting
final methodChannel = const MethodChannel('amap_flutter');
final eventChannel = const EventChannel('amap_flutter/events');
@override
Future<void> setApiKey({String? iosKey, String? androidKey}) async {
await methodChannel.invokeMethod('setApiKey', {
"iosKey": iosKey,
"androidKey": androidKey,
});
}
@override
Future<void> initLocation() async {
return await methodChannel.invokeMethod('initLocation');
}
@override
Future<void> startLocation() async {
return await methodChannel.invokeMethod('startLocation');
}
@override
Stream<dynamic> locationStream() {
return eventChannel.receiveBroadcastStream();
}
@override
Future<void> stopLocation() async {
await methodChannel.invokeMethod('stopLocation');
}
@override
Future<void> dispose() async {
await methodChannel.invokeMethod('dispose');
}
}

49
local_plugins/amap_flutter/lib/amap_flutter_platform_interface.dart

@ -1,49 +0,0 @@
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'amap_flutter_method_channel.dart';
abstract class AmapFlutterPlatform extends PlatformInterface {
/// Constructs a AmapFlutterPlatform.
AmapFlutterPlatform() : super(token: _token);
static final Object _token = Object();
static AmapFlutterPlatform _instance = MethodChannelAmapFlutter();
/// The default instance of [AmapFlutterPlatform] to use.
///
/// Defaults to [MethodChannelAmapFlutter].
static AmapFlutterPlatform get instance => _instance;
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [AmapFlutterPlatform] when
/// they register themselves.
static set instance(AmapFlutterPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Future<void> setApiKey({String? iosKey, String? androidKey}) {
throw UnimplementedError('init() has not been implemented.');
}
Future<void> initLocation() {
throw UnimplementedError('initLocation() has not been implemented.');
}
Future<void> startLocation() {
throw UnimplementedError('startLocation() has not been implemented.');
}
Stream<dynamic> locationStream() {
throw UnimplementedError('locationStream() has not been implemented.');
}
Future<void> stopLocation() {
throw UnimplementedError('stopLocation() has not been implemented.');
}
Future<void> dispose() {
throw UnimplementedError('dispose() has not been implemented.');
}
}

0
local_plugins/amap_flutter/.gitignore → local_plugins/amap_service/.gitignore

14
local_plugins/amap_flutter/.metadata → local_plugins/amap_service/.metadata

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
revision: "35c388afb57ef061d06a39b537336c87e0e3d1b1"
revision: "c23637390482d4cf9598c3ce3f2be31aa7332daf"
channel: "stable"
project_type: plugin
@ -13,14 +13,14 @@ project_type: plugin
migration:
platforms:
- platform: root
create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: android
create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: ios
create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
# User provided section

0
local_plugins/amap_flutter/CHANGELOG.md → local_plugins/amap_service/CHANGELOG.md

0
local_plugins/amap_flutter/LICENSE → local_plugins/amap_service/LICENSE

2
local_plugins/amap_flutter/README.md → local_plugins/amap_service/README.md

@ -1,4 +1,4 @@
# amap_flutter
# amap_service
A new Flutter project.

0
local_plugins/amap_flutter/analysis_options.yaml → local_plugins/amap_service/analysis_options.yaml

0
local_plugins/amap_flutter/android/.gitignore → local_plugins/amap_service/android/.gitignore

27
local_plugins/amap_flutter/android/build.gradle → local_plugins/amap_service/android/build.gradle

@ -1,7 +1,8 @@
group = "com.example.amap_flutter"
version = "1.0"
group = "com.yunqiinnovation.amap_service"
version = "1.0-SNAPSHOT"
buildscript {
ext.kotlin_version = "1.8.22"
repositories {
google()
mavenCentral()
@ -9,10 +10,11 @@ buildscript {
dependencies {
classpath("com.android.tools.build:gradle:8.7.0")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
}
}
rootProject.allprojects {
allprojects {
repositories {
google()
mavenCentral()
@ -20,9 +22,10 @@ rootProject.allprojects {
}
apply plugin: "com.android.library"
apply plugin: "kotlin-android"
android {
namespace = "com.example.amap_flutter"
namespace = "com.yunqiinnovation.amap_service"
compileSdk = 35
@ -31,19 +34,29 @@ android {
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11
}
sourceSets {
main.java.srcDirs += "src/main/kotlin"
test.java.srcDirs += "src/test/kotlin"
}
defaultConfig {
minSdk = 21
}
dependencies {
implementation 'com.amap.api:3dmap-location-search:latest.integration'
testImplementation("junit:junit:4.13.2")
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}

1
local_plugins/amap_service/android/settings.gradle

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

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

@ -0,0 +1,48 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yunqiinnovation.amap_service">
<!--允许访问网络,必选权限-->
<uses-permission android:name="android.permission.INTERNET" />
<!--允许获取精确位置,实时导航为必选-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--允许获取粗略位置,实时导航为必选-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--允许获取网络状态,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--允许获取wifi网络信息,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--允许获取wifi状态改变,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!--后台获取位置信息,若需后台定位或持续导航则必选-->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!--用于申请调用A-GPS模块,卫星定位加速-->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<!--用于用户链接蓝牙时,在导航组件页面的蓝牙连接提醒,建立链接后开发者可选用蓝牙通道进行tts播报-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!--用与导航状态中保持屏幕常亮-->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!--允许写设备缓存,用于问题排查-->
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<application>
<activity
android:name="com.amap.api.navi.AmapRouteActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden|screenSize|navigation" />
<service android:name="com.amap.api.location.APSService"/>
<service
android:name=".NavigationService"
android:exported="true"
android:enabled="true" android:foregroundServiceType="location|mediaPlayback">
</service>
</application>
</manifest>

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

@ -0,0 +1,54 @@
package com.yunqiinnovation.amap_service
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 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
/** MusicServicePlugin */
class AmapServicePlugin: FlutterPlugin, MethodCallHandler {
private val TAG = "MusicServicePlugin"
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.music_service")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
// FileLogger.d(TAG, "Method call: ${call.method}")
when (call.method) {
"start" -> {
val startpos = call.argument<String>("startpos")
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)
result.success(null)
} else {
result.error("INVALID_ARGUMENT", "Missing 'song' map", null)
}
}
"stop" -> {
val songs = call.argument<List<Map<String, String>>>("songs")
if (songs != null && songs.isNotEmpty()) {
NavigationServiceStarter.startServiceWithCommand(context, "stop")
result.success(null)
} else {
result.error("INVALID_ARGUMENT", "Missing or empty 'songs' list", null)
}
}
else -> result.notImplemented()
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {}
}

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

@ -0,0 +1,314 @@
package com.yunqiinnovation.amap_service
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
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
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 java.io.FileNotFoundException
import java.util.concurrent.atomic.AtomicBoolean
class NavigationService: Service() {
companion object {
private const val TAG = "NavigationService"
private const val CHANNEL_ID = "navigation_channel"
private const val CHANNEL_Name = "导航服务"
private const val NOTIFICATION_ID = 1003
private val isRunning = AtomicBoolean(false)
private var startpos: String = ""
private var endpos: String = ""
private var naviType: String? = null
private var playlist = mutableListOf<Map<String,String>>()
private var currentIndex = 0
fun isServiceRunning() = isRunning.get()
}
private lateinit var aMapNavi: AMapNavi
override fun onCreate() {
super.onCreate()
startForegroundNotification()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
val command = intent.getStringExtra("command") ?: return START_NOT_STICKY
when (command) {
"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)
}catch (e: FileNotFoundException){
Log.w("NavigationService", "Custom voice bin not found: ${e.message}")
}
}
"stop" -> {
aMapNavi.stopNavi();
}
}
return START_STICKY
}
private fun startForegroundNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
CHANNEL_Name,
NotificationManager.IMPORTANCE_LOW
).apply {
description = "用于导航的前台服务"
setShowBadge(false)
}
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("导航进行中")
.setContentText("正在导航中,请保持应用运行")
.setSmallIcon(R.drawable.ic_navigation) // 使用你自己的图标资源
.setOngoing(true)
.build()
startForeground(NOTIFICATION_ID, notification)
}
// 实现监听器,处理导航播报等
private val naviListener = object : AMapNaviListener {
override fun onInitNaviFailure() {
Log.e(TAG, "onInitNaviFailure!")
// 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 = aMapNavi.calculateDriveRoute(startList, endList, null, PathPlanningStrategy.DRIVING_MULTIPLE_ROUTES_DEFAULT);
if (!success) {
Log.e(TAG, "路径规划请求失败")
}
}
override fun onStartNavi(p0: Int) {
Log.e(TAG, "onStartNavi!")
// TODO("Not yet implemented")
}
override fun onTrafficStatusUpdate() {
Log.e(TAG, "onTrafficStatusUpdate!")
// TODO("Not yet implemented")
}
override fun onLocationChange(location: AMapNaviLocation) {
Log.e(TAG, "onLocationChange! ${location}")
// 可通过广播发送位置信息到 Flutter
}
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 onEndEmulatorNavi() {
Log.e(TAG, "onEndEmulatorNavi! ")
// TODO("Not yet implemented")
}
override fun onArriveDestination() {
Log.e(TAG, "onArriveDestination! ")
// TODO("Not yet implemented")
}
override fun onCalculateRouteFailure(p0: Int) {
Log.e(TAG, "路径规划失败, 错误码: $p0")
// TODO: 可添加反馈机制给 Flutter 层或用户提示
}
override fun onCalculateRouteFailure(p0: AMapCalcRouteResult?) {
Log.e(TAG, "onCalculateRouteFailure! ${p0}")
// TODO("Not yet implemented")
}
override fun onReCalculateRouteForYaw() {
Log.e(TAG, "onReCalculateRouteForYaw!")
// TODO("Not yet implemented")
}
override fun onReCalculateRouteForTrafficJam() {
Log.e(TAG, "onReCalculateRouteForTrafficJam!")
// TODO("Not yet implemented")
}
override fun onArrivedWayPoint(p0: Int) {
Log.e(TAG, "onArrivedWayPoint!")
// TODO("Not yet implemented")
}
override fun onGpsOpenStatus(p0: Boolean) {
Log.e(TAG, "onGpsOpenStatus!")
// TODO("Not yet implemented")
}
override fun onNaviInfoUpdate(p0: NaviInfo?) {
Log.e(TAG, "onNaviInfoUpdate! $p0")
// TODO("Not yet implemented")
}
override fun updateCameraInfo(p0: Array<out AMapNaviCameraInfo>?) {
Log.e(TAG, "updateCameraInfo! $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 onServiceAreaUpdate(p0: Array<out AMapServiceAreaInfo>?) {
Log.e(TAG, "onServiceAreaUpdate! $p0")
// TODO("Not yet implemented")
}
override fun showCross(p0: AMapNaviCross?) {
Log.e(TAG, "showCross! $p0")
// TODO("Not yet implemented")
}
override fun hideCross() {
Log.e(TAG, "hideCross!")
// TODO("Not yet implemented")
}
override fun showModeCross(p0: AMapModelCross?) {
Log.e(TAG, "showModeCross! $p0")
// TODO("Not yet implemented")
}
override fun hideModeCross() {
Log.e(TAG, "hideModeCross!")
// 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 showLaneInfo(p0: AMapLaneInfo?) {
Log.e(TAG, "showLaneInfo! $p0")
// TODO("Not yet implemented")
}
override fun hideLaneInfo() {
Log.e(TAG, "hideLaneInfo!")
// TODO("Not yet implemented")
}
override fun onCalculateRouteSuccess(ids: IntArray?) {
Log.e(TAG, "onCalculateRouteSuccess! $ids")
aMapNavi.startNavi(NaviType.EMULATOR)
}
override fun onCalculateRouteSuccess(p0: AMapCalcRouteResult?) {
Log.e(TAG, "onCalculateRouteSuccess! $p0")
// TODO("Not yet implemented")
}
override fun notifyParallelRoad(p0: Int) {
Log.d(TAG, "是否在拥挤平行路段: $p0")
// TODO("Not yet implemented")
}
override fun OnUpdateTrafficFacility(p0: Array<out AMapNaviTrafficFacilityInfo>?) {
Log.d(TAG, "OnUpdateTrafficFacility: $p0")
// TODO("Not yet implemented")
}
override fun OnUpdateTrafficFacility(p0: AMapNaviTrafficFacilityInfo?) {
Log.d(TAG, "OnUpdateTrafficFacility: $p0")
// TODO("Not yet implemented")
}
override fun updateAimlessModeStatistics(p0: AimLessModeStat?) {
// TODO("Not yet implemented")
Log.d(TAG, "updateAimlessModeStatistics: $p0")
}
override fun updateAimlessModeCongestionInfo(p0: AimLessModeCongestionInfo?) {
// TODO("Not yet implemented")
Log.d(TAG, "updateAimlessModeCongestionInfo: $p0")
}
override fun onPlayRing(p0: Int) {
Log.d(TAG, "onPlayRing: $p0")
// TODO("Not yet implemented")
}
override fun onNaviRouteNotify(p0: AMapNaviRouteNotifyData?) {
Log.w(TAG, "GPS信号弱")
// TODO("Not yet implemented")
}
override fun onGpsSignalWeak(p0: Boolean) {
Log.w(TAG, "GPS信号弱")
// TODO("Not yet implemented")
}
}
override fun onBind(intent: Intent?): IBinder? = null
}

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

@ -0,0 +1,34 @@
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")
}
}

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

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M12,2L3,21h9v-6h6v6h3L12,2z" />
</vector>

0
local_plugins/amap_flutter/ios/.gitignore → local_plugins/amap_service/ios/.gitignore

0
local_plugins/amap_flutter/ios/Assets/.gitkeep → local_plugins/amap_service/ios/Assets/.gitkeep

6
local_plugins/amap_flutter/ios/Classes/AmapFlutterPlugin.swift → local_plugins/amap_service/ios/Classes/AmapServicePlugin.swift

@ -1,10 +1,10 @@
import Flutter
import UIKit
public class AmapFlutterPlugin: NSObject, FlutterPlugin {
public class AmapServicePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "amap_flutter", binaryMessenger: registrar.messenger())
let instance = AmapFlutterPlugin()
let channel = FlutterMethodChannel(name: "amap_service", binaryMessenger: registrar.messenger())
let instance = AmapServicePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}

0
local_plugins/amap_flutter/ios/Resources/PrivacyInfo.xcprivacy → local_plugins/amap_service/ios/Resources/PrivacyInfo.xcprivacy

6
local_plugins/amap_flutter/ios/amap_flutter.podspec → local_plugins/amap_service/ios/amap_service.podspec

@ -1,9 +1,9 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint amap_flutter.podspec` to validate before publishing.
# Run `pod lib lint amap_service.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'amap_flutter'
s.name = 'amap_service'
s.version = '0.0.1'
s.summary = 'A new Flutter project.'
s.description = <<-DESC
@ -25,5 +25,5 @@ A new Flutter project.
# 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 = {'amap_flutter_privacy' => ['Resources/PrivacyInfo.xcprivacy']}
# s.resource_bundles = {'amap_service_privacy' => ['Resources/PrivacyInfo.xcprivacy']}
end

60
local_plugins/amap_service/lib/amap_service.dart

@ -0,0 +1,60 @@
import 'package:flutter/services.dart';
class AmapService {
static final AmapService _instance = AmapService._internal();
/// 获取单例
factory AmapService() => _instance;
// 方法通道
final MethodChannel _methodChannel = const MethodChannel(
'com.yunqiinnovation.amap_service',
);
// 事件通道 - 简化为只使用数据和状态通道
late final EventChannel _dataEventChannel;
late final EventChannel _statusEventChannel;
// 事件流
late final Stream<dynamic> _dataStream;
late final Stream<dynamic> _statusStream;
// 获取事件流
Stream<dynamic> get onDataReceived => _dataStream;
// 状态流直接传递原始数据
Stream<dynamic> get onStatusUpdated => _statusStream;
// 内部构造函数
AmapService._internal() {
_dataEventChannel = const EventChannel(
'com.yunqiinnovation.amap_service/data_events',
);
_statusEventChannel = const EventChannel(
'com.yunqiinnovation.amap_service/status_events',
);
// 初始化事件流
_dataStream = _dataEventChannel.receiveBroadcastStream();
_statusStream = _statusEventChannel.receiveBroadcastStream();
}
// 初始化SDK
Future<void> initSdk({String? iosKey, String? androidKey}) async {}
// 定位初始化
Future<void> initLocation() async {}
// 开始定位
Future<void> startLocation() async {}
// 监听定位返回位置
Stream<dynamic> locationStream() {
return _dataStream;
}
// 停止定位
Future<void> stopLocation() async {}
// 销毁
Future<void> dispose() async {}
}

10
local_plugins/amap_flutter/pubspec.yaml → local_plugins/amap_service/pubspec.yaml

@ -1,10 +1,10 @@
name: amap_flutter
name: amap_service
description: "A new Flutter project."
version: 0.0.1
homepage:
environment:
sdk: ^3.7.0
sdk: ^3.7.2
flutter: '>=3.3.0'
dependencies:
@ -35,10 +35,10 @@ flutter:
plugin:
platforms:
android:
package: com.example.amap_flutter
pluginClass: AmapFlutterPlugin
package: com.yunqiinnovation.amap_service
pluginClass: AmapServicePlugin
ios:
pluginClass: AmapFlutterPlugin
pluginClass: AmapServicePlugin
# To add assets to your plugin package, add an assets section, like this:
# assets:

1
local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicService.kt

@ -81,7 +81,6 @@ class MusicService : MediaBrowserServiceCompat() {
}
}
"playlist" -> {
@Suppress("UNCHECKED_CAST")
val list = intent.getSerializableExtra("songs") as? ArrayList<HashMap<String, String>>
list?.let {

6
pubspec.yaml

@ -75,8 +75,8 @@ dependencies:
path: local_plugins/chat_storage
spotify_sdk:
path: local_plugins/spotify_sdk
amap_flutter:
path: local_plugins/amap_flutter
amap_service:
path: local_plugins/amap_service
jl_opus:
path: local_plugins/jl_opus
jl_ota:
@ -91,8 +91,6 @@ 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