I have ionic native geolocation plugin installed
"#ionic-native/geolocation": "^4.15.0"
I have also tried "4.6.0" and "4.20.0". It is working absolutely fine when I keep my GPS enabled before going to that page. But when GPS is not enabled, It won't ask me to turn it ON, gives an error on console and carry undefined coordinates with it.
I wrote the method of getCurrentPosition in constructor/ionViewDidLoad. So even user enable it on that page, the method does not invoke and the coordinates remain undefined.
Following is code
this.geolocation
.getCurrentPosition()
.then(resp => {
console.log(resp);
this.longitude = resp.coords.longitude;
this.latitude = resp.coords.latitude;
})
.catch(error => {
console.log("Error getting location", error);
});
I don't know if I'll have to give manual permissions or what?? I did the same before a couple of months before and everything was fine. First time I am facing this kind of issue. Please help me to get out of this.
you should manually ask permission and request the user to enable location. You can do this with the Diagnostic plugin (#ionic-native/diagnostic). You should use the following methods:
diagnostic.isLocationEnabled()
diagnostic.isLocationAvailable()
diagnostic.requestLocationAuthorization()
If you want to update location after permission is granted you you can use this method:
diagnostic.registerLocationStateChangeHandler()
You can pass a callback here check if location is enabled and available de what you need.
Install the Diagnostics plugin from here: https://ionicframework.com/docs/v3/native/diagnostic/, then check if location is enabled with diagnostics.isLocationAvailable(), if not, prompt the user to enable it from the device settings screen using diagnostics.switchToLocationSettings().
I am trying to access the HTML Geolocation API available in Android WebView (using SDK version 24).
The main problem is that the call to navigator.geolocation.getCurrentPosition() in JavaScript never returns (neither with an error, nor with position data), while on application side I check for permissions and properly pass them to WebView using android.webkit.GeolocationPermissions.Callback class.
UPDATE: Just to clarify here, by "never returns" I mean that none of the two supplied callbacks navigator.geolocation.getCurrentPosition(success, error) are ever called.
In a sample app I built to test this (with just one small activity hosting WebView) I declare the permissions in manifest and request them properly on App start. I see the prompt and can grant or deny permission to location information.
Manifest:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Code in the main form:
public boolean checkFineLocationPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION) && !mIsPermissionDialogShown) {
showPermissionDialog(R.string.dialog_permission_location);
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSION_ACCESS_FINE_LOCATION);
}
return false;
} else {
return true;
}
}
I can check for permissions during runtime using Context.checkSelfPermission() and I see that the respective permissions are granted to my app.
Then I try to open a web page in a WebView control.
I enable all required options in the settings:
mWebSettings.setJavaScriptEnabled(true);
mWebSettings.setAppCacheEnabled(true);
mWebSettings.setDatabaseEnabled(true);
mWebSettings.setDomStorageEnabled(true);
mWebSettings.setGeolocationEnabled(true);
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
mWebSettings.setSupportZoom(true);
I use the following WebChromeClient overload for handling geolocation requests from JavaScript:
protected class EmbeddedChromeClient extends android.webkit.WebChromeClient {
#Override
public void onGeolocationPermissionsShowPrompt(String origin,
android.webkit.GeolocationPermissions.Callback callback) {
// do we need to request permissions ?
if (ContextCompat.checkSelfPermission(EmbeddedBrowserActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// this should never happen, it means user revoked permissions
// need to warn and quit?
callback.invoke(origin, false, false);
}
else {
callback.invoke(origin, true, true);
}
}
}
To test this I use the following code (taken from Mozilla API help page, shortened here):
function geoFindMe() {
function success(position) {}
function error() {}
navigator.geolocation.getCurrentPosition(success, error);
}
What I see is that the call tonavigator.geolocation.getCurrentPosition(success, error) in JavaScript never returns. I see that onGeolocationPermissionsShowPrompt() method in Java gets properly called and as I check for permissions there I always get the result 0, i.e. PackageManager.PERMISSION_GRANTED, so callback.invoke(origin, true, true) is executed on every call. If I try several times, I see several calls to my Java code. Still, nothing happens on the JavaScript side here after I call invoke().
I added the code to check for granted permissions using the invocation of getOrigins(ValueCallback<Set<String>> callback) in GeolocationPermissions class, as described here in the documentation. I see in the callback that my origins are allowed to request locations (they are listed in the set).
Any ideas what might be wrong here?
Try with options to set timeout (source):
var options = {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
};
navigator.geolocation.getCurrentPosition(success, error, options);
If it fails then try to override getCurrentPosition (source):
(function() {
if (navigator.geolocation) {
function PositionError(code, message) {
this.code = code;
this.message = message;
}
PositionError.PERMISSION_DENIED = 1;
PositionError.POSITION_UNAVAILABLE = 2;
PositionError.TIMEOUT = 3;
PositionError.prototype = new Error();
navigator.geolocation._getCurrentPosition = navigator.geolocation.getCurrentPosition;
navigator.geolocation.getCurrentPosition = function(success, failure, options) {
var successHandler = function(position) {
if ((position.coords.latitude == 0 && position.coords.longitude == 0) ||
(position.coords.latitude == 37.38600158691406 && position.coords.longitude == -122.08200073242188))
return failureHandler(new PositionError(PositionError.POSITION_UNAVAILABLE, 'Position unavailable'));
failureHandler = function() {};
success(position);
}
var failureHandler = function(error) {
failureHandler = function() {};
failure(error);
}
navigator.geolocation._getCurrentPosition(successHandler, failureHandler, options);
window.setTimeout(function() { failureHandler(new PositionError(PositionError.TIMEOUT, 'Timed out')) }, 10000);
}
}
})();
As a third option annotate with #JavascriptInterface (source) in EmbeddedChromeClient
Also add at the proper place in your code:
mWebSettings.setJavaScriptEnabled(true);
//...
mWebSettings.setSupportZoom(true);
webView.addJavascriptInterface(new EmbeddedChromeClient(webView), "injectedObject");
webView.loadData("html here", "text/html", null);
The last option is just to use tags in html, load the html from disk storage, replace tags in the calling function, load the html/string in the webView. I have used this approach before in Android when positioning frustrated me too much. Then you don't have to worry about https either.
Looks like there are 2 different issues in your case:
getCurrentPosition never fails
getCurrentPosition never succeed
First point could be just because method has infinite timeout
The default value is Infinity, meaning that getCurrentPosition() won't return until the position is available.
Second point could be tricky, there is a param maximumAge which means
The PositionOptions.maximumAge property is a positive long value indicating the maximum age in milliseconds of a possible cached position that is acceptable to return. If set to 0, it means that the device cannot use a cached position and must attempt to retrieve the real current position. If set to Infinity the device must return a cached position regardless of its age.
0 by default means that device won't use cached position and will try to fetch the real one and it could be an issue for long response.
Also you could check this reporst which could mean that this API doesn't work really good on Android:
https://github.com/ionic-team/ionic-native/issues/1958
https://issues.apache.org/jira/browse/CB-13241
*Cordova leaves geolocation stuff for browser.
Change your request for permission like this,
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
},0);
This seems to work.
Actually, navigator is a child of browser global object, the window. you should first access to window then call the navigator. In some modern browsers, In addition to the window, some child object is presented like a global object, for example: location, navigator and etc.
The WebView has no global object window. So you can add it manually, for this action please read this medium article.
Maybe your code will be like below:
my_web_view.evaluateJavascript("javascript: " + "updateFromAndroid(\"" + edit_text_to_web.text + "\")", null);
And then add JavaScript interface, like the window object, to this evaluator:
my_web_view.addJavascriptInterface(JavaScriptInterface(), JAVASCRIPT_OBJ); //JAVASCRIPT_OBJ: like window, like navigator, like location and etc.
For applications targeting Android N and later SDKs (API level > Build.VERSION_CODES.M), the method onGeolocationPermissionsShowPrompt (String origin, GeolocationPermissions.Callback callback) is only called for requests originating from secure origins such as HTTPS. On non-secure origins, geolocation requests are automatically denied.
You could narrow down your problem by yourself if you had tried putting a breakpoint or a log inside the method.
You have two options:
Target a lower level API, which is obviously much easier but not really appreciated.
Set up SSL in your website.
In case some of you are still experiencing this issue, I was able to get the navigator.geolocation.getCurrentPosition() to work for me by setting some values for the options parameter, as follows:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
async (p) => {
await this.workMyCurrentPosition(p);
},
(e) => this.navigatorError(e),
{ timeout: 7000, enableHighAccuracy: true, maximumAge: 0 }
);
}
Hope it works for you!
There is an extensive post on this subject:
navigator.geolocation.getCurrentPosition sometimes works sometimes doesn't
Apparently the solution is to check on navigator.geolocation then make the call:
if(navigator.geolocation) { // dummy call
navigator.geolocation.getCurrentPosition(success, error)
}
I experienced this problem on Android 8. The error callback was called with error.code==1 "PERMISSION_DENIED".
https://developers.google.com/web/updates/2016/04/geolocation-on-secure-contexts-only
Geolocation API Removed from Unsecured Origins in Chrome 50
Meaning, the URL of your app must begin with https:// for the call to work.
My site uses desktop notifications which have never worked on mobile devices but I've recently started to receive the following exception in Chrome Version 42.0.2311.108 on Android 4.4:
Failed to construct 'Notification': Illegal constructor. Use ServiceWorkerRegistration.showNotification() instead. TypeError: Failed to construct 'Notification': Illegal constructor. Use ServiceWorkerRegistration.showNotification() instead.
My notification code is simple, after checking if the user has granted permissions I initialize a new Notification object as follows:
var notification = new Notification(messageOptions.title, { icon: messageOptions.icon });
How do I change this code to use the ServiceWorkerRegistration.showNotification, which comes up as undefined, to support notifications in the mobile version of Chrome or if that isn't possible do a feature detection and prevent the exceptions from happening if this really isn't supported [yet].
See crbug.com/481856 on the Chrome issue tracker:
new Notification() is on the path to deprecation, because it implicitly assumes that the page will outlive the notification, which is very unlikely on mobile (and far from guaranteed on desktop too).
Hence we will never implement it on Android. We might one day remove it on desktop too, after a deprecation period.
Websites should use ServiceWorkerRegistration.showNotification() (see spec) instead whenever it is available.
The best way I can think of to feature-detect new Notification() is to try it (before you have permission) and catch the error:
function isNewNotificationSupported() {
if (!window.Notification || !Notification.requestPermission)
return false;
if (Notification.permission == 'granted')
throw new Error('You must only call this *before* calling Notification.requestPermission(), otherwise this feature detect would bug the user with an actual notification!');
try {
new Notification('');
} catch (e) {
if (e.name == 'TypeError')
return false;
}
return true;
}
You could then use it like this:
if (window.Notification && Notification.permission == 'granted') {
// We would only have prompted the user for permission if new
// Notification was supported (see below), so assume it is supported.
doStuffThatUsesNewNotification();
} else if (isNewNotificationSupported()) {
// new Notification is supported, so prompt the user for permission.
showOptInUIForNotifications();
}
According to this:
Note: Trying to create a notification inside the ServiceWorkerGlobalScope using the Notification() constructor will throw an error.
If you want to send notification in a Service Worker, use self.registration.showNotification(). See https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification
I am faced with a situation where the user does not want to allow the system to use the google location api to center the map on them.
I need to set a default value if the user opts out of using the location. Is there a way to accomplish this?
Yes there is! Google maps uses navigator.geolocation.getCurrentPosition() method to get your current location. This method allows a success callback (when a user says yes) and error callback (when user says no). It is also good to check if the browser supports geolocation. The following shows how to use it
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(hasGeolocation, noGeolocation)
} else {
geolocationNotSupported();
}
function hasGeolocation() {
console.log("yeah!")
}
function noGeolocation() {
console.log("user said no!")
}
function geolocationNotSupported() {
console.log("this browser does not support geolocation")
}
There is also an example on google maps api docs to show this in context of google maps usage
https://developers.google.com/maps/articles/geolocation
when you are initializing your map you are defining map options. Just define the 'center' option in this moment.
I am trying to detect whether my extension was uninstalled.
I can't use chrome.management.onUninstalled because it will be fired on other extension.
As of Chrome 41, you can now open a URL when the extension is uninstalled. That could contain an exit survey or track the uninstall event as some sort of analytics.
Google Chrome, unlike Firefox, doesn’t allow to detect when the user uninstalls the extension, which is quite useful to understand user behaviour.
There is a feature request on crbug.com with a discussion of this feature but it has not been implemented yet.
You can call chrome.runtime.setUninstallURL("www.example.com/survey") and redirect user to a url. Unfortunately, as soon as the extension is removed, the background script is removed too, and you can't do anything like log event or send hit to google analytics.
What I did is to set the redirect url to my server endpoint, and do some tasks like logging event to my own db, or sending hit to google analytics (ga hit builder). Then call res.status(301).redirect("www.example.com/survey") to some survey url. Finally I can send the uninstall event to google analysis.
If you're on Manifest V3, you can add it on your onInstalled Listener. If you want to capture uninstall for existing users as well, you need to add it to 'update' as well.
Place this code in your background page:
chrome.runtime.onInstalled.addListener(function (details) {
if (details.reason == 'install') {
... can add things like sending a user to a tutorial page on your website
chrome.runtime.setUninstallURL('https://www.yourwebsite.com/uninstall');
} else if (details.reason == 'update') {
... can add things like sending user to a update page on your website
chrome.runtime.setUninstallURL('https://www.yourwebsite.com/uninstall');
}
});
Find more information here: https://developer.chrome.com/docs/extensions/reference/runtime/#method-setUninstallURL
For mv3: An easy way would be to have
// Redirect users to a form when the extension is uninstalled.
const uninstallListener = (details) => {
if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
chrome.runtime.setUninstallURL('https://forms.gle/...');
}
if (details.reason === chrome.runtime.OnInstalledReason.UPDATE) {
// TODO: show changelog
}
};
chrome.runtime.onInstalled.addListener(uninstallListener);
Place it in your background.
Content Script can Detect an Uninstall
Simply check the value of chrome.runtime, which becomes undefined when an extension is uninstalled.
A good trigger to check this is port disconnect:
// content_script.js
const port = chrome.runtime.connect();
port.onDisconnect.addListener(onPortDisconnect);
function onPortDisconnect() {
// After the extension is disabled/uninstalled, `chrome.runtime` may take
// a few milliseconds to get cleared, so use a delay before checking.
setTimeout(() => {
if (!chrome.runtime?.id) {
console.log('Extension disabled!');
}
}, 1000);
};