web-dev-qa-db-fra.com

navigator.geolocation.getCurrentPosition () ne retourne jamais dans WebView sur Android

J'essaie d'accéder à l'API de géolocalisation HTML disponible dans Android WebView (à l'aide du SDK version 24).

Le problème principal est que l'appel à navigator.geolocation.getCurrentPosition() dans JavaScript ne renvoie jamais (ni en cas d'erreur, ni avec les données de position), alors que je suis du côté de l'application, je vérifie les autorisations et les passe correctement à WebView à l'aide de Android.webkit.GeolocationPermissions.Callback class.

UPDATE: Juste pour clarifier ici, par "ne retourne jamais", je veux dire qu'aucun des rappels trop fournis, navigator.geolocation.getCurrentPosition(success, error), n'est jamais appelé.

Dans un exemple d'application que j'ai construit pour tester cela (avec une seule activité hébergeant WebView), je déclare les autorisations dans le manifeste et les demande correctement au démarrage de l'application. Je vois l'invite et peux accorder ou refuser l'autorisation d'accéder aux informations de localisation.

Manifeste:

<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 sous la forme principale:

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;
    }
} 

Je peux vérifier les autorisations lors de l'exécution à l'aide de Context.checkSelfPermission() et je constate que les autorisations respectives sont accordées à mon application.

Ensuite, j'essaie d'ouvrir une page Web dans un contrôle WebView . J'active toutes les options requises dans les paramètres:

    mWebSettings.setJavaScriptEnabled(true);
    mWebSettings.setAppCacheEnabled(true);
    mWebSettings.setDatabaseEnabled(true);
    mWebSettings.setDomStorageEnabled(true);
    mWebSettings.setGeolocationEnabled(true);
    mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
    mWebSettings.setSupportZoom(true);

J'utilise la surcharge WebChromeClient suivante pour le traitement des demandes de géolocalisation à partir de 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);
        }
    }
}

Pour tester cela, j'utilise le code suivant (tiré de page d'aide de l'API Mozilla , abrégé ici):

function geoFindMe() {
  function success(position) {}
  function error() {}
  navigator.geolocation.getCurrentPosition(success, error);
}

Ce que je vois, c'est que l'appel ànavigator.geolocation.getCurrentPosition(success, error) dans JavaScript ne retourne jamais. Je vois que la méthode onGeolocationPermissionsShowPrompt() en Java est correctement appelée et, lorsque je vérifie les autorisations, j’obtiens toujours le résultat 0, c’est-à-dire PackageManager.PERMISSION_GRANTED, de sorte que callback.invoke(Origin, true, true) est exécuté à chaque appel. Si j'essaie plusieurs fois, je vois plusieurs appels à mon code Java. Pourtant, rien ne se passe du côté JavaScript ici après que j'appelle invoke().

J'ai ajouté le code pour vérifier les autorisations accordées à l'aide de l'invocation de getOrigins(ValueCallback<Set<String>> callback) dans la classe GeolocationPermissions, comme décrit ici, dans la documentation . Je vois dans le rappel que mes origines sont autorisées à demander des localisations (elles sont répertoriées dans l'ensemble).

Des idées ce qui pourrait être faux ici?

17
Alexander Galkin

Essayez avec des options pour définir le délai d’expiration ( source ):

var options = {
    enableHighAccuracy: true,
    timeout: 10000,
    maximumAge: 0
};

navigator.geolocation.getCurrentPosition(success, error, options);

Si cela échoue, essayez de remplacer 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);
    }
}
})();

Troisième option, annotez avec @JavascriptInterface ( source ) dans EmbeddedChromeClient

Ajoutez également à la place appropriée dans votre code:

mWebSettings.setJavaScriptEnabled(true);
//...
mWebSettings.setSupportZoom(true);
webView.addJavascriptInterface(new EmbeddedChromeClient(webView), "injectedObject");
webView.loadData("html here", "text/html", null);

