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.
Files changed (238) hide show
  1. package/README.md +296 -7
  2. package/android/build.gradle +12 -4
  3. package/android/src/main/AndroidManifest.xml +10 -1
  4. package/android/src/main/cpp/cluster_jni.cpp +56 -0
  5. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +49 -8
  6. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +83 -15
  7. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +13 -3
  8. package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +36 -39
  9. package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +23 -17
  10. package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterView.kt +5 -2
  11. package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapView.kt +122 -10
  12. package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapViewModule.kt +2 -2
  13. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +37 -25
  14. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +6 -6
  15. package/android/src/main/java/expo/modules/gaodemap/map/search/ExpoGaodeMapSearchModule.kt +751 -0
  16. package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +103 -0
  17. package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviView.kt +1408 -394
  18. package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviViewModule.kt +121 -1
  19. package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNavigationModule.kt +14 -28
  20. package/android/src/main/java/expo/modules/gaodemap/navigation/listeners/IndependentRouteListener.kt +28 -3
  21. package/android/src/main/java/expo/modules/gaodemap/navigation/listeners/RouteCalculateListener.kt +2 -2
  22. package/android/src/main/java/expo/modules/gaodemap/navigation/managers/IndependentRouteManager.kt +114 -15
  23. package/android/src/main/java/expo/modules/gaodemap/navigation/routes/drive/DriveTruckRouteCalculator.kt +24 -35
  24. package/android/src/main/java/expo/modules/gaodemap/navigation/services/IndependentRouteService.kt +50 -36
  25. package/android/src/main/java/expo/modules/gaodemap/navigation/services/NavigationForegroundService.kt +661 -0
  26. package/android/src/main/java/expo/modules/gaodemap/navigation/utils/Converters.kt +21 -12
  27. package/android/src/main/res/drawable/ic_nav_notification_small.xml +10 -0
  28. package/android/src/main/res/drawable/landback_0.png +0 -0
  29. package/android/src/main/res/drawable/landback_1.png +0 -0
  30. package/android/src/main/res/drawable/landback_2.png +0 -0
  31. package/android/src/main/res/drawable/landback_3.png +0 -0
  32. package/android/src/main/res/drawable/landback_4.png +0 -0
  33. package/android/src/main/res/drawable/landback_5.png +0 -0
  34. package/android/src/main/res/drawable/landback_6.png +0 -0
  35. package/android/src/main/res/drawable/landback_7.png +0 -0
  36. package/android/src/main/res/drawable/landback_8.png +0 -0
  37. package/android/src/main/res/drawable/landback_9.png +0 -0
  38. package/android/src/main/res/drawable/landback_a.png +0 -0
  39. package/android/src/main/res/drawable/landback_b.png +0 -0
  40. package/android/src/main/res/drawable/landback_c.png +0 -0
  41. package/android/src/main/res/drawable/landback_d.png +0 -0
  42. package/android/src/main/res/drawable/landback_e.png +0 -0
  43. package/android/src/main/res/drawable/landback_f.png +0 -0
  44. package/android/src/main/res/drawable/landback_g.png +0 -0
  45. package/android/src/main/res/drawable/landback_h.png +0 -0
  46. package/android/src/main/res/drawable/landback_i.png +0 -0
  47. package/android/src/main/res/drawable/landback_j.png +0 -0
  48. package/android/src/main/res/drawable/landback_k.png +0 -0
  49. package/android/src/main/res/drawable/landback_l.png +0 -0
  50. package/android/src/main/res/drawable/landfront_0.png +0 -0
  51. package/android/src/main/res/drawable/landfront_00.png +0 -0
  52. package/android/src/main/res/drawable/landfront_1.png +0 -0
  53. package/android/src/main/res/drawable/landfront_11.png +0 -0
  54. package/android/src/main/res/drawable/landfront_20.png +0 -0
  55. package/android/src/main/res/drawable/landfront_21.png +0 -0
  56. package/android/src/main/res/drawable/landfront_22.png +0 -0
  57. package/android/src/main/res/drawable/landfront_3.png +0 -0
  58. package/android/src/main/res/drawable/landfront_33.png +0 -0
  59. package/android/src/main/res/drawable/landfront_40.png +0 -0
  60. package/android/src/main/res/drawable/landfront_43.png +0 -0
  61. package/android/src/main/res/drawable/landfront_44.png +0 -0
  62. package/android/src/main/res/drawable/landfront_5.png +0 -0
  63. package/android/src/main/res/drawable/landfront_55.png +0 -0
  64. package/android/src/main/res/drawable/landfront_61.png +0 -0
  65. package/android/src/main/res/drawable/landfront_63.png +0 -0
  66. package/android/src/main/res/drawable/landfront_66.png +0 -0
  67. package/android/src/main/res/drawable/landfront_70.png +0 -0
  68. package/android/src/main/res/drawable/landfront_71.png +0 -0
  69. package/android/src/main/res/drawable/landfront_73.png +0 -0
  70. package/android/src/main/res/drawable/landfront_77.png +0 -0
  71. package/android/src/main/res/drawable/landfront_8.png +0 -0
  72. package/android/src/main/res/drawable/landfront_88.png +0 -0
  73. package/android/src/main/res/drawable/landfront_90.png +0 -0
  74. package/android/src/main/res/drawable/landfront_95.png +0 -0
  75. package/android/src/main/res/drawable/landfront_99.png +0 -0
  76. package/android/src/main/res/drawable/landfront_a0.png +0 -0
  77. package/android/src/main/res/drawable/landfront_a8.png +0 -0
  78. package/android/src/main/res/drawable/landfront_aa.png +0 -0
  79. package/android/src/main/res/drawable/landfront_b1.png +0 -0
  80. package/android/src/main/res/drawable/landfront_b5.png +0 -0
  81. package/android/src/main/res/drawable/landfront_bb.png +0 -0
  82. package/android/src/main/res/drawable/landfront_c3.png +0 -0
  83. package/android/src/main/res/drawable/landfront_c8.png +0 -0
  84. package/android/src/main/res/drawable/landfront_cc.png +0 -0
  85. package/android/src/main/res/drawable/landfront_d.png +0 -0
  86. package/android/src/main/res/drawable/landfront_dd.png +0 -0
  87. package/android/src/main/res/drawable/landfront_e1.png +0 -0
  88. package/android/src/main/res/drawable/landfront_e5.png +0 -0
  89. package/android/src/main/res/drawable/landfront_ee.png +0 -0
  90. package/android/src/main/res/drawable/landfront_f0.png +0 -0
  91. package/android/src/main/res/drawable/landfront_f1.png +0 -0
  92. package/android/src/main/res/drawable/landfront_f5.png +0 -0
  93. package/android/src/main/res/drawable/landfront_ff.png +0 -0
  94. package/android/src/main/res/drawable/landfront_g3.png +0 -0
  95. package/android/src/main/res/drawable/landfront_g5.png +0 -0
  96. package/android/src/main/res/drawable/landfront_gg.png +0 -0
  97. package/android/src/main/res/drawable/landfront_h1.png +0 -0
  98. package/android/src/main/res/drawable/landfront_h3.png +0 -0
  99. package/android/src/main/res/drawable/landfront_h5.png +0 -0
  100. package/android/src/main/res/drawable/landfront_hh.png +0 -0
  101. package/android/src/main/res/drawable/landfront_i0.png +0 -0
  102. package/android/src/main/res/drawable/landfront_i3.png +0 -0
  103. package/android/src/main/res/drawable/landfront_i5.png +0 -0
  104. package/android/src/main/res/drawable/landfront_ii.png +0 -0
  105. package/android/src/main/res/drawable/landfront_j1.png +0 -0
  106. package/android/src/main/res/drawable/landfront_j8.png +0 -0
  107. package/android/src/main/res/drawable/landfront_jj.png +0 -0
  108. package/android/src/main/res/drawable/landfront_kk.png +0 -0
  109. package/android/src/main/res/drawable/landfront_ll.png +0 -0
  110. package/android/src/main/res/drawable/nav_notification_brand_icon.xml +16 -0
  111. package/android/src/main/res/drawable/navi_arrow_leftline.png +0 -0
  112. package/android/src/main/res/drawable/navi_lane_shape_bg_center.xml +5 -0
  113. package/android/src/main/res/drawable/navi_lane_shape_bg_left.xml +8 -0
  114. package/android/src/main/res/drawable/navi_lane_shape_bg_over.xml +6 -0
  115. package/android/src/main/res/drawable/navi_lane_shape_bg_right.xml +8 -0
  116. package/android/src/main/res/drawable-nodpi/nav_tracker_car.png +0 -0
  117. package/build/ExpoGaodeMapNaviView.d.ts +16 -0
  118. package/build/ExpoGaodeMapNaviView.d.ts.map +1 -1
  119. package/build/ExpoGaodeMapNaviView.js +74 -1
  120. package/build/ExpoGaodeMapNaviView.js.map +1 -1
  121. package/build/index.d.ts +56 -8
  122. package/build/index.d.ts.map +1 -1
  123. package/build/index.js +452 -10
  124. package/build/index.js.map +1 -1
  125. package/build/map/ExpoGaodeMapModule.d.ts +15 -13
  126. package/build/map/ExpoGaodeMapModule.d.ts.map +1 -1
  127. package/build/map/ExpoGaodeMapModule.js +31 -39
  128. package/build/map/ExpoGaodeMapModule.js.map +1 -1
  129. package/build/map/ExpoGaodeMapOfflineModule.d.ts +5 -0
  130. package/build/map/ExpoGaodeMapOfflineModule.d.ts.map +1 -1
  131. package/build/map/ExpoGaodeMapOfflineModule.js.map +1 -1
  132. package/build/map/ExpoGaodeMapView.d.ts +3 -4
  133. package/build/map/ExpoGaodeMapView.d.ts.map +1 -1
  134. package/build/map/ExpoGaodeMapView.js +28 -25
  135. package/build/map/ExpoGaodeMapView.js.map +1 -1
  136. package/build/map/components/overlays/Circle.d.ts.map +1 -1
  137. package/build/map/components/overlays/Circle.js +1 -30
  138. package/build/map/components/overlays/Circle.js.map +1 -1
  139. package/build/map/components/overlays/Cluster.d.ts.map +1 -1
  140. package/build/map/components/overlays/Cluster.js +1 -42
  141. package/build/map/components/overlays/Cluster.js.map +1 -1
  142. package/build/map/components/overlays/HeatMap.d.ts.map +1 -1
  143. package/build/map/components/overlays/HeatMap.js +21 -21
  144. package/build/map/components/overlays/HeatMap.js.map +1 -1
  145. package/build/map/components/overlays/Marker.d.ts.map +1 -1
  146. package/build/map/components/overlays/Marker.js +76 -80
  147. package/build/map/components/overlays/Marker.js.map +1 -1
  148. package/build/map/components/overlays/Polygon.d.ts.map +1 -1
  149. package/build/map/components/overlays/Polygon.js +1 -25
  150. package/build/map/components/overlays/Polygon.js.map +1 -1
  151. package/build/map/components/overlays/Polyline.d.ts.map +1 -1
  152. package/build/map/components/overlays/Polyline.js +1 -31
  153. package/build/map/components/overlays/Polyline.js.map +1 -1
  154. package/build/map/index.d.ts +9 -2
  155. package/build/map/index.d.ts.map +1 -1
  156. package/build/map/index.js +9 -2
  157. package/build/map/index.js.map +1 -1
  158. package/build/map/search/ExpoGaodeMapSearch.types.d.ts +340 -0
  159. package/build/map/search/ExpoGaodeMapSearch.types.d.ts.map +1 -0
  160. package/build/map/search/ExpoGaodeMapSearch.types.js +19 -0
  161. package/build/map/search/ExpoGaodeMapSearch.types.js.map +1 -0
  162. package/build/map/search/ExpoGaodeMapSearchModule.d.ts +74 -0
  163. package/build/map/search/ExpoGaodeMapSearchModule.d.ts.map +1 -0
  164. package/build/map/search/ExpoGaodeMapSearchModule.js +47 -0
  165. package/build/map/search/ExpoGaodeMapSearchModule.js.map +1 -0
  166. package/build/map/search/index.d.ts +156 -0
  167. package/build/map/search/index.d.ts.map +1 -0
  168. package/build/map/search/index.js +171 -0
  169. package/build/map/search/index.js.map +1 -0
  170. package/build/map/types/index.d.ts +2 -2
  171. package/build/map/types/index.d.ts.map +1 -1
  172. package/build/map/types/index.js.map +1 -1
  173. package/build/map/types/map-view.types.d.ts +4 -2
  174. package/build/map/types/map-view.types.d.ts.map +1 -1
  175. package/build/map/types/map-view.types.js.map +1 -1
  176. package/build/map/types/native-module.types.d.ts +11 -12
  177. package/build/map/types/native-module.types.d.ts.map +1 -1
  178. package/build/map/types/native-module.types.js.map +1 -1
  179. package/build/map/types/overlays.types.d.ts +9 -14
  180. package/build/map/types/overlays.types.d.ts.map +1 -1
  181. package/build/map/types/overlays.types.js.map +1 -1
  182. package/build/map/types/route-playback.types.d.ts +16 -0
  183. package/build/map/types/route-playback.types.d.ts.map +1 -1
  184. package/build/map/types/route-playback.types.js.map +1 -1
  185. package/build/map/utils/ErrorHandler.js +11 -11
  186. package/build/map/utils/ErrorHandler.js.map +1 -1
  187. package/build/map/utils/OfflineMapManager.d.ts +4 -0
  188. package/build/map/utils/OfflineMapManager.d.ts.map +1 -1
  189. package/build/map/utils/OfflineMapManager.js +6 -0
  190. package/build/map/utils/OfflineMapManager.js.map +1 -1
  191. package/build/types/coordinates.types.d.ts +3 -0
  192. package/build/types/coordinates.types.d.ts.map +1 -1
  193. package/build/types/coordinates.types.js.map +1 -1
  194. package/build/types/independent.types.d.ts +111 -12
  195. package/build/types/independent.types.d.ts.map +1 -1
  196. package/build/types/independent.types.js.map +1 -1
  197. package/build/types/native-module.types.d.ts +1 -1
  198. package/build/types/native-module.types.js.map +1 -1
  199. package/build/types/naviview.types.d.ts +304 -14
  200. package/build/types/naviview.types.d.ts.map +1 -1
  201. package/build/types/naviview.types.js.map +1 -1
  202. package/build/types/route.types.d.ts +12 -2
  203. package/build/types/route.types.d.ts.map +1 -1
  204. package/build/types/route.types.js.map +1 -1
  205. package/expo-module.config.json +4 -2
  206. package/ios/ExpoGaodeMapNaviView.swift +2331 -201
  207. package/ios/ExpoGaodeMapNaviViewModule.swift +109 -1
  208. package/ios/ExpoGaodeMapNavigation.podspec +2 -1
  209. package/ios/ExpoGaodeMapNavigationModule.swift +7 -5
  210. package/ios/managers/IndependentRouteManager.swift +90 -26
  211. package/ios/map/ExpoGaodeMapModule.swift +72 -21
  212. package/ios/map/ExpoGaodeMapOfflineModule.swift +61 -0
  213. package/ios/map/ExpoGaodeMapSearchModule.swift +773 -0
  214. package/ios/map/ExpoGaodeMapView.swift +23 -5
  215. package/ios/map/GaodeMapPrivacyManager.swift +26 -18
  216. package/ios/map/cpp/GeometryEngine.cpp +112 -0
  217. package/ios/map/cpp/GeometryEngine.hpp +21 -0
  218. package/ios/map/modules/LocationManager.swift +37 -5
  219. package/ios/map/overlays/MarkerView.swift +11 -11
  220. package/ios/map/overlays/MarkerViewModule.swift +4 -4
  221. package/ios/map/overlays/PolylineView.swift +6 -12
  222. package/ios/map/utils/ClusterNative.h +8 -0
  223. package/ios/map/utils/ClusterNative.mm +27 -0
  224. package/ios/map/utils/PermissionManager.swift +115 -6
  225. package/ios/routes/drive/DriveTruckRouteCalculator.swift +165 -77
  226. package/ios/routes/walkride/WalkRideRouteCalculator.swift +127 -1
  227. package/ios/services/IndependentRouteService.swift +198 -39
  228. package/ios/services/NavigationLiveActivityAttributes.swift +48 -0
  229. package/ios/services/NavigationLiveActivityManager.swift +359 -0
  230. package/package.json +22 -7
  231. package/plugin/build/withGaodeMap.d.ts +8 -0
  232. package/plugin/build/withGaodeMap.js +60 -4
  233. package/scripts/check-expo-modules.js +68 -0
  234. package/shared/cpp/GeometryEngine.cpp +112 -0
  235. package/shared/cpp/GeometryEngine.hpp +21 -0
  236. package/widget-template/README.md +46 -0
  237. package/widget-template/ios/NavigationLiveActivityWidget.swift +367 -0
  238. package/android/src/main/java/expo/modules/gaodemap/navigation/managers/RouteCalculator.kt +0 -173
