113

I have to retrieve a user's location in a WebView. I do this with the following Javascript:

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

But the permission request popup never opens.

I've set these settings:

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

What is the correct way to access a user's location from within a WebView?

Chris Cashwell
  • 22,308
  • 13
  • 63
  • 94
Ste
  • 2,116
  • 7
  • 24
  • 24

8 Answers8

230
  • JavaScript must be enabled in the WebView, using WebSettings.setJavaScriptEnabled(true);
  • The app needs permission ACCESS_FINE_LOCATION
  • The WebView must use a custom WebChromeClient which implements WebChromeClient.onGeolocationPermissionsShowPrompt(). This method is called by the WebView to obtain permission to disclose the user's location to JavaScript. (In the case of the browser, we show a prompt to the user.) The default implementation does nothing, so permission is never obtained and the location is never passed to JavaScript. A simple implementation which always grants permission is ...

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

Geolocation uses databases to persist cached positions and permissions between sessions. The location of the database is set using WebSettings.setGeolocationDatabasePath(...). If the location of the database is not set, the persistent storage will not be available, but Geolocation will continue to function correctly otherwise. To set the location of the databases, use ...

webView.getSettings().setGeolocationDatabasePath( context.getFilesDir().getPath() );
jpw
  • 343
  • 2
  • 9
