diff --git a/CHANGELOG.md b/CHANGELOG.md index 595351c2..4a4b9411 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ - Deprecated old classes/properties/methods to make them eventually compatible with other Platforms and WebView engines. - Added Web support -- Added `pauseAllMediaPlayback`, `setAllMediaPlaybackSuspended`, `closeAllMediaPresentations`, `requestMediaPlaybackState`, `isInFullscreen` WebView controller methods +- Added `pauseAllMediaPlayback`, `setAllMediaPlaybackSuspended`, `closeAllMediaPresentations`, `requestMediaPlaybackState`, `isInFullscreen`, `getCameraCaptureState`, `setCameraCaptureState`, `getMicrophoneCaptureState`, `setMicrophoneCaptureState` WebView controller methods - Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS` WebView settings - Added support for `onPermissionRequest` event on iOS 15.0+ - Updated `getMetaThemeColor` on iOS 15.0+ diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 84f407c8..afd058be 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -114,7 +114,7 @@ class _InAppWebViewExampleScreenState extends State { InAppWebView( key: webViewKey, initialUrlRequest: - URLRequest(url: Uri.parse('https://flutter.dev')), + URLRequest(url: Uri.parse('https://www.onlinemictest.com/')), // initialUrlRequest: // URLRequest(url: Uri.parse(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html')), // initialFile: "assets/index.html", diff --git a/ios/Classes/InAppWebViewMethodHandler.swift b/ios/Classes/InAppWebViewMethodHandler.swift index e504060b..c267e81d 100644 --- a/ios/Classes/InAppWebViewMethodHandler.swift +++ b/ios/Classes/InAppWebViewMethodHandler.swift @@ -603,6 +603,40 @@ public class InAppWebViewMethodHandler: FlutterMethodCallDelegate { result(false) } break + case "getCameraCaptureState": + if let webView = webView, #available(iOS 15.0, *) { + result(webView.cameraCaptureState.rawValue) + } else { + result(nil) + } + break + case "setCameraCaptureState": + if let webView = webView, #available(iOS 15.0, *) { + let state = WKMediaCaptureState.init(rawValue: arguments!["state"] as! Int) ?? WKMediaCaptureState.none + webView.setCameraCaptureState(state) { + result(true) + } + } else { + result(false) + } + break + case "getMicrophoneCaptureState": + if let webView = webView, #available(iOS 15.0, *) { + result(webView.microphoneCaptureState.rawValue) + } else { + result(nil) + } + break + case "setMicrophoneCaptureState": + if let webView = webView, #available(iOS 15.0, *) { + let state = WKMediaCaptureState.init(rawValue: arguments!["state"] as! Int) ?? WKMediaCaptureState.none + webView.setMicrophoneCaptureState(state) { + result(true) + } + } else { + result(false) + } + break default: result(FlutterMethodNotImplemented) break 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 ffdd9fcb..9f6795db 100644 --- a/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/lib/src/in_app_webview/in_app_webview_controller.dart @@ -3244,6 +3244,56 @@ class InAppWebViewController { return await _channel.invokeMethod('isInFullscreen', args); } + ///Returns a [MediaCaptureState] that indicates whether the webpage is using the camera to capture images or video. + /// + ///**NOTE for iOS**: available on iOS 15.0+. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - WKWebView.cameraCaptureState](https://developer.apple.com/documentation/webkit/wkwebview/3763093-cameracapturestate)). + Future getCameraCaptureState() async { + Map args = {}; + return MediaCaptureState.fromValue( + await _channel.invokeMethod('getCameraCaptureState', args)); + } + + ///Changes whether the webpage is using the camera to capture images or video. + /// + ///**NOTE for iOS**: available on iOS 15.0+. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - WKWebView.setCameraCaptureState](https://developer.apple.com/documentation/webkit/wkwebview/3763097-setcameracapturestate)). + Future setCameraCaptureState( + {required MediaCaptureState state}) async { + Map args = {}; + args.putIfAbsent('state', () => state.toValue()); + await _channel.invokeMethod('setCameraCaptureState', args); + } + + ///Returns a [MediaCaptureState] that indicates whether the webpage is using the microphone to capture audio. + /// + ///**NOTE for iOS**: available on iOS 15.0+. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - WKWebView.microphoneCaptureState](https://developer.apple.com/documentation/webkit/wkwebview/3763096-microphonecapturestate)). + Future getMicrophoneCaptureState() async { + Map args = {}; + return MediaCaptureState.fromValue( + await _channel.invokeMethod('getMicrophoneCaptureState', args)); + } + + ///Changes whether the webpage is using the microphone to capture audio. + /// + ///**NOTE for iOS**: available on iOS 15.0+. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - WKWebView.setMicrophoneCaptureState](https://developer.apple.com/documentation/webkit/wkwebview/3763098-setmicrophonecapturestate)). + Future setMicrophoneCaptureState( + {required MediaCaptureState state}) async { + Map args = {}; + args.putIfAbsent('state', () => state.toValue()); + await _channel.invokeMethod('setMicrophoneCaptureState', args); + } + ///Returns the iframe `id` attribute used on the Web platform. /// ///**Supported Platforms/Implementations**: diff --git a/lib/src/types/main.dart b/lib/src/types/main.dart index 59074a9e..847ef700 100644 --- a/lib/src/types/main.dart +++ b/lib/src/types/main.dart @@ -143,4 +143,5 @@ export 'webview_package_info.dart'; export 'webview_render_process_action.dart'; export 'window_features.dart'; export 'web_resource_error.dart'; -export 'web_resource_error_type.dart'; \ No newline at end of file +export 'web_resource_error_type.dart'; +export 'media_capture_state.dart'; \ No newline at end of file diff --git a/lib/src/types/media_capture_state.dart b/lib/src/types/media_capture_state.dart new file mode 100644 index 00000000..4d331b04 --- /dev/null +++ b/lib/src/types/media_capture_state.dart @@ -0,0 +1,56 @@ +///Class that describes whether a media device, like a camera or microphone, is currently capturing audio or video. +class MediaCaptureState { + final int _value; + + const MediaCaptureState._internal(this._value); + + ///Set of all values of [MediaCaptureState]. + static final Set values = [ + MediaCaptureState.NONE, + MediaCaptureState.ACTIVE, + MediaCaptureState.MUTED, + ].toSet(); + + ///Gets a possible [MediaCaptureState] instance from an [int] value. + static MediaCaptureState? fromValue(int? value) { + if (value != null) { + try { + return MediaCaptureState.values + .firstWhere((element) => element.toValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + ///Gets [int] value. + int toValue() => _value; + + @override + String toString() { + switch (_value) { + case 1: + return "ACTIVE"; + case 2: + return "MUTED"; + case 0: + default: + return "NONE"; + } + } + + ///The media device is off. + static const NONE = const MediaCaptureState._internal(0); + + ///The media device is actively capturing audio or video. + static const ACTIVE = const MediaCaptureState._internal(1); + + ///The media device is muted, and not actively capturing audio or video. + static const MUTED = const MediaCaptureState._internal(2); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; +} \ No newline at end of file diff --git a/lib/src/types/permission_request.dart b/lib/src/types/permission_request.dart index 65a94176..ed669db8 100644 --- a/lib/src/types/permission_request.dart +++ b/lib/src/types/permission_request.dart @@ -46,7 +46,7 @@ class PermissionRequest { Map toMap() { return { "origin": origin.toString(), - "resources": resources.map((e) => e.toValue()).toList(), + "resources": resources.map((e) => e.toNativeValue()).toList(), "frame": frame?.toMap() }; } diff --git a/lib/src/types/permission_response.dart b/lib/src/types/permission_response.dart index 7d1c79b6..e5e7600d 100644 --- a/lib/src/types/permission_response.dart +++ b/lib/src/types/permission_response.dart @@ -18,7 +18,7 @@ class PermissionResponse { ///Converts instance to a map. Map toMap() { return { - "resources": resources.map((e) => e.toValue()).toList(), + "resources": resources.map((e) => e.toNativeValue()).toList(), "action": action?.toValue() }; } diff --git a/lib/src/types/ssl_error.dart b/lib/src/types/ssl_error.dart index 66f77e5f..0df7a245 100644 --- a/lib/src/types/ssl_error.dart +++ b/lib/src/types/ssl_error.dart @@ -47,11 +47,11 @@ class SslError { Map toMap() { return { // ignore: deprecated_member_use_from_same_package - "androidError": code?.toValue() ?? androidError?.toValue(), + "androidError": code?.toNativeValue() ?? androidError?.toValue(), // ignore: deprecated_member_use_from_same_package - "iosError": code?.toValue() ?? iosError?.toValue(), + "iosError": code?.toNativeValue() ?? iosError?.toValue(), // ignore: deprecated_member_use_from_same_package - "code": code?.toValue() ?? androidError?.toValue() ?? iosError?.toValue(), + "code": code?.toNativeValue() ?? androidError?.toValue() ?? iosError?.toValue(), "message": message, }; }