updated onPermissionRequest event for ios, added DebugSettings

This commit is contained in:
Lorenzo Pichilli 2022-05-02 18:59:29 +02:00
parent 17ed6c881a
commit 28455c696a
10 changed files with 112 additions and 53 deletions

View File

@ -7,6 +7,7 @@
- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS`, `forceDarkStrategy` WebView settings - Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS`, `forceDarkStrategy` WebView settings
- Added `onCameraCaptureStateChanged`, `onMicrophoneCaptureStateChanged` WebView events - Added `onCameraCaptureStateChanged`, `onMicrophoneCaptureStateChanged` WebView events
- Added support for `onPermissionRequest` event on iOS 15.0+ - Added support for `onPermissionRequest` event on iOS 15.0+
- Added `debugSettings` static property for WebView and ChromeSafariBrowser
- Updated `getMetaThemeColor` on iOS 15.0+ - Updated `getMetaThemeColor` on iOS 15.0+
- Deprecated `onLoadError` for `onReceivedError`. `onReceivedError` will be called also for subframes. - Deprecated `onLoadError` for `onReceivedError`. `onReceivedError` will be called also for subframes.
- Deprecated `onLoadHttpError` for `onReceivedError`. `onReceivedHttpError` will be called also for subframes. - Deprecated `onLoadHttpError` for `onReceivedError`. `onReceivedHttpError` will be called also for subframes.

View File

@ -96,7 +96,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
TextField( TextField(
decoration: InputDecoration(prefixIcon: Icon(Icons.search)), decoration: InputDecoration(prefixIcon: Icon(Icons.search)),
controller: urlController, controller: urlController,
keyboardType: TextInputType.url, keyboardType: TextInputType.text,
onSubmitted: (value) { onSubmitted: (value) {
var url = Uri.parse(value); var url = Uri.parse(value);
if (url.scheme.isEmpty) { if (url.scheme.isEmpty) {

View File

@ -19,7 +19,7 @@ Future main() async {
// await Permission.microphone.request(); // await Permission.microphone.request();
// await Permission.storage.request(); // await Permission.storage.request();
WebView.debugLogging = false; WebView.debugSettings.maxLogMessageLength = 500;
if (defaultTargetPlatform == TargetPlatform.android) { if (defaultTargetPlatform == TargetPlatform.android) {
await InAppWebViewController.setWebContentsDebuggingEnabled(true); await InAppWebViewController.setWebContentsDebuggingEnabled(true);

View File

@ -669,12 +669,14 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
if let newValue = change?[.newKey] as? Int { if let newValue = change?[.newKey] as? Int {
newState = WKMediaCaptureState.init(rawValue: newValue) newState = WKMediaCaptureState.init(rawValue: newValue)
} }
if oldState != newState {
if keyPath == #keyPath(WKWebView.cameraCaptureState) { if keyPath == #keyPath(WKWebView.cameraCaptureState) {
onCameraCaptureStateChanged(oldState: oldState, newState: newState) onCameraCaptureStateChanged(oldState: oldState, newState: newState)
} else { } else {
onMicrophoneCaptureStateChanged(oldState: oldState, newState: newState) onMicrophoneCaptureStateChanged(oldState: oldState, newState: newState)
} }
} }
}
// else if keyPath == #keyPath(WKWebView.fullscreenState) { // else if keyPath == #keyPath(WKWebView.fullscreenState) {
// if fullscreenState == .enteringFullscreen { // if fullscreenState == .enteringFullscreen {
// onEnterFullscreen() // onEnterFullscreen()
@ -1552,10 +1554,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
onPermissionRequest(request: permissionRequest, result: {(result) -> Void in onPermissionRequest(request: permissionRequest, result: {(result) -> Void in
if result is FlutterError { if result is FlutterError {
print((result as! FlutterError).message ?? "") print((result as! FlutterError).message ?? "")
decisionHandler(.deny) decisionHandler(.prompt)
} }
else if (result as? NSObject) == FlutterMethodNotImplemented { else if (result as? NSObject) == FlutterMethodNotImplemented {
decisionHandler(.deny) decisionHandler(.prompt)
} }
else { else {
var response: [String: Any] var response: [String: Any]
@ -1575,7 +1577,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
} }
return; return;
} }
decisionHandler(.deny) decisionHandler(.prompt)
} }
}) })
} }
@ -1593,10 +1595,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
onPermissionRequest(request: permissionRequest, result: {(result) -> Void in onPermissionRequest(request: permissionRequest, result: {(result) -> Void in
if result is FlutterError { if result is FlutterError {
print((result as! FlutterError).message ?? "") print((result as! FlutterError).message ?? "")
decisionHandler(.deny) decisionHandler(.prompt)
} }
else if (result as? NSObject) == FlutterMethodNotImplemented { else if (result as? NSObject) == FlutterMethodNotImplemented {
decisionHandler(.deny) decisionHandler(.prompt)
} }
else { else {
var response: [String: Any] var response: [String: Any]
@ -1616,7 +1618,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
} }
return; return;
} }
decisionHandler(.deny) decisionHandler(.prompt)
} }
}) })
} }