Chris Cashwell
  • 22,308
  • 13
  • 63
  • 94
  • 6
    this answer is correct, but you must use android.webkit.GeolocationPermissions.Callback – NullPointerException Sep 18 '13 at 15:27
  • Perhaps a getApplicationContext().getDatabasePath("Geo-Locations").getAbsolutePath() would be a nice approach. – TacB0sS Nov 24 '13 at 19:56
  • 3
    Here is a complete code example http://turbomanage.wordpress.com/2012/04/23/how-to-enable-geolocation-in-a-webview-android/ – giorgio79 Jan 18 '14 at 13:45
  • 2
    where is the "context" defined? – C graphics Feb 03 '15 at 00:15
  • Just starting out with Android Studio, so apologies for newbie question, but I'm getting "Error:(32, 41) error: cannot find symbol class WebChromeClient" when I add this to my very basic app code. Is there something else that needs to be declared or included somewhere to get this working? – Fat Monk Dec 26 '15 at 15:30
  • 5
    I ended up having to use the fully qualified `android.webkit.WebChromeClient()` instead of just `WebChromeClient()` and `android.webkit.GeolocationPermissions.Callback` to get this working. – Fat Monk Dec 26 '15 at 18:43
  • I have used the above to allow my HTML page inside my webview to get locations - but the problem that persists is that its not using the GPS radio to get location data - it appears to only use a Wi-Fi signal. Is this supposed to turn the GPS radio no for you or is that something I have to-do in Java (i.e., create a locationManger, locationListener, listen for locations? Doesn't seem right to me. – celoftis Jun 24 '16 at 22:11
  • Is there something like this for iOS? I'm having the same problem I was having on Android but there is no simple solution like this. – thailey01 Nov 14 '16 at 01:32
  • 1
    Confirming this still works in 2017, with the fix mentioned by @FatMonk Monk to use the fully qualified android.webkit.WebChromeClient() and android.webkit.GeolocationPermissions.Callback. – Jason Jan 13 '17 at 22:03
  • Is this supposed to show the same javascript prompt, that browser is showing? – CharithJ Aug 01 '17 at 03:18
  • 1
    I am using the same callback handler and I am calling "callback.Invoke(origin, true, false);" but success handler of JavaScript is not being called. even error handler is also not firing. If I put debugger I can see this call back is being called but JavaScript does not fire success handler. – Ali Aug 22 '17 at 01:28
  • 2
    This doesnt work anymore with target/runtime > Android 6.0, cause of runtime permissions, right? – 最白目 May 18 '18 at 13:31
  • What do we do after API level 23? It says `Caught security exception while registering for location updates from the system. The application does not have sufficient geolocation permissions.` – Trev14 May 24 '18 at 00:46
  • 1
    I face the same problem as @Ali with API 24: after invoking the callback I still can't get the JavaScript to get the success handle to fire. – Alexander Galkin Aug 06 '18 at 21:58
  • @celoftis long time but I'm experiencing the same. Did you ever solve it? – Lior Iluz Dec 13 '18 at 06:39
  • @Lior I believe I ended up creating a binding js interface (https://developer.android.com/guide/webapps/webview#BindingJavaScript) that listens for js events, then back on the java (Andorid) side turns on the GPS radio as needed... – celoftis Feb 05 '19 at 22:13
  • With android 6+ you need to ask for the GPS permissions at runtime using ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 123); Full example here: https://stackoverflow.com/a/33866959/556090 – MeatPopsicle Feb 22 '19 at 21:08
  • @AlexanderGalkin Have you guys solve the issue? I'm facing it, permission granted, then callback.invoke() but success handler of JavaScript is not being called. It's only happens with some devices, Note 4 is in my case. – cuasodayleo Sep 23 '19 at 04:27
  • @cuasodayleo No, we couldn't solve it. Android 9 in our tests doesn't seem to have this error, so we are moving to Android 9 soon... – Alexander Galkin Sep 23 '19 at 11:00
25

Dialog to accept or decline user location is design by programmer :D. As Chris Cashwell said, you just use a callback like this:

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

In some cases, HTML5 requires the use of storage, therefore you must enable some properties so that webview has full access to run normal.

    // HTML5 API flags
    webView.getSettings().setAppCacheEnabled(true);
    webView.getSettings().setDatabaseEnabled(true);
    webView.getSettings().setDomStorageEnabled(true);
pfrank
  • 2,090
  • 1
  • 19
  • 26
earthquake
  • 269
  • 3
  • 3
20

Sharing my Working activity class, this is a complete solution which can demonstrate

  • Showing loading dialog while the web page is loading
  • Ask for permission in marshmallow and above
  • Handle webpage error
  • Check for the internet connection and open setting page
  • Handling Geolocation permission with and without dialog

Hope, it saves someone's time

    /**
     * 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;
        }
    }
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
  • 5
    This is the only answer that took into consideration the fact that when building to android version 6+ you need to ask permissions during run time. – hatzaviv Sep 18 '17 at 10:45
17

Are you declaring that permission in your manifest?

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

You may also need to declare other location permissions, like these:

<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" />
Chris Cashwell
  • 22,308
  • 13
  • 63
  • 94
  • 3
    Yes i declare this permissions. The webview takes the user location, but the problem is that the dialog to accept o decline user location doesn not show up. – Ste Mar 17 '11 at 08:17
  • 4
    Only the first one is required! – TacB0sS Nov 24 '13 at 19:57
6

I have recently come across this type of situation and have done below steps to achieve this:

Step 1: Add permissions in your AndroidManifest.xml file

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

Step 2: Create an activity that would contain a WebView and ProgressBar (in my case)

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>

Step 3: In your activity class, add below code and make changes according to your need

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

        }
    }
}
Suraj Bahadur
  • 3,730
  • 3
  • 27
  • 55
5

This is an example of showing alert dialog to promote for user permission to use his/her location:

    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();
     }
pfrank
  • 2,090
  • 1
  • 19
  • 26
Gary S.
  • 394
  • 2
  • 7
  • 4
    Thanks! One minor tweak: The argument passed to the AlertDialog.Builder constructor should be your activity. In that context, "this" is incorrect. – Joseph Lennox Sep 10 '13 at 15:46
  • could any one give me a fully working sample code for this. Thanks in advance. – shams Feb 04 '14 at 09:51
  • 2
    i have done all these but the code is still not woking what is wrong? I am testing it in the eclipse emulator – shams Feb 04 '14 at 10:27
2

you need to dynamically request for Permission when location is requested from Webview

Make sure You you have added ACCESS_FINE_LOCATION to Manifest

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

Define Your callback and origin at top level Class ,to be able to assign them to the callbacks and origin provided by ChromeClient

public class MainActivity extends AppCompatActivity {

    private android.webkit.WebView myWebView;
    String mGeoLocationRequestOrigin = null;
    GeolocationPermissions.Callback  mGeoLocationCallback = null;

...................................................

Handle geoLocation Request and Assign value to callbacks to be able to use them once permission is granted

      myWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public void onGeolocationPermissionsShowPrompt(final String origin,
                                                           final GeolocationPermissions.Callback callback) {

                int permissionCheckFineLocation = ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION);
                if (permissionCheckFineLocation!= PackageManager.PERMISSION_GRANTED) {
                    mGeoLocationCallback=callback;
                    mGeoLocationRequestOrigin=origin;
                    //requesting permission
                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 123);
                }

                else {// permission and the user has therefore already granted it
                    callback.invoke(origin, true, false);
                }

            }
        });

once permission is received invoke calbacks using origin

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(grantResults[0]== PackageManager.PERMISSION_GRANTED){
            //you have the permission now.
            if(requestCode==123) {
                mGeoLocationCallback.invoke(mGeoLocationRequestOrigin, true, false);
            }
        }
1

Posting as a new answer for updated Android with everything in one post, because you no longer need to use setWebChromeClient.

With android 6+ you just to ask for the GPS permissions at runtime using ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 123);.

pfrank
  • 2,090
  • 1
  • 19
  • 26