web-dev-qa-db-ja.com

navigator.geolocation.getCurrentPosition()がAndroid

Android WebView(SDKバージョン24を使用))で利用可能なHTML Geolocation APIにアクセスしようとしています。

主な問題は、JavaScriptでのnavigator.geolocation.getCurrentPosition()の呼び出しが(エラーも位置データもない)を返さないことです。アプリケーション側では、権限を確認し、_Android.webkit.GeolocationPermissions.Callback_クラスを使用してWebViewに適切に渡します。

UPDATE:ここで明確にするために、「決して戻らない」とは、あまりにも提供されたコールバックnavigator.geolocation.getCurrentPosition(success, error)が呼び出されないことを意味します。

これをテストするために作成したサンプルアプリ(WebViewをホストする1つの小さなアクティビティのみ)で、マニフェストで権限を宣言し、アプリの起動時に適切にリクエストします。プロンプトが表示され、位置情報へのアクセスを許可または拒否できます。

マニフェスト:

_<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" />
_

メインフォームのコード:

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

ランタイム中にContext.checkSelfPermission()を使用して権限を確認できますが、それぞれの権限がアプリに付与されていることがわかります。

次に、WebViewコントロールでWebページを開こうとします。設定で必要なすべてのオプションを有効にします。

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

JavaScriptからの位置情報要求を処理するために、次のWebChromeClientオーバーロードを使用します。

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

これをテストするには、次のコードを使用します( Mozilla APIヘルプページ から取得、ここでは省略):

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

JavaScriptのtonavigator.geolocation.getCurrentPosition(success, error)の呼び出しがを返さないことがわかります。 JavaのonGeolocationPermissionsShowPrompt()メソッドが適切に呼び出され、そこでアクセス許可を確認すると、常に結果0、つまり_PackageManager.PERMISSION_GRANTED_が得られるので、callback.invoke(Origin, true, true)はすべての呼び出しで実行されます。数回試行すると、my Javaコードへの呼び出しがいくつか表示されます。それでも、invoke()

記述されているように、GeolocationPermissionsクラスのgetOrigins(ValueCallback<Set<String>> callback)の呼び出しを使用して、許可されている権限を確認するコードを追加しました ここではドキュメント 。コールバックで、オリジンが場所をリクエストできることを確認しました(これらはセットにリストされています)。

ここで何が間違っているのでしょうか?

19

タイムアウトを設定するオプションを試してください( ソース ):

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

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

失敗した場合は、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);
    }
}
})();

3番目のオプションとして、EmbeddedChromeClientで@JavascriptInterface( source )で注釈を付けます。

また、コードの適切な場所に追加します。

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

最後のオプションは、htmlでタグを使用し、ディスクストレージからhtmlをロードし、呼び出し元の関数でタグを置き換え、html/stringをwebViewにロードすることです。 Androidでこのアプローチを以前に使用したことがあるので、ポジショニングが私を苛立たせすぎた場合。httpsについても心配する必要はありません。

5
Gillsoft AB

あなたの場合には2つの異なる問題があるようです:

  1. getCurrentPosition失敗しない
  2. getCurrentPosition成功しない

最初のポイントは、 method に無限の timeout があるためである可能性があります

デフォルト値はInfinityです。つまり、getCurrentPosition()は、位置が利用可能になるまで戻りません。

2番目の点は注意が必要です。param maximumAge があり、これは

PositionOptions.maximumAgeプロパティは正のlong値であり、返されることが許容されるキャッシュ可能な位置の最大経過時間(ミリ秒)を示します。 0に設定されている場合、デバイスはキャッシュされた位置を使用できず、実際の現在位置を取得する必要があることを意味します。 Infinityに設定した場合、デバイスは経過時間に関係なく、キャッシュされた位置を返す必要があります。

0デフォルトでは、デバイスはキャッシュされた位置を使用せず、実際の位置をフェッチしようとします。これは、長い応答の問題になる可能性があります。

また、このAPIがAndroidではうまく機能しないことを意味する可能性があるこのレポートを確認することもできます: https://github.com/ionic-team/ionic-native/issues/1958https://issues.Apache.org/jira/browse/CB-13241

* Cordovaは地理位置情報をブラウザに残します。

2
dilix

実際、navigatorはブラウザのグローバルオブジェクトwindowの子です。最初にwindowにアクセスし、次にnavigatorを呼び出す必要があります。一部の最近のブラウザーでは、windowに加えて、いくつかの子オブジェクトがグローバルオブジェクトのように表示されます。たとえば、locationnavigatorなどです。

WebViewにはグローバルオブジェクトwindowがありません。したがって、手動で追加できます。このアクションについては、 この中程度の記事 を参照してください。

多分あなたのコードは以下のようになります:

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

次に、JavaScriptオブジェクトのようなwindowインターフェイスをこのエバリュエーターに追加します。

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

Android N以降SDK(APIレベル> Build.VERSION_CODES.M)をターゲットとするアプリケーションの場合、メソッド onGeolocationPermissionsShowPrompt (String Origin, GeolocationPermissions.Callback callback) は、HTTPSなどの安全なオリジンから発信されたリクエストに対してのみ呼び出されます。安全でないオリジンでは、ジオロケーション要求は自動的に拒否されます。

メソッド内にブレークポイントまたはログを配置しようとした場合は、自分で問題を絞り込むことができます。

次の2つのオプションがあります。

  1. 明らかにはるかに簡単ですが、あまり高く評価されていない、より低いレベルのAPIをターゲットにします。
  2. WebサイトでSSLを設定します。
1

このような許可のリクエストを変更し、

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

これは動作するようです。

このテーマに関する広範な投稿があります:

navigator.geolocation.getCurrentPositionが機能する場合もあれば機能しない場合もあります

どうやら解決策はnavigator.geolocation次に呼び出しを行います:

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