updated README, fixed swift Util getUrlAsset and getAbsPathAsset, fixed NSColor, added WKWebView runOpenPanelWith event

This commit is contained in:
Lorenzo Pichilli 2022-10-19 13:14:53 +02:00
parent 69fb76d10c
commit 78abd773b1
17 changed files with 163 additions and 185 deletions

View File

@ -50,6 +50,7 @@ Send a submission request to the [Submit App](https://inappwebview.dev/submit-ap
- Flutter: ">=2.5.0"
- Android: `minSdkVersion 19` and add support for `androidx` (see [AndroidX Migration](https://flutter.dev/docs/development/androidx-migration) to migrate an existing app)
- iOS 9.0+: `--ios-language swift`, Xcode version `>= 14`
- MacOS 10.11+: `--ios-language swift`, Xcode version `>= 14`
## Installation

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import '../in_app_webview/take_screenshot.dart';
import 'take_screenshot.dart';
import 'custom_size.dart';
import 'run_and_dispose.dart';
import 'set_get_settings.dart';

View File

@ -19,21 +19,38 @@ void loadAssetFile(InAppLocalhostServer localhostServer) {
final Completer<InAppWebViewController> controllerCompleter =
Completer<InAppWebViewController>();
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: InAppWebView(
key: GlobalKey(),
initialUrlRequest: URLRequest(
url: Uri.parse('http://localhost:8080/test_assets/index.html')),
onWebViewCreated: (controller) {
controllerCompleter.complete(controller);
},
),
),
var headlessWebView = new HeadlessInAppWebView(
initialUrlRequest: URLRequest(
url: Uri.parse('http://localhost:8080/test_assets/index.html')),
onWebViewCreated: (controller) {
controllerCompleter.complete(controller);
},
);
if (defaultTargetPlatform == TargetPlatform.macOS) {
await headlessWebView.run();
} else {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: InAppWebView(
key: GlobalKey(),
initialUrlRequest: URLRequest(
url: Uri.parse('http://localhost:8080/test_assets/index.html')),
onWebViewCreated: (controller) {
controllerCompleter.complete(controller);
},
),
),
);
}
final InAppWebViewController controller = await controllerCompleter.future;
final String? currentUrl = (await controller.getUrl())?.toString();
expect(currentUrl, 'http://localhost:8080/test_assets/index.html');
if (defaultTargetPlatform == TargetPlatform.macOS) {
await headlessWebView.dispose();
}
}, skip: shouldSkip);
}

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_test/flutter_test.dart';
@ -20,23 +21,27 @@ void takeScreenshot() {
Completer<InAppWebViewController>();
final Completer<void> pageLoaded = Completer<void>();
var headlessWebView = new HeadlessInAppWebView(
initialUrlRequest: URLRequest(url: TEST_CROSS_PLATFORM_URL_1),
onWebViewCreated: (controller) {
controllerCompleter.complete(controller);
},
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: InAppWebView(
key: GlobalKey(),
initialUrlRequest: URLRequest(url: TEST_CROSS_PLATFORM_URL_1),
onWebViewCreated: (controller) {
controllerCompleter.complete(controller);
},
onLoadStop: (controller, url) {
pageLoaded.complete();
},
),
),
);
headlessWebView.onLoadStop = (controller, url) async {
pageLoaded.complete();
};
await headlessWebView.run();
expect(headlessWebView.isRunning(), true);
final InAppWebViewController controller = await controllerCompleter.future;
await pageLoaded.future;
await Future.delayed(Duration(seconds: 1));
await tester.pump();
var screenshotConfiguration = ScreenshotConfiguration(
compressFormat: CompressFormat.JPEG,

View File

@ -33,13 +33,13 @@ void main() {
FindInteractionController.debugLoggingSettings.usePrint = true;
FindInteractionController.debugLoggingSettings.maxLogMessageLength = 7000;
// in_app_webview_tests.main();
// find_interaction_controller_tests.main();
in_app_webview_tests.main();
find_interaction_controller_tests.main();
service_worker_controller_tests.main();
proxy_controller_tests.main();
headless_in_app_webview_tests.main();
cookie_manager_tests.main();
in_app_browser_tests.main();
// chrome_safari_browser_tests.main();
chrome_safari_browser_tests.main();
in_app_localhost_server_tests.main();
}

View File

@ -3,11 +3,12 @@
export "FLUTTER_ROOT=/Users/lorenzopichilli/fvm/versions/2.10.4"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_TARGET=integration_test/webview_flutter_test.dart"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.dart_tool/package_config.json"
export "PACKAGE_CONFIG=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/.dart_tool/package_config.json"

View File

@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:collection';
import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@ -73,29 +72,12 @@ class ChromeSafariBrowser {
}
_debugLog(String method, dynamic args) {
if (ChromeSafariBrowser.debugLoggingSettings.enabled) {
for (var regExp
in ChromeSafariBrowser.debugLoggingSettings.excludeFilter) {
if (regExp.hasMatch(method)) return;
}
var maxLogMessageLength =
ChromeSafariBrowser.debugLoggingSettings.maxLogMessageLength;
String message =
"(${defaultTargetPlatform.name}) ChromeSafariBrowser ID " +
id +
" calling \"" +
method.toString() +
"\" using " +
args.toString();
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
message = message.substring(0, maxLogMessageLength) + "...";
}
if (!ChromeSafariBrowser.debugLoggingSettings.usePrint) {
developer.log(message, name: this.runtimeType.toString());
} else {
print("[${this.runtimeType.toString()}] $message");
}
}
debugLog(
className: this.runtimeType.toString(),
id: id,
debugLoggingSettings: ChromeSafariBrowser.debugLoggingSettings,
method: method,
args: args);
}
Future<dynamic> _handleMethod(MethodCall call) async {

View File

@ -1,10 +1,8 @@
import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import '../in_app_webview/in_app_webview_settings.dart';
import '../debug_logging_settings.dart';
import '../types/main.dart';
import '../util.dart';
///**Supported Platforms/Implementations**:
///- Android native WebView
@ -54,28 +52,11 @@ class FindInteractionController {
}
_debugLog(String method, dynamic args) {
if (FindInteractionController.debugLoggingSettings.enabled) {
for (var regExp
in FindInteractionController.debugLoggingSettings.excludeFilter) {
if (regExp.hasMatch(method)) return;
}
var maxLogMessageLength =
FindInteractionController.debugLoggingSettings.maxLogMessageLength;
String message =
"(${defaultTargetPlatform.name}) FindInteractionController " +
" calling \"" +
method.toString() +
"\" using " +
args.toString();
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
message = message.substring(0, maxLogMessageLength) + "...";
}
if (!FindInteractionController.debugLoggingSettings.usePrint) {
developer.log(message, name: this.runtimeType.toString());
} else {
print("[${this.runtimeType.toString()}] $message");
}
}
debugLog(
className: this.runtimeType.toString(),
debugLoggingSettings: FindInteractionController.debugLoggingSettings,
method: method,
args: args);
}
Future<dynamic> _handleMethod(MethodCall call) async {

View File

@ -1,8 +1,6 @@
import 'dart:async';
import 'dart:collection';
import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import '../context_menu.dart';
@ -73,12 +71,13 @@ class InAppBrowser {
static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_inappbrowser');
InAppWebViewController? _webViewController;
late final InAppWebViewController _webViewController;
///WebView Controller that can be used to access the [InAppWebViewController] API.
///When [onExit] is fired, this will be `null` and cannot be used anymore.
InAppWebViewController? get webViewController {
return _webViewController;
}
return _isOpened ? _webViewController : null;
}
///The window id of a [CreateWindowAction.windowId].
final int? windowId;
@ -108,27 +107,12 @@ class InAppBrowser {
}
_debugLog(String method, dynamic args) {
if (InAppBrowser.debugLoggingSettings.enabled) {
for (var regExp in InAppBrowser.debugLoggingSettings.excludeFilter) {
if (regExp.hasMatch(method)) return;
}
var maxLogMessageLength =
InAppBrowser.debugLoggingSettings.maxLogMessageLength;
String message = "(${defaultTargetPlatform.name}) InAppBrowser ID " +
id +
" calling \"" +
method.toString() +
"\" using " +
args.toString();
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
message = message.substring(0, maxLogMessageLength) + "...";
}
if (!InAppBrowser.debugLoggingSettings.usePrint) {
developer.log(message, name: this.runtimeType.toString());
} else {
print("[${this.runtimeType.toString()}] $message");
}
}
debugLog(
className: this.runtimeType.toString(),
id: id,
debugLoggingSettings: InAppBrowser.debugLoggingSettings,
method: method,
args: args);
}
Future<dynamic> _handleMethod(MethodCall call) async {
@ -143,11 +127,10 @@ class InAppBrowser {
case "onExit":
_debugLog(call.method, call.arguments);
this._isOpened = false;
this._webViewController = null;
onExit();
break;
default:
return _webViewController?.handleMethod(call);
return _webViewController.handleMethod(call);
}
}

