Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
[Android] Fixes cancel Webview Navigation (#5458) fixes #4891 fixes #…
Browse files Browse the repository at this point in the history
…4919 closes #5460

* [Android] Fixes cancel Webview Navigation

* - added fix 4891
- refactoring

* Fix obsolete message

Co-authored-by: Ralph Brackert <[email protected]>
  • Loading branch information
2 people authored and rmarinho committed Mar 19, 2019
1 parent c275297 commit 6a3a10a
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;

#if UITEST
using Xamarin.Forms.Core.UITests;
using Xamarin.UITest;
using NUnit.Framework;
#endif

namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[Category(UITestCategories.ManualReview)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 4891, "[Android] WebView Navigating Cancel property not working with custom scheme", PlatformAffected.Android)]
public class Issue4891 : TestContentPage // or TestMasterDetailPage, etc ...
{
Button _back;
WebView _myWebView;
Label _log;
ScrollView _logScrollView;

protected override void Init()
{

_back = new Button
{
Text = "Back"
};
_back.Clicked += Back_Clicked;

_myWebView = new WebView
{
HorizontalOptions = LayoutOptions.StartAndExpand,
VerticalOptions = LayoutOptions.Start,
HeightRequest = 240
};
_myWebView.Navigating += MyWebView_Navigating;
_myWebView.Navigated += MyWebView_Navigated;
_myWebView.Source = new HtmlWebViewSource()
{
Html = "<html><body>Click on the link below. Expected results:<br/>1. Navigating event logged.<br/>2. Navigated event NOT logged.<br/>3. This page stays loaded in the WebView control.<br/><br/><a href='xamforms4223://custom'>Navigate to Custom xamforms4223 scheme</a></body></html>"
};

_log = new Label
{
VerticalOptions = LayoutOptions.StartAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Text = ""
};

_logScrollView = new ScrollView
{
VerticalOptions = LayoutOptions.StartAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Content = _log
};

Content = new StackLayout
{
Children =
{
_myWebView,
_back,
_logScrollView
}
};
}


void MyWebView_Navigating(object sender, WebNavigatingEventArgs e)
{
LogToScreen($"Navigating: {e.Url}");
if (e.Url.StartsWith("xamforms4223", StringComparison.OrdinalIgnoreCase))
{
LogToScreen("Caught custom scheme, cancelling navigation.");
e.Cancel = true;
}
}

void MyWebView_Navigated(object sender, WebNavigatedEventArgs e)
{
LogToScreen($"Navigated: ({e.Result}) {e.Url}");
}

void LogToScreen(string text)
{
_log.Text += $"{text}\n";

InvalidateMeasure();
_logScrollView.ScrollToAsync(_log, ScrollToPosition.End, false);
}

void Back_Clicked(object sender, EventArgs e)
{
if (_myWebView.CanGoBack)
{
_myWebView.GoBack();
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;

namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 4919, "Webview Navigation cancel not working", PlatformAffected.Android)]
public class Issue4919 : TestContentPage // or TestMasterDetailPage, etc ...
{
protected override void Init()
{
var url = "https://www.microsoft.com/";
var cancel = true;
var log = new Label
{
VerticalOptions = LayoutOptions.StartAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Text = ""
};
var webView = new WebView()
{
HorizontalOptions = LayoutOptions.Fill,
VerticalOptions = LayoutOptions.FillAndExpand,
Source = url
};
webView.Navigating += (_, e) =>
{
e.Cancel = cancel;
var resultText = cancel ? "[Canceled]" : "[OK]";
log.Text += $"{resultText} {e.Url}{System.Environment.NewLine}";
};

Content = new StackLayout
{
Children =
{
new Label { Text = "WebView must be empty on init" },
webView,
new Button
{
Text = "Go to github",
Command = new Command(() => webView.Source = "https://github.com/xamarin/Xamarin.Forms")
},
new Button
{
Text = "Toggle cancel navigation",
Command = new Command(() => cancel = !cancel)
},
new ScrollView
{
VerticalOptions = LayoutOptions.EndAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Content = log
}
}
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<DependentUpon>Bugzilla60787.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue4919.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5461.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue2102.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue1588.xaml.cs">
Expand Down Expand Up @@ -133,6 +134,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla37601.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla38105.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue3652.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue4891.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla38723.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla38770.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla38827.xaml.cs">
Expand Down Expand Up @@ -1123,4 +1125,4 @@
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>
</Project>
67 changes: 36 additions & 31 deletions Xamarin.Forms.Platform.Android/Renderers/FormsWebViewClient.cs
Original file line number Diff line number Diff line change
@@ -1,60 +1,60 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Webkit;
using Android.Widget;
using WView = Android.Webkit.WebView;

namespace Xamarin.Forms.Platform.Android
{
public class FormsWebViewClient : WebViewClient
{
WebNavigationResult _navigationResult = WebNavigationResult.Success;
WebViewRenderer _renderer;
string _lastUrlNavigatedCancel;

public FormsWebViewClient(WebViewRenderer renderer)
{
if (renderer == null)
throw new ArgumentNullException("renderer");
_renderer = renderer;
}
=> _renderer = renderer ?? throw new ArgumentNullException("renderer");

protected FormsWebViewClient(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{

}

public override void OnPageStarted(global::Android.Webkit.WebView view, string url, Bitmap favicon)
{
if (_renderer?.Element == null || url == WebViewRenderer.AssetBaseUrl)
return;
bool SendNavigatingCanceled(string url) => _renderer?.SendNavigatingCanceled(url) ?? true;

var args = new WebNavigatingEventArgs(WebNavigationEvent.NewPage, new UrlWebViewSource { Url = url }, url);
[Obsolete("ShouldOverrideUrlLoading(view,url) is obsolete as of version 4.0.0. This method was deprecated in API level 24.")]
[EditorBrowsable(EditorBrowsableState.Never)]
// api 19-23
public override bool ShouldOverrideUrlLoading(WView view, string url)
=> SendNavigatingCanceled(url);

_renderer.ElementController.SendNavigating(args);
_navigationResult = WebNavigationResult.Success;
// api 24+
public override bool ShouldOverrideUrlLoading(WView view, IWebResourceRequest request)
=> SendNavigatingCanceled(request?.Url?.ToString());

_renderer.UpdateCanGoBackForward();
public override void OnPageStarted(WView view, string url, Bitmap favicon)
{
if (_renderer == null || string.IsNullOrWhiteSpace(url) || url == WebViewRenderer.AssetBaseUrl)
return;

if (args.Cancel)
var cancel = false;
if (!url.Equals(_renderer.UrlCanceled, StringComparison.OrdinalIgnoreCase))
cancel = SendNavigatingCanceled(url);
_renderer.UrlCanceled = null;

if (cancel)
{
_renderer.Control.StopLoading();
_navigationResult = WebNavigationResult.Cancel;
view.StopLoading();
}
else
{
_navigationResult = WebNavigationResult.Success;
base.OnPageStarted(view, url, favicon);
}
}

public override void OnPageFinished(global::Android.Webkit.WebView view, string url)
public override void OnPageFinished(WView view, string url)
{
if (_renderer?.Element == null || url == WebViewRenderer.AssetBaseUrl)
return;
Expand All @@ -64,9 +64,14 @@ public override void OnPageFinished(global::Android.Webkit.WebView view, string
_renderer.ElementController.SetValueFromRenderer(WebView.SourceProperty, source);
_renderer.IgnoreSourceChanges = false;

var args = new WebNavigatedEventArgs(WebNavigationEvent.NewPage, source, url, _navigationResult);
bool navigate = _navigationResult == WebNavigationResult.Failure ? !url.Equals(_lastUrlNavigatedCancel, StringComparison.OrdinalIgnoreCase) : true;
_lastUrlNavigatedCancel = _navigationResult == WebNavigationResult.Cancel ? url : null;

_renderer.ElementController.SendNavigated(args);
if (navigate)
{
var args = new WebNavigatedEventArgs(WebNavigationEvent.NewPage, source, url, _navigationResult);
_renderer.ElementController.SendNavigated(args);
}

_renderer.UpdateCanGoBackForward();

Expand All @@ -75,7 +80,7 @@ public override void OnPageFinished(global::Android.Webkit.WebView view, string

[Obsolete("OnReceivedError is obsolete as of version 2.3.0. This method was deprecated in API level 23.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public override void OnReceivedError(global::Android.Webkit.WebView view, ClientError errorCode, string description, string failingUrl)
public override void OnReceivedError(WView view, ClientError errorCode, string description, string failingUrl)
{
_navigationResult = WebNavigationResult.Failure;
if (errorCode == ClientError.Timeout)
Expand All @@ -85,7 +90,7 @@ public override void OnReceivedError(global::Android.Webkit.WebView view, Client
#pragma warning restore 618
}

public override void OnReceivedError(global::Android.Webkit.WebView view, IWebResourceRequest request, WebResourceError error)
public override void OnReceivedError(WView view, IWebResourceRequest request, WebResourceError error)
{
_navigationResult = WebNavigationResult.Failure;
if (error.ErrorCode == ClientError.Timeout)
Expand All @@ -100,4 +105,4 @@ protected override void Dispose(bool disposing)
_renderer = null;
}
}
}
}
21 changes: 18 additions & 3 deletions Xamarin.Forms.Platform.Android/Renderers/WebViewRenderer.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System;
using System.ComponentModel;
using Android.App;
using Android.Content;
using Android.Webkit;
using Android.Widget;
using Android.OS;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
using Xamarin.Forms.Internals;
Expand All @@ -22,6 +20,7 @@ public class WebViewRenderer : ViewRenderer<WebView, AWebView>, IWebViewDelegate

protected internal IWebViewController ElementController => Element;
protected internal bool IgnoreSourceChanges { get; set; }
protected internal string UrlCanceled { get; set; }

public WebViewRenderer(Context context) : base(context)
{
Expand All @@ -42,7 +41,23 @@ public void LoadHtml(string html, string baseUrl)

public void LoadUrl(string url)
{
Control.LoadUrl(url);
if (!SendNavigatingCanceled(url))
Control.LoadUrl(url);
}

protected internal bool SendNavigatingCanceled(string url)
{
if (Element == null || string.IsNullOrWhiteSpace(url))
return true;

if (url == AssetBaseUrl)
return false;

var args = new WebNavigatingEventArgs(WebNavigationEvent.NewPage, new UrlWebViewSource { Url = url }, url);
ElementController.SendNavigating(args);
UpdateCanGoBackForward();
UrlCanceled = args.Cancel ? null : url;
return args.Cancel;
}

protected override void Dispose(bool disposing)
Expand Down

0 comments on commit 6a3a10a

Please sign in to comment.