capacitor-google-navigation 0.0.6 → 0.0.8
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
CHANGED
|
@@ -490,6 +490,27 @@ The Android Navigation SDK validates the API key from `AndroidManifest.xml`. Ens
|
|
|
490
490
|
**iOS — "This app has attempted to access privacy-sensitive data"**
|
|
491
491
|
Add both `NSLocationWhenInUseUsageDescription` and `NSLocationAlwaysAndWhenInUseUsageDescription` to `Info.plist` before calling `initialize()`.
|
|
492
492
|
|
|
493
|
+
**iOS — App crashes with "Invalid parameter not satisfying: CLClientIsBackgroundable"**
|
|
494
|
+
The Navigation SDK requires background location capability to track position during guidance. In Xcode:
|
|
495
|
+
1. Select your app target → **Signing & Capabilities** → **+ Capability** → **Background Modes**
|
|
496
|
+
2. Check **Location updates**
|
|
497
|
+
|
|
498
|
+
Also ensure all three keys are present in `Info.plist`:
|
|
499
|
+
```xml
|
|
500
|
+
<key>NSLocationWhenInUseUsageDescription</key>
|
|
501
|
+
<string>This app uses your location for navigation.</string>
|
|
502
|
+
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
|
503
|
+
<string>This app uses your location for navigation, including in the background.</string>
|
|
504
|
+
<key>NSLocationAlwaysUsageDescription</key>
|
|
505
|
+
<string>This app uses your location for navigation, including in the background.</string>
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**iOS — "This application has been blocked by the Google Navigation SDK"**
|
|
509
|
+
The Navigation SDK requires explicit enrollment — it is not available to all Google Cloud projects by default. Ensure:
|
|
510
|
+
1. The **Navigation SDK for iOS** is enabled under APIs & Services → Library in Google Cloud Console
|
|
511
|
+
2. Your project has been granted access (you may need to request it via the [Navigation SDK get started page](https://developers.google.com/maps/documentation/navigation/ios-sdk/get-started))
|
|
512
|
+
3. Billing is active on the project
|
|
513
|
+
|
|
493
514
|
**CocoaPods not found / pod install fails**
|
|
494
515
|
Make sure CocoaPods is installed (`sudo gem install cocoapods`) and run `npx cap sync` before `pod install`.
|
|
495
516
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.attributeai.navigation;
|
|
2
2
|
|
|
3
|
+
import android.app.Activity;
|
|
3
4
|
import android.graphics.Color;
|
|
4
5
|
import android.graphics.Typeface;
|
|
5
6
|
import android.os.Bundle;
|
|
@@ -25,6 +26,7 @@ import com.google.android.libraries.navigation.NavigationView;
|
|
|
25
26
|
public class NavigationFragment extends Fragment {
|
|
26
27
|
|
|
27
28
|
private NavigationView navigationView;
|
|
29
|
+
private Button closeButton;
|
|
28
30
|
private Runnable onCloseListener;
|
|
29
31
|
|
|
30
32
|
public static NavigationFragment newInstance() {
|
|
@@ -44,57 +46,29 @@ public class NavigationFragment extends Fragment {
|
|
|
44
46
|
) {
|
|
45
47
|
navigationView = new NavigationView(requireContext());
|
|
46
48
|
navigationView.onCreate(savedInstanceState);
|
|
47
|
-
|
|
48
|
-
FrameLayout root = new FrameLayout(requireContext());
|
|
49
|
-
root.addView(navigationView, new FrameLayout.LayoutParams(
|
|
50
|
-
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
51
|
-
FrameLayout.LayoutParams.MATCH_PARENT
|
|
52
|
-
));
|
|
53
|
-
|
|
54
|
-
Button closeButton = new Button(requireContext());
|
|
55
|
-
closeButton.setText("✕");
|
|
56
|
-
closeButton.setTextColor(Color.WHITE);
|
|
57
|
-
closeButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
|
|
58
|
-
closeButton.setTypeface(null, Typeface.BOLD);
|
|
59
|
-
closeButton.setBackgroundColor(Color.argb(153, 0, 0, 0)); // 60% black
|
|
60
|
-
closeButton.setOnClickListener(v -> {
|
|
61
|
-
if (onCloseListener != null) onCloseListener.run();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
int sizePx = (int) TypedValue.applyDimension(
|
|
65
|
-
TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics()
|
|
66
|
-
);
|
|
67
|
-
int marginPx = (int) TypedValue.applyDimension(
|
|
68
|
-
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
FrameLayout.LayoutParams btnParams = new FrameLayout.LayoutParams(sizePx, sizePx);
|
|
72
|
-
btnParams.gravity = Gravity.TOP | Gravity.START;
|
|
73
|
-
btnParams.topMargin = marginPx;
|
|
74
|
-
btnParams.leftMargin = marginPx;
|
|
75
|
-
root.addView(closeButton, btnParams);
|
|
76
|
-
|
|
77
|
-
return root;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
@Override
|
|
81
|
-
public void onStart() {
|
|
82
|
-
super.onStart();
|
|
83
|
-
if (navigationView != null) navigationView.onStart();
|
|
49
|
+
return navigationView;
|
|
84
50
|
}
|
|
85
51
|
|
|
86
52
|
@Override
|
|
87
53
|
public void onResume() {
|
|
88
54
|
super.onResume();
|
|
89
55
|
if (navigationView != null) navigationView.onResume();
|
|
56
|
+
attachCloseButtonToDecorView();
|
|
90
57
|
}
|
|
91
58
|
|
|
92
59
|
@Override
|
|
93
60
|
public void onPause() {
|
|
61
|
+
detachCloseButtonFromDecorView();
|
|
94
62
|
if (navigationView != null) navigationView.onPause();
|
|
95
63
|
super.onPause();
|
|
96
64
|
}
|
|
97
65
|
|
|
66
|
+
@Override
|
|
67
|
+
public void onStart() {
|
|
68
|
+
super.onStart();
|
|
69
|
+
if (navigationView != null) navigationView.onStart();
|
|
70
|
+
}
|
|
71
|
+
|
|
98
72
|
@Override
|
|
99
73
|
public void onStop() {
|
|
100
74
|
if (navigationView != null) navigationView.onStop();
|
|
@@ -103,6 +77,7 @@ public class NavigationFragment extends Fragment {
|
|
|
103
77
|
|
|
104
78
|
@Override
|
|
105
79
|
public void onDestroyView() {
|
|
80
|
+
detachCloseButtonFromDecorView();
|
|
106
81
|
if (navigationView != null) navigationView.onDestroy();
|
|
107
82
|
super.onDestroyView();
|
|
108
83
|
}
|
|
@@ -112,4 +87,51 @@ public class NavigationFragment extends Fragment {
|
|
|
112
87
|
super.onSaveInstanceState(outState);
|
|
113
88
|
if (navigationView != null) navigationView.onSaveInstanceState(outState);
|
|
114
89
|
}
|
|
90
|
+
|
|
91
|
+
// MARK: - Close button attached to the Activity decor view, above all SDK UI
|
|
92
|
+
|
|
93
|
+
private void attachCloseButtonToDecorView() {
|
|
94
|
+
Activity activity = getActivity();
|
|
95
|
+
if (activity == null || closeButton != null) return;
|
|
96
|
+
|
|
97
|
+
int sizePx = (int) TypedValue.applyDimension(
|
|
98
|
+
TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics()
|
|
99
|
+
);
|
|
100
|
+
int marginPx = (int) TypedValue.applyDimension(
|
|
101
|
+
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()
|
|
102
|
+
);
|
|
103
|
+
int statusBarHeight = getStatusBarHeight(activity);
|
|
104
|
+
|
|
105
|
+
Button button = new Button(requireContext());
|
|
106
|
+
button.setText("✕");
|
|
107
|
+
button.setTextColor(Color.WHITE);
|
|
108
|
+
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
|
|
109
|
+
button.setTypeface(null, Typeface.BOLD);
|
|
110
|
+
button.setBackgroundColor(Color.argb(153, 0, 0, 0));
|
|
111
|
+
button.setOnClickListener(v -> {
|
|
112
|
+
if (onCloseListener != null) onCloseListener.run();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(sizePx, sizePx);
|
|
116
|
+
params.gravity = Gravity.TOP | Gravity.START;
|
|
117
|
+
params.topMargin = statusBarHeight + marginPx;
|
|
118
|
+
params.leftMargin = marginPx;
|
|
119
|
+
|
|
120
|
+
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
|
|
121
|
+
decorView.addView(button, params);
|
|
122
|
+
this.closeButton = button;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private void detachCloseButtonFromDecorView() {
|
|
126
|
+
Activity activity = getActivity();
|
|
127
|
+
if (activity == null || closeButton == null) return;
|
|
128
|
+
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
|
|
129
|
+
decorView.removeView(closeButton);
|
|
130
|
+
closeButton = null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private int getStatusBarHeight(Activity activity) {
|
|
134
|
+
int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
|
|
135
|
+
return resourceId > 0 ? activity.getResources().getDimensionPixelSize(resourceId) : 0;
|
|
136
|
+
}
|
|
115
137
|
}
|
|
@@ -5,6 +5,7 @@ import GoogleNavigation
|
|
|
5
5
|
class NavigationMapViewController: UIViewController {
|
|
6
6
|
private let session: GMSNavigationSession
|
|
7
7
|
private var mapView: GMSMapView?
|
|
8
|
+
private var overlayWindow: UIWindow?
|
|
8
9
|
var onDismiss: (() -> Void)?
|
|
9
10
|
|
|
10
11
|
init(session: GMSNavigationSession) {
|
|
@@ -17,26 +18,51 @@ class NavigationMapViewController: UIViewController {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
override func loadView() {
|
|
21
|
+
let container = UIView(frame: UIScreen.main.bounds)
|
|
22
|
+
|
|
20
23
|
let options = GMSMapViewOptions()
|
|
21
|
-
options.frame =
|
|
24
|
+
options.frame = container.bounds
|
|
22
25
|
let mapView = GMSMapView(options: options)
|
|
26
|
+
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
27
|
+
container.addSubview(mapView)
|
|
23
28
|
self.mapView = mapView
|
|
24
|
-
|
|
29
|
+
|
|
30
|
+
self.view = container
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
override func viewDidLoad() {
|
|
28
34
|
super.viewDidLoad()
|
|
29
|
-
|
|
30
35
|
guard let mapView = mapView else { return }
|
|
31
36
|
let enabled = mapView.enableNavigation(with: session)
|
|
32
37
|
if enabled {
|
|
33
38
|
mapView.cameraMode = .following
|
|
34
39
|
}
|
|
40
|
+
}
|
|
35
41
|
|
|
36
|
-
|
|
42
|
+
override func viewDidAppear(_ animated: Bool) {
|
|
43
|
+
super.viewDidAppear(animated)
|
|
44
|
+
addCloseButtonWindow()
|
|
37
45
|
}
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
override func viewDidDisappear(_ animated: Bool) {
|
|
48
|
+
super.viewDidDisappear(animated)
|
|
49
|
+
tearDownCloseButtonWindow()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// MARK: - Close button in a top-level UIWindow above all SDK UI
|
|
53
|
+
|
|
54
|
+
private func addCloseButtonWindow() {
|
|
55
|
+
guard let scene = view.window?.windowScene else { return }
|
|
56
|
+
|
|
57
|
+
let window = UIWindow(windowScene: scene)
|
|
58
|
+
window.windowLevel = .alert + 1
|
|
59
|
+
window.backgroundColor = .clear
|
|
60
|
+
window.isHidden = false
|
|
61
|
+
|
|
62
|
+
let overlayVC = UIViewController()
|
|
63
|
+
overlayVC.view.backgroundColor = .clear
|
|
64
|
+
window.rootViewController = overlayVC
|
|
65
|
+
|
|
40
66
|
let button = UIButton(type: .system)
|
|
41
67
|
button.setImage(UIImage(systemName: "xmark"), for: .normal)
|
|
42
68
|
button.tintColor = .white
|
|
@@ -44,17 +70,25 @@ class NavigationMapViewController: UIViewController {
|
|
|
44
70
|
button.layer.cornerRadius = 20
|
|
45
71
|
button.translatesAutoresizingMaskIntoConstraints = false
|
|
46
72
|
button.addTarget(self, action: #selector(closeTapped), for: .touchUpInside)
|
|
47
|
-
view.addSubview(button)
|
|
73
|
+
overlayVC.view.addSubview(button)
|
|
48
74
|
|
|
49
75
|
NSLayoutConstraint.activate([
|
|
50
|
-
button.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16),
|
|
51
|
-
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
|
|
76
|
+
button.topAnchor.constraint(equalTo: overlayVC.view.safeAreaLayoutGuide.topAnchor, constant: 16),
|
|
77
|
+
button.leadingAnchor.constraint(equalTo: overlayVC.view.leadingAnchor, constant: 16),
|
|
52
78
|
button.widthAnchor.constraint(equalToConstant: 40),
|
|
53
|
-
button.heightAnchor.constraint(equalToConstant: 40)
|
|
79
|
+
button.heightAnchor.constraint(equalToConstant: 40),
|
|
54
80
|
])
|
|
81
|
+
|
|
82
|
+
self.overlayWindow = window
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private func tearDownCloseButtonWindow() {
|
|
86
|
+
overlayWindow?.isHidden = true
|
|
87
|
+
overlayWindow = nil
|
|
55
88
|
}
|
|
56
89
|
|
|
57
90
|
@objc private func closeTapped() {
|
|
91
|
+
tearDownCloseButtonWindow()
|
|
58
92
|
dismiss(animated: true) { [weak self] in
|
|
59
93
|
self?.onDismiss?()
|
|
60
94
|
}
|