View File

@ -111,30 +111,13 @@ class InAppWebViewController {
}
_debugLog(String method, dynamic args) {
if (WebView.debugLoggingSettings.enabled) {
for (var regExp in WebView.debugLoggingSettings.excludeFilter) {
if (regExp.hasMatch(method)) return;
}
var maxLogMessageLength =
WebView.debugLoggingSettings.maxLogMessageLength;
String viewId = (getViewId() ?? _inAppBrowser?.id).toString();
String message = "(${defaultTargetPlatform.name}) " +
(_inAppBrowser == null ? "WebView" : "InAppBrowser") +
" ID " +
viewId +
" calling \"" +
method.toString() +
"\" using " +
args.toString();
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
message = message.substring(0, maxLogMessageLength) + "...";
}
if (!WebView.debugLoggingSettings.usePrint) {
developer.log(message, name: this.runtimeType.toString());
} else {
print("[${this.runtimeType.toString()}] $message");
}
}
debugLog(
className: this.runtimeType.toString(),
name: _inAppBrowser == null ? "WebView" : "InAppBrowser",
id: (getViewId() ?? _inAppBrowser?.id).toString(),
debugLoggingSettings: WebView.debugLoggingSettings,
method: method,
args: args);
}
Future<dynamic> handleMethod(MethodCall call) async {

View File

@ -1,6 +1,3 @@
import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import '../in_app_webview/webview.dart';
import '../in_app_browser/in_app_browser.dart';
@ -59,28 +56,11 @@ class PullToRefreshController {
}
_debugLog(String method, dynamic args) {
if (PullToRefreshController.debugLoggingSettings.enabled) {
for (var regExp
in PullToRefreshController.debugLoggingSettings.excludeFilter) {
if (regExp.hasMatch(method)) return;
}
var maxLogMessageLength =
PullToRefreshController.debugLoggingSettings.maxLogMessageLength;
String message =
"(${defaultTargetPlatform.name}) PullToRefreshController " +
" calling \"" +
method.toString() +
"\" using " +
args.toString();
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
message = message.substring(0, maxLogMessageLength) + "...";
}
if (!PullToRefreshController.debugLoggingSettings.usePrint) {
developer.log(message, name: this.runtimeType.toString());
} else {
print("[${this.runtimeType.toString()}] $message");
}
}
debugLog(
className: this.runtimeType.toString(),
debugLoggingSettings: PullToRefreshController.debugLoggingSettings,
method: method,
args: args);
}
Future<dynamic> _handleMethod(MethodCall call) async {

View File

@ -1,8 +1,11 @@
import 'dart:math';
import 'dart:typed_data';
import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'debug_logging_settings.dart';
class IdGenerator {
static int _count = 0;
@ -520,3 +523,33 @@ extension MapEdgeInsets on EdgeInsets {
return {'top': top, 'right': right, 'bottom': bottom, 'left': left};
}
}
void debugLog(
{required DebugLoggingSettings debugLoggingSettings,
required String className,
required String method,
String? name,
String? id,
dynamic args}) {
if (debugLoggingSettings.enabled) {
for (var regExp in debugLoggingSettings.excludeFilter) {
if (regExp.hasMatch(method)) return;
}
var maxLogMessageLength = debugLoggingSettings.maxLogMessageLength;
String message =
"(${kIsWeb ? 'Web' : defaultTargetPlatform.name}) ${name ?? className}" +
(id != null ? ' ID $id' : '') +
' calling "' +
method.toString() +
'" using ' +
args.toString();
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
message = message.substring(0, maxLogMessageLength) + '...';
}
if (!debugLoggingSettings.usePrint) {
developer.log(message, name: className);
} else {
print('[${className}] $message');
}
}
}