@@ -0,0 +1,359 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ #if canImport(ActivityKit)
5
+ import ActivityKit
6
+ #endif
7
+
8
+ struct NavigationLiveActivitySnapshot {
9
+ let appName: String
10
+ let currentRoadName: String
11
+ let nextRoadName: String
12
+ let pathRetainDistance: Int
13
+ let routeTotalDistance: Int
14
+ let pathRetainTime: Int
15
+ let curStepRetainDistance: Int
16
+ let iconType: Int
17
+ let turnIconBase64: String?
18
+ }
19
+
20
+ final class NavigationLiveActivityManager {
21
+ static let shared = NavigationLiveActivityManager()
22
+
23
+ private var currentActivityId: String?
24
+ private var hasLoggedMissingPlistSupport = false
25
+ private var hasLoggedSystemDisabled = false
26
+ private var lastSnapshot: NavigationLiveActivitySnapshot?
27
+ private var autoStopWorkItem: DispatchWorkItem?
28
+ private var delayedStopWorkItem: DispatchWorkItem?
29
+ private let staleTimeoutSeconds: TimeInterval = 45
30
+ private let maxEstimatedContentStateBytes = 3900
31
+ private let maxRoadNameCharacters = 22
32
+ private let maxAppNameCharacters = 18
33
+
34
+ private init() {}
35
+
36
+ func startOrUpdate(snapshot: NavigationLiveActivitySnapshot) {
37
+ #if canImport(ActivityKit)
38
+ guard #available(iOS 16.1, *) else {
39
+ return
40
+ }
41
+
42
+ guard supportsLiveActivityByPlist() else {
43
+ return
44
+ }
45
+
46
+ guard ActivityAuthorizationInfo().areActivitiesEnabled else {
47
+ if !hasLoggedSystemDisabled {
48
+ hasLoggedSystemDisabled = true
49
+ NSLog("[ExpoGaodeMapNaviView][LiveActivity] Activities are disabled by system settings")
50
+ }
51
+ return
52
+ }
53
+ hasLoggedSystemDisabled = false
54
+
55
+ let normalizedSnapshot = NavigationLiveActivitySnapshot(
56
+ appName: snapshot.appName,
57
+ currentRoadName: snapshot.currentRoadName,
58
+ nextRoadName: snapshot.nextRoadName,
59
+ pathRetainDistance: max(snapshot.pathRetainDistance, 0),
60
+ routeTotalDistance: max(snapshot.routeTotalDistance, 0),
61
+ pathRetainTime: max(snapshot.pathRetainTime, 0),
62
+ curStepRetainDistance: max(snapshot.curStepRetainDistance, 0),
63
+ iconType: snapshot.iconType,
64
+ turnIconBase64: snapshot.turnIconBase64
65
+ )
66
+
67
+ let payloadSafeSnapshot = snapshotForPayloadBudget(from: normalizedSnapshot)
68
+ let contentState = makeContentState(from: payloadSafeSnapshot)
69
+ let attributes = NavigationLiveActivityAttributes(appName: payloadSafeSnapshot.appName)
70
+
71
+ if let activity = resolveCurrentActivity() {
72
+ lastSnapshot = payloadSafeSnapshot
73
+ Task {
74
+ if #available(iOS 16.2, *) {
75
+ let content = ActivityContent(state: contentState, staleDate: nil)
76
+ await activity.update(content)
77
+ } else {
78
+ await activity.update(using: contentState)
79
+ }
80
+ }
81
+ scheduleAutoStop()
82
+ return
83
+ }
84
+
85
+ do {
86
+ let activity: Activity<NavigationLiveActivityAttributes>
87
+ if #available(iOS 16.2, *) {
88
+ let content = ActivityContent(state: contentState, staleDate: nil)
89
+ activity = try Activity.request(attributes: attributes, content: content, pushType: nil)
90
+ } else {
91
+ activity = try Activity.request(attributes: attributes, contentState: contentState, pushType: nil)
92
+ }
93
+ currentActivityId = activity.id
94
+ lastSnapshot = payloadSafeSnapshot
95
+ scheduleAutoStop()
96
+ } catch {
97
+ NSLog("[ExpoGaodeMapNaviView][LiveActivity] request failed: \(error.localizedDescription)")
98
+ }
99
+ #endif
100
+ }
101
+
102
+ func stop() {
103
+ #if canImport(ActivityKit)
104
+ cancelAutoStop()
105
+ cancelDelayedStop()
106
+
107
+ guard #available(iOS 16.1, *) else {
108
+ return
109
+ }
110
+
111
+ guard let activity = resolveCurrentActivity() else {
112
+ currentActivityId = nil
113
+ return
114
+ }
115
+
116
+ let fallbackSnapshot = lastSnapshot ?? NavigationLiveActivitySnapshot(
117
+ appName: "",
118
+ currentRoadName: "",
119
+ nextRoadName: "",
120
+ pathRetainDistance: 0,
121
+ routeTotalDistance: 0,
122
+ pathRetainTime: 0,
123
+ curStepRetainDistance: 0,
124
+ iconType: 0,
125
+ turnIconBase64: nil
126
+ )
127
+ let fallback = NavigationLiveActivityAttributes.ContentState(
128
+ currentRoadName: fallbackSnapshot.currentRoadName,
129
+ nextRoadName: fallbackSnapshot.nextRoadName,
130
+ pathRetainDistance: fallbackSnapshot.pathRetainDistance,
131
+ routeTotalDistance: fallbackSnapshot.routeTotalDistance,
132
+ pathRetainTime: fallbackSnapshot.pathRetainTime,
133
+ curStepRetainDistance: fallbackSnapshot.curStepRetainDistance,
134
+ iconType: fallbackSnapshot.iconType,
135
+ turnIconBase64: fallbackSnapshot.turnIconBase64,
136
+ updatedAt: Date()
137
+ )
138
+
139
+ Task {
140
+ if #available(iOS 16.2, *) {
141
+ let content = ActivityContent(state: fallback, staleDate: nil)
142
+ await activity.end(content, dismissalPolicy: .immediate)
143
+ } else {
144
+ await activity.end(using: fallback, dismissalPolicy: .immediate)
145
+ }
146
+ }
147
+
148
+ currentActivityId = nil
149
+ lastSnapshot = nil
150
+ #endif
151
+ }
152
+
153
+ func showArrivedAndStop(snapshot: NavigationLiveActivitySnapshot, dismissAfter seconds: TimeInterval = 6) {
154
+ #if canImport(ActivityKit)
155
+ guard #available(iOS 16.1, *) else {
156
+ return
157
+ }
158
+
159
+ guard let activity = resolveCurrentActivity() else {
160
+ return
161
+ }
162
+
163
+ cancelAutoStop()
164
+ cancelDelayedStop()
165
+
166
+ let normalized = NavigationLiveActivitySnapshot(
167
+ appName: snapshot.appName,
168
+ currentRoadName: "",
169
+ nextRoadName: "",
170
+ pathRetainDistance: 0,
171
+ routeTotalDistance: max(snapshot.routeTotalDistance, 0),
172
+ pathRetainTime: 0,
173
+ curStepRetainDistance: 0,
174
+ iconType: 15,
175
+ turnIconBase64: nil
176
+ )
177
+ let payloadSafeSnapshot = snapshotForPayloadBudget(from: normalized)
178
+ let contentState = makeContentState(from: payloadSafeSnapshot)
179
+ lastSnapshot = payloadSafeSnapshot
180
+
181
+ Task {
182
+ if #available(iOS 16.2, *) {
183
+ let content = ActivityContent(state: contentState, staleDate: nil)
184
+ await activity.update(content)
185
+ } else {
186
+ await activity.update(using: contentState)
187
+ }
188
+ }
189
+
190
+ let delay = max(seconds, 1)
191
+ let workItem = DispatchWorkItem { [weak self] in
192
+ guard let self else {
193
+ return
194
+ }
195
+ NSLog(
196
+ "[ExpoGaodeMapNaviView][LiveActivity] arrived destination card displayed for %.0fs, stopping activity",
197
+ delay
198
+ )
199
+ self.stop()
200
+ }
201
+ delayedStopWorkItem = workItem
202
+ DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: workItem)
203
+ #endif
204
+ }
205
+
206
+ private func scheduleAutoStop() {
207
+ cancelAutoStop()
208
+ let workItem = DispatchWorkItem { [weak self] in
209
+ guard let self else {
210
+ return
211
+ }
212
+ NSLog(
213
+ "[ExpoGaodeMapNaviView][LiveActivity] no updates for %.0fs, auto stopping activity",
214
+ self.staleTimeoutSeconds
215
+ )
216
+ self.stop()
217
+ }
218
+ autoStopWorkItem = workItem
219
+ DispatchQueue.main.asyncAfter(deadline: .now() + staleTimeoutSeconds, execute: workItem)
220
+ }
221
+
222
+ private func cancelAutoStop() {
223
+ autoStopWorkItem?.cancel()
224
+ autoStopWorkItem = nil
225
+ }
226
+
227
+ private func cancelDelayedStop() {
228
+ delayedStopWorkItem?.cancel()
229
+ delayedStopWorkItem = nil
230
+ }
231
+
232
+ private func supportsLiveActivityByPlist() -> Bool {
233
+ let enabled = (Bundle.main.object(forInfoDictionaryKey: "NSSupportsLiveActivities") as? Bool) == true
234
+ if !enabled && !hasLoggedMissingPlistSupport {
235
+ hasLoggedMissingPlistSupport = true
236
+ NSLog("[ExpoGaodeMapNaviView][LiveActivity] NSSupportsLiveActivities is not enabled in Info.plist")
237
+ }
238
+ return enabled
239
+ }
240
+
241
+ #if canImport(ActivityKit)
242
+ @available(iOS 16.1, *)
243
+ private func makeContentState(from snapshot: NavigationLiveActivitySnapshot) -> NavigationLiveActivityAttributes.ContentState {
244
+ NavigationLiveActivityAttributes.ContentState(
245
+ currentRoadName: snapshot.currentRoadName,
246
+ nextRoadName: snapshot.nextRoadName,
247
+ pathRetainDistance: snapshot.pathRetainDistance,
248
+ routeTotalDistance: snapshot.routeTotalDistance,
249
+ pathRetainTime: snapshot.pathRetainTime,
250
+ curStepRetainDistance: snapshot.curStepRetainDistance,
251
+ iconType: snapshot.iconType,
252
+ turnIconBase64: snapshot.turnIconBase64,
253
+ updatedAt: Date()
254
+ )
255
+ }
256
+
257
+ @available(iOS 16.1, *)
258
+ private func estimateContentStateSizeBytes(snapshot: NavigationLiveActivitySnapshot) -> Int {
259
+ let state = makeContentState(from: snapshot)
260
+ guard let encoded = try? JSONEncoder().encode(state) else {
261
+ return Int.max
262
+ }
263
+ return encoded.count
264
+ }
265
+
266
+ private func truncateCharacters(_ value: String, maxCharacters: Int) -> String {
267
+ guard maxCharacters > 0, value.count > maxCharacters else {
268
+ return value
269
+ }
270
+ return String(value.prefix(maxCharacters))
271
+ }
272
+
273
+ @available(iOS 16.1, *)
274
+ private func snapshotForPayloadBudget(from snapshot: NavigationLiveActivitySnapshot) -> NavigationLiveActivitySnapshot {
275
+ let originalEstimated = estimateContentStateSizeBytes(snapshot: snapshot)
276
+ var candidate = snapshot
277
+ var estimated = originalEstimated
278
+ if estimated <= maxEstimatedContentStateBytes {
279
+ return candidate
280
+ }
281
+
282
+ // Keep turn icon as long as possible; trim text first.
283
+ candidate = NavigationLiveActivitySnapshot(
284
+ appName: truncateCharacters(candidate.appName, maxCharacters: maxAppNameCharacters),
285
+ currentRoadName: truncateCharacters(candidate.currentRoadName, maxCharacters: maxRoadNameCharacters),
286
+ nextRoadName: truncateCharacters(candidate.nextRoadName, maxCharacters: maxRoadNameCharacters),
287
+ pathRetainDistance: candidate.pathRetainDistance,
288
+ routeTotalDistance: candidate.routeTotalDistance,
289
+ pathRetainTime: candidate.pathRetainTime,
290
+ curStepRetainDistance: candidate.curStepRetainDistance,
291
+ iconType: candidate.iconType,
292
+ turnIconBase64: candidate.turnIconBase64
293
+ )
294
+ estimated = estimateContentStateSizeBytes(snapshot: candidate)
295
+ if estimated <= maxEstimatedContentStateBytes {
296
+ NSLog(
297
+ "[ExpoGaodeMapNaviView][LiveActivity] payload %dB exceeds budget, trimmed text to %dB while keeping turn icon",
298
+ originalEstimated,
299
+ estimated
300
+ )
301
+ return candidate
302
+ }
303
+
304
+ let strippedText = NavigationLiveActivitySnapshot(
305
+ appName: truncateCharacters(candidate.appName, maxCharacters: maxAppNameCharacters),
306
+ currentRoadName: "",
307
+ nextRoadName: "",
308
+ pathRetainDistance: candidate.pathRetainDistance,
309
+ routeTotalDistance: candidate.routeTotalDistance,
310
+ pathRetainTime: candidate.pathRetainTime,
311
+ curStepRetainDistance: candidate.curStepRetainDistance,
312
+ iconType: candidate.iconType,
313
+ turnIconBase64: candidate.turnIconBase64
314
+ )
315
+ let strippedTextEstimated = estimateContentStateSizeBytes(snapshot: strippedText)
316
+ if strippedTextEstimated <= maxEstimatedContentStateBytes {
317
+ NSLog(
318
+ "[ExpoGaodeMapNaviView][LiveActivity] payload %dB exceeds budget, cleared road text to %dB while keeping turn icon",
319
+ originalEstimated,
320
+ strippedTextEstimated
321
+ )
322
+ return strippedText
323
+ }
324
+
325
+ let droppedIcon = NavigationLiveActivitySnapshot(
326
+ appName: strippedText.appName,
327
+ currentRoadName: strippedText.currentRoadName,
328
+ nextRoadName: strippedText.nextRoadName,
329
+ pathRetainDistance: strippedText.pathRetainDistance,
330
+ routeTotalDistance: strippedText.routeTotalDistance,
331
+ pathRetainTime: strippedText.pathRetainTime,
332
+ curStepRetainDistance: strippedText.curStepRetainDistance,
333
+ iconType: strippedText.iconType,
334
+ turnIconBase64: nil
335
+ )
336
+ let droppedIconEstimated = estimateContentStateSizeBytes(snapshot: droppedIcon)
337
+ NSLog(
338
+ "[ExpoGaodeMapNaviView][LiveActivity] payload still too large after text trim (%dB), dropped turn icon to %dB",
339
+ strippedTextEstimated,
340
+ droppedIconEstimated
341
+ )
342
+ return droppedIcon
343
+ }
344
+ #endif
345
+
346
+ #if canImport(ActivityKit)
347
+ @available(iOS 16.1, *)
348
+ private func resolveCurrentActivity() -> Activity<NavigationLiveActivityAttributes>? {
349
+ if let id = currentActivityId,
350
+ let matched = Activity<NavigationLiveActivityAttributes>.activities.first(where: { $0.id == id }) {
351
+ return matched
352
+ }
353
+
354
+ let first = Activity<NavigationLiveActivityAttributes>.activities.first
355
+ currentActivityId = first?.id
356
+ return first
357
+ }
358
+ #endif
359
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expo-gaode-map-navigation",
3
- "version": "2.0.12-next.0",
4
- "description": "高德地图导航功能模块 - 路径规划、导航引导,独立版本包含完整地图功能",
3
+ "version": "2.0.13",
4
+ "description": "React Native AMap (Gaode Map) navigation module for Expo: route planning and turn-by-turn navigation, includes full map features.",
5
5
  "author": "TomWq <582752848@qq.com> (https://github.com/TomWq)",