La dernière option consiste simplement à utiliser des balises en HTML, charger le code HTML à partir du stockage sur disque, remplacer les balises dans la fonction appelante, charger le code HTML/chaîne dans la vue Web. J'ai déjà utilisé cette approche auparavant dans Android lors du positionnement me frustré trop. Alors vous n'avez pas à vous soucier de https non plus.

4
Gillsoft AB

On dirait qu'il y a 2 problèmes différents dans votre cas:

  1. getCurrentPosition n'échoue jamais
  2. getCurrentPosition ne réussit jamais

Le premier point pourrait être simplement parce que méthode a une durée infinie timeout

La valeur par défaut est Infinity, ce qui signifie que getCurrentPosition () ne sera renvoyé que lorsque la position sera disponible.

Le deuxième point pourrait être délicat, il y a un param maximumAge qui signifie 

La propriété PositionOptions.maximumAge est une valeur longue positive indiquant l'âge maximal, en millisecondes, d'une position en cache possible qu'il est acceptable de renvoyer. Si défini sur 0, cela signifie que le périphérique ne peut pas utiliser une position mise en cache et doit tenter de récupérer la position actuelle réelle. S'il est défini sur Infinity, le périphérique doit renvoyer une position en cache, quel que soit son âge.

0 par défaut signifie que le périphérique n'utilisera pas la position en cache et essaiera d'extraire la vraie, ce qui pourrait poser problème en cas de réponse longue.

Vous pouvez également vérifier ce résultat, ce qui pourrait signifier que cette API ne fonctionne pas vraiment bien sur Android: https://github.com/ionic-team/ionic-native/issues/1958https://issues.Apache.org/jira/browse/CB-13241

* Cordova laisse des trucs de géolocalisation pour navigateur.

2
dilix

En fait, navigator est un enfant de l'objet global de navigateur, le window. vous devez d’abord accéder à window puis appeler le navigator. Dans certains navigateurs modernes, en plus de window, un objet enfant est présenté comme un objet global, par exemple: location, navigator et etc.

La WebView n'a pas d'objet global window. Vous pouvez donc l'ajouter manuellement. Pour cette action, veuillez lire cet article moyen .

Peut-être que votre code sera comme ci-dessous:

my_web_view.evaluateJavascript("javascript: " + "updateFromAndroid(\"" + edit_text_to_web.text + "\")", null);

Et ajoutez ensuite l'interface JavaScript, comme l'objet window, à cet évaluateur:

my_web_view.addJavascriptInterface(JavaScriptInterface(), JAVASCRIPT_OBJ); //JAVASCRIPT_OBJ: like window, like navigator, like location and etc.
1
AmerllicA

Pour les applications ciblant Android N et versions ultérieures SDK (niveau API> Build.VERSION_CODES.M), la méthode onGeolocationPermissionsShowPrompt (String Origin, GeolocationPermissions.Callback callback) est uniquement appelée pour les demandes provenant d’origines sécurisées telles que HTTPS. Sur des origines non sécurisées, les demandes de géolocalisation sont automatiquement refusées.

_ {Vous pouvez résoudre vous-même votre problème si vous aviez essayé d'insérer un point d'arrêt ou un journal dans la méthode.

Vous avez deux options:

  1. Ciblez une API de niveau inférieur, ce qui est évidemment beaucoup plus facile mais pas vraiment apprécié.
  2. Configurez SSL sur votre site web.
1
Anees

Changez votre demande d'autorisation comme ceci,

ActivityCompat.requestPermissions(this, new String[]{
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION
    },0);

Cela semble fonctionner.

1

Il y a un long post sur ce sujet:

navigator.geolocation.getCurrentPosition fonctionne parfois parfois ne fonctionne pas

Apparemment, la solution consiste à vérifier sur navigator.geolocation puis à effectuer l'appel:

if(navigator.geolocation) { // dummy call
    navigator.geolocation.getCurrentPosition(success, error)
}
0
RonTLV