expo-gaode-map-navigation 2.0.6 → 2.0.8-next.0

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 (28) hide show
  1. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +71 -45
  2. package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +35 -0
  3. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +0 -1
  4. package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviView.kt +13 -2
  5. package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNavigationModule.kt +385 -1
  6. package/android/src/main/java/expo/modules/gaodemap/navigation/routes/drive/DriveTruckRouteCalculator.kt +180 -4
  7. package/android/src/main/java/expo/modules/gaodemap/navigation/services/IndependentRouteService.kt +182 -7
  8. package/android/src/main/java/expo/modules/gaodemap/navigation/utils/Converters.kt +31 -1
  9. package/build/index.d.ts +7 -2
  10. package/build/index.d.ts.map +1 -1
  11. package/build/index.js +5 -0
  12. package/build/index.js.map +1 -1
  13. package/build/types/independent.types.d.ts +7 -0
  14. package/build/types/independent.types.d.ts.map +1 -1
  15. package/build/types/independent.types.js.map +1 -1
  16. package/build/types/native-module.types.d.ts +5 -1
  17. package/build/types/native-module.types.d.ts.map +1 -1
  18. package/build/types/native-module.types.js.map +1 -1
  19. package/build/types/route.types.d.ts +108 -0
  20. package/build/types/route.types.d.ts.map +1 -1
  21. package/build/types/route.types.js.map +1 -1
  22. package/ios/ExpoGaodeMapNaviView.swift +25 -4
  23. package/ios/ExpoGaodeMapNavigationModule.swift +504 -12
  24. package/ios/map/ExpoGaodeMapOfflineModule.swift +58 -34
  25. package/ios/map/GaodeMapPrivacyManager.swift +2 -0
  26. package/ios/map/overlays/MarkerView.swift +149 -12
  27. package/package.json +1 -1
  28. package/plugin/build/withGaodeMap.js +28 -0
@@ -3,10 +3,12 @@ package expo.modules.gaodemap.map
3
3
  import android.os.Bundle
4
4
  import android.os.StatFs
5
5
  import android.os.Environment
6
+ import android.util.Log
6
7
  import com.amap.api.maps.offlinemap.OfflineMapCity
7
8
  import com.amap.api.maps.offlinemap.OfflineMapManager
8
9
  import com.amap.api.maps.offlinemap.OfflineMapProvince
9
10
  import com.amap.api.maps.offlinemap.OfflineMapStatus
11
+ import expo.modules.gaodemap.map.modules.SDKInitializer
10
12
  import expo.modules.kotlin.modules.Module
11
13
  import expo.modules.kotlin.modules.ModuleDefinition
12
14
 
