webentwicklung-frage-antwort-db.com.de

navigator.geolocation.getCurrentPosition () kehrt in WebView unter Android nicht zurück

Ich versuche, auf die HTML-Geolocation-API zuzugreifen, die in Android WebView (mit SDK-Version 24) verfügbar ist.

Das Hauptproblem ist, dass der Aufruf von navigator.geolocation.getCurrentPosition() in JavaScript niemals zurückgibt (weder mit einem Fehler noch mit Positionsdaten), während ich auf der Anwendungsseite nach Berechtigungen suche und sie ordnungsgemäß mit der Klasse Android.webkit.GeolocationPermissions.Callback an WebView übergebe.

UPDATE: Um nur zu verdeutlichen, mit "niemals zurückkehren" meine ich, dass keiner der zugeführten Rückrufe navigator.geolocation.getCurrentPosition(success, error) jemals aufgerufen wird.

In einer Beispiel-App, die ich zum Testen erstellt habe (mit nur einer kleinen Aktivität, die WebView hostet), erkläre ich die Berechtigungen im Manifest und fordere sie beim App-Start richtig an. Ich sehe die Aufforderung und kann die Standortinformationen genehmigen oder verweigern.

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 im Hauptformular:

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

Ich kann zur Laufzeit mit Context.checkSelfPermission() nach Berechtigungen suchen und sehe, dass die entsprechenden Berechtigungen meiner App erteilt werden.

Dann versuche ich, eine Webseite in einem WebView-Steuerelement zu öffnen. __ Ich aktiviere alle erforderlichen Optionen in den Einstellungen:

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

Ich verwende die folgende WebChromeClient-Überladung für die Bearbeitung von Geolocation-Anforderungen von 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);
        }
    }
}

Um dies zu testen, verwende ich folgenden Code (entnommen aus Mozilla API-Hilfeseite , hier gekürzt):

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

Was ich sehe ist, dass der Aufruf von navigator.geolocation.getCurrentPosition(success, error) in JavaScript niemals zurückgibt. Ich sehe, dass die onGeolocationPermissionsShowPrompt()-Methode in Java richtig aufgerufen wird, und wenn ich dort nach Berechtigungen prüfe, erhalte ich immer das Ergebnis 0, d. H. PackageManager.PERMISSION_GRANTED. Daher wird callback.invoke(Origin, true, true) bei jedem Aufruf ausgeführt. Wenn ich es mehrmals versuche, sehe ich mehrere Aufrufe meines Java-Codes. Dennoch passiert auf der JavaScript-Seite hier nichts, nachdem ich invoke() aufgerufen habe.

Ich habe den Code hinzugefügt, um mithilfe des Aufrufs von getOrigins(ValueCallback<Set<String>> callback) in der Klasse GeolocationPermissions auf erteilte Berechtigungen zu prüfen, wie in hier in der Dokumentation beschrieben. Ich sehe im Callback, dass meine Ursprünge Standorte abfragen dürfen (diese sind im Set aufgeführt).

Irgendwelche Ideen, was hier falsch sein könnte?

17

Versuchen Sie es mit Optionen, um das Zeitlimit einzustellen ( source ):

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

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

Wenn dies fehlschlägt, versuchen Sie, getCurrentPosition ( source ) zu überschreiben:

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

Als dritte Option Annotate mit @JavascriptInterface ( source ) im EmbeddedChromeClient

Fügen Sie an der richtigen Stelle in Ihrem Code Folgendes hinzu:

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

Die letzte Option ist nur die Verwendung von Tags in HTML, das Laden der HTML-Datei aus dem Plattenspeicher, das Ersetzen von Tags in der aufrufenden Funktion und das Laden der HTML/Zeichenfolge in das WebView. Ich habe diesen Ansatz schon früher in Android verwendet, als die Positionierung mich zu sehr frustrierte. Dann müssen Sie sich auch keine Sorgen um https machen.

4
Gillsoft AB

Sieht so aus, als ob es zwei verschiedene Probleme in Ihrem Fall gibt:

  1. getCurrentPosition schlägt niemals fehl
  2. getCurrentPosition gelingt nie

Der erste Punkt könnte sein, nur weil Methode unendlich Timeout hat

Der Standardwert ist Infinity, was bedeutet, dass getCurrentPosition () erst zurückgegeben wird, wenn die Position verfügbar ist.

Der zweite Punkt könnte schwierig sein, es gibt einen Parameter maximumAge was bedeutet 

Die PositionOptions.maximumAge-Eigenschaft ist ein positiver Long-Wert, der das maximale Alter einer möglichen zwischengespeicherten Position in Millisekunden angibt, deren Rückgabe zulässig ist. Wenn der Wert auf 0 gesetzt ist, bedeutet dies, dass das Gerät keine zwischengespeicherte Position verwenden kann und versuchen muss, die tatsächliche aktuelle Position abzurufen. Bei der Einstellung "Infinity" muss das Gerät unabhängig vom Alter eine zwischengespeicherte Position zurückgeben.

Standardmäßig bedeutet 0, dass das Gerät die zwischengespeicherte Position nicht verwendet und versucht, die tatsächliche Position abzurufen, und dies kann zu Problemen bei der langen Antwort führen.

Sie können dies auch überprüfen. Dies kann bedeuten, dass diese API auf Android nicht wirklich gut funktioniert: https://github.com/ionic-team/ionic-native/issues/1958https://issues.Apache.org/jira/browse/CB-13241

* Cordova hinterlässt Geolocation-Sachen für den Browser.

2
dilix

Tatsächlich ist navigator ein untergeordnetes Objekt des globalen Browsers, window. Sie sollten zuerst auf window zugreifen und dann navigator aufrufen. In einigen modernen Browsern wird zusätzlich zu window ein untergeordnetes Objekt wie ein globales Objekt dargestellt, zum Beispiel: location, navigator usw.

Das WebView hat kein globales Objekt window. Sie können es also manuell hinzufügen. Für diese Aktion lesen Sie bitte diesen mittleren Artikel .

Vielleicht wird Ihr Code wie folgt aussehen:

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

Fügen Sie dann wie dieses JavaScript-Objekt die Schnittstelle window zu diesem Evaluator hinzu:

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

Für Anwendungen, die auf Android N und neuere SDKs (API-Ebene> Build.VERSION_CODES.M) abzielen, wird die Methode onGeolocationPermissionsShowPrompt (String Origin, GeolocationPermissions.Callback callback) nur für Anforderungen aufgerufen, die aus sicheren Quellen wie HTTPS stammen. Bei nicht sicheren Ursprungs werden Geolokationsanfragen automatisch abgelehnt.

Sie könnten Ihr Problem selbst eingrenzen, wenn Sie versucht hätten, einen Haltepunkt oder ein Protokoll in die Methode einzufügen.

Sie haben zwei Möglichkeiten:

  1. Richten Sie eine niedrigere API aus, die offensichtlich viel einfacher ist, aber nicht wirklich geschätzt wird.
  2. Richten Sie SSL in Ihrer Website ein.
1
Anees

Ändern Sie Ihre Erlaubnisanfrage so,

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

Das scheint zu funktionieren.

Zu diesem Thema gibt es einen umfangreichen Beitrag:

navigator.geolocation.getCurrentPosition funktioniert manchmal nicht

Anscheinend besteht die Lösung darin, navigator.geolocation zu überprüfen und dann den Anruf zu tätigen:

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