@capgo/capacitor-launch-navigator 7.0.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.
@@ -0,0 +1,394 @@
1
+ import Foundation
2
+ import UIKit
3
+ import MapKit
4
+
5
+ @objc public class LaunchNavigator: NSObject {
6
+
7
+ private let navigationApps: [String: (name: String, urlScheme: String)] = [
8
+ "apple_maps": ("Apple Maps", "maps://"),
9
+ "google_maps": ("Google Maps", "comgooglemaps://"),
10
+ "waze": ("Waze", "waze://"),
11
+ "citymapper": ("Citymapper", "citymapper://"),
12
+ "garmin_navigon": ("Garmin Navigon", "navigon://"),
13
+ "transit_app": ("Transit App", "transit://"),
14
+ "yandex": ("Yandex Navigator", "yandexnavi://"),
15
+ "uber": ("Uber", "uber://"),
16
+ "tomtom": ("TomTom", "tomtomgo://"),
17
+ "sygic": ("Sygic", "com.sygic.aura://"),
18
+ "here": ("HERE Maps", "here-route://"),
19
+ "moovit": ("Moovit", "moovit://"),
20
+ "lyft": ("Lyft", "lyft://"),
21
+ "mapsme": ("MAPS.ME", "mapsme://"),
22
+ "cabify": ("Cabify", "cabify://"),
23
+ "baidu": ("Baidu Maps", "baidumap://"),
24
+ "gaode": ("Gaode Maps", "iosamap://"),
25
+ "99taxi": ("99 Taxi", "99app://")
26
+ ]
27
+
28
+ public func navigate(
29
+ app: String,
30
+ destination: CLLocationCoordinate2D,
31
+ start: CLLocationCoordinate2D?,
32
+ startName: String?,
33
+ destinationName: String?,
34
+ transportMode: String,
35
+ completion: @escaping (Bool, String?) -> Void
36
+ ) {
37
+ switch app {
38
+ case "apple_maps":
39
+ launchAppleMaps(
40
+ destination: destination,
41
+ start: start,
42
+ startName: startName,
43
+ destinationName: destinationName,
44
+ transportMode: transportMode,
45
+ completion: completion
46
+ )
47
+ case "google_maps":
48
+ launchGoogleMaps(
49
+ destination: destination,
50
+ start: start,
51
+ transportMode: transportMode,
52
+ completion: completion
53
+ )
54
+ case "waze":
55
+ launchWaze(
56
+ destination: destination,
57
+ completion: completion
58
+ )
59
+ case "citymapper":
60
+ launchCitymapper(
61
+ destination: destination,
62
+ start: start,
63
+ completion: completion
64
+ )
65
+ case "uber":
66
+ launchUber(
67
+ destination: destination,
68
+ start: start,
69
+ completion: completion
70
+ )
71
+ case "lyft":
72
+ launchLyft(
73
+ destination: destination,
74
+ completion: completion
75
+ )
76
+ case "moovit":
77
+ launchMoovit(
78
+ destination: destination,
79
+ start: start,
80
+ completion: completion
81
+ )
82
+ case "yandex":
83
+ launchYandex(
84
+ destination: destination,
85
+ start: start,
86
+ completion: completion
87
+ )
88
+ case "sygic":
89
+ launchSygic(
90
+ destination: destination,
91
+ completion: completion
92
+ )
93
+ case "here":
94
+ launchHere(
95
+ destination: destination,
96
+ start: start,
97
+ completion: completion
98
+ )
99
+ case "tomtom":
100
+ launchTomTom(
101
+ destination: destination,
102
+ completion: completion
103
+ )
104
+ case "mapsme":
105
+ launchMapsMe(
106
+ destination: destination,
107
+ completion: completion
108
+ )
109
+ case "cabify":
110
+ launchCabify(
111
+ destination: destination,
112
+ start: start,
113
+ completion: completion
114
+ )
115
+ case "baidu":
116
+ launchBaidu(
117
+ destination: destination,
118
+ start: start,
119
+ completion: completion
120
+ )
121
+ case "gaode":
122
+ launchGaode(
123
+ destination: destination,
124
+ start: start,
125
+ completion: completion
126
+ )
127
+ default:
128
+ completion(false, "Unsupported navigation app: \(app)")
129
+ }
130
+ }
131
+
132
+ private func launchAppleMaps(
133
+ destination: CLLocationCoordinate2D,
134
+ start: CLLocationCoordinate2D?,
135
+ startName: String?,
136
+ destinationName: String?,
137
+ transportMode: String,
138
+ completion: @escaping (Bool, String?) -> Void
139
+ ) {
140
+ let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: destination))
141
+ mapItem.name = destinationName
142
+
143
+ var options: [String: Any] = [:]
144
+
145
+ switch transportMode {
146
+ case "driving":
147
+ options[MKLaunchOptionsDirectionsModeKey] = MKLaunchOptionsDirectionsModeDriving
148
+ case "walking":
149
+ options[MKLaunchOptionsDirectionsModeKey] = MKLaunchOptionsDirectionsModeWalking
150
+ case "transit":
151
+ options[MKLaunchOptionsDirectionsModeKey] = MKLaunchOptionsDirectionsModeTransit
152
+ default:
153
+ options[MKLaunchOptionsDirectionsModeKey] = MKLaunchOptionsDirectionsModeDriving
154
+ }
155
+
156
+ if let startCoord = start {
157
+ let startItem = MKMapItem(placemark: MKPlacemark(coordinate: startCoord))
158
+ startItem.name = startName
159
+ MKMapItem.openMaps(with: [startItem, mapItem], launchOptions: options)
160
+ } else {
161
+ mapItem.openInMaps(launchOptions: options)
162
+ }
163
+
164
+ completion(true, nil)
165
+ }
166
+
167
+ private func launchGoogleMaps(
168
+ destination: CLLocationCoordinate2D,
169
+ start: CLLocationCoordinate2D?,
170
+ transportMode: String,
171
+ completion: @escaping (Bool, String?) -> Void
172
+ ) {
173
+ var urlString = "comgooglemaps://?daddr=\(destination.latitude),\(destination.longitude)"
174
+
175
+ if let startCoord = start {
176
+ urlString += "&saddr=\(startCoord.latitude),\(startCoord.longitude)"
177
+ }
178
+
179
+ let modeMap: [String: String] = [
180
+ "driving": "driving",
181
+ "walking": "walking",
182
+ "bicycling": "bicycling",
183
+ "transit": "transit"
184
+ ]
185
+
186
+ if let mode = modeMap[transportMode] {
187
+ urlString += "&directionsmode=\(mode)"
188
+ }
189
+
190
+ openURL(urlString, completion: completion)
191
+ }
192
+
193
+ private func launchWaze(
194
+ destination: CLLocationCoordinate2D,
195
+ completion: @escaping (Bool, String?) -> Void
196
+ ) {
197
+ let urlString = "waze://?ll=\(destination.latitude),\(destination.longitude)&navigate=yes"
198
+ openURL(urlString, completion: completion)
199
+ }
200
+
201
+ private func launchCitymapper(
202
+ destination: CLLocationCoordinate2D,
203
+ start: CLLocationCoordinate2D?,
204
+ completion: @escaping (Bool, String?) -> Void
205
+ ) {
206
+ var urlString = "citymapper://directions?endcoord=\(destination.latitude),\(destination.longitude)"
207
+
208
+ if let startCoord = start {
209
+ urlString += "&startcoord=\(startCoord.latitude),\(startCoord.longitude)"
210
+ }
211
+
212
+ openURL(urlString, completion: completion)
213
+ }
214
+
215
+ private func launchUber(
216
+ destination: CLLocationCoordinate2D,
217
+ start: CLLocationCoordinate2D?,
218
+ completion: @escaping (Bool, String?) -> Void
219
+ ) {
220
+ var urlString = "uber://?action=setPickup&pickup=my_location&dropoff[latitude]=\(destination.latitude)&dropoff[longitude]=\(destination.longitude)"
221
+
222
+ if let startCoord = start {
223
+ urlString = "uber://?action=setPickup&pickup[latitude]=\(startCoord.latitude)&pickup[longitude]=\(startCoord.longitude)&dropoff[latitude]=\(destination.latitude)&dropoff[longitude]=\(destination.longitude)"
224
+ }
225
+
226
+ openURL(urlString, completion: completion)
227
+ }
228
+
229
+ private func launchLyft(
230
+ destination: CLLocationCoordinate2D,
231
+ completion: @escaping (Bool, String?) -> Void
232
+ ) {
233
+ let urlString = "lyft://ridetype?id=lyft&destination[latitude]=\(destination.latitude)&destination[longitude]=\(destination.longitude)"
234
+ openURL(urlString, completion: completion)
235
+ }
236
+
237
+ private func launchMoovit(
238
+ destination: CLLocationCoordinate2D,
239
+ start: CLLocationCoordinate2D?,
240
+ completion: @escaping (Bool, String?) -> Void
241
+ ) {
242
+ var urlString = "moovit://directions?dest_lat=\(destination.latitude)&dest_lon=\(destination.longitude)"
243
+
244
+ if let startCoord = start {
245
+ urlString += "&orig_lat=\(startCoord.latitude)&orig_lon=\(startCoord.longitude)"
246
+ }
247
+
248
+ openURL(urlString, completion: completion)
249
+ }
250
+
251
+ private func launchYandex(
252
+ destination: CLLocationCoordinate2D,
253
+ start: CLLocationCoordinate2D?,
254
+ completion: @escaping (Bool, String?) -> Void
255
+ ) {
256
+ let urlString: String
257
+ if let startCoord = start {
258
+ urlString = "yandexnavi://build_route_on_map?lat_from=\(startCoord.latitude)&lon_from=\(startCoord.longitude)&lat_to=\(destination.latitude)&lon_to=\(destination.longitude)"
259
+ } else {
260
+ urlString = "yandexnavi://build_route_on_map?lat_to=\(destination.latitude)&lon_to=\(destination.longitude)"
261
+ }
262
+ openURL(urlString, completion: completion)
263
+ }
264
+
265
+ private func launchSygic(
266
+ destination: CLLocationCoordinate2D,
267
+ completion: @escaping (Bool, String?) -> Void
268
+ ) {
269
+ let urlString = "com.sygic.aura://coordinate|\(destination.longitude)|\(destination.latitude)|drive"
270
+ openURL(urlString, completion: completion)
271
+ }
272
+
273
+ private func launchHere(
274
+ destination: CLLocationCoordinate2D,
275
+ start: CLLocationCoordinate2D?,
276
+ completion: @escaping (Bool, String?) -> Void
277
+ ) {
278
+ var urlString = "here-route://\(destination.latitude),\(destination.longitude)"
279
+
280
+ if let startCoord = start {
281
+ urlString = "here-route://\(startCoord.latitude),\(startCoord.longitude)/\(destination.latitude),\(destination.longitude)"
282
+ }
283
+
284
+ openURL(urlString, completion: completion)
285
+ }
286
+
287
+ private func launchTomTom(
288
+ destination: CLLocationCoordinate2D,
289
+ completion: @escaping (Bool, String?) -> Void
290
+ ) {
291
+ let urlString = "tomtomgo://x-callback-url/navigate?destination=\(destination.latitude),\(destination.longitude)"
292
+ openURL(urlString, completion: completion)
293
+ }
294
+
295
+ private func launchMapsMe(
296
+ destination: CLLocationCoordinate2D,
297
+ completion: @escaping (Bool, String?) -> Void
298
+ ) {
299
+ let urlString = "mapsme://map?ll=\(destination.latitude),\(destination.longitude)"
300
+ openURL(urlString, completion: completion)
301
+ }
302
+
303
+ private func launchCabify(
304
+ destination: CLLocationCoordinate2D,
305
+ start: CLLocationCoordinate2D?,
306
+ completion: @escaping (Bool, String?) -> Void
307
+ ) {
308
+ var urlString = "cabify://rideto?lat=\(destination.latitude)&lng=\(destination.longitude)"
309
+
310
+ if let startCoord = start {
311
+ urlString = "cabify://ride?pickup[latitude]=\(startCoord.latitude)&pickup[longitude]=\(startCoord.longitude)&dropoff[latitude]=\(destination.latitude)&dropoff[longitude]=\(destination.longitude)"
312
+ }
313
+
314
+ openURL(urlString, completion: completion)
315
+ }
316
+
317
+ private func launchBaidu(
318
+ destination: CLLocationCoordinate2D,
319
+ start: CLLocationCoordinate2D?,
320
+ completion: @escaping (Bool, String?) -> Void
321
+ ) {
322
+ var urlString = "baidumap://map/direction?destination=\(destination.latitude),\(destination.longitude)&mode=driving"
323
+
324
+ if let startCoord = start {
325
+ urlString += "&origin=\(startCoord.latitude),\(startCoord.longitude)"
326
+ }
327
+
328
+ openURL(urlString, completion: completion)
329
+ }
330
+
331
+ private func launchGaode(
332
+ destination: CLLocationCoordinate2D,
333
+ start: CLLocationCoordinate2D?,
334
+ completion: @escaping (Bool, String?) -> Void
335
+ ) {
336
+ var urlString = "iosamap://path?dlat=\(destination.latitude)&dlon=\(destination.longitude)&dev=0&t=0"
337
+
338
+ if let startCoord = start {
339
+ urlString += "&slat=\(startCoord.latitude)&slon=\(startCoord.longitude)"
340
+ }
341
+
342
+ openURL(urlString, completion: completion)
343
+ }
344
+
345
+ private func openURL(_ urlString: String, completion: @escaping (Bool, String?) -> Void) {
346
+ guard let url = URL(string: urlString) else {
347
+ completion(false, "Invalid URL")
348
+ return
349
+ }
350
+
351
+ if UIApplication.shared.canOpenURL(url) {
352
+ UIApplication.shared.open(url, options: [:]) { success in
353
+ if success {
354
+ completion(true, nil)
355
+ } else {
356
+ completion(false, "Failed to open URL")
357
+ }
358
+ }
359
+ } else {
360
+ completion(false, "App not installed or URL scheme not supported")
361
+ }
362
+ }
363
+
364
+ public func isAppAvailable(app: String) -> Bool {
365
+ guard let appInfo = navigationApps[app] else {
366
+ return false
367
+ }
368
+
369
+ guard let url = URL(string: appInfo.urlScheme) else {
370
+ return false
371
+ }
372
+
373
+ return UIApplication.shared.canOpenURL(url)
374
+ }
375
+
376
+ public func getAvailableApps() -> [[String: Any]] {
377
+ var availableApps: [[String: Any]] = []
378
+
379
+ for (key, value) in navigationApps {
380
+ let available = isAppAvailable(app: key)
381
+ availableApps.append([
382
+ "app": key,
383
+ "name": value.name,
384
+ "available": available
385
+ ])
386
+ }
387
+
388
+ return availableApps
389
+ }
390
+
391
+ public func getSupportedApps() -> [String] {
392
+ return Array(navigationApps.keys)
393
+ }
394
+ }
@@ -0,0 +1,94 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import UIKit
4
+ import MapKit
5
+
6
+ /**
7
+ * Please read the Capacitor iOS Plugin Development Guide
8
+ * here: https://capacitorjs.com/docs/plugins/ios
9
+ */
10
+ @objc(LaunchNavigatorPlugin)
11
+ public class LaunchNavigatorPlugin: CAPPlugin, CAPBridgedPlugin {
12
+ public let identifier = "LaunchNavigatorPlugin"
13
+ public let jsName = "LaunchNavigator"
14
+ public let pluginMethods: [CAPPluginMethod] = [
15
+ CAPPluginMethod(name: "navigate", returnType: CAPPluginReturnPromise),
16
+ CAPPluginMethod(name: "isAppAvailable", returnType: CAPPluginReturnPromise),
17
+ CAPPluginMethod(name: "getAvailableApps", returnType: CAPPluginReturnPromise),
18
+ CAPPluginMethod(name: "getSupportedApps", returnType: CAPPluginReturnPromise),
19
+ CAPPluginMethod(name: "getDefaultApp", returnType: CAPPluginReturnPromise)
20
+ ]
21
+ private let implementation = LaunchNavigator()
22
+
23
+ @objc func navigate(_ call: CAPPluginCall) {
24
+ guard let destination = call.getArray("destination") as? [Double],
25
+ destination.count == 2 else {
26
+ call.reject("Destination coordinates [latitude, longitude] are required")
27
+ return
28
+ }
29
+
30
+ let lat = destination[0]
31
+ let lon = destination[1]
32
+ let options = call.getObject("options") ?? [:]
33
+
34
+ let app = options["app"] as? String ?? "apple_maps"
35
+ let transportMode = options["transportMode"] as? String ?? "driving"
36
+
37
+ var start: CLLocationCoordinate2D?
38
+ if let startCoords = options["start"] as? [Double], startCoords.count == 2 {
39
+ start = CLLocationCoordinate2D(latitude: startCoords[0], longitude: startCoords[1])
40
+ }
41
+
42
+ let startName = options["startName"] as? String
43
+ let destinationName = options["destinationName"] as? String
44
+
45
+ DispatchQueue.main.async {
46
+ self.implementation.navigate(
47
+ app: app,
48
+ destination: CLLocationCoordinate2D(latitude: lat, longitude: lon),
49
+ start: start,
50
+ startName: startName,
51
+ destinationName: destinationName,
52
+ transportMode: transportMode
53
+ ) { success, error in
54
+ if success {
55
+ call.resolve()
56
+ } else {
57
+ call.reject(error ?? "Failed to launch navigation app")
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ @objc func isAppAvailable(_ call: CAPPluginCall) {
64
+ guard let app = call.getString("app") else {
65
+ call.reject("App identifier is required")
66
+ return
67
+ }
68
+
69
+ let available = implementation.isAppAvailable(app: app)
70
+ call.resolve([
71
+ "available": available
72
+ ])
73
+ }
74
+
75
+ @objc func getAvailableApps(_ call: CAPPluginCall) {
76
+ let apps = implementation.getAvailableApps()
77
+ call.resolve([
78
+ "apps": apps
79
+ ])
80
+ }
81
+
82
+ @objc func getSupportedApps(_ call: CAPPluginCall) {
83
+ let apps = implementation.getSupportedApps()
84
+ call.resolve([
85
+ "apps": apps
86
+ ])
87
+ }
88
+
89
+ @objc func getDefaultApp(_ call: CAPPluginCall) {
90
+ call.resolve([
91
+ "app": "apple_maps"
92
+ ])
93
+ }
94
+ }
@@ -0,0 +1,15 @@
1
+ import XCTest
2
+ @testable import LaunchNavigatorPlugin
3
+
4
+ class LaunchNavigatorTests: XCTestCase {
5
+ func testEcho() {
6
+ // This is an example of a functional test case for a plugin.
7
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
8
+
9
+ let implementation = LaunchNavigator()
10
+ let value = "Hello, World!"
11
+ let result = implementation.echo(value)
12
+
13
+ XCTAssertEqual(value, result)
14
+ }
15
+ }
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@capgo/capacitor-launch-navigator",
3
+ "version": "7.0.0",
4
+ "description": "Capacitor plugin which launches native route navigation apps for Android, iOS",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Sources",
14
+ "ios/Tests",
15
+ "Package.swift",
16
+ "CapgoCapacitorLaunchNavigator.podspec"
17
+ ],
18
+ "author": "Martin Donadieu <martin@capgo.app>",
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/Cap-go/capacitor-launch-navigator.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/Cap-go/capacitor-launch-navigator/issues"
26
+ },
27
+ "keywords": [
28
+ "capacitor",
29
+ "plugin",
30
+ "native",
31
+ "launch",
32
+ "navigator",
33
+ "navigation",
34
+ "maps",
35
+ "google maps",
36
+ "apple maps",
37
+ "waze",
38
+ "uber", "lyft",
39
+ "transit",
40
+ "taxi", "directions",
41
+ "routing", "geo",
42
+ "location", "gps"
43
+ ],
44
+ "scripts": {
45
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
46
+ "verify:ios": "xcodebuild -scheme CapgoCapacitorLaunchNavigator -destination generic/platform=iOS",
47
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
48
+ "verify:web": "npm run build",
49
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
50
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
51
+ "eslint": "eslint . --ext ts",
52
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
53
+ "swiftlint": "node-swiftlint",
54
+ "docgen": "docgen --api LaunchNavigatorPlugin --output-readme README.md --output-json dist/docs.json",
55
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
56
+ "clean": "rimraf ./dist",
57
+ "watch": "tsc --watch",
58
+ "prepublishOnly": "npm run build"
59
+ },
60
+ "devDependencies": {
61
+ "@capacitor/android": "^7.0.0",
62
+ "@capacitor/core": "^7.0.0",
63
+ "@capacitor/docgen": "^0.3.0",
64
+ "@capacitor/ios": "^7.0.0",
65
+ "@ionic/eslint-config": "^0.4.0",
66
+ "@ionic/prettier-config": "^4.0.0",
67
+ "@ionic/swiftlint-config": "^2.0.0",
68
+ "eslint": "^8.57.0",
69
+ "prettier": "^3.4.2",
70
+ "prettier-plugin-java": "^2.6.6",
71
+ "rimraf": "^6.0.1",
72
+ "rollup": "^4.30.1",
73
+ "swiftlint": "^2.0.0",
74
+ "typescript": "~4.1.5"
75
+ },
76
+ "peerDependencies": {
77
+ "@capacitor/core": ">=7.0.0"
78
+ },
79
+ "prettier": "@ionic/prettier-config",
80
+ "swiftlint": "@ionic/swiftlint-config",
81
+ "eslintConfig": {
82
+ "extends": "@ionic/eslint-config/recommended"
83
+ },
84
+ "capacitor": {
85
+ "ios": {
86
+ "src": "ios"
87
+ },
88
+ "android": {
89
+ "src": "android"
90
+ }
91
+ }
92
+ }