expo-gaode-map-navigation 2.0.12-next.0 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +296 -7
- package/android/build.gradle +12 -4
- package/android/src/main/AndroidManifest.xml +10 -1
- package/android/src/main/cpp/cluster_jni.cpp +56 -0
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +49 -8
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +83 -15
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +13 -3
- package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +36 -39
- package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +23 -17
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterView.kt +5 -2
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapView.kt +122 -10
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapViewModule.kt +2 -2
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +37 -25
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +6 -6
- package/android/src/main/java/expo/modules/gaodemap/map/search/ExpoGaodeMapSearchModule.kt +751 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +103 -0
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviView.kt +1408 -394
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviViewModule.kt +121 -1
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNavigationModule.kt +14 -28
- package/android/src/main/java/expo/modules/gaodemap/navigation/listeners/IndependentRouteListener.kt +28 -3
- package/android/src/main/java/expo/modules/gaodemap/navigation/listeners/RouteCalculateListener.kt +2 -2
- package/android/src/main/java/expo/modules/gaodemap/navigation/managers/IndependentRouteManager.kt +114 -15
- package/android/src/main/java/expo/modules/gaodemap/navigation/routes/drive/DriveTruckRouteCalculator.kt +24 -35
- package/android/src/main/java/expo/modules/gaodemap/navigation/services/IndependentRouteService.kt +50 -36
- package/android/src/main/java/expo/modules/gaodemap/navigation/services/NavigationForegroundService.kt +661 -0
- package/android/src/main/java/expo/modules/gaodemap/navigation/utils/Converters.kt +21 -12
- package/android/src/main/res/drawable/ic_nav_notification_small.xml +10 -0
- package/android/src/main/res/drawable/landback_0.png +0 -0
- package/android/src/main/res/drawable/landback_1.png +0 -0
- package/android/src/main/res/drawable/landback_2.png +0 -0
- package/android/src/main/res/drawable/landback_3.png +0 -0
- package/android/src/main/res/drawable/landback_4.png +0 -0
- package/android/src/main/res/drawable/landback_5.png +0 -0
- package/android/src/main/res/drawable/landback_6.png +0 -0
- package/android/src/main/res/drawable/landback_7.png +0 -0
- package/android/src/main/res/drawable/landback_8.png +0 -0
- package/android/src/main/res/drawable/landback_9.png +0 -0
- package/android/src/main/res/drawable/landback_a.png +0 -0
- package/android/src/main/res/drawable/landback_b.png +0 -0
- package/android/src/main/res/drawable/landback_c.png +0 -0
- package/android/src/main/res/drawable/landback_d.png +0 -0
- package/android/src/main/res/drawable/landback_e.png +0 -0
- package/android/src/main/res/drawable/landback_f.png +0 -0
- package/android/src/main/res/drawable/landback_g.png +0 -0
- package/android/src/main/res/drawable/landback_h.png +0 -0
- package/android/src/main/res/drawable/landback_i.png +0 -0
- package/android/src/main/res/drawable/landback_j.png +0 -0
- package/android/src/main/res/drawable/landback_k.png +0 -0
- package/android/src/main/res/drawable/landback_l.png +0 -0
- package/android/src/main/res/drawable/landfront_0.png +0 -0
- package/android/src/main/res/drawable/landfront_00.png +0 -0
- package/android/src/main/res/drawable/landfront_1.png +0 -0
- package/android/src/main/res/drawable/landfront_11.png +0 -0
- package/android/src/main/res/drawable/landfront_20.png +0 -0
- package/android/src/main/res/drawable/landfront_21.png +0 -0
- package/android/src/main/res/drawable/landfront_22.png +0 -0
- package/android/src/main/res/drawable/landfront_3.png +0 -0
- package/android/src/main/res/drawable/landfront_33.png +0 -0
- package/android/src/main/res/drawable/landfront_40.png +0 -0
- package/android/src/main/res/drawable/landfront_43.png +0 -0
- package/android/src/main/res/drawable/landfront_44.png +0 -0
- package/android/src/main/res/drawable/landfront_5.png +0 -0
- package/android/src/main/res/drawable/landfront_55.png +0 -0
- package/android/src/main/res/drawable/landfront_61.png +0 -0
- package/android/src/main/res/drawable/landfront_63.png +0 -0
- package/android/src/main/res/drawable/landfront_66.png +0 -0
- package/android/src/main/res/drawable/landfront_70.png +0 -0
- package/android/src/main/res/drawable/landfront_71.png +0 -0
- package/android/src/main/res/drawable/landfront_73.png +0 -0
- package/android/src/main/res/drawable/landfront_77.png +0 -0
- package/android/src/main/res/drawable/landfront_8.png +0 -0
- package/android/src/main/res/drawable/landfront_88.png +0 -0
- package/android/src/main/res/drawable/landfront_90.png +0 -0
- package/android/src/main/res/drawable/landfront_95.png +0 -0
- package/android/src/main/res/drawable/landfront_99.png +0 -0
- package/android/src/main/res/drawable/landfront_a0.png +0 -0
- package/android/src/main/res/drawable/landfront_a8.png +0 -0
- package/android/src/main/res/drawable/landfront_aa.png +0 -0
- package/android/src/main/res/drawable/landfront_b1.png +0 -0
- package/android/src/main/res/drawable/landfront_b5.png +0 -0
- package/android/src/main/res/drawable/landfront_bb.png +0 -0
- package/android/src/main/res/drawable/landfront_c3.png +0 -0
- package/android/src/main/res/drawable/landfront_c8.png +0 -0
- package/android/src/main/res/drawable/landfront_cc.png +0 -0
- package/android/src/main/res/drawable/landfront_d.png +0 -0
- package/android/src/main/res/drawable/landfront_dd.png +0 -0
- package/android/src/main/res/drawable/landfront_e1.png +0 -0
- package/android/src/main/res/drawable/landfront_e5.png +0 -0
- package/android/src/main/res/drawable/landfront_ee.png +0 -0
- package/android/src/main/res/drawable/landfront_f0.png +0 -0
- package/android/src/main/res/drawable/landfront_f1.png +0 -0
- package/android/src/main/res/drawable/landfront_f5.png +0 -0
- package/android/src/main/res/drawable/landfront_ff.png +0 -0
- package/android/src/main/res/drawable/landfront_g3.png +0 -0
- package/android/src/main/res/drawable/landfront_g5.png +0 -0
- package/android/src/main/res/drawable/landfront_gg.png +0 -0
- package/android/src/main/res/drawable/landfront_h1.png +0 -0
- package/android/src/main/res/drawable/landfront_h3.png +0 -0
- package/android/src/main/res/drawable/landfront_h5.png +0 -0
- package/android/src/main/res/drawable/landfront_hh.png +0 -0
- package/android/src/main/res/drawable/landfront_i0.png +0 -0
- package/android/src/main/res/drawable/landfront_i3.png +0 -0
- package/android/src/main/res/drawable/landfront_i5.png +0 -0
- package/android/src/main/res/drawable/landfront_ii.png +0 -0
- package/android/src/main/res/drawable/landfront_j1.png +0 -0
- package/android/src/main/res/drawable/landfront_j8.png +0 -0
- package/android/src/main/res/drawable/landfront_jj.png +0 -0
- package/android/src/main/res/drawable/landfront_kk.png +0 -0
- package/android/src/main/res/drawable/landfront_ll.png +0 -0
- package/android/src/main/res/drawable/nav_notification_brand_icon.xml +16 -0
- package/android/src/main/res/drawable/navi_arrow_leftline.png +0 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_center.xml +5 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_left.xml +8 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_over.xml +6 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_right.xml +8 -0
- package/android/src/main/res/drawable-nodpi/nav_tracker_car.png +0 -0
- package/build/ExpoGaodeMapNaviView.d.ts +16 -0
- package/build/ExpoGaodeMapNaviView.d.ts.map +1 -1
- package/build/ExpoGaodeMapNaviView.js +74 -1
- package/build/ExpoGaodeMapNaviView.js.map +1 -1
- package/build/index.d.ts +56 -8
- package/build/index.d.ts.map +1 -1
- package/build/index.js +452 -10
- package/build/index.js.map +1 -1
- package/build/map/ExpoGaodeMapModule.d.ts +15 -13
- package/build/map/ExpoGaodeMapModule.d.ts.map +1 -1
- package/build/map/ExpoGaodeMapModule.js +31 -39
- package/build/map/ExpoGaodeMapModule.js.map +1 -1
- package/build/map/ExpoGaodeMapOfflineModule.d.ts +5 -0
- package/build/map/ExpoGaodeMapOfflineModule.d.ts.map +1 -1
- package/build/map/ExpoGaodeMapOfflineModule.js.map +1 -1
- package/build/map/ExpoGaodeMapView.d.ts +3 -4
- package/build/map/ExpoGaodeMapView.d.ts.map +1 -1
- package/build/map/ExpoGaodeMapView.js +28 -25
- package/build/map/ExpoGaodeMapView.js.map +1 -1
- package/build/map/components/overlays/Circle.d.ts.map +1 -1
- package/build/map/components/overlays/Circle.js +1 -30
- package/build/map/components/overlays/Circle.js.map +1 -1
- package/build/map/components/overlays/Cluster.d.ts.map +1 -1
- package/build/map/components/overlays/Cluster.js +1 -42
- package/build/map/components/overlays/Cluster.js.map +1 -1
- package/build/map/components/overlays/HeatMap.d.ts.map +1 -1
- package/build/map/components/overlays/HeatMap.js +21 -21
- package/build/map/components/overlays/HeatMap.js.map +1 -1
- package/build/map/components/overlays/Marker.d.ts.map +1 -1
- package/build/map/components/overlays/Marker.js +76 -80
- package/build/map/components/overlays/Marker.js.map +1 -1
- package/build/map/components/overlays/Polygon.d.ts.map +1 -1
- package/build/map/components/overlays/Polygon.js +1 -25
- package/build/map/components/overlays/Polygon.js.map +1 -1
- package/build/map/components/overlays/Polyline.d.ts.map +1 -1
- package/build/map/components/overlays/Polyline.js +1 -31
- package/build/map/components/overlays/Polyline.js.map +1 -1
- package/build/map/index.d.ts +9 -2
- package/build/map/index.d.ts.map +1 -1
- package/build/map/index.js +9 -2
- package/build/map/index.js.map +1 -1
- package/build/map/search/ExpoGaodeMapSearch.types.d.ts +340 -0
- package/build/map/search/ExpoGaodeMapSearch.types.d.ts.map +1 -0
- package/build/map/search/ExpoGaodeMapSearch.types.js +19 -0
- package/build/map/search/ExpoGaodeMapSearch.types.js.map +1 -0
- package/build/map/search/ExpoGaodeMapSearchModule.d.ts +74 -0
- package/build/map/search/ExpoGaodeMapSearchModule.d.ts.map +1 -0
- package/build/map/search/ExpoGaodeMapSearchModule.js +47 -0
- package/build/map/search/ExpoGaodeMapSearchModule.js.map +1 -0
- package/build/map/search/index.d.ts +156 -0
- package/build/map/search/index.d.ts.map +1 -0
- package/build/map/search/index.js +171 -0
- package/build/map/search/index.js.map +1 -0
- package/build/map/types/index.d.ts +2 -2
- package/build/map/types/index.d.ts.map +1 -1
- package/build/map/types/index.js.map +1 -1
- package/build/map/types/map-view.types.d.ts +4 -2
- package/build/map/types/map-view.types.d.ts.map +1 -1
- package/build/map/types/map-view.types.js.map +1 -1
- package/build/map/types/native-module.types.d.ts +11 -12
- package/build/map/types/native-module.types.d.ts.map +1 -1
- package/build/map/types/native-module.types.js.map +1 -1
- package/build/map/types/overlays.types.d.ts +9 -14
- package/build/map/types/overlays.types.d.ts.map +1 -1
- package/build/map/types/overlays.types.js.map +1 -1
- package/build/map/types/route-playback.types.d.ts +16 -0
- package/build/map/types/route-playback.types.d.ts.map +1 -1
- package/build/map/types/route-playback.types.js.map +1 -1
- package/build/map/utils/ErrorHandler.js +11 -11
- package/build/map/utils/ErrorHandler.js.map +1 -1
- package/build/map/utils/OfflineMapManager.d.ts +4 -0
- package/build/map/utils/OfflineMapManager.d.ts.map +1 -1
- package/build/map/utils/OfflineMapManager.js +6 -0
- package/build/map/utils/OfflineMapManager.js.map +1 -1
- package/build/types/coordinates.types.d.ts +3 -0
- package/build/types/coordinates.types.d.ts.map +1 -1
- package/build/types/coordinates.types.js.map +1 -1
- package/build/types/independent.types.d.ts +111 -12
- package/build/types/independent.types.d.ts.map +1 -1
- package/build/types/independent.types.js.map +1 -1
- package/build/types/native-module.types.d.ts +1 -1
- package/build/types/native-module.types.js.map +1 -1
- package/build/types/naviview.types.d.ts +304 -14
- package/build/types/naviview.types.d.ts.map +1 -1
- package/build/types/naviview.types.js.map +1 -1
- package/build/types/route.types.d.ts +12 -2
- package/build/types/route.types.d.ts.map +1 -1
- package/build/types/route.types.js.map +1 -1
- package/expo-module.config.json +4 -2
- package/ios/ExpoGaodeMapNaviView.swift +2331 -201
- package/ios/ExpoGaodeMapNaviViewModule.swift +109 -1
- package/ios/ExpoGaodeMapNavigation.podspec +2 -1
- package/ios/ExpoGaodeMapNavigationModule.swift +7 -5
- package/ios/managers/IndependentRouteManager.swift +90 -26
- package/ios/map/ExpoGaodeMapModule.swift +72 -21
- package/ios/map/ExpoGaodeMapOfflineModule.swift +61 -0
- package/ios/map/ExpoGaodeMapSearchModule.swift +773 -0
- package/ios/map/ExpoGaodeMapView.swift +23 -5
- package/ios/map/GaodeMapPrivacyManager.swift +26 -18
- package/ios/map/cpp/GeometryEngine.cpp +112 -0
- package/ios/map/cpp/GeometryEngine.hpp +21 -0
- package/ios/map/modules/LocationManager.swift +37 -5
- package/ios/map/overlays/MarkerView.swift +11 -11
- package/ios/map/overlays/MarkerViewModule.swift +4 -4
- package/ios/map/overlays/PolylineView.swift +6 -12
- package/ios/map/utils/ClusterNative.h +8 -0
- package/ios/map/utils/ClusterNative.mm +27 -0
- package/ios/map/utils/PermissionManager.swift +115 -6
- package/ios/routes/drive/DriveTruckRouteCalculator.swift +165 -77
- package/ios/routes/walkride/WalkRideRouteCalculator.swift +127 -1
- package/ios/services/IndependentRouteService.swift +198 -39
- package/ios/services/NavigationLiveActivityAttributes.swift +48 -0
- package/ios/services/NavigationLiveActivityManager.swift +359 -0
- package/package.json +22 -7
- package/plugin/build/withGaodeMap.d.ts +8 -0
- package/plugin/build/withGaodeMap.js +60 -4
- package/scripts/check-expo-modules.js +68 -0
- package/shared/cpp/GeometryEngine.cpp +112 -0
- package/shared/cpp/GeometryEngine.hpp +21 -0
- package/widget-template/README.md +46 -0
- package/widget-template/ios/NavigationLiveActivityWidget.swift +367 -0
- package/android/src/main/java/expo/modules/gaodemap/navigation/managers/RouteCalculator.kt +0 -173
|
@@ -96,6 +96,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
|
|
|
96
96
|
private var uiManager: UIManager!
|
|
97
97
|
/// 地图是否已加载完成
|
|
98
98
|
private var isMapLoaded = false
|
|
99
|
+
/// 初始相机是否已应用(仅应用一次,避免与运行时相机控制冲突)
|
|
100
|
+
private var hasAppliedInitialCameraPosition = false
|
|
99
101
|
/// 是否正在处理 annotation 选择事件
|
|
100
102
|
private var isHandlingAnnotationSelect = false
|
|
101
103
|
/// MarkerView 的隐藏容器(用于渲染 children)
|
|
@@ -496,9 +498,10 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
|
|
|
496
498
|
|
|
497
499
|
uiManager.setMapType(mapType)
|
|
498
500
|
|
|
499
|
-
//
|
|
500
|
-
if let position = initialCameraPosition {
|
|
501
|
+
// initialCameraPosition 只应用一次,避免每次 props 更新重置相机导致操作延迟感
|
|
502
|
+
if !hasAppliedInitialCameraPosition, let position = initialCameraPosition {
|
|
501
503
|
cameraManager.setInitialCameraPosition(position)
|
|
504
|
+
hasAppliedInitialCameraPosition = true
|
|
502
505
|
}
|
|
503
506
|
|
|
504
507
|
uiManager.setShowsScale(showsScale)
|
|
@@ -969,6 +972,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
|
|
|
969
972
|
|
|
970
973
|
mapView = resolvedMapView
|
|
971
974
|
super.addSubview(resolvedMapView)
|
|
975
|
+
isMapLoaded = false
|
|
976
|
+
hasAppliedInitialCameraPosition = false
|
|
972
977
|
|
|
973
978
|
cameraManager = CameraManager(mapView: resolvedMapView)
|
|
974
979
|
uiManager = UIManager(mapView: resolvedMapView)
|
|
@@ -1026,6 +1031,12 @@ extension ExpoGaodeMapView {
|
|
|
1026
1031
|
public func mapViewDidFinishLoadingMap(_ mapView: MAMapView) {
|
|
1027
1032
|
guard !isMapLoaded else { return }
|
|
1028
1033
|
isMapLoaded = true
|
|
1034
|
+
|
|
1035
|
+
// 兜底:若初始化阶段尚未生效,在加载完成后应用一次初始相机
|
|
1036
|
+
if !hasAppliedInitialCameraPosition, let position = initialCameraPosition {
|
|
1037
|
+
cameraManager?.setInitialCameraPosition(position)
|
|
1038
|
+
hasAppliedInitialCameraPosition = true
|
|
1039
|
+
}
|
|
1029
1040
|
|
|
1030
1041
|
// 地图加载完成后,应用自定义样式
|
|
1031
1042
|
if let styleData = customMapStyleData {
|
|
@@ -1443,17 +1454,24 @@ extension ExpoGaodeMapView {
|
|
|
1443
1454
|
isHandlingAnnotationSelect = true
|
|
1444
1455
|
|
|
1445
1456
|
// 🔑 统一从 overlayViews 查找 MarkerView(新旧架构统一)
|
|
1446
|
-
for
|
|
1447
|
-
if let markerView =
|
|
1457
|
+
for overlayView in overlayViews {
|
|
1458
|
+
if let markerView = overlayView as? MarkerView {
|
|
1448
1459
|
if markerView.annotation === annotation {
|
|
1449
1460
|
let eventData: [String: Any] = [
|
|
1450
1461
|
"latitude": annotation.coordinate.latitude,
|
|
1451
1462
|
"longitude": annotation.coordinate.longitude
|
|
1452
1463
|
]
|
|
1453
1464
|
markerView.onMarkerPress(eventData)
|
|
1465
|
+
// iOS 对“已选中 annotation”再次点击通常不会重复触发 didSelect。
|
|
1466
|
+
// 对自定义 children marker(无系统 callout)这里主动取消选中,
|
|
1467
|
+
// 以保证同一 marker 可以连续触发点击(例如关闭 sheet 后再次点击)。
|
|
1468
|
+
if !markerView.subviews.isEmpty {
|
|
1469
|
+
mapView.deselectAnnotation(annotation, animated: false)
|
|
1470
|
+
isHandlingAnnotationSelect = false
|
|
1471
|
+
}
|
|
1454
1472
|
return
|
|
1455
1473
|
}
|
|
1456
|
-
} else if let clusterView =
|
|
1474
|
+
} else if let clusterView = overlayView as? ClusterView {
|
|
1457
1475
|
if clusterView.containsAnnotation(annotation) {
|
|
1458
1476
|
clusterView.handleAnnotationTap(annotation)
|
|
1459
1477
|
return
|
|
@@ -46,24 +46,6 @@ enum GaodeMapPrivacyManager {
|
|
|
46
46
|
applyPrivacyState()
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
static func setPrivacyShow(_ show: Bool, hasContainsPrivacy: Bool) {
|
|
50
|
-
let previousStatus = status()
|
|
51
|
-
hasShow = show
|
|
52
|
-
self.hasContainsPrivacy = hasContainsPrivacy
|
|
53
|
-
persistState()
|
|
54
|
-
applyPrivacyState()
|
|
55
|
-
notifyIfNeeded(previousStatus: previousStatus)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
static func setPrivacyAgree(_ agree: Bool) {
|
|
59
|
-
let previousStatus = status()
|
|
60
|
-
hasAgree = agree
|
|
61
|
-
agreedPrivacyVersion = agree ? privacyVersion : nil
|
|
62
|
-
persistState()
|
|
63
|
-
applyPrivacyState()
|
|
64
|
-
notifyIfNeeded(previousStatus: previousStatus)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
49
|
static func setPrivacyVersion(_ version: String) {
|
|
68
50
|
let previousStatus = status()
|
|
69
51
|
privacyVersion = version.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
@@ -83,6 +65,32 @@ enum GaodeMapPrivacyManager {
|
|
|
83
65
|
notifyIfNeeded(previousStatus: previousStatus)
|
|
84
66
|
}
|
|
85
67
|
|
|
68
|
+
static func setPrivacyConfig(
|
|
69
|
+
hasShow: Bool,
|
|
70
|
+
hasContainsPrivacy: Bool,
|
|
71
|
+
hasAgree: Bool,
|
|
72
|
+
privacyVersion newPrivacyVersion: String?,
|
|
73
|
+
updatesPrivacyVersion: Bool
|
|
74
|
+
) {
|
|
75
|
+
let previousStatus = status()
|
|
76
|
+
|
|
77
|
+
if updatesPrivacyVersion {
|
|
78
|
+
privacyVersion = newPrivacyVersion?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
79
|
+
if privacyVersion?.isEmpty == true {
|
|
80
|
+
privacyVersion = nil
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
self.hasShow = hasShow
|
|
85
|
+
self.hasContainsPrivacy = hasContainsPrivacy
|
|
86
|
+
self.hasAgree = hasAgree
|
|
87
|
+
agreedPrivacyVersion = hasAgree ? privacyVersion : nil
|
|
88
|
+
|
|
89
|
+
persistState()
|
|
90
|
+
applyPrivacyState()
|
|
91
|
+
notifyIfNeeded(previousStatus: previousStatus)
|
|
92
|
+
}
|
|
93
|
+
|
|
86
94
|
static func resetPrivacyConsent() {
|
|
87
95
|
let previousStatus = status()
|
|
88
96
|
clearConsentPersistedState(keepCurrentVersion: false)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <cmath>
|
|
4
4
|
#include <map>
|
|
5
5
|
#include <algorithm>
|
|
6
|
+
#include <limits>
|
|
6
7
|
|
|
7
8
|
namespace gaodemap {
|
|
8
9
|
|
|
@@ -19,6 +20,54 @@ static inline double geo_toDegrees(double radians) {
|
|
|
19
20
|
return radians * kRadiansToDegrees;
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
static inline double clampMercatorLatitude(double lat) {
|
|
24
|
+
static constexpr double kMaxMercatorLatitude = 85.05112878;
|
|
25
|
+
if (lat > kMaxMercatorLatitude) return kMaxMercatorLatitude;
|
|
26
|
+
if (lat < -kMaxMercatorLatitude) return -kMaxMercatorLatitude;
|
|
27
|
+
return lat;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static inline double mercatorX01(double lon) {
|
|
31
|
+
double wrapped = std::fmod(lon + 180.0, 360.0);
|
|
32
|
+
if (wrapped < 0.0) {
|
|
33
|
+
wrapped += 360.0;
|
|
34
|
+
}
|
|
35
|
+
return wrapped / 360.0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static inline double mercatorY01(double lat) {
|
|
39
|
+
const double clampedLat = clampMercatorLatitude(lat);
|
|
40
|
+
const double latRad = geo_toRadians(clampedLat);
|
|
41
|
+
const double y = (1.0 - std::asinh(std::tan(latRad)) / kPi) * 0.5;
|
|
42
|
+
if (y < 0.0) return 0.0;
|
|
43
|
+
if (y > 1.0) return 1.0;
|
|
44
|
+
return y;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static double wrappedSpan01(std::vector<double> xs) {
|
|
48
|
+
if (xs.size() <= 1) {
|
|
49
|
+
return 0.0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
std::sort(xs.begin(), xs.end());
|
|
53
|
+
double maxGap = 0.0;
|
|
54
|
+
|
|
55
|
+
for (size_t i = 0; i + 1 < xs.size(); ++i) {
|
|
56
|
+
const double gap = xs[i + 1] - xs[i];
|
|
57
|
+
if (gap > maxGap) {
|
|
58
|
+
maxGap = gap;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const double endGap = xs.front() + 1.0 - xs.back();
|
|
63
|
+
if (endGap > maxGap) {
|
|
64
|
+
maxGap = endGap;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const double span = 1.0 - maxGap;
|
|
68
|
+
return span < 0.0 ? 0.0 : span;
|
|
69
|
+
}
|
|
70
|
+
|
|
22
71
|
double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
|
|
23
72
|
const double radLat1 = geo_toRadians(lat1);
|
|
24
73
|
const double radLat2 = geo_toRadians(lat2);
|
|
@@ -512,6 +561,69 @@ GeoPoint pixelToLatLng(double x, double y, int zoom) {
|
|
|
512
561
|
return {lat, lon};
|
|
513
562
|
}
|
|
514
563
|
|
|
564
|
+
double calculateFitZoomForPoints(
|
|
565
|
+
const std::vector<GeoPoint>& points,
|
|
566
|
+
double viewportWidthPx,
|
|
567
|
+
double viewportHeightPx,
|
|
568
|
+
double paddingPx,
|
|
569
|
+
int minZoom,
|
|
570
|
+
int maxZoom
|
|
571
|
+
) {
|
|
572
|
+
if (minZoom > maxZoom) {
|
|
573
|
+
std::swap(minZoom, maxZoom);
|
|
574
|
+
}
|
|
575
|
+
if (points.empty()) {
|
|
576
|
+
return static_cast<double>(minZoom);
|
|
577
|
+
}
|
|
578
|
+
if (points.size() == 1) {
|
|
579
|
+
return static_cast<double>(maxZoom);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const double safeViewportWidth = viewportWidthPx > 1.0 ? viewportWidthPx : 390.0;
|
|
583
|
+
const double safeViewportHeight = viewportHeightPx > 1.0 ? viewportHeightPx : 844.0;
|
|
584
|
+
const double safePadding = std::max(0.0, paddingPx);
|
|
585
|
+
const double availableWidth = std::max(1.0, safeViewportWidth - safePadding * 2.0);
|
|
586
|
+
const double availableHeight = std::max(1.0, safeViewportHeight - safePadding * 2.0);
|
|
587
|
+
|
|
588
|
+
std::vector<double> projectedXs;
|
|
589
|
+
projectedXs.reserve(points.size());
|
|
590
|
+
|
|
591
|
+
double minY = std::numeric_limits<double>::max();
|
|
592
|
+
double maxY = -std::numeric_limits<double>::max();
|
|
593
|
+
|
|
594
|
+
for (const auto& p : points) {
|
|
595
|
+
projectedXs.push_back(mercatorX01(p.lon));
|
|
596
|
+
const double y = mercatorY01(p.lat);
|
|
597
|
+
if (y < minY) minY = y;
|
|
598
|
+
if (y > maxY) maxY = y;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const double spanX = wrappedSpan01(projectedXs);
|
|
602
|
+
const double spanY = std::max(0.0, maxY - minY);
|
|
603
|
+
static constexpr double kTileSize = 256.0;
|
|
604
|
+
static constexpr double kSpanEpsilon = 1e-12;
|
|
605
|
+
|
|
606
|
+
const double zoomX = spanX <= kSpanEpsilon
|
|
607
|
+
? static_cast<double>(maxZoom)
|
|
608
|
+
: std::log2(availableWidth / (kTileSize * spanX));
|
|
609
|
+
const double zoomY = spanY <= kSpanEpsilon
|
|
610
|
+
? static_cast<double>(maxZoom)
|
|
611
|
+
: std::log2(availableHeight / (kTileSize * spanY));
|
|
612
|
+
|
|
613
|
+
double fitZoom = std::min(zoomX, zoomY);
|
|
614
|
+
if (!std::isfinite(fitZoom)) {
|
|
615
|
+
fitZoom = static_cast<double>(minZoom);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (fitZoom < static_cast<double>(minZoom)) {
|
|
619
|
+
fitZoom = static_cast<double>(minZoom);
|
|
620
|
+
} else if (fitZoom > static_cast<double>(maxZoom)) {
|
|
621
|
+
fitZoom = static_cast<double>(maxZoom);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return fitZoom;
|
|
625
|
+
}
|
|
626
|
+
|
|
515
627
|
// --- 批量地理围栏与热力图 ---
|
|
516
628
|
|
|
517
629
|
int findPointInPolygons(double pointLat, double pointLon, const std::vector<std::vector<GeoPoint>>& polygons) {
|
|
@@ -125,6 +125,27 @@ PixelResult latLngToPixel(double lat, double lon, int zoom);
|
|
|
125
125
|
*/
|
|
126
126
|
GeoPoint pixelToLatLng(double x, double y, int zoom);
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* 根据一组坐标点和视口尺寸计算“可同时看到所有点”的推荐缩放级别。
|
|
130
|
+
* 使用 Web Mercator 投影,在跨经线场景下会自动取更小经度跨度。
|
|
131
|
+
*
|
|
132
|
+
* @param points 坐标点集合,至少 1 个
|
|
133
|
+
* @param viewportWidthPx 视口宽度(像素)
|
|
134
|
+
* @param viewportHeightPx 视口高度(像素)
|
|
135
|
+
* @param paddingPx 四周预留的内边距(像素)
|
|
136
|
+
* @param minZoom 最小缩放级别
|
|
137
|
+
* @param maxZoom 最大缩放级别
|
|
138
|
+
* @return 推荐 zoom(已在 [minZoom, maxZoom] 范围内)
|
|
139
|
+
*/
|
|
140
|
+
double calculateFitZoomForPoints(
|
|
141
|
+
const std::vector<GeoPoint>& points,
|
|
142
|
+
double viewportWidthPx,
|
|
143
|
+
double viewportHeightPx,
|
|
144
|
+
double paddingPx,
|
|
145
|
+
int minZoom,
|
|
146
|
+
int maxZoom
|
|
147
|
+
);
|
|
148
|
+
|
|
128
149
|
// --- 批量地理围栏与热力图 ---
|
|
129
150
|
|
|
130
151
|
/**
|
|
@@ -90,14 +90,29 @@ class LocationManager: NSObject, AMapLocationManagerDelegate {
|
|
|
90
90
|
return
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
ensureLocationManager()
|
|
93
|
+
guard let manager = ensureLocationManager() else {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
manager.allowsBackgroundLocationUpdates = allows
|
|
97
|
+
if #available(iOS 11.0, *) {
|
|
98
|
+
let indicatorSelector = NSSelectorFromString("setShowsBackgroundLocationIndicator:")
|
|
99
|
+
if manager.responds(to: indicatorSelector) {
|
|
100
|
+
manager.setValue(allows, forKey: "showsBackgroundLocationIndicator")
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private func setReGeocodeLanguage(_ rawValue: AMapRegionLanguageType.RawValue) {
|
|
106
|
+
if let language = AMapRegionLanguageType(rawValue: rawValue) {
|
|
107
|
+
ensureLocationManager()?.reGeocodeLanguage = language
|
|
108
|
+
}
|
|
94
109
|
}
|
|
95
110
|
|
|
96
111
|
func setGeoLanguage(_ language: Int) {
|
|
97
112
|
switch language {
|
|
98
|
-
case 0:
|
|
99
|
-
case 1:
|
|
100
|
-
case 2:
|
|
113
|
+
case 0: setReGeocodeLanguage(0)
|
|
114
|
+
case 1: setReGeocodeLanguage(0)
|
|
115
|
+
case 2: setReGeocodeLanguage(2)
|
|
101
116
|
default: break
|
|
102
117
|
}
|
|
103
118
|
}
|
|
@@ -112,6 +127,23 @@ class LocationManager: NSObject, AMapLocationManagerDelegate {
|
|
|
112
127
|
ensureLocationManager()?.stopUpdatingHeading()
|
|
113
128
|
}
|
|
114
129
|
|
|
130
|
+
func requestSingleLocation(completion: @escaping (_ location: CLLocation?, _ reGeocode: AMapLocationReGeocode?, _ error: Error?) -> Void) {
|
|
131
|
+
guard let manager = ensureLocationManager() else {
|
|
132
|
+
let error = NSError(
|
|
133
|
+
domain: "ExpoGaodeMap",
|
|
134
|
+
code: -1,
|
|
135
|
+
userInfo: [NSLocalizedDescriptionKey: "定位管理器未初始化,请先完成隐私协议确认"]
|
|
136
|
+
)
|
|
137
|
+
completion(nil, nil, error)
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
manager.requestLocation(
|
|
142
|
+
withReGeocode: manager.locatingWithReGeocode,
|
|
143
|
+
completionBlock: completion
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
115
147
|
// MARK: - 初始化
|
|
116
148
|
|
|
117
149
|
@discardableResult
|
|
@@ -199,7 +231,7 @@ class LocationManager: NSObject, AMapLocationManagerDelegate {
|
|
|
199
231
|
*/
|
|
200
232
|
func coordinateConvert(_ coordinate: [String: Double], type: Int, promise: Promise) {
|
|
201
233
|
guard GaodeMapPrivacyManager.isReady else {
|
|
202
|
-
promise.reject("PRIVACY_NOT_AGREED", "隐私协议未完成确认,请先调用
|
|
234
|
+
promise.reject("PRIVACY_NOT_AGREED", "隐私协议未完成确认,请先调用 setPrivacyConfig")
|
|
203
235
|
return
|
|
204
236
|
}
|
|
205
237
|
|
|
@@ -37,9 +37,9 @@ class MarkerView: ExpoView {
|
|
|
37
37
|
/// 图标高度(用于自定义图标 icon 属性)
|
|
38
38
|
var iconHeight: Double = 40
|
|
39
39
|
/// 自定义视图宽度(用于 children 属性)
|
|
40
|
-
var
|
|
40
|
+
var contentWidth: Double = 0
|
|
41
41
|
/// 自定义视图高度(用于 children 属性)
|
|
42
|
-
var
|
|
42
|
+
var contentHeight: Double = 0
|
|
43
43
|
/// 中心偏移
|
|
44
44
|
var centerOffset: [String: Double]?
|
|
45
45
|
/// 是否显示动画
|
|
@@ -585,9 +585,9 @@ class MarkerView: ExpoView {
|
|
|
585
585
|
return defaultSize
|
|
586
586
|
}
|
|
587
587
|
|
|
588
|
-
if
|
|
589
|
-
let width =
|
|
590
|
-
let height =
|
|
588
|
+
if contentWidth > 0 || contentHeight > 0 {
|
|
589
|
+
let width = contentWidth > 0 ? CGFloat(contentWidth) : defaultSize.width
|
|
590
|
+
let height = contentHeight > 0 ? CGFloat(contentHeight) : defaultSize.height
|
|
591
591
|
return CGSize(width: width, height: height)
|
|
592
592
|
}
|
|
593
593
|
|
|
@@ -1000,17 +1000,17 @@ class MarkerView: ExpoView {
|
|
|
1000
1000
|
}
|
|
1001
1001
|
}
|
|
1002
1002
|
|
|
1003
|
-
func
|
|
1004
|
-
guard
|
|
1005
|
-
|
|
1003
|
+
func setContentWidth(_ width: Double) {
|
|
1004
|
+
guard contentWidth != width else { return }
|
|
1005
|
+
contentWidth = width
|
|
1006
1006
|
if !subviews.isEmpty {
|
|
1007
1007
|
refreshAnnotationAppearance(invalidateChildrenCache: true)
|
|
1008
1008
|
}
|
|
1009
1009
|
}
|
|
1010
1010
|
|
|
1011
|
-
func
|
|
1012
|
-
guard
|
|
1013
|
-
|
|
1011
|
+
func setContentHeight(_ height: Double) {
|
|
1012
|
+
guard contentHeight != height else { return }
|
|
1013
|
+
contentHeight = height
|
|
1014
1014
|
if !subviews.isEmpty {
|
|
1015
1015
|
refreshAnnotationAppearance(invalidateChildrenCache: true)
|
|
1016
1016
|
}
|
|
@@ -41,12 +41,12 @@ public class MarkerViewModule: Module {
|
|
|
41
41
|
view.iconHeight = height
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
Prop("
|
|
45
|
-
view.
|
|
44
|
+
Prop("contentWidth") { (view: MarkerView, width: Double) in
|
|
45
|
+
view.setContentWidth(width)
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
Prop("
|
|
49
|
-
view.
|
|
48
|
+
Prop("contentHeight") { (view: MarkerView, height: Double) in
|
|
49
|
+
view.setContentHeight(height)
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
Prop("centerOffset") { (view: MarkerView, offset: [String: Double]) in
|
|
@@ -71,6 +71,9 @@ class PolylineView: ExpoView {
|
|
|
71
71
|
func setMap(_ map: MAMapView) {
|
|
72
72
|
// 🔑 关键优化:如果是同一个地图引用,跳过重复设置
|
|
73
73
|
if lastSetMapView === map {
|
|
74
|
+
if polyline == nil {
|
|
75
|
+
updatePolyline()
|
|
76
|
+
}
|
|
74
77
|
return
|
|
75
78
|
}
|
|
76
79
|
|
|
@@ -105,10 +108,9 @@ class PolylineView: ExpoView {
|
|
|
105
108
|
// 🔑 至少需要2个点才能绘制折线
|
|
106
109
|
guard coords.count >= 2 else { return }
|
|
107
110
|
|
|
111
|
+
renderer = nil
|
|
108
112
|
polyline = MAPolyline(coordinates: &coords, count: UInt(coords.count))
|
|
109
113
|
mapView.add(polyline!)
|
|
110
|
-
|
|
111
|
-
renderer = nil
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
/**
|
|
@@ -221,18 +223,10 @@ class PolylineView: ExpoView {
|
|
|
221
223
|
|
|
222
224
|
/**
|
|
223
225
|
* 强制重新渲染折线
|
|
224
|
-
*
|
|
226
|
+
* 通过重建 overlay 来触发地图重新请求 renderer
|
|
225
227
|
*/
|
|
226
228
|
private func forceRerender() {
|
|
227
|
-
|
|
228
|
-
return
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// 移除旧的 overlay
|
|
232
|
-
mapView.remove(polyline)
|
|
233
|
-
|
|
234
|
-
// 重新添加(地图会调用 rendererFor overlay)
|
|
235
|
-
mapView.add(polyline)
|
|
229
|
+
updatePolyline()
|
|
236
230
|
}
|
|
237
231
|
|
|
238
232
|
func setDotted(_ dotted: Bool) {
|
|
@@ -55,6 +55,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
55
55
|
+ (NSDictionary * _Nullable)calculatePathBoundsWithLatitudes:(NSArray<NSNumber *> *)latitudes
|
|
56
56
|
longitudes:(NSArray<NSNumber *> *)longitudes NS_SWIFT_NAME(calculatePathBounds(latitudes:longitudes:));
|
|
57
57
|
|
|
58
|
+
+ (double)calculateFitZoomWithLatitudes:(NSArray<NSNumber *> *)latitudes
|
|
59
|
+
longitudes:(NSArray<NSNumber *> *)longitudes
|
|
60
|
+
viewportWidthPx:(double)viewportWidthPx
|
|
61
|
+
viewportHeightPx:(double)viewportHeightPx
|
|
62
|
+
paddingPx:(double)paddingPx
|
|
63
|
+
minZoom:(int)minZoom
|
|
64
|
+
maxZoom:(int)maxZoom NS_SWIFT_NAME(calculateFitZoom(latitudes:longitudes:viewportWidthPx:viewportHeightPx:paddingPx:minZoom:maxZoom:));
|
|
65
|
+
|
|
58
66
|
+ (NSString *)encodeGeoHashWithLat:(double)lat
|
|
59
67
|
lon:(double)lon
|
|
60
68
|
precision:(int)precision NS_SWIFT_NAME(encodeGeoHash(lat:lon:precision:));
|
|
@@ -242,6 +242,33 @@
|
|
|
242
242
|
};
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
+ (double)calculateFitZoomWithLatitudes:(NSArray<NSNumber *> *)latitudes
|
|
246
|
+
longitudes:(NSArray<NSNumber *> *)longitudes
|
|
247
|
+
viewportWidthPx:(double)viewportWidthPx
|
|
248
|
+
viewportHeightPx:(double)viewportHeightPx
|
|
249
|
+
paddingPx:(double)paddingPx
|
|
250
|
+
minZoom:(int)minZoom
|
|
251
|
+
maxZoom:(int)maxZoom {
|
|
252
|
+
if (latitudes.count == 0 || latitudes.count != longitudes.count) {
|
|
253
|
+
return (double)minZoom;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
std::vector<gaodemap::GeoPoint> points;
|
|
257
|
+
points.reserve(latitudes.count);
|
|
258
|
+
for (NSUInteger i = 0; i < latitudes.count; i++) {
|
|
259
|
+
points.push_back({[latitudes[i] doubleValue], [longitudes[i] doubleValue]});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return gaodemap::calculateFitZoomForPoints(
|
|
263
|
+
points,
|
|
264
|
+
viewportWidthPx,
|
|
265
|
+
viewportHeightPx,
|
|
266
|
+
paddingPx,
|
|
267
|
+
minZoom,
|
|
268
|
+
maxZoom
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
245
272
|
+ (NSString *)encodeGeoHashWithLat:(double)lat
|
|
246
273
|
lon:(double)lon
|
|
247
274
|
precision:(int)precision {
|
|
@@ -39,6 +39,39 @@ class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
|
39
39
|
private var locationManager: CLLocationManager?
|
|
40
40
|
/// 权限请求回调
|
|
41
41
|
private var permissionCallback: ((Bool, String) -> Void)?
|
|
42
|
+
/// 后台(始终)权限请求回调
|
|
43
|
+
private var backgroundPermissionCallback: ((Bool, String) -> Void)?
|
|
44
|
+
/// 在 notDetermined 状态下,先申请前台权限,再升级为始终权限
|
|
45
|
+
private var pendingAlwaysAuthorizationUpgrade = false
|
|
46
|
+
|
|
47
|
+
private func resolveBackgroundPermissionIfNeeded(_ status: CLAuthorizationStatus) {
|
|
48
|
+
guard let backgroundPermissionCallback else {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
let backgroundGranted = status == .authorizedAlways
|
|
52
|
+
backgroundPermissionCallback(backgroundGranted, getAuthorizationStatusString(status))
|
|
53
|
+
self.backgroundPermissionCallback = nil
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private func scheduleAlwaysAuthorizationFallback(_ manager: CLLocationManager) {
|
|
57
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { [weak self] in
|
|
58
|
+
guard let self else {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
guard self.backgroundPermissionCallback != nil else {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let latestStatus: CLAuthorizationStatus
|
|
66
|
+
if #available(iOS 14.0, *) {
|
|
67
|
+
latestStatus = manager.authorizationStatus
|
|
68
|
+
} else {
|
|
69
|
+
latestStatus = CLLocationManager.authorizationStatus()
|
|
70
|
+
}
|
|
71
|
+
self.pendingAlwaysAuthorizationUpgrade = false
|
|
72
|
+
self.resolveBackgroundPermissionIfNeeded(latestStatus)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
42
75
|
|
|
43
76
|
/**
|
|
44
77
|
* 请求位置权限
|
|
@@ -48,17 +81,22 @@ class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
|
48
81
|
self.permissionCallback = callback
|
|
49
82
|
|
|
50
83
|
// 确保在主线程操作
|
|
51
|
-
DispatchQueue.main.async {
|
|
52
|
-
guard let self = self else { return }
|
|
53
|
-
|
|
84
|
+
DispatchQueue.main.async {
|
|
54
85
|
if self.locationManager == nil {
|
|
55
86
|
self.locationManager = CLLocationManager()
|
|
56
|
-
self.locationManager?.delegate = self
|
|
57
87
|
}
|
|
88
|
+
|
|
89
|
+
guard let locationManager = self.locationManager else {
|
|
90
|
+
self.permissionCallback?(false, "unknown")
|
|
91
|
+
self.permissionCallback = nil
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
locationManager.delegate = self
|
|
58
96
|
|
|
59
97
|
var currentStatus: CLAuthorizationStatus
|
|
60
98
|
if #available(iOS 14.0, *) {
|
|
61
|
-
currentStatus =
|
|
99
|
+
currentStatus = locationManager.authorizationStatus
|
|
62
100
|
} else {
|
|
63
101
|
currentStatus = CLLocationManager.authorizationStatus()
|
|
64
102
|
}
|
|
@@ -84,7 +122,62 @@ class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
|
84
122
|
return
|
|
85
123
|
}
|
|
86
124
|
|
|
87
|
-
|
|
125
|
+
locationManager.requestWhenInUseAuthorization()
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 请求后台(始终)位置权限
|
|
131
|
+
* @param callback 权限结果回调 (granted, status)
|
|
132
|
+
*/
|
|
133
|
+
func requestAlwaysPermission(callback: @escaping (Bool, String) -> Void) {
|
|
134
|
+
self.backgroundPermissionCallback = callback
|
|
135
|
+
|
|
136
|
+
DispatchQueue.main.async {
|
|
137
|
+
if self.locationManager == nil {
|
|
138
|
+
self.locationManager = CLLocationManager()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
guard let locationManager = self.locationManager else {
|
|
142
|
+
self.backgroundPermissionCallback?(false, "unknown")
|
|
143
|
+
self.backgroundPermissionCallback = nil
|
|
144
|
+
self.pendingAlwaysAuthorizationUpgrade = false
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
locationManager.delegate = self
|
|
149
|
+
|
|
150
|
+
let currentStatus: CLAuthorizationStatus
|
|
151
|
+
if #available(iOS 14.0, *) {
|
|
152
|
+
currentStatus = locationManager.authorizationStatus
|
|
153
|
+
} else {
|
|
154
|
+
currentStatus = CLLocationManager.authorizationStatus()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if currentStatus == .authorizedAlways {
|
|
158
|
+
self.backgroundPermissionCallback?(true, self.getAuthorizationStatusString(currentStatus))
|
|
159
|
+
self.backgroundPermissionCallback = nil
|
|
160
|
+
self.pendingAlwaysAuthorizationUpgrade = false
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if currentStatus == .denied || currentStatus == .restricted {
|
|
165
|
+
self.backgroundPermissionCallback?(false, self.getAuthorizationStatusString(currentStatus))
|
|
166
|
+
self.backgroundPermissionCallback = nil
|
|
167
|
+
self.pendingAlwaysAuthorizationUpgrade = false
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if currentStatus == .authorizedWhenInUse {
|
|
172
|
+
self.pendingAlwaysAuthorizationUpgrade = false
|
|
173
|
+
locationManager.requestAlwaysAuthorization()
|
|
174
|
+
self.scheduleAlwaysAuthorizationFallback(locationManager)
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// notDetermined: 先请求前台定位,再在回调中升级到始终权限
|
|
179
|
+
self.pendingAlwaysAuthorizationUpgrade = true
|
|
180
|
+
locationManager.requestWhenInUseAuthorization()
|
|
88
181
|
}
|
|
89
182
|
}
|
|
90
183
|
|
|
@@ -110,6 +203,18 @@ class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
|
110
203
|
if status == .notDetermined {
|
|
111
204
|
return
|
|
112
205
|
}
|
|
206
|
+
|
|
207
|
+
if pendingAlwaysAuthorizationUpgrade && status == .authorizedWhenInUse {
|
|
208
|
+
pendingAlwaysAuthorizationUpgrade = false
|
|
209
|
+
if let locationManager {
|
|
210
|
+
locationManager.requestAlwaysAuthorization()
|
|
211
|
+
scheduleAlwaysAuthorizationFallback(locationManager)
|
|
212
|
+
} else {
|
|
213
|
+
resolveBackgroundPermissionIfNeeded(status)
|
|
214
|
+
}
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
pendingAlwaysAuthorizationUpgrade = false
|
|
113
218
|
|
|
114
219
|
// 状态已确定(授予或拒绝),返回结果
|
|
115
220
|
var granted = false
|
|
@@ -120,6 +225,8 @@ class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
|
120
225
|
}
|
|
121
226
|
let statusString = getAuthorizationStatusString(status)
|
|
122
227
|
|
|
228
|
+
resolveBackgroundPermissionIfNeeded(status)
|
|
229
|
+
|
|
123
230
|
permissionCallback?(granted, statusString)
|
|
124
231
|
permissionCallback = nil
|
|
125
232
|
}
|
|
@@ -231,6 +338,8 @@ class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
|
231
338
|
locationManager?.delegate = nil
|
|
232
339
|
locationManager = nil
|
|
233
340
|
permissionCallback = nil
|
|
341
|
+
backgroundPermissionCallback = nil
|
|
342
|
+
pendingAlwaysAuthorizationUpgrade = false
|
|
234
343
|
}
|
|
235
344
|
}
|
|
236
345
|
#endif
|