Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

newdev_shunjiawei
lxm 1 year ago
parent
commit
03169cac1a
  1. 156
      lib/core/widgets/intro_overlay_builder.dart
  2. 3
      lib/data/services/asr_service.dart
  3. 3
      lib/data/services/ast_service.dart
  4. 12
      lib/data/services/speech_impl/azure_asr_service.dart
  5. 7
      lib/data/services/speech_impl/azure_ast_service.dart
  6. 4
      lib/modules/meeting/controllers/meeting_record_controller.dart
  7. 3
      lib/modules/speech_test/controllers/speech_test_controller.dart
  8. 2
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  9. 2
      local_plugins/agent_service/ios/agent_service/Package.swift
  10. 35
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift
  11. 6
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt
  12. 8
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt
  13. 2
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt
  14. 57
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt
  15. 4
      local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift
  16. 7
      local_plugins/location_service/android/src/main/AndroidManifest.xml
  17. 362
      local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt
  18. 38
      local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationServicePlugin.kt
  19. 499
      local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/NativeLocationService.kt
  20. 10
      local_plugins/location_service/ios/location_service/Sources/location_service/LocationData.swift
  21. 9
      local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift

156
lib/core/widgets/intro_overlay_builder.dart

@ -89,22 +89,9 @@ class IntroOverlayBuilder {
}
static Widget _button(String text, Function() onTap) {
return GestureDetector(
return _AnimatedBorderButton(
text: text,
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.r),
border: Border.all(color: Colors.white),
),
child: Text(
text,
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
),
),
),
);
}
}
@ -166,3 +153,142 @@ class _AnimatedLottieWidgetState extends State<_AnimatedLottieWidget>
);
}
}
// 新增的带边框流动动画的按钮组件
class _AnimatedBorderButton extends StatefulWidget {
final String text;
final Function() onTap;
const _AnimatedBorderButton({
required this.text,
required this.onTap,
});
@override
_AnimatedBorderButtonState createState() => _AnimatedBorderButtonState();
}
class _AnimatedBorderButtonState extends State<_AnimatedBorderButton>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(_animationController);
_animationController.repeat();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onTap,
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return CustomPaint(
painter: _FlowingBorderPainter(
progress: _animation.value,
borderRadius: 20.r,
),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.r),
border: Border.all(color: Colors.transparent),
),
child: Text(
widget.text,
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
),
),
),
);
},
),
);
}
}
// 自定义绘制器,用于绘制流动的边框
class _FlowingBorderPainter extends CustomPainter {
final double progress;
final double borderRadius;
_FlowingBorderPainter({
required this.progress,
required this.borderRadius,
});
@override
void paint(Canvas canvas, Size size) {
final rect = Rect.fromLTWH(0, 0, size.width, size.height);
final rrect = RRect.fromRectAndRadius(rect, Radius.circular(borderRadius));
// 计算路径总长度
final path = Path()..addRRect(rrect);
final pathMetrics = path.computeMetrics().first;
final totalLength = pathMetrics.length;
// 流动效果:创建一个渐变的虚线效果
final dashLength = totalLength * 0.3; // 虚线长度
final startOffset = (progress * totalLength) % totalLength;
// 绘制流动的边框段
for (int i = 0; i < 3; i++) {
final offset = (startOffset + i * totalLength / 3) % totalLength;
final endOffset = (offset + dashLength) % totalLength;
Path extractedPath;
if (endOffset > offset) {
extractedPath = pathMetrics.extractPath(offset, endOffset);
} else {
// 处理跨越路径起点的情况
final path1 = pathMetrics.extractPath(offset, totalLength);
final path2 = pathMetrics.extractPath(0, endOffset);
extractedPath = Path()
..addPath(path1, Offset.zero)
..addPath(path2, Offset.zero);
}
// 创建渐变效果
const gradient = LinearGradient(
colors: [
Colors.green,
Colors.yellow,
Colors.red,
],
stops: [0.0, 0.5, 1.0],
);
final gradientPaint = Paint()
..shader = gradient.createShader(rect)
..style = PaintingStyle.stroke
..strokeWidth = 1.5;
canvas.drawPath(extractedPath, gradientPaint);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}

3
lib/data/services/asr_service.dart

@ -85,9 +85,6 @@ enum RecognitionEventType {
/// 中间识别结果(实时反馈)
intermediateResult,
/// 音频
onAudio,
/// 会话开始
sessionStarted,

3
lib/data/services/ast_service.dart

@ -36,9 +36,6 @@ enum RecognitionEventType1 {
/// 中间识别结果(实时反馈)
intermediateResult,
/// 音频
onAudio,
/// 会话开始
sessionStarted,

12
lib/data/services/speech_impl/azure_asr_service.dart

@ -261,11 +261,6 @@ class AzureAsrService extends GetxService implements AsrService {
// 发送到音频数据流
_audioDataStreamController?.add(data);
// 也可以通过识别事件流发送
_eventStreamController?.add(RecognitionEvent(
type: RecognitionEventType.onAudio,
audio: data,
));
break;
}
}
@ -322,13 +317,6 @@ class AzureAsrService extends GetxService implements AsrService {
detectedLanguage: detectedLanguage,
));
break;
case 'onAudio':
final Uint8List data = eventMap['data'] as Uint8List? ?? Uint8List(0);
_eventStreamController?.add(RecognitionEvent(
type: RecognitionEventType.onAudio,
audio: data,
));
break;
case 'sessionStarted':
_eventStreamController?.add(RecognitionEvent(
type: RecognitionEventType.sessionStarted,

7
lib/data/services/speech_impl/azure_ast_service.dart

@ -97,13 +97,6 @@ class AzureAstService extends GetxService implements AstService {
detectedLanguage: detectedLanguage,
));
break;
case 'onAudio':
final Uint8List data = eventMap['data'] as Uint8List? ?? Uint8List(0);
_eventStreamController?.add(RecognitionEvent1(
type: RecognitionEventType1.onAudio,
audio: data,
));
break;
case 'sessionStarted':
_eventStreamController?.add(RecognitionEvent1(
type: RecognitionEventType1.sessionStarted,

4
lib/modules/meeting/controllers/meeting_record_controller.dart

@ -320,10 +320,6 @@ class MeetingRecordController extends GetxController
case RecognitionEventType.intermediateResult:
intermediateContent.value = event.text;
break;
case RecognitionEventType.onAudio:
// 添加新音频数据到波形
_processAudioData(event.audio!);
break;
case RecognitionEventType.finalResult:
finalContent.value += _formatTranscript(event);
intermediateContent.value = '';

3
lib/modules/speech_test/controllers/speech_test_controller.dart

@ -184,9 +184,6 @@ class SpeechTestController extends GetxController {
// 清空中间结果
intermediateResult.value = '';
break;
case RecognitionEventType.onAudio:
// TODO: Handle this case.
break;
}
}, onError: (error) {
isListening.value = false;

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

@ -591,9 +591,7 @@ object AgentService : CoroutineScope {
}
override fun onAudio(data: ByteArray) {
}
override fun onSessionStopped() {
sendEvent("recognition_stopped", mapOf("status" to "stopped"))

2
local_plugins/agent_service/ios/agent_service/Package.swift

@ -14,6 +14,7 @@ let package = Package(
.package(path: "../../../azure_speech/ios/azure_speech"),
.package(path: "../../../chat_api/ios/chat_api"),
.package(path: "../../../chat_storage/ios/chat_storage"),
.package(path: "../../../location_service/ios/location_service"),
.package(path: "../../../music_service/ios/music_service"),
.package(path: "../../../music_service/ios/user_portrait")
@ -28,6 +29,7 @@ let package = Package(
.product(name: "azure-speech", package: "azure_speech"),
.product(name: "chat-api", package: "chat_api"),
.product(name: "chat-storage", package: "chat_storage"),
.product(name: "location-service", package: "location_service"),
.product(name: "music-service", package: "music_service"),
.product(name: "user-portrait", package: "user_portrait"),
],

35
local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift

@ -1,6 +1,7 @@
import Foundation
import AVFoundation
import ble_service
import location_service
import speech
import azure_speech
import os
@ -65,7 +66,7 @@ class AgentServiceImpl: NSObject {
private let maxIdleSeconds: TimeInterval = 10
internal var audioPlayer: AudioPlayer?
private var locationInfo:[String: String]?
// private var locationInfo:[String: String]?
internal let logger = OSLog(subsystem: "com.yunqiinnovation.agent_service", category: "AgentServiceImpl")
@ -182,7 +183,7 @@ class AgentServiceImpl: NSObject {
}
//启动定位服务
NotificationCenter.default.post(name: .locationstartEvent, object: nil)
addlocationmonitor()
// addlocationmonitor()
if let mcpServer = config["mcpServer"] as? String {
self.mcpServer = mcpServer
}
@ -204,7 +205,9 @@ class AgentServiceImpl: NSObject {
if isInitialized {
loadChatHistory()
}
LocationService.shared.getCurrentLocation() { location in
// self.locationInfo = location
}
return isInitialized
}
@ -841,19 +844,19 @@ private func jsonToString(_ json: [String: Any]) -> String? {
}
// 添加定位监听
private func addlocationmonitor(){
// 通常放在启动后或模块初始化时
NotificationCenter.default.addObserver(
forName: .updatelocationEvent,
object: nil,
queue: .main
) { notification in
if let info = notification.userInfo as? [String: String] {
print("📤 地址信息:\(info)")
self.locationInfo = info
}
}
}
// private func addlocationmonitor(){
// // 通常放在启动后或模块初始化时
// NotificationCenter.default.addObserver(
// forName: .updatelocationEvent,
// object: nil,
// queue: .main
// ) { notification in
// if let info = notification.userInfo as? [String: String] {
// print("📤 地址信息:\(info)")
// self.locationInfo = info
// }
// }
// }
/**
* 填充模板字符串,使用正则表达式匹配 {key} 格式的占位符
* 对应 Kotlin 版本的 fillTemplate 方法

6
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt

@ -681,12 +681,6 @@ class AzureAsrHelper(private val context: Context) {
*/
fun onSessionStopped()
/**
* 返回音频
*
* @param data 识别的音频
*/
fun onAudio(data: ByteArray)
/**
* 识别取消时调用

8
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt

@ -242,14 +242,6 @@ class AzureSpeechPlugin : BleService.Callback, FlutterPlugin {
sendAsrEvent(mapOf("type" to "sessionStopped"))
}
override fun onAudio(data: ByteArray) {
sendAsrEvent(
mapOf(
"type" to "onAudio",
"data" to data
)
)
}
override fun onCanceled(reason: String, errorDetails: String) {

2
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt

@ -403,7 +403,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
Log.e("ChatApiService", "openAI对象为null")
throw IllegalStateException("OpenAI客户端未初始化")
}
// Log.e("ChatApiService", "liwei---------,发送AI请求 model:${currentModel} messages:${chatMessages}")
Log.e("ChatApiService", "liwei---------,发送AI请求 model:${currentModel} messages:${chatMessages}")
val flow = openAI!!.chatCompletions(chatCompletionRequest)
// Log.d(TAG, "liwei-------------------------开始AI 对话 6")
flow

57
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt

@ -19,7 +19,6 @@ import android.telephony.SmsManager
import android.util.Log
import androidx.core.content.ContextCompat
import com.yunqiinnovation.location_service.LocationService
import androidx.core.content.ContextCompat.RECEIVER_NOT_EXPORTED
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.*
@ -1144,67 +1143,19 @@ private class GetCurrentLocationHandler(private val context: Context?) : Functio
})
}.toString()
}
val handler = context.let { SystemFunctionHandler(it) }
// 检查位置权限
if (!handler.checkPermissionAndGuide(Manifest.permission.ACCESS_FINE_LOCATION, "位置信息", "获取位置")) {
return JSONObject().apply {
put("error", JSONObject().apply {
put("code", "permission_denied")
put("message", "需要位置权限才能获取当前位置,请在权限管理中开启位置权限")
})
}.toString()
}
// 获取缓存的位置数据
val cachedLocation = LocationService.getCachedLocationData()
// 异步触发最新定位获取(不等待结果)
LocationService.requestLocationInBackground(object : LocationService.LocationCallback {
override fun onLocationSuccess(latitude: Double, longitude: Double, address: String) {
Log.d(SystemFunctionHandler.TAG, "后台定位成功,缓存已更新: $latitude, $longitude, $address")
}
override fun onLocationError(errorCode: Int, errorMessage: String) {
Log.w(SystemFunctionHandler.TAG, "后台定位失败: $errorCode - $errorMessage")
}
})
// 立即返回缓存数据(如果存在)
if (cachedLocation != null) {
val cacheAge = System.currentTimeMillis() - cachedLocation.timestamp
val cacheAgeMinutes = cacheAge / (60 * 1000)
return JSONObject().apply {
put("result", JSONObject().apply {
put("latitude", cachedLocation.latitude)
put("longitude", cachedLocation.longitude)
put("address", cachedLocation.address)
put("location", "${cachedLocation.latitude},${cachedLocation.longitude}")
put("source", "cache")
put("timestamp", cachedLocation.timestamp)
put("cacheAgeMinutes", cacheAgeMinutes)
put("note", "正在后台更新位置信息")
})
}.toString()
}
// 如果没有缓存,则同步获取一次
return try {
kotlinx.coroutines.suspendCancellableCoroutine { continuation ->
LocationService.requestLocation(object : LocationService.LocationCallback {
override fun onLocationSuccess(latitude: Double, longitude: Double, address: String) {
Log.d(SystemFunctionHandler.TAG, "首次定位成功: $latitude, $longitude, $address")
override fun onLocationSuccess(latitude: Double, longitude: Double) {
Log.d(SystemFunctionHandler.TAG, "首次定位成功: $latitude, $longitude")
val result = JSONObject().apply {
put("result", JSONObject().apply {
put("latitude", latitude)
put("longitude", longitude)
put("address", address)
put("source", "gps")
put("latitude", latitude)
put("timestamp", System.currentTimeMillis())
put("note", "首次获取位置信息")
})
}.toString()

4
local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift

@ -490,9 +490,9 @@ private class GetCurrentLocationHandler: FunctionHandler {
let result: [String: Any] = [
"success": true,
"data": [
"latitude": latitude,
"longitude": longitude,
"location": "\(latitude),\(longitude)",
"latitude": latitude,
"location": "\(longitude),\(latitude)",
"timestamp": Date().timeIntervalSince1970 * 1000
]
]

7
local_plugins/location_service/android/src/main/AndroidManifest.xml

@ -1,7 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yunqiinnovation.location_service">
<!-- 必须:精确定位权限(包含GPS) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 可选:粗略定位权限(网络定位兜底) -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Android 10+ 后台定位需额外声明(若需要后台持续定位) -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- 辅助:获取Wi-Fi/基站信息提升网络定位精度 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

362
local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt

@ -16,30 +16,34 @@ import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.json.JSONObject
object LocationService {
private const val TAG = "LocationService"
private const val PERMISSION_REQUEST_CODE = 1001
// 只接受高精度定位
private const val HIGH_ACCURACY_THRESHOLD = 50.0 // 只接受50米以内的高精度定位
private const val LOCATION_TIMEOUT_MS = 60000L // 增加到60秒超时,给更多时间获取高精度定位
private const val CACHE_PREFS_NAME = "location_cache"
private var appcontext: Context? = null
private var locationClient: AMapLocationClient? = null
private var initialized = false
private var permissionRetryHandler: Handler? = null
private var permissionRetryRunnable: Runnable? = null
// 存储当前的定位请求回调(Flutter用)
private var currentLocationCallback: MethodChannel.Result? = null
// 存储权限请求回调(Flutter用)
private var permissionRequestCallback: MethodChannel.Result? = null
// 存储服务间调用的回调
private val serviceCallbacks = mutableListOf<LocationCallback>()
private var isHighAccuracyMode = true
private var cachedLocationData: LocationData? = null
private var locationTimeoutHandler: Handler? = null
private var locationStartTime: Long = 0
/**
* 定位回调接口,供其他Android服务使用
*/
// 修正接口,包含address参数
interface LocationCallback {
fun onLocationSuccess(latitude: Double, longitude: Double, address: String)
fun onLocationSuccess(latitude: Double, longitude: Double)
fun onLocationError(errorCode: Int, errorMessage: String)
}
@ -65,50 +69,73 @@ object LocationService {
}
fun initialize(context: Context) {
if (initialized) return
appcontext = context
if (!checkPermissions(context)) {
Log.w(TAG, "没有定位权限,等待用户授权中...")
startPermissionMonitor()
if (initialized) {
Log.d(TAG, "定位服务已初始化")
return
}
appcontext = context.applicationContext
try {
AMapLocationClient.updatePrivacyShow(context, true, true)
AMapLocationClient.updatePrivacyAgree(context, true)
locationClient = AMapLocationClient(context.applicationContext)
val option = AMapLocationClientOption().apply {
locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy
isOnceLocation = true // 单次定位
isNeedAddress = true // 需要地址信息
httpTimeOut = 20000 // 设置超时时间
}
locationClient?.setLocationOption(option)
// 优化后的定位参数配置
val option = AMapLocationClientOption()
// 高精度定位模式(融合GPS、网络、传感器)
option.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy)
// 设置定位场景
option.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.SignIn) // 改为签到模式,更适合高精度定位
// 定位间隔(首次定位建议设置较短间隔)
option.setInterval(2000) // 改为2秒,更快响应
// 设置定位监听器
// 超时时间
option.setHttpTimeOut(30000) // 30秒足够
// 启用所有辅助定位技术
option.setGpsFirst(false) // 不强制GPS优先,让SDK智能选择
option.setWifiScan(true) // 启用WiFi辅助定位
option.setSensorEnable(true) // 启用传感器辅助
option.setLocationCacheEnable(true) // 启用位置缓存
// 单次定位设置
option.setOnceLocation(true) // 改为单次定位,避免持续定位的干扰
option.setOnceLocationLatest(true) // 获取最新的定位结果
// 其他设置
option.setMockEnable(false) // 禁用模拟定位
option.setNeedAddress(true) // 启用地址解析,有助于定位精度
locationClient?.setLocationOption(option)
locationClient?.setLocationListener { location ->
handleLocationResult(location)
}
initialized = true
Log.d(TAG, "定位服务初始化完成")
//首次请求一次定位
requestLocation(object : LocationCallback {
override fun onLocationSuccess(latitude: Double, longitude: Double) {
}
override fun onLocationError(errorCode: Int, errorMessage: String) {
}
})
Log.d(TAG, "定位服务初始化完成 - 优化高精度模式")
} catch (e: Exception) {
Log.e(TAG, "高德定位初始化失败: ${e.message}", e)
}
}
// 在LocationService object中添加缓存相关的属性和方法
private var cachedLocationData: LocationData? = null
private const val CACHE_PREFS_NAME = "location_cache"
/**
* 后台异步获取位置信息(不阻塞调用者)
* @param callback 定位结果回调
*/
fun requestLocationInBackground(callback: LocationCallback) {
fun requestLocation(callback: LocationCallback) {
val ctx = appcontext
if (ctx == null) {
callback.onLocationError(-1, "定位服务未初始化")
@ -125,87 +152,82 @@ object LocationService {
return
}
// 优先返回内存中的缓存
if (cachedLocationData != null) {
callback.onLocationSuccess(cachedLocationData!!.latitude,cachedLocationData!!.longitude)
}else{
// 添加到回调列表
serviceCallbacks.add(callback)
}
// 记录定位开始时间
locationStartTime = System.currentTimeMillis()
// 启动超时计时器
startLocationTimeout()
// 使用协程在后台执行
CoroutineScope(Dispatchers.IO).launch {
try {
// 添加到回调列表
serviceCallbacks.add(callback)
// 如果没有正在进行的定位请求,开始定位
if (currentLocationCallback == null && serviceCallbacks.size == 1) {
locationClient?.startLocation()
}
} catch (e: Exception) {
stopLocationTimeout()
callback.onLocationError(-4, "启动后台定位失败: ${e.message}")
}
}
}
// 修改handleLocationResult方法,添加缓存逻辑
// 统一的handleLocationResult方法(整合优化逻辑)
private fun handleLocationResult(location: AMapLocation?) {
// 处理Flutter回调
val flutterCallback = currentLocationCallback
currentLocationCallback = null
if (location == null) {
Log.e(TAG, "定位结果为空")
return
}
Log.d(TAG, "定位结果: 来源=${location.provider}, 精度=${location.accuracy}米, 坐标=(${location.latitude}, ${location.longitude})")
if (location.errorCode == AMapLocation.LOCATION_SUCCESS) {
val accuracy = location.accuracy
Log.d(TAG, "GPS定位成功: 精度=${accuracy}米")
// 只接受高精度GPS定位
if (accuracy <= HIGH_ACCURACY_THRESHOLD) {
Log.d(TAG, "接受高精度GPS定位: ${accuracy}米")
if (location != null && location.errorCode == 0) {
Log.d(TAG, "定位成功: ${location.latitude}, ${location.longitude}")
// 停止超时计时器
stopLocationTimeout()
// 停止定位
try {
locationClient?.stopLocation()
} catch (e: Exception) {
Log.e(TAG, "停止定位失败: ${e.message}", e)
}
// 缓存位置数据
val locationData = LocationData(
latitude = location.latitude,
longitude = location.longitude,
address = location.address ?: "未知地址",
timestamp = System.currentTimeMillis()
)
cachedLocationData = locationData
saveCachedLocationToPrefs(locationData)
// 通知Flutter
if (flutterCallback != null) {
val result = mapOf(
"user_location" to (location.address ?: "未知地址"),
"user_lat" to location.latitude.toString(),
"user_lng" to location.longitude.toString()
)
flutterCallback.success(result)
}
// 通知其他Android服务
// 处理成功的定位结果
serviceCallbacks.forEach { callback ->
try {
callback.onLocationSuccess(
location.latitude,
location.longitude,
location.address ?: "未知地址"
)
} catch (e: Exception) {
Log.e(TAG, "服务回调失败: ${e.message}", e)
}
}
// 清空服务回调列表(单次定位)
serviceCallbacks.clear()
} else {
Log.w(TAG, "定位失败: ${location?.errorCode} - ${location?.errorInfo}")
// 通知Flutter
if (flutterCallback != null) {
flutterCallback.error("LOCATION_ERROR", "定位失败: ${location?.errorInfo ?: "未知错误"}", null)
}
// 通知其他Android服务
serviceCallbacks.forEach { callback ->
try {
callback.onLocationError(
location?.errorCode ?: -1,
location?.errorInfo ?: "未知错误"
)
} catch (e: Exception) {
Log.e(TAG, "服务回调失败: ${e.message}", e)
}
Log.w(TAG, "GPS精度不足(${accuracy}米),继续等待更高精度定位")
}
} else {
Log.e(TAG, "GPS定位失败: ${location.errorCode} - ${location.errorInfo}")
}
// 清空服务回调列表(单次定位)
serviceCallbacks.clear()
}
private fun startPermissionMonitor() {
@ -315,182 +337,56 @@ object LocationService {
}
}
/**
* 处理权限请求结果(Flutter用)
*/
fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
): Boolean {
if (requestCode != PERMISSION_REQUEST_CODE) {
return false
}
val callback = permissionRequestCallback
permissionRequestCallback = null
if (callback == null) {
return true
}
val ctx = appcontext
if (ctx == null) {
callback.error("NO_CONTEXT", "上下文丢失", null)
return true
}
val hasPermission = checkPermissions(ctx)
if (hasPermission) {
// 权限获取成功,重新初始化定位服务
if (!initialized) {
initialize(ctx)
}
callback.success(mapOf("granted" to true))
} else {
callback.success(mapOf("granted" to false))
}
return true
}
/**
* 按需获取当前位置(Flutter用)
*/
fun getCurrentLocation(result: MethodChannel.Result) {
val ctx = appcontext
if (ctx == null) {
result.error("NOT_INITIALIZED", "定位服务未初始化", null)
return
}
if (!checkPermissions(ctx)) {
result.error("NO_PERMISSION", "需要定位权限", null)
return
}
if (!initialized) {
result.error("NOT_INITIALIZED", "定位服务未初始化", null)
return
}
if (currentLocationCallback != null) {
result.error("LOCATION_IN_PROGRESS", "正在定位中,请稍后再试", null)
return
}
// 保存回调,开始定位
currentLocationCallback = result
locationClient?.startLocation()
}
// ========== 以下是供其他Android服务使用的接口 ==========
/**
* 检查是否有定位权限(供其他服务使用)
* 位置数据类
*/
fun hasLocationPermission(): Boolean {
val ctx = appcontext ?: return false
return checkPermissions(ctx)
}
data class LocationData(
val latitude: Double,
val longitude: Double,
val timestamp: Long
)
/**
* 获取当前位置(供其他Android服务使用)
* @param callback 定位结果回调
* 启动定位超时计时器
*/
fun requestLocation(callback: LocationCallback) {
val ctx = appcontext
if (ctx == null) {
callback.onLocationError(-1, "定位服务未初始化")
return
}
if (!checkPermissions(ctx)) {
callback.onLocationError(-2, "没有定位权限")
return
}
private fun startLocationTimeout() {
// 如果已经有超时计时器在运行,先停止它
stopLocationTimeout()
if (!initialized) {
callback.onLocationError(-3, "定位服务未初始化")
return
}
// 添加到回调列表
serviceCallbacks.add(callback)
// 如果没有正在进行的定位请求,开始定位
if (currentLocationCallback == null && serviceCallbacks.size == 1) {
locationClient?.startLocation()
}
}
/**
* 同步获取缓存的位置信息(供其他服务使用)
* 注意:这个方法返回的可能是过期的位置信息
*/
fun getCachedLocationData(): LocationData? {
// 优先返回内存中的缓存
if (cachedLocationData != null) {
return cachedLocationData
}
locationTimeoutHandler = Handler(Looper.getMainLooper())
locationTimeoutHandler?.postDelayed({
Log.w(TAG, "定位超时,停止定位请求")
// 从SharedPreferences中读取缓存
val ctx = appcontext ?: return null
return loadCachedLocationFromPrefs(ctx)
// 停止定位
try {
locationClient?.stopLocation()
} catch (e: Exception) {
Log.e(TAG, "停止定位失败: ${e.message}", e)
}
private fun saveCachedLocationToPrefs(locationData: LocationData) {
val ctx = appcontext ?: return
// 通知所有等待的回调超时错误
serviceCallbacks.forEach { callback ->
try {
val prefs = ctx.getSharedPreferences(CACHE_PREFS_NAME, Context.MODE_PRIVATE)
prefs.edit().apply {
putString("latitude", locationData.latitude.toString())
putString("longitude", locationData.longitude.toString())
putString("address", locationData.address)
putLong("timestamp", locationData.timestamp)
apply()
}
callback.onLocationError(-5, "定位超时,未能在${LOCATION_TIMEOUT_MS/1000}秒内获取到高精度位置")
} catch (e: Exception) {
Log.e(TAG, "保存位置缓存失败: ${e.message}", e)
Log.e(TAG, "超时回调失败: ${e.message}", e)
}
}
private fun loadCachedLocationFromPrefs(context: Context): LocationData? {
return try {
val prefs = context.getSharedPreferences(CACHE_PREFS_NAME, Context.MODE_PRIVATE)
val latitude = prefs.getString("latitude", null)?.toDoubleOrNull() ?: return null
val longitude = prefs.getString("longitude", null)?.toDoubleOrNull() ?: return null
val address = prefs.getString("address", null) ?: return null
val timestamp = prefs.getLong("timestamp", 0L)
if (timestamp == 0L) return null
// 清空回调列表
serviceCallbacks.clear()
LocationData(latitude, longitude, address, timestamp)
} catch (e: Exception) {
Log.e(TAG, "读取位置缓存失败: ${e.message}", e)
null
}
// 清理超时处理器
locationTimeoutHandler = null
}, LOCATION_TIMEOUT_MS)
}
/**
* 位置数据类
* 停止定位超时计时器
*/
data class LocationData(
val latitude: Double,
val longitude: Double,
val address: String,
val timestamp: Long
)
fun stop() {
locationClient?.stopLocation()
locationClient?.onDestroy()
locationClient = null
currentLocationCallback = null
permissionRequestCallback = null
serviceCallbacks.clear()
initialized = false
stopPermissionMonitor()
private fun stopLocationTimeout() {
locationTimeoutHandler?.removeCallbacksAndMessages(null)
locationTimeoutHandler = null
}
}

38
local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationServicePlugin.kt

@ -12,11 +12,10 @@ import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry
/** LocationServicePlugin */
class LocationServicePlugin: FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.RequestPermissionsResultListener {
class LocationServicePlugin: FlutterPlugin, MethodCallHandler {
private val TAG = "LocationServicePlugin"
private lateinit var context: Context
private lateinit var channel : MethodChannel
private var activityBinding: ActivityPluginBinding? = null
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext
@ -25,43 +24,12 @@ class LocationServicePlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Pl
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
// Log.d(TAG, "Method call: ${call.method}")
when (call.method) {
"location" -> LocationService.getCurrentLocation(result)
"requestPermission" -> LocationService.requestLocationPermission(activityBinding?.activity, result)
"checkPermission" -> LocationService.checkLocationPermission(context, result)
else -> result.notImplemented()
}
Log.d(TAG, "Method call: ${call.method}")
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activityBinding = binding
binding.addRequestPermissionsResultListener(this)
LocationService.initialize(context)
}
override fun onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity()
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
onAttachedToActivity(binding)
}
override fun onDetachedFromActivity() {
activityBinding?.removeRequestPermissionsResultListener(this)
activityBinding = null
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
): Boolean {
return LocationService.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}

499
local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/NativeLocationService.kt

@ -0,0 +1,499 @@
package com.yunqiinnovation.location_service
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.location.*
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.*
object NativeLocationService {
private const val TAG = "NativeLocationService"
private const val PERMISSION_REQUEST_CODE = 1002
// 高精度定位阈值
private const val HIGH_ACCURACY_THRESHOLD = 50.0f // 只接受50米以内的高精度定位
private const val LOCATION_TIMEOUT_MS = 60000L // 60秒超时
private const val MIN_TIME_BETWEEN_UPDATES = 5000L // 5秒最小更新间隔
private const val MIN_DISTANCE_CHANGE_FOR_UPDATES = 10f // 10米最小距离变化
private var appContext: Context? = null
private var locationManager: LocationManager? = null
private var initialized = false
private val serviceCallbacks = mutableListOf<LocationCallback>()
private var cachedLocationData: LocationData? = null
private var locationTimeoutHandler: Handler? = null
private var locationStartTime: Long = 0
private var gpsLocationListener: LocationListener? = null
private var networkLocationListener: LocationListener? = null
// 定位回调接口
interface LocationCallback {
fun onLocationSuccess(latitude: Double, longitude: Double)
fun onLocationError(errorCode: Int, errorMessage: String)
}
/**
* 初始化原生定位服务
*/
fun initialize(context: Context) {
if (initialized) {
Log.d(TAG, "原生定位服务已初始化")
return
}
appContext = context.applicationContext
locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (locationManager == null) {
Log.e(TAG, "无法获取LocationManager")
return
}
initialized = true
Log.d(TAG, "原生定位服务初始化完成")
}
/**
* 检查GPS是否可用
*/
private fun isGpsEnabled(): Boolean {
return locationManager?.isProviderEnabled(LocationManager.GPS_PROVIDER) ?: false
}
/**
* 检查网络定位是否可用
*/
private fun isNetworkEnabled(): Boolean {
return locationManager?.isProviderEnabled(LocationManager.NETWORK_PROVIDER) ?: false
}
/**
* 获取位置信息(异步)
*/
fun requestLocation(callback: LocationCallback) {
val ctx = appContext
if (ctx == null) {
callback.onLocationError(-1, "定位服务未初始化")
return
}
if (!checkPermissions(ctx)) {
callback.onLocationError(-2, "没有定位权限")
return
}
if (!initialized) {
callback.onLocationError(-3, "定位服务未初始化")
return
}
// 检查定位服务是否可用
if (!isGpsEnabled() && !isNetworkEnabled()) {
callback.onLocationError(-4, "GPS和网络定位都不可用,请检查设置")
return
}
// 优先返回缓存的位置
cachedLocationData?.let { cached ->
val currentTime = System.currentTimeMillis()
// 如果缓存时间在5分钟内,直接返回
if (currentTime - cached.timestamp < 5 * 60 * 1000) {
callback.onLocationSuccess(cached.latitude, cached.longitude)
return
}
}
// 添加到回调列表
serviceCallbacks.add(callback)
// 记录定位开始时间
locationStartTime = System.currentTimeMillis()
// 启动超时计时器
startLocationTimeout()
// 使用协程在后台执行定位
CoroutineScope(Dispatchers.IO).launch {
try {
startLocationUpdates()
} catch (e: Exception) {
stopLocationTimeout()
callback.onLocationError(-5, "启动定位失败: ${e.message}")
}
}
}
/**
* 开始位置更新
*/
private fun startLocationUpdates() {
val ctx = appContext ?: return
val locManager = locationManager ?: return
// 检查权限
if (ActivityCompat.checkSelfPermission(
ctx,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(
ctx,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
Log.e(TAG, "没有定位权限")
return
}
Log.d(TAG, "=== 启动混合定位模式 ===")
Log.d(TAG, "网络定位可用: ${isNetworkEnabled()}")
Log.d(TAG, "GPS定位可用: ${isGpsEnabled()}")
// 优先启动网络定位
if (isNetworkEnabled()) {
Log.d(TAG, "🌐 启动网络定位(优先)")
networkLocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
Log.d(TAG, "🌐 网络定位更新: 精度=${location.accuracy}米")
handleLocationResult(location, "NETWORK")
}
override fun onProviderEnabled(provider: String) {
Log.d(TAG, "网络提供者已启用: $provider")
}
override fun onProviderDisabled(provider: String) {
Log.d(TAG, "网络提供者已禁用: $provider")
}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
Log.d(TAG, "网络状态变化: $provider, status: $status")
}
}
locManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MIN_TIME_BETWEEN_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES,
networkLocationListener!!,
Looper.getMainLooper()
)
Log.d(TAG, "✅ 网络定位已启动")
}
// GPS作为补充定位
if (isGpsEnabled()) {
Log.d(TAG, "🛰️ 启动GPS定位(补充)")
gpsLocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
Log.d(TAG, "🛰️ GPS定位更新: 精度=${location.accuracy}米")
handleLocationResult(location, "GPS")
}
override fun onProviderEnabled(provider: String) {
Log.d(TAG, "GPS提供者已启用: $provider")
}
override fun onProviderDisabled(provider: String) {
Log.d(TAG, "GPS提供者已禁用: $provider")
}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
when (status) {
LocationProvider.AVAILABLE -> Log.d(TAG, "GPS状态: 可用")
LocationProvider.OUT_OF_SERVICE -> Log.d(TAG, "GPS状态: 服务中断")
LocationProvider.TEMPORARILY_UNAVAILABLE -> Log.d(TAG, "GPS状态: 暂时不可用")
}
}
}
locManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
MIN_TIME_BETWEEN_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES,
gpsLocationListener!!,
Looper.getMainLooper()
)
Log.d(TAG, "✅ GPS定位已启动")
} else {
Log.w(TAG, "GPS未启用,仅使用网络定位")
}
if (!isNetworkEnabled() && !isGpsEnabled()) {
Log.e(TAG, "❌ 所有定位服务都不可用")
serviceCallbacks.forEach { callback ->
callback.onLocationError(-6, "定位服务不可用")
}
}
}
/**
* 处理定位结果
*/
// 修改handleLocationResult方法,对网络定位进行更细致的处理
private fun handleLocationResult(location: Location, provider: String) {
Log.d(TAG, "定位结果: 来源=$provider, 精度=${location.accuracy}米, 坐标=(${location.latitude}, ${location.longitude})")
// 混合定位策略:优先接受可用的定位结果
when (provider) {
"NETWORK" -> {
// 网络定位优先策略
when {
location.accuracy <= 200f -> {
Log.d(TAG, "✅ 接受网络定位: ${location.accuracy}米")
processLocationSuccess(location)
}
location.accuracy <= 1000f -> {
Log.d(TAG, "⚠️ 接受中等精度网络定位: ${location.accuracy}米")
// 短暂等待,看是否有更好的定位
val elapsedTime = System.currentTimeMillis() - locationStartTime
if (elapsedTime > 5000) { // 5秒后接受
processLocationSuccess(location)
} else {
Log.d(TAG, "等待更高精度定位中...")
}
}
location.accuracy <= 3000f -> {
Log.w(TAG, "⚠️ 网络定位精度较低: ${location.accuracy}米")
// 10秒后接受低精度定位
val elapsedTime = System.currentTimeMillis() - locationStartTime
if (elapsedTime > 10000) {
Log.d(TAG, "接受低精度网络定位")
processLocationSuccess(location)
}
}
else -> {
Log.w(TAG, "网络定位精度过低(${location.accuracy}米),继续等待")
}
}
}
"GPS" -> {
// GPS作为高精度补充
if (location.accuracy <= HIGH_ACCURACY_THRESHOLD) {
Log.d(TAG, "✅ 接受高精度GPS定位: ${location.accuracy}米")
processLocationSuccess(location)
} else if (location.accuracy <= HIGH_ACCURACY_THRESHOLD * 2) {
Log.d(TAG, "✅ 接受GPS定位: ${location.accuracy}米")
processLocationSuccess(location)
} else {
Log.w(TAG, "GPS定位精度不足(${location.accuracy}米),继续等待")
}
}
}
}
/**
* 处理定位成功
*/
private fun processLocationSuccess(location: Location) {
// 停止超时计时器
stopLocationTimeout()
// 停止定位更新
stopLocationUpdates()
// 获取地址信息(可选)
val address = getAddressFromLocation(location)
// 缓存位置数据
val locationData = LocationData(
latitude = location.latitude,
longitude = location.longitude,
timestamp = System.currentTimeMillis()
)
cachedLocationData = locationData
// 处理成功的定位结果
serviceCallbacks.forEach { callback ->
try {
callback.onLocationSuccess(
location.latitude,
location.longitude
)
} catch (e: Exception) {
Log.e(TAG, "服务回调失败: ${e.message}", e)
}
}
// 清空服务回调列表
serviceCallbacks.clear()
}
/**
* 停止定位更新
*/
private fun stopLocationUpdates() {
val locManager = locationManager ?: return
gpsLocationListener?.let {
locManager.removeUpdates(it)
gpsLocationListener = null
}
networkLocationListener?.let {
locManager.removeUpdates(it)
networkLocationListener = null
}
Log.d(TAG, "已停止定位更新")
}
/**
* 从位置获取地址信息
*/
private fun getAddressFromLocation(location: Location): String {
return try {
val ctx = appContext ?: return ""
val geocoder = Geocoder(ctx, Locale.getDefault())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Android 13+ 使用新的异步API
var addressResult = ""
geocoder.getFromLocation(location.latitude, location.longitude, 1) { addresses ->
if (addresses.isNotEmpty()) {
addressResult = addresses[0].getAddressLine(0) ?: ""
}
}
addressResult
} else {
// 旧版本使用同步API
@Suppress("DEPRECATION")
val addresses = geocoder.getFromLocation(location.latitude, location.longitude, 1)
if (!addresses.isNullOrEmpty()) {
addresses[0].getAddressLine(0) ?: ""
} else {
""
}
}
} catch (e: Exception) {
Log.w(TAG, "获取地址失败: ${e.message}")
""
}
}
/**
* 检查定位权限
*/
private fun checkPermissions(context: Context): Boolean {
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
/**
* 检查精确定位权限
*/
private fun hasFineLocationPermission(context: Context): Boolean {
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
/**
* 检查粗略定位权限
*/
private fun hasCoarseLocationPermission(context: Context): Boolean {
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
/**
* 检查后台定位权限
*/
private fun hasBackgroundLocationPermission(context: Context): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
) == PackageManager.PERMISSION_GRANTED
} else {
true // Android 9 及以下没有这个权限
}
}
/**
* 位置数据类
*/
data class LocationData(
val latitude: Double,
val longitude: Double,
val timestamp: Long
)
/**
* 启动定位超时计时器
*/
private fun startLocationTimeout() {
// 如果已经有超时计时器在运行,先停止它
stopLocationTimeout()
locationTimeoutHandler = Handler(Looper.getMainLooper())
locationTimeoutHandler?.postDelayed({
Log.w(TAG, "定位超时,停止定位请求")
// 停止定位
stopLocationUpdates()
// 通知所有等待的回调超时错误
serviceCallbacks.forEach { callback ->
try {
callback.onLocationError(-6, "定位超时,未能在${LOCATION_TIMEOUT_MS/1000}秒内获取到位置")
} catch (e: Exception) {
Log.e(TAG, "超时回调失败: ${e.message}", e)
}
}
// 清空回调列表
serviceCallbacks.clear()
// 清理超时处理器
locationTimeoutHandler = null
}, LOCATION_TIMEOUT_MS)
}
/**
* 停止定位超时计时器
*/
private fun stopLocationTimeout() {
locationTimeoutHandler?.removeCallbacksAndMessages(null)
locationTimeoutHandler = null
}
/**
* 清理资源
*/
fun cleanup() {
stopLocationUpdates()
stopLocationTimeout()
serviceCallbacks.clear()
cachedLocationData = null
initialized = false
Log.d(TAG, "原生定位服务已清理")
}
}
// 调整精度阈值,更适合混合定位
private const val HIGH_ACCURACY_THRESHOLD = 100.0f // 放宽到100米
private const val LOCATION_TIMEOUT_MS = 30000L // 缩短到30秒
private const val MIN_TIME_BETWEEN_UPDATES = 2000L // 2秒更新
private const val MIN_DISTANCE_CHANGE_FOR_UPDATES = 5f // 5米距离变化

10
local_plugins/location_service/ios/location_service/Sources/location_service/LocationData.swift

@ -2,15 +2,15 @@ import Foundation
/// 位置数据结构
public struct LocationData: Codable {
public let latitude: Double
public let longitude: Double
public let latitude: Double
public let timestamp: TimeInterval
public let latitudeandlongitude: String
public let location: String
public init(latitude: Double, longitude: Double,timestamp: TimeInterval) {
self.latitude = latitude
public init( longitude: Double,latitude: Double,timestamp: TimeInterval) {
self.longitude = longitude
self.latitude = latitude
self.timestamp = timestamp
self.latitudeandlongitude = "\(latitude),\(longitude)"
self.location = "\(longitude),\(latitude)"
}
}

9
local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift

@ -204,8 +204,8 @@ public class LocationService: NSObject {
let longitude = Double(lngStr) {
let locationData = LocationData(
latitude: latitude,
longitude: longitude,
latitude: latitude,
timestamp: Date().timeIntervalSince1970 * 1000
)
completion(.success(locationData))
@ -245,8 +245,8 @@ public class LocationService: NSObject {
/// 处理位置数据
private func processLocation(_ location: CLLocation) {
let locationInfo: [String: Any] = [
"user_lat": String(location.coordinate.latitude),
"user_lng": String(location.coordinate.longitude),
"user_lat": String(location.coordinate.latitude),
"timestamp": Date().timeIntervalSince1970 * 1000,
"accuracy": location.horizontalAccuracy // 添加精度信息
]
@ -255,8 +255,8 @@ public class LocationService: NSObject {
// 更新缓存
cachedLocationData = LocationData(
latitude: location.coordinate.latitude,
longitude: location.coordinate.longitude,
latitude: location.coordinate.latitude,
timestamp: Date().timeIntervalSince1970 * 1000
)
@ -268,8 +268,9 @@ public class LocationService: NSObject {
/// 格式化位置数据
private func formatLocationData(_ locationData: LocationData) -> [String: Any] {
return [
"user_lat": String(locationData.latitude),
"user_lng": String(locationData.longitude),
"user_lat": String(locationData.latitude),
"location": "\(locationData.longitude),\(locationData.latitude)",
"timestamp": locationData.timestamp
]
}

Loading…
Cancel
Save