29 changed files with 1244 additions and 379 deletions
@ -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), |
|
||||
) |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
}, |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
@ -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, ""); |
||||
|
}, |
||||
|
), |
||||
|
) |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
); |
||||
|
}, |
||||
|
), |
||||
|
); |
||||
|
} |
||||
@ -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()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
import 'package:get/get.dart'; |
||||
|
|
||||
|
class NavigationController extends GetxController { |
||||
|
static NavigationController get to => Get.find(); //单例访问模式 |
||||
|
} |
||||
@ -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: [], |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -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") |
||||
|
} |
||||
|
} |
||||
@ -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) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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") |
|
||||
} |
|
||||
} |
|
||||
@ -1,9 +1,9 @@ |
|||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:width="24dp" |
android:width="24dp" |
||||
android:height="24dp" |
android:height="24dp" |
||||
android:viewportWidth="24" |
android:viewportWidth="24.0" |
||||
android:viewportHeight="24"> |
android:viewportHeight="24.0"> |
||||
<path |
<path |
||||
android:fillColor="#FFFFFF" |
android:fillColor="#000000" |
||||
android:pathData="M12,2L3,21h9v-6h6v6h3L12,2z" /> |
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> |
</vector> |
||||
|
|||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -1,2 +1,2 @@ |
|||||
#Sun May 18 21:39:38 IST 2025 |
#Wed May 21 01:03:35 CST 2025 |
||||
gradle.version=8.10 |
gradle.version=8.9 |
||||
|
|||||
Loading…
Reference in new issue