Navigator.la géolocalisation.getCurrentPosition () ne retourne jamais dans WebView sur Android

j'essaie d'accéder à L'API de géolocalisation HTML disponible dans Android WebView (en utilisant la version 24 du SDK).

Le principal problème est que l'appel à navigator.geolocation.getCurrentPosition() en JavaScript ne retourne jamais (ni avec une erreur, ni avec des données de position), pendant que du côté de l'application je vérifie les permissions et les passe correctement à WebView en utilisant android.webkit.GeolocationPermissions.Callback classe.

mise à jour: juste pour clarifier ici, par "ne revient jamais" je veux dire qu'aucun des trop fourni rappels navigator.geolocation.getCurrentPosition(success, error) sont jamais appelés.

dans un exemple d'application que j'ai construit pour tester ceci (avec seulement une petite activité hébergeant WebView) je déclare les permissions dans le manifeste et les demande correctement au démarrage de L'application. Je l'invite et peut accorder ou refuser la permission d'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:

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 permissions pendant l'exécution en utilisant Context.checkSelfPermission() et je vois que les autorisations sont accordées pour mon application.

Alors je tente d'ouvrir une page web dans un WebView de contrôle. J'ai activé 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 suite WebChromeClient surcharge pour traiter les requêtes 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 Mozilla aide de l'API de la page, 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) en JavaScript ne retourne jamais. Je vois que onGeolocationPermissionsShowPrompt() la méthode en Java est correctement appelée et quand je vérifie les permissions, j'obtiens toujours le résultat 0, i.e. PackageManager.PERMISSION_GRANTED, donc 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'ai appelé invoke().

j'ai ajouté le code pour vérifier les permissions accordées en utilisant l'invocation de getOrigins(ValueCallback<Set<String>> callback)GeolocationPermissions de la classe, tel que décrit ici dans la documentation. Je vois dans le rappel que mes origines sont autorisés à demander des emplacements (ils sont listés dans l'ensemble).

une idée de ce qui pourrait ne pas aller ici?

16
demandé sur Alexander Galkin 2018-08-07 01:37:36

6 réponses

Essayez avec des options pour régler le délai d'attente ( source):

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

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

si elle échoue, essayez de surcharger 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);
    }
}
})();

comme troisième option annoter avec @JavascriptInterface ( source) in EmbeddedChromeClient

Également ajouter à l'endroit approprié 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 est juste d'utiliser des tags en html, de charger le html à partir du stockage sur disque, de remplacer les tags dans l'appel fonction, chargez la chaîne html/dans le webView. J'ai utilisé cette approche avant dans Android lorsque le positionnement frustré moi trop. Ensuite, vous n'avez pas à vous soucier de https.

3
répondu Gillsoft AB 2018-08-19 08:39:04

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

  1. getCurrentPosition ne manque jamais
  2. getCurrentPosition ne jamais réussir

Premier point pourrait être juste parce qu' méthode a l'infini timeout

la valeur par défaut est Infinity, ce qui signifie que getCurrentPosition() ne retournera pas tant que la position n'est pas disponible.

Second point pourrait être difficile, il y a un param maximumAge ce qui signifie

Les Positionsoptions.la propriété de maximumAge est une valeur longue positive indiquant l'âge maximum en millisecondes d'une position de mise en cache qui est acceptable à retourner. S'il est mis à 0, cela signifie que le périphérique ne peut pas utiliser une position cache et doit tenter de récupérer la position réelle actuelle. S'il est réglé à L'infini, l'appareil doit retourner une position de cache quel que soit son âge.

0 par défaut signifie cet appareil ne va pas utiliser la position cache et va essayer de récupérer le vrai et il pourrait être un problème pour la réponse longue.

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

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

2
répondu dilix 2018-08-17 15:58:41

changez votre demande de permission comme ceci,

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

Cela semble fonctionner.

1
répondu Saikrishna Rajaraman 2018-08-15 11:32:00

en Fait, navigator est un enfant de navigateur d'objet global, le window. vous devez d'abord l'accès à l' window alors appelez le navigator. Dans certains navigateurs modernes, En plus de l' window, un objet enfant est présenté comme un objet global, par exemple:location,navigator et etc.

WebView n'a pas d'objet global window. Vous pouvez donc l'ajouter manuellement, pour cette action veuillez lire cette medium de l'article.

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

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

puis ajouter JavaScript interface, comme l' window objet, à cet évaluateur:

my_web_view.addJavascriptInterface(JavaScriptInterface(), JAVASCRIPT_OBJ); //JAVASCRIPT_OBJ: like window, like navigator, like location and etc.
1
répondu AngelHotxxx 2018-08-18 07:46:41

Pour les applications de ciblage Androïde et plus tard SDKs (niveau API > Build.VERSION_CODES.M), la méthode onGeolocationPermissionsShowPrompt (String origin, GeolocationPermissions.Callback callback) n'est appelé que pour les requêtes provenant d'origines sécurisées telles que HTTPS. En cas d'origine non sécurisée, les demandes de géolocalisation sont automatiquement refusées.

vous pourriez circonscrire votre problème par vous-même si vous aviez essayé de mettre un point de rupture ou un log à l'intérieur de la méthode.

vous en avez deux options:

  1. cibler une API de niveau inférieur, ce qui est évidemment beaucoup plus facile mais pas vraiment apprécié.
  2. configurer SSL sur votre site web.
1
répondu Anees 2018-08-19 11:15:30

Il y a une vaste post sur ce sujet:

navigateur.la géolocalisation.getCurrentPosition parfois fonctionne parfois ne fonctionne pas

apparemment la solution est de vérifier navigator.geolocation et ensuite faire l'appel:

if(navigator.geolocation) { // dummy call
    navigator.geolocation.getCurrentPosition(success, error)
}
0
répondu RonTLV 2018-08-18 19:35:14