expo-gaode-map 2.2.27-next.0 → 2.2.27

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 (63) hide show
  1. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +46 -46
  2. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapOfflineModule.kt +70 -50
  3. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +2 -3
  4. package/android/src/main/java/expo/modules/gaodemap/modules/SDKInitializer.kt +143 -14
  5. package/build/ExpoGaodeMapModule.d.ts +245 -4
  6. package/build/ExpoGaodeMapModule.d.ts.map +1 -1
  7. package/build/ExpoGaodeMapModule.js +201 -14
  8. package/build/ExpoGaodeMapModule.js.map +1 -1
  9. package/build/ExpoGaodeMapOfflineModule.d.ts.map +1 -1
  10. package/build/ExpoGaodeMapOfflineModule.js +19 -2
  11. package/build/ExpoGaodeMapOfflineModule.js.map +1 -1
  12. package/build/ExpoGaodeMapView.d.ts.map +1 -1
  13. package/build/ExpoGaodeMapView.js +8 -2
  14. package/build/ExpoGaodeMapView.js.map +1 -1
  15. package/build/components/overlays/Circle.d.ts.map +1 -1
  16. package/build/components/overlays/Circle.js +3 -2
  17. package/build/components/overlays/Circle.js.map +1 -1
  18. package/build/components/overlays/Cluster.d.ts.map +1 -1
  19. package/build/components/overlays/Cluster.js +3 -2
  20. package/build/components/overlays/Cluster.js.map +1 -1
  21. package/build/components/overlays/HeatMap.d.ts.map +1 -1
  22. package/build/components/overlays/HeatMap.js +3 -2
  23. package/build/components/overlays/HeatMap.js.map +1 -1
  24. package/build/components/overlays/Marker.d.ts.map +1 -1
  25. package/build/components/overlays/Marker.js +3 -2
  26. package/build/components/overlays/Marker.js.map +1 -1
  27. package/build/components/overlays/MultiPoint.d.ts.map +1 -1
  28. package/build/components/overlays/MultiPoint.js +3 -2
  29. package/build/components/overlays/MultiPoint.js.map +1 -1
  30. package/build/components/overlays/Polygon.d.ts.map +1 -1
  31. package/build/components/overlays/Polygon.js +3 -2
  32. package/build/components/overlays/Polygon.js.map +1 -1
  33. package/build/components/overlays/Polyline.d.ts.map +1 -1
  34. package/build/components/overlays/Polyline.js +3 -2
  35. package/build/components/overlays/Polyline.js.map +1 -1
  36. package/build/types/common.types.d.ts +26 -0
  37. package/build/types/common.types.d.ts.map +1 -1
  38. package/build/types/common.types.js.map +1 -1
  39. package/build/types/native-module.types.d.ts +32 -1
  40. package/build/types/native-module.types.d.ts.map +1 -1
  41. package/build/types/native-module.types.js.map +1 -1
  42. package/build/utils/ErrorHandler.d.ts +6 -0
  43. package/build/utils/ErrorHandler.d.ts.map +1 -1
  44. package/build/utils/ErrorHandler.js +42 -0
  45. package/build/utils/ErrorHandler.js.map +1 -1
  46. package/build/utils/GeoUtils.d.ts.map +1 -1
  47. package/build/utils/GeoUtils.js.map +1 -1
  48. package/build/utils/lazyNativeViewManager.d.ts +3 -0
  49. package/build/utils/lazyNativeViewManager.d.ts.map +1 -0
  50. package/build/utils/lazyNativeViewManager.js +11 -0
  51. package/build/utils/lazyNativeViewManager.js.map +1 -0
  52. package/build/utils/throttle.d.ts +1 -1
  53. package/build/utils/throttle.d.ts.map +1 -1
  54. package/build/utils/throttle.js +1 -1
  55. package/build/utils/throttle.js.map +1 -1
  56. package/ios/ExpoGaodeMapModule.swift +27 -40
  57. package/ios/ExpoGaodeMapOfflineModule.swift +63 -33
  58. package/ios/ExpoGaodeMapView.swift +165 -78
  59. package/ios/GaodeMapPrivacyManager.swift +155 -0
  60. package/ios/modules/LocationManager.swift +43 -24
  61. package/package.json +1 -1
  62. package/android/src/main/java/expo/modules/gaodemap/MapPreloadManager.kt +0 -494
  63. package/ios/MapPreloadManager.swift +0 -348