@@ -81,20 +83,8 @@ class ExpoGaodeMapOfflineModule : Module() {
81
83
  // ==================== 模块生命周期 ====================
82
84
 
83
85
  OnCreate {
84
- // 初始化离线地图管理器
85
- offlineMapManager = OfflineMapManager(appContext.reactContext, object : OfflineMapManager.OfflineMapDownloadListener {
86
- override fun onDownload(status: Int, completeCode: Int, downName: String?) {
87
- handleDownloadStatus(status, completeCode, downName)
88
- }
89
-
90
- override fun onCheckUpdate(hasNew: Boolean, name: String?) {
91
- // 更新检查回调
92
- }
93
-
94
- override fun onRemove(success: Boolean, name: String?, describe: String?) {
95
- // 删除回调
96
- }
97
- })
86
+ // 避免在应用启动早期(JS 隐私同意前)触发 OfflineMapManager 初始化导致 555571
87
+ getOfflineMapManager()
98
88
  }
99
89
 
100
90
  OnDestroy {
@@ -106,24 +96,24 @@ class ExpoGaodeMapOfflineModule : Module() {
106
96
  // ==================== 地图列表管理 ====================
107
97
 
108
98
  AsyncFunction("getAvailableCities") {
109
- val cities = offlineMapManager?.offlineMapCityList ?: emptyList()
99
+ val cities = getOfflineMapManager()?.offlineMapCityList ?: emptyList()
110
100
  cities.map { city -> convertCityToMap(city) }
111
101
  }
112
102
 
113
103
  AsyncFunction("getAvailableProvinces") {
114
- val provinces = offlineMapManager?.offlineMapProvinceList ?: emptyList()
104
+ val provinces = getOfflineMapManager()?.offlineMapProvinceList ?: emptyList()
115
105
  provinces.map { province -> convertProvinceToMap(province) }
116
106
  }
117
107
 
118
108
  AsyncFunction("getCitiesByProvince") { provinceCode: String ->
119
- val province = offlineMapManager?.offlineMapProvinceList?.find {
109
+ val province = getOfflineMapManager()?.offlineMapProvinceList?.find {
120
110
  it.provinceCode == provinceCode
121
111
  }
122
112
  province?.cityList?.map { city -> convertCityToMap(city) } ?: emptyList()
123
113
  }
124
114
 
125
115
  AsyncFunction("getDownloadedMaps") {
126
- val cities = offlineMapManager?.downloadOfflineMapCityList ?: emptyList()
116
+ val cities = getOfflineMapManager()?.downloadOfflineMapCityList ?: emptyList()
127
117
  cities.map { city -> convertCityToMap(city) }
128
118
  }
129
119
 
@@ -137,11 +127,11 @@ class ExpoGaodeMapOfflineModule : Module() {
137
127
  downloadingCities.add(cityCode)
138
128
  pausedCities.remove(cityCode)
139
129
  }
140
- offlineMapManager?.downloadByCityCode(cityCode)
130
+ getOfflineMapManager()?.downloadByCityCode(cityCode)
141
131
  }
142
132
 
143
133
  AsyncFunction("pauseDownload") { cityCode: String ->
144
- val city = offlineMapManager?.getItemByCityCode(cityCode)
134
+ val city = getOfflineMapManager()?.getItemByCityCode(cityCode)
145
135
 
146
136
  synchronized(lock) {
147
137
  pausedCities.add(cityCode)
@@ -150,7 +140,7 @@ class ExpoGaodeMapOfflineModule : Module() {
150
140
 
151
141
  // 使用 pauseByName 暂停指定城市
152
142
  city?.city?.let { cityName ->
153
- offlineMapManager?.pauseByName(cityName)
143
+ getOfflineMapManager()?.pauseByName(cityName)
154
144
  }
155
145
 
156
146
  if (city != null) {
@@ -168,11 +158,11 @@ class ExpoGaodeMapOfflineModule : Module() {
168
158
  }
169
159
  // Android SDK 没有针对单个城市的恢复方法
170
160
  // 需要重新调用 downloadByCityCode 来继续下载
171
- offlineMapManager?.downloadByCityCode(cityCode)
161
+ getOfflineMapManager()?.downloadByCityCode(cityCode)
172
162
  }
173
163
 
174
164
  AsyncFunction("cancelDownload") { cityCode: String ->
175
- val city = offlineMapManager?.getItemByCityCode(cityCode)
165
+ val city = getOfflineMapManager()?.getItemByCityCode(cityCode)
176
166
 
177
167
  synchronized(lock) {
178
168
  downloadingCities.remove(cityCode)
@@ -180,7 +170,7 @@ class ExpoGaodeMapOfflineModule : Module() {
180
170
  }
181
171
 
182
172
  // 使用 stop() 停止所有下载(包括队列)
183
- offlineMapManager?.stop()
173
+ getOfflineMapManager()?.stop()
184
174
 
185
175
  if (city != null) {
186
176
  sendEvent("onDownloadCancelled", Bundle().apply {
@@ -191,11 +181,11 @@ class ExpoGaodeMapOfflineModule : Module() {
191
181
  }
192
182
 
193
183
  AsyncFunction("deleteMap") { cityCode: String ->
194
- val city = offlineMapManager?.getItemByCityCode(cityCode)
184
+ val city = getOfflineMapManager()?.getItemByCityCode(cityCode)
195
185
  ?: throw IllegalArgumentException("City not found: $cityCode")
196
186
 
197
187
  // 官方文档:remove() 需要传入城市名称,不是城市代码
198
- offlineMapManager?.remove(city.city)
188
+ getOfflineMapManager()?.remove(city.city)
199
189
 
200
190
  synchronized(lock) {
201
191
  downloadingCities.remove(cityCode)
@@ -207,29 +197,29 @@ class ExpoGaodeMapOfflineModule : Module() {
207
197
  synchronized(lock) {
208
198
  downloadingCities.add(cityCode)
209
199
  }
210
- offlineMapManager?.updateOfflineCityByCode(cityCode)
200
+ getOfflineMapManager()?.updateOfflineCityByCode(cityCode)
211
201
  }
212
202
 
213
203
  AsyncFunction("checkUpdate") { cityCode: String ->
214
- val city = offlineMapManager?.getItemByCityCode(cityCode)
204
+ val city = getOfflineMapManager()?.getItemByCityCode(cityCode)
215
205
  city?.state == OfflineMapStatus.NEW_VERSION
216
206
  }
217
207
 
218
208
  // ==================== 状态查询 ====================
219
209
 
220
210
  AsyncFunction("isMapDownloaded") { cityCode: String ->
221
- val city = offlineMapManager?.getItemByCityCode(cityCode)
211
+ val city = getOfflineMapManager()?.getItemByCityCode(cityCode)
222
212
  city?.state == OfflineMapStatus.SUCCESS ||
223
213
  city?.state == OfflineMapStatus.CHECKUPDATES
224
214
  }
225
215
 
226
216
  AsyncFunction("getMapStatus") { cityCode: String ->
227
- val city = offlineMapManager?.getItemByCityCode(cityCode)
217
+ val city = getOfflineMapManager()?.getItemByCityCode(cityCode)
228
218
  city?.let { convertCityToMap(it) } ?: Bundle()
229
219
  }
230
220
 
231
221
  AsyncFunction("getTotalProgress") {
232
- val downloadedCities = offlineMapManager?.downloadOfflineMapCityList ?: emptyList()
222
+ val downloadedCities = getOfflineMapManager()?.downloadOfflineMapCityList ?: emptyList()
233
223
  if (downloadedCities.isEmpty()) {
234
224
  0.0
235
225
  } else {
@@ -246,12 +236,12 @@ class ExpoGaodeMapOfflineModule : Module() {
246
236
 
247
237
  AsyncFunction("getStorageSize") {
248
238
  // 计算所有已下载地图的大小
249
- val cities = offlineMapManager?.downloadOfflineMapCityList ?: emptyList()
239
+ val cities = getOfflineMapManager()?.downloadOfflineMapCityList ?: emptyList()
250
240
  cities.sumOf { it.size }
251
241
  }
252
242
 
253
243
  AsyncFunction("getStorageInfo") {
254
- val cities = offlineMapManager?.downloadOfflineMapCityList ?: emptyList()
244
+ val cities = getOfflineMapManager()?.downloadOfflineMapCityList ?: emptyList()
255
245
  val offlineMapSize = cities.sumOf { it.size }
256
246
 
257
247
  // 获取存储路径的统计信息
@@ -273,9 +263,9 @@ class ExpoGaodeMapOfflineModule : Module() {
273
263
  }
274
264
 
275
265
  AsyncFunction("clearAllMaps") {
276
- offlineMapManager?.downloadOfflineMapCityList?.forEach { city ->
266
+ getOfflineMapManager()?.downloadOfflineMapCityList?.forEach { city ->
277
267
  // 使用城市名称删除
278
- offlineMapManager?.remove(city.city)
268
+ getOfflineMapManager()?.remove(city.city)
279
269
  }
280
270
  synchronized(lock) {
281
271
  downloadingCities.clear()
@@ -302,16 +292,16 @@ class ExpoGaodeMapOfflineModule : Module() {
302
292
  }
303
293
  }
304
294
  cityCodes.forEach { cityCode ->
305
- offlineMapManager?.downloadByCityCode(cityCode)
295
+ getOfflineMapManager()?.downloadByCityCode(cityCode)
306
296
  }
307
297
  }
308
298
 
309
299
  AsyncFunction("batchDelete") { cityCodes: List<String> ->
310
300
  cityCodes.forEach { cityCode ->
311
- val city = offlineMapManager?.getItemByCityCode(cityCode)
301
+ val city = getOfflineMapManager()?.getItemByCityCode(cityCode)
312
302
  // 使用城市名称删除,不是城市代码
313
303
  city?.city?.let { cityName ->
314
- offlineMapManager?.remove(cityName)
304
+ getOfflineMapManager()?.remove(cityName)
315
305
  }
316
306
  }
317
307
  synchronized(lock) {
@@ -329,18 +319,18 @@ class ExpoGaodeMapOfflineModule : Module() {
329
319
  }
330
320
  }
331
321
  cityCodes.forEach { cityCode ->
332
- offlineMapManager?.updateOfflineCityByCode(cityCode)
322
+ getOfflineMapManager()?.updateOfflineCityByCode(cityCode)
333
323
  }
334
324
  }
335
325
 
336
326
  AsyncFunction("pauseAllDownloads") {
337
327
  // pause() 只暂停正在下载的,不包括队列
338
- offlineMapManager?.pause()
328
+ getOfflineMapManager()?.pause()
339
329
 
340
330
  synchronized(lock) {
341
331
  pausedCities.addAll(downloadingCities)
342
332
  downloadingCities.forEach { cityCode ->
343
- val city = offlineMapManager?.getItemByCityCode(cityCode)
333
+ val city = getOfflineMapManager()?.getItemByCityCode(cityCode)
344
334
  if (city != null) {
345
335
  sendEvent("onDownloadPaused", Bundle().apply {
346
336
  putString("cityCode", cityCode)
@@ -363,7 +353,7 @@ class ExpoGaodeMapOfflineModule : Module() {
363
353
  downloadingCities.add(cityCode)
364
354
  pausedCities.remove(cityCode)
365
355
  }
366
- offlineMapManager?.downloadByCityCode(cityCode)
356
+ getOfflineMapManager()?.downloadByCityCode(cityCode)
367
357
  }
368
358
  }
369
359
  }
@@ -377,9 +367,9 @@ class ExpoGaodeMapOfflineModule : Module() {
377
367
  if (downName == null) return
378
368
 
379
369
  // downName 可能是城市代码或城市名称,尝试两种方式查找
380
- var city = offlineMapManager?.getItemByCityCode(downName)
370
+ var city = getOfflineMapManager()?.getItemByCityCode(downName)
381
371
  if (city == null) {
382
- city = offlineMapManager?.offlineMapCityList?.find { it.city == downName }
372
+ city = getOfflineMapManager()?.offlineMapCityList?.find { it.city == downName }
383
373
  }
384
374
 
385
375
  if (city == null) return
@@ -532,4 +522,40 @@ class ExpoGaodeMapOfflineModule : Module() {
532
522
  else -> "not_downloaded"
533
523
  }
534
524
  }
535
- }
525
+
526
+ /**
527
+ * 按需初始化 OfflineMapManager:
528
+ * - 隐私未同意时不初始化,避免应用启动阶段触发 555571
529
+ * - 用户同意后首次调用离线接口时自动初始化
530
+ */
531
+ private fun getOfflineMapManager(): OfflineMapManager? {
532
+ offlineMapManager?.let { return it }
533
+
534
+ val context = appContext.reactContext ?: return null
535
+ return try {
536
+ SDKInitializer.restorePersistedState(context)
537
+ if (!SDKInitializer.isPrivacyReady()) {
538
+ null
539
+ } else {
540
+ OfflineMapManager(context, object : OfflineMapManager.OfflineMapDownloadListener {
541
+ override fun onDownload(status: Int, completeCode: Int, downName: String?) {
542
+ handleDownloadStatus(status, completeCode, downName)
543
+ }
544
+
545
+ override fun onCheckUpdate(hasNew: Boolean, name: String?) {
546
+ // 更新检查回调
547
+ }
548
+
549
+ override fun onRemove(success: Boolean, name: String?, describe: String?) {
550
+ // 删除回调
551
+ }
552
+ }).also { manager ->
553
+ offlineMapManager = manager
554
+ }
555
+ }
556
+ } catch (e: Exception) {
557
+ Log.w("ExpoGaodeMapOffline", "OfflineMapManager 初始化失败: ${e.message}")
558
+ null
559
+ }
560
+ }
561
+ }
@@ -102,6 +102,7 @@ object SDKInitializer {
102
102
  AMapLocationClient.updatePrivacyShow(appContext, privacyShown, privacyContains)
103
103
  MapsInitializer.updatePrivacyAgree(appContext, privacyAgreed)
104
104
  AMapLocationClient.updatePrivacyAgree(appContext, privacyAgreed)
105
+ applyNavigationPrivacyState(appContext)
105
106
  } catch (e: Exception) {
106
107
  android.util.Log.w("ExpoGaodeMap", "同步隐私状态失败: ${e.message}")
107
108
  }
@@ -194,4 +195,38 @@ object SDKInitializer {
194
195
 
195
196
  editor.apply()
196
197
  }
198
+
199
+ /**
200
+ * 导航 SDK 隐私同步(NaviSetting)
201
+ *
202
+ * 说明:
203
+ * - 导航 SDK 需要在 AMapNavi.getInstance(...) 前调用 updatePrivacyShow/updatePrivacyAgree。
204
+ * - 使用反射兼容不同版本导航 SDK(某些版本可能不存在 NaviSetting 或方法签名变化)。
205
+ */
206
+ private fun applyNavigationPrivacyState(context: Context) {
207
+ try {
208
+ val naviSettingClass = Class.forName("com.amap.api.navi.NaviSetting")
209
+ val updatePrivacyShow = naviSettingClass.getDeclaredMethod(
210
+ "updatePrivacyShow",
211
+ Context::class.java,
212
+ Boolean::class.javaPrimitiveType,
213
+ Boolean::class.javaPrimitiveType
214
+ )
215
+ val updatePrivacyAgree = naviSettingClass.getDeclaredMethod(
216
+ "updatePrivacyAgree",
217
+ Context::class.java,
218
+ Boolean::class.javaPrimitiveType
219
+ )
220
+
221
+ // NaviSetting.updatePrivacyShow(context, isContains, isShow)
222
+ updatePrivacyShow.invoke(null, context, privacyContains, privacyShown)
223
+ updatePrivacyAgree.invoke(null, context, privacyAgreed)
224
+ } catch (e: ClassNotFoundException) {
225
+ android.util.Log.w("ExpoGaodeMap", "未检测到 NaviSetting,跳过导航隐私同步: ${e.message}")
226
+ } catch (e: NoSuchMethodException) {
227
+ android.util.Log.w("ExpoGaodeMap", "NaviSetting 隐私方法签名不匹配,跳过导航隐私同步: ${e.message}")
228
+ } catch (e: Exception) {
229
+ android.util.Log.w("ExpoGaodeMap", "同步导航 SDK 隐私状态失败: ${e.message}")
230
+ }
231
+ }
197
232
  }
@@ -41,7 +41,6 @@ import androidx.core.view.isEmpty
41
41
  import androidx.core.graphics.scale
42
42
  import android.view.ViewGroup
43
43
  import android.widget.ImageView
44
- import android.widget.TextView
45
44
  import com.amap.api.maps.model.animation.AlphaAnimation
46
45
  import com.amap.api.maps.model.animation.AnimationSet
47
46
  import com.amap.api.maps.model.animation.ScaleAnimation
@@ -14,6 +14,7 @@ import com.amap.api.navi.AMapNaviViewOptions
14
14
  import com.amap.api.navi.enums.MapStyle
15
15
  import com.amap.api.navi.enums.NaviType
16
16
  import com.amap.api.navi.model.*
17
+ import expo.modules.gaodemap.map.modules.SDKInitializer
17
18
  import expo.modules.kotlin.AppContext
18
19
  import expo.modules.kotlin.viewevent.EventDispatcher
19
20
  import expo.modules.kotlin.views.ExpoView
@@ -59,7 +60,9 @@ class ExpoGaodeMapNaviView(context: Context, appContext: AppContext) : ExpoView(
59
60
  internal var isVectorLineShow: Boolean = true
60
61
  internal var isNaviTravelView : Boolean = false
61
62
  internal var isCompassEnabled: Boolean = true
62
- private val naviView: AMapNaviView = AMapNaviView(context)
63
+ private val naviView: AMapNaviView by lazy(LazyThreadSafetyMode.NONE) {
64
+ AMapNaviView(context)
65
+ }
63
66
  private var aMapNavi: AMapNavi? = null
64
67
  private var startCoordinate: NaviLatLng? = null
65
68
  private var endCoordinate: NaviLatLng? = null
@@ -299,6 +302,14 @@ class ExpoGaodeMapNaviView(context: Context, appContext: AppContext) : ExpoView(
299
302
 
300
303
  init {
301
304
  try {
305
+ val appCtx = context.applicationContext ?: context
306
+ SDKInitializer.restorePersistedState(appCtx)
307
+ if (!SDKInitializer.isPrivacyReady()) {
308
+ throw IllegalStateException(
309
+ "隐私协议未完成确认,请先调用 setPrivacyConfig(或 setPrivacyShow/setPrivacyAgree)"
310
+ )
311
+ }
312
+
302
313
  // 初始化导航视图
303
314
  naviView.onCreate(Bundle())
304
315
  naviView.layoutParams = android.widget.FrameLayout.LayoutParams(
@@ -315,7 +326,7 @@ class ExpoGaodeMapNaviView(context: Context, appContext: AppContext) : ExpoView(
315
326
  ensureOverlayInsetHook()
316
327
 
317
328
  // 使用单例获取导航实例
318
- aMapNavi = AMapNavi.getInstance(context.applicationContext)
329
+ aMapNavi = AMapNavi.getInstance(appCtx)
319
330
  aMapNavi?.addAMapNaviListener(naviListener)
320
331
 
321
332
  // 使用内置语音播报功能(v5.6.0+)