From ec24f41914d06ebd97972a74db341cf8afa15910 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Thu, 21 Apr 2022 12:20:48 +0200 Subject: [PATCH] updated onPermissionRequest signature --- .../InAppWebViewChromeClient.java | 1 + example/android/app/build.gradle | 4 +- example/lib/in_app_webiew_example.screen.dart | 2 - lib/src/in_app_browser/in_app_browser.dart | 4 +- .../headless_in_app_webview.dart | 4 +- lib/src/in_app_webview/in_app_webview.dart | 5 +- .../in_app_webview_controller.dart | 14 +- lib/src/in_app_webview/webview.dart | 5 +- lib/src/types.dart | 246 ++++++++++++++++++ 9 files changed, 266 insertions(+), 19 deletions(-) diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java index a5c7765e..1aaf70c8 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java @@ -1113,6 +1113,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR Map obj = new HashMap<>(); obj.put("origin", request.getOrigin().toString()); obj.put("resources", Arrays.asList(request.getResources())); + obj.put("frame", null); channel.invokeMethod("onPermissionRequest", obj, new MethodChannel.Result() { @Override public void success(Object response) { diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index e97f43d0..de44914a 100755 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -62,8 +62,8 @@ flutter { dependencies { testImplementation 'junit:junit:4.13' - androidTestImplementation 'androidx.test:runner:1.3.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'com.google.android.material:material:1.3.0' implementation 'com.android.support:multidex:1.0.3' } diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 0f51ad99..8293ff2c 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -131,7 +131,6 @@ class _InAppWebViewExampleScreenState extends State { this.url = url.toString(); urlController.text = this.url; }); - print((await controller.getSettings())?.upgradeKnownHostsToHTTPS); }, onPermissionRequest: (controller, origin, resources) async { return PermissionRequestResponse( @@ -168,7 +167,6 @@ class _InAppWebViewExampleScreenState extends State { this.url = url.toString(); urlController.text = this.url; }); - print((await controller.getSettings())?.upgradeKnownHostsToHTTPS); }, onLoadError: (controller, url, code, message) { pullToRefreshController.endRefreshing(); diff --git a/lib/src/in_app_browser/in_app_browser.dart b/lib/src/in_app_browser/in_app_browser.dart index 8df9535d..abd513f8 100755 --- a/lib/src/in_app_browser/in_app_browser.dart +++ b/lib/src/in_app_browser/in_app_browser.dart @@ -796,8 +796,8 @@ class InAppBrowser { /// ///**Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest))) - Future? onPermissionRequest( - String origin, List resources) {} + Future? onPermissionRequest( + PermissionRequest permissionRequest) {} ///Use [onGeolocationPermissionsShowPrompt] instead. @Deprecated("Use onGeolocationPermissionsShowPrompt instead") diff --git a/lib/src/in_app_webview/headless_in_app_webview.dart b/lib/src/in_app_webview/headless_in_app_webview.dart index eee235ad..da8d5064 100644 --- a/lib/src/in_app_webview/headless_in_app_webview.dart +++ b/lib/src/in_app_webview/headless_in_app_webview.dart @@ -570,8 +570,8 @@ class HeadlessInAppWebView implements WebView { NavigationResponse navigationResponse)? onNavigationResponse; @override - Future Function(InAppWebViewController controller, - String origin, List resources)? onPermissionRequest; + Future Function(InAppWebViewController controller, + PermissionRequest permissionRequest)? onPermissionRequest; @override void Function(InAppWebViewController controller, Uint8List icon)? diff --git a/lib/src/in_app_webview/in_app_webview.dart b/lib/src/in_app_webview/in_app_webview.dart index 70057cad..5a48f574 100755 --- a/lib/src/in_app_webview/in_app_webview.dart +++ b/lib/src/in_app_webview/in_app_webview.dart @@ -469,10 +469,9 @@ class InAppWebView extends StatefulWidget implements WebView { NavigationResponse navigationResponse)? onNavigationResponse; @override - final Future Function( + final Future Function( InAppWebViewController controller, - String origin, - List resources)? onPermissionRequest; + PermissionRequest permissionRequest)? onPermissionRequest; @override final void Function(InAppWebViewController controller, Uint8List icon)? diff --git a/lib/src/in_app_webview/in_app_webview_controller.dart b/lib/src/in_app_webview/in_app_webview_controller.dart index daebf88c..33dd0bb2 100644 --- a/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/lib/src/in_app_webview/in_app_webview_controller.dart @@ -731,10 +731,15 @@ class InAppWebViewController String origin = call.arguments["origin"]; List resources = call.arguments["resources"].cast(); + Map arguments = + call.arguments.cast(); + PermissionRequest permissionRequest = + PermissionRequest.fromMap(arguments)!; + if (_webview != null) { if (_webview!.onPermissionRequest != null) return (await _webview!.onPermissionRequest!( - this, origin, resources)) + this, permissionRequest)) ?.toMap(); else { // ignore: deprecated_member_use_from_same_package @@ -743,12 +748,11 @@ class InAppWebViewController ?.toMap(); } } else { - return ((await _inAppBrowser! - .onPermissionRequest(origin, resources)) ?? + return (await _inAppBrowser! + .onPermissionRequest(permissionRequest))?.toMap() ?? (await _inAppBrowser! // ignore: deprecated_member_use_from_same_package - .androidOnPermissionRequest(origin, resources))) - ?.toMap(); + .androidOnPermissionRequest(origin, resources))?.toMap(); } } break; diff --git a/lib/src/in_app_webview/webview.dart b/lib/src/in_app_webview/webview.dart index e955af1c..af7036fa 100644 --- a/lib/src/in_app_webview/webview.dart +++ b/lib/src/in_app_webview/webview.dart @@ -510,10 +510,9 @@ abstract class WebView { /// ///**Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest))) - final Future Function( + final Future Function( InAppWebViewController controller, - String origin, - List resources)? onPermissionRequest; + PermissionRequest permissionRequest)? onPermissionRequest; ///Use [onGeolocationPermissionsShowPrompt] instead. @Deprecated("Use onGeolocationPermissionsShowPrompt instead") diff --git a/lib/src/types.dart b/lib/src/types.dart index 7506b029..51718c26 100755 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -4703,7 +4703,29 @@ class Cookie { } } +///Class used by [PermissionResponse] class. +class PermissionResponseAction { + final int _value; + + const PermissionResponseAction._internal(this._value); + + int toValue() => _value; + + ///Denies the request. + static const DENY = const PermissionResponseAction._internal(0); + + ///Grants origin the permission to access the given resources. + static const GRANT = const PermissionResponseAction._internal(1); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; +} + ///Class used by [PermissionRequestResponse] class. +///Use [PermissionResponseAction] instead. +@Deprecated("Use PermissionResponseAction instead") class PermissionRequestResponseAction { final int _value; @@ -4723,7 +4745,207 @@ class PermissionRequestResponseAction { int get hashCode => _value.hashCode; } +///Class that represents a type of resource used to ask user's permission. +class PermissionResourceType { + final dynamic _value; + + const PermissionResourceType._internal(this._value); + + static final Set values = [ + PermissionResourceType.RESOURCE_AUDIO_CAPTURE, + PermissionResourceType.RESOURCE_MIDI_SYSEX, + PermissionResourceType.RESOURCE_PROTECTED_MEDIA_ID, + PermissionResourceType.RESOURCE_VIDEO_CAPTURE, + ].toSet(); + + static final Set _androidValues = [ + PermissionResourceType.RESOURCE_AUDIO_CAPTURE, + PermissionResourceType.RESOURCE_MIDI_SYSEX, + PermissionResourceType.RESOURCE_PROTECTED_MEDIA_ID, + PermissionResourceType.RESOURCE_VIDEO_CAPTURE, + ].toSet(); + + static final Set _appleValues = [ + + ].toSet(); + + static PermissionResourceType? fromValue(dynamic? value) { + if (value != null) { + try { + Set valueList = [].toSet(); + if (Platform.isAndroid) { + valueList = PermissionResourceType._androidValues; + } else if (Platform.isIOS || Platform.isMacOS) { + valueList = PermissionResourceType._appleValues; + } + return valueList.firstWhere((element) => element.toValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + dynamic toValue() => _value; + + @override + String toString() { + if (_value is String) { + return _value; + } + switch (_value) { + case 0: + return "CAMERA"; + case 1: + return "MICROPHONE"; + case 2: + return "CAMERA_AND_MICROPHONE"; + default: + return ""; + } + } + + ///Resource belongs to audio capture device, like microphone. + /// + ///**NOTE**: available only on Android. + static const RESOURCE_AUDIO_CAPTURE = const PermissionResourceType._internal('android.webkit.resource.AUDIO_CAPTURE'); + + ///Resource will allow sysex messages to be sent to or received from MIDI devices. + ///These messages are privileged operations, e.g. modifying sound libraries and sampling data, or even updating the MIDI device's firmware. + ///Permission may be requested for this resource in API levels 21 and above, if the Android device has been updated to WebView 45 or above. + /// + ///**NOTE**: available only on Android. + static const RESOURCE_MIDI_SYSEX = + const PermissionResourceType._internal('android.webkit.resource.MIDI_SYSEX'); + + ///Resource belongs to protected media identifier. After the user grants this resource, the origin can use EME APIs to generate the license requests. + /// + ///**NOTE**: available only on Android. + static const RESOURCE_PROTECTED_MEDIA_ID = + const PermissionResourceType._internal('android.webkit.resource.PROTECTED_MEDIA_ID'); + + + ///Resource belongs to video capture device, like camera. + /// + ///**NOTE**: available only on Android. + static const RESOURCE_VIDEO_CAPTURE = + const PermissionResourceType._internal('android.webkit.resource.VIDEO_CAPTURE'); + + ///A media device that can capture video. + /// + ///**NOTE**: available only on iOS. + static const CAMERA = + const PermissionResourceType._internal(0); + + ///A media device that can capture audio. + /// + ///**NOTE**: available only on iOS. + static const MICROPHONE = + const PermissionResourceType._internal(1); + + ///A media device or devices that can capture audio and video. + /// + ///**NOTE**: available only on iOS. + static const CAMERA_AND_MICROPHONE = + const PermissionResourceType._internal(2); + + ///Resource belongs to the device’s orientation and motion. + /// + ///**NOTE**: available only on iOS. + static const DEVICE_ORIENTATION_AND_MOTION = + const PermissionResourceType._internal('deviceOrientationAndMotion'); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; +} + ///Class that represents the response used by the [WebView.onPermissionRequest] event. +class PermissionRequest { + ///The origin of web content which attempt to access the restricted resources. + Uri origin; + + ///List of resources the web content wants to access. + List resources; + + ///The frame that initiates the request in the web view. + FrameInfo? frame; + + PermissionRequest( + {required this.origin, + this.resources = const [], + this.frame}); + + static PermissionRequest? fromMap(Map? map) { + if (map == null) { + return null; + } + + List resources = []; + if (map["resources"] != null) { + (map["resources"].cast() as List) + .forEach((element) { + var resource = PermissionResourceType.fromValue(element); + if (resource != null) { + resources.add(resource); + } + }); + } + + return PermissionRequest( + origin: Uri.parse(map["origin"]), + resources: resources, + frame: FrameInfo.fromMap(map["frame"]?.cast())); + } + + Map toMap() { + return { + "origin": origin.toString(), + "resources": resources.map((e) => e.toValue()).toList(), + "initiatedByFrame": frame?.toMap() + }; + } + + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return toMap().toString(); + } +} + +///Class that represents the response used by the [WebView.onPermissionRequest] event. +class PermissionResponse { + ///Resources granted to be accessed by origin. + List resources; + + ///Indicate the [PermissionResponseAction] to take in response of a permission request. + PermissionResponseAction? action; + + PermissionResponse( + {this.resources = const [], + this.action = PermissionResponseAction.DENY}); + + Map toMap() { + return {"resources": resources, "action": action?.toValue()}; + } + + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return toMap().toString(); + } +} + +///Class that represents the response used by the [WebView.androidOnPermissionRequest] event. +///Use [PermissionResponse] instead. +@Deprecated("Use PermissionResponse instead") class PermissionRequestResponse { ///Resources granted to be accessed by origin. List resources; @@ -7182,39 +7404,63 @@ class SslErrorType { } ///The certificate is not yet valid + /// + ///**NOTE**: available only on Android static const SSL_NOTYETVALID = const SslErrorType._internal(0); ///The certificate has expired + /// + ///**NOTE**: available only on Android static const SSL_EXPIRED = const SslErrorType._internal(1); ///Hostname mismatch + /// + ///**NOTE**: available only on Android static const SSL_IDMISMATCH = const SslErrorType._internal(2); ///The certificate authority is not trusted + /// + ///**NOTE**: available only on Android static const SSL_UNTRUSTED = const SslErrorType._internal(3); ///The date of the certificate is invalid + /// + ///**NOTE**: available only on Android static const SSL_DATE_INVALID = const SslErrorType._internal(4); ///A generic error occurred + /// + ///**NOTE**: available only on Android static const SSL_INVALID = const SslErrorType._internal(5); ///Indicates an invalid setting or result. + /// + ///**NOTE**: available only on iOS static const INVALID = const SslErrorType._internal(0); ///Indicates a user-configured deny; do not proceed. + /// + ///**NOTE**: available only on iOS static const DENY = const SslErrorType._internal(3); ///Indicates the evaluation succeeded and the certificate is implicitly trusted, but user intent was not explicitly specified. + /// + ///**NOTE**: available only on iOS static const UNSPECIFIED = const SslErrorType._internal(4); ///Indicates a trust policy failure which can be overridden by the user. + /// + ///**NOTE**: available only on iOS static const RECOVERABLE_TRUST_FAILURE = const SslErrorType._internal(5); ///Indicates a trust failure which cannot be overridden by the user. + /// + ///**NOTE**: available only on iOS static const FATAL_TRUST_FAILURE = const SslErrorType._internal(6); ///Indicates a failure other than that of trust evaluation. + /// + ///**NOTE**: available only on iOS static const OTHER_ERROR = const SslErrorType._internal(7); bool operator ==(value) => value == _value;