【问题标题】:android webview geolocationandroid webview 地理定位
【发布时间】:2011-07-16 19:48:04
【问题描述】:

我必须在WebView 中检索用户的位置。我使用以下 Javascript 执行此操作:

function getLocation() {
   navigator.geolocation.getCurrentPosition(displayLocation, handleError);
}

但是权限请求弹出窗口永远不会打开。

我已经设置了这些设置:

ws.setJavaScriptEnabled(true);
ws.setGeolocationEnabled(true);
ws.setJavaScriptCanOpenWindowsAutomatically(true);

WebView 中访问用户位置的正确方法是什么?

【问题讨论】:

    标签: android geolocation


    【解决方案1】:

    分享我的工作活动课,这是一个完整的解决方案,可以演示

    • 网页加载时显示加载对话框
    • 在棉花糖及以上要求许可
    • 处理网页错误
    • 检查互联网连接并打开设置页面
    • 使用和不使用对话框处理地理位置权限

    希望,它可以节省某人的时间

        /**
         * Created by Hitesh.Sahu on 3/24/2017.
         */
        
        public class WebViewActivity extends AppCompatActivity {
        
            final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
            private int webViewPreviousState;
            private final int PAGE_STARTED = 0x1;
            private final int PAGE_REDIRECTED = 0x2;
            private CoordinatorLayout rootView;
            private WebView webView;
        
        
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_webview);
                webView = (WebView) findViewById(R.id.webView);
                rootView = (CoordinatorLayout) findViewById(R.id.root_view);
        
                if (Build.VERSION.SDK_INT >= 23) {
                    // Marshmallow+ Permission APIs
                    askRuntimePermission();
                }
        
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)) {
                        WebView.setWebContentsDebuggingEnabled(true);
                    }
                }
                webView.setInitialScale(1);
                webView.getSettings().setLoadWithOverviewMode(true);
                webView.getSettings().setUseWideViewPort(true);
                webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
                webView.setScrollbarFadingEnabled(false);
        
                webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
                webView.getSettings().setBuiltInZoomControls(true);
                webView.setWebViewClient(new GeoWebViewClient());
                // Below required for geolocation
                webView.getSettings().setJavaScriptEnabled(true);
                webView.getSettings().setGeolocationEnabled(true);
                webView.setWebChromeClient(new GeoWebChromeClient());
        
                webView.getSettings().setAppCacheEnabled(true);
                webView.getSettings().setDatabaseEnabled(true);
                webView.getSettings().setDomStorageEnabled(true);
        
                webView.getSettings().setGeolocationDatabasePath(getFilesDir().getPath());
        
                webView.loadUrl("file:///android_asset/index.html");
            }
        
            /**
             * WebChromeClient subclass handles UI-related calls
             * Note: think chrome as in decoration, not the Chrome browser
             */
            public class GeoWebChromeClient extends android.webkit.WebChromeClient {
                @Override
                public void onGeolocationPermissionsShowPrompt(final String origin,
                                                               final GeolocationPermissions.Callback callback) {
                    // Always grant permission since the app itself requires location
                    // permission and the user has therefore already granted it
                    callback.invoke(origin, true, false);
        
        //            final boolean remember = false;
        //            AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this);
        //            builder.setTitle("Locations");
        //            builder.setMessage("Would like to use your Current Location ")
        //                    .setCancelable(true).setPositiveButton("Allow", new DialogInterface.OnClickListener() {
        //                public void onClick(DialogInterface dialog, int id) {
        //                    // origin, allow, remember
        //                    callback.invoke(origin, true, remember);
        //                }
        //            }).setNegativeButton("Don't Allow", new DialogInterface.OnClickListener() {
        //                public void onClick(DialogInterface dialog, int id) {
        //                    // origin, allow, remember
        //                    callback.invoke(origin, false, remember);
        //                }
        //            });
        //            AlertDialog alert = builder.create();
        //            alert.show();
                }
            }
        
            /**
             * WebViewClient subclass loads all hyperlinks in the existing WebView
             */
            public class GeoWebViewClient extends WebViewClient {
                @Override
                public boolean shouldOverrideUrlLoading(WebView view, String url) {
                    // When user clicks a hyperlink, load in the existing WebView
                    view.loadUrl(url);
                    return true;
                }
        
                Dialog loadingDialog = new Dialog(WebViewActivity.this);
        
                @Override
                public void onPageStarted(WebView view, String url, Bitmap favicon) {
                    super.onPageStarted(view, url, favicon);
                    webViewPreviousState = PAGE_STARTED;
        
                    if (loadingDialog == null || !loadingDialog.isShowing())
                        loadingDialog = ProgressDialog.show(WebViewActivity.this, "",
                                "Loading Please Wait", true, true,
                                new DialogInterface.OnCancelListener() {
        
                                    @Override
                                    public void onCancel(DialogInterface dialog) {
                                        // do something
                                    }
                                });
        
                    loadingDialog.setCancelable(false);
                }
        
        
                @RequiresApi(api = Build.VERSION_CODES.M)
                @Override
                public void onReceivedError(WebView view, WebResourceRequest request,
                                            WebResourceError error) {
        
        
                    if (isConnected()) {
                        final Snackbar snackBar = Snackbar.make(rootView, "onReceivedError : " + error.getDescription(), Snackbar.LENGTH_INDEFINITE);
                        snackBar.setAction("Reload", new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                webView.loadUrl("javascript:window.location.reload( true )");
                            }
                        });
                        snackBar.show();
                    } else {
                        final Snackbar snackBar = Snackbar.make(rootView, "No Internet Connection ", Snackbar.LENGTH_INDEFINITE);
                        snackBar.setAction("Enable Data", new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                startActivityForResult(new Intent(Settings.ACTION_WIRELESS_SETTINGS), 0);
                                webView.loadUrl("javascript:window.location.reload( true )");
                                snackBar.dismiss();
                            }
                        });
                        snackBar.show();
                    }
        
                    super.onReceivedError(view, request, error);
        
                }
        
                @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
                @Override
                public void onReceivedHttpError(WebView view,
                                                WebResourceRequest request, WebResourceResponse errorResponse) {
        
                    if (isConnected()) {
                        final Snackbar snackBar = Snackbar.make(rootView, "HttpError : " + errorResponse.getReasonPhrase(), Snackbar.LENGTH_INDEFINITE);
        
                        snackBar.setAction("Reload", new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                webView.loadUrl("javascript:window.location.reload( true )");
                            }
                        });
                        snackBar.show();
                    } else {
                        final Snackbar snackBar = Snackbar.make(rootView, "No Internet Connection ", Snackbar.LENGTH_INDEFINITE);
                        snackBar.setAction("Enable Data", new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                startActivityForResult(new Intent(Settings.ACTION_WIRELESS_SETTINGS), 0);
                                webView.loadUrl("javascript:window.location.reload( true )");
                                snackBar.dismiss();
                            }
                        });
                        snackBar.show();
                    }
                    super.onReceivedHttpError(view, request, errorResponse);
                }
        
                @Override
                public void onPageFinished(WebView view, String url) {
        
                    if (webViewPreviousState == PAGE_STARTED) {
        
                        if (null != loadingDialog) {
                            loadingDialog.dismiss();
                            loadingDialog = null;
                        }
                    }
                }
            }
        
        
            /**
             * Check if there is any connectivity
             *
             * @return is Device Connected
             */
            public boolean isConnected() {
        
                ConnectivityManager cm = (ConnectivityManager)
                        this.getSystemService(Context.CONNECTIVITY_SERVICE);
        
                if (null != cm) {
                    NetworkInfo info = cm.getActiveNetworkInfo();
                    return (info != null && info.isConnected());
                }
        
                return false;
        
            }
        
            @Override
            public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
                switch (requestCode) {
                    case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: {
                        Map<String, Integer> perms = new HashMap<String, Integer>();
                        // Initial
                        perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
        
        
                        // Fill with results
                        for (int i = 0; i < permissions.length; i++)
                            perms.put(permissions[i], grantResults[i]);
        
                        // Check for ACCESS_FINE_LOCATION
                        if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
        
        
                                ) {
                            // All Permissions Granted
        
                            // Permission Denied
                            Toast.makeText(WebViewActivity.this, "All Permission GRANTED !! Thank You :)", Toast.LENGTH_SHORT)
                                    .show();
        
                        } else {
                            // Permission Denied
                            Toast.makeText(WebViewActivity.this, "One or More Permissions are DENIED Exiting App :(", Toast.LENGTH_SHORT)
                                    .show();
        
                            finish();
                        }
                    }
                    break;
                    default:
                        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
                }
            }
        
            @TargetApi(Build.VERSION_CODES.M)
            private void askRuntimePermission() {
                List<String> permissionsNeeded = new ArrayList<String>();
        
                final List<String> permissionsList = new ArrayList<String>();
                if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
                    permissionsNeeded.add("Show Location");
        
                if (permissionsList.size() > 0) {
                    if (permissionsNeeded.size() > 0) {
        
                        // Need Rationale
                        String message = "App need access to " + permissionsNeeded.get(0);
        
                        for (int i = 1; i < permissionsNeeded.size(); i++)
                            message = message + ", " + permissionsNeeded.get(i);
        
                        showMessageOKCancel(message,
                                new DialogInterface.OnClickListener() {
        
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                                                REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                                    }
                                });
                        return;
                    }
                    requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                            REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                    return;
                }
        
                Toast.makeText(WebViewActivity.this, "No new Permission Required- Launching App .You are Awesome!!", Toast.LENGTH_SHORT)
                        .show();
            }
        
        
            private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
                new AlertDialog.Builder(WebViewActivity.this)
                        .setMessage(message)
                        .setPositiveButton("OK", okListener)
                        .setNegativeButton("Cancel", null)
                        .create()
                        .show();
            }
        
            @TargetApi(Build.VERSION_CODES.M)
            private boolean addPermission(List<String> permissionsList, String permission) {
        
                if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
                    permissionsList.add(permission);
                    // Check for Rationale Option
                    if (!shouldShowRequestPermissionRationale(permission))
                        return false;
                }
                return true;
            }
        }
    

    【讨论】:

    • 这是考虑到构建到 android 版本 6+ 时需要在运行时询问权限这一事实的唯一答案。
    • LMAO,F 棉花糖。哈哈哈。
    【解决方案2】:

    这是一个显示警告对话框以促进用户使用其位置的示例:

        final Context context = this;
    
         @Override
         public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback callback) {
            Log.i(TAG, "onGeolocationPermissionsShowPrompt()");
    
            final boolean remember = false;
            AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setTitle("Locations");
            builder.setMessage("Would like to use your Current Location ")
            .setCancelable(true).setPositiveButton("Allow", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                  // origin, allow, remember
                  callback.invoke(origin, true, remember);
               }
            }).setNegativeButton("Don't Allow", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                  // origin, allow, remember
                  callback.invoke(origin, false, remember);
               }
            });
            AlertDialog alert = builder.create();
            alert.show();
         }
    

    【讨论】:

    • 谢谢!一个小调整:传递给 AlertDialog.Builder 构造函数的参数应该是您的活动。在这种情况下,“this”是不正确的。
    • 谁能给我一个完整的工作示例代码。提前致谢。
    • 我已经完成了所有这些,但代码仍然无法正常工作,这是怎么回事?我正在eclipse模拟器中测试它
    【解决方案3】:

    接受或拒绝用户位置的对话框由程序员设计:D。正如 Chris Cashwell 所说,您只需使用这样的回调:

    webview.setWebChromeClient(new WebChromeClient(){
      public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
        // callback.invoke(String origin, boolean allow, boolean remember);
        callback.invoke(origin, true, false);
      }
    });
    

    在某些情况下,HTML5 需要使用存储,因此您必须启用一些属性,以便 webview 具有完全访问权限才能正常运行。

        // HTML5 API flags
        webView.getSettings().setAppCacheEnabled(true);
        webView.getSettings().setDatabaseEnabled(true);
        webView.getSettings().setDomStorageEnabled(true);
    

    【讨论】:

      【解决方案4】:

      发布作为更新 Android 的新答案,将所有内容集中在一篇文章中,因为您不再需要使用 setWebChromeClient

      对于 android 6+,您只需在运行时使用 ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 123); 请求 GPS 权限。

      【讨论】:

        【解决方案5】:

        我最近遇到了这种情况,并采取了以下步骤来实现这一点:

        第 1 步: 在您的 AndroidManifest.xml 文件中添加权限

        <uses-permission android:name="android.permission.INTERNET"/>
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> 
        

        第 2 步:创建一个包含 WebViewProgressBar 的活动(在我的例子中)

        xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">
            <ProgressBar
                android:id="@+id/progressBar"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_margin="0dp"
                android:minHeight="4dp"
                android:padding="0dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
            <WebView
                android:id="@+id/webView"
                android:layout_width="0dp"
                android:layout_height="0dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/progressBar" />
        </androidx.constraintlayout.widget.ConstraintLayout>
        

        第 3 步:在您的活动类中,添加以下代码并根据您的需要进行更改

        class WebActivity : AppCompatActivity() {
            var pageUrl: String = "https://couponia.co/"
            var mGeoLocationRequestOrigin: String? = null
            var mGeoLocationCallback: GeolocationPermissions.Callback? = null
            val MAX_PROGRESS = 100
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_web)
                initWebView()
                setWebClient()
                loadUrl(pageUrl)
            }
        
        
            @SuppressLint("SetJavaScriptEnabled")
            private fun initWebView() {
                webView.settings.javaScriptEnabled = true
                webView.settings.loadWithOverviewMode = true
                webView.settings.useWideViewPort = true
                webView.settings.domStorageEnabled = true
                webView.settings.databaseEnabled = true
                webView.settings.setAppCacheEnabled(true)
                webView.webViewClient = object : WebViewClient() {
                    override
                    fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
                        handler?.proceed()
                    }
                }
        
            }
        
            private fun setWebClient() {
        
                webView.webChromeClient = object : WebChromeClient() {
                    override fun onGeolocationPermissionsShowPrompt(
                        origin: String?,
                        callback: GeolocationPermissions.Callback?
                    ) {
        
                        if (ContextCompat.checkSelfPermission(
                                this@WebActivity,
                                Manifest.permission.ACCESS_FINE_LOCATION
                            )
                            != PackageManager.PERMISSION_GRANTED
                        ) {
        
                            if (ActivityCompat.shouldShowRequestPermissionRationale(
                                    this@WebActivity,
                                    Manifest.permission.ACCESS_FINE_LOCATION
                                )
                            ) {
                                AlertDialog.Builder(this@WebActivity)
                                    .setMessage("Please turn ON the GPS to make app work smoothly")
                                    .setNeutralButton(
                                        android.R.string.ok,
                                        DialogInterface.OnClickListener { dialogInterface, i ->
                                            mGeoLocationCallback = callback
                                            mGeoLocationRequestOrigin = origin
                                            ActivityCompat.requestPermissions(
                                                this@WebActivity,
                                                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1001
                                            )
        
                                        })
                                    .show()
        
                            } else {
                                //no explanation need we can request the locatio
                                mGeoLocationCallback = callback
                                mGeoLocationRequestOrigin = origin
                                ActivityCompat.requestPermissions(
                                    this@WebActivity,
                                    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1001
                                )
                            }
                        } else {
                            //tell the webview that permission has granted
                            callback!!.invoke(origin, true, true)
                        }
        
                    }
        
                    override fun onProgressChanged(view: WebView?, newProgress: Int) {
                        super.onProgressChanged(view, newProgress)
                        progressBar.progress = newProgress
                        if (newProgress < MAX_PROGRESS && progressBar.visibility == ProgressBar.GONE) {
                            progressBar.visibility = ProgressBar.VISIBLE
                        }
                        if (newProgress == MAX_PROGRESS) {
                            progressBar.visibility = ProgressBar.GONE
                        }
                    }
        
        
                }
            }
        
            override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
                // Check if the key event was the Back button and if there's history
                if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
                    webView.goBack()
                    return true
                }
                // If it wasn't the Back key or there's no web page history, exit the activity)
                return super.onKeyDown(keyCode, event)
            }
        
            private fun loadUrl(pageUrl: String) {
                webView.loadUrl(pageUrl)
            }
        
        
            override fun onRequestPermissionsResult(
                requestCode: Int,
                permissions: Array<out String>,
                grantResults: IntArray
            ) {
                super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        
                when (requestCode) {
                    1001 -> {
                        //if permission is cancel result array would be empty
                        if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                            //permission was granted
                            if (mGeoLocationCallback != null) {
                                mGeoLocationCallback!!.invoke(mGeoLocationRequestOrigin, true, true)
                            }
                        } else {
                            //permission denied
                            if (mGeoLocationCallback != null) {
                                mGeoLocationCallback!!.invoke(mGeoLocationRequestOrigin, false, false)
                            }
                        }
                    }
        
                }
            }
        }
        

        【讨论】:

        • 太棒了!你帮我解决了我在媒体上找到的这个代码示例的问题 :-) 谢谢
        【解决方案6】:
        • 必须在WebView 中启用JavaScript,使用WebSettings.setJavaScriptEnabled(true);
        • 应用需要权限ACCESS_FINE_LOCATION
        • WebView 必须使用自定义的WebChromeClient,它实现了WebChromeClient.onGeolocationPermissionsShowPrompt()。这种方法 由WebView 调用以获取向 JavaScript 公开用户位置的权限。 (在浏览器的情况下,我们向用户显示一个提示。)默认实现什么都不做,所以永远不会获得许可,也永远不会将位置传递给 JavaScript。始终授予权限的简单实现是...

          webView.setWebChromeClient(new WebChromeClient() {
           public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
              callback.invoke(origin, true, false);
           }
          });
          

        地理定位使用数据库在会话之间保留缓存的位置和权限。使用WebSettings.setGeolocationDatabasePath(...) 设置数据库的位置。如果未设置数据库的位置,则持久存储将不可用,但 Geolocation 将继续正常工作,否则。要设置数据库的位置,请使用 ...

        webView.getSettings().setGeolocationDatabasePath( context.getFilesDir().getPath() );
        

        【讨论】:

        • 这个答案是正确的,但是你必须使用android.webkit.GeolocationPermissions.Callback
        • 这是一个完整的代码示例turbomanage.wordpress.com/2012/04/23/…
        • “上下文”在哪里定义?
        • 我最终不得不使用完全限定的 android.webkit.WebChromeClient() 而不是仅使用 WebChromeClient()android.webkit.GeolocationPermissions.Callback 来完成这项工作。
        • 这不再适用于目标/运行时> Android 6.0,运行时权限的原因,对吧?
        【解决方案7】:

        您是否在清单中声明了该权限?

        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        

        您可能还需要声明其他位置权限,例如:

        <uses-permission android:name="android.permission.ACCESS_GPS" />
        <uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" />
        <uses-permission android:name="android.permission.ACCESS_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        

        【讨论】:

        • 是的,我声明了这个权限。 webview 获取用户位置,但问题是没有显示接受或拒绝用户位置的对话框。
        • 只需要第一个!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-05
        • 2017-08-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多