code refactoring, new features, replaced String url to Uri uri everywhere, added URLRequest

This commit is contained in:
Lorenzo Pichilli 2021-02-22 12:16:23 +01:00
parent de9d081af2
commit 0b0bce66aa
218 changed files with 13975 additions and 8591 deletions

772
.idea/libraries/Dart_Packages.xml generated Normal file
View File

@ -0,0 +1,772 @@
<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="device_info">
<value>
<list>
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/lib" />
</list>
</value>
</entry>
<entry key="device_info_platform_interface">
<value>
<list>
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/device_info_platform_interface-2.0.0-nullsafety.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="intl">
<value>
<list>
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/intl-0.17.0/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-1.0.0/lib" />
<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="uuid">
<value>
<list>
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/uuid-3.0.0-nullsafety.0/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/device_info-2.0.0-nullsafety.2/lib" />
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/device_info_platform_interface-2.0.0-nullsafety.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/intl-0.17.0/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/mime-1.0.0/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/uuid-3.0.0-nullsafety.0/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>

View File

@ -1,9 +1,6 @@
<component name="libraryTable">
<library name="Flutter Plugins">
<CLASSES>
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2" />
<root url="file://$PROJECT_DIR$" />
</CLASSES>
<CLASSES />
<JAVADOC />
<SOURCES />
</library>

View File

@ -5,11 +5,11 @@
- Added `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options also for iOS (also thanks to [liranhao](https://github.com/liranhao))
- Added limited cookies support on iOS below 11.0 using JavaScript
- Added `IOSCookieManager` class and `CookieManager.instance().ios.getAllCookies` iOS-specific method
- Added `UserScript`, `UserScriptInjectionTime`, `ContentWorld`, `AndroidWebViewFeature`, `AndroidServiceWorkerController`, `AndroidServiceWorkerClient`, `ScreenshotConfiguration`, `IOSWKPDFConfiguration` classes
- Added `UserScript`, `UserScriptInjectionTime`, `ContentWorld`, `AndroidWebViewFeature`, `AndroidServiceWorkerController`, `AndroidServiceWorkerClient`, `ScreenshotConfiguration`, `IOSWKPDFConfiguration`, `URLRequest` classes
- Added `initialUserScripts` WebView option
- Added `addUserScript`, `addUserScripts`, `removeUserScript`, `removeUserScripts`, `removeAllUserScripts`, `callAsyncJavaScript` WebView methods
- Added `addUserScript`, `addUserScripts`, `removeUserScript`, `removeUserScripts`, `removeUserScriptsByGroupName`, `removeAllUserScripts`, `callAsyncJavaScript`, `isSecureContext` WebView methods
- Added `contentWorld` argument to `evaluateJavascript` WebView method
- Added `isDirectionalLockEnabled`, `mediaType`, `pageZoom`, `limitsNavigationsToAppBoundDomains`, `useOnNavigationResponse`, `applePayAPIEnabled`, `allowingReadAccessTo` iOS-specific WebView options
- Added `isDirectionalLockEnabled`, `mediaType`, `pageZoom`, `limitsNavigationsToAppBoundDomains`, `useOnNavigationResponse`, `applePayAPIEnabled`, `allowingReadAccessTo`, `disableLongPressContextMenuOnLinks` iOS-specific WebView options
- Added `handlesURLScheme`, `createPdf`, `createWebArchiveData` iOS-specific WebView methods
- Added `iosOnNavigationResponse` and `iosShouldAllowDeprecatedTLS` iOS-specific WebView events
- Added `iosAnimated` optional argument to `zoomBy` WebView method
@ -18,6 +18,7 @@
- Added `cssLinkHtmlTagAttributes` optional argument to `injectCSSFileFromUrl` WebView method
- Added `iosAllowingReadAccessTo` iOS-specific optional argument to `loadUrl` WebView method
- Added new iOS-specific attributes to `ShouldOverrideUrlLoadingRequest` and `CreateWindowRequest` classes
- Added `toolbarTopTranslucent`, `toolbarTopTintColor`, `toolbarBottomTintColor`, `toolbarTopBarTintColor` ios-specific InAppBrowser options
- Updated integration tests
- Merge "Upgraded appcompat to 1.2.0-rc-02" [#465](https://github.com/pichillilorenzo/flutter_inappwebview/pull/465) (thanks to [andreidiaconu](https://github.com/andreidiaconu))
- Merge "Added missing field 'headers' which returned by WebResourceResponse.toMap()" [#490](https://github.com/pichillilorenzo/flutter_inappwebview/pull/490) (thanks to [Doflatango](https://github.com/Doflatango))
@ -47,14 +48,44 @@
- Minimum Flutter version required is `1.22.2` and Dart SDK `>=2.12.0-0 <3.0.0`
- iOS Xcode version `>= 12`
- Removed `debuggingEnabled` WebView option; on Android you should use now the `AndroidInAppWebViewController.setWebContentsDebuggingEnabled(bool debuggingEnabled)` static method; on iOS, debugging is always enabled
- `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options moved from Android-specific options to cross-platform options
- Added `callAsyncJavaScript` name to the list of javaScriptHandlerForbiddenNames
- Changed `zoomBy` WebView method signature
- Moved `saveWebArchive` WebView method from Android-specific to cross-platform
- Moved `progressBar` InAppBroswer from Android-specific option to cross-platform option and renamed to `hideProgressBar`
- Renamed `HttpAuthChallenge` to `URLAuthenticationChallenge`
- Deleted `androidOnRequestFocus` event because it is never called
- Updated `basicConstraints`, `subjectKeyIdentifier`, `authorityKeyIdentifier`, `certificatePolicies`, `cRLDistributionPoints`, `authorityInfoAccess` attributes type of `X509Certificate`
- Updated "WebView.storyboard" for InAppBrowser iOS representation
- Renamed `ShouldOverrideUrlLoadingAction` class to `NavigationActionPolicy`
- Renamed `ProtectionSpace` class to `URLProtectionSpace`
- Renamed `ProtectionSpaceHttpAuthCredentials` to `URLProtectionSpaceHttpAuthCredentials`
- Renamed `CreateWindowRequest` class to `CreateWindowAction`
- Renamed `initialUrl` to `initialUrlRequest` WebView attribute and made it of type `URLRequest`
- Renamed `toolbarTop` InAppBrowser cross-platform option to `hideToolbarTop`
- Renamed `toolbarBottom` InAppBrowser ios-specific option to `hideToolbarBottom`
- Removed `debuggingEnabled` WebView option; on Android you should use now the `AndroidInAppWebViewController.setWebContentsDebuggingEnabled(bool debuggingEnabled)` static method; on iOS, debugging is always enabled
- Removed `androidOnRequestFocus` event because it is never called
- Removed `initialHeaders` WebView attribute. Use `URLRequest.headers` attribute
- Removed `headers` argument from `loadFile` WebView method
- Removed `headers` argument from `openFile` InAppBrowser method
- Removed `headers` argument from `loadUrl` WebView method, renamed the `url` argument to `urlRequest` and made it of type `URLRequest`
- Removed `headers` argument from `openFile` InAppBrowser method
- Removed `headers` argument from `openUrl` InAppBrowser method, renamed the `url` argument to `urlRequest` and made it of type `URLRequest`
- Removed `fallback` argument from `ChromeSafariBrowser` constructor. Check for availability of `ChromeSafariBrowser` if you want show one or the other.
- Removed `scheme` argument from `onLoadResourceCustomScheme` WebView event. Use the `Uri url` parameter now.
- Removed `ShouldOverrideUrlLoadingRequest` class and replaced with `NavigationAction`
- Changed `zoomBy` WebView method signature
- Changed type of `urlFile` argument of `injectCSSFileFromUrl` WebView method to `Uri`
- Changed type of `urlFile` argument of `injectJavascriptFileFromUrl` WebView method to `Uri`
- Changed return type of `getOriginalUrl` Android-specific WebView method to `Uri`
- Changed return type of `getSafeBrowsingPrivacyPolicyUrl` Android-specific WebView method to `Uri`
- Changed type of `url` argument of `onLoadStart`, `onLoadStop`, `onLoadError`, `onLoadHttpError`, `onLoadResourceCustomScheme`, `onUpdateVisitedHistory`, `onPrint`, `onPageCommitVisible`, `androidOnSafeBrowsingHit`, `androidOnRenderProcessUnresponsive`, `androidOnRenderProcessResponsive`, `androidOnFormResubmission`, `androidOnReceivedTouchIconUrl` WebView events to `Uri`
- Changed type of `baseUrl` and `androidHistoryUrl` arguments of `loadData` WebView method and `openData` InAppBrowser method
- Changed `openUrl` InAppBrowser method to `openUrlRequest`
- Changed type of `url` argument of `openWithSystemBrowser` InAppBrowser method to `Uri`
- Changed all InAppBrowser color options type from `String` to `Color`
- Changed all ChromeSafariBrowser color options type from `String` to `Color`
- Updated attributes of `ShouldOverrideUrlLoadingRequest`, `ServerTrustChallenge` and `ClientCertChallenge` classes
- Changed type of `url` attribute to `Uri` for `JsAlertRequest`, `JsAlertConfirm`, `JsPromptRequest` classes
## 4.0.0+4

200
README.md
View File

