expo-gaode-map 2.2.26-next.1 → 2.2.26-next.2
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 +10 -1
- package/android/build.gradle +3 -0
- package/android/src/main/cpp/CMakeLists.txt +2 -0
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +33 -53
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapOfflineModule.kt +70 -50
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +36 -63
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +13 -3
- package/android/src/main/java/expo/modules/gaodemap/modules/SDKInitializer.kt +41 -14
- package/build/ExpoGaodeMapModule.d.ts.map +1 -1
- package/build/ExpoGaodeMapModule.js +90 -15
- package/build/ExpoGaodeMapModule.js.map +1 -1
- package/build/ExpoGaodeMapOfflineModule.d.ts.map +1 -1
- package/build/ExpoGaodeMapOfflineModule.js +12 -2
- package/build/ExpoGaodeMapOfflineModule.js.map +1 -1
- package/build/ExpoGaodeMapView.d.ts.map +1 -1
- package/build/ExpoGaodeMapView.js +3 -2
- package/build/ExpoGaodeMapView.js.map +1 -1
- package/build/components/overlays/Circle.d.ts.map +1 -1
- package/build/components/overlays/Circle.js +3 -2
- package/build/components/overlays/Circle.js.map +1 -1
- package/build/components/overlays/Cluster.d.ts.map +1 -1
- package/build/components/overlays/Cluster.js +3 -2
- package/build/components/overlays/Cluster.js.map +1 -1
- package/build/components/overlays/HeatMap.d.ts.map +1 -1
- package/build/components/overlays/HeatMap.js +3 -2
- package/build/components/overlays/HeatMap.js.map +1 -1
- package/build/components/overlays/Marker.d.ts.map +1 -1
- package/build/components/overlays/Marker.js +3 -2
- package/build/components/overlays/Marker.js.map +1 -1
- package/build/components/overlays/MultiPoint.d.ts.map +1 -1
- package/build/components/overlays/MultiPoint.js +3 -2
- package/build/components/overlays/MultiPoint.js.map +1 -1
- package/build/components/overlays/Polygon.d.ts.map +1 -1
- package/build/components/overlays/Polygon.js +3 -2
- package/build/components/overlays/Polygon.js.map +1 -1
- package/build/components/overlays/Polyline.d.ts.map +1 -1
- package/build/components/overlays/Polyline.js +3 -2
- package/build/components/overlays/Polyline.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/types/common.types.d.ts +9 -0
- package/build/types/common.types.d.ts.map +1 -1
- package/build/types/common.types.js.map +1 -1
- package/build/types/native-module.types.d.ts +17 -1
- package/build/types/native-module.types.d.ts.map +1 -1
- package/build/types/native-module.types.js.map +1 -1
- package/build/utils/lazyNativeViewManager.d.ts +3 -0
- package/build/utils/lazyNativeViewManager.d.ts.map +1 -0
- package/build/utils/lazyNativeViewManager.js +11 -0
- package/build/utils/lazyNativeViewManager.js.map +1 -0
- package/build/utils/throttle.d.ts +10 -0
- package/build/utils/throttle.d.ts.map +1 -0
- package/build/utils/throttle.js +19 -0
- package/build/utils/throttle.js.map +1 -0
- package/ios/ExpoGaodeMap.podspec +5 -4
- package/ios/ExpoGaodeMapModule.swift +17 -43
- package/ios/ExpoGaodeMapView.swift +171 -113
- package/ios/ExpoGaodeMapViewModule.swift +10 -2
- package/ios/GaodeMapPrivacyManager.swift +65 -0
- package/ios/modules/LocationManager.swift +43 -24
- package/package.json +7 -11
- package/plugin/build/withGaodeMap.d.ts +1 -1
- package/plugin/build/withGaodeMap.js +18 -10
- package/android/src/main/java/expo/modules/gaodemap/MapPreloadManager.kt +0 -494
- package/ios/MapPreloadManager.swift +0 -348
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
- ✅ 高测试覆盖率(75.7%,207 个单元测试)
|
|
31
31
|
- ✅ 支持自定义Marker覆盖物
|
|
32
32
|
- ✅ 默认实现了优化地图加载,减少内存占用
|
|
33
|
-
- ✅
|
|
33
|
+
- ✅ 几何运算(距离/面积、点在圆/多边形、质心/边界、路径长度/抽稀、GeoHash、瓦片/像素坐标转换、最近点、热力网格等,由 C++ 实现)
|
|
34
34
|
- ✅ 丰富的使用案例
|
|
35
35
|
- ✅ 提供AI编程助手,帮助开发者快速集成和使用(https://TomWq.github.io/expo-gaode-map/guide/ai-skills.html)
|
|
36
36
|
- ✅ 更多内容和功能请查看 [完整文档](https://TomWq.github.io/expo-gaode-map/)
|
|
@@ -44,6 +44,14 @@
|
|
|
44
44
|
|
|
45
45
|
## 📦 安装
|
|
46
46
|
|
|
47
|
+
> ⚠️ **版本兼容性说明**:
|
|
48
|
+
> - 如果你的项目使用 **Expo SDK 54 及以上**,请安装 默认的 版本。
|
|
49
|
+
> - 如果你的项目使用 **Expo SDK 53 及以下**(如 50, 51, 52, 53),请使用 **V1** 版本(Tag: `v1`)。
|
|
50
|
+
> ```bash
|
|
51
|
+
> npm install expo-gaode-map@v1
|
|
52
|
+
> ```
|
|
53
|
+
> **说明**:V1 版本除了不支持**世界地图**功能外,其余 API 与 V2 (Latest) 版本完全一致。
|
|
54
|
+
|
|
47
55
|
### 方案一:仅使用地图和定位(核心包)
|
|
48
56
|
|
|
49
57
|
```bash
|
|
@@ -222,6 +230,7 @@ MIT
|
|
|
222
230
|
|
|
223
231
|
- [在线文档](https://TomWq.github.io/expo-gaode-map/)
|
|
224
232
|
- [错误处理指南](./ERROR_HANDLING_GUIDE.md) 🆕
|
|
233
|
+
- [性能优化指南](./PERFORMANCE_GUIDE.md) 🆕
|
|
225
234
|
- [GitHub 仓库](https://github.com/TomWq/expo-gaode-map)
|
|
226
235
|
- [示例项目(地图)](https://github.com/TomWq/expo-gaode-map-example)
|
|
227
236
|
- [示例项目(导航)](https://github.com/TomWq/expo-gaode-map-navigation-example)
|
package/android/build.gradle
CHANGED
|
@@ -31,6 +31,9 @@ if (useManagedAndroidSdkVersions) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
logger.warn("[expo-gaode-map] Thank you for using expo-gaode-map ❤️")
|
|
35
|
+
logger.warn("[expo-gaode-map] If you enjoy using expo-gaode-map, please consider sponsoring this project: https://github.com/TomWq")
|
|
36
|
+
|
|
34
37
|
android {
|
|
35
38
|
namespace "expo.modules.gaodemap"
|
|
36
39
|
defaultConfig {
|
|
@@ -30,42 +30,6 @@ 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
|
-
// 初始化预加载管理器(注册内存监听)
|
|
39
|
-
MapPreloadManager.initialize(context)
|
|
40
|
-
|
|
41
|
-
// 尝试从 AndroidManifest.xml 读取并设置 API Key
|
|
42
|
-
val apiKey = context.packageManager
|
|
43
|
-
.getApplicationInfo(context.packageName, android.content.pm.PackageManager.GET_META_DATA)
|
|
44
|
-
.metaData?.getString("com.amap.api.v2.apikey")
|
|
45
|
-
|
|
46
|
-
if (!apiKey.isNullOrEmpty()) {
|
|
47
|
-
try {
|
|
48
|
-
MapsInitializer.setApiKey(apiKey)
|
|
49
|
-
com.amap.api.location.AMapLocationClient.setApiKey(apiKey)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// 只有在 API Key 已设置的情况下才启动预加载
|
|
53
|
-
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
54
|
-
android.util.Log.i("ExpoGaodeMap", "🚀 自动启动地图预加载")
|
|
55
|
-
MapPreloadManager.startPreload(context, poolSize = 1)
|
|
56
|
-
}, 2000)
|
|
57
|
-
} catch (e: Exception) {
|
|
58
|
-
android.util.Log.w("ExpoGaodeMap", "设置 API Key 失败: ${e.message}")
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
android.util.Log.w("ExpoGaodeMap", "⚠️ AndroidManifest.xml 未找到 API Key,跳过自动预加载")
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
} catch (e: Exception) {
|
|
65
|
-
android.util.Log.w("ExpoGaodeMap", "恢复隐私状态时出现问题: ${e.message}")
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
33
|
// ==================== SDK 初始化 ====================
|
|
70
34
|
|
|
71
35
|
/**
|
|
@@ -74,26 +38,43 @@ class ExpoGaodeMapModule : Module() {
|
|
|
74
38
|
*/
|
|
75
39
|
Function("initSDK") { config: Map<String, String> ->
|
|
76
40
|
val androidKey = config["androidKey"]
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
throw expo.modules.kotlin.exception.CodedException("PRIVACY_NOT_AGREED", e.message ?: "用户未同意隐私协议", e)
|
|
90
|
-
} catch (e: Exception) {
|
|
91
|
-
android.util.Log.e("ExpoGaodeMap", "SDK 初始化失败: ${e.message}")
|
|
92
|
-
throw expo.modules.kotlin.exception.CodedException("INIT_FAILED", e.message ?: "SDK 初始化失败", e)
|
|
41
|
+
try {
|
|
42
|
+
val context = appContext.reactContext!!
|
|
43
|
+
if (androidKey != null) {
|
|
44
|
+
SDKInitializer.initSDK(context, androidKey)
|
|
45
|
+
} else if (!SDKInitializer.isPrivacyReady()) {
|
|
46
|
+
throw expo.modules.kotlin.exception.CodedException(
|
|
47
|
+
"PRIVACY_NOT_AGREED",
|
|
48
|
+
"隐私协议未完成确认,请先调用 setPrivacyShow/setPrivacyAgree",
|
|
49
|
+
null
|
|
50
|
+
)
|
|
51
|
+
} else {
|
|
52
|
+
SDKInitializer.applyPrivacyState(context)
|
|
93
53
|
}
|
|
54
|
+
|
|
55
|
+
getLocationManager()
|
|
56
|
+
android.util.Log.i("ExpoGaodeMap", "✅ SDK 初始化完成 - Version: ${MapsInitializer.getVersion()}")
|
|
57
|
+
} catch (e: SecurityException) {
|
|
58
|
+
android.util.Log.e("ExpoGaodeMap", "隐私协议未同意: ${e.message}")
|
|
59
|
+
throw expo.modules.kotlin.exception.CodedException("PRIVACY_NOT_AGREED", e.message ?: "用户未同意隐私协议", e)
|
|
60
|
+
} catch (e: Exception) {
|
|
61
|
+
android.util.Log.e("ExpoGaodeMap", "SDK 初始化失败: ${e.message}")
|
|
62
|
+
throw expo.modules.kotlin.exception.CodedException("INIT_FAILED", e.message ?: "SDK 初始化失败", e)
|
|
94
63
|
}
|
|
95
64
|
}
|
|
96
65
|
|
|
66
|
+
Function("setPrivacyShow") { hasShow: Boolean, hasContainsPrivacy: Boolean ->
|
|
67
|
+
SDKInitializer.setPrivacyShow(appContext.reactContext!!, hasShow, hasContainsPrivacy)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Function("setPrivacyAgree") { hasAgree: Boolean ->
|
|
71
|
+
SDKInitializer.setPrivacyAgree(appContext.reactContext!!, hasAgree)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Function("getPrivacyStatus") {
|
|
75
|
+
SDKInitializer.getPrivacyStatus()
|
|
76
|
+
}
|
|
77
|
+
|
|
97
78
|
/**
|
|
98
79
|
* 设置是否加载世界向量地图
|
|
99
80
|
* @param enable 是否开启
|
|
@@ -893,7 +874,6 @@ class ExpoGaodeMapModule : Module() {
|
|
|
893
874
|
OnDestroy {
|
|
894
875
|
locationManager?.destroy()
|
|
895
876
|
locationManager = null
|
|
896
|
-
MapPreloadManager.cleanup()
|
|
897
877
|
}
|
|
898
878
|
}
|
|
899
879
|
|
|
@@ -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 =
|
|
86
|
+
val cities = getOfflineMapManager().offlineMapCityList ?: emptyList()
|
|
68
87
|
cities.map { city -> convertCityToMap(city) }
|
|
69
88
|
}
|
|
70
89
|
|
|
71
90
|
AsyncFunction("getAvailableProvinces") {
|
|
72
|
-
val provinces =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
117
|
+
getOfflineMapManager().downloadByCityCode(cityCode)
|
|
99
118
|
}
|
|
100
119
|
|
|
101
120
|
AsyncFunction("pauseDownload") { cityCode: String ->
|
|
102
|
-
val city =
|
|
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
|
-
|
|
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
|
-
|
|
148
|
+
getOfflineMapManager().downloadByCityCode(cityCode)
|
|
130
149
|
}
|
|
131
150
|
|
|
132
151
|
AsyncFunction("cancelDownload") { cityCode: String ->
|
|
133
|
-
val city =
|
|
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
|
-
|
|
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 =
|
|
171
|
+
val city = getOfflineMapManager().getItemByCityCode(cityCode)
|
|
153
172
|
?: throw IllegalArgumentException("City not found: $cityCode")
|
|
154
173
|
|
|
155
174
|
// 官方文档:remove() 需要传入城市名称,不是城市代码
|
|
156
|
-
|
|
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
|
-
|
|
187
|
+
getOfflineMapManager().updateOfflineCityByCode(cityCode)
|
|
169
188
|
}
|
|
170
189
|
|
|
171
190
|
AsyncFunction("checkUpdate") { cityCode: String ->
|
|
172
|
-
val city =
|
|
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 =
|
|
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 =
|
|
204
|
+
val city = getOfflineMapManager().getItemByCityCode(cityCode)
|
|
186
205
|
city?.let { convertCityToMap(it) } ?: Bundle()
|
|
187
206
|
}
|
|
188
207
|
|
|
189
208
|
AsyncFunction("getTotalProgress") {
|
|
190
|
-
val downloadedCities =
|
|
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 =
|
|
226
|
+
val cities = getOfflineMapManager().downloadOfflineMapCityList ?: emptyList()
|
|
208
227
|
cities.sumOf { it.size }
|
|
209
228
|
}
|
|
210
229
|
|
|
211
230
|
AsyncFunction("getStorageInfo") {
|
|
212
|
-
val cities =
|
|
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
|
-
|
|
253
|
+
getOfflineMapManager().downloadOfflineMapCityList?.forEach { city ->
|
|
235
254
|
// 使用城市名称删除
|
|
236
|
-
|
|
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
|
-
|
|
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 =
|
|
288
|
+
val city = getOfflineMapManager().getItemByCityCode(cityCode)
|
|
270
289
|
// 使用城市名称删除,不是城市代码
|
|
271
290
|
city?.city?.let { cityName ->
|
|
272
|
-
|
|
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
|
-
|
|
309
|
+
getOfflineMapManager().updateOfflineCityByCode(cityCode)
|
|
291
310
|
}
|
|
292
311
|
}
|
|
293
312
|
|
|
294
313
|
AsyncFunction("pauseAllDownloads") {
|
|
295
314
|
// pause() 只暂停正在下载的,不包括队列
|
|
296
|
-
|
|
315
|
+
getOfflineMapManager().pause()
|
|
297
316
|
|
|
298
317
|
synchronized(lock) {
|
|
299
318
|
pausedCities.addAll(downloadingCities)
|
|
300
319
|
downloadingCities.forEach { cityCode ->
|
|
301
|
-
val city =
|
|
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
|
-
|
|
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
|
-
|
|
357
|
+
val manager = offlineMapManager ?: return
|
|
358
|
+
var city = manager.getItemByCityCode(downName)
|
|
339
359
|
if (city == null) {
|
|
340
|
-
city =
|
|
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
|
+
}
|
|
@@ -5,7 +5,7 @@ import android.content.Context
|
|
|
5
5
|
import android.view.View
|
|
6
6
|
import android.view.ViewGroup
|
|
7
7
|
import com.amap.api.maps.AMap
|
|
8
|
-
import com.amap.api.maps.
|
|
8
|
+
import com.amap.api.maps.TextureMapView
|
|
9
9
|
import com.amap.api.maps.MapsInitializer
|
|
10
10
|
import com.amap.api.maps.model.LatLng
|
|
11
11
|
import expo.modules.kotlin.AppContext
|
|
@@ -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
|
|
@@ -64,23 +65,11 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
64
65
|
private val onCameraMove by EventDispatcher()
|
|
65
66
|
private val onCameraIdle by EventDispatcher()
|
|
66
67
|
|
|
67
|
-
//
|
|
68
|
-
/** 相机移动事件节流间隔(毫秒) */
|
|
69
|
-
private val CAMERA_MOVE_THROTTLE_MS = 100L
|
|
70
|
-
/** 上次触发相机移动事件的时间戳 */
|
|
71
|
-
private var lastCameraMoveTime = 0L
|
|
72
|
-
/** 缓存的相机移动事件数据 */
|
|
68
|
+
// 缓存的相机移动事件数据
|
|
73
69
|
private var pendingCameraMoveData: Map<String, Any>? = null
|
|
74
|
-
/** 节流定时器 Runnable */
|
|
75
|
-
private val throttleRunnable = Runnable {
|
|
76
|
-
pendingCameraMoveData?.let { data ->
|
|
77
|
-
onCameraMove(data)
|
|
78
|
-
pendingCameraMoveData = null
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
70
|
|
|
82
71
|
// 高德地图视图
|
|
83
|
-
private lateinit var mapView:
|
|
72
|
+
private lateinit var mapView: TextureMapView
|
|
84
73
|
private lateinit var aMap: AMap
|
|
85
74
|
|
|
86
75
|
// 管理器
|
|
@@ -93,22 +82,11 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
93
82
|
|
|
94
83
|
init {
|
|
95
84
|
try {
|
|
96
|
-
|
|
97
|
-
MapsInitializer.updatePrivacyShow(context, true, true)
|
|
98
|
-
MapsInitializer.updatePrivacyAgree(context, true)
|
|
85
|
+
SDKInitializer.applyPrivacyState(context)
|
|
99
86
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (preloadedMapView != null) {
|
|
104
|
-
mapView = preloadedMapView
|
|
105
|
-
android.util.Log.i("ExpoGaodeMapView", "🚀 使用预加载的 MapView 实例")
|
|
106
|
-
} else {
|
|
107
|
-
// 创建地图视图
|
|
108
|
-
mapView = MapView(context)
|
|
109
|
-
mapView.onCreate(null)
|
|
110
|
-
android.util.Log.i("ExpoGaodeMapView", "⚠️ 创建新的 MapView 实例 (未命中预加载池)")
|
|
111
|
-
}
|
|
87
|
+
// 创建地图视图 - 使用 TextureMapView 以支持截图
|
|
88
|
+
mapView = TextureMapView(context)
|
|
89
|
+
mapView.onCreate(null)
|
|
112
90
|
|
|
113
91
|
aMap = mapView.map
|
|
114
92
|
|
|
@@ -184,7 +162,6 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
184
162
|
|
|
185
163
|
// 相机移动中 - 应用节流优化
|
|
186
164
|
cameraPosition?.let {
|
|
187
|
-
val currentTime = System.currentTimeMillis()
|
|
188
165
|
val visibleRegion = aMap.projection.visibleRegion
|
|
189
166
|
val eventData = mapOf(
|
|
190
167
|
"cameraPosition" to mapOf(
|
|
@@ -208,23 +185,16 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
208
185
|
)
|
|
209
186
|
)
|
|
210
187
|
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
pendingCameraMoveData = eventData
|
|
222
|
-
mainHandler.removeCallbacks(throttleRunnable)
|
|
223
|
-
mainHandler.postDelayed(
|
|
224
|
-
throttleRunnable,
|
|
225
|
-
CAMERA_MOVE_THROTTLE_MS - (currentTime - lastCameraMoveTime)
|
|
226
|
-
)
|
|
227
|
-
}
|
|
188
|
+
// 使用 onCameraMove 自身的节流机制(如果在 Module 定义中配置了 Coalescing)
|
|
189
|
+
// 或者在这里简单发送,让 JS 端处理节流,或者依赖 Expo 的事件批处理
|
|
190
|
+
// 这里我们移除自定义的 Handler 实现,直接发送事件,简化代码逻辑
|
|
191
|
+
// 注意:高德地图的 onCameraChange 调用频率非常高,
|
|
192
|
+
// 建议在 Module 定义中使用 Events("onCameraMove") 时考虑是否需要原生侧节流
|
|
193
|
+
// 目前 Expo Modules 默认没有自动节流,但为了代码简洁和避免 Handler 泄漏风险,
|
|
194
|
+
// 我们可以依赖 JS 端的 debounce/throttle,或者如果性能是瓶颈,再加回轻量级的节流。
|
|
195
|
+
// 鉴于之前的 Handler 实现比较复杂且容易出错,我们先简化。
|
|
196
|
+
|
|
197
|
+
onCameraMove(eventData)
|
|
228
198
|
}
|
|
229
199
|
}
|
|
230
200
|
|
|
@@ -493,27 +463,34 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
493
463
|
|
|
494
464
|
aMap.getMapScreenShot(object : AMap.OnMapScreenShotListener {
|
|
495
465
|
override fun onMapScreenShot(bitmap: android.graphics.Bitmap?) {
|
|
496
|
-
//
|
|
466
|
+
// 这个回调通常在旧版 SDK 或部分机型触发
|
|
467
|
+
// 如果已经处理过(通过带 status 的回调),则忽略
|
|
497
468
|
if (isSettled.getAndSet(true)) return
|
|
498
469
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
470
|
+
if (bitmap == null) {
|
|
471
|
+
promise.reject("SNAPSHOT_FAILED", "Bitmap is null", null)
|
|
472
|
+
return
|
|
502
473
|
}
|
|
474
|
+
handleSnapshot(bitmap, promise)
|
|
503
475
|
}
|
|
504
476
|
|
|
505
477
|
override fun onMapScreenShot(bitmap: android.graphics.Bitmap?, status: Int) {
|
|
506
478
|
// 如果已经处理过,直接返回
|
|
507
479
|
if (isSettled.getAndSet(true)) return
|
|
508
480
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
promise.reject("SNAPSHOT_FAILED", "Failed to take snapshot, status code: $status", null)
|
|
481
|
+
if (bitmap == null) {
|
|
482
|
+
promise.reject("SNAPSHOT_FAILED", "Bitmap is null", null)
|
|
512
483
|
return
|
|
513
484
|
}
|
|
514
|
-
|
|
515
|
-
|
|
485
|
+
|
|
486
|
+
// 根据高德文档:
|
|
487
|
+
// status != 0 地图渲染完成,截屏无网格
|
|
488
|
+
// status == 0 地图未渲染完成,截屏有网格
|
|
489
|
+
if (status == 0) {
|
|
490
|
+
android.util.Log.w("ExpoGaodeMapView", "Warning: Map snapshot taken before rendering completed (grid may be visible)")
|
|
516
491
|
}
|
|
492
|
+
|
|
493
|
+
handleSnapshot(bitmap, promise)
|
|
517
494
|
}
|
|
518
495
|
})
|
|
519
496
|
}
|
|
@@ -601,10 +578,6 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
601
578
|
@Suppress("unused")
|
|
602
579
|
fun onDestroy() {
|
|
603
580
|
try {
|
|
604
|
-
// 清理节流定时器
|
|
605
|
-
mainHandler.removeCallbacks(throttleRunnable)
|
|
606
|
-
pendingCameraMoveData = null
|
|
607
|
-
|
|
608
581
|
// 清理 Handler 回调,防止内存泄露
|
|
609
582
|
mainHandler.removeCallbacksAndMessages(null)
|
|
610
583
|
|
|
@@ -654,7 +627,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
654
627
|
return
|
|
655
628
|
}
|
|
656
629
|
|
|
657
|
-
if (child is
|
|
630
|
+
if (child is TextureMapView) {
|
|
658
631
|
super.addView(child, index)
|
|
659
632
|
return
|
|
660
633
|
}
|
|
@@ -702,7 +675,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
702
675
|
try {
|
|
703
676
|
val child = super.getChildAt(index)
|
|
704
677
|
|
|
705
|
-
if (child is
|
|
678
|
+
if (child is TextureMapView) {
|
|
706
679
|
return
|
|
707
680
|
}
|
|
708
681
|
|
|
@@ -3,6 +3,16 @@ package expo.modules.gaodemap
|
|
|
3
3
|
import expo.modules.kotlin.modules.Module
|
|
4
4
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
5
5
|
|
|
6
|
+
import expo.modules.kotlin.types.Enumerable
|
|
7
|
+
|
|
8
|
+
enum class MapType(val value: Int) : Enumerable {
|
|
9
|
+
STANDARD(1),
|
|
10
|
+
SATELLITE(2),
|
|
11
|
+
NIGHT(3),
|
|
12
|
+
NAVI(4),
|
|
13
|
+
BUS(5)
|
|
14
|
+
}
|
|
15
|
+
|
|
6
16
|
/**
|
|
7
17
|
* 高德地图视图 Module
|
|
8
18
|
*/
|
|
@@ -24,9 +34,9 @@ class ExpoGaodeMapViewModule : Module() {
|
|
|
24
34
|
}
|
|
25
35
|
|
|
26
36
|
|
|
27
|
-
Prop<
|
|
28
|
-
view.mapType = type
|
|
29
|
-
view.setMapType(type)
|
|
37
|
+
Prop<MapType>("mapType") { view, type ->
|
|
38
|
+
view.mapType = type.value
|
|
39
|
+
view.setMapType(type.value)
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
Prop<Map<String, Any?>?>("initialCameraPosition") { view, position ->
|