@capawesome/capacitor-maps-launcher 0.0.1
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/CapawesomeCapacitorMapsLauncher.podspec +17 -0
- package/LICENSE +21 -0
- package/Package.swift +28 -0
- package/README.md +292 -0
- package/android/build.gradle +58 -0
- package/android/src/main/AndroidManifest.xml +10 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/MapsLauncher.java +200 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/MapsLauncherPlugin.java +117 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/classes/CustomException.java +20 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/classes/CustomExceptions.java +15 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/classes/options/Destination.java +57 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/classes/options/NavigateOptions.java +54 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/classes/results/GetAvailableAppsResult.java +29 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/classes/results/GetDefaultAppResult.java +25 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/interfaces/Callback.java +5 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/interfaces/EmptyCallback.java +5 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/interfaces/NonEmptyResultCallback.java +7 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mapslauncher/interfaces/Result.java +7 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +329 -0
- package/dist/esm/definitions.d.ts +195 -0
- package/dist/esm/definitions.js +47 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +7 -0
- package/dist/esm/web.js +13 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +74 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +77 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/Classes/Options/Destination.swift +36 -0
- package/ios/Plugin/Classes/Options/NavigateOptions.swift +23 -0
- package/ios/Plugin/Classes/Results/GetAvailableAppsResult.swift +16 -0
- package/ios/Plugin/Enums/CustomError.swift +36 -0
- package/ios/Plugin/Info.plist +24 -0
- package/ios/Plugin/MapsLauncher.swift +182 -0
- package/ios/Plugin/MapsLauncherPlugin.swift +71 -0
- package/ios/Plugin/Protocols/Result.swift +5 -0
- package/package.json +94 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
import CoreLocation
|
|
4
|
+
import MapKit
|
|
5
|
+
import UIKit
|
|
6
|
+
|
|
7
|
+
@objc public class MapsLauncher: NSObject {
|
|
8
|
+
static let appAppleMaps = "appleMaps"
|
|
9
|
+
static let appGoogleMaps = "googleMaps"
|
|
10
|
+
static let appWaze = "waze"
|
|
11
|
+
static let schemeGoogleMaps = "comgooglemaps"
|
|
12
|
+
static let schemeWaze = "waze"
|
|
13
|
+
|
|
14
|
+
private let plugin: MapsLauncherPlugin
|
|
15
|
+
|
|
16
|
+
init(plugin: MapsLauncherPlugin) {
|
|
17
|
+
self.plugin = plugin
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@objc public func getAvailableApps(completion: @escaping (GetAvailableAppsResult?, Error?) -> Void) {
|
|
21
|
+
var apps = [Self.appAppleMaps]
|
|
22
|
+
if canOpen(Self.schemeGoogleMaps) {
|
|
23
|
+
apps.append(Self.appGoogleMaps)
|
|
24
|
+
}
|
|
25
|
+
if canOpen(Self.schemeWaze) {
|
|
26
|
+
apps.append(Self.appWaze)
|
|
27
|
+
}
|
|
28
|
+
completion(GetAvailableAppsResult(apps: apps), nil)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@objc public func navigate(_ options: NavigateOptions, completion: @escaping (Error?) -> Void) {
|
|
32
|
+
switch options.app {
|
|
33
|
+
case nil, Self.appAppleMaps:
|
|
34
|
+
openAppleMaps(options, completion: completion)
|
|
35
|
+
case Self.appGoogleMaps:
|
|
36
|
+
openGoogleMaps(options, completion: completion)
|
|
37
|
+
case Self.appWaze:
|
|
38
|
+
openWaze(options, completion: completion)
|
|
39
|
+
default:
|
|
40
|
+
completion(CustomError.appNotAvailable)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private func appleMapsDirectionsMode(_ travelMode: String?) -> String {
|
|
45
|
+
switch travelMode {
|
|
46
|
+
case "walking":
|
|
47
|
+
return MKLaunchOptionsDirectionsModeWalking
|
|
48
|
+
case "transit":
|
|
49
|
+
return MKLaunchOptionsDirectionsModeTransit
|
|
50
|
+
case "driving":
|
|
51
|
+
return MKLaunchOptionsDirectionsModeDriving
|
|
52
|
+
default:
|
|
53
|
+
return MKLaunchOptionsDirectionsModeDefault
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private func appleMapsDirflg(_ travelMode: String?) -> String? {
|
|
58
|
+
switch travelMode {
|
|
59
|
+
case "walking":
|
|
60
|
+
return "w"
|
|
61
|
+
case "transit":
|
|
62
|
+
return "r"
|
|
63
|
+
case "driving":
|
|
64
|
+
return "d"
|
|
65
|
+
default:
|
|
66
|
+
return nil
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private func buildAppleMapsUrl(_ options: NavigateOptions) -> URL? {
|
|
71
|
+
var components = URLComponents(string: "https://maps.apple.com/")
|
|
72
|
+
var queryItems = [URLQueryItem(name: "daddr", value: formatDestination(options.destination))]
|
|
73
|
+
if let start = options.start {
|
|
74
|
+
queryItems.append(URLQueryItem(name: "saddr", value: formatDestination(start)))
|
|
75
|
+
}
|
|
76
|
+
if let dirflg = appleMapsDirflg(options.travelMode) {
|
|
77
|
+
queryItems.append(URLQueryItem(name: "dirflg", value: dirflg))
|
|
78
|
+
}
|
|
79
|
+
components?.queryItems = queryItems
|
|
80
|
+
return components?.url
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private func buildUrl(scheme: String, queryItems: [URLQueryItem]) -> URL? {
|
|
84
|
+
var components = URLComponents()
|
|
85
|
+
components.scheme = scheme
|
|
86
|
+
components.host = ""
|
|
87
|
+
components.queryItems = queryItems
|
|
88
|
+
return components.url
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private func canOpen(_ scheme: String) -> Bool {
|
|
92
|
+
guard let url = URL(string: scheme + "://") else {
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
return UIApplication.shared.canOpenURL(url)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private func formatDestination(_ destination: Destination) -> String {
|
|
99
|
+
if let coordinate = destination.coordinate {
|
|
100
|
+
return "\(coordinate.latitude),\(coordinate.longitude)"
|
|
101
|
+
}
|
|
102
|
+
return destination.address ?? ""
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private func googleDirectionsMode(_ travelMode: String?) -> String {
|
|
106
|
+
switch travelMode {
|
|
107
|
+
case "walking", "bicycling", "transit":
|
|
108
|
+
return travelMode ?? "driving"
|
|
109
|
+
default:
|
|
110
|
+
return "driving"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private func open(_ url: URL, completion: @escaping (Error?) -> Void) {
|
|
115
|
+
DispatchQueue.main.async {
|
|
116
|
+
UIApplication.shared.open(url, options: [:]) { success in
|
|
117
|
+
completion(success ? nil : CustomError.launchFailed)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private func openAppleMaps(_ options: NavigateOptions, completion: @escaping (Error?) -> Void) {
|
|
123
|
+
let canUseMapItems = options.destination.coordinate != nil && (options.start == nil || options.start?.coordinate != nil)
|
|
124
|
+
if canUseMapItems, let destinationCoordinate = options.destination.coordinate {
|
|
125
|
+
let destinationItem = MKMapItem(placemark: MKPlacemark(coordinate: destinationCoordinate))
|
|
126
|
+
var items: [MKMapItem] = []
|
|
127
|
+
if let startCoordinate = options.start?.coordinate {
|
|
128
|
+
items.append(MKMapItem(placemark: MKPlacemark(coordinate: startCoordinate)))
|
|
129
|
+
} else {
|
|
130
|
+
items.append(MKMapItem.forCurrentLocation())
|
|
131
|
+
}
|
|
132
|
+
items.append(destinationItem)
|
|
133
|
+
let launchOptions = [MKLaunchOptionsDirectionsModeKey: appleMapsDirectionsMode(options.travelMode)]
|
|
134
|
+
DispatchQueue.main.async {
|
|
135
|
+
MKMapItem.openMaps(with: items, launchOptions: launchOptions)
|
|
136
|
+
completion(nil)
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
guard let url = buildAppleMapsUrl(options) else {
|
|
140
|
+
completion(CustomError.launchFailed)
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
open(url, completion: completion)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private func openGoogleMaps(_ options: NavigateOptions, completion: @escaping (Error?) -> Void) {
|
|
148
|
+
guard canOpen(Self.schemeGoogleMaps) else {
|
|
149
|
+
completion(CustomError.appNotAvailable)
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
var queryItems = [URLQueryItem(name: "daddr", value: formatDestination(options.destination))]
|
|
153
|
+
if let start = options.start {
|
|
154
|
+
queryItems.append(URLQueryItem(name: "saddr", value: formatDestination(start)))
|
|
155
|
+
}
|
|
156
|
+
queryItems.append(URLQueryItem(name: "directionsmode", value: googleDirectionsMode(options.travelMode)))
|
|
157
|
+
guard let url = buildUrl(scheme: Self.schemeGoogleMaps, queryItems: queryItems) else {
|
|
158
|
+
completion(CustomError.launchFailed)
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
open(url, completion: completion)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private func openWaze(_ options: NavigateOptions, completion: @escaping (Error?) -> Void) {
|
|
165
|
+
guard canOpen(Self.schemeWaze) else {
|
|
166
|
+
completion(CustomError.appNotAvailable)
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
var queryItems: [URLQueryItem] = []
|
|
170
|
+
if let coordinate = options.destination.coordinate {
|
|
171
|
+
queryItems.append(URLQueryItem(name: "ll", value: "\(coordinate.latitude),\(coordinate.longitude)"))
|
|
172
|
+
} else if let address = options.destination.address {
|
|
173
|
+
queryItems.append(URLQueryItem(name: "q", value: address))
|
|
174
|
+
}
|
|
175
|
+
queryItems.append(URLQueryItem(name: "navigate", value: "yes"))
|
|
176
|
+
guard let url = buildUrl(scheme: Self.schemeWaze, queryItems: queryItems) else {
|
|
177
|
+
completion(CustomError.launchFailed)
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
open(url, completion: completion)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
@objc(MapsLauncherPlugin)
|
|
5
|
+
public class MapsLauncherPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
6
|
+
public let identifier = "MapsLauncherPlugin"
|
|
7
|
+
public let jsName = "MapsLauncher"
|
|
8
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
9
|
+
CAPPluginMethod(name: "getAvailableApps", returnType: CAPPluginReturnPromise),
|
|
10
|
+
CAPPluginMethod(name: "getDefaultApp", returnType: CAPPluginReturnPromise),
|
|
11
|
+
CAPPluginMethod(name: "navigate", returnType: CAPPluginReturnPromise)
|
|
12
|
+
]
|
|
13
|
+
public static let tag = "MapsLauncherPlugin"
|
|
14
|
+
|
|
15
|
+
private var implementation: MapsLauncher?
|
|
16
|
+
|
|
17
|
+
override public func load() {
|
|
18
|
+
self.implementation = MapsLauncher(plugin: self)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@objc func getAvailableApps(_ call: CAPPluginCall) {
|
|
22
|
+
implementation?.getAvailableApps { result, error in
|
|
23
|
+
if let error = error {
|
|
24
|
+
self.rejectCall(call, error)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
self.resolveCall(call, result)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@objc func getDefaultApp(_ call: CAPPluginCall) {
|
|
32
|
+
rejectCallAsUnimplemented(call)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@objc func navigate(_ call: CAPPluginCall) {
|
|
36
|
+
do {
|
|
37
|
+
let options = try NavigateOptions(call)
|
|
38
|
+
implementation?.navigate(options) { error in
|
|
39
|
+
if let error = error {
|
|
40
|
+
self.rejectCall(call, error)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
self.resolveCall(call)
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
self.rejectCall(call, error)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private func rejectCall(_ call: CAPPluginCall, _ error: Error) {
|
|
51
|
+
CAPLog.print("[", MapsLauncherPlugin.tag, "] ", error)
|
|
52
|
+
let code = (error as? CustomError)?.code
|
|
53
|
+
call.reject(error.localizedDescription, code)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private func rejectCallAsUnimplemented(_ call: CAPPluginCall) {
|
|
57
|
+
call.unimplemented("This method is not available on this platform.")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private func resolveCall(_ call: CAPPluginCall) {
|
|
61
|
+
call.resolve()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private func resolveCall(_ call: CAPPluginCall, _ result: Result?) {
|
|
65
|
+
if let result = result?.toJSObject() as? JSObject {
|
|
66
|
+
call.resolve(result)
|
|
67
|
+
} else {
|
|
68
|
+
call.resolve()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@capawesome/capacitor-maps-launcher",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Capacitor plugin to launch navigation apps with turn-by-turn directions.",
|
|
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/Plugin/",
|
|
14
|
+
"CapawesomeCapacitorMapsLauncher.podspec",
|
|
15
|
+
"Package.swift"
|
|
16
|
+
],
|
|
17
|
+
"author": "Robin Genz <mail@robingenz.dev>",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/capawesome-team/capacitor-plugins.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/capawesome-team/capacitor-plugins/issues"
|
|
25
|
+
},
|
|
26
|
+
"funding": [
|
|
27
|
+
{
|
|
28
|
+
"type": "github",
|
|
29
|
+
"url": "https://github.com/sponsors/capawesome-team/"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "opencollective",
|
|
33
|
+
"url": "https://opencollective.com/capawesome"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"homepage": "https://capawesome.io/docs/plugins/maps-launcher/",
|
|
37
|
+
"keywords": [
|
|
38
|
+
"capacitor",
|
|
39
|
+
"plugin",
|
|
40
|
+
"native",
|
|
41
|
+
"maps",
|
|
42
|
+
"navigation",
|
|
43
|
+
"launch navigator"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
|
|
47
|
+
"verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
|
|
48
|
+
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
49
|
+
"verify:web": "npm run build",
|
|
50
|
+
"lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
|
|
51
|
+
"fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
|
|
52
|
+
"eslint": "eslint . --ext ts",
|
|
53
|
+
"prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
|
|
54
|
+
"swiftlint": "node-swiftlint",
|
|
55
|
+
"docgen": "docgen --api MapsLauncherPlugin --output-readme README.md --output-json dist/docs.json",
|
|
56
|
+
"build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
|
|
57
|
+
"clean": "rimraf ./dist",
|
|
58
|
+
"watch": "tsc --watch",
|
|
59
|
+
"ios:pod:install": "cd ios && pod install --repo-update && cd ..",
|
|
60
|
+
"ios:spm:install": "cd ios && swift package resolve && cd ..",
|
|
61
|
+
"prepublishOnly": "npm run build"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@capacitor/android": "8.0.0",
|
|
65
|
+
"@capacitor/cli": "8.0.0",
|
|
66
|
+
"@capacitor/core": "8.0.0",
|
|
67
|
+
"@capacitor/docgen": "0.3.1",
|
|
68
|
+
"@capacitor/ios": "8.0.0",
|
|
69
|
+
"@ionic/eslint-config": "0.4.0",
|
|
70
|
+
"eslint": "8.57.0",
|
|
71
|
+
"prettier-plugin-java": "2.6.7",
|
|
72
|
+
"rimraf": "6.1.2",
|
|
73
|
+
"rollup": "4.53.3",
|
|
74
|
+
"swiftlint": "2.0.0",
|
|
75
|
+
"typescript": "5.9.3"
|
|
76
|
+
},
|
|
77
|
+
"peerDependencies": {
|
|
78
|
+
"@capacitor/core": ">=8.0.0"
|
|
79
|
+
},
|
|
80
|
+
"eslintConfig": {
|
|
81
|
+
"extends": "@ionic/eslint-config/recommended"
|
|
82
|
+
},
|
|
83
|
+
"capacitor": {
|
|
84
|
+
"ios": {
|
|
85
|
+
"src": "ios"
|
|
86
|
+
},
|
|
87
|
+
"android": {
|
|
88
|
+
"src": "android"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"publishConfig": {
|
|
92
|
+
"access": "public"
|
|
93
|
+
}
|
|
94
|
+
}
|