11

I'm working on an Android Project. Basically, I'm trying to run WebView that displays Google Maps.

I wanted to display my own location but when I press the button for this, I get exceptions like:

11-18 17:29:56.237 7678-7678/com.example.ali.bellsolution E/LocationProvider: Caught security exception while registering for location updates from the system. The application does not have sufficient geolocation permissions.
11-18 17:29:56.257 7678-7678/com.example.ali.bellsolution E/LocationProvider: Caught security exception while registering for location updates from the system. The application does not have sufficient geolocation permissions.
11-18 17:29:56.260 7678-7678/com.example.ali.bellsolution E/LocationProvider: Caught security exception while registering for location updates from the system. The application does not have sufficient geolocation permissions.

I have added these permissions to the manifest 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" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

MainActivity.java:

package com.example.ali.bellsolution;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends Activity {

    private static final int REQUEST_FINE_LOCATION = 0;

    WebView myWebView;

    String mapPath = "https://www.google.co.uk/maps/@51.5112044,-0.2712415,14z";

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



    /**
     * WebChromeClient subclass handles UI-related calls
     * Note: think chrome as in decoration, not the Chrome browser
     */
    public class GeoWebChromeClient extends WebChromeClient {
        @Override
        public void onGeolocationPermissionsShowPrompt(String origin,
                                                       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);
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {



        super.onCreate(savedInstanceState);

        loadPermissions(Manifest.permission.ACCESS_COARSE_LOCATION, REQUEST_FINE_LOCATION);

        setContentView(R.layout.activity_main);
        myWebView = (WebView) this.findViewById(R.id.mapview);


        myWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
        myWebView.getSettings().setBuiltInZoomControls(true);
        myWebView.setWebViewClient(new GeoWebViewClient());



        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setGeolocationEnabled(true);
        myWebView.setWebChromeClient(new GeoWebChromeClient());
        myWebView.loadUrl(mapPath);

    }

    @Override
    public void onBackPressed() {
        // Pop the browser back stack or exit the activity
        if (myWebView.canGoBack()) {
            myWebView.goBack();
        }
        else {
            super.onBackPressed();
        }
    }


    private void loadPermissions(String perm, int requestCode) {
        if (ContextCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) {
            if (!ActivityCompat.shouldShowRequestPermissionRationale(this, perm)) {
                ActivityCompat.requestPermissions(this, new String[]{perm}, requestCode);
            }
        }
    }



}

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.ali.bellsolution" >



    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="23" />



    <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" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="WebViewApp"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity"
                  android:label="WebViewApp"
            >

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Zoe
  • 27,060
  • 21
  • 118
  • 148
Rob Neal
  • 201
  • 2
  • 13
  • What version of Android are you testing this on? What is your `targetSdkVersion`? – CommonsWare Nov 18 '15 at 17:48
  • @CommonsWare my target sdk version is 23 – Rob Neal Nov 18 '15 at 17:54
  • @CommonsWare hey thanks for the duplicate, i have dropped my target SDK to 22 and used loadpermissions method. It still doesnt work. Please see edited code – Rob Neal Nov 18 '15 at 18:37
  • Your `loadPermissions()` will not be used with a `targetSdkVersion` of 22 AFAIK, and it's a flawed implementation with a `targetSdkVersion` of 23 (you go ahead without waiting for the user to grant the permissions). Try your app on Android 5.1 or older. If it works, then the problem is probably tied to Android 6.0's runtime permissions, and perhaps a `targetSdkVersion` of 22 won't help with `WebView` for some reason. If it fails on Android 5.1 or older, then you have some other problem, in which case I suggest that you post your entire manifest. – CommonsWare Nov 18 '15 at 18:43
  • @CommonsWare thanks im going to try out your advice – Rob Neal Nov 18 '15 at 18:58
  • @CommonsWare it still doesnt work. I tried running it on 5.1 and targetsdkversion 23. I have posted the manifest file now. – Rob Neal Nov 18 '15 at 19:22
  • I presume that you are getting the same errors. If not, you might want to start a fresh question with the latest code and error data. If you're getting the same errors on 5.1, then this isn't a runtime permissions issue. Your manifest seems fine (sometimes, people put the `` elements in the wrong spot). I haven't tried using `setGeolocationEnabled()`, so I don't know what else `WebView` might be expecting here, but it wouldn't seem to be related to standard Android permissions. I have reopened the question, and I apologize for barking up the wrong tree with prior advice. – CommonsWare Nov 18 '15 at 19:32
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/95483/discussion-between-rob-neal-and-commonsware). – Rob Neal Nov 18 '15 at 19:34

3 Answers3

9

You may want to ask the user for Geolocation permissions right when they are needed, but wait for the user's response before invoking the Callback:

private static final int REQUEST_FINE_LOCATION = 1;
private String geolocationOrigin;
private GeolocationPermissions.Callback geolocationCallback;

public class GeoWebChromeClient extends WebChromeClient {
    @Override
    public void onGeolocationPermissionsShowPrompt(String origin,
                                                   GeolocationPermissions.Callback callback) {
        // Geolocation permissions coming from this app's Manifest will only be valid for devices with
        // API_VERSION < 23. On API 23 and above, we must check for permissions, and possibly
        // ask for them.
        String perm = Manifest.permission.ACCESS_FINE_LOCATION;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
                ContextCompat.checkSelfPermission(MainActivity.this, perm) == PackageManager.PERMISSION_GRANTED) {
            // we're on SDK < 23 OR user has already granted permission
            callback.invoke(origin, true, false);
        } else {
            if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, perm)) {
                // ask the user for permission
                ActivityCompat.requestPermissions(MainActivity.this, new String[] {perm}, REQUEST_FINE_LOCATION);

                // we will use these when user responds
                geolocationOrigin = origin;
                geolocationCallback = callback;
            }
        }
    }
}

and receive the result of the permission request:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
    case REQUEST_FINE_LOCATION:
        boolean allow = false;
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // user has allowed this permission
            allow = true;
        }
        if (geolocationCallback != null) {
            // call back to web chrome client
            geolocationCallback.invoke(geolocationOrigin, allow, false);
        }
        break;
    }
}
EricRobertBrewer
  • 1,750
  • 1
  • 22
  • 25
6

The fix for those Android 6 users is the following somewhere in your code:

if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) ==
        PackageManager.PERMISSION_GRANTED &&
        ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) ==
                PackageManager.PERMISSION_GRANTED) {
} else {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 2);
}

This is required because Android 6 requires that you prompt users for certain permissions.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Keith Adler
  • 20,880
  • 28
  • 119
  • 189
  • Android studio throws an error on the lines in the `else` part on `ACCESS_FINE_LOCATION` and `ACCESS_COARSE_LOCATION`. The error is **Cannot resolve symbol**. Does it need to be defined as a variable or something? – Whip May 19 '17 at 17:34
  • @VeeK import android.Manifest; – PayToPwn Dec 03 '18 at 19:27
0

Add below line to your webview instance.

myWebView.setGeolocationEnabled(true)
Praveen
  • 388
  • 3
  • 7