@@ -30,38 +30,12 @@ class ExpoGaodeMapModule : Module() {
30
30
  override fun definition() = ModuleDefinition {
31
31
  Name("ExpoGaodeMap")
32
32
 
33
- // 在模块加载时尝试从本地缓存恢复隐私同意状态,避免每次启动都必须 JS 调用
34
- try {
35
- val context = appContext.reactContext!!
36
- SDKInitializer.restorePrivacyState(context)
37
-
38
- // 尝试从 AndroidManifest.xml 读取并设置 API Key
39
- val apiKey = context.packageManager
40
- .getApplicationInfo(context.packageName, android.content.pm.PackageManager.GET_META_DATA)
41
- .metaData?.getString("com.amap.api.v2.apikey")
42
-
43
- if (!apiKey.isNullOrEmpty()) {
44
- try {
45
- MapsInitializer.setApiKey(apiKey)
46
- com.amap.api.location.AMapLocationClient.setApiKey(apiKey)
47
-
48
-
49
- android.util.Log.i(
50
- "ExpoGaodeMap",
51
- "✅ Android API Key 已加载(core Android 已关闭自动预加载,避免隐藏 MapView 与实际 TextureMapView 并存)"
52
- )
53
- } catch (e: Exception) {
54
- android.util.Log.w("ExpoGaodeMap", "设置 API Key 失败: ${e.message}")
55
- }
56
- } else {
57
- android.util.Log.w("ExpoGaodeMap", "⚠️ AndroidManifest.xml 未找到 API Key")
58
- }
59
-
60
- } catch (e: Exception) {
61
- android.util.Log.w("ExpoGaodeMap", "恢复隐私状态时出现问题: ${e.message}")
33
+ OnCreate {
34
+ appContext.reactContext?.let { context ->
35
+ SDKInitializer.restorePersistedState(context)
36
+ }
62
37
  }
63
-
64
-
38
+
65
39
  // ==================== SDK 初始化 ====================
66
40
 
67
41
  /**
@@ -70,24 +44,51 @@ class ExpoGaodeMapModule : Module() {
70
44
  */
71
45
  Function("initSDK") { config: Map<String, String> ->
72
46
  val androidKey = config["androidKey"]
73
- if (androidKey != null) {
74
- try {
75
- SDKInitializer.initSDK(appContext.reactContext!!, androidKey)
76
- getLocationManager() // 初始化定位管理器
77
-
78
- // 打印当前 SDK 版本信息,便于验证依赖来源
79
- android.util.Log.i("ExpoGaodeMap", "✅ SDK 初始化完成 - Version: ${MapsInitializer.getVersion()}")
80
-
81
- } catch (e: SecurityException) {
82
- android.util.Log.e("ExpoGaodeMap", "隐私协议未同意: ${e.message}")
83
- throw expo.modules.kotlin.exception.CodedException("PRIVACY_NOT_AGREED", e.message ?: "用户未同意隐私协议", e)
84
- } catch (e: Exception) {
85
- android.util.Log.e("ExpoGaodeMap", "SDK 初始化失败: ${e.message}")
86
- throw expo.modules.kotlin.exception.CodedException("INIT_FAILED", e.message ?: "SDK 初始化失败", e)
47
+ try {
48
+ val context = appContext.reactContext!!
49
+ if (androidKey != null) {
50
+ SDKInitializer.initSDK(context, androidKey)
51
+ } else if (!SDKInitializer.isPrivacyReady()) {
52
+ throw expo.modules.kotlin.exception.CodedException(
53
+ "PRIVACY_NOT_AGREED",
54
+ "隐私协议未完成确认,请先调用 setPrivacyShow/setPrivacyAgree",
55
+ null
56
+ )
57
+ } else {
58
+ SDKInitializer.applyPrivacyState(context)
87
59
  }
60
+
61
+ getLocationManager()
62
+ android.util.Log.i("ExpoGaodeMap", "✅ SDK 初始化完成 - Version: ${MapsInitializer.getVersion()}")
63
+ } catch (e: SecurityException) {
64
+ android.util.Log.e("ExpoGaodeMap", "隐私协议未同意: ${e.message}")
65
+ throw expo.modules.kotlin.exception.CodedException("PRIVACY_NOT_AGREED", e.message ?: "用户未同意隐私协议", e)
66
+ } catch (e: Exception) {
67
+ android.util.Log.e("ExpoGaodeMap", "SDK 初始化失败: ${e.message}")
68
+ throw expo.modules.kotlin.exception.CodedException("INIT_FAILED", e.message ?: "SDK 初始化失败", e)
88
69
  }
89
70
  }
90
71
 
72
+ Function("setPrivacyShow") { hasShow: Boolean, hasContainsPrivacy: Boolean ->
73
+ SDKInitializer.setPrivacyShow(appContext.reactContext!!, hasShow, hasContainsPrivacy)
74
+ }
75
+
76
+ Function("setPrivacyAgree") { hasAgree: Boolean ->
77
+ SDKInitializer.setPrivacyAgree(appContext.reactContext!!, hasAgree)
78
+ }
79
+
80
+ Function("setPrivacyVersion") { version: String ->
81
+ SDKInitializer.setPrivacyVersion(appContext.reactContext!!, version)
82
+ }
83
+
84
+ Function("resetPrivacyConsent") {
85
+ SDKInitializer.resetPrivacyConsent(appContext.reactContext!!)
86
+ }
87
+
88
+ Function("getPrivacyStatus") {
89
+ SDKInitializer.getPrivacyStatus()
90
+ }
91
+
91
92
  /**
92
93
  * 设置是否加载世界向量地图
93
94
  * @param enable 是否开启
@@ -887,7 +888,6 @@ class ExpoGaodeMapModule : Module() {
887
888
  OnDestroy {
888
889
  locationManager?.destroy()
889
890
  locationManager = null
890
- MapPreloadManager.cleanup()
891
891
  }
892
892
  }
893
893
 
@@ -7,6 +7,8 @@ import com.amap.api.maps.offlinemap.OfflineMapCity
7
7
  import com.amap.api.maps.offlinemap.OfflineMapManager
8
8
  import com.amap.api.maps.offlinemap.OfflineMapProvince
9
9
  import com.amap.api.maps.offlinemap.OfflineMapStatus
10
+ import expo.modules.gaodemap.modules.SDKInitializer
11
+ import expo.modules.kotlin.exception.CodedException
10
12
  import expo.modules.kotlin.modules.Module
11
13
  import expo.modules.kotlin.modules.ModuleDefinition
12
14
 
@@ -21,6 +23,42 @@ class ExpoGaodeMapOfflineModule : Module() {
21
23
 
22
24
  // 线程安全锁
23
25
  private val lock = Any()
26
+
27
+ private fun createDownloadListener(): OfflineMapManager.OfflineMapDownloadListener {
28
+ return object : OfflineMapManager.OfflineMapDownloadListener {
29
+ override fun onDownload(status: Int, completeCode: Int, downName: String?) {
30
+ handleDownloadStatus(status, completeCode, downName)
31
+ }
32
+
33
+ override fun onCheckUpdate(hasNew: Boolean, name: String?) {
34
+ }
35
+
36
+ override fun onRemove(success: Boolean, name: String?, describe: String?) {
37
+ }
38
+ }
39
+ }
40
+
41
+ private fun getOfflineMapManager(): OfflineMapManager {
42
+ if (!SDKInitializer.isPrivacyReady()) {
43
+ throw CodedException(
44
+ "PRIVACY_NOT_AGREED",
45
+ "隐私协议未完成确认,请先调用 setPrivacyShow/setPrivacyAgree",
46
+ null
47
+ )
48
+ }
49
+
50
+ val reactContext = appContext.reactContext
51
+ ?: throw CodedException("NO_CONTEXT", "React context not available", null)
52
+
53
+ if (offlineMapManager == null) {
54
+ offlineMapManager = OfflineMapManager(
55
+ reactContext.applicationContext,
56
+ createDownloadListener()
57
+ )
58
+ }
59
+
60
+ return offlineMapManager!!
61
+ }
24
62
 
25
63
  override fun definition() = ModuleDefinition {
26
64
  Name("ExpoGaodeMapOffline")
@@ -36,25 +74,6 @@ class ExpoGaodeMapOfflineModule : Module() {
36
74
  "onDownloadCancelled"
37
75
  )
38
76
 
39
- // ==================== 模块生命周期 ====================
40
-
41
- OnCreate {
42
- // 初始化离线地图管理器
43
- offlineMapManager = OfflineMapManager(appContext.reactContext, object : OfflineMapManager.OfflineMapDownloadListener {
44
- override fun onDownload(status: Int, completeCode: Int, downName: String?) {
45
- handleDownloadStatus(status, completeCode, downName)
46
- }
47
-
48
- override fun onCheckUpdate(hasNew: Boolean, name: String?) {
49
- // 更新检查回调
50
- }
51
-
52
- override fun onRemove(success: Boolean, name: String?, describe: String?) {
53
- // 删除回调
54
- }
55
- })
56
- }
57
-
58
77
  OnDestroy {
59
78
  offlineMapManager?.destroy()
60
79
  offlineMapManager = null
@@ -64,24 +83,24 @@ class ExpoGaodeMapOfflineModule : Module() {
64
83
  // ==================== 地图列表管理 ====================
65
84
 
66
85
  AsyncFunction("getAvailableCities") {
67
- val cities = offlineMapManager?.offlineMapCityList ?: emptyList()
86
+ val cities = getOfflineMapManager().offlineMapCityList ?: emptyList()
68
87
  cities.map { city -> convertCityToMap(city) }
69
88
  }
70
89
 
71
90
  AsyncFunction("getAvailableProvinces") {
72
- val provinces = offlineMapManager?.offlineMapProvinceList ?: emptyList()
91
+ val provinces = getOfflineMapManager().offlineMapProvinceList ?: emptyList()
73
92
  provinces.map { province -> convertProvinceToMap(province) }
74
93
  }
75
94
 
76
95
  AsyncFunction("getCitiesByProvince") { provinceCode: String ->
77
- val province = offlineMapManager?.offlineMapProvinceList?.find {
96
+ val province = getOfflineMapManager().offlineMapProvinceList?.find {
78
97
  it.provinceCode == provinceCode
79
98
  }
80
99
  province?.cityList?.map { city -> convertCityToMap(city) } ?: emptyList()
81
100
  }
82
101
 
83
102
  AsyncFunction("getDownloadedMaps") {
84
- val cities = offlineMapManager?.downloadOfflineMapCityList ?: emptyList()
103
+ val cities = getOfflineMapManager().downloadOfflineMapCityList ?: emptyList()
85
104
  cities.map { city -> convertCityToMap(city) }
86
105
  }
87
106
 
@@ -95,11 +114,11 @@ class ExpoGaodeMapOfflineModule : Module() {
95
114
  downloadingCities.add(cityCode)
96
115
  pausedCities.remove(cityCode)
97
116
  }
98
- offlineMapManager?.downloadByCityCode(cityCode)
117
+ getOfflineMapManager().downloadByCityCode(cityCode)
99
118
  }
100
119
 
101
120
  AsyncFunction("pauseDownload") { cityCode: String ->
102
- val city = offlineMapManager?.getItemByCityCode(cityCode)
121
+ val city = getOfflineMapManager().getItemByCityCode(cityCode)
103
122
 
104
123
  synchronized(lock) {
105
124
  pausedCities.add(cityCode)
@@ -108,7 +127,7 @@ class ExpoGaodeMapOfflineModule : Module() {
108
127
 
109
128
  // 使用 pauseByName 暂停指定城市
110
129
  city?.city?.let { cityName ->
111
- offlineMapManager?.pauseByName(cityName)
130
+ getOfflineMapManager().pauseByName(cityName)
112
131
  }
113
132
 
114
133
  if (city != null) {
@@ -126,11 +145,11 @@ class ExpoGaodeMapOfflineModule : Module() {
126
145
  }
127
146
  // Android SDK 没有针对单个城市的恢复方法
128
147
  // 需要重新调用 downloadByCityCode 来继续下载
129
- offlineMapManager?.downloadByCityCode(cityCode)
148
+ getOfflineMapManager().downloadByCityCode(cityCode)
130
149
  }
131
150
 
132
151
  AsyncFunction("cancelDownload") { cityCode: String ->
133
- val city = offlineMapManager?.getItemByCityCode(cityCode)
152
+ val city = getOfflineMapManager().getItemByCityCode(cityCode)
134
153
 
135
154
  synchronized(lock) {
136
155
  downloadingCities.remove(cityCode)
@@ -138,7 +157,7 @@ class ExpoGaodeMapOfflineModule : Module() {
138
157
  }
139
158
 
140
159
  // 使用 stop() 停止所有下载(包括队列)
141
- offlineMapManager?.stop()
160
+ getOfflineMapManager().stop()
142
161
 
143
162
  if (city != null) {
144
163
  sendEvent("onDownloadCancelled", Bundle().apply {
@@ -149,11 +168,11 @@ class ExpoGaodeMapOfflineModule : Module() {
149
168
  }
150
169
 
151
170
  AsyncFunction("deleteMap") { cityCode: String ->
152
- val city = offlineMapManager?.getItemByCityCode(cityCode)
171
+ val city = getOfflineMapManager().getItemByCityCode(cityCode)
153
172
  ?: throw IllegalArgumentException("City not found: $cityCode")
154
173
 
155
174
  // 官方文档:remove() 需要传入城市名称,不是城市代码
156
- offlineMapManager?.remove(city.city)
175
+ getOfflineMapManager().remove(city.city)
157
176
 
158
177
  synchronized(lock) {
159
178
  downloadingCities.remove(cityCode)
@@ -165,29 +184,29 @@ class ExpoGaodeMapOfflineModule : Module() {
165
184
  synchronized(lock) {
166
185
  downloadingCities.add(cityCode)
167
186
  }
168
- offlineMapManager?.updateOfflineCityByCode(cityCode)
187
+ getOfflineMapManager().updateOfflineCityByCode(cityCode)
169
188
  }
170
189
 
171
190
  AsyncFunction("checkUpdate") { cityCode: String ->
172
- val city = offlineMapManager?.getItemByCityCode(cityCode)
191
+ val city = getOfflineMapManager().getItemByCityCode(cityCode)
173
192
  city?.state == OfflineMapStatus.NEW_VERSION
174
193
  }
175
194
 
176
195
  // ==================== 状态查询 ====================
177
196
 
178
197
  AsyncFunction("isMapDownloaded") { cityCode: String ->
179
- val city = offlineMapManager?.getItemByCityCode(cityCode)
198
+ val city = getOfflineMapManager().getItemByCityCode(cityCode)
180
199
  city?.state == OfflineMapStatus.SUCCESS ||
181
200
  city?.state == OfflineMapStatus.CHECKUPDATES
182
201
  }
183
202
 
184
203
  AsyncFunction("getMapStatus") { cityCode: String ->
185
- val city = offlineMapManager?.getItemByCityCode(cityCode)
204
+ val city = getOfflineMapManager().getItemByCityCode(cityCode)
186
205
  city?.let { convertCityToMap(it) } ?: Bundle()
187
206
  }
188
207
 
189
208
  AsyncFunction("getTotalProgress") {
190
- val downloadedCities = offlineMapManager?.downloadOfflineMapCityList ?: emptyList()
209
+ val downloadedCities = getOfflineMapManager().downloadOfflineMapCityList ?: emptyList()
191
210
  if (downloadedCities.isEmpty()) {
192
211
  0.0
193
212
  } else {
@@ -204,12 +223,12 @@ class ExpoGaodeMapOfflineModule : Module() {
204
223
 
205
224
  AsyncFunction("getStorageSize") {
206
225
  // 计算所有已下载地图的大小
207
- val cities = offlineMapManager?.downloadOfflineMapCityList ?: emptyList()
226
+ val cities = getOfflineMapManager().downloadOfflineMapCityList ?: emptyList()
208
227
  cities.sumOf { it.size }
209
228
  }
210
229
 
211
230
  AsyncFunction("getStorageInfo") {
212
- val cities = offlineMapManager?.downloadOfflineMapCityList ?: emptyList()
231
+ val cities = getOfflineMapManager().downloadOfflineMapCityList ?: emptyList()
213
232
  val offlineMapSize = cities.sumOf { it.size }
214
233
 
215
234
  // 获取存储路径的统计信息
@@ -231,9 +250,9 @@ class ExpoGaodeMapOfflineModule : Module() {
231
250
  }
232
251
 
233
252
  AsyncFunction("clearAllMaps") {
234
- offlineMapManager?.downloadOfflineMapCityList?.forEach { city ->
253
+ getOfflineMapManager().downloadOfflineMapCityList?.forEach { city ->
235
254
  // 使用城市名称删除
236
- offlineMapManager?.remove(city.city)
255
+ getOfflineMapManager().remove(city.city)
237
256
  }
238
257
  synchronized(lock) {
239
258
  downloadingCities.clear()
@@ -260,16 +279,16 @@ class ExpoGaodeMapOfflineModule : Module() {
260
279
  }
261
280
  }
262
281
  cityCodes.forEach { cityCode ->
263
- offlineMapManager?.downloadByCityCode(cityCode)
282
+ getOfflineMapManager().downloadByCityCode(cityCode)
264
283
  }
265
284
  }
266
285
 
267
286
  AsyncFunction("batchDelete") { cityCodes: List<String> ->
268
287
  cityCodes.forEach { cityCode ->
269
- val city = offlineMapManager?.getItemByCityCode(cityCode)
288
+ val city = getOfflineMapManager().getItemByCityCode(cityCode)
270
289
  // 使用城市名称删除,不是城市代码
271
290
  city?.city?.let { cityName ->
272
- offlineMapManager?.remove(cityName)
291
+ getOfflineMapManager().remove(cityName)
273
292
  }
274
293
  }
275
294
  synchronized(lock) {
@@ -287,18 +306,18 @@ class ExpoGaodeMapOfflineModule : Module() {
287
306
  }
288
307
  }
289
308
  cityCodes.forEach { cityCode ->
290
- offlineMapManager?.updateOfflineCityByCode(cityCode)
309
+ getOfflineMapManager().updateOfflineCityByCode(cityCode)
291
310
  }
292
311
  }
293
312
 
294
313
  AsyncFunction("pauseAllDownloads") {
295
314
  // pause() 只暂停正在下载的,不包括队列
296
- offlineMapManager?.pause()
315
+ getOfflineMapManager().pause()
297
316
 
298
317
  synchronized(lock) {
299
318
  pausedCities.addAll(downloadingCities)
300
319
  downloadingCities.forEach { cityCode ->
301
- val city = offlineMapManager?.getItemByCityCode(cityCode)
320
+ val city = getOfflineMapManager().getItemByCityCode(cityCode)
302
321
  if (city != null) {
303
322
  sendEvent("onDownloadPaused", Bundle().apply {
304
323
  putString("cityCode", cityCode)
@@ -321,7 +340,7 @@ class ExpoGaodeMapOfflineModule : Module() {
321
340
  downloadingCities.add(cityCode)
322
341
  pausedCities.remove(cityCode)
323
342
  }
324
- offlineMapManager?.downloadByCityCode(cityCode)
343
+ getOfflineMapManager().downloadByCityCode(cityCode)
325
344
  }
326
345
  }
327
346
  }
@@ -335,9 +354,10 @@ class ExpoGaodeMapOfflineModule : Module() {
335
354
  if (downName == null) return
336
355
 
337
356
  // downName 可能是城市代码或城市名称,尝试两种方式查找
338
- var city = offlineMapManager?.getItemByCityCode(downName)
357
+ val manager = offlineMapManager ?: return
358
+ var city = manager.getItemByCityCode(downName)
339
359
  if (city == null) {
340
- city = offlineMapManager?.offlineMapCityList?.find { it.city == downName }
360
+ city = manager.offlineMapCityList?.find { it.city == downName }
341
361
  }
342
362
 
343
363
  if (city == null) return
@@ -532,4 +552,4 @@ class ExpoGaodeMapOfflineModule : Module() {
532
552
  else -> "not_downloaded"
533
553
  }
534
554
  }
535
- }
555
+ }
@@ -13,6 +13,7 @@ import expo.modules.kotlin.viewevent.EventDispatcher
13
13
  import expo.modules.kotlin.views.ExpoView
14
14
  import expo.modules.gaodemap.managers.CameraManager
15
15
  import expo.modules.gaodemap.managers.UIManager
16
+ import expo.modules.gaodemap.modules.SDKInitializer
16
17
  import expo.modules.gaodemap.overlays.*
17
18
  import androidx.core.graphics.createBitmap
18
19
  import androidx.core.view.isVisible
@@ -81,9 +82,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
81
82
 
82
83
  init {
83
84
  try {
84
- // 确保隐私合规已设置
85
- MapsInitializer.updatePrivacyShow(context, true, true)
86
- MapsInitializer.updatePrivacyAgree(context, true)
85
+ SDKInitializer.applyPrivacyState(context)
87
86
 
88
87
  // 创建地图视图 - 使用 TextureMapView 以支持截图
89
88
  mapView = TextureMapView(context)
@@ -14,24 +14,115 @@ import com.amap.api.maps.MapsInitializer
14
14
  * - 获取 SDK 版本信息
15
15
  */
16
16
  object SDKInitializer {
17
-
18
- /** 隐私协议是否已同意(进程内缓存) */
19
- private var privacyAgreed = true
17
+ private const val PREFS_NAME = "expo_gaodemap_privacy"
18
+ private const val KEY_PRIVACY_SHOWN = "privacy_shown"
19
+ private const val KEY_PRIVACY_CONTAINS = "privacy_contains"
20
+ private const val KEY_PRIVACY_AGREED = "privacy_agreed"
21
+ private const val KEY_PRIVACY_VERSION = "privacy_version"
22
+ private const val KEY_AGREED_PRIVACY_VERSION = "agreed_privacy_version"
20
23
 
24
+ private var privacyAgreed = false
25
+ private var privacyShown = false
26
+ private var privacyContains = false
27
+ private var privacyVersion: String? = null
28
+ private var agreedPrivacyVersion: String? = null
29
+ private var restoredFromStorage = false
21
30
 
22
-
23
- fun restorePrivacyState(context: Context) {
31
+ private fun resolveContext(context: Context): Context {
32
+ return context.applicationContext ?: context
33
+ }
34
+
35
+ private fun prefs(context: Context) =
36
+ resolveContext(context).getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
37
+
38
+ fun restorePersistedState(context: Context) {
39
+ val appContext = resolveContext(context)
40
+ val preferences = prefs(appContext)
41
+
42
+ privacyShown = preferences.getBoolean(KEY_PRIVACY_SHOWN, false)
43
+ privacyContains = preferences.getBoolean(KEY_PRIVACY_CONTAINS, false)
44
+ privacyAgreed = preferences.getBoolean(KEY_PRIVACY_AGREED, false)
45
+ privacyVersion = preferences.getString(KEY_PRIVACY_VERSION, null)
46
+ agreedPrivacyVersion = preferences.getString(KEY_AGREED_PRIVACY_VERSION, null)
47
+ restoredFromStorage = true
48
+
49
+ if (!privacyVersion.isNullOrEmpty() &&
50
+ !agreedPrivacyVersion.isNullOrEmpty() &&
51
+ privacyVersion != agreedPrivacyVersion
52
+ ) {
53
+ clearConsentPersistedState(appContext, keepCurrentVersion = true)
54
+ }
55
+
56
+ applyPrivacyState(appContext)
57
+ }
58
+
59
+ fun setPrivacyShow(context: Context, hasShow: Boolean, hasContainsPrivacy: Boolean) {
60
+ privacyShown = hasShow
61
+ privacyContains = hasContainsPrivacy
62
+ val appContext = resolveContext(context)
63
+ persistState(appContext)
64
+ applyPrivacyState(appContext)
65
+ }
66
+
67
+ fun setPrivacyAgree(context: Context, hasAgree: Boolean) {
68
+ privacyAgreed = hasAgree
69
+ agreedPrivacyVersion = if (hasAgree) privacyVersion else null
70
+ val appContext = resolveContext(context)
71
+ persistState(appContext)
72
+ applyPrivacyState(appContext)
73
+ }
74
+
75
+ fun setPrivacyVersion(context: Context, version: String) {
76
+ val sanitizedVersion = version.trim().takeIf { it.isNotEmpty() }
77
+ privacyVersion = sanitizedVersion
78
+
79
+ val appContext = resolveContext(context)
80
+ if (!privacyVersion.isNullOrEmpty() &&
81
+ !agreedPrivacyVersion.isNullOrEmpty() &&
82
+ privacyVersion != agreedPrivacyVersion
83
+ ) {
84
+ clearConsentPersistedState(appContext, keepCurrentVersion = true)
85
+ } else {
86
+ persistState(appContext)
87
+ }
88
+
89
+ applyPrivacyState(appContext)
90
+ }
91
+
92
+ fun resetPrivacyConsent(context: Context) {
93
+ val appContext = resolveContext(context)
94
+ clearConsentPersistedState(appContext, keepCurrentVersion = false)
95
+ applyPrivacyState(appContext)
96
+ }
97
+
98
+ fun applyPrivacyState(context: Context) {
99
+ val appContext = resolveContext(context)
24
100
  try {
25
- // 同步到 SDK
26
- MapsInitializer.updatePrivacyShow(context, true, true)
27
- AMapLocationClient.updatePrivacyShow(context, true, true)
28
- MapsInitializer.updatePrivacyAgree(context, privacyAgreed)
29
- AMapLocationClient.updatePrivacyAgree(context, privacyAgreed)
101
+ MapsInitializer.updatePrivacyShow(appContext, privacyShown, privacyContains)
102
+ AMapLocationClient.updatePrivacyShow(appContext, privacyShown, privacyContains)
103
+ MapsInitializer.updatePrivacyAgree(appContext, privacyAgreed)
104
+ AMapLocationClient.updatePrivacyAgree(appContext, privacyAgreed)
30
105
  } catch (e: Exception) {
31
- android.util.Log.w("ExpoGaodeMap", "恢复隐私状态失败: ${e.message}")
106
+ android.util.Log.w("ExpoGaodeMap", "同步隐私状态失败: ${e.message}")
32
107
  }
33
108
  }
34
109
 
110
+ fun isPrivacyReady(): Boolean {
111
+ return privacyShown && privacyContains && privacyAgreed
112
+ }
113
+
114
+ fun getPrivacyStatus(): Map<String, Any?> {
115
+ return mapOf(
116
+ "hasShow" to privacyShown,
117
+ "hasContainsPrivacy" to privacyContains,
118
+ "hasAgree" to privacyAgreed,
119
+ "isReady" to isPrivacyReady(),
120
+ "privacyVersion" to privacyVersion,
121
+ "agreedPrivacyVersion" to agreedPrivacyVersion,
122
+ "restoredFromStorage" to restoredFromStorage,
123
+ )
124
+ }
125
+
35
126
  /**
36
127
  * 初始化高德地图和定位 SDK
37
128
  *
@@ -40,13 +131,19 @@ object SDKInitializer {
40
131
  * @throws Exception 初始化失败时抛出异常
41
132
  */
42
133
  fun initSDK(context: Context, androidKey: String) {
134
+ val appContext = resolveContext(context)
135
+ restorePersistedState(appContext)
43
136
  // 检查隐私协议状态
44
- if (!privacyAgreed) {
45
- // 使用 Kotlin 模块的 CodedException,让 JS 能收到标准化异常
46
- throw expo.modules.kotlin.exception.CodedException("用户未同意隐私协议,无法初始化 SDK")
137
+ if (!isPrivacyReady()) {
138
+ throw expo.modules.kotlin.exception.CodedException(
139
+ "PRIVACY_NOT_AGREED",
140
+ "隐私协议未完成确认,请先调用 setPrivacyShow/setPrivacyAgree",
141
+ null
142
+ )
47
143
  }
48
144
 
49
145
  try {
146
+ applyPrivacyState(appContext)
50
147
  // 设置 API Key
51
148
  MapsInitializer.setApiKey(androidKey)
52
149
  AMapLocationClient.setApiKey(androidKey)
@@ -65,4 +162,36 @@ object SDKInitializer {
65
162
  fun getVersion(): String {
66
163
  return MapsInitializer.getVersion()
67
164
  }
165
+
166
+ private fun persistState(context: Context) {
167
+ prefs(context).edit()
168
+ .putBoolean(KEY_PRIVACY_SHOWN, privacyShown)
169
+ .putBoolean(KEY_PRIVACY_CONTAINS, privacyContains)
170
+ .putBoolean(KEY_PRIVACY_AGREED, privacyAgreed)
171
+ .putString(KEY_PRIVACY_VERSION, privacyVersion)
172
+ .putString(KEY_AGREED_PRIVACY_VERSION, agreedPrivacyVersion)
173
+ .apply()
174
+ }
175
+
176
+ private fun clearConsentPersistedState(context: Context, keepCurrentVersion: Boolean) {
177
+ privacyShown = false
178
+ privacyContains = false
179
+ privacyAgreed = false
180
+ agreedPrivacyVersion = null
181
+
182
+ val editor = prefs(context).edit()
183
+ .putBoolean(KEY_PRIVACY_SHOWN, false)
184
+ .putBoolean(KEY_PRIVACY_CONTAINS, false)
185
+ .putBoolean(KEY_PRIVACY_AGREED, false)
186
+ .remove(KEY_AGREED_PRIVACY_VERSION)
187
+
188
+ if (keepCurrentVersion) {
189
+ editor.putString(KEY_PRIVACY_VERSION, privacyVersion)
190
+ } else {
191
+ privacyVersion = null
192
+ editor.remove(KEY_PRIVACY_VERSION)
193
+ }
194
+
195
+ editor.apply()
196
+ }
68
197
  }