expo-gaode-map 2.2.33 → 2.2.35-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.
- package/README.md +5 -5
- package/android/build.gradle +8 -4
- package/android/src/main/AndroidManifest.xml +14 -0
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +7 -8
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapOfflineModule.kt +150 -27
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +24 -14
- package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +38 -41
- package/android/src/main/java/expo/modules/gaodemap/modules/SDKInitializer.kt +18 -17
- package/android/src/main/java/expo/modules/gaodemap/overlays/CircleView.kt +3 -1
- package/android/src/main/java/expo/modules/gaodemap/overlays/ClusterView.kt +6 -1
- package/android/src/main/java/expo/modules/gaodemap/overlays/HeatMapView.kt +124 -10
- package/android/src/main/java/expo/modules/gaodemap/overlays/HeatMapViewModule.kt +2 -2
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerBitmapRenderer.kt +10 -9
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +7 -11
- package/android/src/main/java/expo/modules/gaodemap/overlays/MultiPointView.kt +3 -1
- package/android/src/main/java/expo/modules/gaodemap/overlays/PolygonView.kt +2 -1
- package/android/src/main/java/expo/modules/gaodemap/overlays/PolylineView.kt +1 -0
- package/android/src/main/java/expo/modules/gaodemap/search/ExpoGaodeMapSearchModule.kt +751 -0
- package/android/src/main/java/expo/modules/gaodemap/utils/GeometryUtils.kt +5 -5
- package/android/src/main/java/expo/modules/gaodemap/utils/PermissionHelper.kt +13 -16
- package/build/ExpoGaodeMapOfflineModule.d.ts +5 -0
- package/build/ExpoGaodeMapOfflineModule.d.ts.map +1 -1
- package/build/ExpoGaodeMapOfflineModule.js.map +1 -1
- package/build/components/overlays/HeatMap.d.ts.map +1 -1
- package/build/components/overlays/HeatMap.js +21 -2
- package/build/components/overlays/HeatMap.js.map +1 -1
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -0
- package/build/index.js.map +1 -1
- package/build/search/ExpoGaodeMapSearch.types.d.ts +340 -0
- package/build/search/ExpoGaodeMapSearch.types.d.ts.map +1 -0
- package/build/search/ExpoGaodeMapSearch.types.js +19 -0
- package/build/search/ExpoGaodeMapSearch.types.js.map +1 -0
- package/build/search/ExpoGaodeMapSearchModule.d.ts +74 -0
- package/build/search/ExpoGaodeMapSearchModule.d.ts.map +1 -0
- package/build/search/ExpoGaodeMapSearchModule.js +47 -0
- package/build/search/ExpoGaodeMapSearchModule.js.map +1 -0
- package/build/search/index.d.ts +156 -0
- package/build/search/index.d.ts.map +1 -0
- package/build/search/index.js +171 -0
- package/build/search/index.js.map +1 -0
- package/build/types/map-view.types.d.ts +4 -2
- package/build/types/map-view.types.d.ts.map +1 -1
- package/build/types/map-view.types.js.map +1 -1
- package/build/utils/OfflineMapManager.d.ts +4 -0
- package/build/utils/OfflineMapManager.d.ts.map +1 -1
- package/build/utils/OfflineMapManager.js +6 -0
- package/build/utils/OfflineMapManager.js.map +1 -1
- package/expo-module.config.json +4 -2
- package/ios/ExpoGaodeMap.podspec +2 -2
- package/ios/ExpoGaodeMapOfflineModule.swift +60 -0
- package/ios/ExpoGaodeMapSearchModule.swift +773 -0
- package/ios/modules/LocationManager.swift +9 -3
- package/ios/overlays/PolylineView.swift +6 -12
- package/package.json +1 -1
- package/plugin/build/withGaodeMap.js +12 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## 📖 完整文档
|
|
8
8
|
|
|
9
|
-
**👉 [在线文档网站](https://TomWq.github.io/expo-gaode-map/)** · **👉 [
|
|
9
|
+
**👉 [在线文档网站](https://TomWq.github.io/expo-gaode-map/)** · **👉 本地示例:[`example/`](../../example) / [`example-navigation/`](../../example-navigation)**
|
|
10
10
|
|
|
11
11
|
包含完整的 API 文档、使用指南和示例代码:
|
|
12
12
|
- [快速开始](https://TomWq.github.io/expo-gaode-map/guide/getting-started.html)
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
- [导航功能](https://TomWq.github.io/expo-gaode-map/guide/navigation.html)
|
|
16
16
|
- [Web API](https://TomWq.github.io/expo-gaode-map/guide/web-api.html)
|
|
17
17
|
- [API 参考](https://TomWq.github.io/expo-gaode-map/api/)
|
|
18
|
-
- [
|
|
18
|
+
- [本地示例工程](../../example) / [导航示例工程](../../example-navigation)
|
|
19
19
|
|
|
20
20
|
## ✨ 主要特性
|
|
21
21
|
|
|
@@ -104,7 +104,7 @@ npx expo run:ios
|
|
|
104
104
|
|
|
105
105
|
详细的初始化和使用指南请查看:
|
|
106
106
|
- 📖 [快速开始文档](https://TomWq.github.io/expo-gaode-map/guide/getting-started.html)
|
|
107
|
-
- 💻 [
|
|
107
|
+
- 💻 [地图示例工程](../../example) / [导航示例工程](../../example-navigation)
|
|
108
108
|
|
|
109
109
|
## 📚 功能模块对比
|
|
110
110
|
|
|
@@ -231,8 +231,8 @@ MIT
|
|
|
231
231
|
- [错误处理指南](./ERROR_HANDLING_GUIDE.md) 🆕
|
|
232
232
|
- [性能优化指南](./PERFORMANCE_GUIDE.md) 🆕
|
|
233
233
|
- [GitHub 仓库](https://github.com/TomWq/expo-gaode-map)
|
|
234
|
-
- [
|
|
235
|
-
- [
|
|
234
|
+
- [地图示例工程](../../example)
|
|
235
|
+
- [导航示例工程](../../example-navigation)
|
|
236
236
|
- [高德地图开放平台](https://lbs.amap.com/)
|
|
237
237
|
- [Expo Modules API](https://docs.expo.dev/modules/overview/)
|
|
238
238
|
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
|
|
3
3
|
group = 'expo.modules.gaodemap'
|
|
4
|
-
version = '2.2.
|
|
4
|
+
version = '2.2.34'
|
|
5
5
|
|
|
6
6
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
7
|
apply from: expoModulesCorePlugin
|
|
@@ -38,7 +38,7 @@ android {
|
|
|
38
38
|
namespace "expo.modules.gaodemap"
|
|
39
39
|
defaultConfig {
|
|
40
40
|
versionCode 1
|
|
41
|
-
versionName "2.2.
|
|
41
|
+
versionName "2.2.34"
|
|
42
42
|
externalNativeBuild {
|
|
43
43
|
cmake {
|
|
44
44
|
cppFlags "-std=c++17"
|
|
@@ -55,8 +55,10 @@ android {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
|
|
59
|
+
|
|
58
60
|
dependencies {
|
|
59
|
-
//
|
|
61
|
+
// 高德地图组合 SDK:地图 + 定位 + 搜索。
|
|
60
62
|
def customSdkPath = null
|
|
61
63
|
println "ExpoGaodeMap: Checking for custom SDK property in rootProject: ${rootProject.name}"
|
|
62
64
|
if (rootProject.hasProperty("EXPO_GAODE_MAP_CUSTOM_SDK_PATH")) {
|
|
@@ -80,6 +82,8 @@ dependencies {
|
|
|
80
82
|
throw new FileNotFoundException("ExpoGaodeMap: Could not find custom SDK at ${customSdkPath}. Please check your customMapSdkPath configuration.")
|
|
81
83
|
}
|
|
82
84
|
} else {
|
|
83
|
-
|
|
85
|
+
// 10.1.600 is the last Android 3D map SDK line that supports offline
|
|
86
|
+
// map downloads without the separate 11.x offline-download permission.
|
|
87
|
+
implementation('com.amap.api:3dmap-location-search:10.1.700_loc6.5.1_sea9.7.4')
|
|
84
88
|
}
|
|
85
89
|
}
|
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
6
6
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
7
7
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
|
8
|
+
|
|
9
|
+
<!-- 离线地图读写本地地图包 (Android 12 及以下) -->
|
|
10
|
+
<uses-permission
|
|
11
|
+
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
|
12
|
+
android:maxSdkVersion="32" />
|
|
13
|
+
<uses-permission
|
|
14
|
+
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
|
15
|
+
android:maxSdkVersion="32" />
|
|
8
16
|
|
|
9
17
|
<application allowBackup="false" >
|
|
10
18
|
|
|
@@ -13,6 +21,12 @@
|
|
|
13
21
|
android:name="com.amap.api.location.APSService"
|
|
14
22
|
android:enabled="true"
|
|
15
23
|
android:exported="false" />
|
|
24
|
+
|
|
25
|
+
<!-- 高德 3D 地图 SDK 官方离线地图 UI 组件 -->
|
|
26
|
+
<activity
|
|
27
|
+
android:name="com.amap.api.maps.offlinemap.OfflineMapActivity"
|
|
28
|
+
android:screenOrientation="portrait"
|
|
29
|
+
android:exported="false" />
|
|
16
30
|
|
|
17
31
|
</application>
|
|
18
32
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
package expo.modules.gaodemap
|
|
2
2
|
|
|
3
3
|
import com.amap.api.maps.MapsInitializer
|
|
4
|
-
import com.amap.api.maps.model.LatLng
|
|
5
4
|
import expo.modules.kotlin.modules.Module
|
|
6
5
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
7
6
|
import expo.modules.gaodemap.modules.SDKInitializer
|
|
@@ -170,8 +169,8 @@ class ExpoGaodeMapModule : Module() {
|
|
|
170
169
|
AsyncFunction("coordinateConvert") { coordinate: Map<String, Any>?, type: Int, promise: expo.modules.kotlin.Promise ->
|
|
171
170
|
val latLng = LatLngParser.parseLatLng(coordinate)
|
|
172
171
|
if (latLng != null) {
|
|
173
|
-
val
|
|
174
|
-
getLocationManager().coordinateConvert(
|
|
172
|
+
val cordMap = mapOf("latitude" to latLng.latitude, "longitude" to latLng.longitude)
|
|
173
|
+
getLocationManager().coordinateConvert(cordMap, type, promise)
|
|
175
174
|
} else {
|
|
176
175
|
promise.reject("INVALID_COORDINATE", "Invalid coordinate format", null)
|
|
177
176
|
}
|
|
@@ -186,10 +185,10 @@ class ExpoGaodeMapModule : Module() {
|
|
|
186
185
|
* @returns 两点之间的距离(单位:米)
|
|
187
186
|
*/
|
|
188
187
|
Function("distanceBetweenCoordinates") { p1: Map<String, Any>?, p2: Map<String, Any>? ->
|
|
189
|
-
val
|
|
190
|
-
val
|
|
191
|
-
if (
|
|
192
|
-
GeometryUtils.calculateDistance(
|
|
188
|
+
val cord1 = LatLngParser.parseLatLng(p1)
|
|
189
|
+
val cord2 = LatLngParser.parseLatLng(p2)
|
|
190
|
+
if (cord1 != null && cord2 != null) {
|
|
191
|
+
GeometryUtils.calculateDistance(cord1, cord2)
|
|
193
192
|
} else {
|
|
194
193
|
0.0
|
|
195
194
|
}
|
|
@@ -580,7 +579,7 @@ class ExpoGaodeMapModule : Module() {
|
|
|
580
579
|
* @param gridSizeMeters 网格大小(米)
|
|
581
580
|
*/
|
|
582
581
|
Function("generateHeatmapGrid") { points: List<Map<String, Any>>?, gridSizeMeters: Double ->
|
|
583
|
-
if (points
|
|
582
|
+
if (points.isNullOrEmpty()) return@Function emptyList<Map<String, Any>>()
|
|
584
583
|
|
|
585
584
|
val count = points.size
|
|
586
585
|
val latitudes = DoubleArray(count)
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
package expo.modules.gaodemap
|
|
2
2
|
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.Intent
|
|
3
6
|
import android.os.Bundle
|
|
4
7
|
import android.os.StatFs
|
|
5
8
|
import android.os.Environment
|
|
9
|
+
import android.util.Log
|
|
10
|
+
import com.amap.api.maps.offlinemap.OfflineMapActivity
|
|
6
11
|
import com.amap.api.maps.offlinemap.OfflineMapCity
|
|
7
12
|
import com.amap.api.maps.offlinemap.OfflineMapManager
|
|
8
13
|
import com.amap.api.maps.offlinemap.OfflineMapProvince
|
|
@@ -17,6 +22,7 @@ import expo.modules.kotlin.modules.ModuleDefinition
|
|
|
17
22
|
*/
|
|
18
23
|
class ExpoGaodeMapOfflineModule : Module() {
|
|
19
24
|
|
|
25
|
+
private val logTag = "ExpoGaodeMapOffline"
|
|
20
26
|
private var offlineMapManager: OfflineMapManager? = null
|
|
21
27
|
private val downloadingCities = mutableSetOf<String>()
|
|
22
28
|
private val pausedCities = mutableSetOf<String>()
|
|
@@ -39,6 +45,11 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
private fun getOfflineMapManager(): OfflineMapManager {
|
|
48
|
+
val reactContext = appContext.reactContext
|
|
49
|
+
?: throw CodedException("NO_CONTEXT", "React context not available", null)
|
|
50
|
+
|
|
51
|
+
SDKInitializer.restorePersistedState(reactContext.applicationContext)
|
|
52
|
+
|
|
42
53
|
if (!SDKInitializer.isPrivacyReady()) {
|
|
43
54
|
throw CodedException(
|
|
44
55
|
"PRIVACY_NOT_AGREED",
|
|
@@ -47,9 +58,6 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
47
58
|
)
|
|
48
59
|
}
|
|
49
60
|
|
|
50
|
-
val reactContext = appContext.reactContext
|
|
51
|
-
?: throw CodedException("NO_CONTEXT", "React context not available", null)
|
|
52
|
-
|
|
53
61
|
if (offlineMapManager == null) {
|
|
54
62
|
offlineMapManager = OfflineMapManager(
|
|
55
63
|
reactContext.applicationContext,
|
|
@@ -59,6 +67,31 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
59
67
|
|
|
60
68
|
return offlineMapManager!!
|
|
61
69
|
}
|
|
70
|
+
|
|
71
|
+
private fun getActivityLaunchContext(): Context {
|
|
72
|
+
return appContext.currentActivity
|
|
73
|
+
?: appContext.reactContext
|
|
74
|
+
?: throw CodedException("NO_CONTEXT", "React context not available", null)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private fun openOfflineMapUI() {
|
|
78
|
+
val context = getActivityLaunchContext()
|
|
79
|
+
SDKInitializer.restorePersistedState(context.applicationContext)
|
|
80
|
+
|
|
81
|
+
if (!SDKInitializer.isPrivacyReady()) {
|
|
82
|
+
throw CodedException(
|
|
83
|
+
"PRIVACY_NOT_AGREED",
|
|
84
|
+
"隐私协议未完成确认,请先调用 setPrivacyConfig",
|
|
85
|
+
null
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
val intent = Intent(context, OfflineMapActivity::class.java)
|
|
90
|
+
if (context !is Activity) {
|
|
91
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
92
|
+
}
|
|
93
|
+
context.startActivity(intent)
|
|
94
|
+
}
|
|
62
95
|
|
|
63
96
|
override fun definition() = ModuleDefinition {
|
|
64
97
|
Name("ExpoGaodeMapOffline")
|
|
@@ -79,12 +112,18 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
79
112
|
offlineMapManager = null
|
|
80
113
|
downloadingCities.clear()
|
|
81
114
|
}
|
|
115
|
+
|
|
116
|
+
AsyncFunction("openOfflineMapUI") {
|
|
117
|
+
openOfflineMapUI()
|
|
118
|
+
}
|
|
82
119
|
|
|
83
120
|
// ==================== 地图列表管理 ====================
|
|
84
121
|
|
|
85
122
|
AsyncFunction("getAvailableCities") {
|
|
86
|
-
val
|
|
87
|
-
cities
|
|
123
|
+
val manager = getOfflineMapManager()
|
|
124
|
+
val cities = manager.offlineMapCityList ?: emptyList()
|
|
125
|
+
val downloadedCityKeys = getDownloadedCityKeys(manager)
|
|
126
|
+
cities.map { city -> convertCityToMap(city, downloadedCityKeys) }
|
|
88
127
|
}
|
|
89
128
|
|
|
90
129
|
AsyncFunction("getAvailableProvinces") {
|
|
@@ -93,15 +132,19 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
93
132
|
}
|
|
94
133
|
|
|
95
134
|
AsyncFunction("getCitiesByProvince") { provinceCode: String ->
|
|
96
|
-
val
|
|
135
|
+
val manager = getOfflineMapManager()
|
|
136
|
+
val province = manager.offlineMapProvinceList?.find {
|
|
97
137
|
it.provinceCode == provinceCode
|
|
98
138
|
}
|
|
99
|
-
|
|
139
|
+
val downloadedCityKeys = getDownloadedCityKeys(manager)
|
|
140
|
+
province?.cityList?.map { city -> convertCityToMap(city, downloadedCityKeys) } ?: emptyList()
|
|
100
141
|
}
|
|
101
142
|
|
|
102
143
|
AsyncFunction("getDownloadedMaps") {
|
|
103
|
-
val
|
|
104
|
-
cities
|
|
144
|
+
val manager = getOfflineMapManager()
|
|
145
|
+
val cities = manager.downloadOfflineMapCityList ?: emptyList()
|
|
146
|
+
val downloadedCityKeys = getDownloadedCityKeys(manager)
|
|
147
|
+
cities.map { city -> convertCityToMap(city, downloadedCityKeys) }
|
|
105
148
|
}
|
|
106
149
|
|
|
107
150
|
// ==================== 下载管理 ====================
|
|
@@ -114,7 +157,7 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
114
157
|
downloadingCities.add(cityCode)
|
|
115
158
|
pausedCities.remove(cityCode)
|
|
116
159
|
}
|
|
117
|
-
getOfflineMapManager()
|
|
160
|
+
startCityDownload(getOfflineMapManager(), cityCode, "startDownload")
|
|
118
161
|
}
|
|
119
162
|
|
|
120
163
|
AsyncFunction("pauseDownload") { cityCode: String ->
|
|
@@ -145,7 +188,7 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
145
188
|
}
|
|
146
189
|
// Android SDK 没有针对单个城市的恢复方法
|
|
147
190
|
// 需要重新调用 downloadByCityCode 来继续下载
|
|
148
|
-
getOfflineMapManager()
|
|
191
|
+
startCityDownload(getOfflineMapManager(), cityCode, "resumeDownload")
|
|
149
192
|
}
|
|
150
193
|
|
|
151
194
|
AsyncFunction("cancelDownload") { cityCode: String ->
|
|
@@ -195,14 +238,17 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
195
238
|
// ==================== 状态查询 ====================
|
|
196
239
|
|
|
197
240
|
AsyncFunction("isMapDownloaded") { cityCode: String ->
|
|
198
|
-
val
|
|
199
|
-
city
|
|
200
|
-
city?.state == OfflineMapStatus.
|
|
241
|
+
val manager = getOfflineMapManager()
|
|
242
|
+
val city = manager.getItemByCityCode(cityCode)
|
|
243
|
+
city?.state == OfflineMapStatus.SUCCESS ||
|
|
244
|
+
city?.let { isDownloadedCity(it, getDownloadedCityKeys(manager)) } == true
|
|
201
245
|
}
|
|
202
246
|
|
|
203
247
|
AsyncFunction("getMapStatus") { cityCode: String ->
|
|
204
|
-
val
|
|
205
|
-
city
|
|
248
|
+
val manager = getOfflineMapManager()
|
|
249
|
+
val city = manager.getItemByCityCode(cityCode)
|
|
250
|
+
val downloadedCityKeys = getDownloadedCityKeys(manager)
|
|
251
|
+
city?.let { convertCityToMap(it, downloadedCityKeys) } ?: Bundle()
|
|
206
252
|
}
|
|
207
253
|
|
|
208
254
|
AsyncFunction("getTotalProgress") {
|
|
@@ -279,7 +325,7 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
279
325
|
}
|
|
280
326
|
}
|
|
281
327
|
cityCodes.forEach { cityCode ->
|
|
282
|
-
getOfflineMapManager()
|
|
328
|
+
startCityDownload(getOfflineMapManager(), cityCode, "batchDownload")
|
|
283
329
|
}
|
|
284
330
|
}
|
|
285
331
|
|
|
@@ -340,17 +386,53 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
340
386
|
downloadingCities.add(cityCode)
|
|
341
387
|
pausedCities.remove(cityCode)
|
|
342
388
|
}
|
|
343
|
-
getOfflineMapManager()
|
|
389
|
+
startCityDownload(getOfflineMapManager(), cityCode, "resumeAllDownloads")
|
|
344
390
|
}
|
|
345
391
|
}
|
|
346
392
|
}
|
|
347
393
|
|
|
348
394
|
// ==================== 辅助方法 ====================
|
|
395
|
+
|
|
396
|
+
private fun findCity(manager: OfflineMapManager, cityCode: String): OfflineMapCity? {
|
|
397
|
+
return manager.getItemByCityCode(cityCode)
|
|
398
|
+
?: manager.offlineMapCityList?.find { it.code == cityCode || it.adcode == cityCode }
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private fun startCityDownload(manager: OfflineMapManager, cityCode: String, action: String) {
|
|
402
|
+
val city = findCity(manager, cityCode)
|
|
403
|
+
?: throw IllegalArgumentException("City not found: $cityCode")
|
|
404
|
+
|
|
405
|
+
logCityBeforeDownload(city, cityCode, action)
|
|
406
|
+
downloadCity(manager, city)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private fun downloadCity(manager: OfflineMapManager, city: OfflineMapCity) {
|
|
410
|
+
try {
|
|
411
|
+
manager.downloadByCityCode(city.code)
|
|
412
|
+
return
|
|
413
|
+
} catch (codeError: Exception) {
|
|
414
|
+
Log.w(logTag, "downloadByCityCode failed cityCode=${city.code} cityName=${city.city} error=${codeError.message}")
|
|
415
|
+
try {
|
|
416
|
+
manager.downloadByCityName(city.city)
|
|
417
|
+
} catch (nameError: Exception) {
|
|
418
|
+
throw IllegalStateException(
|
|
419
|
+
"离线地图下载失败: cityCode=${city.code}, cityName=${city.city}, codeError=${codeError.message}, nameError=${nameError.message}",
|
|
420
|
+
nameError
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private fun logCityBeforeDownload(city: OfflineMapCity?, cityCode: String, action: String) {
|
|
427
|
+
Log.i(logTag, "$action cityCode=$cityCode cityName=${city?.city} code=${city?.code} adcode=${city?.adcode} state=${city?.state} progress=${city?.let { getDownloadProgress(it) }} size=${city?.size} url=${city?.url}")
|
|
428
|
+
}
|
|
349
429
|
|
|
350
430
|
/**
|
|
351
431
|
* 处理下载状态回调
|
|
352
432
|
*/
|
|
353
433
|
private fun handleDownloadStatus(status: Int, completeCode: Int, downName: String?) {
|
|
434
|
+
Log.i(logTag, "onDownload raw status=$status completeCode=$completeCode downName=$downName")
|
|
435
|
+
|
|
354
436
|
if (downName == null) return
|
|
355
437
|
|
|
356
438
|
// downName 可能是城市代码或城市名称,尝试两种方式查找
|
|
@@ -360,10 +442,14 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
360
442
|
city = manager.offlineMapCityList?.find { it.city == downName }
|
|
361
443
|
}
|
|
362
444
|
|
|
363
|
-
if (city == null)
|
|
445
|
+
if (city == null) {
|
|
446
|
+
Log.w(logTag, "onDownload city not found for downName=$downName status=$status completeCode=$completeCode")
|
|
447
|
+
return
|
|
448
|
+
}
|
|
364
449
|
|
|
365
450
|
val cityCode = city.code
|
|
366
451
|
val cityName = city.city
|
|
452
|
+
Log.i(logTag, "onDownload city cityCode=$cityCode cityName=$cityName adcode=${city.adcode} state=${city.state} progress=${getDownloadProgress(city)} size=${city.size} url=${city.url} status=$status completeCode=$completeCode")
|
|
367
453
|
|
|
368
454
|
when (status) {
|
|
369
455
|
OfflineMapStatus.SUCCESS -> {
|
|
@@ -404,6 +490,18 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
404
490
|
putString("error", "解压失败,数据可能有问题")
|
|
405
491
|
})
|
|
406
492
|
}
|
|
493
|
+
|
|
494
|
+
startDownloadFailedCode -> {
|
|
495
|
+
synchronized(lock) {
|
|
496
|
+
downloadingCities.remove(cityCode)
|
|
497
|
+
}
|
|
498
|
+
sendEvent("onDownloadError", Bundle().apply {
|
|
499
|
+
putString("cityCode", cityCode)
|
|
500
|
+
putString("cityName", cityName)
|
|
501
|
+
putString("error", "下载地址为空或离线包不可用")
|
|
502
|
+
putInt("errorCode", status)
|
|
503
|
+
})
|
|
504
|
+
}
|
|
407
505
|
|
|
408
506
|
OfflineMapStatus.EXCEPTION_NETWORK_LOADING -> {
|
|
409
507
|
sendEvent("onDownloadError", Bundle().apply {
|
|
@@ -451,13 +549,37 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
451
549
|
/**
|
|
452
550
|
* 转换城市对象为 Map
|
|
453
551
|
*/
|
|
454
|
-
private fun
|
|
552
|
+
private fun getDownloadedCityKeys(manager: OfflineMapManager): Set<String> {
|
|
553
|
+
return manager.downloadOfflineMapCityList
|
|
554
|
+
?.flatMap { getCityIdentityKeys(it) }
|
|
555
|
+
?.toSet()
|
|
556
|
+
?: emptySet()
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
private fun getCityIdentityKeys(city: OfflineMapCity): List<String> {
|
|
560
|
+
return listOfNotNull(
|
|
561
|
+
city.code?.trim()?.takeIf { it.isNotEmpty() }?.let { "code:$it" },
|
|
562
|
+
city.adcode?.trim()?.takeIf { it.isNotEmpty() }?.let { "adcode:$it" },
|
|
563
|
+
city.city?.trim()?.takeIf { it.isNotEmpty() }?.let { "name:$it" }
|
|
564
|
+
)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private fun isDownloadedCity(city: OfflineMapCity, downloadedCityKeys: Set<String>): Boolean {
|
|
568
|
+
return getCityIdentityKeys(city).any { downloadedCityKeys.contains(it) }
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private fun convertCityToMap(
|
|
572
|
+
city: OfflineMapCity,
|
|
573
|
+
downloadedCityKeys: Set<String> = emptySet()
|
|
574
|
+
): Bundle {
|
|
455
575
|
val isPaused = synchronized(lock) { pausedCities.contains(city.code) }
|
|
456
576
|
val isDownloading = synchronized(lock) { downloadingCities.contains(city.code) }
|
|
577
|
+
val isDownloaded = isDownloadedCity(city, downloadedCityKeys)
|
|
457
578
|
|
|
458
579
|
val status = when {
|
|
459
580
|
isPaused -> "paused"
|
|
460
581
|
isDownloading -> "downloading"
|
|
582
|
+
isDownloaded -> "downloaded"
|
|
461
583
|
else -> getStatusString(city.state)
|
|
462
584
|
}
|
|
463
585
|
|
|
@@ -495,10 +617,10 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
495
617
|
private val startDownloadFailedCode: Int by lazy {
|
|
496
618
|
try {
|
|
497
619
|
OfflineMapStatus::class.java.getField("START_DOWNLOAD_FAILED").getInt(null)
|
|
498
|
-
} catch (
|
|
620
|
+
} catch (_: Exception) {
|
|
499
621
|
try {
|
|
500
622
|
OfflineMapStatus::class.java.getField("START_DOWNLOAD_FAILD").getInt(null)
|
|
501
|
-
} catch (
|
|
623
|
+
} catch (_: Exception) {
|
|
502
624
|
-1
|
|
503
625
|
}
|
|
504
626
|
}
|
|
@@ -513,17 +635,17 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
513
635
|
// 尝试标准版的命名 (getcompleteCode)
|
|
514
636
|
val method = obj.javaClass.getMethod("getcompleteCode")
|
|
515
637
|
return method.invoke(obj) as Int
|
|
516
|
-
} catch (
|
|
638
|
+
} catch (_: Exception) {
|
|
517
639
|
try {
|
|
518
640
|
// 尝试修正后的命名 (getCompleteCode) - Google Play 版本可能使用此命名
|
|
519
641
|
val method = obj.javaClass.getMethod("getCompleteCode")
|
|
520
642
|
return method.invoke(obj) as Int
|
|
521
|
-
} catch (
|
|
643
|
+
} catch (_: Exception) {
|
|
522
644
|
// 如果都失败了,尝试直接访问 completeCode 字段
|
|
523
645
|
try {
|
|
524
646
|
val field = obj.javaClass.getField("completeCode")
|
|
525
647
|
return field.getInt(obj)
|
|
526
|
-
} catch (
|
|
648
|
+
} catch (_: Exception) {
|
|
527
649
|
return 0
|
|
528
650
|
}
|
|
529
651
|
}
|
|
@@ -531,8 +653,9 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
531
653
|
}
|
|
532
654
|
|
|
533
655
|
/**
|
|
534
|
-
*
|
|
535
|
-
*
|
|
656
|
+
* 获取状态字符串。
|
|
657
|
+
* CHECKUPDATES / NEW_VERSION 在 10.1.600 冷启动时可能出现在普通城市列表,
|
|
658
|
+
* 不能单独作为已下载依据;已下载状态以 downloadOfflineMapCityList 为准。
|
|
536
659
|
*/
|
|
537
660
|
private fun getStatusString(state: Int): String {
|
|
538
661
|
return when (state) {
|
|
@@ -7,7 +7,6 @@ import android.view.View
|
|
|
7
7
|
import android.view.ViewGroup
|
|
8
8
|
import com.amap.api.maps.AMap
|
|
9
9
|
import com.amap.api.maps.TextureMapView
|
|
10
|
-
import com.amap.api.maps.MapsInitializer
|
|
11
10
|
import com.amap.api.maps.model.LatLng
|
|
12
11
|
import expo.modules.kotlin.AppContext
|
|
13
12
|
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
@@ -19,6 +18,7 @@ import expo.modules.gaodemap.overlays.*
|
|
|
19
18
|
import androidx.core.graphics.createBitmap
|
|
20
19
|
import androidx.core.view.isVisible
|
|
21
20
|
import androidx.core.graphics.withTranslation
|
|
21
|
+
import androidx.core.view.isGone
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* 高德地图视图组件
|
|
@@ -50,20 +50,20 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
53
|
-
val measuredWidth =
|
|
54
|
-
val measuredHeight =
|
|
53
|
+
val measuredWidth = MeasureSpec.getSize(widthMeasureSpec)
|
|
54
|
+
val measuredHeight = MeasureSpec.getSize(heightMeasureSpec)
|
|
55
55
|
|
|
56
56
|
setMeasuredDimension(measuredWidth, measuredHeight)
|
|
57
57
|
|
|
58
58
|
for (i in 0 until childCount) {
|
|
59
59
|
val child = getChildAt(i) ?: continue
|
|
60
|
-
if (child.
|
|
60
|
+
if (child.isGone) {
|
|
61
61
|
continue
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
if (child === mapView) {
|
|
65
|
-
val childWidthSpec =
|
|
66
|
-
val childHeightSpec =
|
|
65
|
+
val childWidthSpec = MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY)
|
|
66
|
+
val childHeightSpec = MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY)
|
|
67
67
|
child.measure(childWidthSpec, childHeightSpec)
|
|
68
68
|
continue
|
|
69
69
|
}
|
|
@@ -71,15 +71,15 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
71
71
|
val lp = child.layoutParams
|
|
72
72
|
val childWidthSpec = when {
|
|
73
73
|
lp?.width != null && lp.width > 0 ->
|
|
74
|
-
|
|
74
|
+
MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY)
|
|
75
75
|
else ->
|
|
76
|
-
|
|
76
|
+
MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.AT_MOST)
|
|
77
77
|
}
|
|
78
78
|
val childHeightSpec = when {
|
|
79
79
|
lp?.height != null && lp.height > 0 ->
|
|
80
|
-
|
|
80
|
+
MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY)
|
|
81
81
|
else ->
|
|
82
|
-
|
|
82
|
+
MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.AT_MOST)
|
|
83
83
|
}
|
|
84
84
|
child.measure(childWidthSpec, childHeightSpec)
|
|
85
85
|
}
|
|
@@ -91,7 +91,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
91
91
|
|
|
92
92
|
for (i in 0 until childCount) {
|
|
93
93
|
val child = getChildAt(i) ?: continue
|
|
94
|
-
if (child.
|
|
94
|
+
if (child.isGone) {
|
|
95
95
|
continue
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -150,6 +150,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
150
150
|
// 缓存初始相机位置,等待地图加载完成后设置
|
|
151
151
|
private var pendingCameraPosition: Map<String, Any?>? = null
|
|
152
152
|
private var isMapLoaded = false
|
|
153
|
+
private var hasAppliedInitialCameraPosition = false
|
|
153
154
|
|
|
154
155
|
init {
|
|
155
156
|
try {
|
|
@@ -192,7 +193,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
192
193
|
|
|
193
194
|
val positionToApply = initialCameraPosition ?: pendingCameraPosition
|
|
194
195
|
positionToApply?.let { position ->
|
|
195
|
-
|
|
196
|
+
applyInitialCameraPositionIfNeeded(position)
|
|
196
197
|
pendingCameraPosition = null
|
|
197
198
|
}
|
|
198
199
|
|
|
@@ -352,10 +353,14 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
352
353
|
fun setInitialCameraPosition(position: Map<String, Any?>) {
|
|
353
354
|
initialCameraPosition = position
|
|
354
355
|
|
|
356
|
+
if (hasAppliedInitialCameraPosition) {
|
|
357
|
+
return
|
|
358
|
+
}
|
|
359
|
+
|
|
355
360
|
// 如果地图已加载,立即应用;否则缓存等待地图加载完成
|
|
356
361
|
if (isMapLoaded) {
|
|
357
362
|
mainHandler.post {
|
|
358
|
-
|
|
363
|
+
applyInitialCameraPositionIfNeeded(position)
|
|
359
364
|
}
|
|
360
365
|
} else {
|
|
361
366
|
pendingCameraPosition = position
|
|
@@ -366,8 +371,12 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
366
371
|
* 实际应用相机位置
|
|
367
372
|
* @param position 相机位置配置
|
|
368
373
|
*/
|
|
369
|
-
private fun
|
|
374
|
+
private fun applyInitialCameraPositionIfNeeded(position: Map<String, Any?>) {
|
|
375
|
+
if (hasAppliedInitialCameraPosition) {
|
|
376
|
+
return
|
|
377
|
+
}
|
|
370
378
|
cameraManager.setInitialCameraPosition(position)
|
|
379
|
+
hasAppliedInitialCameraPosition = true
|
|
371
380
|
}
|
|
372
381
|
|
|
373
382
|
// ==================== UI 控件和手势 ====================
|
|
@@ -649,6 +658,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
649
658
|
|
|
650
659
|
// 销毁地图实例
|
|
651
660
|
mapView.onDestroy()
|
|
661
|
+
hasAppliedInitialCameraPosition = false
|
|
652
662
|
} catch (e: Exception) {
|
|
653
663
|
// 静默处理异常,确保销毁流程不会中断
|
|
654
664
|
android.util.Log.e("ExpoGaodeMapView", "Error destroying map", e)
|