View File

@ -6,6 +6,7 @@ import 'dart:developer' as developer;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import '../util.dart'; import '../util.dart';
import '../debug_settings.dart';
import 'chrome_safari_browser_settings.dart'; import 'chrome_safari_browser_settings.dart';
@ -44,12 +45,8 @@ class ChromeSafariBrowserNotOpenedException implements Exception {
///- Android native WebView ///- Android native WebView
///- iOS ///- iOS
class ChromeSafariBrowser { class ChromeSafariBrowser {
///Enables [ChromeSafariBrowser] debug logging info. ///Debug settings.
/// static DebugSettings debugSettings = DebugSettings();
///The default value is the same value of [kDebugMode],
///so it is enabled by default when the application is compiled in debug mode
///and disabled when it is not.
static bool debugLogging = kDebugMode;
///View ID used internally. ///View ID used internally.
late final String id; late final String id;
@ -70,12 +67,22 @@ class ChromeSafariBrowser {
} }
_debugLog(String method, dynamic args) { _debugLog(String method, dynamic args) {
if (ChromeSafariBrowser.debugLogging) { if (ChromeSafariBrowser.debugSettings.enabled) {
String message = for (var regExp in ChromeSafariBrowser.debugSettings.excludeFilter) {
"ChromeSafariBrowser ID " + id + " calling \"" + if (regExp.hasMatch(method)) return;
method.toString() + "\" using " + args.toString(); }
developer.log(message, var maxLogMessageLength =
name: this.runtimeType.toString()); ChromeSafariBrowser.debugSettings.maxLogMessageLength;
String message = "ChromeSafariBrowser ID " +
id +
" calling \"" +
method.toString() +
"\" using " +
args.toString();
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
message = message.substring(0, maxLogMessageLength) + "...";
}
developer.log(message, name: this.runtimeType.toString());
} }
} }

View File

@ -0,0 +1,28 @@
import 'package:flutter/foundation.dart';
import 'in_app_webview/webview.dart';
import 'chrome_safari_browser/chrome_safari_browser.dart';
///Class that represents the debug settings used by [WebView] and [ChromeSafariBrowser].
class DebugSettings {
///Enables debug logging info.
///
///The default value is the same value of [kDebugMode],
///so it is enabled by default when the application is compiled in debug mode
///and disabled when it is not.
bool enabled;
///Filters used to exclude some logs from logging.
List<RegExp> excludeFilter;
///Max length of the log message.
///Set to `-1` to indicate that the log message needs to display the full content.
///
///The default value is `-1`.
int maxLogMessageLength;
DebugSettings({
this.enabled = kDebugMode,
this.excludeFilter = const [],
this.maxLogMessageLength = -1
});
}

View File

@ -804,10 +804,13 @@ class InAppBrowser {
/// ///
///[resources] represents the array of resources the web content wants to access. ///[resources] represents the array of resources the web content wants to access.
/// ///
///**NOTE**: available only on Android 23+. ///**NOTE for Android**: available only on Android 23+.
///
///**NOTE for iOS**: available only on iOS 15.0+. The default [PermissionResponse.action] is [PermissionResponseAction.PROMPT].
/// ///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest))) ///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest)))
///- iOS
Future<PermissionResponse?>? onPermissionRequest( Future<PermissionResponse?>? onPermissionRequest(
PermissionRequest permissionRequest) {} PermissionRequest permissionRequest) {}

View File

@ -104,7 +104,11 @@ class InAppWebViewController {
} }
_debugLog(String method, dynamic args) { _debugLog(String method, dynamic args) {
if (WebView.debugLogging) { if (WebView.debugSettings.enabled) {
for (var regExp in WebView.debugSettings.excludeFilter) {
if (regExp.hasMatch(method)) return;
}
var maxLogMessageLength = WebView.debugSettings.maxLogMessageLength;
String viewId = (getViewId() ?? _inAppBrowser?.id).toString(); String viewId = (getViewId() ?? _inAppBrowser?.id).toString();
String message = (_inAppBrowser == null ? "WebView" : "InAppBrowser") + String message = (_inAppBrowser == null ? "WebView" : "InAppBrowser") +
" ID " + " ID " +
@ -113,12 +117,15 @@ class InAppWebViewController {
method.toString() + method.toString() +
"\" using " + "\" using " +
args.toString(); args.toString();
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
message = message.substring(0, maxLogMessageLength) + "...";
}
developer.log(message, name: this.runtimeType.toString()); developer.log(message, name: this.runtimeType.toString());
} }
} }
Future<dynamic> handleMethod(MethodCall call) async { Future<dynamic> handleMethod(MethodCall call) async {
if (WebView.debugLogging && call.method != "onCallJsHandler") { if (WebView.debugSettings.enabled && call.method != "onCallJsHandler") {
_debugLog(call.method, call.arguments); _debugLog(call.method, call.arguments);
} }
@ -163,15 +170,15 @@ class InAppWebViewController {
_webview!.onReceivedError!(this, request, error); _webview!.onReceivedError!(this, request, error);
else if (isForMainFrame) { else if (isForMainFrame) {
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
_webview!.onLoadError!(this, request.url, error.type.toNativeValue(), _webview!.onLoadError!(this, request.url,
error.description); error.type.toNativeValue(), error.description);
} }
} else { } else {
if (isForMainFrame) { if (isForMainFrame) {
_inAppBrowser! _inAppBrowser!
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
.onLoadError( .onLoadError(request.url, error.type.toNativeValue(),
request.url, error.type.toNativeValue(), error.description); error.description);
} }
_inAppBrowser!.onReceivedError(request, error); _inAppBrowser!.onReceivedError(request, error);
} }
@ -1091,10 +1098,13 @@ class InAppWebViewController {
} }
break; break;
case "onCameraCaptureStateChanged": case "onCameraCaptureStateChanged":
if ((_webview != null && _webview!.onCameraCaptureStateChanged != null) || if ((_webview != null &&
_webview!.onCameraCaptureStateChanged != null) ||
_inAppBrowser != null) { _inAppBrowser != null) {
var oldState = MediaCaptureState.fromValue(call.arguments["oldState"]); var oldState =
var newState = MediaCaptureState.fromValue(call.arguments["newState"]); MediaCaptureState.fromValue(call.arguments["oldState"]);
var newState =
MediaCaptureState.fromValue(call.arguments["newState"]);
if (_webview != null && _webview!.onCameraCaptureStateChanged != null) if (_webview != null && _webview!.onCameraCaptureStateChanged != null)
_webview!.onCameraCaptureStateChanged!(this, oldState, newState); _webview!.onCameraCaptureStateChanged!(this, oldState, newState);
@ -1103,13 +1113,18 @@ class InAppWebViewController {
} }
break; break;
case "onMicrophoneCaptureStateChanged": case "onMicrophoneCaptureStateChanged":
if ((_webview != null && _webview!.onMicrophoneCaptureStateChanged != null) || if ((_webview != null &&
_webview!.onMicrophoneCaptureStateChanged != null) ||
_inAppBrowser != null) { _inAppBrowser != null) {
var oldState = MediaCaptureState.fromValue(call.arguments["oldState"]); var oldState =
var newState = MediaCaptureState.fromValue(call.arguments["newState"]); MediaCaptureState.fromValue(call.arguments["oldState"]);
var newState =
MediaCaptureState.fromValue(call.arguments["newState"]);
if (_webview != null && _webview!.onMicrophoneCaptureStateChanged != null) if (_webview != null &&
_webview!.onMicrophoneCaptureStateChanged!(this, oldState, newState); _webview!.onMicrophoneCaptureStateChanged != null)
_webview!.onMicrophoneCaptureStateChanged!(
this, oldState, newState);
else else
_inAppBrowser!.onMicrophoneCaptureStateChanged(oldState, newState); _inAppBrowser!.onMicrophoneCaptureStateChanged(oldState, newState);
} }
@ -3286,8 +3301,7 @@ class InAppWebViewController {
/// ///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- iOS ([Official API - WKWebView.setCameraCaptureState](https://developer.apple.com/documentation/webkit/wkwebview/3763097-setcameracapturestate)). ///- iOS ([Official API - WKWebView.setCameraCaptureState](https://developer.apple.com/documentation/webkit/wkwebview/3763097-setcameracapturestate)).
Future<void> setCameraCaptureState( Future<void> setCameraCaptureState({required MediaCaptureState state}) async {
{required MediaCaptureState state}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('state', () => state.toValue()); args.putIfAbsent('state', () => state.toValue());
await _channel.invokeMethod('setCameraCaptureState', args); await _channel.invokeMethod('setCameraCaptureState', args);

View File

@ -1,8 +1,6 @@
import 'dart:collection'; import 'dart:collection';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import '../pull_to_refresh/pull_to_refresh_controller.dart'; import '../pull_to_refresh/pull_to_refresh_controller.dart';
import '../context_menu.dart'; import '../context_menu.dart';
@ -12,14 +10,16 @@ import 'in_app_webview_controller.dart';
import 'in_app_webview_settings.dart'; import 'in_app_webview_settings.dart';
import 'headless_in_app_webview.dart'; import 'headless_in_app_webview.dart';
import '../debug_settings.dart';
///Abstract class that represents a WebView. Used by [InAppWebView], [HeadlessInAppWebView] and the WebView of [InAppBrowser]. ///Abstract class that represents a WebView. Used by [InAppWebView], [HeadlessInAppWebView] and the WebView of [InAppBrowser].
abstract class WebView { abstract class WebView {
///Enables [WebView] debug logging info. ///Debug settings used by [InAppWebView], [HeadlessInAppWebView] and [InAppBrowser].
/// ///The default value excludes the [WebView.onScrollChanged] and [WebView.onOverScrolled] events.
///The default value is the same value of [kDebugMode], static DebugSettings debugSettings = DebugSettings(excludeFilter: [
///so it is enabled by default when the application is compiled in debug mode RegExp(r"onScrollChanged"),
///and disabled when it is not. RegExp(r"onOverScrolled")
static bool debugLogging = kDebugMode; ]);
///The window id of a [CreateWindowAction.windowId]. ///The window id of a [CreateWindowAction.windowId].
final int? windowId; final int? windowId;
@ -567,10 +567,13 @@ abstract class WebView {
/// ///
///[resources] represents the array of resources the web content wants to access. ///[resources] represents the array of resources the web content wants to access.
/// ///
///**NOTE**: available only on Android 23+. ///**NOTE for Android**: available only on Android 23+.
///
///**NOTE for iOS**: available only on iOS 15.0+. The default [PermissionResponse.action] is [PermissionResponseAction.PROMPT].
/// ///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest))) ///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest)))
///- iOS
final Future<PermissionResponse?> Function(InAppWebViewController controller, final Future<PermissionResponse?> Function(InAppWebViewController controller,
PermissionRequest permissionRequest)? onPermissionRequest; PermissionRequest permissionRequest)? onPermissionRequest;

View File

@ -14,3 +14,4 @@ export 'http_auth_credentials_database.dart';
export 'context_menu.dart'; export 'context_menu.dart';
export 'pull_to_refresh/main.dart'; export 'pull_to_refresh/main.dart';
export 'web_message/main.dart'; export 'web_message/main.dart';
export 'debug_settings.dart';