Added WebMessageChannel and WebMessageListener features, Updated Android Gradle distributionUrl version to 5.6.4, AndroidInAppWebViewController.getCurrentWebViewPackage is available now starting from Android API 21+, Attempt to fix #665, fix #579, Fixed wrong mapping of NavigationAction class on Android for androidHasGesture and androidIsRedirect properties
This commit is contained in:
parent
54b31ca885
commit
a40831087f
|
@ -1,738 +0,0 @@
|
||||||
<component name="libraryTable">
|
|
||||||
<library name="Dart Packages" type="DartPackagesLibraryType">
|
|
||||||
<properties>
|
|
||||||
<option name="packageNameToDirsMap">
|
|
||||||
<entry key="_fe_analyzer_shared">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/_fe_analyzer_shared-12.0.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="analyzer">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/analyzer-0.40.6/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="archive">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/archive-3.0.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/archive-2.0.13/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="args">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/args-1.6.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="async">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.5.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.5.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="boolean_selector">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="characters">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0-nullsafety.5/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="charcode">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.2.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.2.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="cli_util">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/cli_util-0.2.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="clock">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="collection">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0-nullsafety.5/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="convert">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/convert-2.1.1/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="coverage">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/coverage-0.14.2/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="crypto">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/crypto-2.1.5/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="cupertino_icons">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.2/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="fake_async">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="ffi">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/ffi-1.0.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="file">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.1.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.0.0-nullsafety.4/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="flutter">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/packages/flutter/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="flutter_downloader">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="flutter_driver">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/packages/flutter_driver/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="flutter_test">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/packages/flutter_test/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="fuchsia_remote_debug_protocol">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/packages/fuchsia_remote_debug_protocol/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="glob">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/glob-1.2.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="http_multi_server">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.2.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="http_parser">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/http_parser-3.1.4/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="integration_test">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/packages/integration_test/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="io">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/io-0.3.4/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="js">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.3-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="json_rpc_2">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/json_rpc_2-2.2.2/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="logging">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/logging-0.11.4/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="matcher">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="meta">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.3.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.3.0-nullsafety.6/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="mime">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/mime-0.9.7/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="mockito">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/mockito-4.1.1/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="node_interop">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/node_interop-1.2.1/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="node_io">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/node_io-1.1.1/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="node_preamble">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/node_preamble-1.4.13/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="package_config">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/package_config-1.9.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="path">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="path_provider">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="path_provider_linux">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="path_provider_macos">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="path_provider_platform_interface">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_platform_interface-2.0.0-nullsafety/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="path_provider_windows">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="pedantic">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/pedantic-1.10.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/pedantic-1.10.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="permission_handler">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="permission_handler_platform_interface">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler_platform_interface-2.0.2/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="platform">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/platform-3.0.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/platform-3.0.0-nullsafety.4/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="plugin_platform_interface">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-1.1.0-nullsafety.1/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="pool">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/pool-1.5.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="process">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.1.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.0.0-nullsafety.4/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="pub_semver">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.4.4/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="shelf">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf-0.7.5/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="shelf_packages_handler">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-2.0.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="shelf_static">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_static-0.2.9+1/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="shelf_web_socket">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_web_socket-0.2.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="sky_engine">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/bin/cache/pkg/sky_engine/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="source_map_stack_trace">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-2.1.0-nullsafety.4/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="source_maps">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.10-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="source_span">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.0-nullsafety.4/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="stack_trace">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0-nullsafety.6/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="stream_channel">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="string_scanner">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="sync_http">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/sync_http-0.3.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/sync_http-0.2.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="term_glyph">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="test">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/test-1.16.0-nullsafety.9/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="test_api">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.19/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.19-nullsafety.6/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="test_core">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/test_core-0.3.12-nullsafety.9/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="typed_data">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0-nullsafety.5/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="url_launcher">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="url_launcher_linux">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="url_launcher_macos">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="url_launcher_platform_interface">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_platform_interface-2.0.0-nullsafety.1/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="url_launcher_windows">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="vector_math">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0-nullsafety.5/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="vm_service">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/vm_service-6.0.1-nullsafety.1/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/vm_service-5.5.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="watcher">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7+15/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="web_socket_channel">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/web_socket_channel-1.2.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="webdriver">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/webdriver-3.0.0/lib" />
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/webdriver-2.1.2/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="webkit_inspection_protocol">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/webkit_inspection_protocol-0.7.4/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="win32">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/win32-2.0.0/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="xdg_directories">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/xdg_directories-0.2.0-nullsafety.1/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="yaml">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/yaml-2.2.1/lib" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</option>
|
|
||||||
</properties>
|
|
||||||
<CLASSES>
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/_fe_analyzer_shared-12.0.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/analyzer-0.40.6/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/archive-2.0.13/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/archive-3.0.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/args-1.6.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.5.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.5.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0-nullsafety.5/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.2.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.2.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/cli_util-0.2.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0-nullsafety.5/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/convert-2.1.1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/coverage-0.14.2/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/crypto-2.1.5/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.2/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/ffi-1.0.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.0.0-nullsafety.4/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.1.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/glob-1.2.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.2.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/http_parser-3.1.4/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/io-0.3.4/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.3-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/json_rpc_2-2.2.2/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/logging-0.11.4/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.3.0-nullsafety.6/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.3.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/mime-0.9.7/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/mockito-4.1.1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/node_interop-1.2.1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/node_io-1.1.1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/node_preamble-1.4.13/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/package_config-1.9.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_platform_interface-2.0.0-nullsafety/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/pedantic-1.10.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/pedantic-1.10.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler_platform_interface-2.0.2/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/platform-3.0.0-nullsafety.4/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/platform-3.0.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-1.1.0-nullsafety.1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/pool-1.5.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.0.0-nullsafety.4/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.1.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.4.4/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf-0.7.5/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-2.0.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_static-0.2.9+1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_web_socket-0.2.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-2.1.0-nullsafety.4/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.10-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.0-nullsafety.4/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0-nullsafety.6/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/sync_http-0.2.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/sync_http-0.3.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/test-1.16.0-nullsafety.9/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.19-nullsafety.6/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.19/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/test_core-0.3.12-nullsafety.9/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0-nullsafety.5/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_platform_interface-2.0.0-nullsafety.1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0-nullsafety.5/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/vm_service-5.5.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/vm_service-6.0.1-nullsafety.1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7+15/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/web_socket_channel-1.2.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/webdriver-2.1.2/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/webdriver-3.0.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/webkit_inspection_protocol-0.7.4/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/win32-2.0.0/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/xdg_directories-0.2.0-nullsafety.1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/yaml-2.2.1/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/bin/cache/pkg/sky_engine/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/packages/flutter/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/packages/flutter_driver/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/packages/flutter_test/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/packages/fuchsia_remote_debug_protocol/lib" />
|
|
||||||
<root url="file://$USER_HOME$/flutter/packages/integration_test/lib" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
|
@ -1,6 +1,8 @@
|
||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="Flutter Plugins">
|
<library name="Flutter Plugins">
|
||||||
<CLASSES />
|
<CLASSES>
|
||||||
|
<root url="file://$PROJECT_DIR$" />
|
||||||
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
</library>
|
</library>
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
## 5.2.0
|
||||||
|
|
||||||
|
- Added `WebMessageChannel` and `WebMessageListener` features
|
||||||
|
- `AndroidInAppWebViewController.getCurrentWebViewPackage` is available now starting from Android API 21+.
|
||||||
|
- Updated Android Gradle distributionUrl version to `5.6.4`
|
||||||
|
- Attempt to fix "InAppBrowserActivity.onCreate NullPointerException - Attempt to invoke virtual method 'java.lang.String android.os.Bundle.getString(java.lang.String)' on a null object reference" [#665](https://github.com/pichillilorenzo/flutter_inappwebview/issues/665)
|
||||||
|
- Fixed "[iOS] Application crashes when processing onCreateWindow" [#579](https://github.com/pichillilorenzo/flutter_inappwebview/issues/579)
|
||||||
|
- Fixed wrong mapping of `NavigationAction` class on Android for `androidHasGesture` and `androidIsRedirect` properties
|
||||||
|
|
||||||
## 5.1.0+4
|
## 5.1.0+4
|
||||||
|
|
||||||
- Fixed "IOS scrolling crash the application" [#707](https://github.com/pichillilorenzo/flutter_inappwebview/issues/707)
|
- Fixed "IOS scrolling crash the application" [#707](https://github.com/pichillilorenzo/flutter_inappwebview/issues/707)
|
||||||
|
|
|
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
|
@ -2,8 +2,13 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.pichillilorenzo.flutter_inappwebview">
|
package="com.pichillilorenzo.flutter_inappwebview">
|
||||||
<application>
|
<application>
|
||||||
<activity android:theme="@style/AppTheme" android:name="com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserActivity" android:configChanges="orientation|screenSize"></activity>
|
<activity
|
||||||
<activity android:theme="@style/ThemeTransparent" android:name="com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ChromeCustomTabsActivity" android:configChanges="orientation|screenSize"></activity>
|
android:theme="@style/AppTheme"
|
||||||
|
android:name="com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density" />
|
||||||
|
<activity
|
||||||
|
android:theme="@style/ThemeTransparent"
|
||||||
|
android:name="com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ChromeCustomTabsActivity" />
|
||||||
<receiver android:name="com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ActionBroadcastReceiver" />
|
<receiver android:name="com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ActionBroadcastReceiver" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedded_views_preview"
|
android:name="io.flutter.embedded_views_preview"
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package com.pichillilorenzo.flutter_inappwebview;
|
package com.pichillilorenzo.flutter_inappwebview;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.webkit.WebMessageCompat;
|
||||||
|
import androidx.webkit.WebMessagePortCompat;
|
||||||
import androidx.webkit.WebViewCompat;
|
import androidx.webkit.WebViewCompat;
|
||||||
import androidx.webkit.WebViewFeature;
|
import androidx.webkit.WebViewFeature;
|
||||||
|
|
||||||
|
@ -16,9 +20,13 @@ import com.pichillilorenzo.flutter_inappwebview.types.ContentWorld;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.SslCertificateExt;
|
import com.pichillilorenzo.flutter_inappwebview.types.SslCertificateExt;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
|
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.WebMessageChannel;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.WebMessageListener;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import io.flutter.plugin.common.MethodCall;
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
@ -505,6 +513,56 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle
|
||||||
result.success(false);
|
result.success(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "createWebMessageChannel":
|
||||||
|
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||||
|
WebViewFeature.isFeatureSupported(WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL)) {
|
||||||
|
result.success(webView.createCompatWebMessageChannel().toMap());
|
||||||
|
} else {
|
||||||
|
result.success(null);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "postWebMessage":
|
||||||
|
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||||
|
WebViewFeature.isFeatureSupported(WebViewFeature.POST_WEB_MESSAGE)) {
|
||||||
|
Map<String, Object> message = (Map<String, Object>) call.argument("message");
|
||||||
|
String targetOrigin = (String) call.argument("targetOrigin");
|
||||||
|
List<WebMessagePortCompat> ports = new ArrayList<>();
|
||||||
|
List<Map<String, Object>> portsMap = (List<Map<String, Object>>) message.get("ports");
|
||||||
|
if (portsMap != null) {
|
||||||
|
for (Map<String, Object> portMap : portsMap) {
|
||||||
|
String webMessageChannelId = (String) portMap.get("webMessageChannelId");
|
||||||
|
Integer index = (Integer) portMap.get("index");
|
||||||
|
WebMessageChannel webMessageChannel = webView.webMessageChannels.get(webMessageChannelId);
|
||||||
|
if (webMessageChannel != null) {
|
||||||
|
ports.add(webMessageChannel.ports.get(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WebMessageCompat webMessage = new WebMessageCompat((String) message.get("data"), ports.toArray(new WebMessagePortCompat[0]));
|
||||||
|
try {
|
||||||
|
WebViewCompat.postWebMessage(webView, webMessage, Uri.parse(targetOrigin));
|
||||||
|
result.success(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.error(LOG_TAG, e.getMessage(), null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.success(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "addWebMessageListener":
|
||||||
|
if (webView != null && WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
|
||||||
|
Map<String, Object> webMessageListenerMap = (Map<String, Object>) call.argument("webMessageListener");
|
||||||
|
WebMessageListener webMessageListener = WebMessageListener.fromMap(webMessageListenerMap);
|
||||||
|
try {
|
||||||
|
webView.addWebMessageListener(webMessageListener);
|
||||||
|
result.success(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.error(LOG_TAG, e.getMessage(), null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.success(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
result.notImplemented();
|
result.notImplemented();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.pichillilorenzo.flutter_inappwebview;
|
package com.pichillilorenzo.flutter_inappwebview;
|
||||||
|
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
|
@ -9,6 +10,7 @@ import android.webkit.WebView;
|
||||||
import androidx.webkit.WebViewCompat;
|
import androidx.webkit.WebViewCompat;
|
||||||
import androidx.webkit.WebViewFeature;
|
import androidx.webkit.WebViewFeature;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -66,8 +68,20 @@ public class InAppWebViewStatic implements MethodChannel.MethodCallHandler {
|
||||||
break;
|
break;
|
||||||
case "getCurrentWebViewPackage":
|
case "getCurrentWebViewPackage":
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
result.success(
|
result.success(convertWebViewPackageToMap(WebViewCompat.getCurrentWebViewPackage(Shared.activity)));
|
||||||
convertWebViewPackageToMap(WebViewCompat.getCurrentWebViewPackage(Shared.activity)));
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
//with Android Lollipop (API 21) they started to update the WebView
|
||||||
|
//as a separate APK with the PlayStore and they added the
|
||||||
|
//getLoadedPackageInfo() method to the WebViewFactory class and this
|
||||||
|
//should handle the Android 7.0 behaviour changes too
|
||||||
|
try {
|
||||||
|
Class webViewFactory = Class.forName("android.webkit.WebViewFactory");
|
||||||
|
Method method = webViewFactory.getMethod("getLoadedPackageInfo");
|
||||||
|
PackageInfo pInfo = (PackageInfo) method.invoke(null);
|
||||||
|
result.success(convertWebViewPackageToMap(pInfo));
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success(null);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
result.success(null);
|
result.success(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,11 +65,8 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bundle b = getIntent().getExtras();
|
Bundle b = getIntent().getExtras();
|
||||||
|
assert b != null;
|
||||||
id = b.getString("id");
|
id = b.getString("id");
|
||||||
windowId = b.getInt("windowId");
|
windowId = b.getInt("windowId");
|
||||||
|
|
||||||
|
@ -77,7 +74,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow
|
||||||
|
|
||||||
setContentView(R.layout.activity_web_view);
|
setContentView(R.layout.activity_web_view);
|
||||||
|
|
||||||
Map<String, Object> pullToRefreshInitialOptions = (Map<String, Object>) b.getSerializable("pullToRefreshInitialOptions");
|
Map<String, Object> pullToRefreshInitialOptions = (Map<String, Object>) b.getSerializable("pullToRefreshInitialOptions");
|
||||||
MethodChannel pullToRefreshLayoutChannel = new MethodChannel(Shared.messenger, "com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_" + id);
|
MethodChannel pullToRefreshLayoutChannel = new MethodChannel(Shared.messenger, "com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_" + id);
|
||||||
PullToRefreshOptions pullToRefreshOptions = new PullToRefreshOptions();
|
PullToRefreshOptions pullToRefreshOptions = new PullToRefreshOptions();
|
||||||
pullToRefreshOptions.parse(pullToRefreshInitialOptions);
|
pullToRefreshOptions.parse(pullToRefreshInitialOptions);
|
||||||
|
|
|
@ -32,6 +32,8 @@ import android.os.Bundle;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -58,7 +60,7 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMethodCall(final MethodCall call, final Result result) {
|
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
||||||
final Activity activity = Shared.activity;
|
final Activity activity = Shared.activity;
|
||||||
|
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
@ -41,30 +41,28 @@ import android.webkit.WebBackForwardList;
|
||||||
import android.webkit.WebHistoryItem;
|
import android.webkit.WebHistoryItem;
|
||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
import android.webkit.WebStorage;
|
import android.webkit.WebStorage;
|
||||||
|
import android.webkit.WebView;
|
||||||
import android.widget.HorizontalScrollView;
|
import android.widget.HorizontalScrollView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.webkit.JavaScriptReplyProxy;
|
||||||
|
import androidx.webkit.WebMessageCompat;
|
||||||
import androidx.webkit.WebViewCompat;
|
import androidx.webkit.WebViewCompat;
|
||||||
import androidx.webkit.WebViewFeature;
|
import androidx.webkit.WebViewFeature;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
|
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType;
|
import com.pichillilorenzo.flutter_inappwebview.R;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.UserContentController;
|
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlocker;
|
import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlocker;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlockerAction;
|
import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlockerAction;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlockerHandler;
|
import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlockerHandler;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlockerTrigger;
|
import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlockerTrigger;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.ContentWorld;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserDelegate;
|
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserDelegate;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.R;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.ConsoleLogJS;
|
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.ConsoleLogJS;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.InterceptAjaxRequestJS;
|
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.InterceptAjaxRequestJS;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.InterceptFetchRequestJS;
|
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.InterceptFetchRequestJS;
|
||||||
|
@ -75,19 +73,25 @@ import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.OnWindowFocusE
|
||||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PluginScriptsUtil;
|
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PluginScriptsUtil;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PrintJS;
|
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PrintJS;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PromisePolyfillJS;
|
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PromisePolyfillJS;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.ContentWorld;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.UserContentController;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.WebMessageChannel;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.WebMessageListener;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -140,6 +144,8 @@ final public class InAppWebView extends InputAwareWebView {
|
||||||
public Map<String, ValueCallback<String>> callAsyncJavaScriptCallbacks = new HashMap<>();
|
public Map<String, ValueCallback<String>> callAsyncJavaScriptCallbacks = new HashMap<>();
|
||||||
public Map<String, ValueCallback<String>> evaluateJavaScriptContentWorldCallbacks = new HashMap<>();
|
public Map<String, ValueCallback<String>> evaluateJavaScriptContentWorldCallbacks = new HashMap<>();
|
||||||
|
|
||||||
|
public Map<String, WebMessageChannel> webMessageChannels = new HashMap<>();
|
||||||
|
|
||||||
public InAppWebView(Context context) {
|
public InAppWebView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
@ -1587,6 +1593,30 @@ final public class InAppWebView extends InputAwareWebView {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
|
public WebMessageChannel createCompatWebMessageChannel() {
|
||||||
|
String id = UUID.randomUUID().toString();
|
||||||
|
WebMessageChannel webMessageChannel = new WebMessageChannel(id, this);
|
||||||
|
webMessageChannels.put(id, webMessageChannel);
|
||||||
|
return webMessageChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addWebMessageListener(@NonNull WebMessageListener webMessageListener) {
|
||||||
|
WebViewCompat.addWebMessageListener(this, webMessageListener.jsObjectName, webMessageListener.allowedOriginRules, webMessageListener.listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disposeWebMessageChannels() {
|
||||||
|
for (WebMessageChannel webMessageChannel : webMessageChannels.values()) {
|
||||||
|
webMessageChannel.dispose();
|
||||||
|
}
|
||||||
|
webMessageChannels.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// protected void onWindowVisibilityChanged(int visibility) {
|
||||||
|
// if (visibility != View.GONE) super.onWindowVisibilityChanged(View.VISIBLE);
|
||||||
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if (windowId != null) {
|
if (windowId != null) {
|
||||||
|
@ -1594,6 +1624,7 @@ final public class InAppWebView extends InputAwareWebView {
|
||||||
}
|
}
|
||||||
headlessHandler.removeCallbacksAndMessages(null);
|
headlessHandler.removeCallbacksAndMessages(null);
|
||||||
mHandler.removeCallbacksAndMessages(null);
|
mHandler.removeCallbacksAndMessages(null);
|
||||||
|
disposeWebMessageChannels();
|
||||||
removeAllViews();
|
removeAllViews();
|
||||||
if (checkContextMenuShouldBeClosedTask != null)
|
if (checkContextMenuShouldBeClosedTask != null)
|
||||||
removeCallbacks(checkContextMenuShouldBeClosedTask);
|
removeCallbacks(checkContextMenuShouldBeClosedTask);
|
||||||
|
|
|
@ -190,7 +190,7 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||||
final InAppWebView webView = (InAppWebView) view;
|
final InAppWebView webView = (InAppWebView) view;
|
||||||
webView.isLoading = true;
|
webView.isLoading = true;
|
||||||
|
webView.disposeWebMessageChannels();
|
||||||
webView.userContentController.resetContentWorlds();
|
webView.userContentController.resetContentWorlds();
|
||||||
loadCustomJavaScriptOnPageStarted(webView);
|
loadCustomJavaScriptOnPageStarted(webView);
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ public class NavigationAction {
|
||||||
Map<String, Object> navigationActionMap = new HashMap<>();
|
Map<String, Object> navigationActionMap = new HashMap<>();
|
||||||
navigationActionMap.put("request", request.toMap());
|
navigationActionMap.put("request", request.toMap());
|
||||||
navigationActionMap.put("isForMainFrame", isForMainFrame);
|
navigationActionMap.put("isForMainFrame", isForMainFrame);
|
||||||
navigationActionMap.put("hasGesture", hasGesture);
|
navigationActionMap.put("androidHasGesture", hasGesture);
|
||||||
navigationActionMap.put("isRedirect", isRedirect);
|
navigationActionMap.put("androidIsRedirect", isRedirect);
|
||||||
return navigationActionMap;
|
return navigationActionMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
package com.pichillilorenzo.flutter_inappwebview.types;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.webkit.WebMessageCompat;
|
||||||
|
import androidx.webkit.WebMessagePortCompat;
|
||||||
|
import androidx.webkit.WebViewCompat;
|
||||||
|
import androidx.webkit.WebViewFeature;
|
||||||
|
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
|
||||||
|
public class WebMessageChannel implements MethodChannel.MethodCallHandler {
|
||||||
|
static final String LOG_TAG = "WebMessageChannel";
|
||||||
|
|
||||||
|
public String id;
|
||||||
|
public MethodChannel channel;
|
||||||
|
public final List<WebMessagePortCompat> ports;
|
||||||
|
private InAppWebView webView;
|
||||||
|
|
||||||
|
public WebMessageChannel(@NonNull String id, @NonNull InAppWebView webView) {
|
||||||
|
this.id = id;
|
||||||
|
this.channel = new MethodChannel(Shared.messenger, "com.pichillilorenzo/flutter_inappwebview_web_message_channel_" + id);
|
||||||
|
this.channel.setMethodCallHandler(this);
|
||||||
|
this.ports = new ArrayList<>(Arrays.asList(WebViewCompat.createWebMessageChannel(webView)));
|
||||||
|
this.webView = webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
||||||
|
switch (call.method) {
|
||||||
|
case "setWebMessageCallback":
|
||||||
|
if (webView != null && ports.size() > 0 &&
|
||||||
|
WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK)) {
|
||||||
|
final Integer index = (Integer) call.argument("index");
|
||||||
|
final WebMessagePortCompat webMessagePort = ports.get(index);
|
||||||
|
try {
|
||||||
|
webMessagePort.setWebMessageCallback(new WebMessagePortCompat.WebMessageCallbackCompat() {
|
||||||
|
@Override
|
||||||
|
public void onMessage(@NonNull WebMessagePortCompat port, @Nullable WebMessageCompat message) {
|
||||||
|
super.onMessage(port, message);
|
||||||
|
|
||||||
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
obj.put("index", index);
|
||||||
|
obj.put("message", message != null ? message.getData() : null);
|
||||||
|
channel.invokeMethod("onMessage", obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result.success(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.error(LOG_TAG, e.getMessage(), null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.success(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "postMessage":
|
||||||
|
if (webView != null && ports.size() > 0 &&
|
||||||
|
WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE)) {
|
||||||
|
final Integer index = (Integer) call.argument("index");
|
||||||
|
WebMessagePortCompat port = ports.get(index);
|
||||||
|
Map<String, Object> message = (Map<String, Object>) call.argument("message");
|
||||||
|
List<WebMessagePortCompat> webMessagePorts = new ArrayList<>();
|
||||||
|
List<Map<String, Object>> portsMap = (List<Map<String, Object>>) message.get("ports");
|
||||||
|
if (portsMap != null) {
|
||||||
|
for (Map<String, Object> portMap : portsMap) {
|
||||||
|
String webMessageChannelId = (String) portMap.get("webMessageChannelId");
|
||||||
|
Integer portIndex = (Integer) portMap.get("index");
|
||||||
|
WebMessageChannel webMessageChannel = webView.webMessageChannels.get(webMessageChannelId);
|
||||||
|
if (webMessageChannel != null) {
|
||||||
|
webMessagePorts.add(webMessageChannel.ports.get(portIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WebMessageCompat webMessage = new WebMessageCompat((String) message.get("data"), webMessagePorts.toArray(new WebMessagePortCompat[0]));
|
||||||
|
try {
|
||||||
|
port.postMessage(webMessage);
|
||||||
|
result.success(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.error(LOG_TAG, e.getMessage(), null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.success(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "close":
|
||||||
|
if (webView != null && ports.size() > 0 &&
|
||||||
|
WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_PORT_CLOSE)) {
|
||||||
|
Integer index = (Integer) call.argument("index");
|
||||||
|
WebMessagePortCompat port = ports.get(index);
|
||||||
|
try {
|
||||||
|
port.close();
|
||||||
|
result.success(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.error(LOG_TAG, e.getMessage(), null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.success(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result.notImplemented();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> toMap() {
|
||||||
|
Map<String, Object> webMessageChannelMap = new HashMap<>();
|
||||||
|
webMessageChannelMap.put("id", id);
|
||||||
|
return webMessageChannelMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_PORT_CLOSE)) {
|
||||||
|
for (WebMessagePortCompat port : ports) {
|
||||||
|
try {
|
||||||
|
port.close();
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.channel.setMethodCallHandler(null);
|
||||||
|
this.ports.clear();
|
||||||
|
this.webView = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package com.pichillilorenzo.flutter_inappwebview.types;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.webkit.JavaScriptReplyProxy;
|
||||||
|
import androidx.webkit.WebMessageCompat;
|
||||||
|
import androidx.webkit.WebViewCompat;
|
||||||
|
import androidx.webkit.WebViewFeature;
|
||||||
|
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
|
||||||
|
public class WebMessageListener implements MethodChannel.MethodCallHandler {
|
||||||
|
static final String LOG_TAG = "WebMessageListener";
|
||||||
|
|
||||||
|
public String jsObjectName;
|
||||||
|
public Set<String> allowedOriginRules;
|
||||||
|
public WebViewCompat.WebMessageListener listener;
|
||||||
|
public JavaScriptReplyProxy replyProxy;
|
||||||
|
public MethodChannel channel;
|
||||||
|
|
||||||
|
public WebMessageListener(@NonNull String jsObjectName, @NonNull Set<String> allowedOriginRules) {
|
||||||
|
this.jsObjectName = jsObjectName;
|
||||||
|
this.allowedOriginRules = allowedOriginRules;
|
||||||
|
this.channel = new MethodChannel(Shared.messenger, "com.pichillilorenzo/flutter_inappwebview_web_message_listener_" + this.jsObjectName);
|
||||||
|
this.channel.setMethodCallHandler(this);
|
||||||
|
this.listener = new WebViewCompat.WebMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onPostMessage(@NonNull WebView view, @NonNull WebMessageCompat message, @NonNull Uri sourceOrigin, boolean isMainFrame, @NonNull JavaScriptReplyProxy javaScriptReplyProxy) {
|
||||||
|
replyProxy = javaScriptReplyProxy;
|
||||||
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
obj.put("message", message.getData());
|
||||||
|
obj.put("sourceOrigin", sourceOrigin.toString().equals("null") ? null : sourceOrigin.toString());
|
||||||
|
obj.put("isMainFrame", isMainFrame);
|
||||||
|
channel.invokeMethod("onPostMessage", obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static WebMessageListener fromMap(@Nullable Map<String, Object> map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String jsObjectName = (String) map.get("jsObjectName");
|
||||||
|
assert jsObjectName != null;
|
||||||
|
List<String> allowedOriginRuleList = (List<String>) map.get("allowedOriginRules");
|
||||||
|
assert allowedOriginRuleList != null;
|
||||||
|
Set<String> allowedOriginRules = new HashSet<>(allowedOriginRuleList);
|
||||||
|
return new WebMessageListener(jsObjectName, allowedOriginRules);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
||||||
|
switch (call.method) {
|
||||||
|
case "postMessage":
|
||||||
|
if (replyProxy != null && WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
|
||||||
|
String message = (String) call.argument("message");
|
||||||
|
replyProxy.postMessage(message);
|
||||||
|
}
|
||||||
|
result.success(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result.notImplemented();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
this.channel.setMethodCallHandler(null);
|
||||||
|
this.listener = null;
|
||||||
|
this.replyProxy = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"android":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-03-08 17:17:50.745041","version":"2.1.0-10.0.pre"}
|
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"android":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-03-11 22:30:27.356562","version":"2.1.0-10.0.pre"}
|
|
@ -2,13 +2,12 @@
|
||||||
# This is a generated file; do not edit or check into version control.
|
# This is a generated file; do not edit or check into version control.
|
||||||
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
|
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
|
||||||
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
|
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
|
||||||
export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/lib/main.dart"
|
export "FLUTTER_TARGET=lib/main.dart"
|
||||||
export "FLUTTER_BUILD_DIR=build"
|
export "FLUTTER_BUILD_DIR=build"
|
||||||
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
|
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
|
||||||
export "FLUTTER_BUILD_NAME=1.0.0"
|
export "FLUTTER_BUILD_NAME=1.0.0"
|
||||||
export "FLUTTER_BUILD_NUMBER=1"
|
export "FLUTTER_BUILD_NUMBER=1"
|
||||||
export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
|
|
||||||
export "DART_OBFUSCATION=false"
|
export "DART_OBFUSCATION=false"
|
||||||
export "TRACK_WIDGET_CREATION=true"
|
export "TRACK_WIDGET_CREATION=false"
|
||||||
export "TREE_SHAKE_ICONS=false"
|
export "TREE_SHAKE_ICONS=false"
|
||||||
export "PACKAGE_CONFIG=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/.dart_tool/package_config.json"
|
export "PACKAGE_CONFIG=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/.dart_tool/package_config.json"
|
||||||
|
|
|
@ -17,7 +17,7 @@ Future main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
// await Permission.camera.request();
|
// await Permission.camera.request();
|
||||||
// await Permission.microphone.request();
|
// await Permission.microphone.request();
|
||||||
//await Permission.storage.request();
|
// await Permission.storage.request();
|
||||||
|
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
|
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
|
||||||
|
|
|
@ -80,6 +80,5 @@
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||||
<orderEntry type="library" name="Flutter Plugins" level="project" />
|
<orderEntry type="library" name="Flutter Plugins" level="project" />
|
||||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
|
@ -75,6 +75,7 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
|
||||||
pullToRefreshControl.prepare()
|
pullToRefreshControl.prepare()
|
||||||
|
|
||||||
prepareWebView()
|
prepareWebView()
|
||||||
|
webView.windowCreated = true
|
||||||
|
|
||||||
progressBar = UIProgressView(progressViewStyle: .bar)
|
progressBar = UIProgressView(progressViewStyle: .bar)
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,7 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
|
||||||
|
|
||||||
webView!.options = options
|
webView!.options = options
|
||||||
webView!.prepare()
|
webView!.prepare()
|
||||||
|
webView!.windowCreated = true
|
||||||
|
|
||||||
if windowId == nil {
|
if windowId == nil {
|
||||||
if #available(iOS 11.0, *) {
|
if #available(iOS 11.0, *) {
|
||||||
|
|
|
@ -12,10 +12,13 @@ import WebKit
|
||||||
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate, PullToRefreshDelegate {
|
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate, PullToRefreshDelegate {
|
||||||
|
|
||||||
var windowId: Int64?
|
var windowId: Int64?
|
||||||
|
var windowCreated = false
|
||||||
var inAppBrowserDelegate: InAppBrowserDelegate?
|
var inAppBrowserDelegate: InAppBrowserDelegate?
|
||||||
var channel: FlutterMethodChannel?
|
var channel: FlutterMethodChannel?
|
||||||
var options: InAppWebViewOptions?
|
var options: InAppWebViewOptions?
|
||||||
var pullToRefreshControl: PullToRefreshControl?
|
var pullToRefreshControl: PullToRefreshControl?
|
||||||
|
var webMessageChannels: [String:WebMessageChannel] = [:]
|
||||||
|
var webMessageListeners: [WebMessageListener] = []
|
||||||
|
|
||||||
static var sslCertificatesMap: [String: SslCertificate] = [:] // [URL host name : SslCertificate]
|
static var sslCertificatesMap: [String: SslCertificate] = [:] // [URL host name : SslCertificate]
|
||||||
static var credentialsProposed: [URLCredential] = []
|
static var credentialsProposed: [URLCredential] = []
|
||||||
|
@ -450,6 +453,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
}
|
}
|
||||||
configuration.userContentController.removeScriptMessageHandler(forName: "onCallAsyncJavaScriptResultBelowIOS14Received")
|
configuration.userContentController.removeScriptMessageHandler(forName: "onCallAsyncJavaScriptResultBelowIOS14Received")
|
||||||
configuration.userContentController.add(self, name: "onCallAsyncJavaScriptResultBelowIOS14Received")
|
configuration.userContentController.add(self, name: "onCallAsyncJavaScriptResultBelowIOS14Received")
|
||||||
|
configuration.userContentController.removeScriptMessageHandler(forName: "onWebMessagePortMessageReceived")
|
||||||
|
configuration.userContentController.add(self, name: "onWebMessagePortMessageReceived")
|
||||||
|
configuration.userContentController.removeScriptMessageHandler(forName: "onWebMessageListenerPostMessageReceived")
|
||||||
|
configuration.userContentController.add(self, name: "onWebMessageListenerPostMessageReceived")
|
||||||
configuration.userContentController.addUserOnlyScripts(initialUserScripts)
|
configuration.userContentController.addUserOnlyScripts(initialUserScripts)
|
||||||
configuration.userContentController.sync(scriptMessageHandler: self)
|
configuration.userContentController.sync(scriptMessageHandler: self)
|
||||||
}
|
}
|
||||||
|
@ -1418,6 +1425,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
decidePolicyFor navigationAction: WKNavigationAction,
|
decidePolicyFor navigationAction: WKNavigationAction,
|
||||||
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||||
|
|
||||||
|
if windowId != nil, !windowCreated {
|
||||||
|
decisionHandler(.cancel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if navigationAction.request.url != nil {
|
if navigationAction.request.url != nil {
|
||||||
|
|
||||||
if let useShouldOverrideUrlLoading = options?.useShouldOverrideUrlLoading, useShouldOverrideUrlLoading {
|
if let useShouldOverrideUrlLoading = options?.useShouldOverrideUrlLoading, useShouldOverrideUrlLoading {
|
||||||
|
@ -1513,6 +1525,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
}
|
}
|
||||||
|
|
||||||
public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||||
|
disposeWebMessageChannels()
|
||||||
initializeWindowIdJS()
|
initializeWindowIdJS()
|
||||||
|
|
||||||
if #available(iOS 14.0, *) {
|
if #available(iOS 14.0, *) {
|
||||||
|
@ -1561,6 +1574,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
|
|
||||||
public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||||
|
|
||||||
|
if windowId != nil, !windowCreated {
|
||||||
|
completionHandler(.cancelAuthenticationChallenge, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic ||
|
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic ||
|
||||||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault ||
|
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault ||
|
||||||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest ||
|
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest ||
|
||||||
|
@ -1573,6 +1591,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
onReceivedHttpAuthRequest(challenge: challenge, result: {(result) -> Void in
|
onReceivedHttpAuthRequest(challenge: challenge, result: {(result) -> Void in
|
||||||
if result is FlutterError {
|
if result is FlutterError {
|
||||||
print((result as! FlutterError).message ?? "")
|
print((result as! FlutterError).message ?? "")
|
||||||
|
completionHandler(.performDefaultHandling, nil)
|
||||||
}
|
}
|
||||||
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
||||||
completionHandler(.performDefaultHandling, nil)
|
completionHandler(.performDefaultHandling, nil)
|
||||||
|
@ -1642,6 +1661,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
onReceivedServerTrustAuthRequest(challenge: challenge, result: {(result) -> Void in
|
onReceivedServerTrustAuthRequest(challenge: challenge, result: {(result) -> Void in
|
||||||
if result is FlutterError {
|
if result is FlutterError {
|
||||||
print((result as! FlutterError).message ?? "")
|
print((result as! FlutterError).message ?? "")
|
||||||
|
completionHandler(.performDefaultHandling, nil)
|
||||||
}
|
}
|
||||||
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
||||||
completionHandler(.performDefaultHandling, nil)
|
completionHandler(.performDefaultHandling, nil)
|
||||||
|
@ -1677,6 +1697,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
onReceivedClientCertRequest(challenge: challenge, result: {(result) -> Void in
|
onReceivedClientCertRequest(challenge: challenge, result: {(result) -> Void in
|
||||||
if result is FlutterError {
|
if result is FlutterError {
|
||||||
print((result as! FlutterError).message ?? "")
|
print((result as! FlutterError).message ?? "")
|
||||||
|
completionHandler(.performDefaultHandling, nil)
|
||||||
}
|
}
|
||||||
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
||||||
completionHandler(.performDefaultHandling, nil)
|
completionHandler(.performDefaultHandling, nil)
|
||||||
|
@ -1801,6 +1822,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
onJsAlert(frame: frame, message: message, result: {(result) -> Void in
|
onJsAlert(frame: frame, message: message, result: {(result) -> Void in
|
||||||
if result is FlutterError {
|
if result is FlutterError {
|
||||||
print((result as! FlutterError).message ?? "")
|
print((result as! FlutterError).message ?? "")
|
||||||
|
completionHandler()
|
||||||
}
|
}
|
||||||
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
||||||
self.createAlertDialog(message: message, responseMessage: nil, confirmButtonTitle: nil, completionHandler: completionHandler)
|
self.createAlertDialog(message: message, responseMessage: nil, confirmButtonTitle: nil, completionHandler: completionHandler)
|
||||||
|
@ -1862,6 +1884,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
onJsConfirm(frame: frame, message: message, result: {(result) -> Void in
|
onJsConfirm(frame: frame, message: message, result: {(result) -> Void in
|
||||||
if result is FlutterError {
|
if result is FlutterError {
|
||||||
print((result as! FlutterError).message ?? "")
|
print((result as! FlutterError).message ?? "")
|
||||||
|
completionHandler(false)
|
||||||
}
|
}
|
||||||
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
||||||
self.createConfirmDialog(message: message, responseMessage: nil, confirmButtonTitle: nil, cancelButtonTitle: nil, completionHandler: completionHandler)
|
self.createConfirmDialog(message: message, responseMessage: nil, confirmButtonTitle: nil, cancelButtonTitle: nil, completionHandler: completionHandler)
|
||||||
|
@ -1937,6 +1960,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
onJsPrompt(frame: frame, message: message, defaultValue: defaultValue, result: {(result) -> Void in
|
onJsPrompt(frame: frame, message: message, defaultValue: defaultValue, result: {(result) -> Void in
|
||||||
if result is FlutterError {
|
if result is FlutterError {
|
||||||
print((result as! FlutterError).message ?? "")
|
print((result as! FlutterError).message ?? "")
|
||||||
|
completionHandler(nil)
|
||||||
}
|
}
|
||||||
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
||||||
self.createPromptDialog(message: message, defaultValue: defaultValue, responseMessage: nil, confirmButtonTitle: nil, cancelButtonTitle: nil, value: nil, completionHandler: completionHandler)
|
self.createPromptDialog(message: message, defaultValue: defaultValue, responseMessage: nil, confirmButtonTitle: nil, cancelButtonTitle: nil, value: nil, completionHandler: completionHandler)
|
||||||
|
@ -2068,9 +2092,15 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
public func webView(_ webView: WKWebView,
|
public func webView(_ webView: WKWebView,
|
||||||
authenticationChallenge challenge: URLAuthenticationChallenge,
|
authenticationChallenge challenge: URLAuthenticationChallenge,
|
||||||
shouldAllowDeprecatedTLS decisionHandler: @escaping (Bool) -> Void) {
|
shouldAllowDeprecatedTLS decisionHandler: @escaping (Bool) -> Void) {
|
||||||
|
if windowId != nil, !windowCreated {
|
||||||
|
decisionHandler(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
shouldAllowDeprecatedTLS(challenge: challenge, result: {(result) -> Void in
|
shouldAllowDeprecatedTLS(challenge: challenge, result: {(result) -> Void in
|
||||||
if result is FlutterError {
|
if result is FlutterError {
|
||||||
print((result as! FlutterError).message ?? "")
|
print((result as! FlutterError).message ?? "")
|
||||||
|
decisionHandler(false)
|
||||||
}
|
}
|
||||||
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
||||||
decisionHandler(false)
|
decisionHandler(false)
|
||||||
|
@ -2511,6 +2541,45 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
||||||
])
|
])
|
||||||
callAsyncJavaScriptBelowIOS14Results.removeValue(forKey: resultUuid)
|
callAsyncJavaScriptBelowIOS14Results.removeValue(forKey: resultUuid)
|
||||||
}
|
}
|
||||||
|
} else if message.name == "onWebMessagePortMessageReceived" {
|
||||||
|
let body = message.body as! [String: Any?]
|
||||||
|
let webMessageChannelId = body["webMessageChannelId"] as! String
|
||||||
|
let index = body["index"] as! Int64
|
||||||
|
let webMessage = body["message"] as? String
|
||||||
|
if let webMessageChannel = webMessageChannels[webMessageChannelId] {
|
||||||
|
webMessageChannel.onMessage(index: index, message: webMessage)
|
||||||
|
}
|
||||||
|
} else if message.name == "onWebMessageListenerPostMessageReceived" {
|
||||||
|
let body = message.body as! [String: Any?]
|
||||||
|
let jsObjectName = body["jsObjectName"] as! String
|
||||||
|
let messageData = body["message"] as? String
|
||||||
|
if let webMessageListener = webMessageListeners.first(where: ({($0.jsObjectName == jsObjectName)})) {
|
||||||
|
let isMainFrame = message.frameInfo.isMainFrame
|
||||||
|
|
||||||
|
var scheme: String? = nil
|
||||||
|
var host: String? = nil
|
||||||
|
var port: Int? = nil
|
||||||
|
if #available(iOS 9.0, *) {
|
||||||
|
let sourceOrigin = message.frameInfo.securityOrigin
|
||||||
|
scheme = sourceOrigin.protocol
|
||||||
|
host = sourceOrigin.host
|
||||||
|
port = sourceOrigin.port
|
||||||
|
} else if let url = message.frameInfo.request.url {
|
||||||
|
scheme = url.scheme
|
||||||
|
host = url.host
|
||||||
|
port = url.port
|
||||||
|
}
|
||||||
|
|
||||||
|
if !webMessageListener.isOriginAllowed(scheme: scheme, host: host, port: port) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceOrigin: URL? = nil
|
||||||
|
if let scheme = scheme, !scheme.isEmpty, let host = host, !host.isEmpty {
|
||||||
|
sourceOrigin = URL(string: "\(scheme)://\(host)\(port != nil && port != 0 ? ":" + String(port!) : "")")
|
||||||
|
}
|
||||||
|
webMessageListener.onPostMessage(message: messageData, sourceOrigin: sourceOrigin, isMainFrame: isMainFrame)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2685,15 +2754,74 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func createWebMessageChannel(completionHandler: ((WebMessageChannel) -> Void)? = nil) -> WebMessageChannel {
|
||||||
|
let id = NSUUID().uuidString
|
||||||
|
let webMessageChannel = WebMessageChannel(id: id)
|
||||||
|
webMessageChannel.initJsInstance(webView: self, completionHandler: completionHandler)
|
||||||
|
webMessageChannels[id] = webMessageChannel
|
||||||
|
|
||||||
|
return webMessageChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
public func postWebMessage(message: WebMessage, targetOrigin: String, completionHandler: ((Any?) -> Void)? = nil) throws {
|
||||||
|
var portsString = "null"
|
||||||
|
if let ports = message.ports {
|
||||||
|
var portArrayString: [String] = []
|
||||||
|
for port in ports {
|
||||||
|
if port.isStarted {
|
||||||
|
throw NSError(domain: "Port is already started", code: 0)
|
||||||
|
}
|
||||||
|
if port.isClosed || port.isTransferred {
|
||||||
|
throw NSError(domain: "Port is already closed or transferred", code: 0)
|
||||||
|
}
|
||||||
|
port.isTransferred = true
|
||||||
|
portArrayString.append("\(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)['\(port.webMessageChannel!.id)'].\(port.name)")
|
||||||
|
}
|
||||||
|
portsString = "[" + portArrayString.joined(separator: ", ") + "]"
|
||||||
|
}
|
||||||
|
let data = message.data?.replacingOccurrences(of: "\'", with: "\\'") ?? "null"
|
||||||
|
let url = URL(string: targetOrigin)?.absoluteString ?? "*"
|
||||||
|
let source = """
|
||||||
|
(function() {
|
||||||
|
window.postMessage('\(data)', '\(url)', \(portsString));
|
||||||
|
})();
|
||||||
|
"""
|
||||||
|
evaluateJavascript(source: source, completionHandler: completionHandler)
|
||||||
|
message.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addWebMessageListener(webMessageListener: WebMessageListener) throws {
|
||||||
|
if webMessageListeners.map({ ($0.jsObjectName) }).contains(webMessageListener.jsObjectName) {
|
||||||
|
throw NSError(domain: "jsObjectName \(webMessageListener.jsObjectName) was already added.", code: 0)
|
||||||
|
}
|
||||||
|
try webMessageListener.assertOriginRulesValid()
|
||||||
|
webMessageListener.initJsInstance(webView: self)
|
||||||
|
webMessageListeners.append(webMessageListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func disposeWebMessageChannels() {
|
||||||
|
for webMessageChannel in webMessageChannels.values {
|
||||||
|
webMessageChannel.dispose()
|
||||||
|
}
|
||||||
|
webMessageChannels.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
public func dispose() {
|
public func dispose() {
|
||||||
if isPausedTimers, let completionHandler = isPausedTimersCompletionHandler {
|
if isPausedTimers, let completionHandler = isPausedTimersCompletionHandler {
|
||||||
isPausedTimersCompletionHandler = nil
|
isPausedTimersCompletionHandler = nil
|
||||||
completionHandler()
|
completionHandler()
|
||||||
}
|
}
|
||||||
stopLoading()
|
stopLoading()
|
||||||
|
disposeWebMessageChannels()
|
||||||
|
for webMessageListener in webMessageListeners {
|
||||||
|
webMessageListener.dispose()
|
||||||
|
}
|
||||||
|
webMessageListeners.removeAll()
|
||||||
if windowId == nil {
|
if windowId == nil {
|
||||||
configuration.userContentController.removeAllPluginScriptMessageHandlers()
|
configuration.userContentController.removeAllPluginScriptMessageHandlers()
|
||||||
configuration.userContentController.removeScriptMessageHandler(forName: "onCallAsyncJavaScriptResultBelowIOS14Received")
|
configuration.userContentController.removeScriptMessageHandler(forName: "onCallAsyncJavaScriptResultBelowIOS14Received")
|
||||||
|
configuration.userContentController.removeScriptMessageHandler(forName: "onWebMessagePortMessageReceived")
|
||||||
|
configuration.userContentController.removeScriptMessageHandler(forName: "onWebMessageListenerPostMessageReceived")
|
||||||
configuration.userContentController.removeAllUserScripts()
|
configuration.userContentController.removeAllUserScripts()
|
||||||
if #available(iOS 11.0, *) {
|
if #available(iOS 11.0, *) {
|
||||||
configuration.userContentController.removeAllContentRuleLists()
|
configuration.userContentController.removeAllContentRuleLists()
|
||||||
|
|
|
@ -474,6 +474,57 @@ public class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
|
||||||
result(false)
|
result(false)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
case "createWebMessageChannel":
|
||||||
|
if let webView = webView {
|
||||||
|
let _ = webView.createWebMessageChannel { (webMessageChannel) in
|
||||||
|
result(webMessageChannel.toMap())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result(nil)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "postWebMessage":
|
||||||
|
if let webView = webView {
|
||||||
|
let message = arguments!["message"] as! [String: Any?]
|
||||||
|
let targetOrigin = arguments!["targetOrigin"] as! String
|
||||||
|
|
||||||
|
var ports: [WebMessagePort] = []
|
||||||
|
let portsMap = message["ports"] as? [[String: Any?]]
|
||||||
|
if let portsMap = portsMap {
|
||||||
|
for portMap in portsMap {
|
||||||
|
let webMessageChannelId = portMap["webMessageChannelId"] as! String
|
||||||
|
let index = portMap["index"] as! Int
|
||||||
|
if let webMessageChannel = webView.webMessageChannels[webMessageChannelId] {
|
||||||
|
ports.append(webMessageChannel.ports[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let webMessage = WebMessage(data: message["data"] as? String, ports: ports)
|
||||||
|
do {
|
||||||
|
try webView.postWebMessage(message: webMessage, targetOrigin: targetOrigin) { (_) in
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
} catch let error as NSError {
|
||||||
|
result(FlutterError(code: "InAppWebViewMethodHandler", message: error.domain, details: nil))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result(false)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "addWebMessageListener":
|
||||||
|
if let webView = webView {
|
||||||
|
let webMessageListenerMap = arguments!["webMessageListener"] as! [String: Any?]
|
||||||
|
let webMessageListener = WebMessageListener.fromMap(map: webMessageListenerMap)!
|
||||||
|
do {
|
||||||
|
try webView.addWebMessageListener(webMessageListener: webMessageListener)
|
||||||
|
result(false)
|
||||||
|
} catch let error as NSError {
|
||||||
|
result(FlutterError(code: "InAppWebViewMethodHandler", message: error.domain, details: nil))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result(false)
|
||||||
|
}
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
result(FlutterMethodNotImplemented)
|
result(FlutterMethodNotImplemented)
|
||||||
break
|
break
|
||||||
|
|
|
@ -20,6 +20,7 @@ let JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT = PluginScript(
|
||||||
|
|
||||||
let JAVASCRIPT_BRIDGE_JS_SOURCE = """
|
let JAVASCRIPT_BRIDGE_JS_SOURCE = """
|
||||||
window.\(JAVASCRIPT_BRIDGE_NAME) = {};
|
window.\(JAVASCRIPT_BRIDGE_NAME) = {};
|
||||||
|
\(WEB_MESSAGE_CHANNELS_VARIABLE_NAME) = {};
|
||||||
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
|
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
|
||||||
var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE);
|
var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE);
|
||||||
var _callHandlerID = setTimeout(function(){});
|
var _callHandlerID = setTimeout(function(){});
|
||||||
|
@ -28,6 +29,7 @@ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
|
||||||
window.\(JAVASCRIPT_BRIDGE_NAME)[_callHandlerID] = resolve;
|
window.\(JAVASCRIPT_BRIDGE_NAME)[_callHandlerID] = resolve;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
\(WEB_MESSAGE_LISTENER_JS_SOURCE)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
let PLATFORM_READY_JS_SOURCE = "window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady'));";
|
let PLATFORM_READY_JS_SOURCE = "window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady'));";
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
//
|
||||||
|
// WebMessageChannelJS.swift
|
||||||
|
// flutter_inappwebview
|
||||||
|
//
|
||||||
|
// Created by Lorenzo Pichilli on 10/03/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let WEB_MESSAGE_CHANNELS_VARIABLE_NAME = "window.\(JAVASCRIPT_BRIDGE_NAME)._webMessageChannels"
|
|
@ -0,0 +1,112 @@
|
||||||
|
//
|
||||||
|
// WebMessageListenerJS.swift
|
||||||
|
// flutter_inappwebview
|
||||||
|
//
|
||||||
|
// Created by Lorenzo Pichilli on 10/03/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let WEB_MESSAGE_LISTENER_JS_SOURCE = """
|
||||||
|
function FlutterInAppWebViewWebMessageListener(jsObjectName) {
|
||||||
|
this.jsObjectName = jsObjectName;
|
||||||
|
this.listeners = [];
|
||||||
|
this.onmessage = null;
|
||||||
|
}
|
||||||
|
FlutterInAppWebViewWebMessageListener.prototype.postMessage = function(message) {
|
||||||
|
window.webkit.messageHandlers['onWebMessageListenerPostMessageReceived'].postMessage({jsObjectName: this.jsObjectName, message: message});
|
||||||
|
};
|
||||||
|
FlutterInAppWebViewWebMessageListener.prototype.addEventListener = function(type, listener) {
|
||||||
|
if (listener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.listeners.push(listener);
|
||||||
|
};
|
||||||
|
FlutterInAppWebViewWebMessageListener.prototype.removeEventListener = function(type, listener) {
|
||||||
|
if (listener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var index = this.listeners.indexOf(listener);
|
||||||
|
if (index >= 0) {
|
||||||
|
this.listeners.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.\(JAVASCRIPT_BRIDGE_NAME)._normalizeIPv6 = function(ip_string) {
|
||||||
|
// replace ipv4 address if any
|
||||||
|
var ipv4 = ip_string.match(/(.*:)([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)/);
|
||||||
|
if (ipv4) {
|
||||||
|
var ip_string = ipv4[1];
|
||||||
|
ipv4 = ipv4[2].match(/[0-9]+/g);
|
||||||
|
for (var i = 0;i < 4;i ++) {
|
||||||
|
var byte = parseInt(ipv4[i],10);
|
||||||
|
ipv4[i] = ("0" + byte.toString(16)).substr(-2);
|
||||||
|
}
|
||||||
|
ip_string += ipv4[0] + ipv4[1] + ':' + ipv4[2] + ipv4[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// take care of leading and trailing ::
|
||||||
|
ip_string = ip_string.replace(/^:|:$/g, '');
|
||||||
|
|
||||||
|
var ipv6 = ip_string.split(':');
|
||||||
|
|
||||||
|
for (var i = 0; i < ipv6.length; i ++) {
|
||||||
|
var hex = ipv6[i];
|
||||||
|
if (hex != "") {
|
||||||
|
// normalize leading zeros
|
||||||
|
ipv6[i] = ("0000" + hex).substr(-4);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// normalize grouped zeros ::
|
||||||
|
hex = [];
|
||||||
|
for (var j = ipv6.length; j <= 8; j ++) {
|
||||||
|
hex.push('0000');
|
||||||
|
}
|
||||||
|
ipv6[i] = hex.join(':');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv6.join(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.\(JAVASCRIPT_BRIDGE_NAME)._isOriginAllowed = function(allowedOriginRules, scheme, host, port) {
|
||||||
|
for (var rule of allowedOriginRules) {
|
||||||
|
if (rule === "*") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (scheme == null || scheme === "") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((scheme == null || scheme === "") && (host == null || host === "") && (port === 0 || port === "" || port == null)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var rulePort = rule.port == null || rule.port === 0 ? (rule.scheme == "https" ? 443 : 80) : rule.port;
|
||||||
|
var currentPort = port === 0 || port === "" || port == null ? (scheme == "https" ? 443 : 80) : port;
|
||||||
|
var IPv6 = null;
|
||||||
|
if (rule.host != null && rule.host[0] === "[") {
|
||||||
|
try {
|
||||||
|
IPv6 = normalizeIPv6(rule.host.substring(1, rule.host.length - 1));
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
var hostIPv6 = null;
|
||||||
|
try {
|
||||||
|
hostIPv6 = normalizeIPv6(host);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
var schemeAllowed = scheme == rule.scheme;
|
||||||
|
|
||||||
|
var hostAllowed = rule.host == null ||
|
||||||
|
rule.host === "" ||
|
||||||
|
host === rule.host ||
|
||||||
|
(rule.host[0] === "*" && host != null && host.indexOf(rule.host.split("*")[1]) >= 0) ||
|
||||||
|
(hostIPv6 != null && IPv6 != null && hostIPv6 === IPv6);
|
||||||
|
|
||||||
|
var portAllowed = rulePort === currentPort
|
||||||
|
|
||||||
|
if (schemeAllowed && hostAllowed && portAllowed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
"""
|
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// WebMessage.swift
|
||||||
|
// flutter_inappwebview
|
||||||
|
//
|
||||||
|
// Created by Lorenzo Pichilli on 10/03/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class WebMessage : NSObject {
|
||||||
|
var data: String?
|
||||||
|
var ports: [WebMessagePort]?
|
||||||
|
|
||||||
|
public init(data: String?, ports: [WebMessagePort]?) {
|
||||||
|
super.init()
|
||||||
|
self.data = data
|
||||||
|
self.ports = ports
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
ports?.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
print("WebMessage - dealloc")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
//
|
||||||
|
// WebMessageChannel.swift
|
||||||
|
// flutter_inappwebview
|
||||||
|
//
|
||||||
|
// Created by Lorenzo Pichilli on 10/03/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class WebMessageChannel : FlutterMethodCallDelegate {
|
||||||
|
var id: String
|
||||||
|
var channel: FlutterMethodChannel?
|
||||||
|
var webView: InAppWebView?
|
||||||
|
var ports: [WebMessagePort] = []
|
||||||
|
|
||||||
|
public init(id: String) {
|
||||||
|
self.id = id
|
||||||
|
super.init()
|
||||||
|
self.channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappwebview_web_message_channel_" + id,
|
||||||
|
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
|
||||||
|
self.channel?.setMethodCallHandler(self.handle)
|
||||||
|
self.ports = [
|
||||||
|
WebMessagePort(name: "port1", webMessageChannel: self),
|
||||||
|
WebMessagePort(name: "port2", webMessageChannel: self)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func initJsInstance(webView: InAppWebView, completionHandler: ((WebMessageChannel) -> Void)? = nil) {
|
||||||
|
self.webView = webView
|
||||||
|
if let webView = self.webView {
|
||||||
|
webView.evaluateJavascript(source: """
|
||||||
|
(function() {
|
||||||
|
\(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"] = new MessageChannel();
|
||||||
|
})();
|
||||||
|
""") { (_) in
|
||||||
|
completionHandler?(self)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completionHandler?(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
let arguments = call.arguments as? NSDictionary
|
||||||
|
|
||||||
|
switch call.method {
|
||||||
|
case "setWebMessageCallback":
|
||||||
|
if let _ = webView, ports.count > 0 {
|
||||||
|
let index = arguments!["index"] as! Int
|
||||||
|
let port = ports[index]
|
||||||
|
do {
|
||||||
|
try port.setWebMessageCallback { (_) in
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
} catch let error as NSError {
|
||||||
|
result(FlutterError(code: "WebMessageChannel", message: error.domain, details: nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "postMessage":
|
||||||
|
if let webView = webView, ports.count > 0 {
|
||||||
|
let index = arguments!["index"] as! Int
|
||||||
|
let port = ports[index]
|
||||||
|
let message = arguments!["message"] as! [String: Any?]
|
||||||
|
|
||||||
|
var webMessagePorts: [WebMessagePort] = []
|
||||||
|
let portsMap = message["ports"] as? [[String: Any?]]
|
||||||
|
if let portsMap = portsMap {
|
||||||
|
for portMap in portsMap {
|
||||||
|
let webMessageChannelId = portMap["webMessageChannelId"] as! String
|
||||||
|
let index = portMap["index"] as! Int
|
||||||
|
if let webMessageChannel = webView.webMessageChannels[webMessageChannelId] {
|
||||||
|
webMessagePorts.append(webMessageChannel.ports[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let webMessage = WebMessage(data: message["data"] as? String, ports: webMessagePorts)
|
||||||
|
do {
|
||||||
|
try port.postMessage(message: webMessage) { (_) in
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
} catch let error as NSError {
|
||||||
|
result(FlutterError(code: "WebMessageChannel", message: error.domain, details: nil))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "close":
|
||||||
|
if let _ = webView, ports.count > 0 {
|
||||||
|
let index = arguments!["index"] as! Int
|
||||||
|
let port = ports[index]
|
||||||
|
do {
|
||||||
|
try port.close { (_) in
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
} catch let error as NSError {
|
||||||
|
result(FlutterError(code: "WebMessageChannel", message: error.domain, details: nil))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
result(FlutterMethodNotImplemented)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onMessage(index: Int64, message: String?) {
|
||||||
|
let arguments: [String:Any?] = [
|
||||||
|
"index": index,
|
||||||
|
"message": message
|
||||||
|
]
|
||||||
|
channel?.invokeMethod("onMessage", arguments: arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func toMap () -> [String:Any?] {
|
||||||
|
return [
|
||||||
|
"id": id
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
channel?.setMethodCallHandler(nil)
|
||||||
|
for port in ports {
|
||||||
|
port.dispose()
|
||||||
|
}
|
||||||
|
ports.removeAll()
|
||||||
|
webView?.evaluateJavascript(source: """
|
||||||
|
(function() {
|
||||||
|
var webMessageChannel = \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"];
|
||||||
|
if (webMessageChannel != null) {
|
||||||
|
webMessageChannel.port1.close();
|
||||||
|
webMessageChannel.port2.close();
|
||||||
|
delete \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"];
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
""")
|
||||||
|
channel = nil
|
||||||
|
webView = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
print("WebMessageChannel - dealloc")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,226 @@
|
||||||
|
//
|
||||||
|
// WebMessageListener.swift
|
||||||
|
// flutter_inappwebview
|
||||||
|
//
|
||||||
|
// Created by Lorenzo Pichilli on 10/03/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import WebKit
|
||||||
|
|
||||||
|
public class WebMessageListener : FlutterMethodCallDelegate {
|
||||||
|
|
||||||
|
var jsObjectName: String
|
||||||
|
var allowedOriginRules: Set<String>
|
||||||
|
var channel: FlutterMethodChannel?
|
||||||
|
var webView: InAppWebView?
|
||||||
|
|
||||||
|
public init(jsObjectName: String, allowedOriginRules: Set<String>) {
|
||||||
|
self.jsObjectName = jsObjectName
|
||||||
|
self.allowedOriginRules = allowedOriginRules
|
||||||
|
super.init()
|
||||||
|
self.channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappwebview_web_message_listener_" + self.jsObjectName,
|
||||||
|
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
|
||||||
|
self.channel?.setMethodCallHandler(self.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func assertOriginRulesValid() throws {
|
||||||
|
for (index, originRule) in allowedOriginRules.enumerated() {
|
||||||
|
if originRule.isEmpty {
|
||||||
|
throw NSError(domain: "allowedOriginRules[\(index)] is empty", code: 0)
|
||||||
|
}
|
||||||
|
if originRule == "*" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if let url = URL(string: originRule) {
|
||||||
|
guard let scheme = url.scheme else {
|
||||||
|
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||||
|
}
|
||||||
|
if scheme == "http" || scheme == "https", url.host == nil || url.host!.isEmpty {
|
||||||
|
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||||
|
}
|
||||||
|
if scheme != "http", scheme != "https", url.host != nil || url.port != nil {
|
||||||
|
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||||
|
}
|
||||||
|
if url.host == nil || url.host!.isEmpty, url.port != nil {
|
||||||
|
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||||
|
}
|
||||||
|
if !url.path.isEmpty {
|
||||||
|
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||||
|
}
|
||||||
|
if let hostname = url.host {
|
||||||
|
if let firstIndex = hostname.firstIndex(of: "*") {
|
||||||
|
let distance = hostname.distance(from: hostname.startIndex, to: firstIndex)
|
||||||
|
if distance != 0 || (distance == 0 && hostname.prefix(2) != "*.") {
|
||||||
|
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hostname.hasPrefix("[") {
|
||||||
|
if !hostname.hasSuffix("]") {
|
||||||
|
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||||
|
}
|
||||||
|
let fromIndex = hostname.index(hostname.startIndex, offsetBy: 1)
|
||||||
|
let toIndex = hostname.index(hostname.startIndex, offsetBy: hostname.count - 1)
|
||||||
|
let indexRange = Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))
|
||||||
|
let ipv6 = String(hostname[indexRange])
|
||||||
|
if !Util.isIPv6(address: ipv6) {
|
||||||
|
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func initJsInstance(webView: InAppWebView) {
|
||||||
|
self.webView = webView
|
||||||
|
if let webView = self.webView {
|
||||||
|
let jsObjectNameEscaped = jsObjectName.replacingOccurrences(of: "\'", with: "\\'")
|
||||||
|
let allowedOriginRulesString = allowedOriginRules.map { (allowedOriginRule) -> String in
|
||||||
|
if allowedOriginRule == "*" {
|
||||||
|
return "'*'"
|
||||||
|
}
|
||||||
|
let rule = URL(string: allowedOriginRule)!
|
||||||
|
return """
|
||||||
|
{scheme: '\(rule.scheme!)', host: '\(rule.host?.replacingOccurrences(of: "\'", with: "\\'") ?? "null")', port: \(rule.port != nil ? String(rule.port!) : "null")}
|
||||||
|
"""
|
||||||
|
}.joined(separator: ", ")
|
||||||
|
let source = """
|
||||||
|
(function() {
|
||||||
|
var allowedOriginRules = [\(allowedOriginRulesString)];
|
||||||
|
var isPageBlank = window.location.href === "about:blank";
|
||||||
|
var scheme = !isPageBlank ? window.location.protocol.replace(":", "") : null;
|
||||||
|
var host = !isPageBlank ? window.location.hostname : null;
|
||||||
|
var port = !isPageBlank ? window.location.port : null;
|
||||||
|
if (window.\(JAVASCRIPT_BRIDGE_NAME)._isOriginAllowed(allowedOriginRules, scheme, host, port)) {
|
||||||
|
window['\(jsObjectNameEscaped)'] = new FlutterInAppWebViewWebMessageListener('\(jsObjectNameEscaped)');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
"""
|
||||||
|
webView.configuration.userContentController.addPluginScript(PluginScript(
|
||||||
|
groupName: "WebMessageListener-" + jsObjectName,
|
||||||
|
source: source,
|
||||||
|
injectionTime: .atDocumentStart,
|
||||||
|
forMainFrameOnly: false,
|
||||||
|
requiredInAllContentWorlds: false,
|
||||||
|
messageHandlerNames: ["onWebMessageListenerPostMessageReceived"]
|
||||||
|
))
|
||||||
|
webView.configuration.userContentController.sync(scriptMessageHandler: webView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func fromMap(map: [String:Any?]?) -> WebMessageListener? {
|
||||||
|
guard let map = map else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return WebMessageListener(
|
||||||
|
jsObjectName: map["jsObjectName"] as! String,
|
||||||
|
allowedOriginRules: Set(map["allowedOriginRules"] as! [String])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
let arguments = call.arguments as? NSDictionary
|
||||||
|
|
||||||
|
switch call.method {
|
||||||
|
case "postMessage":
|
||||||
|
if let webView = webView {
|
||||||
|
let jsObjectNameEscaped = jsObjectName.replacingOccurrences(of: "\'", with: "\\'")
|
||||||
|
let messageEscaped = (arguments!["message"] as! String).replacingOccurrences(of: "\'", with: "\\'")
|
||||||
|
let source = """
|
||||||
|
(function() {
|
||||||
|
var webMessageListener = window['\(jsObjectNameEscaped)'];
|
||||||
|
if (webMessageListener != null) {
|
||||||
|
var event = {data: '\(messageEscaped)'};
|
||||||
|
if (webMessageListener.onmessage != null) {
|
||||||
|
webMessageListener.onmessage(event);
|
||||||
|
} else {
|
||||||
|
for (var listener of webMessageListener.listeners) {
|
||||||
|
listener(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
"""
|
||||||
|
webView.evaluateJavascript(source: source) { (_) in
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
result(FlutterMethodNotImplemented)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func isOriginAllowed(scheme: String?, host: String?, port: Int?) -> Bool {
|
||||||
|
for allowedOriginRule in allowedOriginRules {
|
||||||
|
if allowedOriginRule == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if scheme == nil || scheme!.isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if scheme == nil || scheme!.isEmpty, host == nil || host!.isEmpty, port == nil || port == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if let rule = URL(string: allowedOriginRule) {
|
||||||
|
let rulePort = rule.port == nil || rule.port == 0 ? (rule.scheme == "https" ? 443 : 80) : rule.port!
|
||||||
|
let currentPort = port == nil || port == 0 ? (scheme == "https" ? 443 : 80) : port!
|
||||||
|
var IPv6: String? = nil
|
||||||
|
if let hostname = rule.host, hostname.hasPrefix("[") {
|
||||||
|
let fromIndex = hostname.index(hostname.startIndex, offsetBy: 1)
|
||||||
|
let toIndex = hostname.index(hostname.startIndex, offsetBy: hostname.count - 1)
|
||||||
|
let indexRange = Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))
|
||||||
|
do {
|
||||||
|
IPv6 = try Util.normalizeIPv6(address: String(hostname[indexRange]))
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
var hostIPv6: String? = nil
|
||||||
|
if let host = host, Util.isIPv6(address: host) {
|
||||||
|
do {
|
||||||
|
hostIPv6 = try Util.normalizeIPv6(address: host)
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schemeAllowed = scheme != nil && !scheme!.isEmpty && scheme == rule.scheme
|
||||||
|
|
||||||
|
let hostAllowed = rule.host == nil ||
|
||||||
|
rule.host!.isEmpty ||
|
||||||
|
host == rule.host ||
|
||||||
|
(rule.host!.hasPrefix("*") && host != nil && host!.hasSuffix(rule.host!.split(separator: "*", omittingEmptySubsequences: false)[1])) ||
|
||||||
|
(hostIPv6 != nil && IPv6 != nil && hostIPv6 == IPv6)
|
||||||
|
|
||||||
|
let portAllowed = rulePort == currentPort
|
||||||
|
|
||||||
|
if schemeAllowed, hostAllowed, portAllowed {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onPostMessage(message: String?, sourceOrigin: URL?, isMainFrame: Bool) {
|
||||||
|
let arguments: [String:Any?] = [
|
||||||
|
"message": message,
|
||||||
|
"sourceOrigin": sourceOrigin?.absoluteString,
|
||||||
|
"isMainFrame": isMainFrame
|
||||||
|
]
|
||||||
|
channel?.invokeMethod("onPostMessage", arguments: arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
channel?.setMethodCallHandler(nil)
|
||||||
|
channel = nil
|
||||||
|
webView = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
print("WebMessageListener - dealloc")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
//
|
||||||
|
// WebMessagePort.swift
|
||||||
|
// flutter_inappwebview
|
||||||
|
//
|
||||||
|
// Created by Lorenzo Pichilli on 10/03/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class WebMessagePort : NSObject {
|
||||||
|
var name: String
|
||||||
|
var webMessageChannel: WebMessageChannel?
|
||||||
|
var isClosed = false
|
||||||
|
var isTransferred = false
|
||||||
|
var isStarted = false
|
||||||
|
|
||||||
|
public init(name: String, webMessageChannel: WebMessageChannel) {
|
||||||
|
self.name = name
|
||||||
|
super.init()
|
||||||
|
self.webMessageChannel = webMessageChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
public func setWebMessageCallback(completionHandler: ((Any?) -> Void)? = nil) throws {
|
||||||
|
if isClosed || isTransferred {
|
||||||
|
throw NSError(domain: "Port is already closed or transferred", code: 0)
|
||||||
|
}
|
||||||
|
self.isStarted = true
|
||||||
|
if let webMessageChannel = webMessageChannel, let webView = webMessageChannel.webView {
|
||||||
|
let index = name == "port1" ? 0 : 1
|
||||||
|
webView.evaluateJavascript(source: """
|
||||||
|
(function() {
|
||||||
|
var webMessageChannel = \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(webMessageChannel.id)"];
|
||||||
|
if (webMessageChannel != null) {
|
||||||
|
webMessageChannel.\(self.name).onmessage = function (event) {
|
||||||
|
window.webkit.messageHandlers["onWebMessagePortMessageReceived"].postMessage({
|
||||||
|
"webMessageChannelId": "\(webMessageChannel.id)",
|
||||||
|
"index": \(String(index)),
|
||||||
|
"message": event.data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
""") { (_) in
|
||||||
|
completionHandler?(nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completionHandler?(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func postMessage(message: WebMessage, completionHandler: ((Any?) -> Void)? = nil) throws {
|
||||||
|
if isClosed || isTransferred {
|
||||||
|
throw NSError(domain: "Port is already closed or transferred", code: 0)
|
||||||
|
}
|
||||||
|
if let webMessageChannel = webMessageChannel, let webView = webMessageChannel.webView {
|
||||||
|
var portsString = "null"
|
||||||
|
if let ports = message.ports {
|
||||||
|
var portArrayString: [String] = []
|
||||||
|
for port in ports {
|
||||||
|
if port == self {
|
||||||
|
throw NSError(domain: "Source port cannot be transferred", code: 0)
|
||||||
|
}
|
||||||
|
if port.isStarted {
|
||||||
|
throw NSError(domain: "Port is already started", code: 0)
|
||||||
|
}
|
||||||
|
if port.isClosed || port.isTransferred {
|
||||||
|
throw NSError(domain: "Port is already closed or transferred", code: 0)
|
||||||
|
}
|
||||||
|
port.isTransferred = true
|
||||||
|
portArrayString.append("\(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)['\(port.webMessageChannel!.id)'].\(port.name)")
|
||||||
|
}
|
||||||
|
portsString = "[" + portArrayString.joined(separator: ", ") + "]"
|
||||||
|
}
|
||||||
|
let data = message.data?.replacingOccurrences(of: "\'", with: "\\'") ?? "null"
|
||||||
|
let source = """
|
||||||
|
(function() {
|
||||||
|
var webMessageChannel = \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(webMessageChannel.id)"];
|
||||||
|
if (webMessageChannel != null) {
|
||||||
|
webMessageChannel.\(self.name).postMessage('\(data)', \(portsString));
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
"""
|
||||||
|
webView.evaluateJavascript(source: source) { (_) in
|
||||||
|
completionHandler?(nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completionHandler?(nil)
|
||||||
|
}
|
||||||
|
message.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func close(completionHandler: ((Any?) -> Void)? = nil) throws {
|
||||||
|
if isTransferred {
|
||||||
|
throw NSError(domain: "Port is already transferred", code: 0)
|
||||||
|
}
|
||||||
|
isClosed = true
|
||||||
|
if let webMessageChannel = webMessageChannel, let webView = webMessageChannel.webView {
|
||||||
|
let source = """
|
||||||
|
(function() {
|
||||||
|
var webMessageChannel = \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(webMessageChannel.id)"];
|
||||||
|
if (webMessageChannel != null) {
|
||||||
|
webMessageChannel.\(self.name).close();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
"""
|
||||||
|
webView.evaluateJavascript(source: source) { (_) in
|
||||||
|
completionHandler?(nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completionHandler?(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
isClosed = true
|
||||||
|
webMessageChannel = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
print("WebMessagePort - dealloc")
|
||||||
|
}
|
||||||
|
}
|
|
@ -149,4 +149,78 @@ public class Util {
|
||||||
return "NORMAL"
|
return "NORMAL"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func isIPv4(address: String) -> Bool {
|
||||||
|
var sin = sockaddr_in()
|
||||||
|
return address.withCString({ cstring in inet_pton(AF_INET, cstring, &sin.sin_addr) }) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func isIPv6(address: String) -> Bool {
|
||||||
|
var sin6 = sockaddr_in6()
|
||||||
|
return address.withCString({ cstring in inet_pton(AF_INET6, cstring, &sin6.sin6_addr) }) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func isIpAddress(address: String) -> Bool {
|
||||||
|
return Util.isIPv6(address: address) || Util.isIPv4(address: address)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func normalizeIPv6(address: String) throws -> String {
|
||||||
|
if !Util.isIPv6(address: address) {
|
||||||
|
throw NSError(domain: "Invalid address: \(address)", code: 0)
|
||||||
|
}
|
||||||
|
var ipString = address
|
||||||
|
// replace ipv4 address if any
|
||||||
|
let ipv4Regex = try! NSRegularExpression(pattern: "(.*:)([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)")
|
||||||
|
if let match = ipv4Regex.firstMatch(in: address, options: [], range: NSRange(location: 0, length: address.utf16.count)) {
|
||||||
|
if let ipv6PartRange = Range(match.range(at: 1), in: address) {
|
||||||
|
ipString = String(address[ipv6PartRange])
|
||||||
|
}
|
||||||
|
if let ipv4Range = Range(match.range(at: 2), in: address) {
|
||||||
|
let ipv4 = address[ipv4Range]
|
||||||
|
let ipv4Splitted = ipv4.split(separator: ".")
|
||||||
|
var ipv4Converted = Array(repeating: "0000", count: 4)
|
||||||
|
for i in 0...3 {
|
||||||
|
let byte = Int(ipv4Splitted[i])!
|
||||||
|
let hex = ("0" + String(byte, radix: 16))
|
||||||
|
var offset = hex.count - 3
|
||||||
|
offset = offset < 0 ? 0 : offset
|
||||||
|
let fromIndex = hex.index(hex.startIndex, offsetBy: offset)
|
||||||
|
let toIndex = hex.index(hex.startIndex, offsetBy: hex.count - 1)
|
||||||
|
let indexRange = Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))
|
||||||
|
ipv4Converted[i] = String(hex[indexRange])
|
||||||
|
}
|
||||||
|
ipString += ipv4Converted[0] + ipv4Converted[1] + ":" + ipv4Converted[2] + ipv4Converted[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// take care of leading and trailing ::
|
||||||
|
let regex = try! NSRegularExpression(pattern: "^:|:$")
|
||||||
|
ipString = regex.stringByReplacingMatches(in: ipString, options: [], range: NSRange(location: 0, length: ipString.count), withTemplate: "")
|
||||||
|
|
||||||
|
let ipv6 = ipString.split(separator: ":", omittingEmptySubsequences: false)
|
||||||
|
var fullIPv6 = Array(repeating: "0000", count: ipv6.count)
|
||||||
|
|
||||||
|
for (i, hex) in ipv6.enumerated() {
|
||||||
|
if !hex.isEmpty {
|
||||||
|
// normalize leading zeros
|
||||||
|
let hexString = String("0000" + hex)
|
||||||
|
var offset = hexString.count - 5
|
||||||
|
offset = offset < 0 ? 0 : offset
|
||||||
|
let fromIndex = hexString.index(hexString.startIndex, offsetBy: offset)
|
||||||
|
let toIndex = hexString.index(hexString.startIndex, offsetBy: hexString.count - 1)
|
||||||
|
let indexRange = Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))
|
||||||
|
fullIPv6[i] = String(hexString[indexRange])
|
||||||
|
} else {
|
||||||
|
// normalize grouped zeros ::
|
||||||
|
var zeros: [String] = []
|
||||||
|
for j in ipv6.count...8 {
|
||||||
|
zeros.append("0000")
|
||||||
|
}
|
||||||
|
fullIPv6[i] = zeros.joined(separator: ":")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullIPv6.joined(separator: ":")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ class ChromeSafariBrowser {
|
||||||
const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser');
|
const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser');
|
||||||
|
|
||||||
ChromeSafariBrowser() {
|
ChromeSafariBrowser() {
|
||||||
id = ViewIdGenerator.generateId();
|
id = IdGenerator.generate();
|
||||||
this._channel =
|
this._channel =
|
||||||
MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser_$id');
|
MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser_$id');
|
||||||
this._channel.setMethodCallHandler(handleMethod);
|
this._channel.setMethodCallHandler(handleMethod);
|
||||||
|
|
|
@ -66,7 +66,7 @@ class InAppBrowser {
|
||||||
|
|
||||||
///
|
///
|
||||||
InAppBrowser({this.windowId, this.initialUserScripts}) {
|
InAppBrowser({this.windowId, this.initialUserScripts}) {
|
||||||
id = ViewIdGenerator.generateId();
|
id = IdGenerator.generate();
|
||||||
this._channel =
|
this._channel =
|
||||||
MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$id');
|
MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$id');
|
||||||
this._channel.setMethodCallHandler(handleMethod);
|
this._channel.setMethodCallHandler(handleMethod);
|
||||||
|
|
|
@ -176,7 +176,7 @@ class AndroidInAppWebViewController {
|
||||||
///has loaded WebView will be killed.
|
///has loaded WebView will be killed.
|
||||||
///The next time the app starts and loads WebView it will use the new WebView package instead.
|
///The next time the app starts and loads WebView it will use the new WebView package instead.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on Android 26+.
|
///**NOTE**: available only on Android 21+.
|
||||||
///
|
///
|
||||||
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewCompat#getCurrentWebViewPackage(android.content.Context)
|
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewCompat#getCurrentWebViewPackage(android.content.Context)
|
||||||
static Future<AndroidWebViewPackageInfo?> getCurrentWebViewPackage() async {
|
static Future<AndroidWebViewPackageInfo?> getCurrentWebViewPackage() async {
|
||||||
|
|
|
@ -91,7 +91,7 @@ class HeadlessInAppWebView implements WebView {
|
||||||
this.contextMenu,
|
this.contextMenu,
|
||||||
this.initialUserScripts,
|
this.initialUserScripts,
|
||||||
this.pullToRefreshController}) {
|
this.pullToRefreshController}) {
|
||||||
id = ViewIdGenerator.generateId();
|
id = IdGenerator.generate();
|
||||||
webViewController = new InAppWebViewController(id, this);
|
webViewController = new InAppWebViewController(id, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@ import '../types.dart';
|
||||||
import '../in_app_browser/in_app_browser.dart';
|
import '../in_app_browser/in_app_browser.dart';
|
||||||
import '../web_storage/web_storage.dart';
|
import '../web_storage/web_storage.dart';
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
|
import '../web_message/web_message_channel.dart';
|
||||||
|
import '../web_message/web_message_listener.dart';
|
||||||
|
|
||||||
import 'headless_in_app_webview.dart';
|
import 'headless_in_app_webview.dart';
|
||||||
import 'in_app_webview.dart';
|
import 'in_app_webview.dart';
|
||||||
|
@ -51,6 +53,7 @@ class InAppWebViewController {
|
||||||
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
|
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
|
||||||
HashMap<String, JavaScriptHandlerCallback>();
|
HashMap<String, JavaScriptHandlerCallback>();
|
||||||
List<UserScript> _userScripts = [];
|
List<UserScript> _userScripts = [];
|
||||||
|
Set<String> _webMessageListenerObjNames = Set();
|
||||||
|
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
dynamic _id;
|
dynamic _id;
|
||||||
|
@ -1455,7 +1458,7 @@ class InAppWebViewController {
|
||||||
void addJavaScriptHandler(
|
void addJavaScriptHandler(
|
||||||
{required String handlerName,
|
{required String handlerName,
|
||||||
required JavaScriptHandlerCallback callback}) {
|
required JavaScriptHandlerCallback callback}) {
|
||||||
assert(!_JAVASCRIPT_HANDLER_FORBIDDEN_NAMES.contains(handlerName));
|
assert(!_JAVASCRIPT_HANDLER_FORBIDDEN_NAMES.contains(handlerName), '"$handlerName" is a forbidden name!');
|
||||||
this.javaScriptHandlersMap[handlerName] = (callback);
|
this.javaScriptHandlersMap[handlerName] = (callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1467,7 +1470,7 @@ class InAppWebViewController {
|
||||||
return this.javaScriptHandlersMap.remove(handlerName);
|
return this.javaScriptHandlersMap.remove(handlerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
///Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a [Uint8List]. Returns `null` if it wasn't be able to take it.
|
///Takes a screenshot of the WebView's visible viewport and returns a [Uint8List]. Returns `null` if it wasn't be able to take it.
|
||||||
///
|
///
|
||||||
///[screenshotConfiguration] represents the configuration data to use when generating an image from a web view’s contents.
|
///[screenshotConfiguration] represents the configuration data to use when generating an image from a web view’s contents.
|
||||||
///
|
///
|
||||||
|
@ -2087,6 +2090,32 @@ class InAppWebViewController {
|
||||||
return await _channel.invokeMethod('isSecureContext', args);
|
return await _channel.invokeMethod('isSecureContext', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<WebMessageChannel?> createWebMessageChannel() async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
Map<String, dynamic>? result = (await _channel.invokeMethod('createWebMessageChannel', args))
|
||||||
|
?.cast<String, dynamic>();
|
||||||
|
return WebMessageChannel.fromMap(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> postWebMessage({required WebMessage message, Uri? targetOrigin}) async {
|
||||||
|
if (targetOrigin == null) {
|
||||||
|
targetOrigin = Uri.parse("");
|
||||||
|
}
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('message', () => message.toMap());
|
||||||
|
args.putIfAbsent('targetOrigin', () => targetOrigin.toString());
|
||||||
|
await _channel.invokeMethod('postWebMessage', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addWebMessageListener(WebMessageListener webMessageListener) async {
|
||||||
|
assert(!_webMessageListenerObjNames.contains(webMessageListener.jsObjectName), "jsObjectName ${webMessageListener.jsObjectName} was already added.");
|
||||||
|
_webMessageListenerObjNames.add(webMessageListener.jsObjectName);
|
||||||
|
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('webMessageListener', () => webMessageListener.toMap());
|
||||||
|
await _channel.invokeMethod('addWebMessageListener', args);
|
||||||
|
}
|
||||||
|
|
||||||
///Gets the default user agent.
|
///Gets the default user agent.
|
||||||
///
|
///
|
||||||
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebSettings#getDefaultUserAgent(android.content.Context)
|
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebSettings#getDefaultUserAgent(android.content.Context)
|
||||||
|
|
|
@ -15,3 +15,4 @@ export 'content_blocker.dart';
|
||||||
export 'http_auth_credentials_database.dart';
|
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';
|
||||||
|
|
|
@ -6147,8 +6147,10 @@ class IOSURLResponse {
|
||||||
///The name of the text encoding provided by the response’s originating source.
|
///The name of the text encoding provided by the response’s originating source.
|
||||||
String? textEncodingName;
|
String? textEncodingName;
|
||||||
|
|
||||||
|
///All HTTP header fields of the response.
|
||||||
Map<String, String>? headers;
|
Map<String, String>? headers;
|
||||||
|
|
||||||
|
///The response’s HTTP status code.
|
||||||
int? statusCode;
|
int? statusCode;
|
||||||
|
|
||||||
IOSURLResponse(
|
IOSURLResponse(
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class ViewIdGenerator {
|
class IdGenerator {
|
||||||
static int _count = 0;
|
static int _count = 0;
|
||||||
|
|
||||||
/// Math.Random()-based RNG. All platforms, fast, not cryptographically strong. Optional Seed passable.
|
/// Math.Random()-based RNG. All platforms, fast, not cryptographically strong. Optional Seed passable.
|
||||||
|
@ -31,7 +31,7 @@ class ViewIdGenerator {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String generateId() {
|
static String generate() {
|
||||||
_count++;
|
_count++;
|
||||||
return _count.toString() + cryptoRNG().map((e) => e.toString()).join('');
|
return _count.toString() + cryptoRNG().map((e) => e.toString()).join('');
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
export 'web_message_channel.dart';
|
||||||
|
export 'web_message_listener.dart';
|
|
@ -0,0 +1,115 @@
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class WebMessageChannel {
|
||||||
|
String id;
|
||||||
|
WebMessagePort port1;
|
||||||
|
WebMessagePort port2;
|
||||||
|
|
||||||
|
late MethodChannel _channel;
|
||||||
|
|
||||||
|
WebMessageChannel({required this.id, required this.port1, required this.port2}) {
|
||||||
|
this._channel = MethodChannel(
|
||||||
|
'com.pichillilorenzo/flutter_inappwebview_web_message_channel_$id');
|
||||||
|
this._channel.setMethodCallHandler(handleMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
static WebMessageChannel? fromMap(Map<String, dynamic>? map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var webMessageChannel = WebMessageChannel(
|
||||||
|
id: map["id"],
|
||||||
|
port1: WebMessagePort(index: 0),
|
||||||
|
port2: WebMessagePort(index: 1)
|
||||||
|
);
|
||||||
|
webMessageChannel.port1._webMessageChannel = webMessageChannel;
|
||||||
|
webMessageChannel.port2._webMessageChannel = webMessageChannel;
|
||||||
|
return webMessageChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> handleMethod(MethodCall call) async {
|
||||||
|
switch (call.method) {
|
||||||
|
case "onMessage":
|
||||||
|
int index = call.arguments["index"];
|
||||||
|
var port = index == 0 ? this.port1 : this.port2;
|
||||||
|
if (port._onMessage != null) {
|
||||||
|
String? message = call.arguments["message"];
|
||||||
|
port._onMessage!(message);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WebMessagePort {
|
||||||
|
late final int _index;
|
||||||
|
|
||||||
|
Function(String? message)? _onMessage;
|
||||||
|
late WebMessageChannel _webMessageChannel;
|
||||||
|
|
||||||
|
WebMessagePort({required int index}) {
|
||||||
|
this._index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setWebMessageCallback(Function(String? message)? onMessage) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('index', () => this._index);
|
||||||
|
await _webMessageChannel._channel.invokeMethod('setWebMessageCallback', args);
|
||||||
|
this._onMessage = onMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> postMessage(WebMessage message) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('index', () => this._index);
|
||||||
|
args.putIfAbsent('message', () => message.toMap());
|
||||||
|
await _webMessageChannel._channel.invokeMethod('postMessage', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> close() async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('index', () => this._index);
|
||||||
|
await _webMessageChannel._channel.invokeMethod('close', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"index": this._index,
|
||||||
|
"webMessageChannelId": this._webMessageChannel.id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return this.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return toMap().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WebMessage {
|
||||||
|
String? data;
|
||||||
|
List<WebMessagePort>? ports;
|
||||||
|
|
||||||
|
WebMessage({this.data, this.ports});
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"data": this.data,
|
||||||
|
"ports": this.ports?.map((e) => e.toMap()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return this.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return toMap().toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class WebMessageListener {
|
||||||
|
String jsObjectName;
|
||||||
|
late Set<String> allowedOriginRules;
|
||||||
|
JavaScriptReplyProxy? _replyProxy;
|
||||||
|
Function(String? message, Uri? sourceOrigin, bool isMainFrame, JavaScriptReplyProxy replyProxy)? onPostMessage;
|
||||||
|
|
||||||
|
late MethodChannel _channel;
|
||||||
|
|
||||||
|
WebMessageListener({required this.jsObjectName, Set<String>? allowedOriginRules, this.onPostMessage}) {
|
||||||
|
this.allowedOriginRules = allowedOriginRules != null ? allowedOriginRules : Set.from(["*"]);
|
||||||
|
assert(!this.allowedOriginRules.contains(""), "allowedOriginRules cannot contain empty strings");
|
||||||
|
this._channel = MethodChannel(
|
||||||
|
'com.pichillilorenzo/flutter_inappwebview_web_message_listener_$jsObjectName');
|
||||||
|
this._channel.setMethodCallHandler(handleMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> handleMethod(MethodCall call) async {
|
||||||
|
switch (call.method) {
|
||||||
|
case "onPostMessage":
|
||||||
|
if (_replyProxy == null) {
|
||||||
|
_replyProxy = new JavaScriptReplyProxy(this);
|
||||||
|
}
|
||||||
|
if (onPostMessage != null) {
|
||||||
|
String? message = call.arguments["message"];
|
||||||
|
Uri? sourceOrigin = call.arguments["sourceOrigin"] != null ? Uri.parse(call.arguments["sourceOrigin"]) : null;
|
||||||
|
bool isMainFrame = call.arguments["isMainFrame"];
|
||||||
|
onPostMessage!(message, sourceOrigin, isMainFrame, _replyProxy!);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"jsObjectName": jsObjectName,
|
||||||
|
"allowedOriginRules": allowedOriginRules.toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return this.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return toMap().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JavaScriptReplyProxy {
|
||||||
|
late WebMessageListener _webMessageListener;
|
||||||
|
|
||||||
|
JavaScriptReplyProxy(WebMessageListener webMessageListener) {
|
||||||
|
this._webMessageListener = webMessageListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> postMessage(String message) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('message', () => message);
|
||||||
|
await _webMessageListener._channel.invokeMethod('postMessage', args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,10 +10,10 @@ const appAuthBasic = express()
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
key: fs.readFileSync('server-key.pem'),
|
key: fs.readFileSync('server-key.pem'),
|
||||||
cert: fs.readFileSync('server-crt.pem'),
|
cert: fs.readFileSync('server-crt.pem'),
|
||||||
ca: fs.readFileSync('ca-crt.pem'),
|
ca: fs.readFileSync('ca-crt.pem'),
|
||||||
requestCert: true,
|
requestCert: true,
|
||||||
rejectUnauthorized: false
|
rejectUnauthorized: false
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: flutter_inappwebview
|
name: flutter_inappwebview
|
||||||
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
|
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
|
||||||
version: 5.1.0+4
|
version: 5.2.0
|
||||||
homepage: https://github.com/pichillilorenzo/flutter_inappwebview
|
homepage: https://github.com/pichillilorenzo/flutter_inappwebview
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
|
Loading…
Reference in New Issue