4

I'm trying to enable a webview within a xamarin forms app to get the current GPS coordinates of an android device. Currently the webview/website will return the GPS coordinates when opened in a chrome browser on the phone or a laptop, however within the app will not. Trying to get this working as simply as possible and to expand on it after.

Code so far: XAML page:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="UITrial.Page2"
             BackgroundColor = "#f0f0ea">
    <Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />

  <WebView Source="https://danu6.it.nuigalway.ie/OliverInternetProgramming/project/Loginproject.html" />

</ContentPage>

HTML PAGE:

<!DOCTYPE html>
<html>
<body>

<p>Click the button to get your coordinates.</p>

<button onclick="getLocation()">Try It</button>

<p id="demo"></p>

<script>
var x = document.getElementById("demo");

function getLocation() {
    if (navigator.geolocation) {
        navigator.geolocation.watchPosition(showPosition);
    } else { 
        x.innerHTML = "Geolocation is not supported by this browser.";}
    }

function showPosition(position) {
    x.innerHTML="Latitude: " + position.coords.latitude + 
    "<br>Longitude: " + position.coords.longitude;
}
</script>

</body>
</html>
Cœur
  • 34,719
  • 24
  • 185
  • 251
Ollie
  • 63
  • 1
  • 5

2 Answers2

7

Currently the webview/website will return the GPS coordinates when opened in a chrome browser on the phone or a laptop, however within the app will not.

You need to use a custom WebChromeClient for WebView in Droid project. Please refer to Android WebView Geolocation.

In Xamarin.Forms you can follow the below steps to accomplish this:

  1. Create a custom Control for WebView in PCL project:

    public class GeoWebView:WebView
    {
    }
    

    And use it in Xaml:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:WebViewFormsDemo"
             x:Class="WebViewFormsDemo.MainPage">
    <local:GeoWebView 
        Source="https://danu6.it.nuigalway.ie/OliverInternetProgramming/project/Loginproject.html"></local:GeoWebView>
    

  2. Create a Custom Renderer for GeoWebView in Droid Project like below:

    [assembly:ExportRenderer(typeof(GeoWebView),typeof(GeoWebViewRenderer))]
    namespace WebViewFormsDemo.Droid
    {
        public class GeoWebViewRenderer:WebViewRenderer
        {
            protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
            {
                base.OnElementChanged(e);
                Control.Settings.JavaScriptEnabled = true;
                Control.SetWebChromeClient(new MyWebClient());
            }
        }
    
        public class MyWebClient : WebChromeClient
        {
            public override void OnGeolocationPermissionsShowPrompt(string origin, GeolocationPermissions.ICallback callback)
            {
                callback.Invoke(origin, true, false);
            }
        }
    }
    
  3. Add Permissions to AndroidManifest.xml:

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

Then you will get your location in webview correctly.

Community
  • 1
  • 1
Elvis Xia - MSFT
  • 10,691
  • 1
  • 12
  • 24
2

Cheers Elvis managed to get everything working, had to make some minor changes so will post everything I did in detail:

In App.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Xamarin.Forms;

namespace WebViewFormsDemo
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            MainPage = new MainPage();
        }

    protected override void OnStart()
    {
        // Handle when your app starts
    }

    protected override void OnSleep()
    {
        // Handle when your app sleeps
    }

    protected override void OnResume()
    {
        // Handle when your app resumes
    }
}

}

In MainPage.xaml ensure that your website is 'https'

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:WebViewFormsDemo"
             x:Class="WebViewFormsDemo.MainPage">

  <local:GeoWebView
    Source="https:// --- Your Website ----></local:GeoWebView>

</ContentPage>

As Elvis said then need to create a custom control in the PLC project. Right click and add new 'Class' to do this.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace WebViewFormsDemo
{
    public class GeoWebView : WebView
    {
    }
}

Following this create a Custom Render in the Droid Class. Had some errors here initially, mostly with missing 'using' directives and also with keywords needed in the 'assembly'.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Webkit;

[assembly: ExportRenderer(typeof(WebViewFormsDemo.GeoWebView), typeof(WebViewFormsDemo.Droid.GeoWebViewRenderer))]
namespace WebViewFormsDemo.Droid
{
    public class GeoWebViewRenderer : WebViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            base.OnElementChanged(e);
            Control.Settings.JavaScriptEnabled = true;
            Control.SetWebChromeClient(new MyWebClient());
        }
    }

    public class MyWebClient : WebChromeClient
    {
        public override void OnGeolocationPermissionsShowPrompt(string origin, GeolocationPermissions.ICallback callback)
        {
            callback.Invoke(origin, true, false);
        }
    }
}

Following these changes everything worked perfectly. Thanks again Elvis!

Ollie
  • 63
  • 1
  • 5
  • 1
    It doesn't go as smoothly for me. I did as you describe so fine. But I get this error, when javascript tries getting the geolocation: `01-11 22:16:27.544 E/cr_LocationProvider(10680): Caught security exception while registering for location updates from the system. The application does not have sufficient geolocation permissions. 01-11 22:16:27.546 E/cr_LocationProvider(10680): newErrorAvailable application does not have sufficient geolocation permissions.` I have enabled both fine and coarse location permission. – Munk Jan 11 '19 at 21:18
  • 1
    I believe that happens when you target API 23 or above. The code works fine on lower SDKs. For Marshmellow and above, at some point there needs to be permission explicitly granted through a user-dialog, but I'm not sure where or how. – A. Niese Apr 18 '19 at 03:32
  • 1
    I use this in c# - follow James Montemagno permissions. This will add the extra check that you are missing var hasPermission = await UtilsPermissions.CheckPermissions(Permission.Location); – Pxaml Jun 18 '19 at 02:08
  • Please this anyone get this working for API 23 and Above? – Haybee Mar 15 '22 at 11:18