36 changed files with 609 additions and 353 deletions
@ -1 +0,0 @@ |
|||||
rootProject.name = 'amap_flutter' |
|
||||
@ -1,3 +0,0 @@ |
|||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||
package="com.example.amap_flutter"> |
|
||||
</manifest> |
|
||||
@ -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(); |
|
||||
} |
|
||||
} |
|
||||
@ -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(); |
|
||||
} |
|
||||
} |
|
||||
@ -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'); |
|
||||
} |
|
||||
} |
|
||||
@ -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.'); |
|
||||
} |
|
||||
} |
|
||||
@ -1,4 +1,4 @@ |
|||||
# amap_flutter |
# amap_service |
||||
|
|
||||
A new Flutter project. |
A new Flutter project. |
||||
|
|
||||
@ -0,0 +1 @@ |
|||||
|
rootProject.name = 'amap_service' |
||||
@ -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> |
||||
@ -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) {} |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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") |
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
@ -1,10 +1,10 @@ |
|||||
import Flutter |
import Flutter |
||||
import UIKit |
import UIKit |
||||
|
|
||||
public class AmapFlutterPlugin: NSObject, FlutterPlugin { |
public class AmapServicePlugin: NSObject, FlutterPlugin { |
||||
public static func register(with registrar: FlutterPluginRegistrar) { |
public static func register(with registrar: FlutterPluginRegistrar) { |
||||
let channel = FlutterMethodChannel(name: "amap_flutter", binaryMessenger: registrar.messenger()) |
let channel = FlutterMethodChannel(name: "amap_service", binaryMessenger: registrar.messenger()) |
||||
let instance = AmapFlutterPlugin() |
let instance = AmapServicePlugin() |
||||
registrar.addMethodCallDelegate(instance, channel: channel) |
registrar.addMethodCallDelegate(instance, channel: channel) |
||||
} |
} |
||||
|
|
||||
@ -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 {} |
||||
|
} |
||||
Loading…
Reference in new issue