10

I am using Xamarin WebView controller to display a web site (in both iOS/Android). The web site's home page prompts to access the device location. But that prompt is not showing when I have this website on a WebView (from the App). When I open this site on the browser, from the same mobile device it shows the message box. I am just wondering if it is possible to have such prompts on Xamarin WebView?

Here is an example prompt.

enter image description here

This is all I have in the Xamarin app.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:nCollar"
             x:Class="nCollar.MainPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness">
            <OnPlatform.iOS>0,20,0,0</OnPlatform.iOS>
            <OnPlatform.Android>0,0,0,0</OnPlatform.Android>
            <OnPlatform.WinPhone>0,0,0,0</OnPlatform.WinPhone>
        </OnPlatform>
    </ContentPage.Padding>

    <WebView x:Name="webView"
                 Grid.Row="0"
                 VerticalOptions="FillAndExpand" 
                 Source="https://www.ncollar.com.au/"/>

</ContentPage>

UPDATE 1

Android

I've tried the solution mentioned in this post, but still it doesn't show the prompt.

WebViewRenderer.cs

using Android.Webkit;
using Xamarin.Forms.Platform.Android;

[assembly: Xamarin.Forms.ExportRenderer(typeof(TestApp.Controls.WebView), typeof(TestApp.Droid.Renderers.WebViewRenderer))]
namespace TestApp.Droid.Renderers
{
    public class WebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            base.OnElementChanged(e);

            if (this.Control == null)
            {
                return;
            }

            var webView = this.Element as TestApp.Controls.WebView;
            if (webView == null)
            {
                return;
            }


           // webView. ings.JavaScriptEnabled = true;
            this.Control.Settings.DomStorageEnabled = true;
            this.Control.Settings.DisplayZoomControls = true;
            this.Control.Settings.BuiltInZoomControls = true;
            this.Control.Settings.SetGeolocationEnabled(true);
            this.Control.Settings.JavaScriptCanOpenWindowsAutomatically = true;

            this.Control.SetWebViewClient(new GeoWebViewClient());
            this.Control.SetWebChromeClient(new GeoWebChromeClient());
        }
    }

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

    public class GeoWebViewClient : WebViewClient
    {
        public override bool ShouldOverrideUrlLoading(WebView view, string url)
        {
            view.LoadUrl(url);
            return true;
        }
    }
}

Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-sdk android:minSdkVersion="15" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <application android:label="TestApp.Android"></application>
</manifest>

iOS

Tried NSLocationWhenInUseUsageDescription in the plist file, but no difference.

CharithJ
  • 46,289
  • 20
  • 116
  • 131

2 Answers2

3

Updated answer (with code)

In case of Android - you will need a custom renderer for your WebView control. (ref: gist)

public class ExtendedWebViewRenderer : WebViewRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
    {
        base.OnElementChanged(e);

        if (e.OldElement != null || Element == null)
        {
            return;
        }

        Control.Settings.JavaScriptEnabled = true;
        Control.Settings.SetGeolocationEnabled(true);
        Control.Settings.SetGeolocationDatabasePath(Control.Context.FilesDir.Path);

        Control.SetWebViewClient(new CustomWebViewClient());
        Control.SetWebChromeClient(new CustomChromeClient(Control.Context));
    }
}

public class CustomWebViewClient : WebViewClient
{
    public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
    {
        view.LoadUrl(url);
        return true;
    }
}

public class CustomChromeClient : WebChromeClient
{
    private readonly Context _context;

    public CustomChromeClient(Context context)
    {
        _context = context;
    }

    public override void OnGeolocationPermissionsShowPrompt(string origin, GeolocationPermissions.ICallback callback)
    {
        const bool remember = false;
        var builder = new AlertDialog.Builder(_context);
        builder.SetTitle("Location")
            .SetMessage(string.Format("{0} would like to use your current location", origin))
            .SetPositiveButton("Allow", (sender, args) => callback.Invoke(origin, true, remember))
            .SetNegativeButton("Disallow", (sender, args) => callback.Invoke(origin, false, remember));
        var alert = builder.Create();
        alert.Show();
    }
}

Manifest permissions

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

Usage sample

