android webview地理定位
我必须在WebView
检索用户的位置。 我用下面的Javascript来做到这一点:
function getLocation() { navigator.geolocation.getCurrentPosition(displayLocation, handleError); }
但是权限请求popup窗口从不打开。
我已经设置了这些设置:
ws.setJavaScriptEnabled(true); ws.setGeolocationEnabled(true); ws.setJavaScriptCanOpenWindowsAutomatically(true);
从WebView
访问用户位置的正确方法是什么?
- JavaScript必须在
WebView
启用,使用WebSettings.setJavaScriptEnabled(true);
- 该应用程序需要权限
ACCESS_FINE_LOCATION
-
WebView
必须使用实现WebChromeClient.onGeolocationPermissionsShowPrompt()
的自定义WebChromeClient
。 这个方法被WebView
调用来获得将用户的位置透露给JavaScript的权限。 (在浏览器的情况下,我们向用户显示一个提示。)默认实现什么也不做,所以永远不会获得权限,并且永远不会传递给JavaScript。 一个总是授予权限的简单实现是…webView.setWebChromeClient(new WebChromeClient() { public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { callback.invoke(origin, true, false); } });
地理位置使用数据库来保存会话之间的caching位置和权限。 数据库的位置使用WebSettings.setGeolocationDatabasePath(...)
。 如果未设置数据库的位置,则永久存储将不可用,但是“地理位置”将继续正常运行。 要设置数据库的位置,请使用…
webView.getSettings().setGeolocationDatabasePath( context.getFilesDir().getPath() );
你是否在清单中声明了这个权限?
<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" />
接受或拒绝用户位置的对话由程序员devise:D。 正如克里斯·卡什维尔所说,你只需要使用像这样的callback:
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);
这是一个显示警报对话框的例子,以提高用户使用他/她的位置的权限:
@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(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(); }
分享我的工作活动类,这是一个完整的解决scheme,可以演示
- 在加载网页时显示加载对话框
- 要求在棉花糖和以上的许可
- 处理网页错误
- 检查互联网连接并打开设置页面
- 使用和不使用对话框处理地理位置权限
希望能节省一些时间
/** * 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 fuckMarshMallow(); } 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 fuckMarshMallow() { 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; } }