6
6
  "repository": {
7
7
  "type": "git",
@@ -17,22 +17,26 @@
17
17
  "lint": "expo-module lint",
18
18
  "test": "expo-module test",
19
19
  "prepare": "expo-module prepare && yarn build:plugin",
20
+ "postinstall": "node scripts/check-expo-modules.js",
20
21
  "prepublishOnly": "echo 'Skipping proofread check' && exit 0",
21
22
  "expo-module": "expo-module",
22
23
  "android": "expo run:android",
23
24
  "ios": "expo run:ios"
24
25
  },
25
26
  "devDependencies": {
27
+ "@expo/vector-icons": "^15.0.3",
26
28
  "@types/react": "~19.1.0",
27
- "expo": "^54.0.31",
28
- "expo-module-scripts": "^5.0.8",
29
- "react-native": "0.81.5",
29
+ "expo": "^55.0.8",
30
+ "expo-module-scripts": "^55.0.2",
31
+ "react-native": "0.83.2",
30
32
  "typescript": "^5.9.3"
31
33
  },
32
34
  "peerDependencies": {
35
+ "@expo/vector-icons": "*",
33
36
  "expo": "*",
34
37
  "react": "*",
35
- "react-native": "*"
38
+ "react-native": "*",
39
+ "react-native-safe-area-context": "*"
36
40
  },