@ -29,8 +29,8 @@ Also, check the [example/integration_test/webview_flutter_test.dart](https://git
## Articles/Resources
- [InAppWebView: The Real Power of WebViews in Flutter](https://medium.com/flutter-community/inappwebview-the-real-power-of-webviews-in-flutter-c6d52374209d?source=friends_link&sk=cb74487219bcd85e610a670ee0b447d0)
- [Creating a Full-Featured Browser using WebViews in Flutter](https://medium.com/flutter-community/creating-a-full-featured-browser-using-webviews-in-flutter-9c8f2923c574?source=friends_link&sk=55fc8267f351082aa9e73ced546f6bcb)
- [InAppWebView: The Real Power of WebViews in Flutter](https://medium.com/flutter-community/inappwebview-the-real-power-of-webviews-in-flutter-c6d52374209d?source=friends_link&sk=cb74487219bcd85e610a670ee0b447d0) (valid for plugin version 4.0.0)
- [Creating a Full-Featured Browser using WebViews in Flutter](https://medium.com/flutter-community/creating-a-full-featured-browser-using-webviews-in-flutter-9c8f2923c574?source=friends_link&sk=55fc8267f351082aa9e73ced546f6bcb) (valid for plugin version 4.0.0)
- [Flutter Browser App: A Full-Featured Mobile Browser App (such as the Google Chrome mobile browser) created using Flutter and the features offered by the flutter_inappwebview plugin](https://github.com/pichillilorenzo/flutter_browser_app)
## Requirements
@ -323,24 +323,31 @@ class _MyAppState extends State<MyApp> {
decoration:
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialUrlRequest: URLRequest(
url: Uri.parse("https://flutter.dev/")
),
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
crossPlatform: InAppWebViewOptions(
)
),
ios: IOSInAppWebViewOptions(
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (controller, url) {
setState(() {
this.url = url ?? '';
this.url = url?.toString() ?? '';
});
},
onLoadStop: (controller, url) async {
setState(() {
this.url = url ?? '';
this.url = url?.toString() ?? '';
});
},
onProgressChanged: (controller, progress) {
@ -354,19 +361,19 @@ class _MyAppState extends State<MyApp> {
ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
ElevatedButton(
child: Icon(Icons.arrow_back),
onPressed: () {
webView?.goBack();
},
),
RaisedButton(
ElevatedButton(
child: Icon(Icons.arrow_forward),
onPressed: () {
webView?.goForward();
},
),
RaisedButton(
ElevatedButton(
child: Icon(Icons.refresh),
onPressed: () {
webView?.reload();
@ -395,8 +402,8 @@ Screenshots:
##### `InAppWebViewController` Cross-platform methods
* `addJavaScriptHandler({required String handlerName, required JavaScriptHandlerCallback callback})`: Adds a JavaScript message handler callback that listen to post messages sent from JavaScript by the handler with name `handlerName`.
* `addUserScript(UserScript userScript)`: Injects the specified `userScript` into the webpages content.
* `addUserScripts(List<UserScript> userScripts)`: Injects the `userScripts` into the webpages content.
* `addUserScript({required UserScript userScript})`: Injects the specified `userScript` into the webpages content.
* `addUserScripts({required List<UserScript> userScripts})`: Injects the `userScripts` into the webpages content.
* `callAsyncJavaScript({required String functionBody, Map<String, dynamic> arguments = const <String, dynamic>{}, ContentWorld? contentWorld})`: Executes the specified string as an asynchronous JavaScript function.
* `canGoBackOrForward({required int steps})`: Returns a boolean value indicating whether the WebView can go back or forward the given number of steps. Steps is negative if backward and positive if forward.
* `canGoBack`: Returns a boolean value indicating whether the WebView can move backward.
@ -433,26 +440,28 @@ Screenshots:
* `injectCSSFileFromAsset({required String assetFilePath})`: Injects a CSS file into the WebView from the flutter assets directory.
* `injectCSSFileFromUrl({required String urlFile, CSSLinkHtmlTagAttributes? cssLinkHtmlTagAttributes})`: Injects an external CSS file into the WebView from a defined url.
* `injectJavascriptFileFromAsset({required String assetFilePath})`: Injects a JavaScript file into the WebView from the flutter assets directory.
* `injectJavascriptFileFromUrl({required String urlFile, ScriptHtmlTagAttributes? scriptHtmlTagAttributes})`: Injects an external JavaScript file into the WebView from a defined url.
* `injectJavascriptFileFromUrl({required Uri urlFile, ScriptHtmlTagAttributes? scriptHtmlTagAttributes})`: Injects an external JavaScript file into the WebView from a defined url.
* `isLoading`: Check if the WebView instance is in a loading state.
* `loadData({required String data, String mimeType = "text/html", String encoding = "utf8", String baseUrl = "about:blank", String androidHistoryUrl = "about:blank"})`: Loads the given data into this WebView.
* `loadFile({required String assetFilePath, Map<String, String> headers = const {}})`: Loads the given `assetFilePath` with optional headers specified as a map from name to value.
* `loadUrl({required String url, Map<String, String> headers = const {}, String? iosAllowingReadAccessTo})`: Loads the given url with optional headers specified as a map from name to value.
* `isSecureContext`: Indicates whether the webpage context is capable of using features that require secure contexts.
* `loadData({required String data, String mimeType = "text/html", String encoding = "utf8", Uri? baseUrl, Uri? androidHistoryUrl})`: Loads the given data into this WebView.
* `loadFile({required String assetFilePath})`: Loads the given `assetFilePath` with optional headers specified as a map from name to value.
* `loadUrl({required URLRequest urlRequest, Uri? iosAllowingReadAccessTo})`: Loads the given url with optional headers specified as a map from name to value.
* `pauseTimers`: On Android, it pauses all layout, parsing, and JavaScript timers for all WebViews. This is a global requests, not restricted to just this WebView. This can be useful if the application has been paused. On iOS, it is restricted to just this WebView.
* `postUrl({required String url, required Uint8List postData})`: Loads the given url with postData using `POST` method into this WebView.
* `postUrl({required Uri url, required Uint8List postData})`: Loads the given url with postData using `POST` method into this WebView.
* `printCurrentPage`: Prints the current page.
* `reload`: Reloads the WebView.
* `removeAllUserScripts()`: Removes all the user scripts from the webpages content.
* `removeJavaScriptHandler({required String handlerName})`: Removes a JavaScript message handler previously added with the `addJavaScriptHandler()` associated to `handlerName` key.
* `removeUserScript(UserScript userScript)`: Removes the specified `userScript` from the webpages content.
* `removeUserScripts(List<UserScript> userScripts)`: Removes the `userScripts` from the webpages content.
* `removeUserScript({required UserScript userScript})`: Removes the specified `userScript` from the webpages content.
* `removeUserScriptsByGroupName({required String groupName})`: Removes all the `UserScript`s with `groupName` as group name from the webpages content.
* `removeUserScripts({required List<UserScript> userScripts})`: Removes the `userScripts` from the webpages content.
* `requestFocusNodeHref`: Requests the anchor or image element URL at the last tapped point.
* `requestImageRef`: Requests the URL of the image last touched by the user.
* `resumeTimers`: On Android, it resumes all layout, parsing, and JavaScript timers for all WebViews. This will resume dispatching all timers. On iOS, it resumes all layout, parsing, and JavaScript timers to just this WebView.
* `saveWebArchive({required String filePath, bool autoname = false})`: Saves the current view as a web archive.
* `scrollBy({required int x, required int y, bool animated = false})`: Moves the scrolled position of the WebView.
* `scrollTo({required int x, required int y, bool animated = false})`: Scrolls the WebView to the position.
* `setContextMenu(ContextMenu contextMenu)`: Sets or updates the WebView context menu to be used next time it will appear.
* `setContextMenu(ContextMenu? contextMenu)`: Sets or updates the WebView context menu to be used next time it will appear.
* `setOptions({required InAppWebViewGroupOptions options})`: Sets the WebView options with the new options and evaluates them.
* `stopLoading`: Stops the WebView from loading.
* `takeScreenshot({ScreenshotConfiguration? screenshotConfiguration})`: 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.
@ -611,6 +620,7 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
* `minimumLogicalFontSize`: Sets the minimum logical font size. The default is `8`.
* `mixedContentMode`: Configures the WebView's behavior when a secure origin attempts to load a resource from an insecure origin.
* `needInitialFocus`: Tells the WebView whether it needs to set a node. The default value is `true`.
* `networkAvailable`: Informs WebView of the network state.
* `offscreenPreRaster`: Sets whether this WebView should raster tiles when it is offscreen but attached to a window.
* `overScrollMode`: Sets the WebView's over-scroll mode. The default value is `AndroidOverScrollMode.OVER_SCROLL_IF_CONTENT_SCROLLS`.
* `regexToCancelSubFramesLoading`: Regular expression used by `shouldOverrideUrlLoading` event to cancel navigation for frames that are not the main frame. If the url request of a subframe matches the regular expression, then the request of that subframe is canceled.
@ -649,6 +659,7 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
* `contentInsetAdjustmentBehavior`: Configures how safe area insets are added to the adjusted content inset. The default value is `IOSUIScrollViewContentInsetAdjustmentBehavior.NEVER`.
* `dataDetectorTypes`: Specifying a dataDetectoryTypes value adds interactivity to web content that matches the value.
* `decelerationRate`: A `IOSUIScrollViewDecelerationRate` value that determines the rate of deceleration after the user lifts their finger. The default value is `IOSUIScrollViewDecelerationRate.NORMAL`.
* `disableLongPressContextMenuOnLinks`: Set to `true` to disable the context menu (copy, select, etc.) that is shown when the user emits a long press event on a HTML link.
* `disallowOverScroll`: Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`.
* `enableViewportScale`: Set to `true` to allow a viewport meta tag to either disable or restrict the range of user scaling. The default value is `false`.
* `ignoresViewportScaleLimits`: Set to `true` if you want that the WebView should always allow scaling of the webpage, regardless of the author's intent.
@ -664,7 +675,7 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
* `selectionGranularity`: The level of granularity with which the user can interactively select content in the web view.
* `sharedCookiesEnabled`: Set `true` if shared cookies from `HTTPCookieStorage.shared` should used for every load request in the WebView.
* `suppressesIncrementalRendering`: Set to `true` if you want the WebView suppresses content rendering until it is fully loaded into memory. The default value is `false`.
* `useOnNavigationResponse`: Set to `true` to be able to listen at the `iosOnNavigationResponse` event. The default value is `false`.
* `useOnNavigationResponse`: Set to `true` to be able to listen to the `iosOnNavigationResponse` event. The default value is `false`.
#### `InAppWebView` Events
@ -814,25 +825,32 @@ class _MyAppState extends State<MyApp> {
decoration:
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialUrlRequest: URLRequest(
url: Uri.parse("https://flutter.dev/")
),
contextMenu: contextMenu,
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
crossPlatform: InAppWebViewOptions(
)
),
ios: IOSInAppWebViewOptions(
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (controller, url) {
setState(() {
this.url = url ?? '';
this.url = url?.toString() ?? '';
});
},
onLoadStop: (controller, url) async {
setState(() {
this.url = url ?? '';
this.url = url?.toString() ?? '';
});
},
onProgressChanged: (controller, progress) {
@ -846,19 +864,19 @@ class _MyAppState extends State<MyApp> {
ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
ElevatedButton(
child: Icon(Icons.arrow_back),
onPressed: () {
webView?.goBack();
},
),
RaisedButton(
ElevatedButton(
child: Icon(Icons.arrow_forward),
onPressed: () {
webView?.goForward();
},
),
RaisedButton(
ElevatedButton(
child: Icon(Icons.refresh),
onPressed: () {
webView?.reload();
@ -922,7 +940,9 @@ class _MyAppState extends State<MyApp> {
super.initState();
headlessWebView = new HeadlessInAppWebView(
initialUrl: "https://flutter.dev/",
initialUrlRequest: URLRequest(
url: Uri.parse("https://flutter.dev/")
),
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
@ -937,19 +957,19 @@ class _MyAppState extends State<MyApp> {
onLoadStart: (controller, url) async {
print("onLoadStart $url");
setState(() {
this.url = url ?? '';
this.url = url?.toString() ?? '';
});
},
onLoadStop: (controller, url) async {
print("onLoadStop $url");
setState(() {
this.url = url ?? '';
this.url = url?.toString() ?? '';
});
},
onUpdateVisitedHistory: (controller, url, androidIsReload) {
print("onUpdateVisitedHistory $url");
setState(() {
this.url = url ?? '';
this.url = url?.toString() ?? '';
});
},
);
@ -976,7 +996,7 @@ class _MyAppState extends State<MyApp> {
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
),
Center(
child: RaisedButton(
child: ElevatedButton(
onPressed: () async {
await headlessWebView?.dispose();
await headlessWebView?.run();
@ -984,7 +1004,7 @@ class _MyAppState extends State<MyApp> {
child: Text("Run HeadlessInAppWebView")),
),
Center(
child: RaisedButton(
child: ElevatedButton(
onPressed: () async {
try {
await headlessWebView?.webViewController.evaluateJavascript(source: """console.log('Here is the message!');""");
@ -995,7 +1015,7 @@ class _MyAppState extends State<MyApp> {
child: Text("Send console.log message")),
),
Center(
child: RaisedButton(
child: ElevatedButton(
onPressed: () {
headlessWebView?.dispose();
},
@ -1054,9 +1074,9 @@ class MyInAppBrowser extends InAppBrowser {
}
@override
Future<ShouldOverrideUrlLoadingAction> shouldOverrideUrlLoading(ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) async {
print("\n\n override ${shouldOverrideUrlLoadingRequest.url}\n\n");
return ShouldOverrideUrlLoadingAction.ALLOW;
Future<NavigationActionPolicy?>? shouldOverrideUrlLoading(NavigationAction navigationAction) async {
print("\n\n override ${navigationAction.request.url}\n\n");
return NavigationActionPolicy.ALLOW;
}
@override
@ -1066,7 +1086,7 @@ class MyInAppBrowser extends InAppBrowser {
"ms ---> duration: " +
response.duration.toString() +
"ms " +
(response.url ?? ''));
(response.url?.toString() ?? ''));
}
@override
@ -1074,7 +1094,7 @@ class MyInAppBrowser extends InAppBrowser {
print("""
console output:
message: ${consoleMessage.message}
messageLevel: ${consoleMessage.messageLevel?.toValue()}
messageLevel: ${consoleMessage.messageLevel.toValue()}
""");
}
}
@ -1106,7 +1126,7 @@ class _MyAppState extends State<MyApp> {
title: const Text('InAppBrowser Example'),
),
body: Center(
child: RaisedButton(
child: ElevatedButton(
onPressed: () {
widget.browser.openFile(
assetFilePath: "assets/index.html",
@ -1136,10 +1156,10 @@ Screenshots:
#### `InAppBrowser` Methods
* `open({String url = "about:blank", Map<String, String> headers = const {}, InAppBrowserClassOptions options})`: Opens an `url` in a new `InAppBrowser` instance.
* `openFile({required String assetFilePath, Map<String, String> headers = const {}, InAppBrowserClassOptions options})`: Opens the given `assetFilePath` file in a new `InAppBrowser` instance. The other arguments are the same of `InAppBrowser.open`.
* `openData({required String data, String mimeType = "text/html", String encoding = "utf8", String baseUrl = "about:blank", String historyUrl = "about:blank", InAppBrowserClassOptions options})`: Opens a new `InAppBrowser` instance with `data` as a content, using `baseUrl` as the base URL for it.
* `openWithSystemBrowser({required String url})`: This is a static method that opens an `url` in the system browser. You wont be able to use the `InAppBrowser` methods here!
* `openUrlRequest({required URLRequest urlRequest, InAppBrowserClassOptions? options})`: Opens an `url` in a new `InAppBrowser` instance.
* `openFile({required String assetFilePath, InAppBrowserClassOptions? options})`: Opens the given `assetFilePath` file in a new `InAppBrowser` instance. The other arguments are the same of `InAppBrowser.open`.
* `openData({required String data, String mimeType = "text/html", String encoding = "utf8", Uri? baseUrl, Uri? androidHistoryUrl, InAppBrowserClassOptions? options})`: Opens a new `InAppBrowser` instance with `data` as a content, using `baseUrl` as the base URL for it.
* `openWithSystemBrowser({required Uri url})`: This is a static method that opens an `url` in the system browser. You wont be able to use the `InAppBrowser` methods here!
* `show`: Displays an `InAppBrowser` window that was opened hidden. Calling this has no effect if the `InAppBrowser` was already visible.
* `hide`: Hides the `InAppBrowser` window. Calling this has no effect if the `InAppBrowser` was already hidden.
* `close`: Closes the `InAppBrowser` window.
@ -1157,25 +1177,28 @@ Specific options of the `InAppBrowser` class are:
* `hidden`: Set to `true` to create the browser and load the page, but not show it. Omit or set to `false` to have the browser open and load normally. The default value is `false`.
* `hideUrlBar`: Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`.
* `hideProgressBar`: Set to `true` to hide the progress bar when the WebView is loading a page. The default value is `false`.
* `hideToolbarTop`: Set to `true` to hide the toolbar at the top of the WebView. The default value is `false`.
* `toolbarTopBackgroundColor`: Set the custom background color of the toolbar at the top.
* `toolbarTop`: Set to `false` to hide the toolbar at the top of the WebView. The default value is `true`.
##### `InAppBrowser` Android-specific options
* `closeOnCannotGoBack`: Set to `false` to not close the InAppBrowser when the user click on the back button and the WebView cannot go back to the history. The default value is `true`.
* `hideTitleBar`: Set to `true` if you want the title should be displayed. The default value is `false`.
* `progressBar`: Set to `false` to hide the progress bar at the bottom of the toolbar at the top. The default value is `true`.
* `toolbarTopFixedTitle`: Set the action bar's title.
##### `InAppBrowser` iOS-specific options
* `closeButtonCaption`: Set the custom text for the close button.
* `closeButtonColor`: Set the custom color for the close button.
* `hideToolbarBottom`: Set to `true` to hide the toolbar at the bottom of the WebView. The default value is `false`.
* `presentationStyle`: Set the custom modal presentation style when presenting the WebView. The default value is `IOSUIModalPresentationStyle.FULL_SCREEN`.
* `spinner`: Set to `false` to hide the spinner when the WebView is loading a page. The default value is `true`.
* `toolbarBottomBackgroundColor`: Set the custom background color of the toolbar at the bottom.
* `toolbarBottomTintColor`: Set the tint color to apply to the bar button items.
* `toolbarBottomTranslucent`: Set to `true` to set the toolbar at the bottom translucent. The default value is `true`.
* `toolbarBottom`: Set to `false` to hide the toolbar at the bottom of the WebView. The default value is `true`.
* `toolbarTopTranslucent`: Set to `true` to set the toolbar at the top translucent. The default value is `true`.
* `toolbarTopBarTintColor`: Set the tint color to apply to the navigation bar background.
* `toolbarTopTintColor`: Set the tint color to apply to the navigation items and bar button items.
* `transitionStyle`: Set to the custom transition style when presenting the WebView. The default value is `IOSUIModalTransitionStyle.COVER_VERTICAL`.
#### `InAppBrowser` Events
@ -1192,8 +1215,6 @@ Specific events of the `InAppBrowser` class are:
If you want to use the `ChromeSafariBrowser` class on Android 11+ you need to specify your app querying for `android.support.customtabs.action.CustomTabsService` in your `AndroidManifest.xml` (you can read more about it here: https://developers.google.com/web/android/custom-tabs/best-practices#applications_targeting_android_11_api_level_30_or_above).
You can initialize the `ChromeSafariBrowser` instance with an `InAppBrowser` fallback instance.
Create a Class that extends the `ChromeSafariBrowser` Class in order to override the callbacks to manage the browser events. Example:
```dart
import 'dart:io';
@ -1227,8 +1248,6 @@ class MyInAppBrowser extends InAppBrowser {
class MyChromeSafariBrowser extends ChromeSafariBrowser {
MyChromeSafariBrowser(browserFallback) : super(bFallback: browserFallback);
@override
void onOpened() {
print("ChromeSafari browser opened");
@ -1254,7 +1273,7 @@ Future main() async {
}
class MyApp extends StatefulWidget {
final ChromeSafariBrowser browser = new MyChromeSafariBrowser(new MyInAppBrowser());
final ChromeSafariBrowser browser = new MyChromeSafariBrowser();
@override
_MyAppState createState() => new _MyAppState();
@ -1285,10 +1304,10 @@ class _MyAppState extends State<MyApp> {
title: const Text('ChromeSafariBrowser Example'),
),
body: Center(
child: RaisedButton(
child: ElevatedButton(
onPressed: () async {
await widget.browser.open(
url: "https://flutter.dev/",
url: Uri.parse("https://flutter.dev/"),
options: ChromeSafariBrowserClassOptions(
android: AndroidChromeCustomTabsOptions(addDefaultShareMenuItem: false),
ios: IOSSafariOptions(barCollapsingEnabled: true)));
@ -1316,7 +1335,7 @@ Screenshots:
* `addMenuItems`: Adds a list of `ChromeSafariBrowserMenuItem` to the menu.
* `close`: Closes the `ChromeSafariBrowser` instance.
* `isOpened`: Returns `true` if the `ChromeSafariBrowser` instance is opened, otherwise `false`.
* `open({required String url, ChromeSafariBrowserClassOptions options, Map<String, String> headersFallback = const {}, InAppBrowserClassOptions optionsFallback})`: Opens an `url` in a new `ChromeSafariBrowser` instance.
* `open({required Uri url, ChromeSafariBrowserClassOptions? options})`: Opens an `url` in a new `ChromeSafariBrowser` instance.
* `static isAvailable`: On Android, returns `true` if Chrome Custom Tabs is available. On iOS, returns `true` if SFSafariViewController is available. Otherwise returns `false`.
#### `ChromeSafariBrowser` options
@ -1376,30 +1395,31 @@ Future main() async {
title: const Text('InAppWebView Example'),
),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "http://localhost:8080/assets/index.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrlRequest: URLRequest(
url: Uri.parse("http://localhost:8080/assets/index.html")
),
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
)
)
),
onWebViewCreated: (controller) {
},
onLoadStart: (controller, url) {
},
onLoadStop: (controller, url) {
},
),
onWebViewCreated: (controller) {
},
onLoadStart: (controller, url) {
},
onLoadStop: (controller, url) {
},
),
),
)]
)
)]
)
),
),
);
@ -1426,11 +1446,11 @@ On iOS, it is implemented using [WKHTTPCookieStore](https://developer.apple.com/
#### `CookieManager` methods
* `instance`: Gets the cookie manager shared instance.
* `setCookie({required String url, required String name, required String value, String? domain, String path = "/", int? expiresDate, int? maxAge, bool? isSecure, bool? isHttpOnly, HTTPCookieSameSitePolicy? sameSite, InAppWebViewController? iosBelow11WebViewController})`: Sets a cookie for the given `url`. Any existing cookie with the same `host`, `path` and `name` will be replaced with the new cookie. The cookie being set will be ignored if it is expired.
* `getCookies({required String url, InAppWebViewController? iosBelow11WebViewController})`: Gets all the cookies for the given `url`.
* `getCookie({required String url, required String name, InAppWebViewController? iosBelow11WebViewController})`: Gets a cookie by its `name` for the given `url`.
* `deleteCookie({required String url, required String name, String domain = "", String path = "/", InAppWebViewController? iosBelow11WebViewController})`: Removes a cookie by its `name` for the given `url`, `domain` and `path`.
* `deleteCookies({required String url, String domain = "", String path = "/", InAppWebViewController? iosBelow11WebViewController})`: Removes all cookies for the given `url`, `domain` and `path`.
* `setCookie({required Uri url, required String name, required String value, String? domain, String path = "/", int? expiresDate, int? maxAge, bool? isSecure, bool? isHttpOnly, HTTPCookieSameSitePolicy? sameSite, InAppWebViewController? iosBelow11WebViewController})`: Sets a cookie for the given `url`. Any existing cookie with the same `host`, `path` and `name` will be replaced with the new cookie. The cookie being set will be ignored if it is expired.
* `getCookies({required Uri url, InAppWebViewController? iosBelow11WebViewController})`: Gets all the cookies for the given `url`.
* `getCookie({required Uri url, required String name, InAppWebViewController? iosBelow11WebViewController})`: Gets a cookie by its `name` for the given `url`.
* `deleteCookie({required Uri url, required String name, String domain = "", String path = "/", InAppWebViewController? iosBelow11WebViewController})`: Removes a cookie by its `name` for the given `url`, `domain` and `path`.
* `deleteCookies({required Uri url, String domain = "", String path = "/", InAppWebViewController? iosBelow11WebViewController})`: Removes all cookies for the given `url`, `domain` and `path`.
* `deleteAllCookies()`: Removes all cookies.
#### `CookieManager` iOS-specific methods
@ -1449,10 +1469,10 @@ On Android, this class has a custom implementation using `android.database.sqlit
* `instance`: Gets the database shared instance.
* `getAllAuthCredentials`: Gets a map list of all HTTP auth credentials saved.
* `getHttpAuthCredentials({required ProtectionSpace protectionSpace})`: Gets all the HTTP auth credentials saved for that `protectionSpace`.
* `setHttpAuthCredential({required ProtectionSpace protectionSpace, required HttpAuthCredential credential})`: Saves an HTTP auth `credential` for that `protectionSpace`.
* `removeHttpAuthCredential({required ProtectionSpace protectionSpace, required HttpAuthCredential credential})`: Removes an HTTP auth `credential` for that `protectionSpace`.
* `removeHttpAuthCredentials({required ProtectionSpace protectionSpace})`: Removes all the HTTP auth credentials saved for that `protectionSpace`.
* `getHttpAuthCredentials({required URLProtectionSpace protectionSpace})`: Gets all the HTTP auth credentials saved for that `protectionSpace`.
* `setHttpAuthCredential({required URLProtectionSpace protectionSpace, required URLCredential credential})`: Saves an HTTP auth `credential` for that `protectionSpace`.
* `removeHttpAuthCredential({required URLProtectionSpace protectionSpace, required URLCredential credential})`: Removes an HTTP auth `credential` for that `protectionSpace`.
* `removeHttpAuthCredentials({required URLProtectionSpace protectionSpace})`: Removes all the HTTP auth credentials saved for that `protectionSpace`.
* `clearAllAuthCredentials()`: Removes all the HTTP auth credentials saved in the database.
### `WebStorageManager` class

View File

@ -2,9 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.pichillilorenzo.flutter_inappwebview">
<application>
<activity android:theme="@style/AppTheme" android:name="com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity" android:configChanges="orientation|screenSize"></activity>
<activity android:theme="@style/ThemeTransparent" android:name="com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ChromeCustomTabsActivity" android:configChanges="orientation|screenSize"></activity>
<receiver android:name="com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ActionBroadcastReceiver" />
<activity android:theme="@style/AppTheme" android:name="com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserActivity" android:configChanges="orientation|screenSize"></activity>
<activity android:theme="@style/ThemeTransparent" android:name="com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ChromeCustomTabsActivity" android:configChanges="orientation|screenSize"></activity>
<receiver android:name="com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ActionBroadcastReceiver" />
<meta-data
android:name="io.flutter.embedded_views_preview"
android:value="true" />

View File

@ -1,106 +0,0 @@
package com.pichillilorenzo.flutter_inappwebview;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ChromeCustomTabsActivity;
import com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.CustomTabActivityHelper;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserOptions;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandler {
public MethodChannel channel;
protected static final String LOG_TAG = "ChromeBrowserManager";
public ChromeSafariBrowserManager(BinaryMessenger messenger) {
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_chromesafaribrowser");
channel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(final MethodCall call, final MethodChannel.Result result) {
final Activity activity = Shared.activity;
final String uuid = (String) call.argument("uuid");
switch (call.method) {
case "open":
{
String url = (String) call.argument("url");
HashMap<String, Object> options = (HashMap<String, Object>) call.argument("options");
List<HashMap<String, Object>> menuItemList = (List<HashMap<String, Object>>) call.argument("menuItemList");
String uuidFallback = (String) call.argument("uuidFallback");
Map<String, String> headersFallback = (Map<String, String>) call.argument("headersFallback");
HashMap<String, Object> optionsFallback = (HashMap<String, Object>) call.argument("optionsFallback");
HashMap<String, Object> contextMenuFallback = (HashMap<String, Object>) call.argument("contextMenuFallback");
Integer windowIdFallback = (Integer) call.argument("windowIdFallback");
open(activity, uuid, url, options, menuItemList, uuidFallback, headersFallback, optionsFallback, contextMenuFallback, windowIdFallback, result);
}
break;
case "isAvailable":
result.success(CustomTabActivityHelper.isAvailable(activity));
break;
default:
result.notImplemented();
}
}
public void open(Activity activity, String uuid, String url, HashMap<String, Object> options, List<HashMap<String, Object>> menuItemList, String uuidFallback,
Map<String, String> headersFallback, HashMap<String, Object> optionsFallback, HashMap<String, Object> contextMenuFallback, Integer windowIdFallback,
MethodChannel.Result result) {
Intent intent = null;
Bundle extras = new Bundle();
extras.putString("fromActivity", activity.getClass().getName());
extras.putString("url", url);
extras.putBoolean("isData", false);
extras.putString("uuid", uuid);
extras.putSerializable("options", options);
extras.putSerializable("menuItemList", (Serializable) menuItemList);
extras.putSerializable("headers", (Serializable) headersFallback);
extras.putSerializable("contextMenu", (Serializable) contextMenuFallback);
extras.putInt("windowId", windowIdFallback != null ? windowIdFallback : -1);
if (CustomTabActivityHelper.isAvailable(activity)) {
intent = new Intent(activity, ChromeCustomTabsActivity.class);
}
// check for webview fallback
else if (uuidFallback != null) {
Log.d(LOG_TAG, "WebView fallback declared.");
// overwrite with extras fallback parameters
extras.putString("uuid", uuidFallback);
if (optionsFallback != null)
extras.putSerializable("options", optionsFallback);
else
extras.putSerializable("options", (Serializable) (new InAppBrowserOptions()).toMap());
intent = new Intent(activity, InAppBrowserActivity.class);
}
if (intent != null) {
intent.putExtras(extras);
activity.startActivity(intent);
result.success(true);
return;
}
result.error(LOG_TAG, "No WebView fallback declared.", null);
}
public void dispose() {
channel.setMethodCallHandler(null);
}
}

View File

@ -1,11 +0,0 @@
package com.pichillilorenzo.flutter_inappwebview.ContentBlocker;
public class ContentBlocker {
public ContentBlockerTrigger trigger;
public ContentBlockerAction action;
public ContentBlocker (ContentBlockerTrigger trigger, ContentBlockerAction action) {
this.trigger = trigger;
this.action = action;
}
}

View File

@ -1,22 +0,0 @@
package com.pichillilorenzo.flutter_inappwebview.ContentBlocker;
import java.util.Map;
public class ContentBlockerAction {
ContentBlockerActionType type;
String selector;
ContentBlockerAction(ContentBlockerActionType type, String selector) {
this.type = type;
if (this.type.equals(ContentBlockerActionType.CSS_DISPLAY_NONE)) {
assert(selector != null);
}
this.selector = selector;
}
public static ContentBlockerAction fromMap(Map<String, Object> map) {
ContentBlockerActionType type = ContentBlockerActionType.fromValue((String) map.get("type"));
String selector = (String) map.get("selector");
return new ContentBlockerAction(type, selector);
}
}

View File

@ -1,61 +0,0 @@
package com.pichillilorenzo.flutter_inappwebview.ContentBlocker;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class ContentBlockerTrigger {
public String urlFilter;
public Pattern urlFilterPatternCompiled;
public Boolean urlFilterIsCaseSensitive;
public List<ContentBlockerTriggerResourceType> resourceType = new ArrayList<>();
public List<String> ifDomain = new ArrayList<>();
public List<String> unlessDomain = new ArrayList<>();
public List<String> loadType = new ArrayList<>();
public List<String> ifTopUrl = new ArrayList<>();
public List<String> unlessTopUrl = new ArrayList<>();
public ContentBlockerTrigger(String urlFilter, Boolean urlFilterIsCaseSensitive, List<ContentBlockerTriggerResourceType> resourceType, List<String> ifDomain,
List<String> unlessDomain, List<String> loadType, List<String> ifTopUrl, List<String> unlessTopUrl) {
this.urlFilter = urlFilter;
this.urlFilterPatternCompiled = Pattern.compile(this.urlFilter);
this.resourceType = resourceType != null ? resourceType : this.resourceType;
this.urlFilterIsCaseSensitive = urlFilterIsCaseSensitive != null ? urlFilterIsCaseSensitive : false;
this.ifDomain = ifDomain != null ? ifDomain : this.ifDomain;
this.unlessDomain = unlessDomain != null ? unlessDomain : this.unlessDomain;
if ((!(this.ifDomain.isEmpty() || this.unlessDomain.isEmpty()) != false))
throw new AssertionError();
this.loadType = loadType != null ? loadType : this.loadType;
if ((this.loadType.size() > 2)) throw new AssertionError();
this.ifTopUrl = ifTopUrl != null ? ifTopUrl : this.ifTopUrl;
this.unlessTopUrl = unlessTopUrl != null ? unlessTopUrl : this.unlessTopUrl;
if ((!(this.ifTopUrl.isEmpty() || this.unlessTopUrl.isEmpty()) != false))
throw new AssertionError();
}
public static ContentBlockerTrigger fromMap(Map<String, Object> map) {
String urlFilter = (String) map.get("url-filter");
Boolean urlFilterIsCaseSensitive = (Boolean) map.get("url-filter-is-case-sensitive");
List<String> resourceTypeStringList = (List<String>) map.get("resource-type");
List<ContentBlockerTriggerResourceType> resourceType = new ArrayList<>();
if (resourceTypeStringList != null) {
for (String type : resourceTypeStringList) {
resourceType.add(ContentBlockerTriggerResourceType.fromValue(type));
}
} else {
resourceType.addAll(Arrays.asList(ContentBlockerTriggerResourceType.values()));
}
List<String> ifDomain = (List<String>) map.get("if-domain");
List<String> unlessDomain = (List<String>) map.get("unless-domain");
List<String> loadType = (List<String>) map.get("load-type");
List<String> ifTopUrl = (List<String>) map.get("if-top-url");
List<String> unlessTopUrl = (List<String>) map.get("unless-top-url");
return new ContentBlockerTrigger(urlFilter, urlFilterIsCaseSensitive, resourceType, ifDomain, unlessDomain, loadType, ifTopUrl, unlessTopUrl);
}
}

View File

@ -1,25 +0,0 @@
package com.pichillilorenzo.flutter_inappwebview.CredentialDatabase;
import java.util.HashMap;
import java.util.Map;
public class Credential {
public Long id;
public String username;
public String password;
public Long protectionSpaceId;
public Credential (Long id, String username, String password, Long protectionSpaceId) {
this.id = id;
this.username = username;
this.password = password;
this.protectionSpaceId = protectionSpaceId;
}
public Map<String, Object> toMap() {
Map<String, Object> credentialMap = new HashMap<>();
credentialMap.put("username", username);
credentialMap.put("password", password);
return credentialMap;
}
}

View File

@ -1,104 +0,0 @@
package com.pichillilorenzo.flutter_inappwebview.CredentialDatabase;
import android.content.ContentValues;
import android.database.Cursor;
import java.util.ArrayList;
import java.util.List;
public class CredentialDao {
CredentialDatabaseHelper credentialDatabaseHelper;
String[] projection = {
CredentialContract.FeedEntry._ID,
CredentialContract.FeedEntry.COLUMN_NAME_USERNAME,
CredentialContract.FeedEntry.COLUMN_NAME_PASSWORD,
CredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID
};
public CredentialDao(CredentialDatabaseHelper credentialDatabaseHelper) {
this.credentialDatabaseHelper = credentialDatabaseHelper;
}
public List<Credential> getAllByProtectionSpaceId(Long protectionSpaceId) {
String selection = CredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID + " = ?";
String[] selectionArgs = {protectionSpaceId.toString()};
Cursor cursor = credentialDatabaseHelper.getReadableDatabase().query(
CredentialContract.FeedEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
null
);
List<Credential> credentials = new ArrayList<>();
while (cursor.moveToNext()) {
Long id = cursor.getLong(cursor.getColumnIndexOrThrow(CredentialContract.FeedEntry._ID));
String username = cursor.getString(cursor.getColumnIndexOrThrow(CredentialContract.FeedEntry.COLUMN_NAME_USERNAME));
String password = cursor.getString(cursor.getColumnIndexOrThrow(CredentialContract.FeedEntry.COLUMN_NAME_PASSWORD));
credentials.add(new Credential(id, username, password, protectionSpaceId));
}
cursor.close();
return credentials;
}
public Credential find(String username, String password, Long protectionSpaceId) {
String selection = CredentialContract.FeedEntry.COLUMN_NAME_USERNAME + " = ? AND " +
CredentialContract.FeedEntry.COLUMN_NAME_PASSWORD + " = ? AND " +
CredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID + " = ?";
String[] selectionArgs = {username, password, protectionSpaceId.toString()};
Cursor cursor = credentialDatabaseHelper.getReadableDatabase().query(
CredentialContract.FeedEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
null
);
Credential credential = null;
if (cursor.moveToNext()) {
Long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(CredentialContract.FeedEntry._ID));
String rowUsername = cursor.getString(cursor.getColumnIndexOrThrow(CredentialContract.FeedEntry.COLUMN_NAME_USERNAME));
String rowPassword = cursor.getString(cursor.getColumnIndexOrThrow(CredentialContract.FeedEntry.COLUMN_NAME_PASSWORD));
credential = new Credential(rowId, rowUsername, rowPassword, protectionSpaceId);
}
cursor.close();
return credential;
}
public long insert(Credential credential) {
ContentValues credentialValues = new ContentValues();
credentialValues.put(CredentialContract.FeedEntry.COLUMN_NAME_USERNAME, credential.username);
credentialValues.put(CredentialContract.FeedEntry.COLUMN_NAME_PASSWORD, credential.password);
credentialValues.put(CredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID, credential.protectionSpaceId);
return credentialDatabaseHelper.getWritableDatabase().insert(CredentialContract.FeedEntry.TABLE_NAME, null, credentialValues);
}
public long update(Credential credential) {
ContentValues credentialValues = new ContentValues();
credentialValues.put(CredentialContract.FeedEntry.COLUMN_NAME_USERNAME, credential.username);
credentialValues.put(CredentialContract.FeedEntry.COLUMN_NAME_PASSWORD, credential.password);
String whereClause = CredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID + " = ?";
String[] whereArgs = {credential.protectionSpaceId.toString()};
return credentialDatabaseHelper.getWritableDatabase().update(CredentialContract.FeedEntry.TABLE_NAME, credentialValues, whereClause, whereArgs);
}
public long delete(Credential credential) {
String whereClause = CredentialContract.FeedEntry._ID + " = ?";
String[] whereArgs = {credential.id.toString()};
return credentialDatabaseHelper.getWritableDatabase().delete(CredentialContract.FeedEntry.TABLE_NAME, whereClause, whereArgs);
}
}

View File

@ -1,92 +0,0 @@
package com.pichillilorenzo.flutter_inappwebview.CredentialDatabase;
import android.content.Context;
import java.util.ArrayList;
import java.util.List;
public class CredentialDatabase {
private static CredentialDatabase instance;
static final String LOG_TAG = "CredentialDatabase";
// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 2;
public static final String DATABASE_NAME = "CredentialDatabase.db";
public ProtectionSpaceDao protectionSpaceDao;
public CredentialDao credentialDao;
public CredentialDatabaseHelper db;
private CredentialDatabase() {}
private CredentialDatabase(CredentialDatabaseHelper db, ProtectionSpaceDao protectionSpaceDao, CredentialDao credentialDao) {
this.db = db;
this.protectionSpaceDao = protectionSpaceDao;
this.credentialDao = credentialDao;
}
public static CredentialDatabase getInstance(Context context) {
if (instance != null)
return instance;
CredentialDatabaseHelper db = new CredentialDatabaseHelper(context);
instance = new CredentialDatabase(db, new ProtectionSpaceDao(db), new CredentialDao(db));
return instance;
}
public List<Credential> getHttpAuthCredentials(String host, String protocol, String realm, Integer port) {
List<Credential> credentialList = new ArrayList<>();
ProtectionSpace protectionSpace = protectionSpaceDao.find(host, protocol, realm, port);
if (protectionSpace != null) {
credentialList = credentialDao.getAllByProtectionSpaceId(protectionSpace.id);
}
return credentialList;
}
public void clearAllAuthCredentials() {
db.clearAllTables(db.getWritableDatabase());
}
public void removeHttpAuthCredentials(String host, String protocol, String realm, Integer port) {
ProtectionSpace protectionSpace = protectionSpaceDao.find(host, protocol, realm, port);
if (protectionSpace != null) {
protectionSpaceDao.delete(protectionSpace);
}
}
public void removeHttpAuthCredential(String host, String protocol, String realm, Integer port, String username, String password) {
ProtectionSpace protectionSpace = protectionSpaceDao.find(host, protocol, realm, port);
if (protectionSpace != null) {
Credential credential = credentialDao.find(username, password, protectionSpace.id);
credentialDao.delete(credential);
}
}
public void setHttpAuthCredential(String host, String protocol, String realm, Integer port, String username, String password) {
ProtectionSpace protectionSpace = protectionSpaceDao.find(host, protocol, realm, port);
Long protectionSpaceId;
if (protectionSpace == null) {
protectionSpaceId = protectionSpaceDao.insert(new ProtectionSpace(null, host, protocol, realm, port));
} else {
protectionSpaceId = protectionSpace.id;
}
Credential credential = credentialDao.find(username, password, protectionSpaceId);
if (credential != null) {
boolean needUpdate = false;
if (!credential.username.equals(username)) {
credential.username = username;
needUpdate = true;
}
if (!credential.password.equals(password)) {
credential.password = password;
needUpdate = true;
}
if (needUpdate)
credentialDao.update(credential);
} else {
credential = new Credential(null, username, password, protectionSpaceId);
credential.id = credentialDao.insert(credential);
}
}
}

View File

@ -1,66 +0,0 @@
package com.pichillilorenzo.flutter_inappwebview.CredentialDatabase;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class CredentialDatabaseHelper extends SQLiteOpenHelper {
private static final String SQL_CREATE_PROTECTION_SPACE_TABLE =
"CREATE TABLE " + ProtectionSpaceContract.FeedEntry.TABLE_NAME + " (" +
ProtectionSpaceContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
ProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST + " TEXT NOT NULL," +
ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL + " TEXT," +
ProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM + " TEXT," +
ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT + " INTEGER," +
"UNIQUE(" + ProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST + ", " + ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL + ", " +
ProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM + ", " + ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT +
")" +
");";
private static final String SQL_CREATE_CREDENTIAL_TABLE =
"CREATE TABLE " + CredentialContract.FeedEntry.TABLE_NAME + " (" +
CredentialContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
CredentialContract.FeedEntry.COLUMN_NAME_USERNAME + " TEXT NOT NULL," +
CredentialContract.FeedEntry.COLUMN_NAME_PASSWORD + " TEXT NOT NULL," +
CredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID + " INTEGER NOT NULL," +
"UNIQUE(" + CredentialContract.FeedEntry.COLUMN_NAME_USERNAME + ", " + CredentialContract.FeedEntry.COLUMN_NAME_PASSWORD + ", " +
CredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID +
")," +
"FOREIGN KEY (" + CredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID + ") REFERENCES " +
ProtectionSpaceContract.FeedEntry.TABLE_NAME + " (" + ProtectionSpaceContract.FeedEntry._ID + ") ON DELETE CASCADE" +
");";
private static final String SQL_DELETE_PROTECTION_SPACE_TABLE =
"DROP TABLE IF EXISTS " + ProtectionSpaceContract.FeedEntry.TABLE_NAME;
private static final String SQL_DELETE_CREDENTIAL_TABLE =
"DROP TABLE IF EXISTS " + CredentialContract.FeedEntry.TABLE_NAME;
public CredentialDatabaseHelper(Context context) {
super(context, CredentialDatabase.DATABASE_NAME, null, CredentialDatabase.DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_PROTECTION_SPACE_TABLE);
db.execSQL(SQL_CREATE_CREDENTIAL_TABLE);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
db.execSQL(SQL_DELETE_PROTECTION_SPACE_TABLE);
db.execSQL(SQL_DELETE_CREDENTIAL_TABLE);
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
public void clearAllTables(SQLiteDatabase db) {
db.execSQL(SQL_DELETE_PROTECTION_SPACE_TABLE);
db.execSQL(SQL_DELETE_CREDENTIAL_TABLE);
onCreate(db);
}
}

View File

@ -1,29 +0,0 @@
package com.pichillilorenzo.flutter_inappwebview.CredentialDatabase;
import java.util.HashMap;
import java.util.Map;
public class ProtectionSpace {
public Long id;
public String host;
public String procotol;
public String realm;
public Integer port;
public ProtectionSpace (Long id, String host, String protocol, String realm, Integer port) {
this.id = id;
this.host = host;
this.procotol = protocol;
this.realm = realm;
this.port = port;
}
public Map<String, Object> toMap() {
Map<String, Object> protectionSpaceMap = new HashMap<>();
protectionSpaceMap.put("host", host);
protectionSpaceMap.put("protocol", procotol);
protectionSpaceMap.put("realm", realm);
protectionSpaceMap.put("port", port);
return protectionSpaceMap;
}
}

View File

@ -1,98 +0,0 @@
package com.pichillilorenzo.flutter_inappwebview.CredentialDatabase;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import java.util.ArrayList;
import java.util.List;
public class ProtectionSpaceDao {
CredentialDatabaseHelper credentialDatabaseHelper;
String[] projection = {
ProtectionSpaceContract.FeedEntry._ID,
ProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST,
ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL,
ProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM,
ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT
};
public ProtectionSpaceDao(CredentialDatabaseHelper credentialDatabaseHelper) {
this.credentialDatabaseHelper = credentialDatabaseHelper;
}
public List<ProtectionSpace> getAll() {
SQLiteDatabase readableDatabase = credentialDatabaseHelper.getReadableDatabase();
Cursor cursor = readableDatabase.query(
ProtectionSpaceContract.FeedEntry.TABLE_NAME,
projection,
null,
null,
null,
null,
null
);
List<ProtectionSpace> protectionSpaces = new ArrayList<>();
while (cursor.moveToNext()) {
Long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(ProtectionSpaceContract.FeedEntry._ID));
String rowHost = cursor.getString(cursor.getColumnIndexOrThrow(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST));
String rowProtocol = cursor.getString(cursor.getColumnIndexOrThrow(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL));
String rowRealm = cursor.getString(cursor.getColumnIndexOrThrow(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM));
Integer rowPort = cursor.getInt(cursor.getColumnIndexOrThrow(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT));
protectionSpaces.add(new ProtectionSpace(rowId, rowHost, rowProtocol, rowRealm, rowPort));
}
cursor.close();
return protectionSpaces;
}
public ProtectionSpace find(String host, String protocol, String realm, Integer port) {
SQLiteDatabase readableDatabase = credentialDatabaseHelper.getReadableDatabase();
String selection = ProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST + " = ? AND " + ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL + " = ? AND " +
ProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM + " = ? AND " + ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT + " = ?";
String[] selectionArgs = {host, protocol, realm, port.toString()};
Cursor cursor = readableDatabase.query(
ProtectionSpaceContract.FeedEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
null
);
ProtectionSpace protectionSpace = null;
if (cursor.moveToNext()) {
Long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(ProtectionSpaceContract.FeedEntry._ID));
String rowHost = cursor.getString(cursor.getColumnIndexOrThrow(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST));
String rowProtocol = cursor.getString(cursor.getColumnIndexOrThrow(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL));
String rowRealm = cursor.getString(cursor.getColumnIndexOrThrow(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM));
Integer rowPort = cursor.getInt(cursor.getColumnIndexOrThrow(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT));
protectionSpace = new ProtectionSpace(rowId, rowHost, rowProtocol, rowRealm, rowPort);
}
cursor.close();
return protectionSpace;
}
public long insert(ProtectionSpace protectionSpace) {
ContentValues protectionSpaceValues = new ContentValues();
protectionSpaceValues.put(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST, protectionSpace.host);
protectionSpaceValues.put(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL, protectionSpace.procotol);
protectionSpaceValues.put(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM, protectionSpace.realm);
protectionSpaceValues.put(ProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT, protectionSpace.port);
return credentialDatabaseHelper.getWritableDatabase().insert(ProtectionSpaceContract.FeedEntry.TABLE_NAME, null, protectionSpaceValues);
};
public long delete(ProtectionSpace protectionSpace) {
String whereClause = ProtectionSpaceContract.FeedEntry._ID + " = ?";
String[] whereArgs = {protectionSpace.id.toString()};
return credentialDatabaseHelper.getWritableDatabase().delete(ProtectionSpaceContract.FeedEntry.TABLE_NAME, whereClause, whereArgs);
}
}

View File

@ -4,10 +4,13 @@ import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import android.webkit.ValueCallback;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.FlutterWebViewFactory;
import com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ChromeSafariBrowserManager;
import com.pichillilorenzo.flutter_inappwebview.credential_database.CredentialDatabaseHandler;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserManager;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.FlutterWebViewFactory;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.HeadlessInAppWebViewManager;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
@ -15,7 +18,6 @@ import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterView;
public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {

View File

@ -8,10 +8,16 @@ import androidx.annotation.NonNull;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserOptions;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserOptions;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebViewOptions;
import com.pichillilorenzo.flutter_inappwebview.types.ContentWorld;
import com.pichillilorenzo.flutter_inappwebview.types.SslCertificateExt;
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@ -40,42 +46,55 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle
result.success((webView != null) ? webView.getProgress() : null);
break;
case "loadUrl":
if (webView != null)
webView.loadUrl((String) call.argument("url"), (Map<String, String>) call.argument("headers"), result);
else
result.success(false);
if (webView != null) {
Map<String, Object> urlRequest = (Map<String, Object>) call.argument("urlRequest");
webView.loadUrl(URLRequest.fromMap(urlRequest));
}
result.success(true);
break;
case "postUrl":
if (webView != null)
webView.postUrl((String) call.argument("url"), (byte[]) call.argument("postData"), result);
else
result.success(false);
if (webView != null) {
String url = (String) call.argument("url");
byte[] postData = (byte[]) call.argument("postData");
webView.postUrl(url, postData);
}
result.success(true);
break;
case "loadData":
{
String data = (String) call.argument("data");
String mimeType = (String) call.argument("mimeType");
String encoding = (String) call.argument("encoding");
String baseUrl = (String) call.argument("baseUrl");
String historyUrl = (String) call.argument("historyUrl");
if (webView != null)
webView.loadData(data, mimeType, encoding, baseUrl, historyUrl, result);
else
result.success(false);
}
if (webView != null) {
String data = (String) call.argument("data");
String mimeType = (String) call.argument("mimeType");
String encoding = (String) call.argument("encoding");
String baseUrl = (String) call.argument("baseUrl");
String historyUrl = (String) call.argument("historyUrl");
webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
result.success(true);
break;
case "loadFile":
if (webView != null)
webView.loadFile((String) call.argument("url"), (Map<String, String>) call.argument("headers"), result);
else
result.success(false);
if (webView != null) {
String assetFilePath = (String) call.argument("url");
try {
webView.loadFile(assetFilePath);
} catch (IOException e) {
e.printStackTrace();
result.error(LOG_TAG, e.getMessage(), null);
return;
}
}
result.success(true);
break;
case "evaluateJavascript":
if (webView != null) {
String source = (String) call.argument("source");
String contentWorldName = (String) call.argument("contentWorld");
webView.evaluateJavascript(source, contentWorldName, result);
Map<String, Object> contentWorldMap = (Map<String, Object>) call.argument("contentWorld");
ContentWorld contentWorld = ContentWorld.fromMap(contentWorldMap);
webView.evaluateJavascript(source, contentWorld, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
result.success(value);
}
});
}
else {
result.success(null);
@ -150,11 +169,12 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle
result.success(null);
break;
case "setOptions":
if (webView != null && webView.inAppBrowserActivity != null) {
if (webView != null && webView.inAppBrowserDelegate != null && webView.inAppBrowserDelegate instanceof InAppBrowserActivity) {
InAppBrowserActivity inAppBrowserActivity = (InAppBrowserActivity) webView.inAppBrowserDelegate;
InAppBrowserOptions inAppBrowserOptions = new InAppBrowserOptions();
HashMap<String, Object> inAppBrowserOptionsMap = (HashMap<String, Object>) call.argument("options");
inAppBrowserOptions.parse(inAppBrowserOptionsMap);
webView.inAppBrowserActivity.setOptions(inAppBrowserOptions, inAppBrowserOptionsMap);
inAppBrowserActivity.setOptions(inAppBrowserOptions, inAppBrowserOptionsMap);
} else if (webView != null) {
InAppWebViewOptions inAppWebViewOptions = new InAppWebViewOptions();
HashMap<String, Object> inAppWebViewOptionsMap = (HashMap<String, Object>) call.argument("options");
@ -164,30 +184,34 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle
result.success(true);
break;
case "getOptions":
if (webView != null && webView.inAppBrowserActivity != null) {
result.success(webView.inAppBrowserActivity.getOptions());
if (webView != null && webView.inAppBrowserDelegate != null && webView.inAppBrowserDelegate instanceof InAppBrowserActivity) {
InAppBrowserActivity inAppBrowserActivity = (InAppBrowserActivity) webView.inAppBrowserDelegate;
result.success(inAppBrowserActivity.getOptions());
} else {
result.success((webView != null) ? webView.getOptions() : null);
}
break;
case "close":
if (webView != null && webView.inAppBrowserActivity != null) {
webView.inAppBrowserActivity.close(result);
if (webView != null && webView.inAppBrowserDelegate != null && webView.inAppBrowserDelegate instanceof InAppBrowserActivity) {
InAppBrowserActivity inAppBrowserActivity = (InAppBrowserActivity) webView.inAppBrowserDelegate;
inAppBrowserActivity.close(result);
} else {
result.notImplemented();
}
break;
case "show":
if (webView != null && webView.inAppBrowserActivity != null) {
webView.inAppBrowserActivity.show();
if (webView != null && webView.inAppBrowserDelegate != null && webView.inAppBrowserDelegate instanceof InAppBrowserActivity) {
InAppBrowserActivity inAppBrowserActivity = (InAppBrowserActivity) webView.inAppBrowserDelegate;
inAppBrowserActivity.show();
result.success(true);
} else {
result.notImplemented();
}
break;
case "hide":
if (webView != null && webView.inAppBrowserActivity != null) {
webView.inAppBrowserActivity.hide();
if (webView != null && webView.inAppBrowserDelegate != null && webView.inAppBrowserDelegate instanceof InAppBrowserActivity) {
InAppBrowserActivity inAppBrowserActivity = (InAppBrowserActivity) webView.inAppBrowserDelegate;
inAppBrowserActivity.hide();
result.success(true);
} else {
result.notImplemented();
@ -409,7 +433,7 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle
break;
case "getCertificate":
if (webView != null) {
result.success(webView.getCertificateMap());
result.success(SslCertificateExt.toMap(webView.getCertificate()));
} else {
result.success(null);
}
@ -421,24 +445,34 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle
result.success(true);
break;
case "addUserScript":
if (webView != null) {
Map<String, Object> userScript = (Map<String, Object>) call.argument("userScript");
result.success(webView.addUserScript(userScript));
if (webView != null && webView.userContentController != null) {
Map<String, Object> userScriptMap = (Map<String, Object>) call.argument("userScript");
UserScript userScript = UserScript.fromMap(userScriptMap);
result.success(webView.userContentController.addUserOnlyScript(userScript));
} else {
result.success(false);
}
break;
case "removeUserScript":
if (webView != null) {
if (webView != null && webView.userContentController != null) {
Integer index = (Integer) call.argument("index");
result.success(webView.removeUserScript(index));
Map<String, Object> userScriptMap = (Map<String, Object>) call.argument("userScript");
UserScript userScript = UserScript.fromMap(userScriptMap);
result.success(webView.userContentController.removePluginScriptAt(index, userScript.getInjectionTime()));
} else {
result.success(false);
}
break;
case "removeUserScriptsByGroupName":
if (webView != null && webView.userContentController != null) {
String groupName = (String) call.argument("groupName");
webView.userContentController.removeUserOnlyScriptsByGroupName(groupName);
}
result.success(true);
break;
case "removeAllUserScripts":
if (webView != null) {
webView.removeAllUserScripts();
if (webView != null && webView.userContentController != null) {
webView.userContentController.removeAllUserOnlyScripts();
}
result.success(true);
break;
@ -446,13 +480,31 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
String functionBody = (String) call.argument("functionBody");
Map<String, Object> functionArguments = (Map<String, Object>) call.argument("arguments");
String contentWorldName = (String) call.argument("contentWorld");
webView.callAsyncJavaScript(functionBody, functionArguments, contentWorldName, result);
Map<String, Object> contentWorldMap = (Map<String, Object>) call.argument("contentWorld");
ContentWorld contentWorld = ContentWorld.fromMap(contentWorldMap);
webView.callAsyncJavaScript(functionBody, functionArguments, contentWorld, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
result.success(value);
}
});
}
else {
result.success(null);
}
break;
case "isSecureContext":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.isSecureContext(new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean value) {
result.success(value);
}
});
} else {
result.success(false);
}
break;
default:
result.notImplemented();
}

View File

@ -8,8 +8,10 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import org.json.JSONArray;
@ -27,12 +29,15 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
@ -244,7 +249,7 @@ public class Util {
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static String JSONStringify(Object value) {
public static String JSONStringify(@Nullable Object value) {
if (value == null) {
return "null";
}
@ -258,4 +263,15 @@ public class Util {
return JSONObject.wrap(value).toString();
}
}
public static boolean objEquals(@Nullable Object a, @Nullable Object b) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return Objects.equals(a, b);
}
return (a == b) || (a != null && a.equals(b));
}
public static String replaceAll(String s, String oldString, String newString) {
return TextUtils.join(newString, s.split(Pattern.quote(oldString)));
}
}

View File

@ -1,10 +1,9 @@
package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.pichillilorenzo.flutter_inappwebview.Shared;

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
import android.app.Activity;
import android.app.PendingIntent;
@ -6,7 +6,6 @@ import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.browser.customtabs.CustomTabsCallback;
import androidx.browser.customtabs.CustomTabsIntent;
@ -16,7 +15,6 @@ import androidx.browser.customtabs.CustomTabsSession;
import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -145,7 +143,7 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
if (options.addDefaultShareMenuItem)
builder.addDefaultShareMenuItem();
if (!options.toolbarBackgroundColor.isEmpty())
if (options.toolbarBackgroundColor != null && !options.toolbarBackgroundColor.isEmpty())
builder.setToolbarColor(Color.parseColor(options.toolbarBackgroundColor));
builder.setShowTitle(options.showTitle);

View File

@ -1,7 +1,9 @@
package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
import android.content.Intent;
import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.Options;
import java.util.HashMap;
@ -13,7 +15,8 @@ public class ChromeCustomTabsOptions implements Options<ChromeCustomTabsActivity
public Boolean addDefaultShareMenuItem = true;
public Boolean showTitle = true;
public String toolbarBackgroundColor = "";
@Nullable
public String toolbarBackgroundColor;
public Boolean enableUrlBarHiding = false;
public Boolean instantAppsEnabled = false;
public String packageName;

View File

@ -0,0 +1,76 @@
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandler {
public MethodChannel channel;
protected static final String LOG_TAG = "ChromeBrowserManager";
public ChromeSafariBrowserManager(BinaryMessenger messenger) {
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_chromesafaribrowser");
channel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(final MethodCall call, final MethodChannel.Result result) {
final Activity activity = Shared.activity;
final String uuid = (String) call.argument("uuid");
switch (call.method) {
case "open":
{
String url = (String) call.argument("url");
HashMap<String, Object> options = (HashMap<String, Object>) call.argument("options");
List<HashMap<String, Object>> menuItemList = (List<HashMap<String, Object>>) call.argument("menuItemList");
open(activity, uuid, url, options, menuItemList, result);
}
break;
case "isAvailable":
result.success(CustomTabActivityHelper.isAvailable(activity));
break;
default:
result.notImplemented();
}
}
public void open(Activity activity, String uuid, String url, HashMap<String, Object> options,
List<HashMap<String, Object>> menuItemList, MethodChannel.Result result) {
Intent intent = null;
Bundle extras = new Bundle();
extras.putString("fromActivity", activity.getClass().getName());
extras.putString("url", url);
extras.putBoolean("isData", false);
extras.putString("uuid", uuid);
extras.putSerializable("options", options);
extras.putSerializable("menuItemList", (Serializable) menuItemList);
if (CustomTabActivityHelper.isAvailable(activity)) {
intent = new Intent(activity, ChromeCustomTabsActivity.class);
intent.putExtras(extras);
activity.startActivity(intent);
result.success(true);
return;
}
result.error(LOG_TAG, "ChromeCustomTabs is not available!", null);
}
public void dispose() {
channel.setMethodCallHandler(null);
}
}

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
import android.app.Activity;
import android.net.Uri;

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
import android.content.Context;
import android.content.Intent;

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
import android.app.Service;
import android.content.Intent;

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
import android.content.ComponentName;
import androidx.browser.customtabs.CustomTabsClient;

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
import androidx.browser.customtabs.CustomTabsClient;

View File

@ -0,0 +1,59 @@
package com.pichillilorenzo.flutter_inappwebview.content_blocker;
import androidx.annotation.NonNull;
public class ContentBlocker {
@NonNull
private ContentBlockerTrigger trigger;
@NonNull
private ContentBlockerAction action;
public ContentBlocker (@NonNull ContentBlockerTrigger trigger, @NonNull ContentBlockerAction action) {
this.trigger = trigger;
this.action = action;
}
@NonNull
public ContentBlockerTrigger getTrigger() {
return trigger;
}
public void setTrigger(@NonNull ContentBlockerTrigger trigger) {
this.trigger = trigger;
}
@NonNull
public ContentBlockerAction getAction() {
return action;
}
public void setAction(@NonNull ContentBlockerAction action) {
this.action = action;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ContentBlocker that = (ContentBlocker) o;
if (!trigger.equals(that.trigger)) return false;
return action.equals(that.action);
}
@Override
public int hashCode() {
int result = trigger.hashCode();
result = 31 * result + action.hashCode();
return result;
}
@Override
public String toString() {
return "ContentBlocker{" +
"trigger=" + trigger +
", action=" + action +
'}';
}
}

View File

@ -0,0 +1,71 @@
package com.pichillilorenzo.flutter_inappwebview.content_blocker;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Map;
public class ContentBlockerAction {
@NonNull
private ContentBlockerActionType type;
@Nullable
private String selector;
ContentBlockerAction(@NonNull ContentBlockerActionType type, @Nullable String selector) {
this.type = type;
if (this.type.equals(ContentBlockerActionType.CSS_DISPLAY_NONE)) {
assert(selector != null);
}
this.selector = selector;
}
public static ContentBlockerAction fromMap(Map<String, Object> map) {
ContentBlockerActionType type = ContentBlockerActionType.fromValue((String) map.get("type"));
String selector = (String) map.get("selector");
return new ContentBlockerAction(type, selector);
}
@NonNull
public ContentBlockerActionType getType() {
return type;
}
public void setType(@NonNull ContentBlockerActionType type) {
this.type = type;
}
public String getSelector() {
return selector;
}
public void setSelector(String selector) {
this.selector = selector;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ContentBlockerAction that = (ContentBlockerAction) o;
if (type != that.type) return false;
return selector != null ? selector.equals(that.selector) : that.selector == null;
}
@Override
public int hashCode() {
int result = type.hashCode();
result = 31 * result + (selector != null ? selector.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "ContentBlockerAction{" +
"type=" + type +
", selector='" + selector + '\'' +
'}';
}
}

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.ContentBlocker;
package com.pichillilorenzo.flutter_inappwebview.content_blocker;
public enum ContentBlockerActionType {
BLOCK ("block"),
@ -23,6 +23,7 @@ public enum ContentBlockerActionType {
throw new IllegalArgumentException("No enum constant: " + value);
}
@Override
public String toString() {
return this.value;
}

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.ContentBlocker;
package com.pichillilorenzo.flutter_inappwebview.content_blocker;
import android.os.Build;
import android.os.Handler;
@ -6,7 +6,7 @@ import android.os.Looper;
import android.util.Log;
import android.webkit.WebResourceResponse;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.Util;
import java.io.ByteArrayInputStream;
@ -21,7 +21,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import io.flutter.plugin.common.MethodChannel;
import okhttp3.Request;
import okhttp3.Response;
@ -64,23 +63,23 @@ public class ContentBlockerHandler {
List<ContentBlocker> ruleListCopy = new CopyOnWriteArrayList<ContentBlocker>(ruleList);
for (ContentBlocker contentBlocker : ruleListCopy) {
ContentBlockerTrigger trigger = contentBlocker.trigger;
List<ContentBlockerTriggerResourceType> resourceTypes = trigger.resourceType;
ContentBlockerTrigger trigger = contentBlocker.getTrigger();
List<ContentBlockerTriggerResourceType> resourceTypes = trigger.getResourceType();
if (resourceTypes.contains(ContentBlockerTriggerResourceType.IMAGE) && !resourceTypes.contains(ContentBlockerTriggerResourceType.SVG_DOCUMENT)) {
resourceTypes.add(ContentBlockerTriggerResourceType.SVG_DOCUMENT);
}
ContentBlockerAction action = contentBlocker.action;
ContentBlockerAction action = contentBlocker.getAction();
Matcher m = trigger.urlFilterPatternCompiled.matcher(url);
Matcher m = trigger.getUrlFilterPatternCompiled().matcher(url);
if (m.matches()) {
if (!resourceTypes.isEmpty() && !resourceTypes.contains(responseResourceType)) {
return null;
}
if (!trigger.ifDomain.isEmpty()) {
if (!trigger.getIfDomain().isEmpty()) {
boolean matchFound = false;
for (String domain : trigger.ifDomain) {
for (String domain : trigger.getIfDomain()) {
if ((domain.startsWith("*") && host.endsWith(domain.replace("*", ""))) || domain.equals(host)) {
matchFound = true;
break;
@ -89,14 +88,14 @@ public class ContentBlockerHandler {
if (!matchFound)
return null;
}
if (!trigger.unlessDomain.isEmpty()) {
for (String domain : trigger.unlessDomain)
if (!trigger.getUnlessDomain().isEmpty()) {
for (String domain : trigger.getUnlessDomain())
if ((domain.startsWith("*") && host.endsWith(domain.replace("*", ""))) || domain.equals(host))
return null;
}
final String[] webViewUrl = new String[1];
if (!trigger.loadType.isEmpty() || !trigger.ifTopUrl.isEmpty() || !trigger.unlessTopUrl.isEmpty()) {
if (!trigger.getLoadType().isEmpty() || !trigger.getIfTopUrl().isEmpty() || !trigger.getUnlessTopUrl().isEmpty()) {
final CountDownLatch latch = new CountDownLatch(1);
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@ -110,19 +109,19 @@ public class ContentBlockerHandler {
}
if (webViewUrl[0] != null) {
if (!trigger.loadType.isEmpty()) {
if (!trigger.getLoadType().isEmpty()) {
URI cUrl = new URI(webViewUrl[0]);
String cHost = cUrl.getHost();
int cPort = cUrl.getPort();
String cScheme = cUrl.getScheme();
if ( (trigger.loadType.contains("first-party") && cHost != null && !(cScheme.equals(scheme) && cHost.equals(host) && cPort == port)) ||
(trigger.loadType.contains("third-party") && cHost != null && cHost.equals(host)) )
if ( (trigger.getLoadType().contains("first-party") && cHost != null && !(cScheme.equals(scheme) && cHost.equals(host) && cPort == port)) ||
(trigger.getLoadType().contains("third-party") && cHost != null && cHost.equals(host)) )
return null;
}
if (!trigger.ifTopUrl.isEmpty()) {
if (!trigger.getIfTopUrl().isEmpty()) {
boolean matchFound = false;
for (String topUrl : trigger.ifTopUrl) {
for (String topUrl : trigger.getIfTopUrl()) {
if (webViewUrl[0].startsWith(topUrl)) {
matchFound = true;
break;
@ -131,20 +130,20 @@ public class ContentBlockerHandler {
if (!matchFound)
return null;
}
if (!trigger.unlessTopUrl.isEmpty()) {
for (String topUrl : trigger.unlessTopUrl)
if (!trigger.getUnlessTopUrl().isEmpty()) {
for (String topUrl : trigger.getUnlessTopUrl())
if (webViewUrl[0].startsWith(topUrl))
return null;
}
}
switch (action.type) {
switch (action.getType()) {
case BLOCK:
return new WebResourceResponse("", "", null);
case CSS_DISPLAY_NONE:
final String cssSelector = action.selector;
final String cssSelector = action.getSelector();
final String jsScript = "(function(d) { " +
" function hide () { " +
" if (!d.getElementById('css-display-none-style')) { " +

View File

@ -0,0 +1,184 @@
package com.pichillilorenzo.flutter_inappwebview.content_blocker;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class ContentBlockerTrigger {
@NonNull
private String urlFilter;
private Pattern urlFilterPatternCompiled;
private Boolean urlFilterIsCaseSensitive;
private List<ContentBlockerTriggerResourceType> resourceType = new ArrayList<>();
private List<String> ifDomain = new ArrayList<>();
private List<String> unlessDomain = new ArrayList<>();
private List<String> loadType = new ArrayList<>();
private List<String> ifTopUrl = new ArrayList<>();
private List<String> unlessTopUrl = new ArrayList<>();
public ContentBlockerTrigger(@NonNull String urlFilter, @Nullable Boolean urlFilterIsCaseSensitive, @Nullable List<ContentBlockerTriggerResourceType> resourceType,
@Nullable List<String> ifDomain, @Nullable List<String> unlessDomain, @Nullable List<String> loadType,
@Nullable List<String> ifTopUrl, @Nullable List<String> unlessTopUrl) {
this.urlFilter = urlFilter;
this.urlFilterPatternCompiled = Pattern.compile(this.urlFilter);
this.resourceType = resourceType != null ? resourceType : this.resourceType;
this.urlFilterIsCaseSensitive = urlFilterIsCaseSensitive != null ? urlFilterIsCaseSensitive : false;
this.ifDomain = ifDomain != null ? ifDomain : this.ifDomain;
this.unlessDomain = unlessDomain != null ? unlessDomain : this.unlessDomain;
if ((!(this.ifDomain.isEmpty() || this.unlessDomain.isEmpty()) != false))
throw new AssertionError();
this.loadType = loadType != null ? loadType : this.loadType;
if ((this.loadType.size() > 2)) throw new AssertionError();
this.ifTopUrl = ifTopUrl != null ? ifTopUrl : this.ifTopUrl;
this.unlessTopUrl = unlessTopUrl != null ? unlessTopUrl : this.unlessTopUrl;
if ((!(this.ifTopUrl.isEmpty() || this.unlessTopUrl.isEmpty()) != false))
throw new AssertionError();
}
public static ContentBlockerTrigger fromMap(Map<String, Object> map) {
String urlFilter = (String) map.get("url-filter");
Boolean urlFilterIsCaseSensitive = (Boolean) map.get("url-filter-is-case-sensitive");
List<String> resourceTypeStringList = (List<String>) map.get("resource-type");
List<ContentBlockerTriggerResourceType> resourceType = new ArrayList<>();
if (resourceTypeStringList != null) {
for (String type : resourceTypeStringList) {
resourceType.add(ContentBlockerTriggerResourceType.fromValue(type));
}
} else {
resourceType.addAll(Arrays.asList(ContentBlockerTriggerResourceType.values()));
}
List<String> ifDomain = (List<String>) map.get("if-domain");
List<String> unlessDomain = (List<String>) map.get("unless-domain");
List<String> loadType = (List<String>) map.get("load-type");
List<String> ifTopUrl = (List<String>) map.get("if-top-url");
List<String> unlessTopUrl = (List<String>) map.get("unless-top-url");
return new ContentBlockerTrigger(urlFilter, urlFilterIsCaseSensitive, resourceType, ifDomain, unlessDomain, loadType, ifTopUrl, unlessTopUrl);
}
@NonNull
public String getUrlFilter() {
return urlFilter;
}
public void setUrlFilter(@NonNull String urlFilter) {
this.urlFilter = urlFilter;
}
public Pattern getUrlFilterPatternCompiled() {
return urlFilterPatternCompiled;
}
public void setUrlFilterPatternCompiled(Pattern urlFilterPatternCompiled) {
this.urlFilterPatternCompiled = urlFilterPatternCompiled;
}
public Boolean getUrlFilterIsCaseSensitive() {
return urlFilterIsCaseSensitive;
}
public void setUrlFilterIsCaseSensitive(Boolean urlFilterIsCaseSensitive) {
this.urlFilterIsCaseSensitive = urlFilterIsCaseSensitive;
}
public List<ContentBlockerTriggerResourceType> getResourceType() {
return resourceType;
}
public void setResourceType(List<ContentBlockerTriggerResourceType> resourceType) {
this.resourceType = resourceType;
}
public List<String> getIfDomain() {
return ifDomain;
}
public void setIfDomain(List<String> ifDomain) {
this.ifDomain = ifDomain;
}
public List<String> getUnlessDomain() {
return unlessDomain;
}
public void setUnlessDomain(List<String> unlessDomain) {
this.unlessDomain = unlessDomain;
}
public List<String> getLoadType() {
return loadType;
}
public void setLoadType(List<String> loadType) {
this.loadType = loadType;
}
public List<String> getIfTopUrl() {
return ifTopUrl;
}
public void setIfTopUrl(List<String> ifTopUrl) {
this.ifTopUrl = ifTopUrl;
}
public List<String> getUnlessTopUrl() {
return unlessTopUrl;
}
public void setUnlessTopUrl(List<String> unlessTopUrl) {
this.unlessTopUrl = unlessTopUrl;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ContentBlockerTrigger that = (ContentBlockerTrigger) o;
if (!urlFilter.equals(that.urlFilter)) return false;
if (!urlFilterPatternCompiled.equals(that.urlFilterPatternCompiled)) return false;
if (!urlFilterIsCaseSensitive.equals(that.urlFilterIsCaseSensitive)) return false;
if (!resourceType.equals(that.resourceType)) return false;
if (!ifDomain.equals(that.ifDomain)) return false;
if (!unlessDomain.equals(that.unlessDomain)) return false;
if (!loadType.equals(that.loadType)) return false;
if (!ifTopUrl.equals(that.ifTopUrl)) return false;
return unlessTopUrl.equals(that.unlessTopUrl);
}
@Override
public int hashCode() {
int result = urlFilter.hashCode();
result = 31 * result + urlFilterPatternCompiled.hashCode();
result = 31 * result + urlFilterIsCaseSensitive.hashCode();
result = 31 * result + resourceType.hashCode();
result = 31 * result + ifDomain.hashCode();
result = 31 * result + unlessDomain.hashCode();
result = 31 * result + loadType.hashCode();
result = 31 * result + ifTopUrl.hashCode();
result = 31 * result + unlessTopUrl.hashCode();
return result;
}
@Override
public String toString() {
return "ContentBlockerTrigger{" +
"urlFilter='" + urlFilter + '\'' +
", urlFilterPatternCompiled=" + urlFilterPatternCompiled +
", urlFilterIsCaseSensitive=" + urlFilterIsCaseSensitive +
", resourceType=" + resourceType +
", ifDomain=" + ifDomain +
", unlessDomain=" + unlessDomain +
", loadType=" + loadType +
", ifTopUrl=" + ifTopUrl +
", unlessTopUrl=" + unlessTopUrl +
'}';
}
}

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.ContentBlocker;
package com.pichillilorenzo.flutter_inappwebview.content_blocker;
public enum ContentBlockerTriggerResourceType {
DOCUMENT ("document"),
@ -29,6 +29,7 @@ public enum ContentBlockerTriggerResourceType {
throw new IllegalArgumentException("No enum constant: " + value);
}
@Override
public String toString() {
return this.value;
}

View File

@ -0,0 +1,95 @@
package com.pichillilorenzo.flutter_inappwebview.credential_database;
import android.content.Context;
import com.pichillilorenzo.flutter_inappwebview.types.URLCredential;
import com.pichillilorenzo.flutter_inappwebview.types.URLProtectionSpace;
import java.util.ArrayList;
import java.util.List;
public class CredentialDatabase {
private static CredentialDatabase instance;
static final String LOG_TAG = "CredentialDatabase";
// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 2;
public static final String DATABASE_NAME = "CredentialDatabase.db";
public URLProtectionSpaceDao protectionSpaceDao;
public URLCredentialDao credentialDao;
public CredentialDatabaseHelper db;
private CredentialDatabase() {}
private CredentialDatabase(CredentialDatabaseHelper db, URLProtectionSpaceDao protectionSpaceDao, URLCredentialDao credentialDao) {
this.db = db;
this.protectionSpaceDao = protectionSpaceDao;
this.credentialDao = credentialDao;
}
public static CredentialDatabase getInstance(Context context) {
if (instance != null)
return instance;
CredentialDatabaseHelper db = new CredentialDatabaseHelper(context);
instance = new CredentialDatabase(db, new URLProtectionSpaceDao(db), new URLCredentialDao(db));
return instance;
}
public List<URLCredential> getHttpAuthCredentials(String host, String protocol, String realm, Integer port) {
List<URLCredential> credentials = new ArrayList<>();
URLProtectionSpace protectionSpace = protectionSpaceDao.find(host, protocol, realm, port);
if (protectionSpace != null) {
credentials = credentialDao.getAllByProtectionSpaceId(protectionSpace.getId());
}
return credentials;
}
public void clearAllAuthCredentials() {
db.clearAllTables(db.getWritableDatabase());
}
public void removeHttpAuthCredentials(String host, String protocol, String realm, Integer port) {
URLProtectionSpace URLProtectionSpace = protectionSpaceDao.find(host, protocol, realm, port);
if (URLProtectionSpace != null) {
protectionSpaceDao.delete(URLProtectionSpace);
}
}
public void removeHttpAuthCredential(String host, String protocol, String realm, Integer port, String username, String password) {
URLProtectionSpace protectionSpace = protectionSpaceDao.find(host, protocol, realm, port);
if (protectionSpace != null) {
URLCredential credential = credentialDao.find(username, password, protectionSpace.getId());
credentialDao.delete(credential);
}
}
public void setHttpAuthCredential(String host, String protocol, String realm, Integer port, String username, String password) {
URLProtectionSpace protectionSpace = protectionSpaceDao.find(host, protocol, realm, port);
Long protectionSpaceId;
if (protectionSpace == null) {
protectionSpaceId = protectionSpaceDao.insert(new URLProtectionSpace(null, host, protocol, realm, port));
} else {
protectionSpaceId = protectionSpace.getId();
}
URLCredential credential = credentialDao.find(username, password, protectionSpaceId);
if (credential != null) {
boolean needUpdate = false;
if (!credential.getUsername().equals(username)) {
credential.setUsername(username);
needUpdate = true;
}
if (!credential.getPassword().equals(password)) {
credential.setPassword(password);
needUpdate = true;
}
if (needUpdate)
credentialDao.update(credential);
} else {
credential = new URLCredential(null, username, password, protectionSpaceId);
credential.setId(credentialDao.insert(credential));
}
}
}

View File

@ -1,13 +1,14 @@
package com.pichillilorenzo.flutter_inappwebview;
package com.pichillilorenzo.flutter_inappwebview.credential_database;
import android.os.Build;
import android.webkit.WebViewDatabase;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.Credential;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.CredentialDatabase;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.ProtectionSpace;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.types.URLCredential;
import com.pichillilorenzo.flutter_inappwebview.types.URLProtectionSpace;
import java.util.ArrayList;
import java.util.HashMap;
@ -17,7 +18,6 @@ import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
@RequiresApi(api = Build.VERSION_CODES.O)
public class CredentialDatabaseHandler implements MethodChannel.MethodCallHandler {
@ -34,15 +34,15 @@ public class CredentialDatabaseHandler implements MethodChannel.MethodCallHandle
}
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
public void onMethodCall(MethodCall call, @NonNull MethodChannel.Result result) {
switch (call.method) {
case "getAllAuthCredentials":
{
List<Map<String, Object>> allCredentials = new ArrayList<>();
List<ProtectionSpace> protectionSpaces = credentialDatabase.protectionSpaceDao.getAll();
for (ProtectionSpace protectionSpace : protectionSpaces) {
List<URLProtectionSpace> protectionSpaces = credentialDatabase.protectionSpaceDao.getAll();
for (URLProtectionSpace protectionSpace : protectionSpaces) {
List<Map<String, Object>> credentials = new ArrayList<>();
for (Credential credential : credentialDatabase.credentialDao.getAllByProtectionSpaceId(protectionSpace.id)) {
for (URLCredential credential : credentialDatabase.credentialDao.getAllByProtectionSpaceId(protectionSpace.getId())) {
credentials.add(credential.toMap());
}
Map<String, Object> obj = new HashMap<>();
@ -61,7 +61,7 @@ public class CredentialDatabaseHandler implements MethodChannel.MethodCallHandle
Integer port = (Integer) call.argument("port");
List<Map<String, Object>> credentials = new ArrayList<>();
for (Credential credential : credentialDatabase.getHttpAuthCredentials(host, protocol, realm, port)) {
for (URLCredential credential : credentialDatabase.getHttpAuthCredentials(host, protocol, realm, port)) {
credentials.add(credential.toMap());
}
result.success(credentials);

View File

@ -0,0 +1,66 @@
package com.pichillilorenzo.flutter_inappwebview.credential_database;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class CredentialDatabaseHelper extends SQLiteOpenHelper {
private static final String SQL_CREATE_PROTECTION_SPACE_TABLE =
"CREATE TABLE " + URLProtectionSpaceContract.FeedEntry.TABLE_NAME + " (" +
URLProtectionSpaceContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST + " TEXT NOT NULL," +
URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL + " TEXT," +
URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM + " TEXT," +
URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT + " INTEGER," +
"UNIQUE(" + URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST + ", " + URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL + ", " +
URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM + ", " + URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT +
")" +
");";
private static final String SQL_CREATE_CREDENTIAL_TABLE =
"CREATE TABLE " + URLCredentialContract.FeedEntry.TABLE_NAME + " (" +
URLCredentialContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
URLCredentialContract.FeedEntry.COLUMN_NAME_USERNAME + " TEXT NOT NULL," +
URLCredentialContract.FeedEntry.COLUMN_NAME_PASSWORD + " TEXT NOT NULL," +
URLCredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID + " INTEGER NOT NULL," +
"UNIQUE(" + URLCredentialContract.FeedEntry.COLUMN_NAME_USERNAME + ", " + URLCredentialContract.FeedEntry.COLUMN_NAME_PASSWORD + ", " +
URLCredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID +
")," +
"FOREIGN KEY (" + URLCredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID + ") REFERENCES " +
URLProtectionSpaceContract.FeedEntry.TABLE_NAME + " (" + URLProtectionSpaceContract.FeedEntry._ID + ") ON DELETE CASCADE" +
");";
private static final String SQL_DELETE_PROTECTION_SPACE_TABLE =
"DROP TABLE IF EXISTS " + URLProtectionSpaceContract.FeedEntry.TABLE_NAME;
private static final String SQL_DELETE_CREDENTIAL_TABLE =
"DROP TABLE IF EXISTS " + URLCredentialContract.FeedEntry.TABLE_NAME;
public CredentialDatabaseHelper(Context context) {
super(context, CredentialDatabase.DATABASE_NAME, null, CredentialDatabase.DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_PROTECTION_SPACE_TABLE);
db.execSQL(SQL_CREATE_CREDENTIAL_TABLE);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
db.execSQL(SQL_DELETE_PROTECTION_SPACE_TABLE);
db.execSQL(SQL_DELETE_CREDENTIAL_TABLE);
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
public void clearAllTables(SQLiteDatabase db) {
db.execSQL(SQL_DELETE_PROTECTION_SPACE_TABLE);
db.execSQL(SQL_DELETE_CREDENTIAL_TABLE);
onCreate(db);
}
}

View File

@ -1,9 +1,9 @@
package com.pichillilorenzo.flutter_inappwebview.CredentialDatabase;
package com.pichillilorenzo.flutter_inappwebview.credential_database;
import android.provider.BaseColumns;
public class CredentialContract {
private CredentialContract() {}
public class URLCredentialContract {
private URLCredentialContract() {}
/* Inner class that defines the table contents */
public static class FeedEntry implements BaseColumns {

View File

@ -0,0 +1,106 @@
package com.pichillilorenzo.flutter_inappwebview.credential_database;
import android.content.ContentValues;
import android.database.Cursor;
import com.pichillilorenzo.flutter_inappwebview.types.URLCredential;
import java.util.ArrayList;
import java.util.List;
public class URLCredentialDao {
CredentialDatabaseHelper credentialDatabaseHelper;
String[] projection = {
URLCredentialContract.FeedEntry._ID,
URLCredentialContract.FeedEntry.COLUMN_NAME_USERNAME,
URLCredentialContract.FeedEntry.COLUMN_NAME_PASSWORD,
URLCredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID
};
public URLCredentialDao(CredentialDatabaseHelper credentialDatabaseHelper) {
this.credentialDatabaseHelper = credentialDatabaseHelper;
}
public List<URLCredential> getAllByProtectionSpaceId(Long protectionSpaceId) {
String selection = URLCredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID + " = ?";
String[] selectionArgs = {protectionSpaceId.toString()};
Cursor cursor = credentialDatabaseHelper.getReadableDatabase().query(
URLCredentialContract.FeedEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
null
);
List<URLCredential> URLCredentials = new ArrayList<>();
while (cursor.moveToNext()) {
Long id = cursor.getLong(cursor.getColumnIndexOrThrow(URLCredentialContract.FeedEntry._ID));
String username = cursor.getString(cursor.getColumnIndexOrThrow(URLCredentialContract.FeedEntry.COLUMN_NAME_USERNAME));
String password = cursor.getString(cursor.getColumnIndexOrThrow(URLCredentialContract.FeedEntry.COLUMN_NAME_PASSWORD));
URLCredentials.add(new URLCredential(id, username, password, protectionSpaceId));
}
cursor.close();
return URLCredentials;
}
public URLCredential find(String username, String password, Long protectionSpaceId) {
String selection = URLCredentialContract.FeedEntry.COLUMN_NAME_USERNAME + " = ? AND " +
URLCredentialContract.FeedEntry.COLUMN_NAME_PASSWORD + " = ? AND " +
URLCredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID + " = ?";
String[] selectionArgs = {username, password, protectionSpaceId.toString()};
Cursor cursor = credentialDatabaseHelper.getReadableDatabase().query(
URLCredentialContract.FeedEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
null
);
URLCredential URLCredential = null;
if (cursor.moveToNext()) {
Long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(URLCredentialContract.FeedEntry._ID));
String rowUsername = cursor.getString(cursor.getColumnIndexOrThrow(URLCredentialContract.FeedEntry.COLUMN_NAME_USERNAME));
String rowPassword = cursor.getString(cursor.getColumnIndexOrThrow(URLCredentialContract.FeedEntry.COLUMN_NAME_PASSWORD));
URLCredential = new URLCredential(rowId, rowUsername, rowPassword, protectionSpaceId);
}
cursor.close();
return URLCredential;
}
public long insert(URLCredential urlCredential) {
ContentValues credentialValues = new ContentValues();
credentialValues.put(URLCredentialContract.FeedEntry.COLUMN_NAME_USERNAME, urlCredential.getUsername());
credentialValues.put(URLCredentialContract.FeedEntry.COLUMN_NAME_PASSWORD, urlCredential.getPassword());
credentialValues.put(URLCredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID, urlCredential.getProtectionSpaceId());
return credentialDatabaseHelper.getWritableDatabase().insert(URLCredentialContract.FeedEntry.TABLE_NAME, null, credentialValues);
}
public long update(URLCredential urlCredential) {
ContentValues credentialValues = new ContentValues();
credentialValues.put(URLCredentialContract.FeedEntry.COLUMN_NAME_USERNAME, urlCredential.getUsername());
credentialValues.put(URLCredentialContract.FeedEntry.COLUMN_NAME_PASSWORD, urlCredential.getPassword());
String whereClause = URLCredentialContract.FeedEntry.COLUMN_NAME_PROTECTION_SPACE_ID + " = ?";
String[] whereArgs = {urlCredential.getProtectionSpaceId().toString()};
return credentialDatabaseHelper.getWritableDatabase().update(URLCredentialContract.FeedEntry.TABLE_NAME, credentialValues, whereClause, whereArgs);
}
public long delete(URLCredential urlCredential) {
String whereClause = URLCredentialContract.FeedEntry._ID + " = ?";
String[] whereArgs = {urlCredential.getId().toString()};
return credentialDatabaseHelper.getWritableDatabase().delete(URLCredentialContract.FeedEntry.TABLE_NAME, whereClause, whereArgs);
}
}

View File

@ -1,9 +1,9 @@
package com.pichillilorenzo.flutter_inappwebview.CredentialDatabase;
package com.pichillilorenzo.flutter_inappwebview.credential_database;
import android.provider.BaseColumns;
public class ProtectionSpaceContract {
private ProtectionSpaceContract() {}
public class URLProtectionSpaceContract {
private URLProtectionSpaceContract() {}
/* Inner class that defines the table contents */
public static class FeedEntry implements BaseColumns {

View File

@ -0,0 +1,100 @@
package com.pichillilorenzo.flutter_inappwebview.credential_database;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.pichillilorenzo.flutter_inappwebview.types.URLProtectionSpace;
import java.util.ArrayList;
import java.util.List;
public class URLProtectionSpaceDao {
CredentialDatabaseHelper credentialDatabaseHelper;
String[] projection = {
URLProtectionSpaceContract.FeedEntry._ID,
URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST,
URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL,
URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM,
URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT
};
public URLProtectionSpaceDao(CredentialDatabaseHelper credentialDatabaseHelper) {
this.credentialDatabaseHelper = credentialDatabaseHelper;
}
public List<URLProtectionSpace> getAll() {
SQLiteDatabase readableDatabase = credentialDatabaseHelper.getReadableDatabase();
Cursor cursor = readableDatabase.query(
URLProtectionSpaceContract.FeedEntry.TABLE_NAME,
projection,
null,
null,
null,
null,
null
);
List<URLProtectionSpace> URLProtectionSpaces = new ArrayList<>();
while (cursor.moveToNext()) {
Long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(URLProtectionSpaceContract.FeedEntry._ID));
String rowHost = cursor.getString(cursor.getColumnIndexOrThrow(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST));
String rowProtocol = cursor.getString(cursor.getColumnIndexOrThrow(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL));
String rowRealm = cursor.getString(cursor.getColumnIndexOrThrow(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM));
Integer rowPort = cursor.getInt(cursor.getColumnIndexOrThrow(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT));
URLProtectionSpaces.add(new URLProtectionSpace(rowId, rowHost, rowProtocol, rowRealm, rowPort));
}
cursor.close();
return URLProtectionSpaces;
}
public URLProtectionSpace find(String host, String protocol, String realm, Integer port) {
SQLiteDatabase readableDatabase = credentialDatabaseHelper.getReadableDatabase();
String selection = URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST + " = ? AND " + URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL + " = ? AND " +
URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM + " = ? AND " + URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT + " = ?";
String[] selectionArgs = {host, protocol, realm, port.toString()};
Cursor cursor = readableDatabase.query(
URLProtectionSpaceContract.FeedEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
null
);
URLProtectionSpace URLProtectionSpace = null;
if (cursor.moveToNext()) {
Long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(URLProtectionSpaceContract.FeedEntry._ID));
String rowHost = cursor.getString(cursor.getColumnIndexOrThrow(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST));
String rowProtocol = cursor.getString(cursor.getColumnIndexOrThrow(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL));
String rowRealm = cursor.getString(cursor.getColumnIndexOrThrow(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM));
Integer rowPort = cursor.getInt(cursor.getColumnIndexOrThrow(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT));
URLProtectionSpace = new URLProtectionSpace(rowId, rowHost, rowProtocol, rowRealm, rowPort);
}
cursor.close();
return URLProtectionSpace;
}
public long insert(URLProtectionSpace URLProtectionSpace) {
ContentValues protectionSpaceValues = new ContentValues();
protectionSpaceValues.put(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_HOST, URLProtectionSpace.getHost());
protectionSpaceValues.put(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PROTOCOL, URLProtectionSpace.getProtocol());
protectionSpaceValues.put(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_REALM, URLProtectionSpace.getRealm());
protectionSpaceValues.put(URLProtectionSpaceContract.FeedEntry.COLUMN_NAME_PORT, URLProtectionSpace.getPort());
return credentialDatabaseHelper.getWritableDatabase().insert(URLProtectionSpaceContract.FeedEntry.TABLE_NAME, null, protectionSpaceValues);
};
public long delete(URLProtectionSpace URLProtectionSpace) {
String whereClause = URLProtectionSpaceContract.FeedEntry._ID + " = ?";
String[] whereArgs = {URLProtectionSpace.getId().toString()};
return credentialDatabaseHelper.getWritableDatabase().delete(URLProtectionSpaceContract.FeedEntry.TABLE_NAME, whereClause, whereArgs);
}
}

View File

@ -0,0 +1,8 @@
package com.pichillilorenzo.flutter_inappwebview.in_app_browser;
import android.content.Intent;
public interface ActivityResultListener {
/** @return true if the result has been handled. */
boolean onActivityResult(int requestCode, int resultCode, Intent data);
}

View File

@ -1,8 +1,10 @@
package com.pichillilorenzo.flutter_inappwebview.InAppBrowser;
package com.pichillilorenzo.flutter_inappwebview.in_app_browser;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
@ -21,13 +23,17 @@ import android.widget.SearchView;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewChromeClient;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebViewChromeClient;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebViewOptions;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewMethodHandler;
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 java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -35,7 +41,7 @@ import java.util.Map;
import io.flutter.plugin.common.MethodChannel;
public class InAppBrowserActivity extends AppCompatActivity {
public class InAppBrowserActivity extends AppCompatActivity implements InAppBrowserDelegate {
static final String LOG_TAG = "InAppBrowserActivity";
public MethodChannel channel;
@ -46,11 +52,10 @@ public class InAppBrowserActivity extends AppCompatActivity {
public Menu menu;
public SearchView searchView;
public InAppBrowserOptions options;
public Map<String, String> headers;
public ProgressBar progressBar;
public boolean isHidden = false;
public String fromActivity;
public List<ActivityResultListener> activityResultListeners = new ArrayList<>();
private List<ActivityResultListener> activityResultListeners = new ArrayList<>();
public InAppWebViewMethodHandler methodCallDelegate;
@Override
@ -71,7 +76,7 @@ public class InAppBrowserActivity extends AppCompatActivity {
webView = findViewById(R.id.webView);
webView.windowId = windowId;
webView.inAppBrowserActivity = this;
webView.inAppBrowserDelegate = this;
webView.channel = channel;
methodCallDelegate = new InAppWebViewMethodHandler(webView);
@ -79,8 +84,8 @@ public class InAppBrowserActivity extends AppCompatActivity {
fromActivity = b.getString("fromActivity");
HashMap<String, Object> optionsMap = (HashMap<String, Object>) b.getSerializable("options");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) b.getSerializable("contextMenu");
Map<String, Object> optionsMap = (Map<String, Object>) b.getSerializable("options");
Map<String, Object> contextMenu = (Map<String, Object>) b.getSerializable("contextMenu");
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) b.getSerializable("initialUserScripts");
options = new InAppBrowserOptions();
@ -90,7 +95,14 @@ public class InAppBrowserActivity extends AppCompatActivity {
webViewOptions.parse(optionsMap);
webView.options = webViewOptions;
webView.contextMenu = contextMenu;
webView.userScripts = initialUserScripts;
List<UserScript> userScripts = new ArrayList<>();
if (initialUserScripts != null) {
for (Map<String, Object> initialUserScript : initialUserScripts) {
userScripts.add(UserScript.fromMap(initialUserScript));
}
}
webView.userContentController.addUserOnlyScripts(userScripts);
actionBar = getSupportActionBar();
@ -103,22 +115,35 @@ public class InAppBrowserActivity extends AppCompatActivity {
resultMsg.sendToTarget();
}
} else {
Boolean isData = b.getBoolean("isData");
if (!isData) {
headers = (HashMap<String, String>) b.getSerializable("headers");
String url = b.getString("url");
webView.loadUrl(url, headers);
String initialFile = b.getString("initialFile");
Map<String, Object> initialUrlRequest = (Map<String, Object>) b.getSerializable("initialUrlRequest");
String initialData = b.getString("initialData");
if (initialFile != null) {
try {
webView.loadFile(initialFile);
} catch (IOException e) {
e.printStackTrace();
Log.e(LOG_TAG, initialFile + " asset file cannot be found!", e);
return;
}
}
else {
String data = b.getString("data");
else if (initialData != null) {
String mimeType = b.getString("mimeType");
String encoding = b.getString("encoding");
String baseUrl = b.getString("baseUrl");
String historyUrl = b.getString("historyUrl");
webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
webView.loadDataWithBaseURL(baseUrl, initialData, mimeType, encoding, historyUrl);
}
else if (initialUrlRequest != null) {
URLRequest urlRequest = URLRequest.fromMap(initialUrlRequest);
webView.loadUrl(urlRequest);
}
}
onBrowserCreated();
}
public void onBrowserCreated() {
Map<String, Object> obj = new HashMap<>();
channel.invokeMethod("onBrowserCreated", obj);
}
@ -134,14 +159,14 @@ public class InAppBrowserActivity extends AppCompatActivity {
progressBar = findViewById(R.id.progressBar);
if (!options.progressBar)
if (options.hideProgressBar)
progressBar.setMax(0);
else
progressBar.setMax(100);
actionBar.setDisplayShowTitleEnabled(!options.hideTitleBar);
if (!options.toolbarTop)
if (options.hideToolbarTop)
actionBar.hide();
if (options.toolbarTopBackgroundColor != null && !options.toolbarTopBackgroundColor.isEmpty())
@ -168,7 +193,7 @@ public class InAppBrowserActivity extends AppCompatActivity {
searchView.setQuery(webView.getUrl(), false);
if (options.toolbarTopFixedTitle.isEmpty())
if (options.toolbarTopFixedTitle == null || options.toolbarTopFixedTitle.isEmpty())
actionBar.setTitle(webView.getTitle());
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@ -316,8 +341,8 @@ public class InAppBrowserActivity extends AppCompatActivity {
show();
}
if (newOptionsMap.get("progressBar") != null && options.progressBar != newOptions.progressBar && progressBar != null) {
if (newOptions.progressBar)
if (newOptionsMap.get("hideProgressBar") != null && options.hideProgressBar != newOptions.hideProgressBar && progressBar != null) {
if (newOptions.hideProgressBar)
progressBar.setMax(0);
else
progressBar.setMax(100);
@ -326,17 +351,18 @@ public class InAppBrowserActivity extends AppCompatActivity {
if (newOptionsMap.get("hideTitleBar") != null && options.hideTitleBar != newOptions.hideTitleBar)
actionBar.setDisplayShowTitleEnabled(!newOptions.hideTitleBar);
if (newOptionsMap.get("toolbarTop") != null && options.toolbarTop != newOptions.toolbarTop) {
if (!newOptions.toolbarTop)
if (newOptionsMap.get("hideToolbarTop") != null && options.hideToolbarTop != newOptions.hideToolbarTop) {
if (newOptions.hideToolbarTop)
actionBar.hide();
else
actionBar.show();
}
if (newOptionsMap.get("toolbarTopBackgroundColor") != null && options.toolbarTopBackgroundColor != newOptions.toolbarTopBackgroundColor && !newOptions.toolbarTopBackgroundColor.isEmpty())
if (newOptionsMap.get("toolbarTopBackgroundColor") != null && !Util.objEquals(options.toolbarTopBackgroundColor, newOptions.toolbarTopBackgroundColor) &&
!newOptions.toolbarTopBackgroundColor.isEmpty())
actionBar.setBackgroundDrawable(new ColorDrawable(Color.parseColor(newOptions.toolbarTopBackgroundColor)));
if (newOptionsMap.get("toolbarTopFixedTitle") != null && options.toolbarTopFixedTitle != newOptions.toolbarTopFixedTitle && !newOptions.toolbarTopFixedTitle.isEmpty())
if (newOptionsMap.get("toolbarTopFixedTitle") != null && !Util.objEquals(options.toolbarTopFixedTitle, newOptions.toolbarTopFixedTitle) && !newOptions.toolbarTopFixedTitle.isEmpty())
actionBar.setTitle(newOptions.toolbarTopFixedTitle);
if (newOptionsMap.get("hideUrlBar") != null && options.hideUrlBar != newOptions.hideUrlBar) {
@ -359,6 +385,69 @@ public class InAppBrowserActivity extends AppCompatActivity {
return optionsMap;
}
@Override
public Activity getActivity() {
return this;
}
@Override
public void didChangeTitle(String title) {
if (options.toolbarTopFixedTitle == null || options.toolbarTopFixedTitle.isEmpty()) {
actionBar.setTitle(title);
}
}
@Override
public void didStartNavigation(String url) {
progressBar.setProgress(0);
searchView.setQuery(url, false);
}
@Override
public void didUpdateVisitedHistory(String url) {
searchView.setQuery(url, false);
}
@Override
public void didFinishNavigation(String url) {
searchView.setQuery(url, false);
progressBar.setProgress(0);
}
@Override
public void didFailNavigation(String url, int errorCode, String description) {
progressBar.setProgress(0);
}
@Override
public void didChangeProgress(int progress) {
progressBar.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
progressBar.setProgress(progress, true);
} else {
progressBar.setProgress(progress);
}
if (progress == 100) {
progressBar.setVisibility(View.GONE);
}
}
public List<ActivityResultListener> getActivityResultListeners() {
return activityResultListeners;
}
@Override
protected void onActivityResult (int requestCode,
int resultCode,
Intent data) {
for (ActivityResultListener listener : activityResultListeners) {
if (listener.onActivityResult(requestCode, resultCode, data)) {
return;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public void dispose() {
channel.setMethodCallHandler(null);
activityResultListeners.clear();
@ -385,26 +474,9 @@ public class InAppBrowserActivity extends AppCompatActivity {
}
}
@Override
protected void onActivityResult (int requestCode,
int resultCode,
Intent data) {
for (ActivityResultListener listener : activityResultListeners) {
if (listener.onActivityResult(requestCode, resultCode, data)) {
return;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onDestroy() {
dispose();
super.onDestroy();
}
public interface ActivityResultListener {
/** @return true if the result has been handled. */
boolean onActivityResult(int requestCode, int resultCode, Intent data);
}
}

View File

@ -0,0 +1,16 @@
package com.pichillilorenzo.flutter_inappwebview.in_app_browser;
import android.app.Activity;
import java.util.List;
public interface InAppBrowserDelegate {
Activity getActivity();
List<ActivityResultListener> getActivityResultListeners();
void didChangeTitle(String title);
void didStartNavigation(String url);
void didUpdateVisitedHistory(String url);
void didFinishNavigation(String url);
void didFailNavigation(String url, int errorCode, String description);
void didChangeProgress(int progress);
}

View File

@ -19,7 +19,7 @@
*
*/
package com.pichillilorenzo.flutter_inappwebview;
package com.pichillilorenzo.flutter_inappwebview.in_app_browser;
import android.app.Activity;
import android.content.Intent;
@ -32,12 +32,10 @@ import android.os.Bundle;
import android.webkit.MimeTypeMap;
import android.util.Log;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -62,49 +60,42 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
@Override
public void onMethodCall(final MethodCall call, final Result result) {
final Activity activity = Shared.activity;
final String uuid = (String) call.argument("uuid");
switch (call.method) {
case "openUrl":
case "openUrlRequest":
{
String url = (String) call.argument("url");
HashMap<String, Object> options = (HashMap<String, Object>) call.argument("options");
Map<String, String> headers = (Map<String, String>) call.argument("headers");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
String uuid = (String) call.argument("uuid");
Map<String, Object> urlRequest = (Map<String, Object>) call.argument("urlRequest");
Map<String, Object> options = (Map<String, Object>) call.argument("options");
Map<String, Object> contextMenu = (Map<String, Object>) call.argument("contextMenu");
Integer windowId = (Integer) call.argument("windowId");
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) call.argument("initialUserScripts");
openUrl(activity, uuid, url, options, headers, contextMenu, windowId, initialUserScripts);
openUrlRequest(activity, uuid, urlRequest, options, contextMenu, windowId, initialUserScripts);
}
result.success(true);
break;
case "openFile":
{
String url = (String) call.argument("url");
try {
url = Util.getUrlAsset(url);
} catch (IOException e) {
e.printStackTrace();
result.error(LOG_TAG, url + " asset file cannot be found!", e);
return;
}
HashMap<String, Object> options = (HashMap<String, Object>) call.argument("options");
Map<String, String> headers = (Map<String, String>) call.argument("headers");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
String uuid = (String) call.argument("uuid");
String assetFilePath = (String) call.argument("assetFilePath");
Map<String, Object> options = (Map<String, Object>) call.argument("options");
Map<String, Object> contextMenu = (Map<String, Object>) call.argument("contextMenu");
Integer windowId = (Integer) call.argument("windowId");
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) call.argument("initialUserScripts");
openUrl(activity, uuid, url, options, headers, contextMenu, windowId, initialUserScripts);
openFile(activity, uuid, assetFilePath, options, contextMenu, windowId, initialUserScripts);
}
result.success(true);
break;
case "openData":
{
HashMap<String, Object> options = (HashMap<String, Object>) call.argument("options");
String uuid = (String) call.argument("uuid");
Map<String, Object> options = (Map<String, Object>) call.argument("options");
String data = (String) call.argument("data");
String mimeType = (String) call.argument("mimeType");
String encoding = (String) call.argument("encoding");
String baseUrl = (String) call.argument("baseUrl");
String historyUrl = (String) call.argument("historyUrl");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
Map<String, Object> contextMenu = (Map<String, Object>) call.argument("contextMenu");
Integer windowId = (Integer) call.argument("windowId");
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) call.argument("initialUserScripts");
openData(activity, uuid, options, data, mimeType, encoding, baseUrl, historyUrl, contextMenu, windowId, initialUserScripts);
@ -198,32 +189,42 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
}
}
public void openUrl(Activity activity, String uuid, String url, HashMap<String, Object> options, Map<String, String> headers,
HashMap<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
public void openUrlRequest(Activity activity, String uuid, Map<String, Object> urlRequest, Map<String, Object> options,
Map<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
Bundle extras = new Bundle();
extras.putString("fromActivity", activity.getClass().getName());
extras.putString("url", url);
extras.putBoolean("isData", false);
extras.putSerializable("initialUrlRequest", (Serializable) urlRequest);
extras.putString("uuid", uuid);
extras.putSerializable("options", options);
extras.putSerializable("headers", (Serializable) headers);
extras.putSerializable("options", (Serializable) options);
extras.putSerializable("contextMenu", (Serializable) contextMenu);
extras.putInt("windowId", windowId != null ? windowId : -1);
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
startInAppBrowserActivity(activity, extras);
}
public void openData(Activity activity, String uuid, HashMap<String, Object> options, String data, String mimeType, String encoding,
String baseUrl, String historyUrl, HashMap<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
public void openFile(Activity activity, String uuid, String assetFilePath, Map<String, Object> options,
Map<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
Bundle extras = new Bundle();
extras.putBoolean("isData", true);
extras.putString("fromActivity", activity.getClass().getName());
extras.putString("initialFile", assetFilePath);
extras.putString("uuid", uuid);
extras.putSerializable("options", options);
extras.putString("data", data);
extras.putString("mimeType", mimeType);
extras.putString("encoding", encoding);
extras.putString("baseUrl", baseUrl);
extras.putString("historyUrl", historyUrl);
extras.putSerializable("options", (Serializable) options);
extras.putSerializable("contextMenu", (Serializable) contextMenu);
extras.putInt("windowId", windowId != null ? windowId : -1);
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
startInAppBrowserActivity(activity, extras);
}
public void openData(Activity activity, String uuid, Map<String, Object> options, String data, String mimeType, String encoding,
String baseUrl, String historyUrl, Map<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
Bundle extras = new Bundle();
extras.putString("uuid", uuid);
extras.putSerializable("options", (Serializable) options);
extras.putString("initialData", data);
extras.putString("initialMimeType", mimeType);
extras.putString("initialEncoding", encoding);
extras.putString("initialBaseUrl", baseUrl);
extras.putString("initialHistoryUrl", historyUrl);
extras.putSerializable("contextMenu", (Serializable) contextMenu);
extras.putInt("windowId", windowId != null ? windowId : -1);
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);

View File

@ -1,6 +1,9 @@
package com.pichillilorenzo.flutter_inappwebview.InAppBrowser;
package com.pichillilorenzo.flutter_inappwebview.in_app_browser;
import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.Options;
import com.pichillilorenzo.flutter_inappwebview.R;
import java.util.HashMap;
import java.util.Map;
@ -10,14 +13,16 @@ public class InAppBrowserOptions implements Options<InAppBrowserActivity> {
public static final String LOG_TAG = "InAppBrowserOptions";
public Boolean hidden = false;
public Boolean toolbarTop = true;
public String toolbarTopBackgroundColor = "";
public String toolbarTopFixedTitle = "";
public Boolean hideToolbarTop = false;
@Nullable
public String toolbarTopBackgroundColor = null;
@Nullable
public String toolbarTopFixedTitle;
public Boolean hideUrlBar = false;
public Boolean hideProgressBar = false;
public Boolean hideTitleBar = false;
public Boolean closeOnCannotGoBack = true;
public Boolean progressBar = true;
@Override
public InAppBrowserOptions parse(Map<String, Object> options) {
@ -32,8 +37,8 @@ public class InAppBrowserOptions implements Options<InAppBrowserActivity> {
case "hidden":
hidden = (Boolean) value;
break;
case "toolbarTop":
toolbarTop = (Boolean) value;
case "hideToolbarTop":
hideToolbarTop = (Boolean) value;
break;
case "toolbarTopBackgroundColor":
toolbarTopBackgroundColor = (String) value;
@ -50,8 +55,8 @@ public class InAppBrowserOptions implements Options<InAppBrowserActivity> {
case "closeOnCannotGoBack":
closeOnCannotGoBack = (Boolean) value;
break;
case "progressBar":
progressBar = (Boolean) value;
case "hideProgressBar":
hideProgressBar = (Boolean) value;
break;
}
}
@ -63,19 +68,22 @@ public class InAppBrowserOptions implements Options<InAppBrowserActivity> {
public Map<String, Object> toMap() {
Map<String, Object> options = new HashMap<>();
options.put("hidden", hidden);
options.put("toolbarTop", toolbarTop);
options.put("hideToolbarTop", hideToolbarTop);
options.put("toolbarTopBackgroundColor", toolbarTopBackgroundColor);
options.put("toolbarTopFixedTitle", toolbarTopFixedTitle);
options.put("hideUrlBar", hideUrlBar);
options.put("hideTitleBar", hideTitleBar);
options.put("closeOnCannotGoBack", closeOnCannotGoBack);
options.put("progressBar", progressBar);
options.put("hideProgressBar", hideProgressBar);
return options;
}
@Override
public Map<String, Object> getRealOptions(InAppBrowserActivity inAppBrowserActivity) {
Map<String, Object> realOptions = toMap();
realOptions.put("hideToolbarTop", inAppBrowserActivity.actionBar.isShowing());
realOptions.put("hideUrlBar", inAppBrowserActivity.menu.findItem(R.id.menu_search).isVisible());
realOptions.put("hideProgressBar", inAppBrowserActivity.progressBar.getMax() == 0);
return realOptions;
}
}

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import com.pichillilorenzo.flutter_inappwebview.Options;

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import static android.hardware.display.DisplayManager.DisplayListener;

View File

@ -1,7 +1,8 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Message;
import android.util.Log;
import android.view.View;
@ -10,11 +11,18 @@ import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewMethodHandler;
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.JavaScriptBridgeJS;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -38,10 +46,9 @@ public class FlutterWebView implements PlatformView {
DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
displayListenerProxy.onPreWebViewInitialization(displayManager);
String initialUrl = (String) params.get("initialUrl");
Map<String, Object> initialUrlRequest = (Map<String, Object>) params.get("initialUrlRequest");
final String initialFile = (String) params.get("initialFile");
final Map<String, String> initialData = (Map<String, String>) params.get("initialData");
final Map<String, String> initialHeaders = (Map<String, String>) params.get("initialHeaders");
Map<String, Object> initialOptions = (Map<String, Object>) params.get("initialOptions");
Map<String, Object> contextMenu = (Map<String, Object>) params.get("contextMenu");
Integer windowId = (Integer) params.get("windowId");
@ -57,7 +64,14 @@ public class FlutterWebView implements PlatformView {
"- See the official wiki here: https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects\n\n\n");
}
webView = new InAppWebView(context, this, id, windowId, options, contextMenu, containerView, initialUserScripts);
List<UserScript> userScripts = new ArrayList<>();
if (initialUserScripts != null) {
for (Map<String, Object> initialUserScript : initialUserScripts) {
userScripts.add(UserScript.fromMap(initialUserScript));
}
}
webView = new InAppWebView(context, channel, id, windowId, options, contextMenu, containerView, userScripts);
displayListenerProxy.onPostWebViewInitialization(displayManager);
methodCallDelegate = new InAppWebViewMethodHandler(webView);
@ -74,15 +88,14 @@ public class FlutterWebView implements PlatformView {
} else {
if (initialFile != null) {
try {
initialUrl = Util.getUrlAsset(initialFile);
webView.loadFile(initialFile);
} catch (IOException e) {
e.printStackTrace();
Log.e(LOG_TAG, initialFile + " asset file cannot be found!", e);
return;
}
}
if (initialData != null) {
else if (initialData != null) {
String data = initialData.get("data");
String mimeType = initialData.get("mimeType");
String encoding = initialData.get("encoding");
@ -90,8 +103,9 @@ public class FlutterWebView implements PlatformView {
String historyUrl = initialData.get("historyUrl");
webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
else {
webView.loadUrl(initialUrl, initialHeaders);
else if (initialUrlRequest != null) {
URLRequest urlRequest = URLRequest.fromMap(initialUrlRequest);
webView.loadUrl(urlRequest);
}
}
@ -114,13 +128,20 @@ public class FlutterWebView implements PlatformView {
methodCallDelegate = null;
}
if (webView != null) {
webView.inAppWebViewChromeClient.dispose();
webView.inAppWebViewClient.dispose();
webView.javaScriptBridgeInterface.dispose();
webView.removeJavascriptInterface(JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE)) {
WebViewCompat.setWebViewRenderProcessClient(webView, null);
}
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
if (webView.inAppWebViewRenderProcessClient != null) {
webView.inAppWebViewRenderProcessClient.dispose();
}
webView.inAppWebViewChromeClient.dispose();
webView.inAppWebViewClient.dispose();
webView.javaScriptBridgeInterface.dispose();
webView.dispose();
webView.destroy();
webView = null;
@ -134,13 +155,13 @@ public class FlutterWebView implements PlatformView {
@Override
public void onInputConnectionLocked() {
if (webView != null && webView.inAppBrowserActivity == null)
if (webView != null && webView.inAppBrowserDelegate == null)
webView.lockInputConnection();
}
@Override
public void onInputConnectionUnlocked() {
if (webView != null && webView.inAppBrowserActivity == null)
if (webView != null && webView.inAppBrowserDelegate == null)
webView.unlockInputConnection();
}

View File

@ -1,10 +1,8 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import android.content.Context;
import android.view.View;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.FlutterWebView;
import java.util.HashMap;
import io.flutter.plugin.common.BinaryMessenger;

View File

@ -19,11 +19,11 @@
*
*/
package com.pichillilorenzo.flutter_inappwebview;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import android.app.Activity;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.FlutterWebView;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.util.HashMap;
import java.util.Map;

View File

@ -1,9 +1,8 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
@ -41,14 +40,16 @@ import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.types.CreateWindowAction;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.ActivityResultListener;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserDelegate;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -61,12 +62,11 @@ import io.flutter.plugin.common.PluginRegistry;
import static android.app.Activity.RESULT_OK;
public class InAppWebViewChromeClient extends WebChromeClient implements PluginRegistry.ActivityResultListener, InAppBrowserActivity.ActivityResultListener {
public class InAppWebViewChromeClient extends WebChromeClient implements PluginRegistry.ActivityResultListener, ActivityResultListener {
protected static final String LOG_TAG = "IABWebChromeClient";
private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity;
public MethodChannel channel;
private InAppBrowserDelegate inAppBrowserDelegate;
private final MethodChannel channel;
public static Map<Integer, Message> windowWebViewMessages = new HashMap<>();
private static int windowAutoincrementId = 0;
@ -101,15 +101,14 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
private int mOriginalOrientation;
private int mOriginalSystemUiVisibility;
public InAppWebViewChromeClient(Object obj) {
if (obj instanceof InAppBrowserActivity) {
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
this.inAppBrowserActivity.activityResultListeners.add(this);
public InAppWebViewChromeClient(MethodChannel channel, InAppBrowserDelegate inAppBrowserDelegate) {
super();
this.channel = channel;
this.inAppBrowserDelegate = inAppBrowserDelegate;
if (this.inAppBrowserDelegate != null) {
this.inAppBrowserDelegate.getActivityResultListeners().add(this);
}
else if (obj instanceof FlutterWebView) {
this.flutterWebView = (FlutterWebView) obj;
}
this.channel = (this.inAppBrowserActivity != null) ? this.inAppBrowserActivity.channel : this.flutterWebView.channel;
if (Shared.registrar != null)
Shared.registrar.addActivityResultListener(this);
@ -122,13 +121,13 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
if (mCustomView == null) {
return null;
}
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
return BitmapFactory.decodeResource(activity.getApplicationContext().getResources(), 2130837573);
}
@Override
public void onHideCustomView() {
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
View decorView = getRootView();
((FrameLayout) decorView).removeView(this.mCustomView);
@ -149,7 +148,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
return;
}
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
View decorView = getRootView();
this.mCustomView = paramView;
@ -233,7 +232,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
};
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage);
@ -326,7 +325,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
};
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage);
@ -445,7 +444,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
};
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage);
@ -479,7 +478,6 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
Map<String, Object> obj = new HashMap<>();
obj.put("url", url);
obj.put("message", message);
obj.put("iosIsMainFrame", null);
channel.invokeMethod("onJsBeforeUnload", obj, new MethodChannel.Result() {
@Override
@ -544,7 +542,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
};
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage);
@ -579,25 +577,19 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
WebView.HitTestResult result = view.getHitTestResult();
String url = result.getExtra();
final Map<String, Object> obj = new HashMap<>();
obj.put("url", url);
obj.put("windowId", windowId);
obj.put("androidIsDialog", isDialog);
obj.put("androidIsUserGesture", isUserGesture);
obj.put("iosWKNavigationType", null);
obj.put("iosIsForMainFrame", null);
obj.put("iosAllowsCellularAccess", null);
obj.put("iosAllowsConstrainedNetworkAccess", null);
obj.put("iosAllowsExpensiveNetworkAccess", null);
obj.put("iosCachePolicy", null);
obj.put("iosHttpShouldHandleCookies", null);
obj.put("iosHttpShouldUsePipelining", null);
obj.put("iosNetworkServiceType", null);
obj.put("iosTimeoutInterval", null);
URLRequest request = new URLRequest(url, "GET", null, null);
CreateWindowAction createWindowAction = new CreateWindowAction(
request,
true,
isUserGesture,
false,
windowId,
isDialog
);
windowWebViewMessages.put(windowId, resultMsg);
channel.invokeMethod("onCreateWindow", obj, new MethodChannel.Result() {
channel.invokeMethod("onCreateWindow", createWindowAction.toMap(), new MethodChannel.Result() {
@Override
public void success(@Nullable Object result) {
boolean handledByClient = false;
@ -678,30 +670,23 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
@Override
public void onProgressChanged(WebView view, int progress) {
if (inAppBrowserActivity != null && inAppBrowserActivity.progressBar != null) {
inAppBrowserActivity.progressBar.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
inAppBrowserActivity.progressBar.setProgress(progress, true);
} else {
inAppBrowserActivity.progressBar.setProgress(progress);
}
if (progress == 100) {
inAppBrowserActivity.progressBar.setVisibility(View.GONE);
}
super.onProgressChanged(view, progress);
if (inAppBrowserDelegate != null) {
inAppBrowserDelegate.didChangeProgress(progress);
}
Map<String, Object> obj = new HashMap<>();
obj.put("progress", progress);
channel.invokeMethod("onProgressChanged", obj);
super.onProgressChanged(view, progress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (inAppBrowserActivity != null && inAppBrowserActivity.actionBar != null && inAppBrowserActivity.options.toolbarTopFixedTitle.isEmpty()) {
inAppBrowserActivity.actionBar.setTitle(title);
if (inAppBrowserDelegate != null) {
inAppBrowserDelegate.didChangeTitle(title);
}
Map<String, Object> obj = new HashMap<>();
@ -744,7 +729,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
protected ViewGroup getRootView() {
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
return (ViewGroup) activity.findViewById(android.R.id.content);
}
@ -838,7 +823,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
private boolean isFileNotEmpty(Uri uri) {
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
long length;
try {
@ -879,7 +864,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
if (chooserIntent.resolveActivity(activity.getPackageManager()) != null) {
activity.startActivityForResult(chooserIntent, PICKER_LEGACY);
} else {
@ -907,7 +892,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
chooserIntent.putExtra(Intent.EXTRA_INTENT, fileSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
if (chooserIntent.resolveActivity(activity.getPackageManager()) != null) {
activity.startActivityForResult(chooserIntent, PICKER);
} else {
@ -920,7 +905,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
protected boolean needsCameraPermission() {
boolean needed = false;
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
PackageManager packageManager = activity.getPackageManager();
try {
String[] requestedPermissions = packageManager.getPackageInfo(activity.getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS).requestedPermissions;
@ -1062,7 +1047,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
return Uri.fromFile(capturedFile);
}
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
// for versions 6.0+ (23) we use the FileProvider to avoid runtime permissions
String packageName = activity.getApplicationContext().getPackageName();
return FileProvider.getUriForFile(activity.getApplicationContext(), packageName + "." + fileProviderAuthorityExtension, capturedFile);
@ -1092,7 +1077,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
return new File(storageDir, filename);
}
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : Shared.activity;
File storageDir = activity.getApplicationContext().getExternalFilesDir(null);
return File.createTempFile(prefix, suffix, storageDir);
}
@ -1150,15 +1135,12 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
public void dispose() {
channel.setMethodCallHandler(null);
if (Shared.activityPluginBinding != null) {
Shared.activityPluginBinding.removeActivityResultListener(this);
}
if (inAppBrowserActivity != null) {
inAppBrowserActivity = null;
}
if (flutterWebView != null) {
flutterWebView = null;
if (inAppBrowserDelegate != null) {
inAppBrowserDelegate.getActivityResultListeners().clear();
inAppBrowserDelegate = null;
}
}
}

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
@ -23,10 +23,18 @@ import android.webkit.WebViewClient;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.Credential;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.CredentialDatabase;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.Util;
import com.pichillilorenzo.flutter_inappwebview.credential_database.CredentialDatabase;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserDelegate;
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
import com.pichillilorenzo.flutter_inappwebview.types.ClientCertChallenge;
import com.pichillilorenzo.flutter_inappwebview.types.HttpAuthenticationChallenge;
import com.pichillilorenzo.flutter_inappwebview.types.NavigationAction;
import com.pichillilorenzo.flutter_inappwebview.types.NavigationActionPolicy;
import com.pichillilorenzo.flutter_inappwebview.types.ServerTrustChallenge;
import com.pichillilorenzo.flutter_inappwebview.types.URLCredential;
import com.pichillilorenzo.flutter_inappwebview.types.URLProtectionSpace;
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
import java.io.ByteArrayInputStream;
import java.net.URI;
@ -42,19 +50,16 @@ import io.flutter.plugin.common.MethodChannel;
public class InAppWebViewClient extends WebViewClient {
protected static final String LOG_TAG = "IAWebViewClient";
private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity;
public MethodChannel channel;
private InAppBrowserDelegate inAppBrowserDelegate;
private final MethodChannel channel;
private static int previousAuthRequestFailureCount = 0;
private static List<Credential> credentialsProposed = null;
private static List<URLCredential> credentialsProposed = null;
public InAppWebViewClient(Object obj) {
public InAppWebViewClient(MethodChannel channel, InAppBrowserDelegate inAppBrowserDelegate) {
super();
if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
else if (obj instanceof FlutterWebView)
this.flutterWebView = (FlutterWebView) obj;
this.channel = (this.inAppBrowserActivity != null) ? this.inAppBrowserActivity.channel : this.flutterWebView.channel;
this.channel = channel;
this.inAppBrowserDelegate = inAppBrowserDelegate;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@ -62,34 +67,24 @@ public class InAppWebViewClient extends WebViewClient {
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
InAppWebView webView = (InAppWebView) view;
if (webView.options.useShouldOverrideUrlLoading) {
boolean isRedirect = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
onShouldOverrideUrlLoading(
webView,
request.getUrl().toString(),
request.getMethod(),
request.getRequestHeaders(),
request.isForMainFrame(),
request.hasGesture(),
request.isRedirect());
} else {
onShouldOverrideUrlLoading(
webView,
request.getUrl().toString(),
request.getMethod(),
request.getRequestHeaders(),
request.isForMainFrame(),
request.hasGesture(),
false);
isRedirect = request.isRedirect();
}
onShouldOverrideUrlLoading(
webView,
request.getUrl().toString(),
request.getMethod(),
request.getRequestHeaders(),
request.isForMainFrame(),
request.hasGesture(),
isRedirect);
if (webView.regexToCancelSubFramesLoadingCompiled != null) {
if (request.isForMainFrame())
return true;
else {
Matcher m = webView.regexToCancelSubFramesLoadingCompiled.matcher(request.getUrl().toString());
if (m.matches())
return true;
else
return false;
return m.matches();
}
} else {
// There isn't any way to load an URL for a frame that is not the main frame,
@ -110,154 +105,99 @@ public class InAppWebViewClient extends WebViewClient {
return false;
}
private void allowShouldOverrideUrlLoading(WebView webView, String url, Map<String, String> headers, boolean isForMainFrame) {
if (isForMainFrame) {
// There isn't any way to load an URL for a frame that is not the main frame,
// so call this only on main frame.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
webView.loadUrl(url, headers);
else
webView.loadUrl(url);
}
}
public void onShouldOverrideUrlLoading(final InAppWebView webView, final String url, final String method, final Map<String, String> headers,
final boolean isForMainFrame, boolean hasGesture, boolean isRedirect) {
Map<String, Object> obj = new HashMap<>();
obj.put("url", url);
obj.put("method", method);
obj.put("headers", headers);
obj.put("isForMainFrame", isForMainFrame);
obj.put("androidHasGesture", hasGesture);
obj.put("androidIsRedirect", isRedirect);
obj.put("iosWKNavigationType", null);
obj.put("iosAllowsCellularAccess", null);
obj.put("iosAllowsConstrainedNetworkAccess", null);
obj.put("iosAllowsExpensiveNetworkAccess", null);
obj.put("iosCachePolicy", null);
obj.put("iosHttpShouldHandleCookies", null);
obj.put("iosHttpShouldUsePipelining", null);
obj.put("iosNetworkServiceType", null);
obj.put("iosTimeoutInterval", null);
URLRequest request = new URLRequest(url, method, null, headers);
NavigationAction navigationAction = new NavigationAction(
request,
isForMainFrame,
hasGesture,
isRedirect
);
channel.invokeMethod("shouldOverrideUrlLoading", obj, new MethodChannel.Result() {
channel.invokeMethod("shouldOverrideUrlLoading", navigationAction.toMap(), new MethodChannel.Result() {
@Override
public void success(Object response) {
if (response != null) {
if (response != null) {
Map<String, Object> responseMap = (Map<String, Object>) response;
Integer action = (Integer) responseMap.get("action");
if (action != null) {
switch (action) {
case 1:
if (isForMainFrame) {
// There isn't any way to load an URL for a frame that is not the main frame,
// so call this only on main frame.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
webView.loadUrl(url, headers);
else
webView.loadUrl(url);
}
action = action != null ? action : NavigationActionPolicy.CANCEL.rawValue();
NavigationActionPolicy navigationActionPolicy = NavigationActionPolicy.fromValue(action);
if (navigationActionPolicy != null) {
switch (navigationActionPolicy) {
case ALLOW:
allowShouldOverrideUrlLoading(webView, url, headers, isForMainFrame);
return;
case 0:
case CANCEL:
default:
return;
}
}
return;
}
allowShouldOverrideUrlLoading(webView, url, headers, isForMainFrame);
}
@Override
public void error(String s, String s1, Object o) {
Log.d(LOG_TAG, "ERROR: " + s + " " + s1);
Log.e(LOG_TAG, "ERROR: " + s + " " + s1);
allowShouldOverrideUrlLoading(webView, url, headers, isForMainFrame);
}
@Override
public void notImplemented() {
allowShouldOverrideUrlLoading(webView, url, headers, isForMainFrame);
}
});
}
private void loadCustomJavaScriptOnPageStarted(WebView view) {
public void loadCustomJavaScriptOnPageStarted(WebView view) {
InAppWebView webView = (InAppWebView) view;
String jsPluginScriptsWrapped = webView.prepareAndWrapPluginUserScripts();
String jsUserScriptsAtDocumentStart = prepareUserScriptsAtDocumentStart(webView);
String js = wrapPluginAndUserScripts(jsPluginScriptsWrapped, jsUserScriptsAtDocumentStart, null);
String source = webView.userContentController.generateWrappedCodeForDocumentStart();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null);
webView.evaluateJavascript(source, (ValueCallback<String>) null);
} else {
webView.loadUrl("javascript:" + js.replaceAll("[\r\n]+", ""));
webView.loadUrl("javascript:" + source.replaceAll("[\r\n]+", ""));
}
}
private void loadCustomJavaScriptOnPageFinished(WebView view) {
public void loadCustomJavaScriptOnPageFinished(WebView view) {
InAppWebView webView = (InAppWebView) view;
// try to reload also custom scripts if they were not loaded during the onPageStarted event
String jsPluginScriptsWrapped = webView.prepareAndWrapPluginUserScripts();
String jsUserScriptsAtDocumentStart = prepareUserScriptsAtDocumentStart(webView);
String jsUserScriptsAtDocumentEnd = prepareUserScriptsAtDocumentEnd(webView);
String js = wrapPluginAndUserScripts(jsPluginScriptsWrapped, jsUserScriptsAtDocumentStart, jsUserScriptsAtDocumentEnd);
String source = webView.userContentController.generateWrappedCodeForDocumentEnd();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null);
webView.evaluateJavascript(source, (ValueCallback<String>) null);
} else {
webView.loadUrl("javascript:" + js.replaceAll("[\r\n]+", ""));
webView.loadUrl("javascript:" + source.replaceAll("[\r\n]+", ""));
}
}
private String prepareUserScripts(InAppWebView webView, int atDocumentInjectionTime) {
StringBuilder js = new StringBuilder();
for (Map<String, Object> userScript : webView.userScripts) {
Integer injectionTime = (Integer) userScript.get("injectionTime");
if ((injectionTime == null && atDocumentInjectionTime == 0) || (injectionTime != null && injectionTime == atDocumentInjectionTime)) {
String source = (String) userScript.get("source");
String contentWorldName = (String) userScript.get("contentWorld");
if (source != null) {
if (contentWorldName != null && !contentWorldName.equals("page")) {
String jsPluginScripts = webView.prepareAndWrapPluginUserScripts();
source = jsPluginScripts + "\n" + source;
}
if (contentWorldName != null && !webView.userScriptsContentWorlds.contains(contentWorldName)) {
webView.userScriptsContentWorlds.add(contentWorldName);
}
String sourceWrapped = webView.wrapSourceCodeInContentWorld(contentWorldName, source);
if (atDocumentInjectionTime == 0 && contentWorldName != null && !contentWorldName.equals("page")) {
// adds another wrapper because sometimes document.body is not ready and it is undefined, causing an error and not adding the iframe element.
sourceWrapped = InAppWebView.documentReadyWrapperJS.replace("$PLACEHOLDER_VALUE", sourceWrapped)
.replace("$PLACEHOLDER_VALUE", sourceWrapped);
}
js.append(sourceWrapped);
}
}
}
return js.toString();
}
private String prepareUserScriptsAtDocumentStart(InAppWebView webView) {
return prepareUserScripts(webView, 0);
}
private String prepareUserScriptsAtDocumentEnd(InAppWebView webView) {
return prepareUserScripts(webView, 1);
}
private String wrapPluginAndUserScripts(String jsPluginScriptsWrapped, @Nullable String jsUserScriptsAtDocumentStart, @Nullable String jsUserScriptsAtDocumentEnd) {
String jsUserScriptsAtDocumentStartWrapped = jsUserScriptsAtDocumentStart == null || jsUserScriptsAtDocumentStart.isEmpty() ? "" :
InAppWebView.userScriptsAtDocumentStartWrapperJS.replace("$PLACEHOLDER_VALUE", jsUserScriptsAtDocumentStart);
String jsUserScriptsAtDocumentEndWrapped = jsUserScriptsAtDocumentEnd == null || jsUserScriptsAtDocumentEnd.isEmpty() ? "" :
InAppWebView.userScriptsAtDocumentEndWrapperJS.replace("$PLACEHOLDER_VALUE", jsUserScriptsAtDocumentEnd);
return jsPluginScriptsWrapped + "\n" + jsUserScriptsAtDocumentStartWrapped + "\n" + jsUserScriptsAtDocumentEndWrapped;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
final InAppWebView webView = (InAppWebView) view;
webView.resetUserScriptsContentWorlds();
webView.isLoading = true;
webView.userContentController.resetContentWorlds();
loadCustomJavaScriptOnPageStarted(webView);
super.onPageStarted(view, url, favicon);
webView.isLoading = true;
if (inAppBrowserActivity != null && inAppBrowserActivity.searchView != null && !url.equals(inAppBrowserActivity.searchView.getQuery().toString())) {
inAppBrowserActivity.searchView.setQuery(url, false);
if (inAppBrowserDelegate != null) {
inAppBrowserDelegate.didStartNavigation(url);
}
Map<String, Object> obj = new HashMap<>();
@ -268,14 +208,16 @@ public class InAppWebViewClient extends WebViewClient {
public void onPageFinished(WebView view, String url) {
final InAppWebView webView = (InAppWebView) view;
webView.isLoading = false;
loadCustomJavaScriptOnPageFinished(webView);
previousAuthRequestFailureCount = 0;
credentialsProposed = null;
super.onPageFinished(view, url);
webView.isLoading = false;
previousAuthRequestFailureCount = 0;
credentialsProposed = null;
if (inAppBrowserDelegate != null) {
inAppBrowserDelegate.didFinishNavigation(url);
}
// WebView not storing cookies reliable to local device storage
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@ -284,7 +226,7 @@ public class InAppWebViewClient extends WebViewClient {
CookieSyncManager.getInstance().sync();
}
String js = InAppWebView.platformReadyJS;
String js = JavaScriptBridgeJS.PLATFORM_READY_JS_SOURCE;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null);
@ -299,17 +241,23 @@ public class InAppWebViewClient extends WebViewClient {
@Override
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
super.doUpdateVisitedHistory(view, url, isReload);
url = view.getUrl();
if (inAppBrowserDelegate != null) {
inAppBrowserDelegate.didUpdateVisitedHistory(url);
}
Map<String, Object> obj = new HashMap<>();
// url argument sometimes doesn't contain the new changed URL, so we get it again from the webview.
obj.put("url", url);
obj.put("androidIsReload", isReload);
channel.invokeMethod("onUpdateVisitedHistory", obj);
super.doUpdateVisitedHistory(view, url, isReload);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
final InAppWebView webView = (InAppWebView) view;
if (webView.options.disableDefaultErrorPage) {
@ -321,6 +269,10 @@ public class InAppWebViewClient extends WebViewClient {
previousAuthRequestFailureCount = 0;
credentialsProposed = null;
if (inAppBrowserDelegate != null) {
inAppBrowserDelegate.didFailNavigation(failingUrl, errorCode, description);
}
Map<String, Object> obj = new HashMap<>();
obj.put("url", failingUrl);
obj.put("code", errorCode);
@ -343,9 +295,6 @@ public class InAppWebViewClient extends WebViewClient {
}
}
/**
* On received http auth request.
*/
@Override
public void onReceivedHttpAuthRequest(final WebView view, final HttpAuthHandler handler, final String host, final String realm) {
@ -374,7 +323,18 @@ public class InAppWebViewClient extends WebViewClient {
obj.put("port", port);
obj.put("previousFailureCount", previousAuthRequestFailureCount);
channel.invokeMethod("onReceivedHttpAuthRequest", obj, new MethodChannel.Result() {
if (credentialsProposed == null)
credentialsProposed = CredentialDatabase.getInstance(view.getContext()).getHttpAuthCredentials(host, protocol, realm, port);
URLCredential credentialProposed = null;
if (credentialsProposed != null && credentialsProposed.size() > 0) {
credentialProposed = credentialsProposed.get(0);
}
URLProtectionSpace protectionSpace = new URLProtectionSpace(host, protocol, realm, port, view.getCertificate(), null);
HttpAuthenticationChallenge challenge = new HttpAuthenticationChallenge(protectionSpace, previousAuthRequestFailureCount, credentialProposed);
channel.invokeMethod("onReceivedHttpAuthRequest", challenge.toMap(), new MethodChannel.Result() {
@Override
public void success(Object response) {
if (response != null) {
@ -386,17 +346,15 @@ public class InAppWebViewClient extends WebViewClient {
String username = (String) responseMap.get("username");
String password = (String) responseMap.get("password");
Boolean permanentPersistence = (Boolean) responseMap.get("permanentPersistence");
if (permanentPersistence != null && permanentPersistence && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (permanentPersistence != null && permanentPersistence) {
CredentialDatabase.getInstance(view.getContext()).setHttpAuthCredential(host, protocol, realm, port, username, password);
}
handler.proceed(username, password);
return;
case 2:
if (credentialsProposed == null)
credentialsProposed = CredentialDatabase.getInstance(view.getContext()).getHttpAuthCredentials(host, protocol, realm, port);
if (credentialsProposed.size() > 0) {
Credential credential = credentialsProposed.remove(0);
handler.proceed(credential.username, credential.password);
URLCredential credential = credentialsProposed.remove(0);
handler.proceed(credential.getUsername(), credential.getPassword());
} else {
handler.cancel();
}
@ -429,10 +387,10 @@ public class InAppWebViewClient extends WebViewClient {
}
@Override
public void onReceivedSslError(final WebView view, final SslErrorHandler handler, final SslError error) {
public void onReceivedSslError(final WebView view, final SslErrorHandler handler, final SslError sslError) {
URI uri;
try {
uri = new URI(view.getUrl());
uri = new URI(sslError.getUrl());
} catch (URISyntaxException e) {
e.printStackTrace();
handler.cancel();
@ -444,40 +402,10 @@ public class InAppWebViewClient extends WebViewClient {
final String realm = null;
final int port = uri.getPort();
Map<String, Object> obj = new HashMap<>();
obj.put("host", host);
obj.put("protocol", protocol);
obj.put("realm", realm);
obj.put("port", port);
obj.put("androidError", error.getPrimaryError());
obj.put("iosError", null);
obj.put("sslCertificate", InAppWebView.getCertificateMap(error.getCertificate()));
URLProtectionSpace protectionSpace = new URLProtectionSpace(host, protocol, realm, port, sslError.getCertificate(), sslError);
ServerTrustChallenge challenge = new ServerTrustChallenge(protectionSpace);
String message;
switch (error.getPrimaryError()) {
case SslError.SSL_DATE_INVALID:
message = "The date of the certificate is invalid";
break;
case SslError.SSL_EXPIRED:
message = "The certificate has expired";
break;
case SslError.SSL_IDMISMATCH:
message = "Hostname mismatch";
break;
default:
case SslError.SSL_INVALID:
message = "A generic error occurred";
break;
case SslError.SSL_NOTYETVALID:
message = "The certificate is not yet valid";
break;
case SslError.SSL_UNTRUSTED:
message = "The certificate authority is not trusted";
break;
}
obj.put("message", message);
channel.invokeMethod("onReceivedServerTrustAuthRequest", obj, new MethodChannel.Result() {
channel.invokeMethod("onReceivedServerTrustAuthRequest", challenge.toMap(), new MethodChannel.Result() {
@Override
public void success(Object response) {
if (response != null) {
@ -496,7 +424,7 @@ public class InAppWebViewClient extends WebViewClient {
}
}
InAppWebViewClient.super.onReceivedSslError(view, handler, error);
InAppWebViewClient.super.onReceivedSslError(view, handler, sslError);
}
@Override
@ -506,7 +434,7 @@ public class InAppWebViewClient extends WebViewClient {
@Override
public void notImplemented() {
InAppWebViewClient.super.onReceivedSslError(view, handler, error);
InAppWebViewClient.super.onReceivedSslError(view, handler, sslError);
}
});
}
@ -524,16 +452,15 @@ public class InAppWebViewClient extends WebViewClient {
return;
}
final String host = request.getHost();
final String protocol = uri.getScheme();
final String realm = null;
final int port = request.getPort();
Map<String, Object> obj = new HashMap<>();
obj.put("host", request.getHost());
obj.put("protocol", protocol);
obj.put("realm", realm);
obj.put("port", request.getPort());
URLProtectionSpace protectionSpace = new URLProtectionSpace(host, protocol, realm, port, view.getCertificate(), null);
ClientCertChallenge challenge = new ClientCertChallenge(protectionSpace, request.getPrincipals(), request.getKeyTypes());
channel.invokeMethod("onReceivedClientCertRequest", obj, new MethodChannel.Result() {
channel.invokeMethod("onReceivedClientCertRequest", challenge.toMap(), new MethodChannel.Result() {
@Override
public void success(Object response) {
if (response != null) {
@ -644,9 +571,7 @@ public class InAppWebViewClient extends WebViewClient {
if (webView.options.useShouldInterceptRequest) {
WebResourceResponse onShouldInterceptResponse = onShouldInterceptRequest(url);
if (onShouldInterceptResponse != null) {
return onShouldInterceptResponse;
}
return onShouldInterceptResponse;
}
URI uri;
@ -669,7 +594,6 @@ public class InAppWebViewClient extends WebViewClient {
if (webView.options.resourceCustomSchemes != null && webView.options.resourceCustomSchemes.contains(scheme)) {
final Map<String, Object> obj = new HashMap<>();
obj.put("url", url);
obj.put("scheme", scheme);
Util.WaitFlutterResult flutterResult;
try {
@ -686,14 +610,14 @@ public class InAppWebViewClient extends WebViewClient {
Map<String, Object> res = (Map<String, Object>) flutterResult.result;
WebResourceResponse response = null;
try {
response = webView.contentBlockerHandler.checkUrl(webView, url, res.get("content-type").toString());
response = webView.contentBlockerHandler.checkUrl(webView, url, res.get("contentType").toString());
} catch (Exception e) {
e.printStackTrace();
}
if (response != null)
return response;
byte[] data = (byte[]) res.get("data");
return new WebResourceResponse(res.get("content-type").toString(), res.get("content-encoding").toString(), new ByteArrayInputStream(data));
return new WebResourceResponse(res.get("contentType").toString(), res.get("contentEncoding").toString(), new ByteArrayInputStream(data));
}
}
@ -717,9 +641,7 @@ public class InAppWebViewClient extends WebViewClient {
if (webView.options.useShouldInterceptRequest) {
WebResourceResponse onShouldInterceptResponse = onShouldInterceptRequest(request);
if (onShouldInterceptResponse != null) {
return onShouldInterceptResponse;
}
return onShouldInterceptResponse;
}
return shouldInterceptRequest(view, url);
@ -729,9 +651,9 @@ public class InAppWebViewClient extends WebViewClient {
String url = request instanceof String ? (String) request : null;
String method = "GET";
Map<String, String> headers = null;
Boolean hasGesture = false;
Boolean isForMainFrame = true;
Boolean isRedirect = false;
boolean hasGesture = false;
boolean isForMainFrame = true;
boolean isRedirect = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && request instanceof WebResourceRequest) {
WebResourceRequest webResourceRequest = (WebResourceRequest) request;
@ -869,12 +791,8 @@ public class InAppWebViewClient extends WebViewClient {
}
public void dispose() {
channel.setMethodCallHandler(null);
if (inAppBrowserActivity != null) {
inAppBrowserActivity = null;
}
if (flutterWebView != null) {
flutterWebView = null;
if (inAppBrowserDelegate != null) {
inAppBrowserDelegate = null;
}
}
}

View File

@ -1,11 +1,11 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.webkit.WebSettings;
import com.pichillilorenzo.flutter_inappwebview.Options;
import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType;
import java.util.ArrayList;
import java.util.HashMap;

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import android.util.Log;
import android.webkit.WebView;
@ -9,8 +9,6 @@ import androidx.webkit.WebViewFeature;
import androidx.webkit.WebViewRenderProcess;
import androidx.webkit.WebViewRenderProcessClient;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import java.util.HashMap;
import java.util.Map;
@ -19,17 +17,12 @@ import io.flutter.plugin.common.MethodChannel;
public class InAppWebViewRenderProcessClient extends WebViewRenderProcessClient {
protected static final String LOG_TAG = "IAWRenderProcessClient";
private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity;
public MethodChannel channel;
private final MethodChannel channel;
public InAppWebViewRenderProcessClient(Object obj) {
public InAppWebViewRenderProcessClient(MethodChannel channel) {
super();
if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
else if (obj instanceof FlutterWebView)
this.flutterWebView = (FlutterWebView) obj;
this.channel = (this.inAppBrowserActivity != null) ? this.inAppBrowserActivity.channel : this.flutterWebView.channel;
this.channel = channel;
}
@Override
@ -95,4 +88,8 @@ public class InAppWebViewRenderProcessClient extends WebViewRenderProcessClient
}
});
}
void dispose() {
}
}

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import static android.content.Context.INPUT_METHOD_SERVICE;

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import android.os.Handler;
import android.os.IBinder;

View File

@ -0,0 +1,41 @@
package com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js;
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
import com.pichillilorenzo.flutter_inappwebview.types.UserScriptInjectionTime;
public class ConsoleLogJS {
public static final String CONSOLE_LOG_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_CONSOLE_LOG_JS_PLUGIN_SCRIPT";
public static final PluginScript CONSOLE_LOG_JS_PLUGIN_SCRIPT = new PluginScript(
ConsoleLogJS.CONSOLE_LOG_JS_PLUGIN_SCRIPT_GROUP_NAME,
ConsoleLogJS.CONSOLE_LOG_JS_SOURCE,
UserScriptInjectionTime.AT_DOCUMENT_START,
null,
true
);
public static final String CONSOLE_LOG_JS_SOURCE = "(function(console) {" +
" var oldLogs = {" +
" 'log': console.log," +
" 'debug': console.debug," +
" 'error': console.error," +
" 'info': console.info," +
" 'warn': console.warn" +
" };" +
" for (var k in oldLogs) {" +
" (function(oldLog) {" +
" console[oldLog] = function() {" +
" var message = '';" +
" for (var i in arguments) {" +
" if (message == '') {" +
" message += arguments[i];" +
" }" +
" else {" +
" message += ' ' + arguments[i];" +
" }" +
" }" +
" oldLogs[oldLog].call(console, message);" +
" }" +
" })(k);" +
" }" +
"})(window.console);";
}

View File

@ -0,0 +1,232 @@
package com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js;
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
import com.pichillilorenzo.flutter_inappwebview.types.UserScriptInjectionTime;
public class InterceptAjaxRequestJS {
public static final String INTERCEPT_AJAX_REQUEST_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_INTERCEPT_AJAX_REQUEST_JS_PLUGIN_SCRIPT";
public static final String FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE = JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "._useShouldInterceptAjaxRequest";
public static final PluginScript INTERCEPT_AJAX_REQUEST_JS_PLUGIN_SCRIPT = new PluginScript(
InterceptAjaxRequestJS.INTERCEPT_AJAX_REQUEST_JS_PLUGIN_SCRIPT_GROUP_NAME,
InterceptAjaxRequestJS.INTERCEPT_AJAX_REQUEST_JS_SOURCE,
UserScriptInjectionTime.AT_DOCUMENT_START,
null,
true
);
public static final String INTERCEPT_AJAX_REQUEST_JS_SOURCE = "(function(ajax) {" +
" var w = (window.top == null || window.top === window) ? window : window.top;" +
" w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE + " = true;" +
" var send = ajax.prototype.send;" +
" var open = ajax.prototype.open;" +
" var setRequestHeader = ajax.prototype.setRequestHeader;" +
" ajax.prototype._flutter_inappwebview_url = null;" +
" ajax.prototype._flutter_inappwebview_method = null;" +
" ajax.prototype._flutter_inappwebview_isAsync = null;" +
" ajax.prototype._flutter_inappwebview_user = null;" +
" ajax.prototype._flutter_inappwebview_password = null;" +
" ajax.prototype._flutter_inappwebview_password = null;" +
" ajax.prototype._flutter_inappwebview_already_onreadystatechange_wrapped = false;" +
" ajax.prototype._flutter_inappwebview_request_headers = {};" +
" function convertRequestResponse(request, callback) {" +
" if (request.response != null && request.responseType != null) {" +
" switch (request.responseType) {" +
" case 'arraybuffer':" +
" callback(new Uint8Array(request.response));" +
" return;" +
" case 'blob':" +
" const reader = new FileReader();" +
" reader.addEventListener('loadend', function() { " +
" callback(new Uint8Array(reader.result));" +
" });" +
" reader.readAsArrayBuffer(blob);" +
" return;" +
" case 'document':" +
" callback(request.response.documentElement.outerHTML);" +
" return;" +
" case 'json':" +
" callback(request.response);" +
" return;" +
" };" +
" }" +
" callback(null);" +
" };" +
" ajax.prototype.open = function(method, url, isAsync, user, password) {" +
" isAsync = (isAsync != null) ? isAsync : true;" +
" this._flutter_inappwebview_url = url;" +
" this._flutter_inappwebview_method = method;" +
" this._flutter_inappwebview_isAsync = isAsync;" +
" this._flutter_inappwebview_user = user;" +
" this._flutter_inappwebview_password = password;" +
" this._flutter_inappwebview_request_headers = {};" +
" open.call(this, method, url, isAsync, user, password);" +
" };" +
" ajax.prototype.setRequestHeader = function(header, value) {" +
" this._flutter_inappwebview_request_headers[header] = value;" +
" setRequestHeader.call(this, header, value);" +
" };" +
" function handleEvent(e) {" +
" var self = this;" +
" var w = (window.top == null || window.top === window) ? window : window.top;" +
" if (w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE + " == null || w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE + " == true) {" +
" var headers = this.getAllResponseHeaders();" +
" var responseHeaders = {};" +
" if (headers != null) {" +
" var arr = headers.trim().split(/[\\r\\n]+/);" +
" arr.forEach(function (line) {" +
" var parts = line.split(': ');" +
" var header = parts.shift();" +
" var value = parts.join(': ');" +
" responseHeaders[header] = value;" +
" });" +
" }" +
" convertRequestResponse(this, function(response) {" +
" var ajaxRequest = {" +
" method: self._flutter_inappwebview_method," +
" url: self._flutter_inappwebview_url," +
" isAsync: self._flutter_inappwebview_isAsync," +
" user: self._flutter_inappwebview_user," +
" password: self._flutter_inappwebview_password," +
" withCredentials: self.withCredentials," +
" headers: self._flutter_inappwebview_request_headers," +
" readyState: self.readyState," +
" status: self.status," +
" responseURL: self.responseURL," +
" responseType: self.responseType," +
" response: response," +
" responseText: (self.responseType == 'text' || self.responseType == '') ? self.responseText : null," +
" responseXML: (self.responseType == 'document' && self.responseXML != null) ? self.responseXML.documentElement.outerHTML : null," +
" statusText: self.statusText," +
" responseHeaders, responseHeaders," +
" event: {" +
" type: e.type," +
" loaded: e.loaded," +
" lengthComputable: e.lengthComputable," +
" total: e.total" +
" }" +
" };" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('onAjaxProgress', ajaxRequest).then(function(result) {" +
" if (result != null) {" +
" switch (result) {" +
" case 0:" +
" self.abort();" +
" return;" +
" };" +
" }" +
" });" +
" });" +
" }" +
" };" +
" ajax.prototype.send = function(data) {" +
" var self = this;" +
" var w = (window.top == null || window.top === window) ? window : window.top;" +
" if (w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE + " == null || w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE + " == true) {" +
" if (!this._flutter_inappwebview_already_onreadystatechange_wrapped) {" +
" this._flutter_inappwebview_already_onreadystatechange_wrapped = true;" +
" var onreadystatechange = this.onreadystatechange;" +
" this.onreadystatechange = function() {" +
" var w = (window.top == null || window.top === window) ? window : window.top;" +
" if (w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE + " == null || w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE + " == true) {" +
" var headers = this.getAllResponseHeaders();" +
" var responseHeaders = {};" +
" if (headers != null) {" +
" var arr = headers.trim().split(/[\\r\\n]+/);" +
" arr.forEach(function (line) {" +
" var parts = line.split(': ');" +
" var header = parts.shift();" +
" var value = parts.join(': ');" +
" responseHeaders[header] = value;" +
" });" +
" }" +
" convertRequestResponse(this, function(response) {" +
" var ajaxRequest = {" +
" method: self._flutter_inappwebview_method," +
" url: self._flutter_inappwebview_url," +
" isAsync: self._flutter_inappwebview_isAsync," +
" user: self._flutter_inappwebview_user," +
" password: self._flutter_inappwebview_password," +
" withCredentials: self.withCredentials," +
" headers: self._flutter_inappwebview_request_headers," +
" readyState: self.readyState," +
" status: self.status," +
" responseURL: self.responseURL," +
" responseType: self.responseType," +
" response: response," +
" responseText: (self.responseType == 'text' || self.responseType == '') ? self.responseText : null," +
" responseXML: (self.responseType == 'document' && self.responseXML != null) ? self.responseXML.documentElement.outerHTML : null," +
" statusText: self.statusText," +
" responseHeaders: responseHeaders" +
" };" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('onAjaxReadyStateChange', ajaxRequest).then(function(result) {" +
" if (result != null) {" +
" switch (result) {" +
" case 0:" +
" self.abort();" +
" return;" +
" };" +
" }" +
" if (onreadystatechange != null) {" +
" onreadystatechange();" +
" }" +
" });" +
" });" +
" } else if (onreadystatechange != null) {" +
" onreadystatechange();" +
" }" +
" };" +
" }" +
" this.addEventListener('loadstart', handleEvent);" +
" this.addEventListener('load', handleEvent);" +
" this.addEventListener('loadend', handleEvent);" +
" this.addEventListener('progress', handleEvent);" +
" this.addEventListener('error', handleEvent);" +
" this.addEventListener('abort', handleEvent);" +
" this.addEventListener('timeout', handleEvent);" +
" var ajaxRequest = {" +
" data: data," +
" method: this._flutter_inappwebview_method," +
" url: this._flutter_inappwebview_url," +
" isAsync: this._flutter_inappwebview_isAsync," +
" user: this._flutter_inappwebview_user," +
" password: this._flutter_inappwebview_password," +
" withCredentials: this.withCredentials," +
" headers: this._flutter_inappwebview_request_headers," +
" responseType: this.responseType" +
" };" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('shouldInterceptAjaxRequest', ajaxRequest).then(function(result) {" +
" if (result != null) {" +
" switch (result.action) {" +
" case 0:" +
" self.abort();" +
" return;" +
" };" +
" data = result.data;" +
" self.withCredentials = result.withCredentials;" +
" if (result.responseType != null) {" +
" self.responseType = result.responseType;" +
" };" +
" for (var header in result.headers) {" +
" var value = result.headers[header];" +
" var flutter_inappwebview_value = self._flutter_inappwebview_request_headers[header];" +
" if (flutter_inappwebview_value == null) {" +
" self._flutter_inappwebview_request_headers[header] = value;" +
" } else {" +
" self._flutter_inappwebview_request_headers[header] += ', ' + value;" +
" }" +
" setRequestHeader.call(self, header, value);" +
" };" +
" if ((self._flutter_inappwebview_method != result.method && result.method != null) || (self._flutter_inappwebview_url != result.url && result.url != null)) {" +
" self.abort();" +
" self.open(result.method, result.url, result.isAsync, result.user, result.password);" +
" return;" +
" }" +
" }" +
" send.call(self, data);" +
" });" +
" } else {" +
" send.call(this, data);" +
" }" +
" };" +
"})(window.XMLHttpRequest);";
}

View File

@ -0,0 +1,200 @@
package com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js;
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
import com.pichillilorenzo.flutter_inappwebview.types.UserScriptInjectionTime;
public class InterceptFetchRequestJS {
public static final String INTERCEPT_FETCH_REQUEST_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_INTERCEPT_FETCH_REQUEST_JS_PLUGIN_SCRIPT";
public static final String FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE = JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "._useShouldInterceptFetchRequest";
public static final PluginScript INTERCEPT_FETCH_REQUEST_JS_PLUGIN_SCRIPT = new PluginScript(
InterceptFetchRequestJS.INTERCEPT_FETCH_REQUEST_JS_PLUGIN_SCRIPT_GROUP_NAME,
InterceptFetchRequestJS.INTERCEPT_FETCH_REQUEST_JS_SOURCE,
UserScriptInjectionTime.AT_DOCUMENT_START,
null,
true
);
public static final String INTERCEPT_FETCH_REQUEST_JS_SOURCE = "(function(fetch) {" +
" var w = (window.top == null || window.top === window) ? window : window.top;" +
" w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE + " = true;" +
" if (fetch == null) {" +
" return;" +
" }" +
" function convertHeadersToJson(headers) {" +
" var headersObj = {};" +
" for (var header of headers.keys()) {" +
" var value = headers.get(header);" +
" headersObj[header] = value;" +
" }" +
" return headersObj;" +
" }" +
" function convertJsonToHeaders(headersJson) {" +
" return new Headers(headersJson);" +
" }" +
" function convertBodyToArray(body) {" +
" return new Response(body).arrayBuffer().then(function(arrayBuffer) {" +
" var arr = Array.from(new Uint8Array(arrayBuffer));" +
" return arr;" +
" })" +
" }" +
" function convertArrayIntBodyToUint8Array(arrayIntBody) {" +
" return new Uint8Array(arrayIntBody);" +
" }" +
" function convertCredentialsToJson(credentials) {" +
" var credentialsObj = {};" +
" if (window.FederatedCredential != null && credentials instanceof FederatedCredential) {" +
" credentialsObj.type = credentials.type;" +
" credentialsObj.id = credentials.id;" +
" credentialsObj.name = credentials.name;" +
" credentialsObj.protocol = credentials.protocol;" +
" credentialsObj.provider = credentials.provider;" +
" credentialsObj.iconURL = credentials.iconURL;" +
" } else if (window.PasswordCredential != null && credentials instanceof PasswordCredential) {" +
" credentialsObj.type = credentials.type;" +
" credentialsObj.id = credentials.id;" +
" credentialsObj.name = credentials.name;" +
" credentialsObj.password = credentials.password;" +
" credentialsObj.iconURL = credentials.iconURL;" +
" } else {" +
" credentialsObj.type = 'default';" +
" credentialsObj.value = credentials;" +
" }" +
" }" +
" function convertJsonToCredential(credentialsJson) {" +
" var credentials;" +
" if (window.FederatedCredential != null && credentialsJson.type === 'federated') {" +
" credentials = new FederatedCredential({" +
" id: credentialsJson.id," +
" name: credentialsJson.name," +
" protocol: credentialsJson.protocol," +
" provider: credentialsJson.provider," +
" iconURL: credentialsJson.iconURL" +
" });" +
" } else if (window.PasswordCredential != null && credentialsJson.type === 'password') {" +
" credentials = new PasswordCredential({" +
" id: credentialsJson.id," +
" name: credentialsJson.name," +
" password: credentialsJson.password," +
" iconURL: credentialsJson.iconURL" +
" });" +
" } else {" +
" credentials = credentialsJson;" +
" }" +
" return credentials;" +
" }" +
" window.fetch = async function(resource, init) {" +
" var w = (window.top == null || window.top === window) ? window : window.top;" +
" if (w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE + " == null || w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE + " == true) {" +
" var fetchRequest = {" +
" url: null," +
" method: null," +
" headers: null," +
" body: null," +
" mode: null," +
" credentials: null," +
" cache: null," +
" redirect: null," +
" referrer: null," +
" referrerPolicy: null," +
" integrity: null," +
" keepalive: null" +
" };" +
" if (resource instanceof Request) {" +
" fetchRequest.url = resource.url;" +
" fetchRequest.method = resource.method;" +
" fetchRequest.headers = resource.headers;" +
" fetchRequest.body = resource.body;" +
" fetchRequest.mode = resource.mode;" +
" fetchRequest.credentials = resource.credentials;" +
" fetchRequest.cache = resource.cache;" +
" fetchRequest.redirect = resource.redirect;" +
" fetchRequest.referrer = resource.referrer;" +
" fetchRequest.referrerPolicy = resource.referrerPolicy;" +
" fetchRequest.integrity = resource.integrity;" +
" fetchRequest.keepalive = resource.keepalive;" +
" } else {" +
" fetchRequest.url = resource;" +
" if (init != null) {" +
" fetchRequest.method = init.method;" +
" fetchRequest.headers = init.headers;" +
" fetchRequest.body = init.body;" +
" fetchRequest.mode = init.mode;" +
" fetchRequest.credentials = init.credentials;" +
" fetchRequest.cache = init.cache;" +
" fetchRequest.redirect = init.redirect;" +
" fetchRequest.referrer = init.referrer;" +
" fetchRequest.referrerPolicy = init.referrerPolicy;" +
" fetchRequest.integrity = init.integrity;" +
" fetchRequest.keepalive = init.keepalive;" +
" }" +
" }" +
" if (fetchRequest.headers instanceof Headers) {" +
" fetchRequest.headers = convertHeadersToJson(fetchRequest.headers);" +
" }" +
" fetchRequest.credentials = convertCredentialsToJson(fetchRequest.credentials);" +
" return convertBodyToArray(fetchRequest.body).then(function(body) {" +
" fetchRequest.body = body;" +
" return window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('shouldInterceptFetchRequest', fetchRequest).then(function(result) {" +
" if (result != null) {" +
" switch (result.action) {" +
" case 0:" +
" var controller = new AbortController();" +
" if (init != null) {" +
" init.signal = controller.signal;" +
" } else {" +
" init = {" +
" signal: controller.signal" +
" };" +
" }" +
" controller.abort();" +
" break;" +
" }" +
" resource = (result.url != null) ? result.url : resource;" +
" if (init == null) {" +
" init = {};" +
" }" +
" if (result.method != null && result.method.length > 0) {" +
" init.method = result.method;" +
" }" +
" if (result.headers != null && Object.keys(result.headers).length > 0) {" +
" init.headers = convertJsonToHeaders(result.headers);" +
" }" +
" if (result.body != null && result.body.length > 0) {" +
" init.body = convertArrayIntBodyToUint8Array(result.body);" +
" }" +
" if (result.mode != null && result.mode.length > 0) {" +
" init.mode = result.mode;" +
" }" +
" if (result.credentials != null) {" +
" init.credentials = convertJsonToCredential(result.credentials);" +
" }" +
" if (result.cache != null && result.cache.length > 0) {" +
" init.cache = result.cache;" +
" }" +
" if (result.redirect != null && result.redirect.length > 0) {" +
" init.redirect = result.redirect;" +
" }" +
" if (result.referrer != null && result.referrer.length > 0) {" +
" init.referrer = result.referrer;" +
" }" +
" if (result.referrerPolicy != null && result.referrerPolicy.length > 0) {" +
" init.referrerPolicy = result.referrerPolicy;" +
" }" +
" if (result.integrity != null && result.integrity.length > 0) {" +
" init.integrity = result.integrity;" +
" }" +
" if (result.keepalive != null) {" +
" init.keepalive = result.keepalive;" +
" }" +
" return fetch(resource, init);" +
" }" +
" return fetch(resource, init);" +
" });" +
" });" +
" } else {" +
" return fetch(resource, init);" +
" }" +
" };" +
"})(window.fetch);";
}

View File

@ -0,0 +1,42 @@
package com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js;
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
import com.pichillilorenzo.flutter_inappwebview.types.UserScriptInjectionTime;
public class JavaScriptBridgeJS {
public static final String JAVASCRIPT_BRIDGE_NAME = "flutter_inappwebview";
public static final String JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT";
public static final PluginScript JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT = new PluginScript(
JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT_GROUP_NAME,
JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_JS_SOURCE,
UserScriptInjectionTime.AT_DOCUMENT_START,
null,
true
);
public static final String JAVASCRIPT_BRIDGE_JS_SOURCE = "if (window.top == null || window.top === window) {" +
" window." + JAVASCRIPT_BRIDGE_NAME + ".callHandler = function() {" +
" var _callHandlerID = setTimeout(function(){});" +
" window." + JAVASCRIPT_BRIDGE_NAME + "._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));" +
" return new Promise(function(resolve, reject) {" +
" window." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = resolve;" +
" });" +
" };"+
"} else {" +
" window." + JAVASCRIPT_BRIDGE_NAME + " = {};" +
" window." + JAVASCRIPT_BRIDGE_NAME + ".callHandler = function() {" +
" var _callHandlerID = setTimeout(function(){});" +
" window.top." + JAVASCRIPT_BRIDGE_NAME + "._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));" +
" return new Promise(function(resolve, reject) {" +
" window.top." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = resolve;" +
" });" +
" };"+
"}";
public static final String PLATFORM_READY_JS_SOURCE = "(function() {" +
" if ((window.top == null || window.top === window) && window." + JAVASCRIPT_BRIDGE_NAME + "._platformReady == null) {" +
" window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady'));" +
" window." + JAVASCRIPT_BRIDGE_NAME + "._platformReady = true;" +
" }" +
"})();";
}

View File

@ -0,0 +1,34 @@
package com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js;
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
import com.pichillilorenzo.flutter_inappwebview.types.UserScriptInjectionTime;
public class OnLoadResourceJS {
public static final String ON_LOAD_RESOURCE_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_ON_LOAD_RESOURCE_JS_PLUGIN_SCRIPT";
public static final String FLAG_VARIABLE_FOR_ON_LOAD_RESOURCE_JS_SOURCE = JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "._useOnLoadResource";
public static final PluginScript ON_LOAD_RESOURCE_JS_PLUGIN_SCRIPT = new PluginScript(
OnLoadResourceJS.ON_LOAD_RESOURCE_JS_PLUGIN_SCRIPT_GROUP_NAME,
OnLoadResourceJS.ON_LOAD_RESOURCE_JS_SOURCE,
UserScriptInjectionTime.AT_DOCUMENT_START,
null,
false
);
public static final String ON_LOAD_RESOURCE_JS_SOURCE = "window." + FLAG_VARIABLE_FOR_ON_LOAD_RESOURCE_JS_SOURCE + " = true;" +
"(function() {" +
" var observer = new PerformanceObserver(function(list) {" +
" list.getEntries().forEach(function(entry) {" +
" if (" + FLAG_VARIABLE_FOR_ON_LOAD_RESOURCE_JS_SOURCE + " == null || " + FLAG_VARIABLE_FOR_ON_LOAD_RESOURCE_JS_SOURCE + " == true) {" +
" var resource = {" +
" 'url': entry.name," +
" 'initiatorType': entry.initiatorType," +
" 'startTime': entry.startTime," +
" 'duration': entry.duration" +
" };" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('onLoadResource', resource);" +
" }" +
" });" +
" });" +
" observer.observe({entryTypes: ['resource']});" +
"})();";
}

View File

@ -0,0 +1,21 @@
package com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js;
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
import com.pichillilorenzo.flutter_inappwebview.types.UserScriptInjectionTime;
public class OnWindowBlurEventJS {
public static final String ON_WINDOW_BLUR_EVENT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_ON_WINDOW_BLUR_EVENT_JS_PLUGIN_SCRIPT";
public static final PluginScript ON_WINDOW_BLUR_EVENT_JS_PLUGIN_SCRIPT = new PluginScript(
OnWindowBlurEventJS.ON_WINDOW_BLUR_EVENT_JS_PLUGIN_SCRIPT_GROUP_NAME,
OnWindowBlurEventJS.ON_WINDOW_BLUR_EVENT_JS_SOURCE,
UserScriptInjectionTime.AT_DOCUMENT_START,
null,
false
);
public static final String ON_WINDOW_BLUR_EVENT_JS_SOURCE = "(function(){" +
" window.addEventListener('blur', function(e) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('onWindowBlur');" +
" });" +
"})();";
}

View File

@ -0,0 +1,21 @@
package com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js;
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
import com.pichillilorenzo.flutter_inappwebview.types.UserScriptInjectionTime;
public class OnWindowFocusEventJS {
public static final String ON_WINDOW_FOCUS_EVENT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_ON_WINDOW_FOCUS_EVENT_JS_PLUGIN_SCRIPT";
public static final PluginScript ON_WINDOW_FOCUS_EVENT_JS_PLUGIN_SCRIPT = new PluginScript(
OnWindowFocusEventJS.ON_WINDOW_FOCUS_EVENT_JS_PLUGIN_SCRIPT_GROUP_NAME,
OnWindowFocusEventJS.ON_WINDOW_FOCUS_EVENT_JS_SOURCE,
UserScriptInjectionTime.AT_DOCUMENT_START,
null,
false
);
public static final String ON_WINDOW_FOCUS_EVENT_JS_SOURCE = "(function(){" +
" window.addEventListener('focus', function(e) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('onWindowFocus');" +
" });" +
"})();";
}

View File

@ -0,0 +1,82 @@
package com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js;
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
import com.pichillilorenzo.flutter_inappwebview.types.UserContentController;
import com.pichillilorenzo.flutter_inappwebview.types.UserScriptInjectionTime;
public class PluginScriptsUtil {
public static final String VAR_PLACEHOLDER_VALUE = "$IN_APP_WEBVIEW_PLACEHOLDER_VALUE";
public static final String VAR_CONTENT_WORLD_NAME_ARRAY = "$IN_APP_WEBVIEW_CONTENT_WORLD_NAME_ARRAY";
public static final String VAR_CONTENT_WORLD_NAME = "$IN_APP_WEBVIEW_CONTENT_WORLD_NAME";
public static final String VAR_JSON_SOURCE_ENCODED = "$IN_APP_WEBVIEW_JSON_SOURCE_ENCODED";
public static final String VAR_FUNCTION_ARGUMENT_NAMES = "$IN_APP_WEBVIEW_FUNCTION_ARGUMENT_NAMES";
public static final String VAR_FUNCTION_ARGUMENT_VALUES = "$IN_APP_WEBVIEW_FUNCTION_ARGUMENT_VALUES";
public static final String VAR_FUNCTION_ARGUMENTS_OBJ = "$IN_APP_WEBVIEW_FUNCTION_ARGUMENTS_OBJ";
public static final String VAR_FUNCTION_BODY = "$IN_APP_WEBVIEW_FUNCTION_BODY";
public static final String VAR_RESULT_UUID = "$IN_APP_WEBVIEW_RESULT_UUID";
public static final String CALL_ASYNC_JAVA_SCRIPT_WRAPPER_JS_SOURCE = "(function(obj) {" +
" (async function(" + VAR_FUNCTION_ARGUMENT_NAMES + ") {" +
" " + VAR_FUNCTION_BODY +
" })(" + VAR_FUNCTION_ARGUMENT_VALUES + ").then(function(value) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('callAsyncJavaScript', {'value': value, 'error': null, 'resultUuid': '" + VAR_RESULT_UUID + "'});" +
" }).catch(function(error) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('callAsyncJavaScript', {'value': null, 'error': error + '', 'resultUuid': '" + VAR_RESULT_UUID + "'});" +
" });" +
" return null;" +
"})(" + VAR_FUNCTION_ARGUMENTS_OBJ + ");";
public static final String EVALUATE_JAVASCRIPT_WITH_CONTENT_WORLD_WRAPPER_JS_SOURCE = "window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('evaluateJavaScriptWithContentWorld', {'value': eval(" + VAR_PLACEHOLDER_VALUE + "), 'resultUuid': '" + VAR_RESULT_UUID + "'});";
public static final String IS_ACTIVE_ELEMENT_INPUT_EDITABLE_JS_SOURCE =
"var activeEl = document.activeElement;" +
"var nodeName = (activeEl != null) ? activeEl.nodeName.toLowerCase() : '';" +
"var isActiveElementInputEditable = activeEl != null && " +
"(activeEl.nodeType == 1 && (nodeName == 'textarea' || (nodeName == 'input' && /^(?:text|email|number|search|tel|url|password)$/i.test(activeEl.type != null ? activeEl.type : 'text')))) && " +
"!activeEl.disabled && !activeEl.readOnly;" +
"var isActiveElementEditable = isActiveElementInputEditable || (activeEl != null && activeEl.isContentEditable) || document.designMode === 'on';";
// android Workaround to hide context menu when selected text is empty
// and the document active element is not an input element.
public static final String CHECK_CONTEXT_MENU_SHOULD_BE_HIDDEN_JS_SOURCE = "(function(){" +
" var txt;" +
" if (window.getSelection) {" +
" txt = window.getSelection().toString();" +
" } else if (window.document.getSelection) {" +
" txt = window.document.getSelection().toString();" +
" } else if (window.document.selection) {" +
" txt = window.document.selection.createRange().text;" +
" }" +
IS_ACTIVE_ELEMENT_INPUT_EDITABLE_JS_SOURCE +
" return txt === '' && !isActiveElementEditable;" +
"})();";
public static final String GET_SELECTED_TEXT_JS_SOURCE = "(function(){" +
" var txt;" +
" if (window.getSelection) {" +
" txt = window.getSelection().toString();" +
" } else if (window.document.getSelection) {" +
" txt = window.document.getSelection().toString();" +
" } else if (window.document.selection) {" +
" txt = window.document.selection.createRange().text;" +
" }" +
" return txt;" +
"})();";
public static final String CHECK_GLOBAL_KEY_DOWN_EVENT_TO_HIDE_CONTEXT_MENU_JS_PLUGIN_SCRIPT_GROUP_NAME = "CHECK_GLOBAL_KEY_DOWN_EVENT_TO_HIDE_CONTEXT_MENU_JS_PLUGIN_SCRIPT";
public static final PluginScript CHECK_GLOBAL_KEY_DOWN_EVENT_TO_HIDE_CONTEXT_MENU_JS_PLUGIN_SCRIPT = new PluginScript(
PluginScriptsUtil.CHECK_GLOBAL_KEY_DOWN_EVENT_TO_HIDE_CONTEXT_MENU_JS_PLUGIN_SCRIPT_GROUP_NAME,
PluginScriptsUtil.CHECK_GLOBAL_KEY_DOWN_EVENT_TO_HIDE_CONTEXT_MENU_JS_SOURCE,
UserScriptInjectionTime.AT_DOCUMENT_START,
null,
false
);
// android Workaround to hide context menu when user emit a keydown event
public static final String CHECK_GLOBAL_KEY_DOWN_EVENT_TO_HIDE_CONTEXT_MENU_JS_SOURCE = "(function(){" +
" document.addEventListener('keydown', function(e) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "._hideContextMenu();" +
" });" +
"})();";
}

View File

@ -0,0 +1,23 @@
package com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js;
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
import com.pichillilorenzo.flutter_inappwebview.types.UserScriptInjectionTime;
public class PrintJS {
public static final String PRINT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_PRINT_JS_PLUGIN_SCRIPT";
public static final PluginScript PRINT_JS_PLUGIN_SCRIPT = new PluginScript(
PrintJS.PRINT_JS_PLUGIN_SCRIPT_GROUP_NAME,
PrintJS.PRINT_JS_SOURCE,
UserScriptInjectionTime.AT_DOCUMENT_START,
null,
false
);
public static final String PRINT_JS_SOURCE = "window.print = function() {" +
" if (window.top == null || window.top === window) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('onPrint', window.location.href);" +
" } else {" +
" window.top.print();" +
" }" +
"};";
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,86 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import androidx.annotation.Nullable;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ClientCertChallenge extends URLAuthenticationChallenge {
@Nullable
private Principal[] principals;
@Nullable
private String[] keyTypes;
public ClientCertChallenge(URLProtectionSpace protectionSpace, @Nullable Principal[] principals, @Nullable String[] keyTypes) {
super(protectionSpace);
this.principals = principals;
this.keyTypes = keyTypes;
}
public Map<String, Object> toMap() {
List<String> principalList = null;
if (principals != null) {
principalList = new ArrayList<>();
for (Principal principal : principals) {
principalList.add(principal.getName());
}
}
Map<String, Object> challengeMap = super.toMap();
challengeMap.put("androidPrincipals", principalList);
challengeMap.put("androidKeyTypes", keyTypes != null ? Arrays.asList(keyTypes) : null);
return challengeMap;
}
@Nullable
public Principal[] getPrincipals() {
return principals;
}
public void setPrincipals(@Nullable Principal[] principals) {
this.principals = principals;
}
@Nullable
public String[] getKeyTypes() {
return keyTypes;
}
public void setKeyTypes(@Nullable String[] keyTypes) {
this.keyTypes = keyTypes;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
ClientCertChallenge that = (ClientCertChallenge) o;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
if (!Arrays.equals(principals, that.principals)) return false;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
return Arrays.equals(keyTypes, that.keyTypes);
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + Arrays.hashCode(principals);
result = 31 * result + Arrays.hashCode(keyTypes);
return result;
}
@Override
public String toString() {
return "ClientCertChallenge{" +
"principals=" + Arrays.toString(principals) +
", keyTypes=" + Arrays.toString(keyTypes) +
"} " + super.toString();
}
}

View File

@ -0,0 +1,63 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Map;
public class ContentWorld {
@NonNull
private String name;
public static final ContentWorld PAGE = new ContentWorld("page");
public static final ContentWorld DEFAULT_CLIENT = new ContentWorld("defaultClient");
private ContentWorld(@NonNull String name) {
this.name = name;
}
public static ContentWorld world(@NonNull String name) {
return new ContentWorld(name);
}
@Nullable
public static ContentWorld fromMap(@Nullable Map<String, Object> map) {
if (map == null) {
return null;
}
String name = (String) map.get("name");
assert name != null;
return new ContentWorld(name);
}
@NonNull
public String getName() {
return name;
}
public void setName(@NonNull String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ContentWorld that = (ContentWorld) o;
return name.equals(that.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return "ContentWorld{" +
"name='" + name + '\'' +
'}';
}
}

View File

@ -0,0 +1,69 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import java.util.Map;
public class CreateWindowAction extends NavigationAction {
int windowId;
boolean isDialog;
public CreateWindowAction(URLRequest request, boolean isForMainFrame, boolean hasGesture, boolean isRedirect, int windowId, boolean isDialog) {
super(request, isForMainFrame, hasGesture, isRedirect);
this.windowId = windowId;
this.isDialog = isDialog;
}
public Map<String, Object> toMap() {
Map<String, Object> createWindowActionMap = super.toMap();
createWindowActionMap.put("windowId", windowId);
createWindowActionMap.put("androidIsDialog", isDialog);
return createWindowActionMap;
}
public int getWindowId() {
return windowId;
}
public void setWindowId(int windowId) {
this.windowId = windowId;
}
public boolean isDialog() {
return isDialog;
}
public void setDialog(boolean dialog) {
isDialog = dialog;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
CreateWindowAction that = (CreateWindowAction) o;
if (windowId != that.windowId) return false;
return isDialog == that.isDialog;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + windowId;
result = 31 * result + (isDialog ? 1 : 0);
return result;
}
@Override
public String toString() {
return "CreateWindowAction{" +
"windowId=" + windowId +
", isDialog=" + isDialog +
", request=" + request +
", isForMainFrame=" + isForMainFrame +
", hasGesture=" + hasGesture +
", isRedirect=" + isRedirect +
'}';
}
}

View File

@ -0,0 +1,69 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import androidx.annotation.Nullable;
import java.util.Map;
public class HttpAuthenticationChallenge extends URLAuthenticationChallenge {
private int previousFailureCount;
@Nullable
URLCredential proposedCredential;
public HttpAuthenticationChallenge(URLProtectionSpace protectionSpace, int previousFailureCount, @Nullable URLCredential proposedCredential) {
super(protectionSpace);
this.previousFailureCount = previousFailureCount;
this.proposedCredential = proposedCredential;
}
public Map<String, Object> toMap() {
Map<String, Object> challengeMap = super.toMap();
challengeMap.put("previousFailureCount", previousFailureCount);
challengeMap.put("proposedCredential", (proposedCredential != null) ? proposedCredential.toMap() : null);
return challengeMap;
}
public int getPreviousFailureCount() {
return previousFailureCount;
}
public void setPreviousFailureCount(int previousFailureCount) {
this.previousFailureCount = previousFailureCount;
}
@Nullable
public URLCredential getProposedCredential() {
return proposedCredential;
}
public void setProposedCredential(@Nullable URLCredential proposedCredential) {
this.proposedCredential = proposedCredential;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
HttpAuthenticationChallenge that = (HttpAuthenticationChallenge) o;
if (previousFailureCount != that.previousFailureCount) return false;
return proposedCredential != null ? proposedCredential.equals(that.proposedCredential) : that.proposedCredential == null;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + previousFailureCount;
result = 31 * result + (proposedCredential != null ? proposedCredential.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "HttpAuthenticationChallenge{" +
"previousFailureCount=" + previousFailureCount +
", proposedCredential=" + proposedCredential +
"} " + super.toString();
}
}

View File

@ -0,0 +1,91 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import java.util.HashMap;
import java.util.Map;
public class NavigationAction {
URLRequest request;
boolean isForMainFrame;
boolean hasGesture;
boolean isRedirect;
public NavigationAction(URLRequest request, boolean isForMainFrame, boolean hasGesture, boolean isRedirect) {
this.request = request;
this.isForMainFrame = isForMainFrame;
this.hasGesture = hasGesture;
this.isRedirect = isRedirect;
}
public Map<String, Object> toMap() {
Map<String, Object> navigationActionMap = new HashMap<>();
navigationActionMap.put("request", request.toMap());
navigationActionMap.put("isForMainFrame", isForMainFrame);
navigationActionMap.put("hasGesture", hasGesture);
navigationActionMap.put("isRedirect", isRedirect);
return navigationActionMap;
}
public URLRequest getRequest() {
return request;
}
public void setRequest(URLRequest request) {
this.request = request;
}
public boolean isForMainFrame() {
return isForMainFrame;
}
public void setForMainFrame(boolean forMainFrame) {
isForMainFrame = forMainFrame;
}
public boolean isHasGesture() {
return hasGesture;
}
public void setHasGesture(boolean hasGesture) {
this.hasGesture = hasGesture;
}
public boolean isRedirect() {
return isRedirect;
}
public void setRedirect(boolean redirect) {
isRedirect = redirect;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NavigationAction that = (NavigationAction) o;
if (isForMainFrame != that.isForMainFrame) return false;
if (hasGesture != that.hasGesture) return false;
if (isRedirect != that.isRedirect) return false;
return request.equals(that.request);
}
@Override
public int hashCode() {
int result = request.hashCode();
result = 31 * result + (isForMainFrame ? 1 : 0);
result = 31 * result + (hasGesture ? 1 : 0);
result = 31 * result + (isRedirect ? 1 : 0);
return result;
}
@Override
public String toString() {
return "NavigationAction{" +
"request=" + request +
", isForMainFrame=" + isForMainFrame +
", hasGesture=" + hasGesture +
", isRedirect=" + isRedirect +
'}';
}
}

View File

@ -0,0 +1,33 @@
package com.pichillilorenzo.flutter_inappwebview.types;
public enum NavigationActionPolicy {
CANCEL(0),
ALLOW(1);
private final int value;
private NavigationActionPolicy(int value) {
this.value = value;
}
public boolean equalsValue(int otherValue) {
return value == otherValue;
}
public static NavigationActionPolicy fromValue(int value) {
for( NavigationActionPolicy type : NavigationActionPolicy.values()) {
if(value == type.value)
return type;
}
throw new IllegalArgumentException("No enum constant: " + value);
}
public int rawValue() {
return this.value;
}
@Override
public String toString() {
return String.valueOf(this.value);
}
}

View File

@ -0,0 +1,46 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class PluginScript extends UserScript {
private boolean requiredInAllContentWorlds;
public PluginScript(@Nullable String groupName, @NonNull String source, @NonNull UserScriptInjectionTime injectionTime, @Nullable ContentWorld contentWorld, boolean requiredInAllContentWorlds) {
super(groupName, source, injectionTime, contentWorld);
this.requiredInAllContentWorlds = requiredInAllContentWorlds;
}
public boolean isRequiredInAllContentWorlds() {
return requiredInAllContentWorlds;
}
public void setRequiredInAllContentWorlds(boolean requiredInAllContentWorlds) {
this.requiredInAllContentWorlds = requiredInAllContentWorlds;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
PluginScript that = (PluginScript) o;
return requiredInAllContentWorlds == that.requiredInAllContentWorlds;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (requiredInAllContentWorlds ? 1 : 0);
return result;
}
@Override
public String toString() {
return "PluginScript{" +
"requiredInContentWorld=" + requiredInAllContentWorlds +
"} " + super.toString();
}
}

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
package com.pichillilorenzo.flutter_inappwebview.types;
public enum PreferredContentModeOptionType {
RECOMMENDED (0),

View File

@ -0,0 +1,12 @@
package com.pichillilorenzo.flutter_inappwebview.types;
public class ServerTrustChallenge extends URLAuthenticationChallenge {
public ServerTrustChallenge(URLProtectionSpace protectionSpace) {
super(protectionSpace);
}
@Override
public String toString() {
return "ServerTrustChallenge{} " + super.toString();
}
}

View File

@ -0,0 +1,77 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import android.net.http.SslCertificate;
import android.os.Build;
import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.Util;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
public class SslCertificateExt extends SslCertificate {
private SslCertificateExt(X509Certificate certificate) {
super(certificate);
}
@Nullable
static public Map<String, Object> toMap(@Nullable SslCertificate sslCertificate) {
if (sslCertificate == null) {
return null;
}
DName issuedByName = sslCertificate.getIssuedBy();
Map<String, Object> issuedBy = null;
if (issuedByName != null) {
issuedBy = new HashMap<>();
issuedBy.put("CName", issuedByName.getCName());
issuedBy.put("DName", issuedByName.getDName());
issuedBy.put("OName", issuedByName.getOName());
issuedBy.put("UName", issuedByName.getUName());
}
DName issuedToName = sslCertificate.getIssuedTo();
Map<String, Object> issuedTo = null;
if (issuedToName != null) {
issuedTo = new HashMap<>();
issuedTo.put("CName", issuedToName.getCName());
issuedTo.put("DName", issuedToName.getDName());
issuedTo.put("OName", issuedToName.getOName());
issuedTo.put("UName", issuedToName.getUName());
}
byte[] x509CertificateData = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
X509Certificate certificate = sslCertificate.getX509Certificate();
if (certificate != null) {
x509CertificateData = certificate.getEncoded();
}
} catch (CertificateEncodingException e) {
e.printStackTrace();
}
} else {
try {
x509CertificateData = Util.getX509CertFromSslCertHack(sslCertificate).getEncoded();
} catch (CertificateEncodingException e) {
e.printStackTrace();
}
}
long validNotAfterDate = sslCertificate.getValidNotAfterDate().getTime();
long validNotBeforeDate = sslCertificate.getValidNotBeforeDate().getTime();
Map<String, Object> sslCertificateMap = new HashMap<>();
sslCertificateMap.put("issuedBy", issuedBy);
sslCertificateMap.put("issuedTo", issuedTo);
sslCertificateMap.put("validNotAfterDate", validNotAfterDate);
sslCertificateMap.put("validNotBeforeDate", validNotBeforeDate);
sslCertificateMap.put("x509Certificate", x509CertificateData);
return sslCertificateMap;
}
}

View File

@ -0,0 +1,56 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import androidx.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public class SslErrorExt extends SslError {
private SslErrorExt(int error, SslCertificate certificate, String url) {
super(error, certificate, url);
}
@Nullable
static public Map<String, Object> toMap(SslError sslError) {
if (sslError == null) {
return null;
}
int primaryError = sslError.getPrimaryError();
String message;
switch (primaryError) {
case SslError.SSL_DATE_INVALID:
message = "The date of the certificate is invalid";
break;
case SslError.SSL_EXPIRED:
message = "The certificate has expired";
break;
case SslError.SSL_IDMISMATCH:
message = "Hostname mismatch";
break;
case SslError.SSL_INVALID:
message = "A generic error occurred";
break;
case SslError.SSL_NOTYETVALID:
message = "The certificate is not yet valid";
break;
case SslError.SSL_UNTRUSTED:
message = "The certificate authority is not trusted";
break;
default:
message = null;
break;
}
Map<String, Object> urlProtectionSpaceMap = new HashMap<>();
urlProtectionSpaceMap.put("androidError", primaryError);
urlProtectionSpaceMap.put("message", message);
return urlProtectionSpaceMap;
}
}

View File

@ -0,0 +1,48 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import java.util.HashMap;
import java.util.Map;
public class URLAuthenticationChallenge {
private URLProtectionSpace protectionSpace;
public URLAuthenticationChallenge(URLProtectionSpace protectionSpace) {
this.protectionSpace = protectionSpace;
}
public Map<String, Object> toMap() {
Map<String, Object> challengeMap = new HashMap<>();
challengeMap.put("protectionSpace", protectionSpace.toMap());
return challengeMap;
}
public URLProtectionSpace getProtectionSpace() {
return protectionSpace;
}
public void setProtectionSpace(URLProtectionSpace protectionSpace) {
this.protectionSpace = protectionSpace;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
URLAuthenticationChallenge challenge = (URLAuthenticationChallenge) o;
return protectionSpace.equals(challenge.protectionSpace);
}
@Override
public int hashCode() {
return protectionSpace.hashCode();
}
@Override
public String toString() {
return "URLAuthenticationChallenge{" +
"protectionSpace=" + protectionSpace +
'}';
}
}

View File

@ -0,0 +1,99 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public class URLCredential {
@Nullable
private Long id;
@Nullable
private String username;
@Nullable
private String password;
@Nullable
private Long protectionSpaceId;
public URLCredential(@Nullable String username, @Nullable String password) {
this.username = username;
this.password = password;
}
public URLCredential (@Nullable Long id, @NonNull String username, @NonNull String password, @Nullable Long protectionSpaceId) {
this.id = id;
this.username = username;
this.password = password;
this.protectionSpaceId = protectionSpaceId;
}
public Map<String, Object> toMap() {
Map<String, Object> urlCredentialMap = new HashMap<>();
urlCredentialMap.put("username", username);
urlCredentialMap.put("password", password);
return urlCredentialMap;
}
@Nullable
public Long getId() {
return id;
}
public void setId(@Nullable Long id) {
this.id = id;
}
@Nullable
public String getUsername() {
return username;
}
public void setUsername(@Nullable String username) {
this.username = username;
}
@Nullable
public String getPassword() {
return password;
}
public void setPassword(@Nullable String password) {
this.password = password;
}
@Nullable
public Long getProtectionSpaceId() {
return protectionSpaceId;
}
public void setProtectionSpaceId(@Nullable Long protectionSpaceId) {
this.protectionSpaceId = protectionSpaceId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
URLCredential that = (URLCredential) o;
if (username != null ? !username.equals(that.username) : that.username != null) return false;
return password != null ? password.equals(that.password) : that.password == null;
}
@Override
public int hashCode() {
int result = username != null ? username.hashCode() : 0;
result = 31 * result + (password != null ? password.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "URLCredential{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}

View File

@ -0,0 +1,150 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import androidx.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public class URLProtectionSpace {
@Nullable
private Long id;
private String host;
private String protocol;
@Nullable
private String realm;
private int port;
@Nullable
private SslCertificate sslCertificate;
@Nullable
private SslError sslError;
public URLProtectionSpace(String host, String protocol, @Nullable String realm, int port, @Nullable SslCertificate sslCertificate, @Nullable SslError sslError) {
this.host = host;
this.protocol = protocol;
this.realm = realm;
this.port = port;
this.sslCertificate = sslCertificate;
this.sslError = sslError;
}
public URLProtectionSpace(@Nullable Long id, String host, String protocol, @Nullable String realm, int port) {
this.id = id;
this.host = host;
this.protocol = protocol;
this.realm = realm;
this.port = port;
}
public Map<String, Object> toMap() {
Map<String, Object> urlProtectionSpaceMap = new HashMap<>();
urlProtectionSpaceMap.put("host", host);
urlProtectionSpaceMap.put("protocol", protocol);
urlProtectionSpaceMap.put("realm", realm);
urlProtectionSpaceMap.put("port", port);
urlProtectionSpaceMap.put("sslCertificate", SslCertificateExt.toMap(sslCertificate));
urlProtectionSpaceMap.put("sslError", SslErrorExt.toMap(sslError));
return urlProtectionSpaceMap;
}
@Nullable
public Long getId() {
return id;
}
public void setId(@Nullable Long id) {
this.id = id;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
@Nullable
public String getRealm() {
return realm;
}
public void setRealm(@Nullable String realm) {
this.realm = realm;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
@Nullable
public SslCertificate getSslCertificate() {
return sslCertificate;
}
public void setSslCertificate(@Nullable SslCertificate sslCertificateExt) {
this.sslCertificate = sslCertificateExt;
}
@Nullable
public SslError getSslError() {
return sslError;
}
public void setSslError(@Nullable SslError sslError) {
this.sslError = sslError;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
URLProtectionSpace that = (URLProtectionSpace) o;
if (port != that.port) return false;
if (!host.equals(that.host)) return false;
if (!protocol.equals(that.protocol)) return false;
if (realm != null ? !realm.equals(that.realm) : that.realm != null) return false;
if (sslCertificate != null ? !sslCertificate.equals(that.sslCertificate) : that.sslCertificate != null)
return false;
return sslError != null ? sslError.equals(that.sslError) : that.sslError == null;
}
@Override
public int hashCode() {
int result = host.hashCode();
result = 31 * result + protocol.hashCode();
result = 31 * result + (realm != null ? realm.hashCode() : 0);
result = 31 * result + port;
result = 31 * result + (sslCertificate != null ? sslCertificate.hashCode() : 0);
result = 31 * result + (sslError != null ? sslError.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "URLProtectionSpace{" +
"host='" + host + '\'' +
", protocol='" + protocol + '\'' +
", realm='" + realm + '\'' +
", port=" + port +
", sslCertificate=" + sslCertificate +
", sslError=" + sslError +
'}';
}
}

View File

@ -0,0 +1,115 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class URLRequest {
@NonNull
private String url;
@Nullable
private String method;
@Nullable
private byte[] body;
@Nullable
private Map<String, String> headers;
public URLRequest(@NonNull String url, @Nullable String method, @Nullable byte[] body, @Nullable Map<String, String> headers) {
this.url = url;
this.method = method;
this.body = body;
this.headers = headers;
}
@Nullable
public static URLRequest fromMap(@Nullable Map<String, Object> map) {
if (map == null) {
return null;
}
String url = (String) map.get("url");
String method = (String) map.get("method");
byte[] body = (byte[]) map.get("body");
Map<String, String> headers = (Map<String, String>) map.get("headers");
assert url != null;
return new URLRequest(url, method, body, headers);
}
public Map<String, Object> toMap() {
Map<String, Object> urlRequestMap = new HashMap<>();
urlRequestMap.put("url", url);
urlRequestMap.put("method", method);
urlRequestMap.put("body", body);
return urlRequestMap;
}
@NonNull
public String getUrl() {
return url;
}
public void setUrl(@NonNull String url) {
this.url = url;
}
@Nullable
public String getMethod() {
return method;
}
public void setMethod(@Nullable String method) {
this.method = method;
}
@Nullable
public byte[] getBody() {
return body;
}
public void setBody(@Nullable byte[] body) {
this.body = body;
}
@Nullable
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(@Nullable Map<String, String> headers) {
this.headers = headers;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
URLRequest that = (URLRequest) o;
if (!url.equals(that.url)) return false;
if (method != null ? !method.equals(that.method) : that.method != null) return false;
if (!Arrays.equals(body, that.body)) return false;
return headers != null ? headers.equals(that.headers) : that.headers == null;
}
@Override
public int hashCode() {
int result = url.hashCode();
result = 31 * result + (method != null ? method.hashCode() : 0);
result = 31 * result + Arrays.hashCode(body);
result = 31 * result + (headers != null ? headers.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "URLRequest{" +
"url='" + url + '\'' +
", method='" + method + '\'' +
", body=" + Arrays.toString(body) +
", headers=" + headers +
'}';
}
}

View File

@ -0,0 +1,364 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.Util;
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PluginScriptsUtil;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class UserContentController {
protected static final String LOG_TAG = "UserContentController";
@NonNull
private final Set<ContentWorld> contentWorlds = new HashSet<ContentWorld>() {{
add(ContentWorld.PAGE);
}};
@NonNull
private final Map<UserScriptInjectionTime, LinkedHashSet<UserScript>> userOnlyScripts = new HashMap<UserScriptInjectionTime, LinkedHashSet<UserScript>>() {{
put(UserScriptInjectionTime.AT_DOCUMENT_START, new LinkedHashSet<UserScript>());
put(UserScriptInjectionTime.AT_DOCUMENT_END, new LinkedHashSet<UserScript>());
}};
@NonNull
private final Map<UserScriptInjectionTime, LinkedHashSet<PluginScript>> pluginScripts = new HashMap<UserScriptInjectionTime, LinkedHashSet<PluginScript>>() {{
put(UserScriptInjectionTime.AT_DOCUMENT_START, new LinkedHashSet<PluginScript>());
put(UserScriptInjectionTime.AT_DOCUMENT_END, new LinkedHashSet<PluginScript>());
}};
public UserContentController() {
}
public String generateWrappedCodeForDocumentStart() {
return Util.replaceAll(
DOCUMENT_READY_WRAPPER_JS_SOURCE,
PluginScriptsUtil.VAR_PLACEHOLDER_VALUE,
generateCodeForDocumentStart());
}
public String generateWrappedCodeForDocumentEnd() {
UserScriptInjectionTime injectionTime = UserScriptInjectionTime.AT_DOCUMENT_END;
// try to reload scripts if they were not loaded during the AT_DOCUMENT_START event
String js = generateCodeForDocumentStart();
js += generatePluginScriptsCodeAt(injectionTime);
js += generateUserOnlyScriptsCodeAt(injectionTime);
js = USER_SCRIPTS_AT_DOCUMENT_END_WRAPPER_JS_SOURCE.replace(PluginScriptsUtil.VAR_PLACEHOLDER_VALUE, js);
return js;
}
private String generateCodeForDocumentStart() {
UserScriptInjectionTime injectionTime = UserScriptInjectionTime.AT_DOCUMENT_START;
String js = "";
js += generatePluginScriptsCodeAt(injectionTime);
js += generateContentWorldsCreatorCode();
js += generateUserOnlyScriptsCodeAt(injectionTime);
js = USER_SCRIPTS_AT_DOCUMENT_START_WRAPPER_JS_SOURCE.replace(PluginScriptsUtil.VAR_PLACEHOLDER_VALUE, js);
return js;
}
private String generateContentWorldsCreatorCode() {
if (this.contentWorlds.size() == 1) {
return "";
}
StringBuilder source = new StringBuilder();
LinkedHashSet<PluginScript> pluginScriptsRequired = this.getPluginScriptsRequiredInAllContentWorlds();
for (PluginScript script : pluginScriptsRequired) {
source.append(script.getSource());
}
List<String> contentWorldsNames = new ArrayList<>();
for (ContentWorld contentWorld : this.contentWorlds) {
if (contentWorld.equals(ContentWorld.PAGE)) {
continue;
}
contentWorldsNames.add("'" + escapeContentWorldName(contentWorld.getName()) + "'");
}
return CONTENT_WORLDS_GENERATOR_JS_SOURCE
.replace(PluginScriptsUtil.VAR_CONTENT_WORLD_NAME_ARRAY, TextUtils.join(", ", contentWorldsNames))
.replace(PluginScriptsUtil.VAR_JSON_SOURCE_ENCODED, escapeCode(source.toString()));
}
private String generatePluginScriptsCodeAt(UserScriptInjectionTime injectionTime) {
StringBuilder js = new StringBuilder();
LinkedHashSet<PluginScript> scripts = this.getPluginScriptsAt(injectionTime);
for (PluginScript script : scripts) {
String source = ";" + script.getSource();
source = wrapSourceCodeInContentWorld(script.getContentWorld(), source);
js.append(source);
}
return js.toString();
}
private String generateUserOnlyScriptsCodeAt(UserScriptInjectionTime injectionTime) {
StringBuilder js = new StringBuilder();
LinkedHashSet<UserScript> scripts = this.getUserOnlyScriptsAt(injectionTime);
for (UserScript script : scripts) {
String source = ";" + script.getSource();
source = wrapSourceCodeInContentWorld(script.getContentWorld(), source);
js.append(source);
}
return js.toString();
}
public String generateCodeForScriptEvaluation(String source, @Nullable ContentWorld contentWorld) {
if (contentWorld != null && !contentWorld.equals(ContentWorld.PAGE)) {
StringBuilder sourceWrapped = new StringBuilder();
if (!contentWorlds.contains(contentWorld)) {
contentWorlds.add(contentWorld);
LinkedHashSet<PluginScript> pluginScriptsRequired = this.getPluginScriptsRequiredInAllContentWorlds();
for (PluginScript script : pluginScriptsRequired) {
sourceWrapped.append(";").append(script.getSource());
}
}
sourceWrapped.append(source);
return wrapSourceCodeInContentWorld(contentWorld, sourceWrapped.toString());
}
return source;
}
public String wrapSourceCodeInContentWorld(@Nullable ContentWorld contentWorld, String source) {
String sourceWrapped = contentWorld == null || contentWorld.equals(ContentWorld.PAGE) ? source :
CONTENT_WORLD_WRAPPER_JS_SOURCE
.replace(PluginScriptsUtil.VAR_CONTENT_WORLD_NAME, escapeContentWorldName(contentWorld.getName()))
.replace(PluginScriptsUtil.VAR_JSON_SOURCE_ENCODED, escapeCode(source));
return sourceWrapped;
}
public static String escapeCode(String code) {
String escapedCode = JSONObject.quote(code);
// escapedCode = escapedCode.substring(1, escapedCode.length() - 1);
return escapedCode;
}
public static String escapeContentWorldName(String name) {
return name.replaceAll("'", "\\\\'");
}
public LinkedHashSet<UserScript> getUserOnlyScriptsAt(UserScriptInjectionTime injectionTime) {
return new LinkedHashSet<>(this.userOnlyScripts.get(injectionTime));
}
public boolean addUserOnlyScript(UserScript userOnlyScript) {
ContentWorld contentWorld = userOnlyScript.getContentWorld();
if (contentWorld != null) {
contentWorlds.add(contentWorld);
}
return this.userOnlyScripts.get(userOnlyScript.getInjectionTime()).add(userOnlyScript);
}
public void addUserOnlyScripts(List<UserScript> userOnlyScripts) {
for (UserScript userOnlyScript : userOnlyScripts) {
this.addUserOnlyScript(userOnlyScript);
}
}
public boolean removeUserOnlyScript(UserScript userOnlyScript) {
return this.userOnlyScripts.get(userOnlyScript.getInjectionTime()).remove(userOnlyScript);
}
public boolean removeUserOnlyScriptAt(int index, UserScriptInjectionTime injectionTime) {
UserScript userOnlyScript = new ArrayList<>(this.userOnlyScripts.get(injectionTime)).get(index);
return this.removeUserOnlyScript(userOnlyScript);
}
public void removeAllUserOnlyScripts() {
this.userOnlyScripts.get(UserScriptInjectionTime.AT_DOCUMENT_START).clear();
this.userOnlyScripts.get(UserScriptInjectionTime.AT_DOCUMENT_END).clear();
}
public LinkedHashSet<PluginScript> getPluginScriptsAt(UserScriptInjectionTime injectionTime) {
return new LinkedHashSet<>(this.pluginScripts.get(injectionTime));
}
public LinkedHashSet<PluginScript> getPluginScriptsRequiredInAllContentWorlds() {
LinkedHashSet<PluginScript> pluginScriptsRequired = new LinkedHashSet<>();
LinkedHashSet<PluginScript> scripts = this.getPluginScriptsAt(UserScriptInjectionTime.AT_DOCUMENT_START);
for (PluginScript script : scripts) {
if (script.isRequiredInAllContentWorlds()) {
pluginScriptsRequired.add(script);
}
}
return pluginScriptsRequired;
}
public boolean addPluginScript(PluginScript pluginScript) {
ContentWorld contentWorld = pluginScript.getContentWorld();
if (contentWorld != null) {
contentWorlds.add(contentWorld);
}
return this.pluginScripts.get(pluginScript.getInjectionTime()).add(pluginScript);
}
public void addPluginScripts(List<PluginScript> pluginScripts) {
for (PluginScript pluginScript : pluginScripts) {
this.addPluginScript(pluginScript);
}
}
public boolean removePluginScript(PluginScript pluginScript) {
return this.pluginScripts.get(pluginScript.getInjectionTime()).remove(pluginScript);
}
public boolean removePluginScriptAt(int index, UserScriptInjectionTime injectionTime) {
PluginScript pluginScript = new ArrayList<>(this.pluginScripts.get(injectionTime)).get(index);
return this.removePluginScript(pluginScript);
}
public void removeAllPluginScripts() {
this.pluginScripts.get(UserScriptInjectionTime.AT_DOCUMENT_START).clear();
this.pluginScripts.get(UserScriptInjectionTime.AT_DOCUMENT_END).clear();
}
public LinkedHashSet<UserScript> getUserOnlyScriptAsList() {
LinkedHashSet<UserScript> userOnlyScripts = new LinkedHashSet<>();
Collection<LinkedHashSet<UserScript>> collection = this.userOnlyScripts.values();
for (LinkedHashSet<UserScript> list : collection) {
userOnlyScripts.addAll(list);
}
return userOnlyScripts;
}
public LinkedHashSet<PluginScript> getPluginScriptAsList() {
LinkedHashSet<PluginScript> pluginScripts = new LinkedHashSet<>();
Collection<LinkedHashSet<PluginScript>> collection = this.pluginScripts.values();
for (LinkedHashSet<PluginScript> list : collection) {
pluginScripts.addAll(list);
}
return pluginScripts;
}
public void resetContentWorlds() {
this.contentWorlds.clear();
this.contentWorlds.add(ContentWorld.PAGE);
LinkedHashSet<PluginScript> pluginScripts = this.getPluginScriptAsList();
for (PluginScript pluginScript : pluginScripts) {
ContentWorld contentWorld = pluginScript.getContentWorld();
this.contentWorlds.add(contentWorld);
}
LinkedHashSet<UserScript> userOnlyScripts = this.getUserOnlyScriptAsList();
for (UserScript userOnlyScript : userOnlyScripts) {
ContentWorld contentWorld = userOnlyScript.getContentWorld();
this.contentWorlds.add(contentWorld);
}
}
public boolean containsPluginScript(PluginScript pluginScript) {
return this.getPluginScriptAsList().contains(pluginScript);
}
public boolean containsPluginScriptByGroupName(String groupName) {
LinkedHashSet<PluginScript> pluginScripts = this.getPluginScriptAsList();
for (PluginScript pluginScript : pluginScripts) {
if (Util.objEquals(groupName, pluginScript.getGroupName())) {
return true;
}
}
return false;
}
public boolean containsUserOnlyScript(UserScript userOnlyScript) {
return this.getUserOnlyScriptAsList().contains(userOnlyScript);
}
public boolean containsUserOnlyScriptByGroupName(String groupName) {
LinkedHashSet<UserScript> userOnlyScripts = this.getUserOnlyScriptAsList();
for (UserScript userOnlyScript : userOnlyScripts) {
if (Util.objEquals(groupName, userOnlyScript.getGroupName())) {
return true;
}
}
return false;
}
public void removePluginScriptsByGroupName(String groupName) {
LinkedHashSet<PluginScript> pluginScripts = this.getPluginScriptAsList();
for (PluginScript pluginScript : pluginScripts) {
if (Util.objEquals(groupName, pluginScript.getGroupName())) {
this.removePluginScript(pluginScript);
}
}
}
public void removeUserOnlyScriptsByGroupName(String groupName) {
LinkedHashSet<UserScript> userOnlyScripts = this.getUserOnlyScriptAsList();
for (UserScript userOnlyScript : userOnlyScripts) {
if (Util.objEquals(groupName, userOnlyScript.getGroupName())) {
this.removeUserOnlyScript(userOnlyScript);
}
}
}
@NonNull
public LinkedHashSet<ContentWorld> getContentWorlds() {
return new LinkedHashSet<>(this.contentWorlds);
}
private static final String USER_SCRIPTS_AT_DOCUMENT_START_WRAPPER_JS_SOURCE = "if (window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentStartLoaded == null || !window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentStartLoaded) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentStartLoaded = true;" +
" " + PluginScriptsUtil.VAR_PLACEHOLDER_VALUE +
"}";
private static final String USER_SCRIPTS_AT_DOCUMENT_END_WRAPPER_JS_SOURCE = "if (window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentEndLoaded == null || !window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentEndLoaded) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentEndLoaded = true;" +
" " + PluginScriptsUtil.VAR_PLACEHOLDER_VALUE +
"}";
private static final String CONTENT_WORLDS_GENERATOR_JS_SOURCE = "(function() {" +
" var contentWorldNames = [" + PluginScriptsUtil.VAR_CONTENT_WORLD_NAME_ARRAY + "];" +
" for (var contentWorldName of contentWorldNames) {" +
" var iframeId = '" + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "_' + contentWorldName;" +
" var iframe = document.getElementById(iframeId);" +
" if (iframe == null) {" +
" iframe = document.createElement('iframe');" +
" iframe.id = iframeId;" +
" iframe.style = 'display: none; z-index: 0; position: absolute; width: 0px; height: 0px';" +
" document.body.append(iframe);" +
" }" +
" var script = iframe.contentWindow.document.createElement('script');" +
" script.innerHTML = "+ PluginScriptsUtil.VAR_JSON_SOURCE_ENCODED + ";" +
" iframe.contentWindow.document.body.append(script);" +
" }" +
"})();";
private static final String CONTENT_WORLD_WRAPPER_JS_SOURCE = "(function() {" +
" var iframeId = '" + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "_" + PluginScriptsUtil.VAR_CONTENT_WORLD_NAME + "';" +
" var iframe = document.getElementById(iframeId);" +
" if (iframe == null) {" +
" iframe = document.createElement('iframe');" +
" iframe.id = iframeId;" +
" iframe.style = 'display: none; z-index: 0; position: absolute; width: 0px; height: 0px';" +
" document.body.append(iframe);" +
" }" +
" var script = iframe.contentWindow.document.createElement('script');" +
" script.innerHTML = "+ PluginScriptsUtil.VAR_JSON_SOURCE_ENCODED + ";" +
" iframe.contentWindow.document.body.append(script);" +
"})();";
private static final String DOCUMENT_READY_WRAPPER_JS_SOURCE = "if (document.readyState === 'interactive' || document.readyState === 'complete') { " +
" " + PluginScriptsUtil.VAR_PLACEHOLDER_VALUE +
"} else {" +
" document.addEventListener('DOMContentLoaded', function() {" +
" " + PluginScriptsUtil.VAR_PLACEHOLDER_VALUE +
" });" +
"}";
}

View File

@ -0,0 +1,105 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Map;
public class UserScript {
@Nullable
private String groupName;
@NonNull
private String source;
@NonNull
private UserScriptInjectionTime injectionTime;
@NonNull
private ContentWorld contentWorld;
public UserScript(@Nullable String groupName, @NonNull String source, @NonNull UserScriptInjectionTime injectionTime, @Nullable ContentWorld contentWorld) {
this.groupName = groupName;
this.source = source;
this.injectionTime = injectionTime;
this.contentWorld = contentWorld == null ? ContentWorld.PAGE : contentWorld;
}
@Nullable
public static UserScript fromMap(@Nullable Map<String, Object> map) {
if (map == null) {
return null;
}
String groupName = (String) map.get("groupName");
String source = (String) map.get("source");
UserScriptInjectionTime injectionTime = UserScriptInjectionTime.fromValue((int) map.get("injectionTime"));
ContentWorld contentWorld = ContentWorld.fromMap((Map<String, Object>) map.get("contentWorld"));
assert source != null;
return new UserScript(groupName, source, injectionTime, contentWorld);
}
@Nullable
public String getGroupName() {
return groupName;
}
public void setGroupName(@Nullable String groupName) {
this.groupName = groupName;
}
@NonNull
public String getSource() {
return source;
}
public void setSource(@NonNull String source) {
this.source = source;
}
@NonNull
public UserScriptInjectionTime getInjectionTime() {
return injectionTime;
}
public void setInjectionTime(@NonNull UserScriptInjectionTime injectionTime) {
this.injectionTime = injectionTime;
}
@NonNull
public ContentWorld getContentWorld() {
return contentWorld;
}
public void setContentWorld(@Nullable ContentWorld contentWorld) {
this.contentWorld = contentWorld == null ? ContentWorld.PAGE : contentWorld;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserScript that = (UserScript) o;
if (groupName != null ? !groupName.equals(that.groupName) : that.groupName != null) return false;
if (!source.equals(that.source)) return false;
if (injectionTime != that.injectionTime) return false;
return contentWorld.equals(that.contentWorld);
}
@Override
public int hashCode() {
int result = groupName != null ? groupName.hashCode() : 0;
result = 31 * result + source.hashCode();
result = 31 * result + injectionTime.hashCode();
result = 31 * result + contentWorld.hashCode();
return result;
}
@Override
public String toString() {
return "UserScript{" +
"groupName='" + groupName + '\'' +
", source='" + source + '\'' +
", injectionTime=" + injectionTime +
", contentWorld=" + contentWorld +
'}';
}
}

View File

@ -0,0 +1,28 @@
package com.pichillilorenzo.flutter_inappwebview.types;
public enum UserScriptInjectionTime {
AT_DOCUMENT_START (0),
AT_DOCUMENT_END (1);
private final int value;
private UserScriptInjectionTime(int value) {
this.value = value;
}
public boolean equalsValue(int otherValue) {
return value == otherValue;
}
public static UserScriptInjectionTime fromValue(int value) {
for( UserScriptInjectionTime type : UserScriptInjectionTime.values()) {
if(value == type.toValue())
return type;
}
throw new IllegalArgumentException("No enum constant: " + value);
}
public int toValue() {
return this.value;
}
}

View File

@ -7,10 +7,10 @@
android:layout_height="match_parent"
android:clickable="true"
android:focusableInTouchMode="true"
tools:context=".InAppBrowser.InAppBrowserActivity"
tools:context=".in_app_browser.InAppBrowserActivity"
android:focusable="true">
<com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView
<com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -3,7 +3,7 @@
xmlns:appcompat="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".InAppBrowser.InAppBrowserActivity">
tools:context=".in_app_browser.InAppBrowserActivity">
<item
android:id="@+id/action_go_back"

View File

@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"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":["device_info"]},{"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":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"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":["device_info"]},{"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":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"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-02-12 16:50:12.665299","version":"1.27.0-2.0.pre.43"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"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":["device_info"]},{"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":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"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":["device_info"]},{"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":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"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-02-22 12:12:06.265826","version":"1.27.0-5.0.pre.90"}

File diff suppressed because it is too large Load Diff

View File

@ -2,12 +2,12 @@
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
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=integration_test/webview_flutter_test.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"

View File

@ -257,6 +257,7 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/OrderedSet/OrderedSet.framework",
"${BUILT_PRODUCTS_DIR}/device_info/device_info.framework",
"${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework",
"${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework",
@ -266,6 +267,7 @@
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OrderedSet.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework",

View File

@ -3,6 +3,7 @@ import Flutter
//import flutter_downloader
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(

View File

@ -4,7 +4,6 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main.dart';
class MyChromeSafariBrowser extends ChromeSafariBrowser {
MyChromeSafariBrowser({browserFallback}) : super(bFallback: browserFallback);
@override
void onOpened() {
@ -24,7 +23,7 @@ class MyChromeSafariBrowser extends ChromeSafariBrowser {
class ChromeSafariBrowserExampleScreen extends StatefulWidget {
final ChromeSafariBrowser browser =
MyChromeSafariBrowser(browserFallback: InAppBrowser());
MyChromeSafariBrowser();
@override
_ChromeSafariBrowserExampleScreenState createState() =>
@ -57,10 +56,10 @@ class _ChromeSafariBrowserExampleScreenState
)),
drawer: myDrawer(context: context),
body: Center(
child: RaisedButton(
child: ElevatedButton(
onPressed: () async {
await widget.browser.open(
url: "https://flutter.dev/",
url: Uri.parse("https://flutter.dev/"),
options: ChromeSafariBrowserClassOptions(
android: AndroidChromeCustomTabsOptions(addDefaultShareMenuItem: false, keepAliveEnabled: true),
ios: IOSSafariOptions(

View File

@ -19,7 +19,9 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
super.initState();
headlessWebView = new HeadlessInAppWebView(
initialUrl: "https://flutter.dev/",
initialUrlRequest: URLRequest(
url: Uri.parse("https://flutter.dev")
),
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
@ -34,19 +36,19 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
onLoadStart: (controller, url) async {
print("onLoadStart $url");
setState(() {
this.url = url ?? '';
this.url = url.toString();
});
},
onLoadStop: (controller, url) async {
print("onLoadStop $url");
setState(() {
this.url = url ?? '';
this.url = url.toString();
});
},
onUpdateVisitedHistory: (controller, url, androidIsReload) {
print("onUpdateVisitedHistory $url");
setState(() {
this.url = url ?? '';
this.url = url.toString();
});
},
);
@ -74,7 +76,7 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
),
Center(
child: RaisedButton(
child: ElevatedButton(
onPressed: () async {
await headlessWebView?.dispose();
await headlessWebView?.run();
@ -82,7 +84,7 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
child: Text("Run HeadlessInAppWebView")),
),
Center(
child: RaisedButton(
child: ElevatedButton(
onPressed: () async {
try {
await headlessWebView?.webViewController.evaluateJavascript(source: """console.log('Here is the message!');""");
@ -93,7 +95,7 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
child: Text("Send console.log message")),
),
Center(
child: RaisedButton(
child: ElevatedButton(
onPressed: () {
headlessWebView?.dispose();
},

View File

@ -41,10 +41,10 @@ class MyInAppBrowser extends InAppBrowser {
}
@override
Future<ShouldOverrideUrlLoadingAction> shouldOverrideUrlLoading(
shouldOverrideUrlLoadingRequest) async {
print("\n\nOverride ${shouldOverrideUrlLoadingRequest.url}\n\n");
return ShouldOverrideUrlLoadingAction.ALLOW;
Future<NavigationActionPolicy> shouldOverrideUrlLoading(
navigationAction) async {
print("\n\nOverride ${navigationAction.request.url}\n\n");
return NavigationActionPolicy.ALLOW;
}
@override
@ -54,7 +54,7 @@ class MyInAppBrowser extends InAppBrowser {
"ms ---> duration: " +
response.duration.toString() +
"ms " +
response.url!);
(response.url ?? '').toString());
}
@override
@ -62,7 +62,7 @@ class MyInAppBrowser extends InAppBrowser {
print("""
console output:
message: ${consoleMessage.message}
messageLevel: ${consoleMessage.messageLevel!.toValue()}
messageLevel: ${consoleMessage.messageLevel.toValue()}
""");
}
}
@ -93,23 +93,24 @@ class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
ElevatedButton(
onPressed: () async {
await widget.browser.openFile(
assetFilePath: "assets/index.html",
await widget.browser.openUrlRequest(
urlRequest: URLRequest(url: Uri.parse("https://flutter.dev")),
options: InAppBrowserClassOptions(
inAppWebViewGroupOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true,
useOnLoadResource: true,
))));
inAppWebViewGroupOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true,
useOnLoadResource: true,
)
)));
},
child: Text("Open In-App Browser")),
Container(height: 40),
RaisedButton(
ElevatedButton(
onPressed: () async {
await InAppBrowser.openWithSystemBrowser(
url: "https://flutter.dev/");
url: Uri.parse("https://flutter.dev/"));
},
child: Text("Open System Browser")),
])));

View File

@ -5,7 +5,6 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:path_provider/path_provider.dart';
// import 'package:path_provider/path_provider.dart';
import 'package:url_launcher/url_launcher.dart';
@ -61,6 +60,20 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
super.dispose();
}
var options = InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: false,
mediaPlaybackRequiresUserGesture: false,
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
// limitsNavigationsToAppBoundDomains: true // adds Service Worker API on iOS 14.0+
)
);
@override
Widget build(BuildContext context) {
return Scaffold(
@ -88,26 +101,14 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
child: InAppWebView(
key: webViewKey,
// contextMenu: contextMenu,
initialUrl: "https://flutter.dev",
initialUrlRequest: URLRequest(
url: Uri.parse("https://github.com")
),
// initialFile: "assets/index.html",
initialHeaders: {},
initialUserScripts: UnmodifiableListView<UserScript>([
]),
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: false,
mediaPlaybackRequiresUserGesture: false,
clearCache: true,
),
android: AndroidInAppWebViewOptions(
useHybridComposition: false,
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
// limitsNavigationsToAppBoundDomains: true // adds Service Worker API on iOS 14.0+
)
),
initialOptions: options,
onWebViewCreated: (controller) {
webView = controller;
print("onWebViewCreated");
@ -115,15 +116,14 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
onLoadStart: (controller, url) {
print("onLoadStart $url");
setState(() {
this.url = url ?? '';
this.url = url.toString();
});
},
androidOnPermissionRequest: (InAppWebViewController controller, String origin, List<String> resources) async {
return PermissionRequestResponse(resources: resources, action: PermissionRequestResponseAction.GRANT);
},
shouldOverrideUrlLoading: (controller, shouldOverrideUrlLoadingRequest) async {
var url = shouldOverrideUrlLoadingRequest.url;
var uri = Uri.parse(url);
shouldOverrideUrlLoading: (controller, navigationAction) async {
var uri = navigationAction.request.url!;
if (!["http", "https", "file",
"chrome", "data", "javascript",
@ -134,17 +134,21 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
url,
);
// and cancel the request
return ShouldOverrideUrlLoadingAction.CANCEL;
return NavigationActionPolicy.CANCEL;
}
}
return ShouldOverrideUrlLoadingAction.ALLOW;
return NavigationActionPolicy.ALLOW;
},
onLoadResource: (controller, resource) {
// print(resource);
},
onLoadStop: (controller, url) async {
print("onLoadStop $url");
setState(() {
this.url = url ?? '';
this.url = url.toString();
});
webView = controller;
// RenderObject renderBox = webViewKey.currentContext!.findRenderObject()!;
// print(renderBox.paintBounds.size);
@ -157,10 +161,11 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
onUpdateVisitedHistory: (controller, url, androidIsReload) {
print("onUpdateVisitedHistory $url");
setState(() {
this.url = url ?? '';
this.url = url.toString();
});
},
onConsoleMessage: (controller, consoleMessage) {
print("CONSOLE MESSAGE FROM MAIN WEBVIEW!");
print(consoleMessage);
},
),
@ -169,19 +174,19 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
ElevatedButton(
child: Icon(Icons.arrow_back),
onPressed: () {
webView?.goBack();
},
),
RaisedButton(
ElevatedButton(
child: Icon(Icons.arrow_forward),
onPressed: () {
webView?.goForward();
},
),
RaisedButton(
ElevatedButton(
child: Icon(Icons.refresh),
onPressed: () {
webView?.reload();

View File

@ -5,9 +5,7 @@
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>InAppWebViewOnLoadResourceTest</title>
<link rel="stylesheet" href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="css/style.css">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<link rel="shortcut icon" href="favicon.ico">
</head>
<body class="text-center">
@ -20,5 +18,17 @@
</p>
</main>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
var script = document.createElement('script');
script.src = "https://code.jquery.com/jquery-3.3.1.min.js"
document.body.append(script);
var link = document.createElement('link');
link.rel = "stylesheet";
link.href = "https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css"
document.body.append(link);
});
</script>
</body>
</html>

View File

@ -80,5 +80,6 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@ -32,29 +32,16 @@ class CredentialDatabase: NSObject, FlutterPlugin {
case "getAllAuthCredentials":
var allCredentials: [[String: Any?]] = []
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials {
let protectionSpaceDict = [
"host": protectionSpace.host,
"protocol": protectionSpace.protocol,
"realm": protectionSpace.realm,
"port": protectionSpace.port
] as [String : Any?]
var crendentials: [[String: String?]] = []
var crendentials: [[String: Any?]] = []
for c in credentials {
if let username = c.value.user, let password = c.value.password {
let credential: [String: String] = [
"username": username,
"password": password,
]
crendentials.append(credential)
}
let credential: [String: Any?] = c.value.toMap()
crendentials.append(credential)
}
if crendentials.count > 0 {
let dict = [
"protectionSpace": protectionSpaceDict,
let dict: [String : Any] = [
"protectionSpace": protectionSpace.toMap(),
"credentials": crendentials
] as [String : Any]
]
allCredentials.append(dict)
} }
result(allCredentials)
@ -67,19 +54,13 @@ class CredentialDatabase: NSObject, FlutterPlugin {
if let r = realm, r.isEmpty {
realm = nil
}
var crendentials: [[String: String?]] = []
var crendentials: [[String: Any?]] = []
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials {
if protectionSpace.host == host && protectionSpace.realm == realm &&
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
for c in credentials {
if let username = c.value.user, let password = c.value.password {
let credential: [String: String] = [
"username": username,
"password": password,
]
crendentials.append(credential)
}
crendentials.append(c.value.toMap())
}
break
}
@ -97,7 +78,8 @@ class CredentialDatabase: NSObject, FlutterPlugin {
let username = arguments!["username"] as! String
let password = arguments!["password"] as! String
let credential = URLCredential(user: username, password: password, persistence: .permanent)
CredentialDatabase.credentialStore!.set(credential, for: URLProtectionSpace(host: host, port: urlPort, protocol: urlProtocol, realm: realm, authenticationMethod: NSURLAuthenticationMethodHTTPBasic))
CredentialDatabase.credentialStore!.set(credential,
for: URLProtectionSpace(host: host, port: urlPort, protocol: urlProtocol, realm: realm, authenticationMethod: NSURLAuthenticationMethodHTTPBasic))
result(true)
break
case "removeHttpAuthCredential":

Some files were not shown because too many files have changed in this diff Show More