Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit a625523

Browse files
authored
[webview_flutter_android] Adds Android implementation of PlatformNavigationDelegate (#6495)
* Adds the Android implementation of PlatformNavigationDelegate. * Fix analyzer warnings (all in existing code) * Fix formatting * Add missing license header * Added PR feedback * Fixed formatting
1 parent d479010 commit a625523

File tree

7 files changed

+784
-28
lines changed

7 files changed

+784
-28
lines changed

packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,9 @@ class JavaObjectFlutterApiImpl implements JavaObjectFlutterApi {
128128
class WebViewHostApiImpl extends WebViewHostApi {
129129
/// Constructs a [WebViewHostApiImpl].
130130
WebViewHostApiImpl({
131-
BinaryMessenger? binaryMessenger,
131+
super.binaryMessenger,
132132
InstanceManager? instanceManager,
133-
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
134-
super(binaryMessenger: binaryMessenger);
133+
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
135134

136135
/// Maintains instances stored to communicate with java objects.
137136
final InstanceManager instanceManager;
@@ -342,10 +341,9 @@ class WebViewHostApiImpl extends WebViewHostApi {
342341
class WebSettingsHostApiImpl extends WebSettingsHostApi {
343342
/// Constructs a [WebSettingsHostApiImpl].
344343
WebSettingsHostApiImpl({
345-
BinaryMessenger? binaryMessenger,
344+
super.binaryMessenger,
346345
InstanceManager? instanceManager,
347-
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
348-
super(binaryMessenger: binaryMessenger);
346+
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
349347

350348
/// Maintains instances stored to communicate with java objects.
351349
final InstanceManager instanceManager;
@@ -484,10 +482,9 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi {
484482
class JavaScriptChannelHostApiImpl extends JavaScriptChannelHostApi {
485483
/// Constructs a [JavaScriptChannelHostApiImpl].
486484
JavaScriptChannelHostApiImpl({
487-
BinaryMessenger? binaryMessenger,
485+
super.binaryMessenger,
488486
InstanceManager? instanceManager,
489-
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
490-
super(binaryMessenger: binaryMessenger);
487+
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
491488

492489
/// Maintains instances stored to communicate with java objects.
493490
final InstanceManager instanceManager;
@@ -529,10 +526,9 @@ class JavaScriptChannelFlutterApiImpl extends JavaScriptChannelFlutterApi {
529526
class WebViewClientHostApiImpl extends WebViewClientHostApi {
530527
/// Constructs a [WebViewClientHostApiImpl].
531528
WebViewClientHostApiImpl({
532-
BinaryMessenger? binaryMessenger,
529+
super.binaryMessenger,
533530
InstanceManager? instanceManager,
534-
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
535-
super(binaryMessenger: binaryMessenger);
531+
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
536532

537533
/// Maintains instances stored to communicate with java objects.
538534
final InstanceManager instanceManager;
@@ -717,10 +713,9 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi {
717713
class DownloadListenerHostApiImpl extends DownloadListenerHostApi {
718714
/// Constructs a [DownloadListenerHostApiImpl].
719715
DownloadListenerHostApiImpl({
720-
BinaryMessenger? binaryMessenger,
716+
super.binaryMessenger,
721717
InstanceManager? instanceManager,
722-
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
723-
super(binaryMessenger: binaryMessenger);
718+
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
724719

725720
/// Maintains instances stored to communicate with java objects.
726721
final InstanceManager instanceManager;
@@ -772,10 +767,9 @@ class DownloadListenerFlutterApiImpl extends DownloadListenerFlutterApi {
772767
class WebChromeClientHostApiImpl extends WebChromeClientHostApi {
773768
/// Constructs a [WebChromeClientHostApiImpl].
774769
WebChromeClientHostApiImpl({
775-
BinaryMessenger? binaryMessenger,
770+
super.binaryMessenger,
776771
InstanceManager? instanceManager,
777-
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
778-
super(binaryMessenger: binaryMessenger);
772+
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
779773

780774
/// Maintains instances stored to communicate with java objects.
781775
final InstanceManager instanceManager;
@@ -822,10 +816,9 @@ class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
822816
class WebStorageHostApiImpl extends WebStorageHostApi {
823817
/// Constructs a [WebStorageHostApiImpl].
824818
WebStorageHostApiImpl({
825-
BinaryMessenger? binaryMessenger,
819+
super.binaryMessenger,
826820
InstanceManager? instanceManager,
827-
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
828-
super(binaryMessenger: binaryMessenger);
821+
}) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
829822

830823
/// Maintains instances stored to communicate with java objects.
831824
final InstanceManager instanceManager;
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:flutter/foundation.dart';
8+
import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart';
9+
10+
import '../../android_webview.dart' as android_webview;
11+
import 'android_proxy.dart';
12+
13+
/// Signature for the `loadUrl` callback responsible for loading the [url]
14+
/// after a navigation request has been approved.
15+
typedef LoadUrlCallback = Future<void> Function(
16+
String url, Map<String, String>? headers);
17+
18+
/// Error returned in `WebView.onWebResourceError` when a web resource loading error has occurred.
19+
class AndroidWebResourceError extends WebResourceError {
20+
/// Creates a new [AndroidWebResourceError].
21+
AndroidWebResourceError._({
22+
required super.errorCode,
23+
required super.description,
24+
this.failingUrl,
25+
}) : super(
26+
errorType: _errorCodeToErrorType(errorCode),
27+
);
28+
29+
/// Gets the URL for which the failing resource request was made.
30+
final String? failingUrl;
31+
32+
static WebResourceErrorType? _errorCodeToErrorType(int errorCode) {
33+
switch (errorCode) {
34+
case android_webview.WebViewClient.errorAuthentication:
35+
return WebResourceErrorType.authentication;
36+
case android_webview.WebViewClient.errorBadUrl:
37+
return WebResourceErrorType.badUrl;
38+
case android_webview.WebViewClient.errorConnect:
39+
return WebResourceErrorType.connect;
40+
case android_webview.WebViewClient.errorFailedSslHandshake:
41+
return WebResourceErrorType.failedSslHandshake;
42+
case android_webview.WebViewClient.errorFile:
43+
return WebResourceErrorType.file;
44+
case android_webview.WebViewClient.errorFileNotFound:
45+
return WebResourceErrorType.fileNotFound;
46+
case android_webview.WebViewClient.errorHostLookup:
47+
return WebResourceErrorType.hostLookup;
48+
case android_webview.WebViewClient.errorIO:
49+
return WebResourceErrorType.io;
50+
case android_webview.WebViewClient.errorProxyAuthentication:
51+
return WebResourceErrorType.proxyAuthentication;
52+
case android_webview.WebViewClient.errorRedirectLoop:
53+
return WebResourceErrorType.redirectLoop;
54+
case android_webview.WebViewClient.errorTimeout:
55+
return WebResourceErrorType.timeout;
56+
case android_webview.WebViewClient.errorTooManyRequests:
57+
return WebResourceErrorType.tooManyRequests;
58+
case android_webview.WebViewClient.errorUnknown:
59+
return WebResourceErrorType.unknown;
60+
case android_webview.WebViewClient.errorUnsafeResource:
61+
return WebResourceErrorType.unsafeResource;
62+
case android_webview.WebViewClient.errorUnsupportedAuthScheme:
63+
return WebResourceErrorType.unsupportedAuthScheme;
64+
case android_webview.WebViewClient.errorUnsupportedScheme:
65+
return WebResourceErrorType.unsupportedScheme;
66+
}
67+
68+
throw ArgumentError(
69+
'Could not find a WebResourceErrorType for errorCode: $errorCode',
70+
);
71+
}
72+
}
73+
74+
/// Object specifying creation parameters for creating a [AndroidNavigationDelegate].
75+
///
76+
/// When adding additional fields make sure they can be null or have a default
77+
/// value to avoid breaking changes. See [PlatformNavigationDelegateCreationParams] for
78+
/// more information.
79+
@immutable
80+
class AndroidNavigationDelegateCreationParams
81+
extends PlatformNavigationDelegateCreationParams {
82+
/// Creates a new [AndroidNavigationDelegateCreationParams] instance.
83+
const AndroidNavigationDelegateCreationParams._({
84+
@visibleForTesting this.androidWebViewProxy = const AndroidWebViewProxy(),
85+
}) : super();
86+
87+
/// Creates a [AndroidNavigationDelegateCreationParams] instance based on [PlatformNavigationDelegateCreationParams].
88+
factory AndroidNavigationDelegateCreationParams.fromPlatformNavigationDelegateCreationParams(
89+
// Recommended placeholder to prevent being broken by platform interface.
90+
// ignore: avoid_unused_constructor_parameters
91+
PlatformNavigationDelegateCreationParams params, {
92+
@visibleForTesting
93+
AndroidWebViewProxy androidWebViewProxy = const AndroidWebViewProxy(),
94+
}) {
95+
return AndroidNavigationDelegateCreationParams._(
96+
androidWebViewProxy: androidWebViewProxy,
97+
);
98+
}
99+
100+
/// Handles constructing objects and calling static methods for the Android WebView
101+
/// native library.
102+
@visibleForTesting
103+
final AndroidWebViewProxy androidWebViewProxy;
104+
}
105+
106+
/// A place to register callback methods responsible to handle navigation events
107+
/// triggered by the [android_webview.WebView].
108+
class AndroidNavigationDelegate extends PlatformNavigationDelegate {
109+
/// Creates a new [AndroidNavigationkDelegate].
110+
AndroidNavigationDelegate(PlatformNavigationDelegateCreationParams params)
111+
: super.implementation(params is AndroidNavigationDelegateCreationParams
112+
? params
113+
: AndroidNavigationDelegateCreationParams
114+
.fromPlatformNavigationDelegateCreationParams(params)) {
115+
final WeakReference<AndroidNavigationDelegate> weakThis =
116+
WeakReference<AndroidNavigationDelegate>(this);
117+
118+
_webChromeClient = (params as AndroidNavigationDelegateCreationParams)
119+
.androidWebViewProxy
120+
.createAndroidWebChromeClient(
121+
onProgressChanged: (android_webview.WebView webView, int progress) {
122+
if (weakThis.target?._onProgress != null) {
123+
weakThis.target!._onProgress!(progress);
124+
}
125+
});
126+
127+
_webViewClient = params.androidWebViewProxy.createAndroidWebViewClient(
128+
onPageFinished: (android_webview.WebView webView, String url) {
129+
if (weakThis.target?._onPageFinished != null) {
130+
weakThis.target!._onPageFinished!(url);
131+
}
132+
},
133+
onPageStarted: (android_webview.WebView webView, String url) {
134+
if (weakThis.target?._onPageStarted != null) {
135+
weakThis.target!._onPageStarted!(url);
136+
}
137+
},
138+
onReceivedRequestError: (
139+
android_webview.WebView webView,
140+
android_webview.WebResourceRequest request,
141+
android_webview.WebResourceError error,
142+
) {
143+
if (weakThis.target?._onWebResourceError != null &&
144+
request.isForMainFrame) {
145+
weakThis.target!._onWebResourceError!(AndroidWebResourceError._(
146+
errorCode: error.errorCode,
147+
description: error.description,
148+
failingUrl: request.url,
149+
));
150+
}
151+
},
152+
onReceivedError: (
153+
android_webview.WebView webView,
154+
int errorCode,
155+
String description,
156+
String failingUrl,
157+
) {
158+
if (weakThis.target?._onWebResourceError != null) {
159+
weakThis.target!._onWebResourceError!(AndroidWebResourceError._(
160+
errorCode: errorCode,
161+
description: description,
162+
failingUrl: failingUrl,
163+
));
164+
}
165+
},
166+
requestLoading: (
167+
android_webview.WebView webView,
168+
android_webview.WebResourceRequest request,
169+
) {
170+
if (weakThis.target != null) {
171+
weakThis.target!._handleNavigation(
172+
request.url,
173+
request.requestHeaders,
174+
);
175+
}
176+
},
177+
urlLoading: (
178+
android_webview.WebView webView,
179+
String url,
180+
) {
181+
if (weakThis.target != null) {
182+
weakThis.target!._handleNavigation(url, <String, String>{});
183+
}
184+
},
185+
);
186+
}
187+
188+
late final android_webview.WebChromeClient _webChromeClient;
189+
190+
/// Gets the native [android_webview.WebChromeClient] that is bridged by this [AndroidNavigationDelegate].
191+
///
192+
/// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setWebChromeClient`.
193+
android_webview.WebChromeClient get androidWebChromeClient =>
194+
_webChromeClient;
195+
196+
late final android_webview.WebViewClient _webViewClient;
197+
198+
/// Gets the native [android_webview.WebViewClient] that is bridged by this [AndroidNavigationDelegate].
199+
///
200+
/// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setWebViewClient`.
201+
android_webview.WebViewClient get androidWebViewClient => _webViewClient;
202+
203+
PageEventCallback? _onPageFinished;
204+
PageEventCallback? _onPageStarted;
205+
ProgressCallback? _onProgress;
206+
WebResourceErrorCallback? _onWebResourceError;
207+
NavigationRequestCallback? _onNavigationRequest;
208+
LoadUrlCallback? _onLoadUrl;
209+
210+
void _handleNavigation(String url, Map<String, String> headers) {
211+
final LoadUrlCallback? onLoadUrl = _onLoadUrl;
212+
final NavigationRequestCallback? onNavigationRequest = _onNavigationRequest;
213+
214+
if (onNavigationRequest == null || onLoadUrl == null) {
215+
return;
216+
}
217+
218+
final FutureOr<NavigationDecision> returnValue = onNavigationRequest(
219+
NavigationRequest(
220+
isMainFrame: true,
221+
url: url,
222+
),
223+
);
224+
225+
if (returnValue is NavigationDecision &&
226+
returnValue == NavigationDecision.navigate) {
227+
onLoadUrl(url, headers);
228+
} else if (returnValue is Future<NavigationDecision>) {
229+
returnValue.then((NavigationDecision shouldLoadUrl) {
230+
if (shouldLoadUrl == NavigationDecision.navigate) {
231+
onLoadUrl(url, headers);
232+
}
233+
});
234+
}
235+
}
236+
237+
/// Invoked when loading the [url] after a navigation request is approved.
238+
Future<void> setOnLoadUrl(
239+
LoadUrlCallback onLoadUrl,
240+
) async {
241+
_onLoadUrl = onLoadUrl;
242+
}
243+
244+
@override
245+
Future<void> setOnNavigationRequest(
246+
NavigationRequestCallback onNavigationRequest,
247+
) async {
248+
_onNavigationRequest = onNavigationRequest;
249+
_webViewClient.setSynchronousReturnValueForShouldOverrideUrlLoading(true);
250+
}
251+
252+
@override
253+
Future<void> setOnPageStarted(
254+
PageEventCallback onPageStarted,
255+
) async {
256+
_onPageStarted = onPageStarted;
257+
}
258+
259+
@override
260+
Future<void> setOnPageFinished(
261+
PageEventCallback onPageFinished,
262+
) async {
263+
_onPageFinished = onPageFinished;
264+
}
265+
266+
@override
267+
Future<void> setOnProgress(
268+
ProgressCallback onProgress,
269+
) async {
270+
_onProgress = onProgress;
271+
}
272+
273+
@override
274+
Future<void> setOnWebResourceError(
275+
WebResourceErrorCallback onWebResourceError,
276+
) async {
277+
_onWebResourceError = onWebResourceError;
278+
}
279+
}

0 commit comments

Comments
 (0)