expo-gaode-map-navigation 2.0.8 → 2.0.9
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 +233 -3
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +4 -2
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviView.kt +931 -391
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviViewModule.kt +86 -1
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNavigationModule.kt +4 -5
- package/android/src/main/java/expo/modules/gaodemap/navigation/listeners/IndependentRouteListener.kt +4 -3
- package/android/src/main/java/expo/modules/gaodemap/navigation/listeners/RouteCalculateListener.kt +2 -2
- package/android/src/main/java/expo/modules/gaodemap/navigation/managers/IndependentRouteManager.kt +96 -14
- package/android/src/main/java/expo/modules/gaodemap/navigation/routes/drive/DriveTruckRouteCalculator.kt +2 -0
- package/android/src/main/java/expo/modules/gaodemap/navigation/utils/Converters.kt +19 -10
- package/android/src/main/res/drawable/landback_0.png +0 -0
- package/android/src/main/res/drawable/landback_1.png +0 -0
- package/android/src/main/res/drawable/landback_2.png +0 -0
- package/android/src/main/res/drawable/landback_3.png +0 -0
- package/android/src/main/res/drawable/landback_4.png +0 -0
- package/android/src/main/res/drawable/landback_5.png +0 -0
- package/android/src/main/res/drawable/landback_6.png +0 -0
- package/android/src/main/res/drawable/landback_7.png +0 -0
- package/android/src/main/res/drawable/landback_8.png +0 -0
- package/android/src/main/res/drawable/landback_9.png +0 -0
- package/android/src/main/res/drawable/landback_a.png +0 -0
- package/android/src/main/res/drawable/landback_b.png +0 -0
- package/android/src/main/res/drawable/landback_c.png +0 -0
- package/android/src/main/res/drawable/landback_d.png +0 -0
- package/android/src/main/res/drawable/landback_e.png +0 -0
- package/android/src/main/res/drawable/landback_f.png +0 -0
- package/android/src/main/res/drawable/landback_g.png +0 -0
- package/android/src/main/res/drawable/landback_h.png +0 -0
- package/android/src/main/res/drawable/landback_i.png +0 -0
- package/android/src/main/res/drawable/landback_j.png +0 -0
- package/android/src/main/res/drawable/landback_k.png +0 -0
- package/android/src/main/res/drawable/landback_l.png +0 -0
- package/android/src/main/res/drawable/landfront_0.png +0 -0
- package/android/src/main/res/drawable/landfront_00.png +0 -0
- package/android/src/main/res/drawable/landfront_1.png +0 -0
- package/android/src/main/res/drawable/landfront_11.png +0 -0
- package/android/src/main/res/drawable/landfront_20.png +0 -0
- package/android/src/main/res/drawable/landfront_21.png +0 -0
- package/android/src/main/res/drawable/landfront_22.png +0 -0
- package/android/src/main/res/drawable/landfront_3.png +0 -0
- package/android/src/main/res/drawable/landfront_33.png +0 -0
- package/android/src/main/res/drawable/landfront_40.png +0 -0
- package/android/src/main/res/drawable/landfront_43.png +0 -0
- package/android/src/main/res/drawable/landfront_44.png +0 -0
- package/android/src/main/res/drawable/landfront_5.png +0 -0
- package/android/src/main/res/drawable/landfront_55.png +0 -0
- package/android/src/main/res/drawable/landfront_61.png +0 -0
- package/android/src/main/res/drawable/landfront_63.png +0 -0
- package/android/src/main/res/drawable/landfront_66.png +0 -0
- package/android/src/main/res/drawable/landfront_70.png +0 -0
- package/android/src/main/res/drawable/landfront_71.png +0 -0
- package/android/src/main/res/drawable/landfront_73.png +0 -0
- package/android/src/main/res/drawable/landfront_77.png +0 -0
- package/android/src/main/res/drawable/landfront_8.png +0 -0
- package/android/src/main/res/drawable/landfront_88.png +0 -0
- package/android/src/main/res/drawable/landfront_90.png +0 -0
- package/android/src/main/res/drawable/landfront_95.png +0 -0
- package/android/src/main/res/drawable/landfront_99.png +0 -0
- package/android/src/main/res/drawable/landfront_a0.png +0 -0
- package/android/src/main/res/drawable/landfront_a8.png +0 -0
- package/android/src/main/res/drawable/landfront_aa.png +0 -0
- package/android/src/main/res/drawable/landfront_b1.png +0 -0
- package/android/src/main/res/drawable/landfront_b5.png +0 -0
- package/android/src/main/res/drawable/landfront_bb.png +0 -0
- package/android/src/main/res/drawable/landfront_c3.png +0 -0
- package/android/src/main/res/drawable/landfront_c8.png +0 -0
- package/android/src/main/res/drawable/landfront_cc.png +0 -0
- package/android/src/main/res/drawable/landfront_d.png +0 -0
- package/android/src/main/res/drawable/landfront_dd.png +0 -0
- package/android/src/main/res/drawable/landfront_e1.png +0 -0
- package/android/src/main/res/drawable/landfront_e5.png +0 -0
- package/android/src/main/res/drawable/landfront_ee.png +0 -0
- package/android/src/main/res/drawable/landfront_f0.png +0 -0
- package/android/src/main/res/drawable/landfront_f1.png +0 -0
- package/android/src/main/res/drawable/landfront_f5.png +0 -0
- package/android/src/main/res/drawable/landfront_ff.png +0 -0
- package/android/src/main/res/drawable/landfront_g3.png +0 -0
- package/android/src/main/res/drawable/landfront_g5.png +0 -0
- package/android/src/main/res/drawable/landfront_gg.png +0 -0
- package/android/src/main/res/drawable/landfront_h1.png +0 -0
- package/android/src/main/res/drawable/landfront_h3.png +0 -0
- package/android/src/main/res/drawable/landfront_h5.png +0 -0
- package/android/src/main/res/drawable/landfront_hh.png +0 -0
- package/android/src/main/res/drawable/landfront_i0.png +0 -0
- package/android/src/main/res/drawable/landfront_i3.png +0 -0
- package/android/src/main/res/drawable/landfront_i5.png +0 -0
- package/android/src/main/res/drawable/landfront_ii.png +0 -0
- package/android/src/main/res/drawable/landfront_j1.png +0 -0
- package/android/src/main/res/drawable/landfront_j8.png +0 -0
- package/android/src/main/res/drawable/landfront_jj.png +0 -0
- package/android/src/main/res/drawable/landfront_kk.png +0 -0
- package/android/src/main/res/drawable/landfront_ll.png +0 -0
- package/android/src/main/res/drawable/navi_arrow_leftline.png +0 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_center.xml +5 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_left.xml +8 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_over.xml +6 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_right.xml +8 -0
- package/build/ExpoGaodeMapNaviView.d.ts +8 -0
- package/build/ExpoGaodeMapNaviView.d.ts.map +1 -1
- package/build/ExpoGaodeMapNaviView.js +38 -1
- package/build/ExpoGaodeMapNaviView.js.map +1 -1
- package/build/index.d.ts +8 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +408 -4
- package/build/index.js.map +1 -1
- package/build/types/independent.types.d.ts +91 -0
- package/build/types/independent.types.d.ts.map +1 -1
- package/build/types/independent.types.js.map +1 -1
- package/build/types/naviview.types.d.ts +256 -12
- package/build/types/naviview.types.d.ts.map +1 -1
- package/build/types/naviview.types.js.map +1 -1
- package/build/types/route.types.d.ts +2 -0
- package/build/types/route.types.d.ts.map +1 -1
- package/build/types/route.types.js.map +1 -1
- package/ios/ExpoGaodeMapNaviView.swift +888 -66
- package/ios/ExpoGaodeMapNaviViewModule.swift +87 -1
- package/ios/ExpoGaodeMapNavigationModule.swift +1 -1
- package/ios/managers/IndependentRouteManager.swift +1 -0
- package/ios/map/ExpoGaodeMapModule.swift +9 -4
- package/ios/map/ExpoGaodeMapView.swift +13 -2
- package/ios/map/modules/LocationManager.swift +17 -0
- package/ios/map/utils/PermissionManager.swift +11 -6
- package/ios/routes/drive/DriveTruckRouteCalculator.swift +9 -0
- package/ios/routes/walkride/WalkRideRouteCalculator.swift +30 -0
- package/ios/services/IndependentRouteService.swift +25 -0
- package/package.json +5 -2
|
@@ -9,7 +9,183 @@ import Foundation
|
|
|
9
9
|
import ExpoModulesCore
|
|
10
10
|
import AMapNaviKit
|
|
11
11
|
|
|
12
|
+
final class ExpoGaodeMapCustomDriveView: AMapNaviDriveView {
|
|
13
|
+
var suppressLaneInfoUI: Bool = false
|
|
14
|
+
var suppressTopInfoUI: Bool = false {
|
|
15
|
+
didSet {
|
|
16
|
+
scheduleTopInfoSuppressionPasses()
|
|
17
|
+
setNeedsLayout()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
private let topInfoCoverView: UIView = {
|
|
21
|
+
let view = UIView()
|
|
22
|
+
view.isHidden = true
|
|
23
|
+
view.isUserInteractionEnabled = false
|
|
24
|
+
view.backgroundColor = UIColor(red: 14.0 / 255.0, green: 18.0 / 255.0, blue: 26.0 / 255.0, alpha: 0.96)
|
|
25
|
+
return view
|
|
26
|
+
}()
|
|
27
|
+
private var scheduledSuppressionPasses = 0
|
|
28
|
+
|
|
29
|
+
override init(frame: CGRect) {
|
|
30
|
+
super.init(frame: frame)
|
|
31
|
+
installTopInfoCoverViewIfNeeded()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
required init?(coder: NSCoder) {
|
|
35
|
+
super.init(coder: coder)
|
|
36
|
+
installTopInfoCoverViewIfNeeded()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override func layoutSubviews() {
|
|
40
|
+
super.layoutSubviews()
|
|
41
|
+
applySuppressedChromeVisibility()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override func didMoveToWindow() {
|
|
45
|
+
super.didMoveToWindow()
|
|
46
|
+
installTopInfoCoverViewIfNeeded()
|
|
47
|
+
if suppressTopInfoUI {
|
|
48
|
+
scheduleTopInfoSuppressionPasses()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override func driveManager(_ driveManager: AMapNaviDriveManager, showLaneBackInfo laneBackInfo: String, laneSelectInfo: String) {
|
|
53
|
+
guard !suppressLaneInfoUI else {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
super.driveManager(driveManager, showLaneBackInfo: laneBackInfo, laneSelectInfo: laneSelectInfo)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
override func driveManagerHideLaneInfo(_ driveManager: AMapNaviDriveManager) {
|
|
60
|
+
guard !suppressLaneInfoUI else {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
super.driveManagerHideLaneInfo(driveManager)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private func applySuppressedChromeVisibility() {
|
|
67
|
+
installTopInfoCoverViewIfNeeded()
|
|
68
|
+
let topCandidates = collectTopInfoCandidates()
|
|
69
|
+
for candidate in topCandidates {
|
|
70
|
+
candidate.isHidden = suppressTopInfoUI
|
|
71
|
+
candidate.alpha = suppressTopInfoUI ? 0.0 : 1.0
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
guard suppressTopInfoUI, !topCandidates.isEmpty else {
|
|
75
|
+
topInfoCoverView.isHidden = true
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let unionFrame = topCandidates.reduce(CGRect.null) { partial, view in
|
|
80
|
+
let frame = view.convert(view.bounds, to: self)
|
|
81
|
+
return partial.union(frame)
|
|
82
|
+
}
|
|
83
|
+
let fallbackFrame = CGRect(x: 0, y: 0, width: bounds.width, height: min(max(bounds.height * 0.17, 96), 150))
|
|
84
|
+
let targetFrame = (unionFrame.isNull ? fallbackFrame : unionFrame.insetBy(dx: -8, dy: -6)).intersection(bounds)
|
|
85
|
+
topInfoCoverView.frame = targetFrame
|
|
86
|
+
topInfoCoverView.isHidden = targetFrame.isEmpty
|
|
87
|
+
if !topInfoCoverView.isHidden {
|
|
88
|
+
bringSubviewToFront(topInfoCoverView)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private func collectTopInfoCandidates() -> [UIView] {
|
|
93
|
+
guard suppressTopInfoUI || !subviews.isEmpty else {
|
|
94
|
+
return []
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let protectedClassNameFragments = [
|
|
98
|
+
"MAMap",
|
|
99
|
+
"Lane",
|
|
100
|
+
"Cross",
|
|
101
|
+
"Eagle",
|
|
102
|
+
"TrafficBar",
|
|
103
|
+
"Compass",
|
|
104
|
+
"Zoom",
|
|
105
|
+
"Scale",
|
|
106
|
+
"Logo",
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
return allDescendantSubviews(of: self).filter { view in
|
|
110
|
+
guard view !== self, view !== topInfoCoverView else {
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let frame = view.convert(view.bounds, to: self)
|
|
115
|
+
guard !frame.isEmpty else {
|
|
116
|
+
return false
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let className = NSStringFromClass(type(of: view))
|
|
120
|
+
if protectedClassNameFragments.contains(where: { className.localizedCaseInsensitiveContains($0) }) {
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let topBandMaxY = min(max(bounds.height * 0.28, 150), 220)
|
|
125
|
+
guard frame.minY <= topBandMaxY && frame.maxY <= topBandMaxY + 70 else {
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
guard frame.height >= 12 && frame.height <= 140 && frame.width >= 20 else {
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Keep small corner controls (for example compass / map tool buttons) out of the suppression set.
|
|
134
|
+
let isCornerControl =
|
|
135
|
+
frame.width <= 60 &&
|
|
136
|
+
frame.height <= 60 &&
|
|
137
|
+
(frame.minX <= 24 || frame.maxX >= bounds.width - 24)
|
|
138
|
+
if isCornerControl {
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return true
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private func allDescendantSubviews(of root: UIView) -> [UIView] {
|
|
147
|
+
root.subviews.flatMap { subview in
|
|
148
|
+
[subview] + allDescendantSubviews(of: subview)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private func installTopInfoCoverViewIfNeeded() {
|
|
153
|
+
guard topInfoCoverView.superview !== self else {
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
addSubview(topInfoCoverView)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private func scheduleTopInfoSuppressionPasses() {
|
|
160
|
+
scheduledSuppressionPasses = suppressTopInfoUI ? 18 : 0
|
|
161
|
+
guard suppressTopInfoUI else {
|
|
162
|
+
topInfoCoverView.isHidden = true
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
runScheduledTopInfoSuppressionPass()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private func runScheduledTopInfoSuppressionPass() {
|
|
169
|
+
applySuppressedChromeVisibility()
|
|
170
|
+
guard scheduledSuppressionPasses > 0 else {
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
scheduledSuppressionPasses -= 1
|
|
174
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.12) { [weak self] in
|
|
175
|
+
self?.runScheduledTopInfoSuppressionPass()
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
func refreshSuppressedTopInfoUIIfNeeded() {
|
|
180
|
+
guard suppressTopInfoUI else {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
applySuppressedChromeVisibility()
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
12
187
|
public class ExpoGaodeMapNaviView: ExpoView {
|
|
188
|
+
private let independentRouteManager = IndependentRouteManager.shared
|
|
13
189
|
|
|
14
190
|
// MARK: - 高德 SDK 初始化检查
|
|
15
191
|
|
|
@@ -121,15 +297,30 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
121
297
|
let onGpsStatusChanged = EventDispatcher()
|
|
122
298
|
let onNavigationInfoUpdate = EventDispatcher()
|
|
123
299
|
let onGpsSignalWeak = EventDispatcher()
|
|
300
|
+
let onNavigationVisualStateUpdate = EventDispatcher()
|
|
301
|
+
let onLaneInfoUpdate = EventDispatcher()
|
|
302
|
+
let onTrafficStatusesUpdate = EventDispatcher()
|
|
124
303
|
|
|
125
304
|
// MARK: - Properties
|
|
126
305
|
private var driveView: AMapNaviDriveView?
|
|
127
|
-
private var driveViewLoaded: Bool = false
|
|
128
306
|
private var pendingShowUIElements: Bool?
|
|
129
|
-
private var pendingShowUIElementsWorkItem: DispatchWorkItem?
|
|
130
307
|
private var hasStartedNavi: Bool = false
|
|
131
308
|
private var hasReceivedFirstNaviData: Bool = false
|
|
132
309
|
private var driveManager: AMapNaviDriveManager?
|
|
310
|
+
private var lastKnownSpeed: Int = 0
|
|
311
|
+
private var currentRouteTotalLength: Int?
|
|
312
|
+
private var lastNavigationInfoPayload: [String: Any]?
|
|
313
|
+
private var lastTurnIconType: Int?
|
|
314
|
+
private var lastNextTurnIconType: Int?
|
|
315
|
+
private var lastTurnIconImageUri: String?
|
|
316
|
+
private var lastNextTurnIconImageUri: String?
|
|
317
|
+
private var isCrossVisible: Bool = false
|
|
318
|
+
private var isLaneInfoVisible: Bool = false
|
|
319
|
+
|
|
320
|
+
private enum LaneStringKind {
|
|
321
|
+
case background
|
|
322
|
+
case selected
|
|
323
|
+
}
|
|
133
324
|
|
|
134
325
|
// Props - 通用属性
|
|
135
326
|
var naviType: Int = 0 // 0: GPS, 1: Emulator
|
|
@@ -139,6 +330,24 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
139
330
|
var showCamera: Bool = true {
|
|
140
331
|
didSet { applyShowCamera(showCamera) }
|
|
141
332
|
}
|
|
333
|
+
var carImageSource: String? {
|
|
334
|
+
didSet { applyCarImageSource() }
|
|
335
|
+
}
|
|
336
|
+
var carCompassImageSource: String? {
|
|
337
|
+
didSet { applyCarCompassImageSource() }
|
|
338
|
+
}
|
|
339
|
+
var startPointImageSource: String? {
|
|
340
|
+
didSet { applyStartPointImageSource() }
|
|
341
|
+
}
|
|
342
|
+
var wayPointImageSource: String? {
|
|
343
|
+
didSet { applyWayPointImageSource() }
|
|
344
|
+
}
|
|
345
|
+
var endPointImageSource: String? {
|
|
346
|
+
didSet { applyEndPointImageSource() }
|
|
347
|
+
}
|
|
348
|
+
var cameraImageSource: String? {
|
|
349
|
+
didSet { applyCameraImageSource() }
|
|
350
|
+
}
|
|
142
351
|
var autoLockCar: Bool = true {
|
|
143
352
|
didSet { applyAutoLockCar(autoLockCar) }
|
|
144
353
|
}
|
|
@@ -171,6 +380,12 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
171
380
|
var showTrafficBar: Bool = true {
|
|
172
381
|
didSet { driveView?.showTrafficBar = showTrafficBar }
|
|
173
382
|
}
|
|
383
|
+
var trafficBarFrame: CGRect = .zero {
|
|
384
|
+
didSet { applyTrafficBarFrame(trafficBarFrame) }
|
|
385
|
+
}
|
|
386
|
+
var trafficBarColors: [String: Any]? {
|
|
387
|
+
didSet { applyTrafficBarColors(trafficBarColors) }
|
|
388
|
+
}
|
|
174
389
|
var showBrowseRouteButton: Bool = true {
|
|
175
390
|
didSet { driveView?.showBrowseRouteButton = showBrowseRouteButton }
|
|
176
391
|
}
|
|
@@ -180,6 +395,12 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
180
395
|
var showTrafficButton: Bool = true {
|
|
181
396
|
didSet { driveView?.showTrafficButton = showTrafficButton }
|
|
182
397
|
}
|
|
398
|
+
var showBackupRoute: Bool = true {
|
|
399
|
+
didSet { driveView?.showBackupRoute = showBackupRoute }
|
|
400
|
+
}
|
|
401
|
+
var showEagleMap: Bool = false {
|
|
402
|
+
didSet { driveView?.showEagleMap = showEagleMap }
|
|
403
|
+
}
|
|
183
404
|
var showUIElements: Bool = true {
|
|
184
405
|
didSet { applyShowUIElementsToDriveViewIfReady() }
|
|
185
406
|
}
|
|
@@ -192,12 +413,43 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
192
413
|
var showTrafficLights: Bool = true {
|
|
193
414
|
didSet { driveView?.showTrafficLights = showTrafficLights }
|
|
194
415
|
}
|
|
416
|
+
var showCompassEnabled: Bool? {
|
|
417
|
+
didSet {
|
|
418
|
+
guard let showCompassEnabled else { return }
|
|
419
|
+
driveView?.showCompass = showCompassEnabled
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
var showDriveCongestion: Bool = true {
|
|
423
|
+
didSet { driveView?.showDriveCongestion = showDriveCongestion }
|
|
424
|
+
}
|
|
425
|
+
var showTrafficLightView: Bool = true {
|
|
426
|
+
didSet { driveView?.showTrafficLightView = showTrafficLightView }
|
|
427
|
+
}
|
|
195
428
|
var mapViewModeType: Int = 0 {
|
|
196
429
|
didSet { applyMapViewModeType(mapViewModeType) }
|
|
197
430
|
}
|
|
198
431
|
var lineWidth: CGFloat = 0 {
|
|
199
432
|
didSet { driveView?.lineWidth = lineWidth }
|
|
200
433
|
}
|
|
434
|
+
var driveViewEdgePadding: UIEdgeInsets = .zero {
|
|
435
|
+
didSet {
|
|
436
|
+
driveView?.setNeedsLayout()
|
|
437
|
+
driveView?.layoutIfNeeded()
|
|
438
|
+
scheduleOverviewRouteVisibleRegionRefresh()
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
var screenAnchor: CGPoint = .zero {
|
|
442
|
+
didSet {
|
|
443
|
+
driveView?.screenAnchor = screenAnchor
|
|
444
|
+
scheduleOverviewRouteVisibleRegionRefresh()
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
var hideNativeTopInfoLayout: Bool = false {
|
|
448
|
+
didSet { applyHideNativeTopInfoLayout(hideNativeTopInfoLayout) }
|
|
449
|
+
}
|
|
450
|
+
var hideNativeLaneInfoLayout: Bool = false {
|
|
451
|
+
didSet { applyHideNativeLaneInfoLayout(hideNativeLaneInfoLayout) }
|
|
452
|
+
}
|
|
201
453
|
|
|
202
454
|
func applyShowUIElements(_ visible: Bool) {
|
|
203
455
|
showUIElements = visible
|
|
@@ -238,13 +490,16 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
238
490
|
|
|
239
491
|
// 初始化驾车导航管理器
|
|
240
492
|
driveManager = AMapNaviDriveManager.sharedInstance()
|
|
241
|
-
|
|
493
|
+
rebindDriveManagerToView()
|
|
242
494
|
|
|
243
495
|
// 使用内置语音
|
|
244
496
|
driveManager?.isUseInternalTTS = true
|
|
245
497
|
|
|
246
498
|
// 初始化导航视图
|
|
247
|
-
|
|
499
|
+
let customDriveView = ExpoGaodeMapCustomDriveView(frame: bounds)
|
|
500
|
+
customDriveView.suppressTopInfoUI = hideNativeTopInfoLayout
|
|
501
|
+
customDriveView.suppressLaneInfoUI = hideNativeLaneInfoLayout
|
|
502
|
+
driveView = customDriveView
|
|
248
503
|
driveView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
249
504
|
driveView?.delegate = self
|
|
250
505
|
|
|
@@ -256,13 +511,233 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
256
511
|
// 应用初始配置
|
|
257
512
|
applyViewOptions()
|
|
258
513
|
}
|
|
514
|
+
|
|
515
|
+
private func rebindDriveManagerToView() {
|
|
516
|
+
driveManager = AMapNaviDriveManager.sharedInstance()
|
|
517
|
+
driveManager?.delegate = self
|
|
518
|
+
driveManager?.removeDataRepresentative(self)
|
|
519
|
+
if let view = driveView {
|
|
520
|
+
driveManager?.removeDataRepresentative(view)
|
|
521
|
+
driveManager?.addDataRepresentative(view)
|
|
522
|
+
}
|
|
523
|
+
driveManager?.addDataRepresentative(self)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
private func resetTransientNavigationState() {
|
|
527
|
+
lastNavigationInfoPayload = nil
|
|
528
|
+
lastTurnIconType = nil
|
|
529
|
+
lastNextTurnIconType = nil
|
|
530
|
+
clearCachedTurnIconUris()
|
|
531
|
+
isCrossVisible = false
|
|
532
|
+
isLaneInfoVisible = false
|
|
533
|
+
emitVisualStateUpdate()
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
private func emitVisualStateUpdate() {
|
|
537
|
+
onNavigationVisualStateUpdate([
|
|
538
|
+
"isCrossVisible": isCrossVisible,
|
|
539
|
+
// iOS 官方导航 SDK 公开的是 showCrossImage / hideCrossImage,
|
|
540
|
+
// 当前没有 Android showModeCross / hideModeCross 对等的 3D 路口模型接口。
|
|
541
|
+
"isModeCrossVisible": false,
|
|
542
|
+
"isLaneInfoVisible": isLaneInfoVisible
|
|
543
|
+
])
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
private func emitNavigationInfoUpdate(_ payload: [String: Any]) {
|
|
547
|
+
(driveView as? ExpoGaodeMapCustomDriveView)?.refreshSuppressedTopInfoUIIfNeeded()
|
|
548
|
+
var nextPayload = payload
|
|
549
|
+
if let lastTurnIconType {
|
|
550
|
+
nextPayload["iconType"] = lastTurnIconType
|
|
551
|
+
nextPayload["iconDirection"] = lastTurnIconType
|
|
552
|
+
}
|
|
553
|
+
if let lastNextTurnIconType {
|
|
554
|
+
nextPayload["nextIconType"] = lastNextTurnIconType
|
|
555
|
+
}
|
|
556
|
+
if let lastTurnIconImageUri {
|
|
557
|
+
nextPayload["turnIconImage"] = lastTurnIconImageUri
|
|
558
|
+
} else {
|
|
559
|
+
nextPayload.removeValue(forKey: "turnIconImage")
|
|
560
|
+
}
|
|
561
|
+
if let lastNextTurnIconImageUri {
|
|
562
|
+
nextPayload["nextTurnIconImage"] = lastNextTurnIconImageUri
|
|
563
|
+
} else {
|
|
564
|
+
nextPayload.removeValue(forKey: "nextTurnIconImage")
|
|
565
|
+
}
|
|
566
|
+
if
|
|
567
|
+
let retainDistance = nextPayload["pathRetainDistance"] as? Int,
|
|
568
|
+
let driveDistance = nextPayload["driveDistance"] as? Int,
|
|
569
|
+
driveDistance > 0
|
|
570
|
+
{
|
|
571
|
+
currentRouteTotalLength = retainDistance + driveDistance
|
|
572
|
+
}
|
|
573
|
+
lastNavigationInfoPayload = nextPayload
|
|
574
|
+
onNavigationInfoUpdate(nextPayload)
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
private func reemitLastNavigationInfoIfNeeded() {
|
|
578
|
+
guard var nextPayload = lastNavigationInfoPayload else {
|
|
579
|
+
return
|
|
580
|
+
}
|
|
581
|
+
if let lastTurnIconType {
|
|
582
|
+
nextPayload["iconType"] = lastTurnIconType
|
|
583
|
+
nextPayload["iconDirection"] = lastTurnIconType
|
|
584
|
+
}
|
|
585
|
+
if let lastNextTurnIconType {
|
|
586
|
+
nextPayload["nextIconType"] = lastNextTurnIconType
|
|
587
|
+
} else {
|
|
588
|
+
nextPayload.removeValue(forKey: "nextIconType")
|
|
589
|
+
}
|
|
590
|
+
if let lastTurnIconImageUri {
|
|
591
|
+
nextPayload["turnIconImage"] = lastTurnIconImageUri
|
|
592
|
+
} else {
|
|
593
|
+
nextPayload.removeValue(forKey: "turnIconImage")
|
|
594
|
+
}
|
|
595
|
+
if let lastNextTurnIconImageUri {
|
|
596
|
+
nextPayload["nextTurnIconImage"] = lastNextTurnIconImageUri
|
|
597
|
+
} else {
|
|
598
|
+
nextPayload.removeValue(forKey: "nextTurnIconImage")
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
lastNavigationInfoPayload = nextPayload
|
|
602
|
+
onNavigationInfoUpdate(nextPayload)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
private func cacheTurnIconImage(_ image: UIImage?, prefix: String, previousUri: String?) -> String? {
|
|
606
|
+
guard let image, let data = image.pngData() else {
|
|
607
|
+
if let previousUri {
|
|
608
|
+
deleteCachedTurnIcon(at: previousUri)
|
|
609
|
+
}
|
|
610
|
+
return nil
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
let filename = "\(prefix)_\(UUID().uuidString).png"
|
|
614
|
+
let fileURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
|
|
615
|
+
|
|
616
|
+
do {
|
|
617
|
+
try data.write(to: fileURL, options: .atomic)
|
|
618
|
+
if let previousUri, previousUri != fileURL.absoluteString {
|
|
619
|
+
deleteCachedTurnIcon(at: previousUri)
|
|
620
|
+
}
|
|
621
|
+
return fileURL.absoluteString
|
|
622
|
+
} catch {
|
|
623
|
+
return previousUri
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
private func deleteCachedTurnIcon(at uriString: String) {
|
|
628
|
+
guard let fileURL = URL(string: uriString), fileURL.isFileURL else {
|
|
629
|
+
return
|
|
630
|
+
}
|
|
631
|
+
try? FileManager.default.removeItem(at: fileURL)
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
private func clearCachedTurnIconUris() {
|
|
635
|
+
if let lastTurnIconImageUri {
|
|
636
|
+
deleteCachedTurnIcon(at: lastTurnIconImageUri)
|
|
637
|
+
}
|
|
638
|
+
if let lastNextTurnIconImageUri {
|
|
639
|
+
deleteCachedTurnIcon(at: lastNextTurnIconImageUri)
|
|
640
|
+
}
|
|
641
|
+
lastTurnIconImageUri = nil
|
|
642
|
+
lastNextTurnIconImageUri = nil
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
private func splitLaneInfoString(_ value: String) -> [String] {
|
|
646
|
+
value
|
|
647
|
+
.split(separator: "|", omittingEmptySubsequences: false)
|
|
648
|
+
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() }
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
private func parseLaneToken(_ token: String, kind: LaneStringKind) -> Int? {
|
|
652
|
+
guard !token.isEmpty else {
|
|
653
|
+
return nil
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if token == "255" || token == "ff" {
|
|
657
|
+
return 255
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Older iOS callbacks may use `f` as filler / unavailable lane marker.
|
|
661
|
+
if token == "f" {
|
|
662
|
+
return kind == .background ? 255 : 255
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if let decimalValue = Int(token) {
|
|
666
|
+
return decimalValue
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
switch token {
|
|
670
|
+
case "a":
|
|
671
|
+
return 10
|
|
672
|
+
case "b":
|
|
673
|
+
return 11
|
|
674
|
+
case "c":
|
|
675
|
+
return 12
|
|
676
|
+
case "d":
|
|
677
|
+
return 13
|
|
678
|
+
case "e":
|
|
679
|
+
return 14
|
|
680
|
+
case "g":
|
|
681
|
+
return 16
|
|
682
|
+
case "h":
|
|
683
|
+
return 17
|
|
684
|
+
case "i":
|
|
685
|
+
return 18
|
|
686
|
+
case "j":
|
|
687
|
+
return 19
|
|
688
|
+
case "k":
|
|
689
|
+
return 20
|
|
690
|
+
case "kk":
|
|
691
|
+
return 21
|
|
692
|
+
case "l":
|
|
693
|
+
return 23
|
|
694
|
+
default:
|
|
695
|
+
return Int(token, radix: 16)
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
private func serializeLaneInfo(laneBackInfo: String, laneSelectInfo: String) -> [String: Any]? {
|
|
700
|
+
let backgroundLane = splitLaneInfoString(laneBackInfo).map {
|
|
701
|
+
parseLaneToken($0, kind: .background) ?? 255
|
|
702
|
+
}
|
|
703
|
+
let frontLane = splitLaneInfoString(laneSelectInfo).map {
|
|
704
|
+
parseLaneToken($0, kind: .selected) ?? 255
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
let sentinelIndex = backgroundLane.firstIndex(of: 255)
|
|
708
|
+
let resolvedCount = [
|
|
709
|
+
sentinelIndex,
|
|
710
|
+
backgroundLane.isEmpty ? nil : backgroundLane.count,
|
|
711
|
+
frontLane.isEmpty ? nil : frontLane.count
|
|
712
|
+
]
|
|
713
|
+
.compactMap { $0 }
|
|
714
|
+
.min() ?? 0
|
|
715
|
+
|
|
716
|
+
guard resolvedCount > 0 else {
|
|
717
|
+
return nil
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
let normalizedBackground = (0..<resolvedCount).map { index in
|
|
721
|
+
backgroundLane.indices.contains(index) ? backgroundLane[index] : 255
|
|
722
|
+
}
|
|
723
|
+
let normalizedFront = (0..<resolvedCount).map { index in
|
|
724
|
+
frontLane.indices.contains(index) ? frontLane[index] : 255
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return [
|
|
728
|
+
"laneCount": resolvedCount,
|
|
729
|
+
"backgroundLane": normalizedBackground,
|
|
730
|
+
"frontLane": normalizedFront
|
|
731
|
+
]
|
|
732
|
+
}
|
|
259
733
|
|
|
260
734
|
private func applyViewOptions() {
|
|
261
735
|
// 通用属性
|
|
736
|
+
driveView?.showUIElements = showUIElements
|
|
262
737
|
driveView?.showCamera = showCamera
|
|
263
738
|
driveView?.autoSwitchShowModeToCarPositionLocked = autoLockCar
|
|
264
739
|
driveView?.autoZoomMapLevel = autoChangeZoom
|
|
265
|
-
driveView?.
|
|
740
|
+
driveView?.mapShowTraffic = trafficLayerEnabled
|
|
266
741
|
driveView?.showCrossImage = realCrossDisplay
|
|
267
742
|
driveView?.trackingMode = naviMode == 0 ? .carNorth : .mapNorth
|
|
268
743
|
|
|
@@ -270,69 +745,275 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
270
745
|
driveView?.showRoute = showRoute
|
|
271
746
|
driveView?.showTurnArrow = showTurnArrow
|
|
272
747
|
driveView?.showTrafficBar = showTrafficBar
|
|
748
|
+
applyTrafficBarFrame(trafficBarFrame)
|
|
749
|
+
applyTrafficBarColors(trafficBarColors)
|
|
273
750
|
driveView?.showBrowseRouteButton = showBrowseRouteButton
|
|
274
751
|
driveView?.showMoreButton = showMoreButton
|
|
275
752
|
driveView?.showTrafficButton = showTrafficButton
|
|
753
|
+
driveView?.showBackupRoute = showBackupRoute
|
|
754
|
+
driveView?.showEagleMap = showEagleMap
|
|
276
755
|
driveView?.showGreyAfterPass = showGreyAfterPass
|
|
277
756
|
driveView?.showVectorline = showVectorline
|
|
278
757
|
driveView?.showTrafficLights = showTrafficLights
|
|
758
|
+
if let showCompassEnabled {
|
|
759
|
+
driveView?.showCompass = showCompassEnabled
|
|
760
|
+
}
|
|
761
|
+
driveView?.showDriveCongestion = showDriveCongestion
|
|
762
|
+
driveView?.showTrafficLightView = showTrafficLightView
|
|
279
763
|
if lineWidth > 0 {
|
|
280
764
|
driveView?.lineWidth = lineWidth
|
|
281
765
|
}
|
|
766
|
+
applyCustomAnnotationImages()
|
|
767
|
+
applyCustomUILayoutOptionsIfNeeded()
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
private func applyCustomAnnotationImages() {
|
|
771
|
+
applyCarImageSource()
|
|
772
|
+
applyCarCompassImageSource()
|
|
773
|
+
applyStartPointImageSource()
|
|
774
|
+
applyWayPointImageSource()
|
|
775
|
+
applyEndPointImageSource()
|
|
776
|
+
applyCameraImageSource()
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
private func resolveLocalImage(_ source: String) -> UIImage? {
|
|
780
|
+
if source.hasPrefix("file://") {
|
|
781
|
+
let path = String(source.dropFirst(7))
|
|
782
|
+
return UIImage(contentsOfFile: path)
|
|
783
|
+
}
|
|
282
784
|
|
|
283
|
-
|
|
785
|
+
return UIImage(named: source) ?? UIImage(contentsOfFile: source)
|
|
284
786
|
}
|
|
285
787
|
|
|
286
|
-
private func
|
|
287
|
-
|
|
288
|
-
|
|
788
|
+
private func applyAnnotationImage(
|
|
789
|
+
source: String?,
|
|
790
|
+
currentSource: @escaping () -> String?,
|
|
791
|
+
apply: @escaping (UIImage?) -> Void
|
|
792
|
+
) {
|
|
793
|
+
guard let source, !source.isEmpty else {
|
|
794
|
+
apply(nil)
|
|
289
795
|
return
|
|
290
796
|
}
|
|
291
797
|
|
|
292
|
-
|
|
293
|
-
|
|
798
|
+
if source.hasPrefix("http://") || source.hasPrefix("https://") {
|
|
799
|
+
DispatchQueue.global().async {
|
|
800
|
+
let image: UIImage? = {
|
|
801
|
+
guard let url = URL(string: source),
|
|
802
|
+
let data = try? Data(contentsOf: url) else {
|
|
803
|
+
return nil
|
|
804
|
+
}
|
|
805
|
+
return UIImage(data: data)
|
|
806
|
+
}()
|
|
807
|
+
|
|
808
|
+
DispatchQueue.main.async {
|
|
809
|
+
guard currentSource() == source else {
|
|
810
|
+
return
|
|
811
|
+
}
|
|
812
|
+
apply(image)
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
apply(resolveLocalImage(source))
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
private func applyCarImageSource() {
|
|
822
|
+
guard let driveView else {
|
|
823
|
+
return
|
|
824
|
+
}
|
|
825
|
+
applyAnnotationImage(source: carImageSource, currentSource: { [weak self] in
|
|
826
|
+
self?.carImageSource
|
|
827
|
+
}) { [weak driveView] image in
|
|
828
|
+
driveView?.setCarImage(image)
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
private func applyCarCompassImageSource() {
|
|
833
|
+
guard let driveView else {
|
|
834
|
+
return
|
|
835
|
+
}
|
|
836
|
+
applyAnnotationImage(source: carCompassImageSource, currentSource: { [weak self] in
|
|
837
|
+
self?.carCompassImageSource
|
|
838
|
+
}) { [weak driveView] image in
|
|
839
|
+
driveView?.setCarCompassImage(image)
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
private func applyStartPointImageSource() {
|
|
844
|
+
guard let driveView else {
|
|
845
|
+
return
|
|
846
|
+
}
|
|
847
|
+
applyAnnotationImage(source: startPointImageSource, currentSource: { [weak self] in
|
|
848
|
+
self?.startPointImageSource
|
|
849
|
+
}) { [weak driveView] image in
|
|
850
|
+
driveView?.setStartPointImage(image)
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
private func applyWayPointImageSource() {
|
|
855
|
+
guard let driveView else {
|
|
856
|
+
return
|
|
857
|
+
}
|
|
858
|
+
applyAnnotationImage(source: wayPointImageSource, currentSource: { [weak self] in
|
|
859
|
+
self?.wayPointImageSource
|
|
860
|
+
}) { [weak driveView] image in
|
|
861
|
+
driveView?.setWayPointImage(image)
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
private func applyEndPointImageSource() {
|
|
866
|
+
guard let driveView else {
|
|
867
|
+
return
|
|
868
|
+
}
|
|
869
|
+
applyAnnotationImage(source: endPointImageSource, currentSource: { [weak self] in
|
|
870
|
+
self?.endPointImageSource
|
|
871
|
+
}) { [weak driveView] image in
|
|
872
|
+
driveView?.setEndPointImage(image)
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
private func applyCameraImageSource() {
|
|
877
|
+
guard let driveView else {
|
|
294
878
|
return
|
|
295
879
|
}
|
|
880
|
+
applyAnnotationImage(source: cameraImageSource, currentSource: { [weak self] in
|
|
881
|
+
self?.cameraImageSource
|
|
882
|
+
}) { [weak driveView] image in
|
|
883
|
+
driveView?.setCameraImage(image)
|
|
884
|
+
}
|
|
885
|
+
}
|
|
296
886
|
|
|
887
|
+
private func applyShowUIElementsToDriveViewIfReady() {
|
|
888
|
+
guard let driveView else {
|
|
889
|
+
pendingShowUIElements = showUIElements
|
|
890
|
+
return
|
|
891
|
+
}
|
|
297
892
|
let value = pendingShowUIElements ?? showUIElements
|
|
298
893
|
pendingShowUIElements = nil
|
|
894
|
+
driveView.showUIElements = value
|
|
895
|
+
applyCustomUILayoutOptionsIfNeeded()
|
|
896
|
+
}
|
|
299
897
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
pendingShowUIElementsWorkItem?.cancel()
|
|
303
|
-
let workItem = DispatchWorkItem { [weak self] in
|
|
304
|
-
guard let self else { return }
|
|
305
|
-
self.applyDriveViewShowUIElements(true, remainingAttempts: 20)
|
|
306
|
-
}
|
|
307
|
-
pendingShowUIElementsWorkItem = workItem
|
|
308
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.08, execute: workItem)
|
|
898
|
+
private func applyCustomUILayoutOptionsIfNeeded() {
|
|
899
|
+
guard let driveView else {
|
|
309
900
|
return
|
|
310
901
|
}
|
|
311
902
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
guard let self else { return }
|
|
315
|
-
self.applyDriveViewShowUIElements(value, remainingAttempts: 20)
|
|
903
|
+
guard showUIElements == false else {
|
|
904
|
+
return
|
|
316
905
|
}
|
|
317
|
-
pendingShowUIElementsWorkItem = workItem
|
|
318
906
|
|
|
319
|
-
|
|
907
|
+
// In custom UI mode, these properties are controlled independently from
|
|
908
|
+
// the built-in widget layer and should be re-applied after toggling it off.
|
|
909
|
+
driveView.showCrossImage = realCrossDisplay
|
|
910
|
+
driveView.showTrafficBar = showTrafficBar
|
|
911
|
+
applyTrafficBarFrame(trafficBarFrame)
|
|
912
|
+
applyTrafficBarColors(trafficBarColors)
|
|
913
|
+
driveView.screenAnchor = screenAnchor
|
|
914
|
+
driveView.setNeedsLayout()
|
|
915
|
+
driveView.layoutIfNeeded()
|
|
916
|
+
scheduleOverviewRouteVisibleRegionRefresh()
|
|
320
917
|
}
|
|
321
918
|
|
|
322
|
-
private func
|
|
323
|
-
guard let driveView else {
|
|
919
|
+
private func refreshOverviewRouteVisibleRegionIfNeeded() {
|
|
920
|
+
guard showUIElements == false, let driveView else {
|
|
921
|
+
return
|
|
922
|
+
}
|
|
324
923
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
924
|
+
guard driveView.showMode == .overview else {
|
|
925
|
+
return
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
driveView.updateRoutePolylineInTheVisualRangeWhenTheShowModeIsOverview()
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
private func scheduleOverviewRouteVisibleRegionRefresh() {
|
|
932
|
+
DispatchQueue.main.async { [weak self] in
|
|
933
|
+
self?.driveView?.setNeedsLayout()
|
|
934
|
+
self?.driveView?.layoutIfNeeded()
|
|
935
|
+
self?.refreshOverviewRouteVisibleRegionIfNeeded()
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
private func applyHideNativeLaneInfoLayout(_ hidden: Bool) {
|
|
940
|
+
(driveView as? ExpoGaodeMapCustomDriveView)?.suppressLaneInfoUI = hidden
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
private func applyHideNativeTopInfoLayout(_ hidden: Bool) {
|
|
944
|
+
(driveView as? ExpoGaodeMapCustomDriveView)?.suppressTopInfoUI = hidden
|
|
945
|
+
driveView?.setNeedsLayout()
|
|
946
|
+
driveView?.layoutIfNeeded()
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
private func applyTrafficBarFrame(_ frame: CGRect) {
|
|
950
|
+
guard let driveView else {
|
|
951
|
+
return
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
guard frame.width > 0, frame.height > 0 else {
|
|
955
|
+
return
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
driveView.tmcRouteFrame = frame
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
private func applyTrafficBarColors(_ colors: [String: Any]?) {
|
|
962
|
+
guard let driveView, let colors else {
|
|
963
|
+
return
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
let mappings: [(String, AMapNaviRouteStatus)] = [
|
|
967
|
+
("unknown", .unknow),
|
|
968
|
+
("smooth", .smooth),
|
|
969
|
+
("fineOpen", .fineOpen),
|
|
970
|
+
("slow", .slow),
|
|
971
|
+
("jam", .jam),
|
|
972
|
+
("seriousJam", .seriousJam),
|
|
973
|
+
("defaultRoad", .default),
|
|
974
|
+
]
|
|
975
|
+
|
|
976
|
+
let resolvedColors = mappings.compactMap { key, status -> AMapNaviTMCStatusColor? in
|
|
977
|
+
guard let value = colors[key], let color = ColorParser.parseColor(value) else {
|
|
978
|
+
return nil
|
|
331
979
|
}
|
|
980
|
+
let item = AMapNaviTMCStatusColor()
|
|
981
|
+
item.status = status
|
|
982
|
+
item.color = color
|
|
983
|
+
return item
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
guard !resolvedColors.isEmpty else {
|
|
332
987
|
return
|
|
333
988
|
}
|
|
334
989
|
|
|
335
|
-
driveView.
|
|
990
|
+
driveView.tmcRouteColor = resolvedColors
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
private func emitTrafficStatusesUpdate(_ trafficStatuses: [AMapNaviTrafficStatus]?) {
|
|
994
|
+
// iOS 会提供 fineStatus;统一事件结构时保留它,方便 RN 自绘层按需细化颜色策略。
|
|
995
|
+
let items = (trafficStatuses ?? []).map { status in
|
|
996
|
+
var payload: [String: Any] = [
|
|
997
|
+
"status": Int(status.status.rawValue),
|
|
998
|
+
"length": status.length
|
|
999
|
+
]
|
|
1000
|
+
payload["fineStatus"] = status.trafficFineStatus
|
|
1001
|
+
return payload
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
var payload: [String: Any] = [
|
|
1005
|
+
"items": items
|
|
1006
|
+
]
|
|
1007
|
+
|
|
1008
|
+
if let totalLength = currentRouteTotalLength, totalLength > 0 {
|
|
1009
|
+
payload["totalLength"] = totalLength
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
if let retainDistance = lastNavigationInfoPayload?["pathRetainDistance"] as? Int {
|
|
1013
|
+
payload["retainDistance"] = retainDistance
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
onTrafficStatusesUpdate(payload)
|
|
336
1017
|
}
|
|
337
1018
|
|
|
338
1019
|
// MARK: - Prop Setters
|
|
@@ -354,7 +1035,7 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
354
1035
|
}
|
|
355
1036
|
|
|
356
1037
|
private func applyTrafficLayerEnabled(_ enabled: Bool) {
|
|
357
|
-
driveView?.
|
|
1038
|
+
driveView?.mapShowTraffic = enabled
|
|
358
1039
|
}
|
|
359
1040
|
|
|
360
1041
|
private func applyRealCrossDisplay(_ enabled: Bool) {
|
|
@@ -377,6 +1058,7 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
377
1058
|
default:
|
|
378
1059
|
break
|
|
379
1060
|
}
|
|
1061
|
+
scheduleOverviewRouteVisibleRegionRefresh()
|
|
380
1062
|
}
|
|
381
1063
|
|
|
382
1064
|
private func applyNightMode(_ enabled: Bool) {
|
|
@@ -420,6 +1102,9 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
420
1102
|
return
|
|
421
1103
|
}
|
|
422
1104
|
|
|
1105
|
+
rebindDriveManagerToView()
|
|
1106
|
+
resetTransientNavigationState()
|
|
1107
|
+
|
|
423
1108
|
startCoordinate = AMapNaviPoint.location(withLatitude: CGFloat(startLat), longitude: CGFloat(startLng))
|
|
424
1109
|
endCoordinate = AMapNaviPoint.location(withLatitude: CGFloat(endLat), longitude: CGFloat(endLng))
|
|
425
1110
|
|
|
@@ -446,9 +1131,61 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
446
1131
|
promise.reject("CALCULATE_FAILED", "启动路线规划失败")
|
|
447
1132
|
}
|
|
448
1133
|
}
|
|
1134
|
+
|
|
1135
|
+
func startNavigationWithIndependentPath(
|
|
1136
|
+
token: Int,
|
|
1137
|
+
routeId: Int?,
|
|
1138
|
+
routeIndex: Int?,
|
|
1139
|
+
naviType: Int?,
|
|
1140
|
+
promise: Promise
|
|
1141
|
+
) {
|
|
1142
|
+
do {
|
|
1143
|
+
try checkPrivacyReady()
|
|
1144
|
+
try checkAMapInitialization()
|
|
1145
|
+
try ensureBackgroundLocationModeForNavigation()
|
|
1146
|
+
} catch {
|
|
1147
|
+
let nsError = error as NSError
|
|
1148
|
+
let code: String
|
|
1149
|
+
if nsError.domain == "ExpoGaodeMapPrivacy" {
|
|
1150
|
+
code = "PRIVACY_NOT_AGREED"
|
|
1151
|
+
} else if nsError.code == -1003 {
|
|
1152
|
+
code = "BACKGROUND_LOCATION_NOT_ENABLED"
|
|
1153
|
+
} else {
|
|
1154
|
+
code = "AMAP_NOT_INITIALIZED"
|
|
1155
|
+
}
|
|
1156
|
+
promise.reject(code, formatError(error))
|
|
1157
|
+
return
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
rebindDriveManagerToView()
|
|
1161
|
+
resetTransientNavigationState()
|
|
1162
|
+
|
|
1163
|
+
if let naviType {
|
|
1164
|
+
self.naviType = naviType
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
let ok = independentRouteManager.start(
|
|
1168
|
+
token: token,
|
|
1169
|
+
naviType: self.naviType,
|
|
1170
|
+
routeId: routeId,
|
|
1171
|
+
routeIndex: routeIndex
|
|
1172
|
+
)
|
|
1173
|
+
|
|
1174
|
+
if ok {
|
|
1175
|
+
promise.resolve([
|
|
1176
|
+
"success": true,
|
|
1177
|
+
"message": "独立路径导航启动中...",
|
|
1178
|
+
"token": token,
|
|
1179
|
+
"naviType": self.naviType
|
|
1180
|
+
])
|
|
1181
|
+
} else {
|
|
1182
|
+
promise.reject("START_INDEPENDENT_NAVI_FAILED", "独立路径导航启动失败")
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
449
1185
|
|
|
450
1186
|
func stopNavigation(promise: Promise) {
|
|
451
1187
|
driveManager?.stopNavi()
|
|
1188
|
+
resetTransientNavigationState()
|
|
452
1189
|
promise.resolve([
|
|
453
1190
|
"success": true,
|
|
454
1191
|
"message": "导航已停止"
|
|
@@ -466,6 +1203,7 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
466
1203
|
public override func layoutSubviews() {
|
|
467
1204
|
super.layoutSubviews()
|
|
468
1205
|
driveView?.frame = bounds
|
|
1206
|
+
scheduleOverviewRouteVisibleRegionRefresh()
|
|
469
1207
|
}
|
|
470
1208
|
|
|
471
1209
|
deinit {
|
|
@@ -473,7 +1211,9 @@ public class ExpoGaodeMapNaviView: ExpoView {
|
|
|
473
1211
|
if let view = driveView {
|
|
474
1212
|
driveManager?.removeDataRepresentative(view)
|
|
475
1213
|
}
|
|
1214
|
+
driveManager?.removeDataRepresentative(self)
|
|
476
1215
|
driveManager?.delegate = nil
|
|
1216
|
+
clearCachedTurnIconUris()
|
|
477
1217
|
}
|
|
478
1218
|
}
|
|
479
1219
|
|
|
@@ -495,6 +1235,9 @@ extension ExpoGaodeMapNaviView: AMapNaviDriveManagerDelegate {
|
|
|
495
1235
|
}
|
|
496
1236
|
|
|
497
1237
|
applyShowUIElementsToDriveViewIfReady()
|
|
1238
|
+
DispatchQueue.main.async { [weak self] in
|
|
1239
|
+
self?.applyCustomAnnotationImages()
|
|
1240
|
+
}
|
|
498
1241
|
}
|
|
499
1242
|
|
|
500
1243
|
public func driveManager(_ driveManager: AMapNaviDriveManager, onCalculateRouteFailure error: Error) {
|
|
@@ -521,37 +1264,10 @@ extension ExpoGaodeMapNaviView: AMapNaviDriveManagerDelegate {
|
|
|
521
1264
|
}
|
|
522
1265
|
|
|
523
1266
|
public func driveManagerDidEndEmulatorNavi(_ driveManager: AMapNaviDriveManager) {
|
|
1267
|
+
resetTransientNavigationState()
|
|
524
1268
|
onNavigationEnded([:])
|
|
525
1269
|
}
|
|
526
1270
|
|
|
527
|
-
public func driveManager(_ driveManager: AMapNaviDriveManager, didUpdate naviLocation: AMapNaviLocation) {
|
|
528
|
-
if !hasReceivedFirstNaviData {
|
|
529
|
-
hasReceivedFirstNaviData = true
|
|
530
|
-
applyShowUIElementsToDriveViewIfReady()
|
|
531
|
-
}
|
|
532
|
-
onLocationUpdate([
|
|
533
|
-
"latitude": naviLocation.coordinate.latitude,
|
|
534
|
-
"longitude": naviLocation.coordinate.longitude,
|
|
535
|
-
"speed": naviLocation.speed,
|
|
536
|
-
"bearing": naviLocation.heading
|
|
537
|
-
])
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
public func driveManager(_ driveManager: AMapNaviDriveManager, didUpdate naviInfo: AMapNaviInfo) {
|
|
541
|
-
if !hasReceivedFirstNaviData {
|
|
542
|
-
hasReceivedFirstNaviData = true
|
|
543
|
-
applyShowUIElementsToDriveViewIfReady()
|
|
544
|
-
}
|
|
545
|
-
onNavigationInfoUpdate([
|
|
546
|
-
"currentRoadName": naviInfo.currentRoadName ?? "",
|
|
547
|
-
"nextRoadName": naviInfo.nextRoadName ?? "",
|
|
548
|
-
"pathRetainDistance": naviInfo.routeRemainDistance,
|
|
549
|
-
"pathRetainTime": naviInfo.routeRemainTime,
|
|
550
|
-
"curStepRetainDistance": naviInfo.segmentRemainDistance,
|
|
551
|
-
"curStepRetainTime": naviInfo.segmentRemainTime
|
|
552
|
-
])
|
|
553
|
-
}
|
|
554
|
-
|
|
555
1271
|
public func driveManager(_ driveManager: AMapNaviDriveManager, playNaviSound soundString: String, soundStringType: AMapNaviSoundType) {
|
|
556
1272
|
onNavigationText([
|
|
557
1273
|
"type": soundStringType.rawValue,
|
|
@@ -587,13 +1303,119 @@ extension ExpoGaodeMapNaviView: AMapNaviDriveManagerDelegate {
|
|
|
587
1303
|
extension ExpoGaodeMapNaviView: AMapNaviDriveViewDelegate {
|
|
588
1304
|
|
|
589
1305
|
public func driveViewDidLoad(_ driveView: AMapNaviDriveView) {
|
|
590
|
-
driveViewLoaded = true
|
|
591
1306
|
applyViewOptions()
|
|
592
1307
|
applyShowUIElementsToDriveViewIfReady()
|
|
1308
|
+
scheduleOverviewRouteVisibleRegionRefresh()
|
|
593
1309
|
onNavigationReady([:])
|
|
594
1310
|
}
|
|
595
1311
|
|
|
596
1312
|
public func driveViewEdgePadding(_ driveView: AMapNaviDriveView) -> UIEdgeInsets {
|
|
597
|
-
return
|
|
1313
|
+
return driveViewEdgePadding
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
public func driveView(_ driveView: AMapNaviDriveView, didChange showMode: AMapNaviDriveViewShowMode) {
|
|
1317
|
+
if showMode == .overview {
|
|
1318
|
+
scheduleOverviewRouteVisibleRegionRefresh()
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
extension ExpoGaodeMapNaviView: AMapNaviDriveDataRepresentable {
|
|
1324
|
+
public func driveManager(_ driveManager: AMapNaviDriveManager, update naviRoute: AMapNaviRoute?) {
|
|
1325
|
+
if let routeLength = naviRoute?.routeLength, routeLength > 0 {
|
|
1326
|
+
currentRouteTotalLength = routeLength
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
public func driveManager(_ driveManager: AMapNaviDriveManager, update naviLocation: AMapNaviLocation?) {
|
|
1331
|
+
guard let naviLocation else {
|
|
1332
|
+
return
|
|
1333
|
+
}
|
|
1334
|
+
if !hasReceivedFirstNaviData {
|
|
1335
|
+
hasReceivedFirstNaviData = true
|
|
1336
|
+
applyShowUIElementsToDriveViewIfReady()
|
|
1337
|
+
}
|
|
1338
|
+
lastKnownSpeed = Int(naviLocation.speed)
|
|
1339
|
+
onLocationUpdate([
|
|
1340
|
+
"latitude": naviLocation.coordinate.latitude,
|
|
1341
|
+
"longitude": naviLocation.coordinate.longitude,
|
|
1342
|
+
"speed": naviLocation.speed,
|
|
1343
|
+
"bearing": naviLocation.heading
|
|
1344
|
+
])
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
public func driveManager(_ driveManager: AMapNaviDriveManager, update naviInfo: AMapNaviInfo?) {
|
|
1348
|
+
guard let naviInfo else {
|
|
1349
|
+
return
|
|
1350
|
+
}
|
|
1351
|
+
if !hasReceivedFirstNaviData {
|
|
1352
|
+
hasReceivedFirstNaviData = true
|
|
1353
|
+
applyShowUIElementsToDriveViewIfReady()
|
|
1354
|
+
}
|
|
1355
|
+
emitNavigationInfoUpdate([
|
|
1356
|
+
"naviMode": naviInfo.naviMode.rawValue,
|
|
1357
|
+
"currentRoadName": naviInfo.currentRoadName ?? "",
|
|
1358
|
+
"nextRoadName": naviInfo.nextRoadName ?? "",
|
|
1359
|
+
"pathRetainDistance": naviInfo.routeRemainDistance,
|
|
1360
|
+
"pathRetainTime": naviInfo.routeRemainTime,
|
|
1361
|
+
"curStepRetainDistance": naviInfo.segmentRemainDistance,
|
|
1362
|
+
"curStepRetainTime": naviInfo.segmentRemainTime,
|
|
1363
|
+
"currentSpeed": lastKnownSpeed,
|
|
1364
|
+
"iconType": lastTurnIconType ?? naviInfo.iconType.rawValue,
|
|
1365
|
+
"iconDirection": lastTurnIconType ?? naviInfo.iconType.rawValue,
|
|
1366
|
+
"currentSegmentIndex": naviInfo.currentSegmentIndex,
|
|
1367
|
+
"currentLinkIndex": naviInfo.currentLinkIndex,
|
|
1368
|
+
"currentPointIndex": naviInfo.currentPointIndex,
|
|
1369
|
+
"routeRemainTrafficLightCount": naviInfo.routeRemainTrafficLightCount,
|
|
1370
|
+
"driveDistance": naviInfo.routeDriveDistance,
|
|
1371
|
+
"driveTime": naviInfo.routeDriveTime
|
|
1372
|
+
])
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
public func driveManager(_ driveManager: AMapNaviDriveManager, showCross crossImage: UIImage?) {
|
|
1376
|
+
isCrossVisible = true
|
|
1377
|
+
emitVisualStateUpdate()
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
public func driveManagerHideCrossImage(_ driveManager: AMapNaviDriveManager) {
|
|
1381
|
+
isCrossVisible = false
|
|
1382
|
+
emitVisualStateUpdate()
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
public func driveManager(_ driveManager: AMapNaviDriveManager, showLaneBackInfo laneBackInfo: String, laneSelectInfo: String) {
|
|
1386
|
+
isLaneInfoVisible = true
|
|
1387
|
+
emitVisualStateUpdate()
|
|
1388
|
+
if let payload = serializeLaneInfo(laneBackInfo: laneBackInfo, laneSelectInfo: laneSelectInfo) {
|
|
1389
|
+
onLaneInfoUpdate(payload)
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
public func driveManagerHideLaneInfo(_ driveManager: AMapNaviDriveManager) {
|
|
1394
|
+
isLaneInfoVisible = false
|
|
1395
|
+
emitVisualStateUpdate()
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
public func driveManager(_ driveManager: AMapNaviDriveManager, updateTrafficStatus trafficStatus: [AMapNaviTrafficStatus]?) {
|
|
1399
|
+
emitTrafficStatusesUpdate(trafficStatus)
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
public func driveManager(_ driveManager: AMapNaviDriveManager, updateTurnIconImage turnIconImage: UIImage?, turn turnIconType: AMapNaviIconType) {
|
|
1403
|
+
lastTurnIconType = Int(turnIconType.rawValue)
|
|
1404
|
+
lastTurnIconImageUri = cacheTurnIconImage(
|
|
1405
|
+
turnIconImage,
|
|
1406
|
+
prefix: "turn_icon",
|
|
1407
|
+
previousUri: lastTurnIconImageUri
|
|
1408
|
+
)
|
|
1409
|
+
reemitLastNavigationInfoIfNeeded()
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
public func driveManager(_ driveManager: AMapNaviDriveManager, updateNextTurnIconImage turnIconImage: UIImage?, nextTurn turnIconType: AMapNaviIconType) {
|
|
1413
|
+
lastNextTurnIconType = Int(turnIconType.rawValue)
|
|
1414
|
+
lastNextTurnIconImageUri = cacheTurnIconImage(
|
|
1415
|
+
turnIconImage,
|
|
1416
|
+
prefix: "next_turn_icon",
|
|
1417
|
+
previousUri: lastNextTurnIconImageUri
|
|
1418
|
+
)
|
|
1419
|
+
reemitLastNavigationInfoIfNeeded()
|
|
598
1420
|
}
|
|
599
1421
|
}
|