<StackLayout Margin="30" BackgroundColor="#ededed">
    <Label Text="WebView location prompt sample" Margin="15" HorizontalOptions="Center" />
    <local:ExtendedWebView x:Name="webView" Source="https://www.yellowpages.com/" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</StackLayout>

android output

And, in case of iOS, you need to set NSLocationWhenInUseUsageDescription description in the info.plist file.

info.plist

And it displays the prompt.

ios output

Complete code sample can be found at this link


Original answer

In case of Android - you should be able to resolve this by adding a platform-specific renderer for WebView and implement the permission handling by adding a custom WebChromeClient.

In case of iOS - you need to add NSLocationWhenInUseUsageDescription description in your info plist

Sharada Gururaj
  • 13,471
  • 1
  • 22
  • 50
  • I will have to research on that and test it myself, before I can get back to you on that. Did the suggestion by @yuri-s about showing a prompt before callback not work out? – Sharada Gururaj Aug 02 '17 at 14:37
  • I am fairly new to Xamarin and I have tried this for couple of days but no luck. You are the only person who provided any hint for a solution. I'll offer you the bounty. Please have a look and let me know if you have any idea why this is not working... Thanks! – CharithJ Aug 03 '17 at 02:31
  • 1
    @SharadaGururaj great link to the same answer I was looking at. Please CC to me if you solve that without implementing custom permission request dialog. I am interested to see how. Thanks. – Yuri S Aug 03 '17 at 22:00
  • @CharithJ: Updated the answer with a working sample. While testing in iOS - please make sure to reset your privacy settings for the prompt to appear. In case of android, the prompt will be shown every time as we have `remember` as false. – Sharada Gururaj Aug 06 '17 at 09:30
  • @SharadaGururaj good work but it is not an answer for Android. I always said you need your own dialog. But for API less than 23 it is not even necessary as permission in manifest will enable access so no need to call callback. For API 23 you need more than just dialog and callback. You need to actually request permissions. So the code works great but you don't need all this :-) The question was "forms swallow the prompt". The answer is "forms don't swallow, you need to implement yourself", which you did. Good job – Yuri S Aug 06 '17 at 15:58
  • @SharadaGururaj: Thank you very much! I have tested this on iOS and it works fine! Will test on Android. – CharithJ Aug 06 '17 at 23:28
  • @SharadaGururaj: It still doesn't show the prompt for Android when I use it for my site http://www.ncollar.com.au In fact the execution never comes to OnGeolocationPermissionsShowPrompt method.. – CharithJ Aug 07 '17 at 01:16
  • @CharithJ : I just tested the sample with your website - it worked as expected (https://imgur.com/a/0U7Dv). Can you please let me know more details about the android version that you are testing with? – Sharada Gururaj Aug 07 '17 at 01:28
  • @CharithJ : Also, I hope you are using fully qualified source 'https://www.ncollar.com.au/' as source instead of 'ncollar.com.au' – Sharada Gururaj Aug 07 '17 at 01:32
  • @SharadaGururaj: May be my project is missing something then... Actually, looks like your github project is still missing something I can't just download and try it. It gives me two errors The name 'InitializeComponent' does not exist in the current context LocationWebViewPage.xaml.cs The name 'InitializeComponent' does not exist in the current context LocationWebViewPage.xaml.cs – CharithJ Aug 07 '17 at 01:43
  • That happens when you haven't restored nuget packages. – Sharada Gururaj Aug 07 '17 at 02:07
1

But that prompt is not showing when I have this website on a WebView (from the App).

If I got it right, you're wrapping this in an app. That's not the expected user experience from a mobile app. your app should use the platform(e.g iOS,Android) location permissions in order to get the user approval.

Xamarin will let you access Location on each platform quite differently. If the Xamarin app has access to location than the webview should inherit these permissions. So it might worth trying this recipe and try accsing location before opening the webview itself

ApriOri
  • 2,618
  • 29
  • 48
  • :I agree that this solution is not ideal. However, there are plenty of apps using this approach (at least to start with). And also some such applications are capable enough to display that prompt. I am trying to do the same thing. – CharithJ Jul 26 '17 at 11:25