View File

@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@ -116,29 +115,12 @@ class WebAuthenticationSession implements Disposable {
}
_debugLog(String method, dynamic args) {
if (WebAuthenticationSession.debugLoggingSettings.enabled) {
for (var regExp
in WebAuthenticationSession.debugLoggingSettings.excludeFilter) {
if (regExp.hasMatch(method)) return;
}
var maxLogMessageLength =
WebAuthenticationSession.debugLoggingSettings.maxLogMessageLength;
String message =
"(${defaultTargetPlatform.name}) WebAuthenticationSession ID " +
id +
" calling \"" +
method.toString() +
"\" using " +
args.toString();
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
message = message.substring(0, maxLogMessageLength) + "...";
}
if (!WebAuthenticationSession.debugLoggingSettings.usePrint) {
developer.log(message, name: this.runtimeType.toString());
} else {
print("[${this.runtimeType.toString()}] $message");
}
}
debugLog(
className: this.runtimeType.toString(),
debugLoggingSettings: WebAuthenticationSession.debugLoggingSettings,
id: id,
method: method,
args: args);
}
Future<dynamic> _handleMethod(MethodCall call) async {

View File

@ -50,7 +50,6 @@ public class InAppBrowserManager: ChannelDelegate {
let encoding = arguments["encoding"] as? String
let baseUrl = arguments["baseUrl"] as? String
let settings = arguments["settings"] as! [String: Any?]
let contextMenu = arguments["contextMenu"] as! [String: Any]
let windowId = arguments["windowId"] as? Int64
let initialUserScripts = arguments["initialUserScripts"] as? [[String: Any]]

View File

@ -47,6 +47,8 @@ public class InAppWebView: WKWebView, WKUIDelegate,
var callAsyncJavaScriptBelowIOS14Results: [String:((Any?) -> Void)] = [:]
var currentOpenPanel: NSOpenPanel?
init(id: Any?, registrar: FlutterPluginRegistrar?, frame: CGRect, configuration: WKWebViewConfiguration,
userScripts: [UserScript] = []) {
super.init(frame: frame, configuration: configuration)
@ -1527,6 +1529,30 @@ public class InAppWebView: WKWebView, WKUIDelegate,
return identityAndTrust;
}
@available(macOS 10.12, *)
public func webView(
_ webView: WKWebView,
runOpenPanelWith parameters: WKOpenPanelParameters,
initiatedByFrame frame: WKFrameInfo,
completionHandler: @escaping ([URL]?) -> Void
) {
let openPanel = NSOpenPanel()
currentOpenPanel = openPanel
openPanel.canChooseFiles = true
if #available(macOS 10.13.4, *) {
openPanel.canChooseDirectories = parameters.allowsDirectories
}
openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection
openPanel.begin { (result) in
if result == .OK {
completionHandler(openPanel.urls)
} else {
completionHandler([])
}
self.currentOpenPanel = nil
}
}
func createAlertDialog(message: String?, responseMessage: String?, confirmButtonTitle: String?, completionHandler: @escaping () -> Void) {
let title = responseMessage != nil && !responseMessage!.isEmpty ? responseMessage : message
let okButton = confirmButtonTitle != nil && !confirmButtonTitle!.isEmpty ? confirmButtonTitle : NSLocalizedString("Ok", comment: "")
@ -2425,6 +2451,9 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
public func dispose() {
channelDelegate?.dispose()
channelDelegate = nil
currentOpenPanel?.cancel(self)
currentOpenPanel?.close()
currentOpenPanel = nil
printJobCompletionHandler = nil
removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
removeObserver(self, forKeyPath: #keyPath(WKWebView.url))

View File

@ -30,10 +30,10 @@ extension NSColor {
guard let rgbColor = usingColorSpace(.sRGB) else {
return "#FFFFFF"
}
var red: CGFloat = rgbColor.redComponent
var green: CGFloat = rgbColor.greenComponent
var blue: CGFloat = rgbColor.blueComponent
var alpha: CGFloat = rgbColor.alphaComponent
let red: CGFloat = rgbColor.redComponent
let green: CGFloat = rgbColor.greenComponent
let blue: CGFloat = rgbColor.blueComponent
let alpha: CGFloat = rgbColor.alphaComponent
if alpha == 1.0 {
return String(

View File

@ -12,6 +12,7 @@ import FlutterMacOS
public class Util {
public static func getUrlAsset(assetFilePath: String) throws -> URL {
// let key = SwiftFlutterPlugin.instance?.registrar?.lookupKey(forAsset: assetFilePath)
let assetFilePath = "../Frameworks/App.framework/Resources/flutter_assets/\(assetFilePath)"
guard let assetURL = Bundle.main.url(forResource: assetFilePath, withExtension: nil) else {
throw NSError(domain: assetFilePath + " asset file cannot be found!", code: 0)
}
@ -20,6 +21,7 @@ public class Util {
public static func getAbsPathAsset(assetFilePath: String) throws -> String {
// let key = SwiftFlutterPlugin.instance?.registrar?.lookupKey(forAsset: assetFilePath)
let assetFilePath = "../Frameworks/App.framework/Resources/flutter_assets/\(assetFilePath)"
guard let assetAbsPath = Bundle.main.path(forResource: assetFilePath, ofType: nil) else {
throw NSError(domain: assetFilePath + " asset file cannot be found!", code: 0)
}