37
41
  "peerDependenciesMeta": {
38
42
  "expo": {
@@ -46,7 +50,9 @@
46
50
  "build",
47
51
  "android",
48
52
  "ios",
53
+ "widget-template",
49
54
  "shared",
55
+ "scripts",
50
56
  "app.plugin.js",
51
57
  "expo-module.config.json",
52
58
  "plugin/build"
@@ -56,11 +62,20 @@
56
62
  "react-native",
57
63
  "expo",
58
64
  "expo-gaode-map",
65
+ "expo-gaode-map-navigation",
66
+ "expo-amap",
67
+ "react-native-amap",
68
+ "react-native-gaode-map",
69
+ "rn-amap-navigation",
59
70
  "amap",
71
+ "gaode-map",
60
72
  "navigation",
73
+ "route-planning",
74
+ "turn-by-turn",
61
75
  "route",
62
76
  "高德地图",
63
- "导航"
77
+ "导航",
78
+ "路径规划"
64
79
  ],
65
80
  "license": "MIT"
66
81
  }
@@ -13,6 +13,14 @@ export type GaodeMapPluginProps = {
13
13
  locationDescription?: string;
14
14
  /** 是否启用后台定位(Android & iOS) */
15
15
  enableBackgroundLocation?: boolean;
16
+ /** iOS 是否启用后台音频(用于后台导航语音播报) */
17
+ enableBackgroundAudio?: boolean;
18
+ /** 是否启用后台导航常驻通知(Android) */
19
+ enableNavigationNotification?: boolean;
20
+ /** 是否启用 iOS Live Activity(锁屏/灵动岛) */
21
+ enableIOSLiveActivity?: boolean;
22
+ /** 是否启用 iOS Live Activity 高频更新能力 */
23
+ enableIOSLiveActivityFrequentUpdates?: boolean;
16
24
  /**
17
25
  * 自定义高德地图 SDK 路径(例如 Google Play 版本)
18
26
  * 相对于项目根目录的路径,例如 "./libs/AMap_3DMap_V9.0.0_GooglePlay.aar"
@@ -27,8 +27,19 @@ const withGaodeMapInfoPlist = (config, props) => {
27
27
  if (!config.modResults.UIBackgroundModes.includes('location')) {
28
28
  config.modResults.UIBackgroundModes.push('location');
29
29
  }
30
+ const shouldEnableBackgroundAudio = props.enableBackgroundAudio ?? true;
31
+ if (shouldEnableBackgroundAudio && !config.modResults.UIBackgroundModes.includes('audio')) {
32
+ config.modResults.UIBackgroundModes.push('audio');
33
+ }
30
34
  }
31
35
  }
36
+ if (props.enableIOSLiveActivity !== undefined) {
37
+ config.modResults.NSSupportsLiveActivities = !!props.enableIOSLiveActivity;
38
+ }
39
+ if (props.enableIOSLiveActivityFrequentUpdates !== undefined) {
40
+ config.modResults.NSSupportsLiveActivitiesFrequentUpdates =
41
+ !!props.enableIOSLiveActivityFrequentUpdates;
42
+ }
32
43
  return config;
33
44
  });
34
45
  };
@@ -54,6 +65,7 @@ const withGaodeMapAndroidManifest = (config, props) => {
54
65
  'android.permission.ACCESS_NETWORK_STATE',
55
66
  'android.permission.ACCESS_WIFI_STATE',
56
67
  'android.permission.READ_PHONE_STATE',
68
+ 'android.permission.WAKE_LOCK',
57
69
  'android.permission.BLUETOOTH',
58
70
  'android.permission.BLUETOOTH_ADMIN',
59
71
  ];
@@ -68,8 +80,9 @@ const withGaodeMapAndroidManifest = (config, props) => {
68
80
  });
69
81
  }
70
82
  });
71
- // 后台定位权限(可选,仅在 enableBackgroundLocation true 时添加)
72
- if (props.enableBackgroundLocation) {
83
+ const requiresForegroundLocationService = props.enableBackgroundLocation || props.enableNavigationNotification;
84
+ // 后台定位/导航前台服务权限(可选)
85
+ if (requiresForegroundLocationService) {
73
86
  const backgroundPermissions = [
74
87
  'android.permission.ACCESS_BACKGROUND_LOCATION',
75
88
  'android.permission.FOREGROUND_SERVICE',
@@ -87,6 +100,21 @@ const withGaodeMapAndroidManifest = (config, props) => {
87
100
  }
88
101
  });
89
102
  }
103
+ // Android 13+ 通知权限(仅在启用导航常驻通知时添加)
104
+ if (props.enableNavigationNotification) {
105
+ const notificationPermissions = [
106
+ 'android.permission.POST_NOTIFICATIONS',
107
+ 'android.permission.POST_PROMOTED_NOTIFICATIONS',
108
+ ];
109
+ notificationPermissions.forEach((permission) => {
110
+ const hasPermission = androidManifest['uses-permission']?.some((item) => item.$?.['android:name'] === permission);
111
+ if (!hasPermission) {
112
+ androidManifest['uses-permission']?.push({
113
+ $: { 'android:name': permission },
114
+ });
115
+ }
116
+ });
117
+ }
90
118
  // 添加前台服务(如果启用后台定位)
91
119
  const mainApplication = androidManifest.application?.[0];
92
120
  if (mainApplication && props.enableBackgroundLocation) {
@@ -94,11 +122,27 @@ const withGaodeMapAndroidManifest = (config, props) => {
94
122
  mainApplication['service'] = [];
95
123
  }
96
124
  // 检查是否已存在 LocationForegroundService
97
- const hasService = mainApplication['service'].some((item) => item.$?.['android:name'] === 'expo.modules.gaodemap.services.LocationForegroundService');
125
+ const hasService = mainApplication['service'].some((item) => item.$?.['android:name'] === 'expo.modules.gaodemap.map.services.LocationForegroundService');
98
126
  if (!hasService) {
99
127
  mainApplication['service'].push({
100
128
  $: {
101
- 'android:name': 'expo.modules.gaodemap.services.LocationForegroundService',
129
+ 'android:name': 'expo.modules.gaodemap.map.services.LocationForegroundService',
130
+ 'android:enabled': 'true',
131
+ 'android:exported': 'false',
132
+ 'android:foregroundServiceType': 'location',
133
+ },
134
+ });
135
+ }
136
+ }
137
+ if (mainApplication && props.enableNavigationNotification) {
138
+ if (!mainApplication['service']) {
139
+ mainApplication['service'] = [];
140
+ }
141
+ const hasNavigationService = mainApplication['service'].some((item) => item.$?.['android:name'] === 'expo.modules.gaodemap.navigation.services.NavigationForegroundService');
142
+ if (!hasNavigationService) {
143
+ mainApplication['service'].push({
144
+ $: {
145
+ 'android:name': 'expo.modules.gaodemap.navigation.services.NavigationForegroundService',
102
146
  'android:enabled': 'true',
103
147
  'android:exported': 'false',
104
148
  'android:foregroundServiceType': 'location',
@@ -192,6 +236,18 @@ configurations.all {
192
236
  */
193
237
  const withGaodeMapAndroidSdk = (config, props) => {
194
238
  return (0, config_plugins_1.withGradleProperties)(config, (config) => {
239
+ const jetifierKey = 'android.enableJetifier';
240
+ const jetifier = config.modResults.find((item) => item.type === 'property' && item.key === jetifierKey);
241
+ if (jetifier && jetifier.type === 'property') {
242
+ jetifier.value = 'true';
243
+ }
244
+ else {
245
+ config.modResults.push({
246
+ type: 'property',
247
+ key: jetifierKey,
248
+ value: 'true',
249
+ });
250
+ }
195
251
  if (props.customMapSdkPath) {
196
252
  const key = 'EXPO_GAODE_MAP_CUSTOM_SDK_PATH';
197
253
  const existing = config.modResults.find((item) => item.type === 'property' && item.key === key);
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ function resolveProjectRoot() {
7
+ return process.env.INIT_CWD || process.env.npm_config_local_prefix || process.cwd();
8
+ }
9
+
10
+ function hasPackage(pkgName, projectRoot) {
11
+ try {
12
+ require.resolve(`${pkgName}/package.json`, { paths: [projectRoot] });
13
+ return true;
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
18
+
19
+ function printWarning(lines) {
20
+ const prefix = '[expo-gaode-map-navigation]';
21
+ console.warn(`${prefix} WARNING: Expo Modules infrastructure was not detected in this React Native project.`);
22
+ for (const line of lines) {
23
+ console.warn(`${prefix} ${line}`);
24
+ }
25
+ }
26
+
27
+ function main() {
28
+ const projectRoot = resolveProjectRoot();
29
+
30
+ // Skip non-React Native consumers.
31
+ if (!hasPackage('react-native', projectRoot)) {
32
+ return;
33
+ }
34
+
35
+ const hasExpo = hasPackage('expo', projectRoot);
36
+ const hasExpoModulesCore = hasPackage('expo-modules-core', projectRoot);
37
+ const podfilePath = path.join(projectRoot, 'ios', 'Podfile');
38
+ const hasPodfile = fs.existsSync(podfilePath);
39
+ const hasUseExpoModules = hasPodfile
40
+ ? /\buse_expo_modules!\b/m.test(fs.readFileSync(podfilePath, 'utf8'))
41
+ : true;
42
+
43
+ if (hasExpo && hasExpoModulesCore && hasUseExpoModules) {
44
+ return;
45
+ }
46
+
47
+ const details = [];
48
+ if (!hasExpo) {
49
+ details.push('Missing dependency: expo');
50
+ }
51
+ if (!hasExpoModulesCore) {
52
+ details.push('Missing dependency: expo-modules-core');
53
+ }
54
+ if (!hasUseExpoModules) {
55
+ details.push('iOS Podfile does not contain use_expo_modules!');
56
+ }
57
+
58
+ printWarning([
59
+ ...details,
60
+ 'This package requires Expo Modules infrastructure even in bare React Native apps.',
61
+ 'Suggested fix:',
62
+ ' npx install-expo-modules@latest',
63
+ hasPodfile ? ' (then) cd ios && pod install' : null,
64
+ 'Docs: https://docs.expo.dev/bare/installing-expo-modules/',
65
+ ].filter(Boolean));
66
+ }
67
+
68
+ main();