diff --git a/.github/ISSUE_TEMPLATE/APP_SHOWCASE.md b/.github/ISSUE_TEMPLATE/APP_SHOWCASE.md new file mode 100755 index 00000000..169f5961 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/APP_SHOWCASE.md @@ -0,0 +1,10 @@ +--- +name: App Showcase +about: Add your App on the Official Showcase Page + +--- + +Check the [Showcase](https://inappwebview.dev/showcase/) page to see an open list of Apps built with **Flutter** and **Flutter InAppWebView**. + +If you are using the **Flutter InAppWebView** plugin and would like to add your App there, +follow the instruction on the [Submit App](https://inappwebview.dev/submit-app/) page! \ No newline at end of file diff --git a/.pubignore b/.pubignore new file mode 100755 index 00000000..6814d022 --- /dev/null +++ b/.pubignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ +.fvm/ + +flutter_driver_tests.log +tool/chromedriver +tool/chromedriver.* + +scripts/ +dev_packages/ +flutter_inappwebview_platform_interface/ +flutter_inappwebview_android/ +flutter_inappwebview_ios/ +flutter_inappwebview_macos/ +flutter_inappwebview_web/ +test_node_server/ +.idea/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f09013d5..a6a3a371 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 6.0.0-beta.29 + +### BREAKING CHANGES + +- Plugin conversion to a [Federated Plugin](https://docs.flutter.dev/packages-and-plugins/developing-packages#federated-plugins) to better support multiple environments and implementations. +- Dart SDK min version `>= 2.17.0` +- Android package name has been changed to `com.pichillilorenzo.flutter_inappwebview_android`. References to old package name `com.pichillilorenzo.flutter_inappwebview` should be updated, for example inside `AndroidManifest.xml` file: `=2.15.0 <4.0.0" +- Dart sdk: ">=2.17.0 <4.0.0" - Flutter: ">=3.0.0" - Android: `minSdkVersion 19` and add support for `androidx` (see [AndroidX Migration](https://docs.flutter.dev/development/platform-integration/android/androidx-migration) to migrate an existing app) - iOS 9.0+: `--ios-language swift`, Xcode version `>= 14.3` @@ -64,7 +64,7 @@ To make it work properly on the Web platform, you need to add the `web_support.j ```html - + ``` diff --git a/analysis_options.yaml b/analysis_options.yaml index 24ec4a21..84964c30 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -3,9 +3,13 @@ include: package:flutter_lints/flutter.yaml linter: rules: constant_identifier_names: ignore + deprecated_member_use_from_same_package: ignore # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options analyzer: errors: - deprecated_member_use_from_same_package: ignore \ No newline at end of file + deprecated_member_use: ignore + deprecated_member_use_from_same_package: ignore + unnecessary_cast: ignore + unnecessary_import: ignore diff --git a/android/.idea/.name b/android/.idea/.name deleted file mode 100755 index 198f3c84..00000000 --- a/android/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -flutter_inappbrowser \ No newline at end of file diff --git a/android/.idea/caches/build_file_checksums.ser b/android/.idea/caches/build_file_checksums.ser deleted file mode 100755 index 118c2ce5..00000000 Binary files a/android/.idea/caches/build_file_checksums.ser and /dev/null differ diff --git a/android/.idea/caches/gradle_models.ser b/android/.idea/caches/gradle_models.ser deleted file mode 100755 index 23c9acaa..00000000 Binary files a/android/.idea/caches/gradle_models.ser and /dev/null differ diff --git a/android/.idea/codeStyles/Project.xml b/android/.idea/codeStyles/Project.xml deleted file mode 100755 index 30aa626c..00000000 --- a/android/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml deleted file mode 100755 index 2996d531..00000000 --- a/android/.idea/gradle.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml deleted file mode 100755 index af0bbdde..00000000 --- a/android/.idea/misc.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/.idea/modules.xml b/android/.idea/modules.xml deleted file mode 100755 index f33b6a32..00000000 --- a/android/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/android/.idea/runConfigurations.xml b/android/.idea/runConfigurations.xml deleted file mode 100755 index 7f68460d..00000000 --- a/android/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android/.idea/vcs.xml b/android/.idea/vcs.xml deleted file mode 100755 index 6c0b8635..00000000 --- a/android/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100755 index 62d7fc6d..00000000 --- a/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'flutter_inappwebview' diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/Disposable.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/Disposable.java deleted file mode 100644 index 468ee1a5..00000000 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/Disposable.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.pichillilorenzo.flutter_inappwebview.types; - -public interface Disposable { - void dispose(); -} diff --git a/dev_packages/generators/lib/src/util.dart b/dev_packages/generators/lib/src/util.dart index 1095fdad..b5bf5b40 100644 --- a/dev_packages/generators/lib/src/util.dart +++ b/dev_packages/generators/lib/src/util.dart @@ -66,11 +66,11 @@ abstract class Util { return """/// ${platformNoteList.join("\n///\n")} /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ${platformSupportedList.join("\n")}"""; } else { return """/// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ${platformSupportedList.join("\n")}"""; } } diff --git a/dev_packages/generators/pubspec.lock b/dev_packages/generators/pubspec.lock deleted file mode 100644 index bd52f163..00000000 --- a/dev_packages/generators/pubspec.lock +++ /dev/null @@ -1,583 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 - url: "https://pub.dev" - source: hosted - version: "64.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" - url: "https://pub.dev" - source: hosted - version: "6.2.0" - args: - dependency: transitive - description: - name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 - url: "https://pub.dev" - source: hosted - version: "2.4.2" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - build: - dependency: "direct main" - description: - name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" - url: "https://pub.dev" - source: hosted - version: "4.0.1" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" - url: "https://pub.dev" - source: hosted - version: "2.4.6" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 - url: "https://pub.dev" - source: hosted - version: "7.2.11" - build_test: - dependency: "direct dev" - description: - name: build_test - sha256: "178a9e8989cbd40b3104b88cb6ab8cbf6e3293d90b31ba44420745cba54730f1" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: "723b4021e903217dfc445ec4cf5b42e27975aece1fc4ebbc1ca6329c2d9fb54e" - url: "https://pub.dev" - source: hosted - version: "8.7.0" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" - url: "https://pub.dev" - source: hosted - version: "4.7.0" - collection: - dependency: transitive - description: - name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 - url: "https://pub.dev" - source: hosted - version: "1.17.2" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - coverage: - dependency: transitive - description: - name: coverage - sha256: ac86d3abab0f165e4b8f561280ff4e066bceaac83c424dd19f1ae2c2fcd12ca9 - url: "https://pub.dev" - source: hosted - version: "1.7.1" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - csslib: - dependency: transitive - description: - name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 - url: "https://pub.dev" - source: hosted - version: "2.3.3" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_inappwebview_internal_annotations: - dependency: "direct main" - description: - path: "../flutter_inappwebview_internal_annotations" - relative: true - source: path - version: "1.1.1" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - graphs: - dependency: transitive - description: - name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 - url: "https://pub.dev" - source: hosted - version: "2.3.1" - html: - dependency: transitive - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 - url: "https://pub.dev" - source: hosted - version: "4.8.1" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" - url: "https://pub.dev" - source: hosted - version: "0.12.16" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" - url: "https://pub.dev" - source: hosted - version: "0.5.0" - meta: - dependency: transitive - description: - name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - mime: - dependency: transitive - description: - name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e - url: "https://pub.dev" - source: hosted - version: "1.0.4" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - path: - dependency: transitive - description: - name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.dev" - source: hosted - version: "1.8.3" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 - url: "https://pub.dev" - source: hosted - version: "1.2.3" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e - url: "https://pub.dev" - source: hosted - version: "1.1.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_gen: - dependency: "direct main" - description: - name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 - url: "https://pub.dev" - source: hosted - version: "1.4.0" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - source_maps: - dependency: transitive - description: - name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" - url: "https://pub.dev" - source: hosted - version: "0.10.12" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test: - dependency: "direct dev" - description: - name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f - url: "https://pub.dev" - source: hosted - version: "1.24.9" - test_api: - dependency: transitive - description: - name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" - url: "https://pub.dev" - source: hosted - version: "0.6.1" - test_core: - dependency: transitive - description: - name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a - url: "https://pub.dev" - source: hosted - version: "0.5.9" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 - url: "https://pub.dev" - source: hosted - version: "13.0.0" - watcher: - dependency: transitive - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" - source: hosted - version: "2.4.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" -sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=2.5.0" diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 61b84110..b3cbd634 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -11,4 +11,14 @@ include: package:flutter_lints/flutter.yaml linter: rules: - constant_identifier_names: ignore \ No newline at end of file + constant_identifier_names: ignore + deprecated_member_use_from_same_package: ignore + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options +analyzer: + errors: + deprecated_member_use: ignore + deprecated_member_use_from_same_package: ignore + unnecessary_cast: ignore + unnecessary_import: ignore diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index bb9ccad5..cc96cd82 100755 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -70,7 +70,7 @@ pageLoaded = Completer(); var headlessWebView = new HeadlessInAppWebView( - initialUrlRequest: URLRequest(url: TEST_CROSS_PLATFORM_URL_1), - onWebViewCreated: (controller) { - controllerCompleter.complete(controller); - }, - ); + initialUrlRequest: URLRequest(url: TEST_CROSS_PLATFORM_URL_1), + onWebViewCreated: (controller) { + controllerCompleter.complete(controller); + }, + onLoadStop: (controller, url) async { + pageLoaded.complete(url!.toString()); + }); if (defaultTargetPlatform == TargetPlatform.macOS) { - headlessWebView.onLoadStop = (controller, url) async { - pageLoaded.complete(url!.toString()); - }; await headlessWebView.run(); } else { await tester.pumpWidget( diff --git a/example/integration_test/headless_in_app_webview/convert_to_inappwebview.dart b/example/integration_test/headless_in_app_webview/convert_to_inappwebview.dart index e553e7c4..af88f38f 100644 --- a/example/integration_test/headless_in_app_webview/convert_to_inappwebview.dart +++ b/example/integration_test/headless_in_app_webview/convert_to_inappwebview.dart @@ -14,14 +14,13 @@ void convertToInAppWebView() { final Completer pageLoaded = Completer(); var headlessWebView = new HeadlessInAppWebView( - initialUrlRequest: URLRequest(url: TEST_CROSS_PLATFORM_URL_1), - onWebViewCreated: (controller) { - controllerCompleter.complete(controller); - }, - ); - headlessWebView.onLoadStop = (controller, url) async { - pageLoaded.complete(); - }; + initialUrlRequest: URLRequest(url: TEST_CROSS_PLATFORM_URL_1), + onWebViewCreated: (controller) { + controllerCompleter.complete(controller); + }, + onLoadStop: (controller, url) async { + pageLoaded.complete(); + }); await headlessWebView.run(); expect(headlessWebView.isRunning(), true); diff --git a/example/integration_test/headless_in_app_webview/run_and_dispose.dart b/example/integration_test/headless_in_app_webview/run_and_dispose.dart index b491aef3..8a9db01f 100644 --- a/example/integration_test/headless_in_app_webview/run_and_dispose.dart +++ b/example/integration_test/headless_in_app_webview/run_and_dispose.dart @@ -15,14 +15,13 @@ void runAndDispose() { final Completer pageLoaded = Completer(); var headlessWebView = new HeadlessInAppWebView( - initialUrlRequest: URLRequest(url: TEST_CROSS_PLATFORM_URL_1), - onWebViewCreated: (controller) { - controllerCompleter.complete(controller); - }, - ); - headlessWebView.onLoadStop = (controller, url) async { - pageLoaded.complete(); - }; + initialUrlRequest: URLRequest(url: TEST_CROSS_PLATFORM_URL_1), + onWebViewCreated: (controller) { + controllerCompleter.complete(controller); + }, + onLoadStop: (controller, url) async { + pageLoaded.complete(); + }); await headlessWebView.run(); expect(headlessWebView.isRunning(), true); diff --git a/example/integration_test/process_global_config/main.dart b/example/integration_test/process_global_config/main.dart index aef30436..07643c25 100644 --- a/example/integration_test/process_global_config/main.dart +++ b/example/integration_test/process_global_config/main.dart @@ -1,12 +1,8 @@ -import 'dart:async'; - import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:path_provider/path_provider.dart'; -import '../constants.dart'; -import '../env.dart'; import '../util.dart'; part 'apply.dart'; diff --git a/example/integration_test/webview_flutter_test.dart b/example/integration_test/webview_flutter_test.dart index cc7e6d0e..5d02287f 100644 --- a/example/integration_test/webview_flutter_test.dart +++ b/example/integration_test/webview_flutter_test.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:integration_test/integration_test.dart'; @@ -18,18 +17,22 @@ import 'tracing_controller/main.dart' as tracing_controller_tests; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - WebView.debugLoggingSettings.usePrint = true; - WebView.debugLoggingSettings.maxLogMessageLength = 7000; - InAppBrowser.debugLoggingSettings.usePrint = true; - InAppBrowser.debugLoggingSettings.maxLogMessageLength = 7000; - ChromeSafariBrowser.debugLoggingSettings.usePrint = true; - ChromeSafariBrowser.debugLoggingSettings.maxLogMessageLength = 7000; - WebAuthenticationSession.debugLoggingSettings.usePrint = true; - WebAuthenticationSession.debugLoggingSettings.maxLogMessageLength = 7000; - PullToRefreshController.debugLoggingSettings.usePrint = true; - PullToRefreshController.debugLoggingSettings.maxLogMessageLength = 7000; - FindInteractionController.debugLoggingSettings.usePrint = true; - FindInteractionController.debugLoggingSettings.maxLogMessageLength = 7000; + PlatformInAppWebViewController.debugLoggingSettings.usePrint = true; + PlatformInAppWebViewController.debugLoggingSettings.maxLogMessageLength = + 7000; + PlatformInAppBrowser.debugLoggingSettings.usePrint = true; + PlatformInAppBrowser.debugLoggingSettings.maxLogMessageLength = 7000; + PlatformChromeSafariBrowser.debugLoggingSettings.usePrint = true; + PlatformChromeSafariBrowser.debugLoggingSettings.maxLogMessageLength = 7000; + PlatformWebAuthenticationSession.debugLoggingSettings.usePrint = true; + PlatformWebAuthenticationSession.debugLoggingSettings.maxLogMessageLength = + 7000; + PlatformPullToRefreshController.debugLoggingSettings.usePrint = true; + PlatformPullToRefreshController.debugLoggingSettings.maxLogMessageLength = + 7000; + PlatformFindInteractionController.debugLoggingSettings.usePrint = true; + PlatformFindInteractionController.debugLoggingSettings.maxLogMessageLength = + 7000; process_global_config_tests.main(); in_app_webview_tests.main(); diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index b34f61c2..652b1176 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -356,7 +356,7 @@ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${BUILT_PRODUCTS_DIR}/OrderedSet/OrderedSet.framework", "${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework", - "${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework", + "${BUILT_PRODUCTS_DIR}/flutter_inappwebview_ios/flutter_inappwebview_ios.framework", "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", @@ -365,7 +365,7 @@ outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OrderedSet.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", @@ -460,7 +460,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-example1.test"; + PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-ios-example.test"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; @@ -497,7 +497,7 @@ ); MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-example1.test"; + PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-ios-example.test"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -646,7 +646,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-example1"; + PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-ios-example"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -681,7 +681,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-example1"; + PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-ios-example"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/example/lib/headless_in_app_webview.screen.dart b/example/lib/headless_in_app_webview.screen.dart index 34e5bf38..87e3dc20 100755 --- a/example/lib/headless_in_app_webview.screen.dart +++ b/example/lib/headless_in_app_webview.screen.dart @@ -7,7 +7,7 @@ import 'main.dart'; class HeadlessInAppWebViewExampleScreen extends StatefulWidget { @override _HeadlessInAppWebViewExampleScreenState createState() => - new _HeadlessInAppWebViewExampleScreenState(); + _HeadlessInAppWebViewExampleScreenState(); } class _HeadlessInAppWebViewExampleScreenState @@ -23,7 +23,7 @@ class _HeadlessInAppWebViewExampleScreenState ? WebUri("https://flutter.dev") : WebUri("http://localhost:${Uri.base.port}/page.html"); - headlessWebView = new HeadlessInAppWebView( + headlessWebView = HeadlessInAppWebView( initialUrlRequest: URLRequest(url: url), initialSettings: InAppWebViewSettings( isInspectable: kDebugMode, @@ -88,8 +88,9 @@ class _HeadlessInAppWebViewExampleScreenState child: ElevatedButton( onPressed: () async { if (headlessWebView?.isRunning() ?? false) { - await headlessWebView?.webViewController?.evaluateJavascript( - source: """console.log('Here is the message!');"""); + await headlessWebView?.webViewController + ?.evaluateJavascript( + source: """console.log('Here is the message!');"""); } else { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( diff --git a/example/lib/in_app_browser_example.screen.dart b/example/lib/in_app_browser_example.screen.dart index a7b7cb3b..11ee9852 100755 --- a/example/lib/in_app_browser_example.screen.dart +++ b/example/lib/in_app_browser_example.screen.dart @@ -11,8 +11,13 @@ import 'main.dart'; class MyInAppBrowser extends InAppBrowser { MyInAppBrowser( - {int? windowId, UnmodifiableListView? initialUserScripts}) - : super(windowId: windowId, initialUserScripts: initialUserScripts); + {int? windowId, + UnmodifiableListView? initialUserScripts, + PullToRefreshController? pullToRefreshController}) + : super( + windowId: windowId, + initialUserScripts: initialUserScripts, + pullToRefreshController: pullToRefreshController); @override Future onBrowserCreated() async { @@ -59,21 +64,19 @@ class MyInAppBrowser extends InAppBrowser { } class InAppBrowserExampleScreen extends StatefulWidget { - final MyInAppBrowser browser = new MyInAppBrowser(); - @override _InAppBrowserExampleScreenState createState() => - new _InAppBrowserExampleScreenState(); + _InAppBrowserExampleScreenState(); } class _InAppBrowserExampleScreenState extends State { - PullToRefreshController? pullToRefreshController; + late final MyInAppBrowser browser; @override void initState() { super.initState(); - pullToRefreshController = kIsWeb || + PullToRefreshController? pullToRefreshController = kIsWeb || ![TargetPlatform.iOS, TargetPlatform.android] .contains(defaultTargetPlatform) ? null @@ -83,15 +86,16 @@ class _InAppBrowserExampleScreenState extends State { ), onRefresh: () async { if (Platform.isAndroid) { - widget.browser.webViewController?.reload(); + browser.webViewController?.reload(); } else if (Platform.isIOS) { - widget.browser.webViewController?.loadUrl( + browser.webViewController?.loadUrl( urlRequest: URLRequest( - url: await widget.browser.webViewController?.getUrl())); + url: await browser.webViewController?.getUrl())); } }, ); - widget.browser.pullToRefreshController = pullToRefreshController; + + browser = MyInAppBrowser(pullToRefreshController: pullToRefreshController); } @override @@ -108,7 +112,7 @@ class _InAppBrowserExampleScreenState extends State { children: [ ElevatedButton( onPressed: () async { - await widget.browser.openUrlRequest( + await browser.openUrlRequest( urlRequest: URLRequest(url: WebUri("https://flutter.dev")), settings: InAppBrowserClassSettings( diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 6eff2d15..79dc016f 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -9,7 +9,7 @@ import 'main.dart'; class InAppWebViewExampleScreen extends StatefulWidget { @override _InAppWebViewExampleScreenState createState() => - new _InAppWebViewExampleScreenState(); + _InAppWebViewExampleScreenState(); } class _InAppWebViewExampleScreenState extends State { diff --git a/example/lib/main.dart b/example/lib/main.dart index 0d9c4ce7..d4655513 100755 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -15,7 +15,7 @@ import 'package:pointer_interceptor/pointer_interceptor.dart'; // import 'package:permission_handler/permission_handler.dart'; InAppLocalhostServer localhostServer = - new InAppLocalhostServer(documentRoot: 'assets'); + InAppLocalhostServer(documentRoot: 'assets'); Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -130,7 +130,7 @@ PointerInterceptor myDrawer({required BuildContext context}) { class MyApp extends StatefulWidget { @override - _MyAppState createState() => new _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 154ccce1..959e5aa0 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,7 +5,7 @@ import FlutterMacOS import Foundation -import flutter_inappwebview +import flutter_inappwebview_macos import path_provider_foundation import url_launcher_macos diff --git a/example/web/index.html b/example/web/index.html index f63aa99b..cb50c162 100644 --- a/example/web/index.html +++ b/example/web/index.html @@ -33,7 +33,7 @@ - + + + diff --git a/flutter_inappwebview_android/example/android/app/src/main/AndroidManifest.xml b/flutter_inappwebview_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..587125fe --- /dev/null +++ b/flutter_inappwebview_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/flutter_inappwebview_android/example/android/app/src/main/java/com/pichillilorenzo/flutter_inappwebview_android_example/MainActivity.java b/flutter_inappwebview_android/example/android/app/src/main/java/com/pichillilorenzo/flutter_inappwebview_android_example/MainActivity.java new file mode 100644 index 00000000..8463e659 --- /dev/null +++ b/flutter_inappwebview_android/example/android/app/src/main/java/com/pichillilorenzo/flutter_inappwebview_android_example/MainActivity.java @@ -0,0 +1,6 @@ +package com.pichillilorenzo.flutter_inappwebview_android_example; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/flutter_inappwebview_android/example/android/app/src/main/res/drawable-v21/launch_background.xml b/flutter_inappwebview_android/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/flutter_inappwebview_android/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/flutter_inappwebview_android/example/android/app/src/main/res/drawable/launch_background.xml b/flutter_inappwebview_android/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/flutter_inappwebview_android/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..db77bb4b Binary files /dev/null and b/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..17987b79 Binary files /dev/null and b/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..09d43914 Binary files /dev/null and b/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..d5f1c8d3 Binary files /dev/null and b/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..4d6372ee Binary files /dev/null and b/flutter_inappwebview_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/flutter_inappwebview_android/example/android/app/src/main/res/values-night/styles.xml b/flutter_inappwebview_android/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..06952be7 --- /dev/null +++ b/flutter_inappwebview_android/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/flutter_inappwebview_android/example/android/app/src/main/res/values/styles.xml b/flutter_inappwebview_android/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..cb1ef880 --- /dev/null +++ b/flutter_inappwebview_android/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/flutter_inappwebview_android/example/android/app/src/profile/AndroidManifest.xml b/flutter_inappwebview_android/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..399f6981 --- /dev/null +++ b/flutter_inappwebview_android/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter_inappwebview_android/example/android/build.gradle b/flutter_inappwebview_android/example/android/build.gradle new file mode 100644 index 00000000..f7eb7f63 --- /dev/null +++ b/flutter_inappwebview_android/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.3.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/flutter_inappwebview_android/example/android/gradle.properties b/flutter_inappwebview_android/example/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/flutter_inappwebview_android/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/flutter_inappwebview_android/example/android/gradle/wrapper/gradle-wrapper.properties b/flutter_inappwebview_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..3c472b99 --- /dev/null +++ b/flutter_inappwebview_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/flutter_inappwebview_android/example/android/settings.gradle b/flutter_inappwebview_android/example/android/settings.gradle new file mode 100644 index 00000000..55c4ca8b --- /dev/null +++ b/flutter_inappwebview_android/example/android/settings.gradle @@ -0,0 +1,20 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + } + settings.ext.flutterSdkPath = flutterSdkPath() + + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + + plugins { + id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false + } +} + +include ":app" + +apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/ios/Assets/.gitkeep b/flutter_inappwebview_android/example/integration_test/plugin_integration_test.dart old mode 100755 new mode 100644 similarity index 100% rename from ios/Assets/.gitkeep rename to flutter_inappwebview_android/example/integration_test/plugin_integration_test.dart diff --git a/flutter_inappwebview_android/example/lib/main.dart b/flutter_inappwebview_android/example/lib/main.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_android/example/pubspec.lock b/flutter_inappwebview_android/example/pubspec.lock new file mode 100644 index 00000000..e3488aae --- /dev/null +++ b/flutter_inappwebview_android/example/pubspec.lock @@ -0,0 +1,283 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" + source: hosted + version: "1.17.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_inappwebview_android: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "1.0.0" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "5f80fd30e208ddded7dbbcd0d569e7995f9f63d45ea3f548d8dd4c0b473fb4c8" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: c221a6788c73421284208e3449cad07d8c65fdace120322483181a2b94d95697 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + url: "https://pub.dev" + source: hosted + version: "2.1.6" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + url: "https://pub.dev" + source: hosted + version: "11.7.1" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + url: "https://pub.dev" + source: hosted + version: "3.0.2" +sdks: + dart: ">=3.1.4 <4.0.0" + flutter: ">=3.0.0" diff --git a/flutter_inappwebview_android/example/pubspec.yaml b/flutter_inappwebview_android/example/pubspec.yaml new file mode 100644 index 00000000..4aaafc26 --- /dev/null +++ b/flutter_inappwebview_android/example/pubspec.yaml @@ -0,0 +1,85 @@ +name: flutter_inappwebview_android_example +description: Demonstrates how to use the flutter_inappwebview_android plugin. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=3.1.4 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + flutter_inappwebview_android: + # When depending on this package from a real application you should use: + # flutter_inappwebview_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + integration_test: + sdk: flutter + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter_inappwebview_android/example/test/widget_test.dart b/flutter_inappwebview_android/example/test/widget_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_android/lib/flutter_inappwebview_android.dart b/flutter_inappwebview_android/lib/flutter_inappwebview_android.dart new file mode 100644 index 00000000..dd86c9e0 --- /dev/null +++ b/flutter_inappwebview_android/lib/flutter_inappwebview_android.dart @@ -0,0 +1,3 @@ +library flutter_inappwebview_android; + +export 'src/main.dart'; diff --git a/flutter_inappwebview_android/lib/src/chrome_safari_browser/chrome_safari_browser.dart b/flutter_inappwebview_android/lib/src/chrome_safari_browser/chrome_safari_browser.dart new file mode 100755 index 00000000..8c2b008f --- /dev/null +++ b/flutter_inappwebview_android/lib/src/chrome_safari_browser/chrome_safari_browser.dart @@ -0,0 +1,376 @@ +import 'dart:async'; +import 'dart:collection'; +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidChromeSafariBrowser]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformChromeSafariBrowserCreationParams] for +/// more information. +@immutable +class AndroidChromeSafariBrowserCreationParams + extends PlatformChromeSafariBrowserCreationParams { + /// Creates a new [AndroidChromeSafariBrowserCreationParams] instance. + const AndroidChromeSafariBrowserCreationParams(); + + /// Creates a [AndroidChromeSafariBrowserCreationParams] instance based on [PlatformChromeSafariBrowserCreationParams]. + factory AndroidChromeSafariBrowserCreationParams.fromPlatformChromeSafariBrowserCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformChromeSafariBrowserCreationParams params) { + return AndroidChromeSafariBrowserCreationParams(); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser} +class AndroidChromeSafariBrowser extends PlatformChromeSafariBrowser + with ChannelController { + @override + final String id = IdGenerator.generate(); + + /// Constructs a [AndroidChromeSafariBrowser]. + AndroidChromeSafariBrowser(PlatformChromeSafariBrowserCreationParams params) + : super.implementation( + params is AndroidChromeSafariBrowserCreationParams + ? params + : AndroidChromeSafariBrowserCreationParams + .fromPlatformChromeSafariBrowserCreationParams(params), + ); + + static final AndroidChromeSafariBrowser _staticValue = + AndroidChromeSafariBrowser(AndroidChromeSafariBrowserCreationParams()); + + /// Provide static access. + factory AndroidChromeSafariBrowser.static() { + return _staticValue; + } + + ChromeSafariBrowserActionButton? _actionButton; + Map _menuItems = new HashMap(); + ChromeSafariBrowserSecondaryToolbar? _secondaryToolbar; + bool _isOpened = false; + static const MethodChannel _staticChannel = + const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser'); + + _init() { + channel = + MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + id: id, + debugLoggingSettings: PlatformChromeSafariBrowser.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + _debugLog(call.method, call.arguments); + + switch (call.method) { + case "onServiceConnected": + eventHandler?.onServiceConnected(); + break; + case "onOpened": + eventHandler?.onOpened(); + break; + case "onCompletedInitialLoad": + final bool? didLoadSuccessfully = call.arguments["didLoadSuccessfully"]; + eventHandler?.onCompletedInitialLoad(didLoadSuccessfully); + break; + case "onNavigationEvent": + final navigationEvent = CustomTabsNavigationEventType.fromNativeValue( + call.arguments["navigationEvent"]); + eventHandler?.onNavigationEvent(navigationEvent); + break; + case "onRelationshipValidationResult": + final relation = + CustomTabsRelationType.fromNativeValue(call.arguments["relation"]); + final requestedOrigin = call.arguments["requestedOrigin"] != null + ? WebUri(call.arguments["requestedOrigin"]) + : null; + final bool result = call.arguments["result"]; + eventHandler?.onRelationshipValidationResult( + relation, requestedOrigin, result); + break; + case "onClosed": + _isOpened = false; + final onClosed = eventHandler?.onClosed; + dispose(); + onClosed?.call(); + break; + case "onItemActionPerform": + String url = call.arguments["url"]; + String title = call.arguments["title"]; + int id = call.arguments["id"].toInt(); + if (this._actionButton?.id == id) { + if (this._actionButton?.action != null) { + this._actionButton?.action!(url, title); + } + if (this._actionButton?.onClick != null) { + this._actionButton?.onClick!(WebUri(url), title); + } + } else if (this._menuItems[id] != null) { + if (this._menuItems[id]?.action != null) { + this._menuItems[id]?.action!(url, title); + } + if (this._menuItems[id]?.onClick != null) { + this._menuItems[id]?.onClick!(WebUri(url), title); + } + } + break; + case "onSecondaryItemActionPerform": + final clickableIDs = this._secondaryToolbar?.clickableIDs; + if (clickableIDs != null) { + WebUri? url = call.arguments["url"] != null + ? WebUri(call.arguments["url"]) + : null; + String name = call.arguments["name"]; + for (final clickable in clickableIDs) { + var clickableFullname = clickable.id.name; + if (clickable.id.defType != null && + !clickableFullname.contains("/")) { + clickableFullname = "${clickable.id.defType}/$clickableFullname"; + } + if (clickable.id.defPackage != null && + !clickableFullname.contains(":")) { + clickableFullname = + "${clickable.id.defPackage}:$clickableFullname"; + } + if (clickableFullname == name) { + if (clickable.onClick != null) { + clickable.onClick!(url); + } + break; + } + } + } + break; + case "onMessageChannelReady": + eventHandler?.onMessageChannelReady(); + break; + case "onPostMessage": + final String message = call.arguments["message"]; + eventHandler?.onPostMessage(message); + break; + case "onVerticalScrollEvent": + final bool isDirectionUp = call.arguments["isDirectionUp"]; + eventHandler?.onVerticalScrollEvent(isDirectionUp); + break; + case "onGreatestScrollPercentageIncreased": + final int scrollPercentage = call.arguments["scrollPercentage"]; + eventHandler?.onGreatestScrollPercentageIncreased(scrollPercentage); + break; + case "onSessionEnded": + final bool didUserInteract = call.arguments["didUserInteract"]; + eventHandler?.onSessionEnded(didUserInteract); + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + @override + Future open( + {WebUri? url, + Map? headers, + List? otherLikelyURLs, + WebUri? referrer, + @Deprecated('Use settings instead') + // ignore: deprecated_member_use_from_same_package + ChromeSafariBrowserClassOptions? options, + ChromeSafariBrowserSettings? settings}) async { + assert(!_isOpened, 'The browser is already opened.'); + _isOpened = true; + + if (Util.isIOS) { + assert(url != null, 'The specified URL must not be null on iOS.'); + assert(['http', 'https'].contains(url!.scheme), + 'The specified URL has an unsupported scheme. Only HTTP and HTTPS URLs are supported on iOS.'); + } + if (url != null) { + assert(url.toString().isNotEmpty, 'The specified URL must not be empty.'); + } + + _init(); + + List> menuItemList = []; + _menuItems.forEach((key, value) { + menuItemList.add(value.toMap()); + }); + + var initialSettings = settings?.toMap() ?? + options?.toMap() ?? + ChromeSafariBrowserSettings().toMap(); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent('url', () => url?.toString()); + args.putIfAbsent('headers', () => headers); + args.putIfAbsent('otherLikelyURLs', + () => otherLikelyURLs?.map((e) => e.toString()).toList()); + args.putIfAbsent('referrer', () => referrer?.toString()); + args.putIfAbsent('settings', () => initialSettings); + args.putIfAbsent('actionButton', () => _actionButton?.toMap()); + args.putIfAbsent('secondaryToolbar', () => _secondaryToolbar?.toMap()); + args.putIfAbsent('menuItemList', () => menuItemList); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future launchUrl({ + required WebUri url, + Map? headers, + List? otherLikelyURLs, + WebUri? referrer, + }) async { + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('headers', () => headers); + args.putIfAbsent('otherLikelyURLs', + () => otherLikelyURLs?.map((e) => e.toString()).toList()); + args.putIfAbsent('referrer', () => referrer?.toString()); + await channel?.invokeMethod("launchUrl", args); + } + + @override + Future mayLaunchUrl( + {WebUri? url, List? otherLikelyURLs}) async { + Map args = {}; + args.putIfAbsent('url', () => url?.toString()); + args.putIfAbsent('otherLikelyURLs', + () => otherLikelyURLs?.map((e) => e.toString()).toList()); + return await channel?.invokeMethod("mayLaunchUrl", args) ?? false; + } + + @override + Future validateRelationship( + {required CustomTabsRelationType relation, + required WebUri origin}) async { + Map args = {}; + args.putIfAbsent('relation', () => relation.toNativeValue()); + args.putIfAbsent('origin', () => origin.toString()); + return await channel?.invokeMethod("validateRelationship", args) ?? + false; + } + + @override + Future close() async { + Map args = {}; + await channel?.invokeMethod("close", args); + } + + @override + void setActionButton(ChromeSafariBrowserActionButton actionButton) { + this._actionButton = actionButton; + } + + @override + Future updateActionButton( + {required Uint8List icon, required String description}) async { + Map args = {}; + args.putIfAbsent('icon', () => icon); + args.putIfAbsent('description', () => description); + await channel?.invokeMethod("updateActionButton", args); + _actionButton?.icon = icon; + _actionButton?.description = description; + } + + @override + void setSecondaryToolbar( + ChromeSafariBrowserSecondaryToolbar secondaryToolbar) { + this._secondaryToolbar = secondaryToolbar; + } + + @override + Future updateSecondaryToolbar( + ChromeSafariBrowserSecondaryToolbar secondaryToolbar) async { + Map args = {}; + args.putIfAbsent('secondaryToolbar', () => secondaryToolbar.toMap()); + await channel?.invokeMethod("updateSecondaryToolbar", args); + this._secondaryToolbar = secondaryToolbar; + } + + @override + void addMenuItem(ChromeSafariBrowserMenuItem menuItem) { + this._menuItems[menuItem.id] = menuItem; + } + + @override + void addMenuItems(List menuItems) { + menuItems.forEach((menuItem) { + this._menuItems[menuItem.id] = menuItem; + }); + } + + @override + Future requestPostMessageChannel( + {required WebUri sourceOrigin, WebUri? targetOrigin}) async { + Map args = {}; + args.putIfAbsent("sourceOrigin", () => sourceOrigin.toString()); + args.putIfAbsent("targetOrigin", () => targetOrigin.toString()); + return await channel?.invokeMethod( + "requestPostMessageChannel", args) ?? + false; + } + + @override + Future postMessage(String message) async { + Map args = {}; + args.putIfAbsent("message", () => message); + return CustomTabsPostMessageResultType.fromNativeValue( + await channel?.invokeMethod("postMessage", args)) ?? + CustomTabsPostMessageResultType.FAILURE_MESSAGING_ERROR; + } + + @override + Future isEngagementSignalsApiAvailable() async { + Map args = {}; + return await channel?.invokeMethod( + "isEngagementSignalsApiAvailable", args) ?? + false; + } + + @override + Future isAvailable() async { + Map args = {}; + return await _staticChannel.invokeMethod("isAvailable", args) ?? + false; + } + + @override + Future getMaxToolbarItems() async { + Map args = {}; + return await _staticChannel.invokeMethod("getMaxToolbarItems", args) ?? + 0; + } + + @override + Future getPackageName( + {List? packages, bool ignoreDefault = false}) async { + Map args = {}; + args.putIfAbsent("packages", () => packages); + args.putIfAbsent("ignoreDefault", () => ignoreDefault); + return await _staticChannel.invokeMethod("getPackageName", args); + } + + @override + bool isOpened() { + return _isOpened; + } + + @override + @mustCallSuper + void dispose() { + disposeChannel(); + eventHandler = null; + } +} diff --git a/flutter_inappwebview_android/lib/src/chrome_safari_browser/main.dart b/flutter_inappwebview_android/lib/src/chrome_safari_browser/main.dart new file mode 100644 index 00000000..9a6238b7 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/chrome_safari_browser/main.dart @@ -0,0 +1 @@ +export 'chrome_safari_browser.dart'; diff --git a/flutter_inappwebview_android/lib/src/cookie_manager.dart b/flutter_inappwebview_android/lib/src/cookie_manager.dart new file mode 100755 index 00000000..1499b6e7 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/cookie_manager.dart @@ -0,0 +1,213 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidCookieManager]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformCookieManagerCreationParams] for +/// more information. +@immutable +class AndroidCookieManagerCreationParams + extends PlatformCookieManagerCreationParams { + /// Creates a new [AndroidCookieManagerCreationParams] instance. + const AndroidCookieManagerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformCookieManagerCreationParams params, + ) : super(); + + /// Creates a [AndroidCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams]. + factory AndroidCookieManagerCreationParams.fromPlatformCookieManagerCreationParams( + PlatformCookieManagerCreationParams params) { + return AndroidCookieManagerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager} +class AndroidCookieManager extends PlatformCookieManager + with ChannelController { + /// Creates a new [AndroidCookieManager]. + AndroidCookieManager(PlatformCookieManagerCreationParams params) + : super.implementation( + params is AndroidCookieManagerCreationParams + ? params + : AndroidCookieManagerCreationParams + .fromPlatformCookieManagerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_cookiemanager'); + handler = handleMethod; + initMethodCallHandler(); + } + + static AndroidCookieManager? _instance; + + ///Gets the [AndroidCookieManager] shared instance. + static AndroidCookieManager instance() { + return (_instance != null) ? _instance! : _init(); + } + + static AndroidCookieManager _init() { + _instance = AndroidCookieManager(AndroidCookieManagerCreationParams( + const PlatformCookieManagerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future setCookie( + {required WebUri url, + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + bool? isHttpOnly, + HTTPCookieSameSitePolicy? sameSite, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + assert(value.isNotEmpty); + assert(path.isNotEmpty); + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('name', () => name); + args.putIfAbsent('value', () => value); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + args.putIfAbsent('expiresDate', () => expiresDate?.toString()); + args.putIfAbsent('maxAge', () => maxAge); + args.putIfAbsent('isSecure', () => isSecure); + args.putIfAbsent('isHttpOnly', () => isHttpOnly); + args.putIfAbsent('sameSite', () => sameSite?.toNativeValue()); + + return await channel?.invokeMethod('setCookie', args) ?? false; + } + + @override + Future> getCookies( + {required WebUri url, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + List cookies = []; + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + List cookieListMap = + await channel?.invokeMethod('getCookies', args) ?? []; + cookieListMap = cookieListMap.cast>(); + + cookieListMap.forEach((cookieMap) { + cookies.add(Cookie( + name: cookieMap["name"], + value: cookieMap["value"], + expiresDate: cookieMap["expiresDate"], + isSessionOnly: cookieMap["isSessionOnly"], + domain: cookieMap["domain"], + sameSite: + HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), + isSecure: cookieMap["isSecure"], + isHttpOnly: cookieMap["isHttpOnly"], + path: cookieMap["path"])); + }); + return cookies; + } + + @override + Future getCookie( + {required WebUri url, + required String name, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + List cookies = + await channel?.invokeMethod('getCookies', args) ?? []; + cookies = cookies.cast>(); + for (var i = 0; i < cookies.length; i++) { + cookies[i] = cookies[i].cast(); + if (cookies[i]["name"] == name) + return Cookie( + name: cookies[i]["name"], + value: cookies[i]["value"], + expiresDate: cookies[i]["expiresDate"], + isSessionOnly: cookies[i]["isSessionOnly"], + domain: cookies[i]["domain"], + sameSite: HTTPCookieSameSitePolicy.fromNativeValue( + cookies[i]["sameSite"]), + isSecure: cookies[i]["isSecure"], + isHttpOnly: cookies[i]["isHttpOnly"], + path: cookies[i]["path"]); + } + return null; + } + + @override + Future deleteCookie( + {required WebUri url, + required String name, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('name', () => name); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + await channel?.invokeMethod('deleteCookie', args); + } + + @override + Future deleteCookies( + {required WebUri url, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + await channel?.invokeMethod('deleteCookies', args); + } + + @override + Future deleteAllCookies() async { + Map args = {}; + await channel?.invokeMethod('deleteAllCookies', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalCookieManager on AndroidCookieManager { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_android/lib/src/find_interaction/find_interaction_controller.dart b/flutter_inappwebview_android/lib/src/find_interaction/find_interaction_controller.dart new file mode 100644 index 00000000..943176a5 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/find_interaction/find_interaction_controller.dart @@ -0,0 +1,125 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidFindInteractionController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformFindInteractionControllerCreationParams] for +/// more information. +@immutable +class AndroidFindInteractionControllerCreationParams + extends PlatformFindInteractionControllerCreationParams { + /// Creates a new [AndroidFindInteractionControllerCreationParams] instance. + const AndroidFindInteractionControllerCreationParams( + {super.onFindResultReceived}); + + /// Creates a [AndroidFindInteractionControllerCreationParams] instance based on [PlatformFindInteractionControllerCreationParams]. + factory AndroidFindInteractionControllerCreationParams.fromPlatformFindInteractionControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformFindInteractionControllerCreationParams params) { + return AndroidFindInteractionControllerCreationParams( + onFindResultReceived: params.onFindResultReceived); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController} +class AndroidFindInteractionController extends PlatformFindInteractionController + with ChannelController { + /// Constructs a [AndroidFindInteractionController]. + AndroidFindInteractionController( + PlatformFindInteractionControllerCreationParams params) + : super.implementation( + params is AndroidFindInteractionControllerCreationParams + ? params + : AndroidFindInteractionControllerCreationParams + .fromPlatformFindInteractionControllerCreationParams(params), + ); + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + debugLoggingSettings: + PlatformFindInteractionController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + _debugLog(call.method, call.arguments); + + switch (call.method) { + case "onFindResultReceived": + if (onFindResultReceived != null) { + int activeMatchOrdinal = call.arguments["activeMatchOrdinal"]; + int numberOfMatches = call.arguments["numberOfMatches"]; + bool isDoneCounting = call.arguments["isDoneCounting"]; + onFindResultReceived!( + this, activeMatchOrdinal, numberOfMatches, isDoneCounting); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.findAll} + Future findAll({String? find}) async { + Map args = {}; + args.putIfAbsent('find', () => find); + await channel?.invokeMethod('findAll', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.findNext} + Future findNext({bool forward = true}) async { + Map args = {}; + args.putIfAbsent('forward', () => forward); + await channel?.invokeMethod('findNext', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.clearMatches} + Future clearMatches() async { + Map args = {}; + await channel?.invokeMethod('clearMatches', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.setSearchText} + Future setSearchText(String? searchText) async { + Map args = {}; + args.putIfAbsent('searchText', () => searchText); + await channel?.invokeMethod('setSearchText', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.getSearchText} + Future getSearchText() async { + Map args = {}; + return await channel?.invokeMethod('getSearchText', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.getActiveFindSession} + Future getActiveFindSession() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('getActiveFindSession', args)) + ?.cast(); + return FindSession.fromMap(result); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.dispose} + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: !isKeepAlive); + } +} + +extension InternalFindInteractionController + on AndroidFindInteractionController { + void init(dynamic id) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } +} diff --git a/flutter_inappwebview_android/lib/src/find_interaction/main.dart b/flutter_inappwebview_android/lib/src/find_interaction/main.dart new file mode 100644 index 00000000..a7adaacf --- /dev/null +++ b/flutter_inappwebview_android/lib/src/find_interaction/main.dart @@ -0,0 +1,2 @@ +export 'find_interaction_controller.dart' + hide InternalFindInteractionController; diff --git a/flutter_inappwebview_android/lib/src/http_auth_credentials_database.dart b/flutter_inappwebview_android/lib/src/http_auth_credentials_database.dart new file mode 100755 index 00000000..06ec37ac --- /dev/null +++ b/flutter_inappwebview_android/lib/src/http_auth_credentials_database.dart @@ -0,0 +1,155 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidHttpAuthCredentialDatabase]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformHttpAuthCredentialDatabaseCreationParams] for +/// more information. +@immutable +class AndroidHttpAuthCredentialDatabaseCreationParams + extends PlatformHttpAuthCredentialDatabaseCreationParams { + /// Creates a new [AndroidHttpAuthCredentialDatabaseCreationParams] instance. + const AndroidHttpAuthCredentialDatabaseCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformHttpAuthCredentialDatabaseCreationParams params, + ) : super(); + + /// Creates a [AndroidHttpAuthCredentialDatabaseCreationParams] instance based on [PlatformHttpAuthCredentialDatabaseCreationParams]. + factory AndroidHttpAuthCredentialDatabaseCreationParams.fromPlatformHttpAuthCredentialDatabaseCreationParams( + PlatformHttpAuthCredentialDatabaseCreationParams params) { + return AndroidHttpAuthCredentialDatabaseCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformHttpAuthCredentialDatabase} +class AndroidHttpAuthCredentialDatabase + extends PlatformHttpAuthCredentialDatabase with ChannelController { + /// Creates a new [AndroidHttpAuthCredentialDatabase]. + AndroidHttpAuthCredentialDatabase( + PlatformHttpAuthCredentialDatabaseCreationParams params) + : super.implementation( + params is AndroidHttpAuthCredentialDatabaseCreationParams + ? params + : AndroidHttpAuthCredentialDatabaseCreationParams + .fromPlatformHttpAuthCredentialDatabaseCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_credential_database'); + handler = handleMethod; + initMethodCallHandler(); + } + + static AndroidHttpAuthCredentialDatabase? _instance; + + ///Gets the database shared instance. + static AndroidHttpAuthCredentialDatabase instance() { + return (_instance != null) ? _instance! : _init(); + } + + static AndroidHttpAuthCredentialDatabase _init() { + _instance = AndroidHttpAuthCredentialDatabase( + AndroidHttpAuthCredentialDatabaseCreationParams( + const PlatformHttpAuthCredentialDatabaseCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future> + getAllAuthCredentials() async { + Map args = {}; + List allCredentials = + await channel?.invokeMethod('getAllAuthCredentials', args) ?? []; + + List result = []; + + for (Map map in allCredentials) { + var element = URLProtectionSpaceHttpAuthCredentials.fromMap( + map.cast()); + if (element != null) { + result.add(element); + } + } + return result; + } + + @override + Future> getHttpAuthCredentials( + {required URLProtectionSpace protectionSpace}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + List credentialList = + await channel?.invokeMethod('getHttpAuthCredentials', args) ?? []; + List credentials = []; + for (Map map in credentialList) { + var credential = URLCredential.fromMap(map.cast()); + if (credential != null) { + credentials.add(credential); + } + } + return credentials; + } + + @override + Future setHttpAuthCredential( + {required URLProtectionSpace protectionSpace, + required URLCredential credential}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + args.putIfAbsent("username", () => credential.username); + args.putIfAbsent("password", () => credential.password); + await channel?.invokeMethod('setHttpAuthCredential', args); + } + + @override + Future removeHttpAuthCredential( + {required URLProtectionSpace protectionSpace, + required URLCredential credential}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + args.putIfAbsent("username", () => credential.username); + args.putIfAbsent("password", () => credential.password); + await channel?.invokeMethod('removeHttpAuthCredential', args); + } + + @override + Future removeHttpAuthCredentials( + {required URLProtectionSpace protectionSpace}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + await channel?.invokeMethod('removeHttpAuthCredentials', args); + } + + @override + Future clearAllAuthCredentials() async { + Map args = {}; + await channel?.invokeMethod('clearAllAuthCredentials', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalHttpAuthCredentialDatabase + on AndroidHttpAuthCredentialDatabase { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_android/lib/src/in_app_browser/in_app_browser.dart b/flutter_inappwebview_android/lib/src/in_app_browser/in_app_browser.dart new file mode 100755 index 00000000..b625f5fa --- /dev/null +++ b/flutter_inappwebview_android/lib/src/in_app_browser/in_app_browser.dart @@ -0,0 +1,375 @@ +import 'dart:async'; +import 'dart:collection'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../find_interaction/find_interaction_controller.dart'; +import '../in_app_webview/in_app_webview_controller.dart'; +import '../pull_to_refresh/pull_to_refresh_controller.dart'; + +/// Object specifying creation parameters for creating a [AndroidInAppBrowser]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInAppBrowserCreationParams] for +/// more information. +class AndroidInAppBrowserCreationParams + extends PlatformInAppBrowserCreationParams { + /// Creates a new [AndroidInAppBrowserCreationParams] instance. + AndroidInAppBrowserCreationParams( + {super.contextMenu, + this.pullToRefreshController, + this.findInteractionController, + super.initialUserScripts, + super.windowId}); + + /// Creates a [AndroidInAppBrowserCreationParams] instance based on [PlatformInAppBrowserCreationParams]. + factory AndroidInAppBrowserCreationParams.fromPlatformInAppBrowserCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformInAppBrowserCreationParams params) { + return AndroidInAppBrowserCreationParams( + contextMenu: params.contextMenu, + pullToRefreshController: + params.pullToRefreshController as AndroidPullToRefreshController?, + findInteractionController: params.findInteractionController + as AndroidFindInteractionController?, + initialUserScripts: params.initialUserScripts, + windowId: params.windowId); + } + + @override + final AndroidFindInteractionController? findInteractionController; + + @override + final AndroidPullToRefreshController? pullToRefreshController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser} +class AndroidInAppBrowser extends PlatformInAppBrowser with ChannelController { + @override + final String id = IdGenerator.generate(); + + /// Constructs a [AndroidInAppBrowser]. + AndroidInAppBrowser(PlatformInAppBrowserCreationParams params) + : super.implementation( + params is AndroidInAppBrowserCreationParams + ? params + : AndroidInAppBrowserCreationParams + .fromPlatformInAppBrowserCreationParams(params), + ) { + _contextMenu = params.contextMenu; + } + + static final AndroidInAppBrowser _staticValue = + AndroidInAppBrowser(AndroidInAppBrowserCreationParams()); + + /// Provide static access. + factory AndroidInAppBrowser.static() { + return _staticValue; + } + + AndroidInAppBrowserCreationParams get _androidParams => + params as AndroidInAppBrowserCreationParams; + + static const MethodChannel _staticChannel = + const MethodChannel('com.pichillilorenzo/flutter_inappbrowser'); + + ContextMenu? _contextMenu; + + @override + ContextMenu? get contextMenu => _contextMenu; + + Map _menuItems = HashMap(); + bool _isOpened = false; + AndroidInAppWebViewController? _webViewController; + + @override + AndroidInAppWebViewController? get webViewController { + return _isOpened ? _webViewController : null; + } + + _init() { + channel = MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$id'); + handler = _handleMethod; + initMethodCallHandler(); + + _webViewController = AndroidInAppWebViewController.fromInAppBrowser( + AndroidInAppWebViewControllerCreationParams(id: id), + channel!, + this, + this.initialUserScripts); + _androidParams.pullToRefreshController?.init(id); + _androidParams.findInteractionController?.init(id); + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + id: id, + debugLoggingSettings: PlatformInAppBrowser.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onBrowserCreated": + _debugLog(call.method, call.arguments); + eventHandler?.onBrowserCreated(); + break; + case "onMenuItemClicked": + _debugLog(call.method, call.arguments); + int id = call.arguments["id"].toInt(); + if (this._menuItems[id] != null) { + if (this._menuItems[id]?.onClick != null) { + this._menuItems[id]?.onClick!(); + } + } + break; + case "onExit": + _debugLog(call.method, call.arguments); + _isOpened = false; + final onExit = eventHandler?.onExit; + dispose(); + onExit?.call(); + break; + default: + return _webViewController?.handleMethod(call); + } + } + + Map _prepareOpenRequest( + {@Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) { + assert(!_isOpened, 'The browser is already opened.'); + _isOpened = true; + _init(); + + var initialSettings = settings?.toMap() ?? + options?.toMap() ?? + InAppBrowserClassSettings().toMap(); + + Map pullToRefreshSettings = + pullToRefreshController?.settings.toMap() ?? + pullToRefreshController?.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + List> menuItemList = []; + _menuItems.forEach((key, value) { + menuItemList.add(value.toMap()); + }); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent('settings', () => initialSettings); + args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); + args.putIfAbsent('windowId', () => windowId); + args.putIfAbsent('initialUserScripts', + () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []); + args.putIfAbsent('pullToRefreshSettings', () => pullToRefreshSettings); + args.putIfAbsent('menuItems', () => menuItemList); + return args; + } + + @override + Future openUrlRequest( + {required URLRequest urlRequest, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); + + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openFile( + {required String assetFilePath, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + assert(assetFilePath.isNotEmpty); + + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('assetFilePath', () => assetFilePath); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openData( + {required String data, + String mimeType = "text/html", + String encoding = "utf8", + WebUri? baseUrl, + @Deprecated("Use historyUrl instead") Uri? androidHistoryUrl, + WebUri? historyUrl, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('data', () => data); + args.putIfAbsent('mimeType', () => mimeType); + args.putIfAbsent('encoding', () => encoding); + args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); + args.putIfAbsent('historyUrl', + () => (historyUrl ?? androidHistoryUrl)?.toString() ?? "about:blank"); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openWithSystemBrowser({required WebUri url}) async { + assert(url.toString().isNotEmpty); + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + return await _staticChannel.invokeMethod('openWithSystemBrowser', args); + } + + @override + void addMenuItem(InAppBrowserMenuItem menuItem) { + _menuItems[menuItem.id] = menuItem; + } + + @override + void addMenuItems(List menuItems) { + menuItems.forEach((menuItem) { + _menuItems[menuItem.id] = menuItem; + }); + } + + @override + bool removeMenuItem(InAppBrowserMenuItem menuItem) { + return _menuItems.remove(menuItem.id) != null; + } + + @override + void removeMenuItems(List menuItems) { + for (final menuItem in menuItems) { + removeMenuItem(menuItem); + } + } + + @override + void removeAllMenuItem() { + _menuItems.clear(); + } + + @override + bool hasMenuItem(InAppBrowserMenuItem menuItem) { + return _menuItems.containsKey(menuItem.id); + } + + @override + Future show() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('show', args); + } + + @override + Future hide() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('hide', args); + } + + @override + Future close() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('close', args); + } + + @override + Future isHidden() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + return await channel?.invokeMethod('isHidden', args) ?? false; + } + + @override + @Deprecated('Use setSettings instead') + Future setOptions({required InAppBrowserClassOptions options}) async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + args.putIfAbsent('settings', () => options.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + @Deprecated('Use getSettings instead') + Future getOptions() async { + assert(_isOpened, 'The browser is not opened.'); + Map args = {}; + + Map? options = + await channel?.invokeMethod('getSettings', args); + if (options != null) { + options = options.cast(); + return InAppBrowserClassOptions.fromMap(options as Map); + } + + return null; + } + + @override + Future setSettings( + {required InAppBrowserClassSettings settings}) async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + args.putIfAbsent('settings', () => settings.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + Future getSettings() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + + Map? settings = + await channel?.invokeMethod('getSettings', args); + if (settings != null) { + settings = settings.cast(); + return InAppBrowserClassSettings.fromMap( + settings as Map); + } + + return null; + } + + @override + bool isOpened() { + return this._isOpened; + } + + @override + @mustCallSuper + void dispose() { + disposeChannel(); + _webViewController?.dispose(); + _webViewController = null; + pullToRefreshController?.dispose(); + findInteractionController?.dispose(); + eventHandler = null; + } +} + +extension InternalInAppBrowser on AndroidInAppBrowser { + void setContextMenu(ContextMenu? contextMenu) { + _contextMenu = contextMenu; + } +} diff --git a/flutter_inappwebview_android/lib/src/in_app_browser/main.dart b/flutter_inappwebview_android/lib/src/in_app_browser/main.dart new file mode 100644 index 00000000..e11eb8b1 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/in_app_browser/main.dart @@ -0,0 +1 @@ +export 'in_app_browser.dart' hide InternalInAppBrowser; diff --git a/flutter_inappwebview_android/lib/src/in_app_webview/_static_channel.dart b/flutter_inappwebview_android/lib/src/in_app_webview/_static_channel.dart new file mode 100644 index 00000000..beb7de70 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/in_app_webview/_static_channel.dart @@ -0,0 +1,4 @@ +import 'package:flutter/services.dart'; + +const IN_APP_WEBVIEW_STATIC_CHANNEL = + MethodChannel('com.pichillilorenzo/flutter_inappwebview_manager'); diff --git a/flutter_inappwebview_android/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview_android/lib/src/in_app_webview/headless_in_app_webview.dart new file mode 100644 index 00000000..c53a36d5 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/in_app_webview/headless_in_app_webview.dart @@ -0,0 +1,440 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import '../find_interaction/find_interaction_controller.dart'; +import '../pull_to_refresh/pull_to_refresh_controller.dart'; +import 'in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [AndroidHeadlessInAppWebView]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformHeadlessInAppWebViewCreationParams] for +/// more information. +@immutable +class AndroidHeadlessInAppWebViewCreationParams + extends PlatformHeadlessInAppWebViewCreationParams { + /// Creates a new [AndroidHeadlessInAppWebViewCreationParams] instance. + AndroidHeadlessInAppWebViewCreationParams( + {super.controllerFromPlatform, + super.initialSize, + super.windowId, + super.onWebViewCreated, + super.onLoadStart, + super.onLoadStop, + @Deprecated('Use onReceivedError instead') super.onLoadError, + super.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError, + super.onReceivedHttpError, + super.onProgressChanged, + super.onConsoleMessage, + super.shouldOverrideUrlLoading, + super.onLoadResource, + super.onScrollChanged, + @Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart, + super.onDownloadStartRequest, + @Deprecated('Use onLoadResourceWithCustomScheme instead') + super.onLoadResourceCustomScheme, + super.onLoadResourceWithCustomScheme, + super.onCreateWindow, + super.onCloseWindow, + super.onJsAlert, + super.onJsConfirm, + super.onJsPrompt, + super.onReceivedHttpAuthRequest, + super.onReceivedServerTrustAuthRequest, + super.onReceivedClientCertRequest, + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + super.onFindResultReceived, + super.shouldInterceptAjaxRequest, + super.onAjaxReadyStateChange, + super.onAjaxProgress, + super.shouldInterceptFetchRequest, + super.onUpdateVisitedHistory, + @Deprecated("Use onPrintRequest instead") super.onPrint, + super.onPrintRequest, + super.onLongPressHitTestResult, + super.onEnterFullscreen, + super.onExitFullscreen, + super.onPageCommitVisible, + super.onTitleChanged, + super.onWindowFocus, + super.onWindowBlur, + super.onOverScrolled, + super.onZoomScaleChanged, + @Deprecated('Use onSafeBrowsingHit instead') + super.androidOnSafeBrowsingHit, + super.onSafeBrowsingHit, + @Deprecated('Use onPermissionRequest instead') + super.androidOnPermissionRequest, + super.onPermissionRequest, + @Deprecated('Use onGeolocationPermissionsShowPrompt instead') + super.androidOnGeolocationPermissionsShowPrompt, + super.onGeolocationPermissionsShowPrompt, + @Deprecated('Use onGeolocationPermissionsHidePrompt instead') + super.androidOnGeolocationPermissionsHidePrompt, + super.onGeolocationPermissionsHidePrompt, + @Deprecated('Use shouldInterceptRequest instead') + super.androidShouldInterceptRequest, + super.shouldInterceptRequest, + @Deprecated('Use onRenderProcessGone instead') + super.androidOnRenderProcessGone, + super.onRenderProcessGone, + @Deprecated('Use onRenderProcessResponsive instead') + super.androidOnRenderProcessResponsive, + super.onRenderProcessResponsive, + @Deprecated('Use onRenderProcessUnresponsive instead') + super.androidOnRenderProcessUnresponsive, + super.onRenderProcessUnresponsive, + @Deprecated('Use onFormResubmission instead') + super.androidOnFormResubmission, + super.onFormResubmission, + @Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged, + @Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon, + super.onReceivedIcon, + @Deprecated('Use onReceivedTouchIconUrl instead') + super.androidOnReceivedTouchIconUrl, + super.onReceivedTouchIconUrl, + @Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload, + super.onJsBeforeUnload, + @Deprecated('Use onReceivedLoginRequest instead') + super.androidOnReceivedLoginRequest, + super.onReceivedLoginRequest, + super.onPermissionRequestCanceled, + super.onRequestFocus, + @Deprecated('Use onWebContentProcessDidTerminate instead') + super.iosOnWebContentProcessDidTerminate, + super.onWebContentProcessDidTerminate, + @Deprecated( + 'Use onDidReceiveServerRedirectForProvisionalNavigation instead') + super.iosOnDidReceiveServerRedirectForProvisionalNavigation, + super.onDidReceiveServerRedirectForProvisionalNavigation, + @Deprecated('Use onNavigationResponse instead') + super.iosOnNavigationResponse, + super.onNavigationResponse, + @Deprecated('Use shouldAllowDeprecatedTLS instead') + super.iosShouldAllowDeprecatedTLS, + super.shouldAllowDeprecatedTLS, + super.onCameraCaptureStateChanged, + super.onMicrophoneCaptureStateChanged, + super.onContentSizeChanged, + super.initialUrlRequest, + super.initialFile, + super.initialData, + @Deprecated('Use initialSettings instead') super.initialOptions, + super.initialSettings, + super.contextMenu, + super.initialUserScripts, + this.pullToRefreshController, + this.findInteractionController}); + + /// Creates a [AndroidHeadlessInAppWebViewCreationParams] instance based on [PlatformHeadlessInAppWebViewCreationParams]. + AndroidHeadlessInAppWebViewCreationParams.fromPlatformHeadlessInAppWebViewCreationParams( + PlatformHeadlessInAppWebViewCreationParams params) + : this( + controllerFromPlatform: params.controllerFromPlatform, + initialSize: params.initialSize, + windowId: params.windowId, + onWebViewCreated: params.onWebViewCreated, + onLoadStart: params.onLoadStart, + onLoadStop: params.onLoadStop, + onLoadError: params.onLoadError, + onReceivedError: params.onReceivedError, + onLoadHttpError: params.onLoadHttpError, + onReceivedHttpError: params.onReceivedHttpError, + onProgressChanged: params.onProgressChanged, + onConsoleMessage: params.onConsoleMessage, + shouldOverrideUrlLoading: params.shouldOverrideUrlLoading, + onLoadResource: params.onLoadResource, + onScrollChanged: params.onScrollChanged, + onDownloadStart: params.onDownloadStart, + onDownloadStartRequest: params.onDownloadStartRequest, + onLoadResourceCustomScheme: params.onLoadResourceCustomScheme, + onLoadResourceWithCustomScheme: + params.onLoadResourceWithCustomScheme, + onCreateWindow: params.onCreateWindow, + onCloseWindow: params.onCloseWindow, + onJsAlert: params.onJsAlert, + onJsConfirm: params.onJsConfirm, + onJsPrompt: params.onJsPrompt, + onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest, + onReceivedServerTrustAuthRequest: + params.onReceivedServerTrustAuthRequest, + onReceivedClientCertRequest: params.onReceivedClientCertRequest, + onFindResultReceived: params.onFindResultReceived, + shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest, + onAjaxReadyStateChange: params.onAjaxReadyStateChange, + onAjaxProgress: params.onAjaxProgress, + shouldInterceptFetchRequest: params.shouldInterceptFetchRequest, + onUpdateVisitedHistory: params.onUpdateVisitedHistory, + onPrint: params.onPrint, + onPrintRequest: params.onPrintRequest, + onLongPressHitTestResult: params.onLongPressHitTestResult, + onEnterFullscreen: params.onEnterFullscreen, + onExitFullscreen: params.onExitFullscreen, + onPageCommitVisible: params.onPageCommitVisible, + onTitleChanged: params.onTitleChanged, + onWindowFocus: params.onWindowFocus, + onWindowBlur: params.onWindowBlur, + onOverScrolled: params.onOverScrolled, + onZoomScaleChanged: params.onZoomScaleChanged, + androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit, + onSafeBrowsingHit: params.onSafeBrowsingHit, + androidOnPermissionRequest: params.androidOnPermissionRequest, + onPermissionRequest: params.onPermissionRequest, + androidOnGeolocationPermissionsShowPrompt: + params.androidOnGeolocationPermissionsShowPrompt, + onGeolocationPermissionsShowPrompt: + params.onGeolocationPermissionsShowPrompt, + androidOnGeolocationPermissionsHidePrompt: + params.androidOnGeolocationPermissionsHidePrompt, + onGeolocationPermissionsHidePrompt: + params.onGeolocationPermissionsHidePrompt, + androidShouldInterceptRequest: params.androidShouldInterceptRequest, + shouldInterceptRequest: params.shouldInterceptRequest, + androidOnRenderProcessGone: params.androidOnRenderProcessGone, + onRenderProcessGone: params.onRenderProcessGone, + androidOnRenderProcessResponsive: + params.androidOnRenderProcessResponsive, + onRenderProcessResponsive: params.onRenderProcessResponsive, + androidOnRenderProcessUnresponsive: + params.androidOnRenderProcessUnresponsive, + onRenderProcessUnresponsive: params.onRenderProcessUnresponsive, + androidOnFormResubmission: params.androidOnFormResubmission, + onFormResubmission: params.onFormResubmission, + androidOnScaleChanged: params.androidOnScaleChanged, + androidOnReceivedIcon: params.androidOnReceivedIcon, + onReceivedIcon: params.onReceivedIcon, + androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl, + onReceivedTouchIconUrl: params.onReceivedTouchIconUrl, + androidOnJsBeforeUnload: params.androidOnJsBeforeUnload, + onJsBeforeUnload: params.onJsBeforeUnload, + androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest, + onReceivedLoginRequest: params.onReceivedLoginRequest, + onPermissionRequestCanceled: params.onPermissionRequestCanceled, + onRequestFocus: params.onRequestFocus, + iosOnWebContentProcessDidTerminate: + params.iosOnWebContentProcessDidTerminate, + onWebContentProcessDidTerminate: + params.onWebContentProcessDidTerminate, + iosOnDidReceiveServerRedirectForProvisionalNavigation: + params.iosOnDidReceiveServerRedirectForProvisionalNavigation, + onDidReceiveServerRedirectForProvisionalNavigation: + params.onDidReceiveServerRedirectForProvisionalNavigation, + iosOnNavigationResponse: params.iosOnNavigationResponse, + onNavigationResponse: params.onNavigationResponse, + iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS, + shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS, + onCameraCaptureStateChanged: params.onCameraCaptureStateChanged, + onMicrophoneCaptureStateChanged: + params.onMicrophoneCaptureStateChanged, + onContentSizeChanged: params.onContentSizeChanged, + initialUrlRequest: params.initialUrlRequest, + initialFile: params.initialFile, + initialData: params.initialData, + initialOptions: params.initialOptions, + initialSettings: params.initialSettings, + contextMenu: params.contextMenu, + initialUserScripts: params.initialUserScripts, + pullToRefreshController: params.pullToRefreshController + as AndroidPullToRefreshController?, + findInteractionController: params.findInteractionController + as AndroidFindInteractionController?); + + @override + final AndroidFindInteractionController? findInteractionController; + + @override + final AndroidPullToRefreshController? pullToRefreshController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView} +class AndroidHeadlessInAppWebView extends PlatformHeadlessInAppWebView + with ChannelController { + @override + late final String id; + + bool _started = false; + bool _running = false; + + static const MethodChannel _sharedChannel = + const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview'); + + AndroidInAppWebViewController? _webViewController; + + /// Constructs a [AndroidHeadlessInAppWebView]. + AndroidHeadlessInAppWebView(PlatformHeadlessInAppWebViewCreationParams params) + : super.implementation( + params is AndroidHeadlessInAppWebViewCreationParams + ? params + : AndroidHeadlessInAppWebViewCreationParams + .fromPlatformHeadlessInAppWebViewCreationParams(params), + ) { + id = IdGenerator.generate(); + } + + @override + AndroidInAppWebViewController? get webViewController => _webViewController; + + dynamic _controllerFromPlatform; + + AndroidHeadlessInAppWebViewCreationParams get _androidParams => + params as AndroidHeadlessInAppWebViewCreationParams; + + _init() { + _webViewController = AndroidInAppWebViewController( + AndroidInAppWebViewControllerCreationParams( + id: id, webviewParams: params), + ); + _controllerFromPlatform = + params.controllerFromPlatform?.call(_webViewController!) ?? + _webViewController!; + _androidParams.pullToRefreshController?.init(id); + _androidParams.findInteractionController?.init(id); + channel = + MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onWebViewCreated": + if (params.onWebViewCreated != null && _webViewController != null) { + params.onWebViewCreated!(_controllerFromPlatform); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + Future run() async { + if (_started) { + return; + } + _started = true; + _init(); + + final initialSettings = params.initialSettings ?? InAppWebViewSettings(); + _inferInitialSettings(initialSettings); + + Map settingsMap = + (params.initialSettings != null ? initialSettings.toMap() : null) ?? + params.initialOptions?.toMap() ?? + initialSettings.toMap(); + + Map pullToRefreshSettings = + _androidParams.pullToRefreshController?.params.settings.toMap() ?? + _androidParams.pullToRefreshController?.params.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent( + 'params', + () => { + 'initialUrlRequest': params.initialUrlRequest?.toMap(), + 'initialFile': params.initialFile, + 'initialData': params.initialData?.toMap(), + 'initialSettings': settingsMap, + 'contextMenu': params.contextMenu?.toMap() ?? {}, + 'windowId': params.windowId, + 'initialUserScripts': + params.initialUserScripts?.map((e) => e.toMap()).toList() ?? + [], + 'pullToRefreshSettings': pullToRefreshSettings, + 'initialSize': params.initialSize.toMap() + }); + await _sharedChannel.invokeMethod('run', args); + _running = true; + } + + void _inferInitialSettings(InAppWebViewSettings settings) { + if (params.shouldOverrideUrlLoading != null && + settings.useShouldOverrideUrlLoading == null) { + settings.useShouldOverrideUrlLoading = true; + } + if (params.onLoadResource != null && settings.useOnLoadResource == null) { + settings.useOnLoadResource = true; + } + if (params.onDownloadStartRequest != null && + settings.useOnDownloadStart == null) { + settings.useOnDownloadStart = true; + } + if (params.shouldInterceptAjaxRequest != null && + settings.useShouldInterceptAjaxRequest == null) { + settings.useShouldInterceptAjaxRequest = true; + } + if (params.shouldInterceptFetchRequest != null && + settings.useShouldInterceptFetchRequest == null) { + settings.useShouldInterceptFetchRequest = true; + } + if (params.shouldInterceptRequest != null && + settings.useShouldInterceptRequest == null) { + settings.useShouldInterceptRequest = true; + } + if (params.onRenderProcessGone != null && + settings.useOnRenderProcessGone == null) { + settings.useOnRenderProcessGone = true; + } + if (params.onNavigationResponse != null && + settings.useOnNavigationResponse == null) { + settings.useOnNavigationResponse = true; + } + } + + @override + bool isRunning() { + return _running; + } + + @override + Future setSize(Size size) async { + if (!_running) { + return; + } + + Map args = {}; + args.putIfAbsent('size', () => size.toMap()); + await channel?.invokeMethod('setSize', args); + } + + @override + Future getSize() async { + if (!_running) { + return null; + } + + Map args = {}; + Map sizeMap = + (await channel?.invokeMethod('getSize', args))?.cast(); + return MapSize.fromMap(sizeMap); + } + + @override + Future dispose() async { + if (!_running) { + return; + } + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + _started = false; + _running = false; + _webViewController?.dispose(); + _webViewController = null; + _controllerFromPlatform = null; + _androidParams.pullToRefreshController?.dispose(); + _androidParams.findInteractionController?.dispose(); + } +} + +extension InternalHeadlessInAppWebView on AndroidHeadlessInAppWebView { + Future internalDispose() async { + _started = false; + _running = false; + } +} diff --git a/flutter_inappwebview_android/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_android/lib/src/in_app_webview/in_app_webview.dart new file mode 100755 index 00000000..8b790a0a --- /dev/null +++ b/flutter_inappwebview_android/lib/src/in_app_webview/in_app_webview.dart @@ -0,0 +1,474 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'headless_in_app_webview.dart'; + +import '../find_interaction/find_interaction_controller.dart'; +import 'in_app_webview_controller.dart'; +import '../pull_to_refresh/main.dart'; +import '../pull_to_refresh/pull_to_refresh_controller.dart'; + +/// Object specifying creation parameters for creating a [PlatformInAppWebViewWidget]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +class AndroidInAppWebViewWidgetCreationParams + extends PlatformInAppWebViewWidgetCreationParams { + AndroidInAppWebViewWidgetCreationParams( + {super.controllerFromPlatform, + super.key, + super.layoutDirection, + super.gestureRecognizers, + super.headlessWebView, + super.keepAlive, + super.preventGestureDelay, + super.windowId, + super.onWebViewCreated, + super.onLoadStart, + super.onLoadStop, + @Deprecated('Use onReceivedError instead') super.onLoadError, + super.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError, + super.onReceivedHttpError, + super.onProgressChanged, + super.onConsoleMessage, + super.shouldOverrideUrlLoading, + super.onLoadResource, + super.onScrollChanged, + @Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart, + super.onDownloadStartRequest, + @Deprecated('Use onLoadResourceWithCustomScheme instead') + super.onLoadResourceCustomScheme, + super.onLoadResourceWithCustomScheme, + super.onCreateWindow, + super.onCloseWindow, + super.onJsAlert, + super.onJsConfirm, + super.onJsPrompt, + super.onReceivedHttpAuthRequest, + super.onReceivedServerTrustAuthRequest, + super.onReceivedClientCertRequest, + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + super.onFindResultReceived, + super.shouldInterceptAjaxRequest, + super.onAjaxReadyStateChange, + super.onAjaxProgress, + super.shouldInterceptFetchRequest, + super.onUpdateVisitedHistory, + @Deprecated("Use onPrintRequest instead") super.onPrint, + super.onPrintRequest, + super.onLongPressHitTestResult, + super.onEnterFullscreen, + super.onExitFullscreen, + super.onPageCommitVisible, + super.onTitleChanged, + super.onWindowFocus, + super.onWindowBlur, + super.onOverScrolled, + super.onZoomScaleChanged, + @Deprecated('Use onSafeBrowsingHit instead') + super.androidOnSafeBrowsingHit, + super.onSafeBrowsingHit, + @Deprecated('Use onPermissionRequest instead') + super.androidOnPermissionRequest, + super.onPermissionRequest, + @Deprecated('Use onGeolocationPermissionsShowPrompt instead') + super.androidOnGeolocationPermissionsShowPrompt, + super.onGeolocationPermissionsShowPrompt, + @Deprecated('Use onGeolocationPermissionsHidePrompt instead') + super.androidOnGeolocationPermissionsHidePrompt, + super.onGeolocationPermissionsHidePrompt, + @Deprecated('Use shouldInterceptRequest instead') + super.androidShouldInterceptRequest, + super.shouldInterceptRequest, + @Deprecated('Use onRenderProcessGone instead') + super.androidOnRenderProcessGone, + super.onRenderProcessGone, + @Deprecated('Use onRenderProcessResponsive instead') + super.androidOnRenderProcessResponsive, + super.onRenderProcessResponsive, + @Deprecated('Use onRenderProcessUnresponsive instead') + super.androidOnRenderProcessUnresponsive, + super.onRenderProcessUnresponsive, + @Deprecated('Use onFormResubmission instead') + super.androidOnFormResubmission, + super.onFormResubmission, + @Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged, + @Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon, + super.onReceivedIcon, + @Deprecated('Use onReceivedTouchIconUrl instead') + super.androidOnReceivedTouchIconUrl, + super.onReceivedTouchIconUrl, + @Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload, + super.onJsBeforeUnload, + @Deprecated('Use onReceivedLoginRequest instead') + super.androidOnReceivedLoginRequest, + super.onReceivedLoginRequest, + super.onPermissionRequestCanceled, + super.onRequestFocus, + @Deprecated('Use onWebContentProcessDidTerminate instead') + super.iosOnWebContentProcessDidTerminate, + super.onWebContentProcessDidTerminate, + @Deprecated( + 'Use onDidReceiveServerRedirectForProvisionalNavigation instead') + super.iosOnDidReceiveServerRedirectForProvisionalNavigation, + super.onDidReceiveServerRedirectForProvisionalNavigation, + @Deprecated('Use onNavigationResponse instead') + super.iosOnNavigationResponse, + super.onNavigationResponse, + @Deprecated('Use shouldAllowDeprecatedTLS instead') + super.iosShouldAllowDeprecatedTLS, + super.shouldAllowDeprecatedTLS, + super.onCameraCaptureStateChanged, + super.onMicrophoneCaptureStateChanged, + super.onContentSizeChanged, + super.initialUrlRequest, + super.initialFile, + super.initialData, + @Deprecated('Use initialSettings instead') super.initialOptions, + super.initialSettings, + super.contextMenu, + super.initialUserScripts, + this.pullToRefreshController, + this.findInteractionController}); + + /// Constructs a [AndroidInAppWebViewWidgetCreationParams] using a + /// [PlatformInAppWebViewWidgetCreationParams]. + AndroidInAppWebViewWidgetCreationParams.fromPlatformInAppWebViewWidgetCreationParams( + PlatformInAppWebViewWidgetCreationParams params) + : this( + controllerFromPlatform: params.controllerFromPlatform, + key: params.key, + layoutDirection: params.layoutDirection, + gestureRecognizers: params.gestureRecognizers, + headlessWebView: params.headlessWebView, + keepAlive: params.keepAlive, + preventGestureDelay: params.preventGestureDelay, + windowId: params.windowId, + onWebViewCreated: params.onWebViewCreated, + onLoadStart: params.onLoadStart, + onLoadStop: params.onLoadStop, + onLoadError: params.onLoadError, + onReceivedError: params.onReceivedError, + onLoadHttpError: params.onLoadHttpError, + onReceivedHttpError: params.onReceivedHttpError, + onProgressChanged: params.onProgressChanged, + onConsoleMessage: params.onConsoleMessage, + shouldOverrideUrlLoading: params.shouldOverrideUrlLoading, + onLoadResource: params.onLoadResource, + onScrollChanged: params.onScrollChanged, + onDownloadStart: params.onDownloadStart, + onDownloadStartRequest: params.onDownloadStartRequest, + onLoadResourceCustomScheme: params.onLoadResourceCustomScheme, + onLoadResourceWithCustomScheme: + params.onLoadResourceWithCustomScheme, + onCreateWindow: params.onCreateWindow, + onCloseWindow: params.onCloseWindow, + onJsAlert: params.onJsAlert, + onJsConfirm: params.onJsConfirm, + onJsPrompt: params.onJsPrompt, + onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest, + onReceivedServerTrustAuthRequest: + params.onReceivedServerTrustAuthRequest, + onReceivedClientCertRequest: params.onReceivedClientCertRequest, + onFindResultReceived: params.onFindResultReceived, + shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest, + onAjaxReadyStateChange: params.onAjaxReadyStateChange, + onAjaxProgress: params.onAjaxProgress, + shouldInterceptFetchRequest: params.shouldInterceptFetchRequest, + onUpdateVisitedHistory: params.onUpdateVisitedHistory, + onPrint: params.onPrint, + onPrintRequest: params.onPrintRequest, + onLongPressHitTestResult: params.onLongPressHitTestResult, + onEnterFullscreen: params.onEnterFullscreen, + onExitFullscreen: params.onExitFullscreen, + onPageCommitVisible: params.onPageCommitVisible, + onTitleChanged: params.onTitleChanged, + onWindowFocus: params.onWindowFocus, + onWindowBlur: params.onWindowBlur, + onOverScrolled: params.onOverScrolled, + onZoomScaleChanged: params.onZoomScaleChanged, + androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit, + onSafeBrowsingHit: params.onSafeBrowsingHit, + androidOnPermissionRequest: params.androidOnPermissionRequest, + onPermissionRequest: params.onPermissionRequest, + androidOnGeolocationPermissionsShowPrompt: + params.androidOnGeolocationPermissionsShowPrompt, + onGeolocationPermissionsShowPrompt: + params.onGeolocationPermissionsShowPrompt, + androidOnGeolocationPermissionsHidePrompt: + params.androidOnGeolocationPermissionsHidePrompt, + onGeolocationPermissionsHidePrompt: + params.onGeolocationPermissionsHidePrompt, + androidShouldInterceptRequest: params.androidShouldInterceptRequest, + shouldInterceptRequest: params.shouldInterceptRequest, + androidOnRenderProcessGone: params.androidOnRenderProcessGone, + onRenderProcessGone: params.onRenderProcessGone, + androidOnRenderProcessResponsive: + params.androidOnRenderProcessResponsive, + onRenderProcessResponsive: params.onRenderProcessResponsive, + androidOnRenderProcessUnresponsive: + params.androidOnRenderProcessUnresponsive, + onRenderProcessUnresponsive: params.onRenderProcessUnresponsive, + androidOnFormResubmission: params.androidOnFormResubmission, + onFormResubmission: params.onFormResubmission, + androidOnScaleChanged: params.androidOnScaleChanged, + androidOnReceivedIcon: params.androidOnReceivedIcon, + onReceivedIcon: params.onReceivedIcon, + androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl, + onReceivedTouchIconUrl: params.onReceivedTouchIconUrl, + androidOnJsBeforeUnload: params.androidOnJsBeforeUnload, + onJsBeforeUnload: params.onJsBeforeUnload, + androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest, + onReceivedLoginRequest: params.onReceivedLoginRequest, + onPermissionRequestCanceled: params.onPermissionRequestCanceled, + onRequestFocus: params.onRequestFocus, + iosOnWebContentProcessDidTerminate: + params.iosOnWebContentProcessDidTerminate, + onWebContentProcessDidTerminate: + params.onWebContentProcessDidTerminate, + iosOnDidReceiveServerRedirectForProvisionalNavigation: + params.iosOnDidReceiveServerRedirectForProvisionalNavigation, + onDidReceiveServerRedirectForProvisionalNavigation: + params.onDidReceiveServerRedirectForProvisionalNavigation, + iosOnNavigationResponse: params.iosOnNavigationResponse, + onNavigationResponse: params.onNavigationResponse, + iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS, + shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS, + onCameraCaptureStateChanged: params.onCameraCaptureStateChanged, + onMicrophoneCaptureStateChanged: + params.onMicrophoneCaptureStateChanged, + onContentSizeChanged: params.onContentSizeChanged, + initialUrlRequest: params.initialUrlRequest, + initialFile: params.initialFile, + initialData: params.initialData, + initialOptions: params.initialOptions, + initialSettings: params.initialSettings, + contextMenu: params.contextMenu, + initialUserScripts: params.initialUserScripts, + pullToRefreshController: params.pullToRefreshController + as AndroidPullToRefreshController?, + findInteractionController: params.findInteractionController + as AndroidFindInteractionController?); + + @override + final AndroidFindInteractionController? findInteractionController; + + @override + final AndroidPullToRefreshController? pullToRefreshController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} +class AndroidInAppWebViewWidget extends PlatformInAppWebViewWidget { + /// Constructs a [AndroidInAppWebViewWidget]. + /// + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} + AndroidInAppWebViewWidget(PlatformInAppWebViewWidgetCreationParams params) + : super.implementation( + params is AndroidInAppWebViewWidgetCreationParams + ? params + : AndroidInAppWebViewWidgetCreationParams + .fromPlatformInAppWebViewWidgetCreationParams(params), + ); + + AndroidInAppWebViewWidgetCreationParams get _androidParams => + params as AndroidInAppWebViewWidgetCreationParams; + + AndroidInAppWebViewController? _controller; + + AndroidHeadlessInAppWebView? get _androidHeadlessInAppWebView => + _androidParams.headlessWebView as AndroidHeadlessInAppWebView?; + + @override + Widget build(BuildContext context) { + final initialSettings = + _androidParams.initialSettings ?? InAppWebViewSettings(); + _inferInitialSettings(initialSettings); + + Map settingsMap = (_androidParams.initialSettings != null + ? initialSettings.toMap() + : null) ?? + // ignore: deprecated_member_use_from_same_package + _androidParams.initialOptions?.toMap() ?? + initialSettings.toMap(); + + Map pullToRefreshSettings = + _androidParams.pullToRefreshController?.params.settings.toMap() ?? + // ignore: deprecated_member_use_from_same_package + _androidParams.pullToRefreshController?.params.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + if ((_androidParams.headlessWebView?.isRunning() ?? false) && + _androidParams.keepAlive != null) { + final headlessId = _androidParams.headlessWebView?.id; + if (headlessId != null) { + // force keep alive id to match headless webview id + _androidParams.keepAlive?.id = headlessId; + } + } + + var useHybridComposition = (_androidParams.initialSettings != null + ? initialSettings.useHybridComposition + : _androidParams.initialOptions?.android.useHybridComposition) ?? + true; + + return PlatformViewLink( + key: _androidParams.key, + viewType: 'com.pichillilorenzo/flutter_inappwebview', + surfaceFactory: ( + BuildContext context, + PlatformViewController controller, + ) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + gestureRecognizers: _androidParams.gestureRecognizers ?? + const >{}, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + onCreatePlatformView: (PlatformViewCreationParams params) { + return _createAndroidViewController( + hybridComposition: useHybridComposition, + id: params.id, + viewType: 'com.pichillilorenzo/flutter_inappwebview', + layoutDirection: _androidParams.layoutDirection ?? + Directionality.maybeOf(context) ?? + TextDirection.rtl, + creationParams: { + 'initialUrlRequest': _androidParams.initialUrlRequest?.toMap(), + 'initialFile': _androidParams.initialFile, + 'initialData': _androidParams.initialData?.toMap(), + 'initialSettings': settingsMap, + 'contextMenu': _androidParams.contextMenu?.toMap() ?? {}, + 'windowId': _androidParams.windowId, + 'headlessWebViewId': + _androidParams.headlessWebView?.isRunning() ?? false + ? _androidParams.headlessWebView?.id + : null, + 'initialUserScripts': _androidParams.initialUserScripts + ?.map((e) => e.toMap()) + .toList() ?? + [], + 'pullToRefreshSettings': pullToRefreshSettings, + 'keepAliveId': _androidParams.keepAlive?.id + }, + ) + ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) + ..addOnPlatformViewCreatedListener((id) => _onPlatformViewCreated(id)) + ..create(); + }, + ); + } + + AndroidViewController _createAndroidViewController({ + required bool hybridComposition, + required int id, + required String viewType, + required TextDirection layoutDirection, + required Map creationParams, + }) { + if (hybridComposition) { + return PlatformViewsService.initExpensiveAndroidView( + id: id, + viewType: viewType, + layoutDirection: layoutDirection, + creationParams: creationParams, + creationParamsCodec: const StandardMessageCodec(), + ); + } + return PlatformViewsService.initSurfaceAndroidView( + id: id, + viewType: viewType, + layoutDirection: layoutDirection, + creationParams: creationParams, + creationParamsCodec: const StandardMessageCodec(), + ); + } + + void _onPlatformViewCreated(int id) { + dynamic viewId = id; + if (_androidParams.headlessWebView?.isRunning() ?? false) { + viewId = _androidParams.headlessWebView?.id; + } + viewId = _androidParams.keepAlive?.id ?? viewId ?? id; + _androidHeadlessInAppWebView?.internalDispose(); + _controller = AndroidInAppWebViewController( + PlatformInAppWebViewControllerCreationParams( + id: viewId, webviewParams: params)); + _androidParams.pullToRefreshController?.init(viewId); + _androidParams.findInteractionController?.init(viewId); + debugLog( + className: runtimeType.toString(), + id: viewId?.toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: "onWebViewCreated", + args: []); + if (_androidParams.onWebViewCreated != null) { + _androidParams.onWebViewCreated!( + params.controllerFromPlatform?.call(_controller!) ?? _controller!); + } + } + + void _inferInitialSettings(InAppWebViewSettings settings) { + if (_androidParams.shouldOverrideUrlLoading != null && + settings.useShouldOverrideUrlLoading == null) { + settings.useShouldOverrideUrlLoading = true; + } + if (_androidParams.onLoadResource != null && + settings.useOnLoadResource == null) { + settings.useOnLoadResource = true; + } + if (_androidParams.onDownloadStartRequest != null && + settings.useOnDownloadStart == null) { + settings.useOnDownloadStart = true; + } + if (_androidParams.shouldInterceptAjaxRequest != null && + settings.useShouldInterceptAjaxRequest == null) { + settings.useShouldInterceptAjaxRequest = true; + } + if (_androidParams.shouldInterceptFetchRequest != null && + settings.useShouldInterceptFetchRequest == null) { + settings.useShouldInterceptFetchRequest = true; + } + if (_androidParams.shouldInterceptRequest != null && + settings.useShouldInterceptRequest == null) { + settings.useShouldInterceptRequest = true; + } + if (_androidParams.onRenderProcessGone != null && + settings.useOnRenderProcessGone == null) { + settings.useOnRenderProcessGone = true; + } + if (_androidParams.onNavigationResponse != null && + settings.useOnNavigationResponse == null) { + settings.useOnNavigationResponse = true; + } + } + + @override + void dispose() { + dynamic viewId = _controller?.getViewId(); + debugLog( + className: runtimeType.toString(), + id: viewId?.toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: "dispose", + args: []); + final isKeepAlive = _androidParams.keepAlive != null; + _controller?.dispose(isKeepAlive: isKeepAlive); + _controller = null; + _androidParams.pullToRefreshController?.dispose(isKeepAlive: isKeepAlive); + _androidParams.findInteractionController?.dispose(isKeepAlive: isKeepAlive); + } + + @override + T controllerFromPlatform(PlatformInAppWebViewController controller) { + // unused + throw UnimplementedError(); + } +} diff --git a/flutter_inappwebview_android/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_android/lib/src/in_app_webview/in_app_webview_controller.dart new file mode 100644 index 00000000..deb5e076 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/in_app_webview/in_app_webview_controller.dart @@ -0,0 +1,2762 @@ +import 'dart:io'; +import 'dart:collection'; +import 'dart:convert'; +import 'dart:core'; +import 'dart:developer' as developer; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../web_message/main.dart'; + +import '../in_app_browser/in_app_browser.dart'; +import '../web_storage/web_storage.dart'; + +import 'headless_in_app_webview.dart'; +import '_static_channel.dart'; + +import '../print_job/main.dart'; + +///List of forbidden names for JavaScript handlers. +// ignore: non_constant_identifier_names +final _JAVASCRIPT_HANDLER_FORBIDDEN_NAMES = UnmodifiableListView([ + "onLoadResource", + "shouldInterceptAjaxRequest", + "onAjaxReadyStateChange", + "onAjaxProgress", + "shouldInterceptFetchRequest", + "onPrintRequest", + "onWindowFocus", + "onWindowBlur", + "callAsyncJavaScript", + "evaluateJavaScriptWithContentWorld" +]); + +/// Object specifying creation parameters for creating a [AndroidInAppWebViewController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInAppWebViewControllerCreationParams] for +/// more information. +@immutable +class AndroidInAppWebViewControllerCreationParams + extends PlatformInAppWebViewControllerCreationParams { + /// Creates a new [AndroidInAppWebViewControllerCreationParams] instance. + const AndroidInAppWebViewControllerCreationParams( + {required super.id, super.webviewParams}); + + /// Creates a [AndroidInAppWebViewControllerCreationParams] instance based on [PlatformInAppWebViewControllerCreationParams]. + factory AndroidInAppWebViewControllerCreationParams.fromPlatformInAppWebViewControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformInAppWebViewControllerCreationParams params) { + return AndroidInAppWebViewControllerCreationParams( + id: params.id, webviewParams: params.webviewParams); + } +} + +///Controls a WebView, such as an [InAppWebView] widget instance, a [AndroidHeadlessInAppWebView] instance or [AndroidInAppBrowser] WebView instance. +/// +///If you are using the [InAppWebView] widget, an [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated] +///callback. Instead, if you are using an [AndroidInAppBrowser] instance, you can get it through the [AndroidInAppBrowser.webViewController] attribute. +class AndroidInAppWebViewController extends PlatformInAppWebViewController + with ChannelController { + static final MethodChannel _staticChannel = IN_APP_WEBVIEW_STATIC_CHANNEL; + + // List of properties to be saved and restored for keep alive feature + Map _javaScriptHandlersMap = + HashMap(); + Map> _userScripts = { + UserScriptInjectionTime.AT_DOCUMENT_START: [], + UserScriptInjectionTime.AT_DOCUMENT_END: [] + }; + Set _webMessageListenerObjNames = Set(); + Map _injectedScriptsFromURL = {}; + Set _webMessageChannels = Set(); + Set _webMessageListeners = Set(); + + // static map that contains the properties to be saved and restored for keep alive feature + static final Map + _keepAliveMap = {}; + + AndroidInAppBrowser? _inAppBrowser; + + PlatformInAppBrowserEvents? get _inAppBrowserEventHandler => + _inAppBrowser?.eventHandler; + + dynamic _controllerFromPlatform; + + @override + late AndroidWebStorage webStorage; + + AndroidInAppWebViewController( + PlatformInAppWebViewControllerCreationParams params) + : super.implementation(params + is AndroidInAppWebViewControllerCreationParams + ? params + : AndroidInAppWebViewControllerCreationParams + .fromPlatformInAppWebViewControllerCreationParams(params)) { + channel = MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id'); + handler = handleMethod; + initMethodCallHandler(); + + final initialUserScripts = webviewParams?.initialUserScripts; + if (initialUserScripts != null) { + for (final userScript in initialUserScripts) { + if (userScript.injectionTime == + UserScriptInjectionTime.AT_DOCUMENT_START) { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] + ?.add(userScript); + } else { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] + ?.add(userScript); + } + } + } + + this._init(params); + } + + static final AndroidInAppWebViewController _staticValue = + AndroidInAppWebViewController( + AndroidInAppWebViewControllerCreationParams(id: null)); + + factory AndroidInAppWebViewController.static() { + return _staticValue; + } + + AndroidInAppWebViewController.fromInAppBrowser( + PlatformInAppWebViewControllerCreationParams params, + MethodChannel channel, + AndroidInAppBrowser inAppBrowser, + UnmodifiableListView? initialUserScripts) + : super.implementation( + params is AndroidInAppWebViewControllerCreationParams + ? params + : AndroidInAppWebViewControllerCreationParams + .fromPlatformInAppWebViewControllerCreationParams(params)) { + this.channel = channel; + this._inAppBrowser = inAppBrowser; + + if (initialUserScripts != null) { + for (final userScript in initialUserScripts) { + if (userScript.injectionTime == + UserScriptInjectionTime.AT_DOCUMENT_START) { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] + ?.add(userScript); + } else { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] + ?.add(userScript); + } + } + } + this._init(params); + } + + void _init(PlatformInAppWebViewControllerCreationParams params) { + _controllerFromPlatform = + params.webviewParams?.controllerFromPlatform?.call(this) ?? this; + + webStorage = AndroidWebStorage(AndroidWebStorageCreationParams( + localStorage: AndroidLocalStorage.defaultStorage(controller: this), + sessionStorage: + AndroidSessionStorage.defaultStorage(controller: this))); + + if (params.webviewParams is PlatformInAppWebViewWidgetCreationParams) { + final keepAlive = + (params.webviewParams as PlatformInAppWebViewWidgetCreationParams) + .keepAlive; + if (keepAlive != null) { + InAppWebViewControllerKeepAliveProps? props = _keepAliveMap[keepAlive]; + if (props == null) { + // save controller properties to restore it later + _keepAliveMap[keepAlive] = InAppWebViewControllerKeepAliveProps( + injectedScriptsFromURL: _injectedScriptsFromURL, + javaScriptHandlersMap: _javaScriptHandlersMap, + userScripts: _userScripts, + webMessageListenerObjNames: _webMessageListenerObjNames, + webMessageChannels: _webMessageChannels, + webMessageListeners: _webMessageListeners); + } else { + // restore controller properties + _injectedScriptsFromURL = props.injectedScriptsFromURL; + _javaScriptHandlersMap = props.javaScriptHandlersMap; + _userScripts = props.userScripts; + _webMessageListenerObjNames = props.webMessageListenerObjNames; + _webMessageChannels = + props.webMessageChannels as Set; + _webMessageListeners = + props.webMessageListeners as Set; + } + } + } + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + name: _inAppBrowser == null + ? "WebView" + : _inAppBrowser.runtimeType.toString(), + id: (getViewId() ?? _inAppBrowser?.id).toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + if (PlatformInAppWebViewController.debugLoggingSettings.enabled && + call.method != "onCallJsHandler") { + _debugLog(call.method, call.arguments); + } + + switch (call.method) { + case "onLoadStart": + _injectedScriptsFromURL.clear(); + if ((webviewParams != null && webviewParams!.onLoadStart != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && webviewParams!.onLoadStart != null) + webviewParams!.onLoadStart!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onLoadStart(uri); + } + break; + case "onLoadStop": + if ((webviewParams != null && webviewParams!.onLoadStop != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && webviewParams!.onLoadStop != null) + webviewParams!.onLoadStop!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onLoadStop(uri); + } + break; + case "onReceivedError": + if ((webviewParams != null && + (webviewParams!.onReceivedError != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadError != null)) || + _inAppBrowserEventHandler != null) { + WebResourceRequest request = WebResourceRequest.fromMap( + call.arguments["request"].cast())!; + WebResourceError error = WebResourceError.fromMap( + call.arguments["error"].cast())!; + var isForMainFrame = request.isForMainFrame ?? false; + + if (webviewParams != null) { + if (webviewParams!.onReceivedError != null) + webviewParams!.onReceivedError!( + _controllerFromPlatform, request, error); + else if (isForMainFrame) { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadError!(_controllerFromPlatform, request.url, + error.type.toNativeValue() ?? -1, error.description); + } + } else { + if (isForMainFrame) { + _inAppBrowserEventHandler!.onLoadError(request.url, + error.type.toNativeValue() ?? -1, error.description); + } + _inAppBrowserEventHandler!.onReceivedError(request, error); + } + } + break; + case "onReceivedHttpError": + if ((webviewParams != null && + (webviewParams!.onReceivedHttpError != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadHttpError != null)) || + _inAppBrowserEventHandler != null) { + WebResourceRequest request = WebResourceRequest.fromMap( + call.arguments["request"].cast())!; + WebResourceResponse errorResponse = WebResourceResponse.fromMap( + call.arguments["errorResponse"].cast())!; + var isForMainFrame = request.isForMainFrame ?? false; + + if (webviewParams != null) { + if (webviewParams!.onReceivedHttpError != null) + webviewParams!.onReceivedHttpError!( + _controllerFromPlatform, request, errorResponse); + else if (isForMainFrame) { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadHttpError!( + _controllerFromPlatform, + request.url, + errorResponse.statusCode ?? -1, + errorResponse.reasonPhrase ?? ''); + } + } else { + if (isForMainFrame) { + _inAppBrowserEventHandler!.onLoadHttpError( + request.url, + errorResponse.statusCode ?? -1, + errorResponse.reasonPhrase ?? ''); + } + _inAppBrowserEventHandler! + .onReceivedHttpError(request, errorResponse); + } + } + break; + case "onProgressChanged": + if ((webviewParams != null && + webviewParams!.onProgressChanged != null) || + _inAppBrowserEventHandler != null) { + int progress = call.arguments["progress"]; + if (webviewParams != null && webviewParams!.onProgressChanged != null) + webviewParams!.onProgressChanged!( + _controllerFromPlatform, progress); + else + _inAppBrowserEventHandler!.onProgressChanged(progress); + } + break; + case "shouldOverrideUrlLoading": + if ((webviewParams != null && + webviewParams!.shouldOverrideUrlLoading != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + NavigationAction navigationAction = + NavigationAction.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldOverrideUrlLoading != null) + return (await webviewParams!.shouldOverrideUrlLoading!( + _controllerFromPlatform, navigationAction)) + ?.toNativeValue(); + return (await _inAppBrowserEventHandler! + .shouldOverrideUrlLoading(navigationAction)) + ?.toNativeValue(); + } + break; + case "onConsoleMessage": + if ((webviewParams != null && + webviewParams!.onConsoleMessage != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ConsoleMessage consoleMessage = ConsoleMessage.fromMap(arguments)!; + if (webviewParams != null && webviewParams!.onConsoleMessage != null) + webviewParams!.onConsoleMessage!( + _controllerFromPlatform, consoleMessage); + else + _inAppBrowserEventHandler!.onConsoleMessage(consoleMessage); + } + break; + case "onScrollChanged": + if ((webviewParams != null && webviewParams!.onScrollChanged != null) || + _inAppBrowserEventHandler != null) { + int x = call.arguments["x"]; + int y = call.arguments["y"]; + if (webviewParams != null && webviewParams!.onScrollChanged != null) + webviewParams!.onScrollChanged!(_controllerFromPlatform, x, y); + else + _inAppBrowserEventHandler!.onScrollChanged(x, y); + } + break; + case "onDownloadStartRequest": + if ((webviewParams != null && + // ignore: deprecated_member_use_from_same_package + (webviewParams!.onDownloadStart != null || + webviewParams!.onDownloadStartRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + DownloadStartRequest downloadStartRequest = + DownloadStartRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onDownloadStartRequest != null) + webviewParams!.onDownloadStartRequest!( + _controllerFromPlatform, downloadStartRequest); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onDownloadStart!( + _controllerFromPlatform, downloadStartRequest.url); + } + } else { + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .onDownloadStart(downloadStartRequest.url); + _inAppBrowserEventHandler! + .onDownloadStartRequest(downloadStartRequest); + } + } + break; + case "onLoadResourceWithCustomScheme": + if ((webviewParams != null && + (webviewParams!.onLoadResourceWithCustomScheme != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadResourceCustomScheme != null)) || + _inAppBrowserEventHandler != null) { + Map requestMap = + call.arguments["request"].cast(); + WebResourceRequest request = WebResourceRequest.fromMap(requestMap)!; + + if (webviewParams != null) { + if (webviewParams!.onLoadResourceWithCustomScheme != null) + return (await webviewParams!.onLoadResourceWithCustomScheme!( + _controllerFromPlatform, request)) + ?.toMap(); + else { + return (await params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .onLoadResourceCustomScheme!( + _controllerFromPlatform, request.url)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onLoadResourceWithCustomScheme(request)) ?? + (await _inAppBrowserEventHandler! + .onLoadResourceCustomScheme(request.url))) + ?.toMap(); + } + } + break; + case "onCreateWindow": + if ((webviewParams != null && webviewParams!.onCreateWindow != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + CreateWindowAction createWindowAction = + CreateWindowAction.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onCreateWindow != null) + return await webviewParams!.onCreateWindow!( + _controllerFromPlatform, createWindowAction); + else + return await _inAppBrowserEventHandler! + .onCreateWindow(createWindowAction); + } + break; + case "onCloseWindow": + if (webviewParams != null && webviewParams!.onCloseWindow != null) + webviewParams!.onCloseWindow!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onCloseWindow(); + break; + case "onTitleChanged": + if ((webviewParams != null && webviewParams!.onTitleChanged != null) || + _inAppBrowserEventHandler != null) { + String? title = call.arguments["title"]; + if (webviewParams != null && webviewParams!.onTitleChanged != null) + webviewParams!.onTitleChanged!(_controllerFromPlatform, title); + else + _inAppBrowserEventHandler!.onTitleChanged(title); + } + break; + case "onGeolocationPermissionsShowPrompt": + if ((webviewParams != null && + (webviewParams!.onGeolocationPermissionsShowPrompt != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsShowPrompt != + null)) || + _inAppBrowserEventHandler != null) { + String origin = call.arguments["origin"]; + + if (webviewParams != null) { + if (webviewParams!.onGeolocationPermissionsShowPrompt != null) + return (await webviewParams!.onGeolocationPermissionsShowPrompt!( + _controllerFromPlatform, origin)) + ?.toMap(); + else { + return (await params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .androidOnGeolocationPermissionsShowPrompt!( + _controllerFromPlatform, origin)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onGeolocationPermissionsShowPrompt(origin)) ?? + (await _inAppBrowserEventHandler! + .androidOnGeolocationPermissionsShowPrompt(origin))) + ?.toMap(); + } + } + break; + case "onGeolocationPermissionsHidePrompt": + if (webviewParams != null && + (webviewParams!.onGeolocationPermissionsHidePrompt != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsHidePrompt != + null)) { + if (webviewParams!.onGeolocationPermissionsHidePrompt != null) + webviewParams! + .onGeolocationPermissionsHidePrompt!(_controllerFromPlatform); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsHidePrompt!( + _controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onGeolocationPermissionsHidePrompt(); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnGeolocationPermissionsHidePrompt(); + } + break; + case "shouldInterceptRequest": + if ((webviewParams != null && + (webviewParams!.shouldInterceptRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidShouldInterceptRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + WebResourceRequest request = WebResourceRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.shouldInterceptRequest != null) + return (await webviewParams!.shouldInterceptRequest!( + _controllerFromPlatform, request)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidShouldInterceptRequest!( + _controllerFromPlatform, request)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .shouldInterceptRequest(request)) ?? + (await _inAppBrowserEventHandler! + .androidShouldInterceptRequest(request))) + ?.toMap(); + } + } + break; + case "onRenderProcessUnresponsive": + if ((webviewParams != null && + (webviewParams!.onRenderProcessUnresponsive != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessUnresponsive != + null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessUnresponsive != null) + return (await webviewParams!.onRenderProcessUnresponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnRenderProcessUnresponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onRenderProcessUnresponsive(uri)) ?? + (await _inAppBrowserEventHandler! + .androidOnRenderProcessUnresponsive(uri))) + ?.toNativeValue(); + } + } + break; + case "onRenderProcessResponsive": + if ((webviewParams != null && + (webviewParams!.onRenderProcessResponsive != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessResponsive != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessResponsive != null) + return (await webviewParams!.onRenderProcessResponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnRenderProcessResponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onRenderProcessResponsive(uri)) ?? + (await _inAppBrowserEventHandler! + .androidOnRenderProcessResponsive(uri))) + ?.toNativeValue(); + } + } + break; + case "onRenderProcessGone": + if ((webviewParams != null && + (webviewParams!.onRenderProcessGone != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessGone != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + RenderProcessGoneDetail detail = + RenderProcessGoneDetail.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessGone != null) + webviewParams!.onRenderProcessGone!( + _controllerFromPlatform, detail); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessGone!( + _controllerFromPlatform, detail); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onRenderProcessGone(detail); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.androidOnRenderProcessGone(detail); + } + } + break; + case "onFormResubmission": + if ((webviewParams != null && + (webviewParams!.onFormResubmission != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnFormResubmission != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onFormResubmission != null) + return (await webviewParams!.onFormResubmission!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnFormResubmission!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onFormResubmission(uri)) ?? + // ignore: deprecated_member_use_from_same_package + (await _inAppBrowserEventHandler! + .androidOnFormResubmission(uri))) + ?.toNativeValue(); + } + } + break; + case "onZoomScaleChanged": + if ((webviewParams != null && + // ignore: deprecated_member_use_from_same_package + (webviewParams!.androidOnScaleChanged != null || + webviewParams!.onZoomScaleChanged != null)) || + _inAppBrowserEventHandler != null) { + double oldScale = call.arguments["oldScale"]; + double newScale = call.arguments["newScale"]; + + if (webviewParams != null) { + if (webviewParams!.onZoomScaleChanged != null) + webviewParams!.onZoomScaleChanged!( + _controllerFromPlatform, oldScale, newScale); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnScaleChanged!( + _controllerFromPlatform, oldScale, newScale); + } + } else { + _inAppBrowserEventHandler!.onZoomScaleChanged(oldScale, newScale); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnScaleChanged(oldScale, newScale); + } + } + break; + case "onReceivedIcon": + if ((webviewParams != null && + (webviewParams!.onReceivedIcon != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedIcon != null)) || + _inAppBrowserEventHandler != null) { + Uint8List icon = + Uint8List.fromList(call.arguments["icon"].cast()); + + if (webviewParams != null) { + if (webviewParams!.onReceivedIcon != null) + webviewParams!.onReceivedIcon!(_controllerFromPlatform, icon); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedIcon!( + _controllerFromPlatform, icon); + } + } else { + _inAppBrowserEventHandler!.onReceivedIcon(icon); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.androidOnReceivedIcon(icon); + } + } + break; + case "onReceivedTouchIconUrl": + if ((webviewParams != null && + (webviewParams!.onReceivedTouchIconUrl != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedTouchIconUrl != null)) || + _inAppBrowserEventHandler != null) { + String url = call.arguments["url"]; + bool precomposed = call.arguments["precomposed"]; + WebUri uri = WebUri(url); + + if (webviewParams != null) { + if (webviewParams!.onReceivedTouchIconUrl != null) + webviewParams!.onReceivedTouchIconUrl!( + _controllerFromPlatform, uri, precomposed); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedTouchIconUrl!( + _controllerFromPlatform, uri, precomposed); + } + } else { + _inAppBrowserEventHandler!.onReceivedTouchIconUrl(uri, precomposed); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnReceivedTouchIconUrl(uri, precomposed); + } + } + break; + case "onJsAlert": + if ((webviewParams != null && webviewParams!.onJsAlert != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsAlertRequest jsAlertRequest = JsAlertRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsAlert != null) + return (await webviewParams!.onJsAlert!( + _controllerFromPlatform, jsAlertRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler!.onJsAlert(jsAlertRequest)) + ?.toMap(); + } + break; + case "onJsConfirm": + if ((webviewParams != null && webviewParams!.onJsConfirm != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsConfirmRequest jsConfirmRequest = + JsConfirmRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsConfirm != null) + return (await webviewParams!.onJsConfirm!( + _controllerFromPlatform, jsConfirmRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onJsConfirm(jsConfirmRequest)) + ?.toMap(); + } + break; + case "onJsPrompt": + if ((webviewParams != null && webviewParams!.onJsPrompt != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsPromptRequest jsPromptRequest = JsPromptRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsPrompt != null) + return (await webviewParams!.onJsPrompt!( + _controllerFromPlatform, jsPromptRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onJsPrompt(jsPromptRequest)) + ?.toMap(); + } + break; + case "onJsBeforeUnload": + if ((webviewParams != null && + (webviewParams!.onJsBeforeUnload != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnJsBeforeUnload != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsBeforeUnloadRequest jsBeforeUnloadRequest = + JsBeforeUnloadRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onJsBeforeUnload != null) + return (await webviewParams!.onJsBeforeUnload!( + _controllerFromPlatform, jsBeforeUnloadRequest)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnJsBeforeUnload!( + _controllerFromPlatform, jsBeforeUnloadRequest)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onJsBeforeUnload(jsBeforeUnloadRequest)) ?? + (await _inAppBrowserEventHandler! + .androidOnJsBeforeUnload(jsBeforeUnloadRequest))) + ?.toMap(); + } + } + break; + case "onSafeBrowsingHit": + if ((webviewParams != null && + (webviewParams!.onSafeBrowsingHit != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnSafeBrowsingHit != null)) || + _inAppBrowserEventHandler != null) { + String url = call.arguments["url"]; + SafeBrowsingThreat? threatType = + SafeBrowsingThreat.fromNativeValue(call.arguments["threatType"]); + WebUri uri = WebUri(url); + + if (webviewParams != null) { + if (webviewParams!.onSafeBrowsingHit != null) + return (await webviewParams!.onSafeBrowsingHit!( + _controllerFromPlatform, uri, threatType)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnSafeBrowsingHit!( + _controllerFromPlatform, uri, threatType)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onSafeBrowsingHit(uri, threatType)) ?? + (await _inAppBrowserEventHandler! + .androidOnSafeBrowsingHit(uri, threatType))) + ?.toMap(); + } + } + break; + case "onReceivedLoginRequest": + if ((webviewParams != null && + (webviewParams!.onReceivedLoginRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedLoginRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + LoginRequest loginRequest = LoginRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onReceivedLoginRequest != null) + webviewParams!.onReceivedLoginRequest!( + _controllerFromPlatform, loginRequest); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedLoginRequest!( + _controllerFromPlatform, loginRequest); + } + } else { + _inAppBrowserEventHandler!.onReceivedLoginRequest(loginRequest); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnReceivedLoginRequest(loginRequest); + } + } + break; + case "onPermissionRequestCanceled": + if ((webviewParams != null && + webviewParams!.onPermissionRequestCanceled != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + PermissionRequest permissionRequest = + PermissionRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onPermissionRequestCanceled != null) + webviewParams!.onPermissionRequestCanceled!( + _controllerFromPlatform, permissionRequest); + else + _inAppBrowserEventHandler! + .onPermissionRequestCanceled(permissionRequest); + } + break; + case "onRequestFocus": + if ((webviewParams != null && webviewParams!.onRequestFocus != null) || + _inAppBrowserEventHandler != null) { + if (webviewParams != null && webviewParams!.onRequestFocus != null) + webviewParams!.onRequestFocus!(_controllerFromPlatform); + else + _inAppBrowserEventHandler!.onRequestFocus(); + } + break; + case "onReceivedHttpAuthRequest": + if ((webviewParams != null && + webviewParams!.onReceivedHttpAuthRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + HttpAuthenticationChallenge challenge = + HttpAuthenticationChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedHttpAuthRequest != null) + return (await webviewParams!.onReceivedHttpAuthRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedHttpAuthRequest(challenge)) + ?.toMap(); + } + break; + case "onReceivedServerTrustAuthRequest": + if ((webviewParams != null && + webviewParams!.onReceivedServerTrustAuthRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ServerTrustChallenge challenge = + ServerTrustChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedServerTrustAuthRequest != null) + return (await webviewParams!.onReceivedServerTrustAuthRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedServerTrustAuthRequest(challenge)) + ?.toMap(); + } + break; + case "onReceivedClientCertRequest": + if ((webviewParams != null && + webviewParams!.onReceivedClientCertRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ClientCertChallenge challenge = + ClientCertChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedClientCertRequest != null) + return (await webviewParams!.onReceivedClientCertRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedClientCertRequest(challenge)) + ?.toMap(); + } + break; + case "onFindResultReceived": + if ((webviewParams != null && + (webviewParams!.onFindResultReceived != null || + (webviewParams!.findInteractionController != null && + webviewParams!.findInteractionController!.params + .onFindResultReceived != + null))) || + _inAppBrowserEventHandler != null) { + int activeMatchOrdinal = call.arguments["activeMatchOrdinal"]; + int numberOfMatches = call.arguments["numberOfMatches"]; + bool isDoneCounting = call.arguments["isDoneCounting"]; + if (webviewParams != null) { + if (webviewParams!.findInteractionController != null && + webviewParams!.findInteractionController!.params + .onFindResultReceived != + null) + webviewParams! + .findInteractionController!.params.onFindResultReceived!( + webviewParams!.findInteractionController!, + activeMatchOrdinal, + numberOfMatches, + isDoneCounting); + else + webviewParams!.onFindResultReceived!(_controllerFromPlatform, + activeMatchOrdinal, numberOfMatches, isDoneCounting); + } else { + if (_inAppBrowser!.findInteractionController != null && + _inAppBrowser! + .findInteractionController!.onFindResultReceived != + null) + _inAppBrowser!.findInteractionController!.onFindResultReceived!( + webviewParams!.findInteractionController!, + activeMatchOrdinal, + numberOfMatches, + isDoneCounting); + else + _inAppBrowserEventHandler!.onFindResultReceived( + activeMatchOrdinal, numberOfMatches, isDoneCounting); + } + } + break; + case "onPermissionRequest": + if ((webviewParams != null && + (webviewParams!.onPermissionRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnPermissionRequest != null)) || + _inAppBrowserEventHandler != null) { + String origin = call.arguments["origin"]; + List resources = call.arguments["resources"].cast(); + + Map arguments = + call.arguments.cast(); + PermissionRequest permissionRequest = + PermissionRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onPermissionRequest != null) + return (await webviewParams!.onPermissionRequest!( + _controllerFromPlatform, permissionRequest)) + ?.toMap(); + else { + return (await webviewParams!.androidOnPermissionRequest!( + _controllerFromPlatform, origin, resources)) + ?.toMap(); + } + } else { + return (await _inAppBrowserEventHandler! + .onPermissionRequest(permissionRequest)) + ?.toMap() ?? + (await _inAppBrowserEventHandler! + .androidOnPermissionRequest(origin, resources)) + ?.toMap(); + } + } + break; + case "onUpdateVisitedHistory": + if ((webviewParams != null && + webviewParams!.onUpdateVisitedHistory != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + bool? isReload = call.arguments["isReload"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && + webviewParams!.onUpdateVisitedHistory != null) + webviewParams!.onUpdateVisitedHistory!( + _controllerFromPlatform, uri, isReload); + else + _inAppBrowserEventHandler!.onUpdateVisitedHistory(uri, isReload); + } + break; + case "onWebContentProcessDidTerminate": + if (webviewParams != null && + (webviewParams!.onWebContentProcessDidTerminate != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosOnWebContentProcessDidTerminate != null)) { + if (webviewParams!.onWebContentProcessDidTerminate != null) + webviewParams! + .onWebContentProcessDidTerminate!(_controllerFromPlatform); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams! + .iosOnWebContentProcessDidTerminate!(_controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onWebContentProcessDidTerminate(); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.iosOnWebContentProcessDidTerminate(); + } + break; + case "onPageCommitVisible": + if ((webviewParams != null && + webviewParams!.onPageCommitVisible != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && + webviewParams!.onPageCommitVisible != null) + webviewParams!.onPageCommitVisible!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onPageCommitVisible(uri); + } + break; + case "onDidReceiveServerRedirectForProvisionalNavigation": + if (webviewParams != null && + (webviewParams! + .onDidReceiveServerRedirectForProvisionalNavigation != + null || + params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .iosOnDidReceiveServerRedirectForProvisionalNavigation != + null)) { + if (webviewParams! + .onDidReceiveServerRedirectForProvisionalNavigation != + null) + webviewParams!.onDidReceiveServerRedirectForProvisionalNavigation!( + _controllerFromPlatform); + else { + params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .iosOnDidReceiveServerRedirectForProvisionalNavigation!( + _controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler! + .onDidReceiveServerRedirectForProvisionalNavigation(); + _inAppBrowserEventHandler! + .iosOnDidReceiveServerRedirectForProvisionalNavigation(); + } + break; + case "onNavigationResponse": + if ((webviewParams != null && + (webviewParams!.onNavigationResponse != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosOnNavigationResponse != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + // ignore: deprecated_member_use_from_same_package + IOSWKNavigationResponse iosOnNavigationResponse = + // ignore: deprecated_member_use_from_same_package + IOSWKNavigationResponse.fromMap(arguments)!; + + NavigationResponse navigationResponse = + NavigationResponse.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onNavigationResponse != null) + return (await webviewParams!.onNavigationResponse!( + _controllerFromPlatform, navigationResponse)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.iosOnNavigationResponse!( + _controllerFromPlatform, iosOnNavigationResponse)) + ?.toNativeValue(); + } + } else { + return (await _inAppBrowserEventHandler! + .onNavigationResponse(navigationResponse)) + ?.toNativeValue() ?? + (await _inAppBrowserEventHandler! + .iosOnNavigationResponse(iosOnNavigationResponse)) + ?.toNativeValue(); + } + } + break; + case "shouldAllowDeprecatedTLS": + if ((webviewParams != null && + (webviewParams!.shouldAllowDeprecatedTLS != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosShouldAllowDeprecatedTLS != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + URLAuthenticationChallenge challenge = + URLAuthenticationChallenge.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.shouldAllowDeprecatedTLS != null) + return (await webviewParams!.shouldAllowDeprecatedTLS!( + _controllerFromPlatform, challenge)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.iosShouldAllowDeprecatedTLS!( + _controllerFromPlatform, challenge)) + ?.toNativeValue(); + } + } else { + return (await _inAppBrowserEventHandler! + .shouldAllowDeprecatedTLS(challenge)) + ?.toNativeValue() ?? + // ignore: deprecated_member_use_from_same_package + (await _inAppBrowserEventHandler! + .iosShouldAllowDeprecatedTLS(challenge)) + ?.toNativeValue(); + } + } + break; + case "onLongPressHitTestResult": + if ((webviewParams != null && + webviewParams!.onLongPressHitTestResult != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + InAppWebViewHitTestResult hitTestResult = + InAppWebViewHitTestResult.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onLongPressHitTestResult != null) + webviewParams!.onLongPressHitTestResult!( + _controllerFromPlatform, hitTestResult); + else + _inAppBrowserEventHandler!.onLongPressHitTestResult(hitTestResult); + } + break; + case "onCreateContextMenu": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null && contextMenu.onCreateContextMenu != null) { + Map arguments = + call.arguments.cast(); + InAppWebViewHitTestResult hitTestResult = + InAppWebViewHitTestResult.fromMap(arguments)!; + + contextMenu.onCreateContextMenu!(hitTestResult); + } + break; + case "onHideContextMenu": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null && contextMenu.onHideContextMenu != null) { + contextMenu.onHideContextMenu!(); + } + break; + case "onContextMenuActionItemClicked": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null) { + int? androidId = call.arguments["androidId"]; + String? iosId = call.arguments["iosId"]; + dynamic id = call.arguments["id"]; + String title = call.arguments["title"]; + + ContextMenuItem menuItemClicked = ContextMenuItem( + id: id, + // ignore: deprecated_member_use_from_same_package + androidId: androidId, + // ignore: deprecated_member_use_from_same_package + iosId: iosId, + title: title, + action: null); + + for (var menuItem in contextMenu.menuItems) { + if (menuItem.id == id) { + menuItemClicked = menuItem; + if (menuItem.action != null) { + menuItem.action!(); + } + break; + } + } + + if (contextMenu.onContextMenuActionItemClicked != null) { + contextMenu.onContextMenuActionItemClicked!(menuItemClicked); + } + } + break; + case "onEnterFullscreen": + if (webviewParams != null && webviewParams!.onEnterFullscreen != null) + webviewParams!.onEnterFullscreen!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onEnterFullscreen(); + break; + case "onExitFullscreen": + if (webviewParams != null && webviewParams!.onExitFullscreen != null) + webviewParams!.onExitFullscreen!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onExitFullscreen(); + break; + case "onOverScrolled": + if ((webviewParams != null && webviewParams!.onOverScrolled != null) || + _inAppBrowserEventHandler != null) { + int x = call.arguments["x"]; + int y = call.arguments["y"]; + bool clampedX = call.arguments["clampedX"]; + bool clampedY = call.arguments["clampedY"]; + + if (webviewParams != null && webviewParams!.onOverScrolled != null) + webviewParams!.onOverScrolled!( + _controllerFromPlatform, x, y, clampedX, clampedY); + else + _inAppBrowserEventHandler!.onOverScrolled(x, y, clampedX, clampedY); + } + break; + case "onWindowFocus": + if (webviewParams != null && webviewParams!.onWindowFocus != null) + webviewParams!.onWindowFocus!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowFocus(); + break; + case "onWindowBlur": + if (webviewParams != null && webviewParams!.onWindowBlur != null) + webviewParams!.onWindowBlur!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowBlur(); + break; + case "onPrintRequest": + if ((webviewParams != null && + (webviewParams!.onPrintRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onPrint != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + String? printJobId = call.arguments["printJobId"]; + WebUri? uri = url != null ? WebUri(url) : null; + AndroidPrintJobController? printJob = printJobId != null + ? AndroidPrintJobController( + AndroidPrintJobControllerCreationParams(id: printJobId)) + : null; + + if (webviewParams != null) { + if (webviewParams!.onPrintRequest != null) + return await webviewParams!.onPrintRequest!( + _controllerFromPlatform, uri, printJob); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onPrint!(_controllerFromPlatform, uri); + return false; + } + } else { + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.onPrint(uri); + return await _inAppBrowserEventHandler! + .onPrintRequest(uri, printJob); + } + } + break; + case "onInjectedScriptLoaded": + String id = call.arguments[0]; + var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onLoadCallback != null) { + onLoadCallback(); + } + break; + case "onInjectedScriptError": + String id = call.arguments[0]; + var onErrorCallback = _injectedScriptsFromURL[id]?.onError; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onErrorCallback != null) { + onErrorCallback(); + } + break; + case "onCameraCaptureStateChanged": + if ((webviewParams != null && + webviewParams!.onCameraCaptureStateChanged != null) || + _inAppBrowserEventHandler != null) { + var oldState = + MediaCaptureState.fromNativeValue(call.arguments["oldState"]); + var newState = + MediaCaptureState.fromNativeValue(call.arguments["newState"]); + + if (webviewParams != null && + webviewParams!.onCameraCaptureStateChanged != null) + webviewParams!.onCameraCaptureStateChanged!( + _controllerFromPlatform, oldState, newState); + else + _inAppBrowserEventHandler! + .onCameraCaptureStateChanged(oldState, newState); + } + break; + case "onMicrophoneCaptureStateChanged": + if ((webviewParams != null && + webviewParams!.onMicrophoneCaptureStateChanged != null) || + _inAppBrowserEventHandler != null) { + var oldState = + MediaCaptureState.fromNativeValue(call.arguments["oldState"]); + var newState = + MediaCaptureState.fromNativeValue(call.arguments["newState"]); + + if (webviewParams != null && + webviewParams!.onMicrophoneCaptureStateChanged != null) + webviewParams!.onMicrophoneCaptureStateChanged!( + _controllerFromPlatform, oldState, newState); + else + _inAppBrowserEventHandler! + .onMicrophoneCaptureStateChanged(oldState, newState); + } + break; + case "onContentSizeChanged": + if ((webviewParams != null && + webviewParams!.onContentSizeChanged != null) || + _inAppBrowserEventHandler != null) { + var oldContentSize = MapSize.fromMap( + call.arguments["oldContentSize"]?.cast())!; + var newContentSize = MapSize.fromMap( + call.arguments["newContentSize"]?.cast())!; + + if (webviewParams != null && + webviewParams!.onContentSizeChanged != null) + webviewParams!.onContentSizeChanged!( + _controllerFromPlatform, oldContentSize, newContentSize); + else + _inAppBrowserEventHandler! + .onContentSizeChanged(oldContentSize, newContentSize); + } + break; + case "onCallJsHandler": + String handlerName = call.arguments["handlerName"]; + // decode args to json + List args = jsonDecode(call.arguments["args"]); + + _debugLog(handlerName, args); + + switch (handlerName) { + case "onLoadResource": + if ((webviewParams != null && + webviewParams!.onLoadResource != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + arguments["startTime"] = arguments["startTime"] is int + ? arguments["startTime"].toDouble() + : arguments["startTime"]; + arguments["duration"] = arguments["duration"] is int + ? arguments["duration"].toDouble() + : arguments["duration"]; + + var response = LoadedResource.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onLoadResource != null) + webviewParams!.onLoadResource!( + _controllerFromPlatform, response); + else + _inAppBrowserEventHandler!.onLoadResource(response); + } + return null; + case "shouldInterceptAjaxRequest": + if ((webviewParams != null && + webviewParams!.shouldInterceptAjaxRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldInterceptAjaxRequest != null) + return jsonEncode( + await params.webviewParams!.shouldInterceptAjaxRequest!( + _controllerFromPlatform, request)); + else + return jsonEncode(await _inAppBrowserEventHandler! + .shouldInterceptAjaxRequest(request)); + } + return null; + case "onAjaxReadyStateChange": + if ((webviewParams != null && + webviewParams!.onAjaxReadyStateChange != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onAjaxReadyStateChange != null) + return (await webviewParams!.onAjaxReadyStateChange!( + _controllerFromPlatform, request)) + ?.toNativeValue(); + else + return (await _inAppBrowserEventHandler! + .onAjaxReadyStateChange(request)) + ?.toNativeValue(); + } + return null; + case "onAjaxProgress": + if ((webviewParams != null && + webviewParams!.onAjaxProgress != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onAjaxProgress != null) + return (await webviewParams!.onAjaxProgress!( + _controllerFromPlatform, request)) + ?.toNativeValue(); + else + return (await _inAppBrowserEventHandler! + .onAjaxProgress(request)) + ?.toNativeValue(); + } + return null; + case "shouldInterceptFetchRequest": + if ((webviewParams != null && + webviewParams!.shouldInterceptFetchRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + FetchRequest request = FetchRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldInterceptFetchRequest != null) + return jsonEncode( + await webviewParams!.shouldInterceptFetchRequest!( + _controllerFromPlatform, request)); + else + return jsonEncode(await _inAppBrowserEventHandler! + .shouldInterceptFetchRequest(request)); + } + return null; + case "onWindowFocus": + if (webviewParams != null && webviewParams!.onWindowFocus != null) + webviewParams!.onWindowFocus!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowFocus(); + return null; + case "onWindowBlur": + if (webviewParams != null && webviewParams!.onWindowBlur != null) + webviewParams!.onWindowBlur!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowBlur(); + return null; + case "onInjectedScriptLoaded": + String id = args[0]; + var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onLoadCallback != null) { + onLoadCallback(); + } + return null; + case "onInjectedScriptError": + String id = args[0]; + var onErrorCallback = _injectedScriptsFromURL[id]?.onError; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onErrorCallback != null) { + onErrorCallback(); + } + return null; + } + + if (_javaScriptHandlersMap.containsKey(handlerName)) { + // convert result to json + try { + return jsonEncode(await _javaScriptHandlersMap[handlerName]!(args)); + } catch (error, stacktrace) { + developer.log(error.toString() + '\n' + stacktrace.toString(), + name: 'JavaScript Handler "$handlerName"'); + throw Exception(error.toString().replaceFirst('Exception: ', '')); + } + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + Future getUrl() async { + Map args = {}; + String? url = await channel?.invokeMethod('getUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + Future getTitle() async { + Map args = {}; + return await channel?.invokeMethod('getTitle', args); + } + + @override + Future getProgress() async { + Map args = {}; + return await channel?.invokeMethod('getProgress', args); + } + + @override + Future getHtml() async { + String? html; + + InAppWebViewSettings? settings = await getSettings(); + if (settings != null && settings.javaScriptEnabled == true) { + html = await evaluateJavascript( + source: "window.document.getElementsByTagName('html')[0].outerHTML;"); + if (html != null && html.isNotEmpty) return html; + } + + var webviewUrl = await getUrl(); + if (webviewUrl == null) { + return html; + } + + if (webviewUrl.isScheme("file")) { + var assetPathSplitted = webviewUrl.toString().split("/flutter_assets/"); + var assetPath = assetPathSplitted[assetPathSplitted.length - 1]; + try { + var bytes = await rootBundle.load(assetPath); + html = utf8.decode(bytes.buffer.asUint8List()); + } catch (e) {} + } else { + try { + HttpClient client = HttpClient(); + var htmlRequest = await client.getUrl(webviewUrl); + html = + await (await htmlRequest.close()).transform(Utf8Decoder()).join(); + } catch (e) { + developer.log(e.toString(), name: this.runtimeType.toString()); + } + } + + return html; + } + + @override + Future> getFavicons() async { + List favicons = []; + + var webviewUrl = await getUrl(); + + if (webviewUrl == null) { + return favicons; + } + + String? manifestUrl; + + var html = await getHtml(); + if (html == null || html.isEmpty) { + return favicons; + } + var assetPathBase; + + if (webviewUrl.isScheme("file")) { + var assetPathSplitted = webviewUrl.toString().split("/flutter_assets/"); + assetPathBase = assetPathSplitted[0] + "/flutter_assets/"; + } + + InAppWebViewSettings? settings = await getSettings(); + if (settings != null && settings.javaScriptEnabled == true) { + List> links = (await evaluateJavascript(source: """ +(function() { + var linkNodes = document.head.getElementsByTagName("link"); + var links = []; + for (var i = 0; i < linkNodes.length; i++) { + var linkNode = linkNodes[i]; + if (linkNode.rel === 'manifest') { + links.push( + { + rel: linkNode.rel, + href: linkNode.href, + sizes: null + } + ); + } else if (linkNode.rel != null && linkNode.rel.indexOf('icon') >= 0) { + links.push( + { + rel: linkNode.rel, + href: linkNode.href, + sizes: linkNode.sizes != null && linkNode.sizes.value != "" ? linkNode.sizes.value : null + } + ); + } + } + return links; +})(); +"""))?.cast>() ?? []; + for (var link in links) { + if (link["rel"] == "manifest") { + manifestUrl = link["href"]; + if (!_isUrlAbsolute(manifestUrl!)) { + if (manifestUrl.startsWith("/")) { + manifestUrl = manifestUrl.substring(1); + } + manifestUrl = ((assetPathBase == null) + ? webviewUrl.scheme + "://" + webviewUrl.host + "/" + : assetPathBase) + + manifestUrl; + } + continue; + } + favicons.addAll(_createFavicons(webviewUrl, assetPathBase, link["href"], + link["rel"], link["sizes"], false)); + } + } + + // try to get /favicon.ico + try { + HttpClient client = HttpClient(); + var faviconUrl = + webviewUrl.scheme + "://" + webviewUrl.host + "/favicon.ico"; + var faviconUri = WebUri(faviconUrl); + var headRequest = await client.headUrl(faviconUri); + var headResponse = await headRequest.close(); + if (headResponse.statusCode == 200) { + favicons.add(Favicon(url: faviconUri, rel: "shortcut icon")); + } + } catch (e) { + developer.log("/favicon.ico file not found: " + e.toString(), + name: this.runtimeType.toString()); + } + + // try to get the manifest file + HttpClientRequest? manifestRequest; + HttpClientResponse? manifestResponse; + bool manifestFound = false; + if (manifestUrl == null) { + manifestUrl = + webviewUrl.scheme + "://" + webviewUrl.host + "/manifest.json"; + } + try { + HttpClient client = HttpClient(); + manifestRequest = await client.getUrl(Uri.parse(manifestUrl)); + manifestResponse = await manifestRequest.close(); + manifestFound = manifestResponse.statusCode == 200 && + manifestResponse.headers.contentType?.mimeType == "application/json"; + } catch (e) { + developer.log("Manifest file not found: " + e.toString(), + name: this.runtimeType.toString()); + } + + if (manifestFound) { + try { + Map manifest = json + .decode(await manifestResponse!.transform(Utf8Decoder()).join()); + if (manifest.containsKey("icons")) { + for (Map icon in manifest["icons"]) { + favicons.addAll(_createFavicons(webviewUrl, assetPathBase, + icon["src"], icon["rel"], icon["sizes"], true)); + } + } + } on FormatException catch (_) { + /// The [manifestResponse] might not has a valid JSON string, catch and + /// ignore the error + } + } + + return favicons; + } + + bool _isUrlAbsolute(String url) { + return url.startsWith("http://") || url.startsWith("https://"); + } + + List _createFavicons(WebUri url, String? assetPathBase, + String urlIcon, String? rel, String? sizes, bool isManifest) { + List favicons = []; + + List urlSplitted = urlIcon.split("/"); + if (!_isUrlAbsolute(urlIcon)) { + if (urlIcon.startsWith("/")) { + urlIcon = urlIcon.substring(1); + } + urlIcon = ((assetPathBase == null) + ? url.scheme + "://" + url.host + "/" + : assetPathBase) + + urlIcon; + } + if (isManifest) { + rel = (sizes != null) + ? urlSplitted[urlSplitted.length - 1] + .replaceFirst("-" + sizes, "") + .split(" ")[0] + .split(".")[0] + : null; + } + if (sizes != null && sizes.isNotEmpty && sizes != "any") { + List sizesSplitted = sizes.split(" "); + for (String size in sizesSplitted) { + int width = int.parse(size.split("x")[0]); + int height = int.parse(size.split("x")[1]); + favicons.add(Favicon( + url: WebUri(urlIcon), rel: rel, width: width, height: height)); + } + } else { + favicons.add( + Favicon(url: WebUri(urlIcon), rel: rel, width: null, height: null)); + } + + return favicons; + } + + @override + Future loadUrl( + {required URLRequest urlRequest, + @Deprecated('Use allowingReadAccessTo instead') + Uri? iosAllowingReadAccessTo, + WebUri? allowingReadAccessTo}) async { + assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); + assert( + allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); + assert(iosAllowingReadAccessTo == null || + iosAllowingReadAccessTo.isScheme("file")); + + Map args = {}; + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + args.putIfAbsent( + 'allowingReadAccessTo', + () => + allowingReadAccessTo?.toString() ?? + iosAllowingReadAccessTo?.toString()); + await channel?.invokeMethod('loadUrl', args); + } + + @override + Future postUrl( + {required WebUri url, required Uint8List postData}) async { + assert(url.toString().isNotEmpty); + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('postData', () => postData); + await channel?.invokeMethod('postUrl', args); + } + + @override + Future loadData( + {required String data, + String mimeType = "text/html", + String encoding = "utf8", + WebUri? baseUrl, + @Deprecated('Use historyUrl instead') Uri? androidHistoryUrl, + WebUri? historyUrl, + @Deprecated('Use allowingReadAccessTo instead') + Uri? iosAllowingReadAccessTo, + WebUri? allowingReadAccessTo}) async { + assert( + allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); + assert(iosAllowingReadAccessTo == null || + iosAllowingReadAccessTo.isScheme("file")); + + Map args = {}; + args.putIfAbsent('data', () => data); + args.putIfAbsent('mimeType', () => mimeType); + args.putIfAbsent('encoding', () => encoding); + args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); + args.putIfAbsent( + 'historyUrl', + () => + historyUrl?.toString() ?? + androidHistoryUrl?.toString() ?? + "about:blank"); + args.putIfAbsent( + 'allowingReadAccessTo', + () => + allowingReadAccessTo?.toString() ?? + iosAllowingReadAccessTo?.toString()); + await channel?.invokeMethod('loadData', args); + } + + @override + Future loadFile({required String assetFilePath}) async { + assert(assetFilePath.isNotEmpty); + Map args = {}; + args.putIfAbsent('assetFilePath', () => assetFilePath); + await channel?.invokeMethod('loadFile', args); + } + + @override + Future reload() async { + Map args = {}; + await channel?.invokeMethod('reload', args); + } + + @override + Future goBack() async { + Map args = {}; + await channel?.invokeMethod('goBack', args); + } + + @override + Future canGoBack() async { + Map args = {}; + return await channel?.invokeMethod('canGoBack', args) ?? false; + } + + @override + Future goForward() async { + Map args = {}; + await channel?.invokeMethod('goForward', args); + } + + @override + Future canGoForward() async { + Map args = {}; + return await channel?.invokeMethod('canGoForward', args) ?? false; + } + + @override + Future goBackOrForward({required int steps}) async { + Map args = {}; + args.putIfAbsent('steps', () => steps); + await channel?.invokeMethod('goBackOrForward', args); + } + + @override + Future canGoBackOrForward({required int steps}) async { + Map args = {}; + args.putIfAbsent('steps', () => steps); + return await channel?.invokeMethod('canGoBackOrForward', args) ?? + false; + } + + @override + Future goTo({required WebHistoryItem historyItem}) async { + var steps = historyItem.offset; + if (steps != null) { + await goBackOrForward(steps: steps); + } + } + + @override + Future isLoading() async { + Map args = {}; + return await channel?.invokeMethod('isLoading', args) ?? false; + } + + @override + Future stopLoading() async { + Map args = {}; + await channel?.invokeMethod('stopLoading', args); + } + + @override + Future evaluateJavascript( + {required String source, ContentWorld? contentWorld}) async { + Map args = {}; + args.putIfAbsent('source', () => source); + args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); + var data = await channel?.invokeMethod('evaluateJavascript', args); + if (data != null) { + try { + // try to json decode the data coming from JavaScript + // otherwise return it as it is. + data = json.decode(data); + } catch (e) {} + } + return data; + } + + @override + Future injectJavascriptFileFromUrl( + {required WebUri urlFile, + ScriptHtmlTagAttributes? scriptHtmlTagAttributes}) async { + assert(urlFile.toString().isNotEmpty); + var id = scriptHtmlTagAttributes?.id; + if (scriptHtmlTagAttributes != null && id != null) { + _injectedScriptsFromURL[id] = scriptHtmlTagAttributes; + } + Map args = {}; + args.putIfAbsent('urlFile', () => urlFile.toString()); + args.putIfAbsent( + 'scriptHtmlTagAttributes', () => scriptHtmlTagAttributes?.toMap()); + await channel?.invokeMethod('injectJavascriptFileFromUrl', args); + } + + @override + Future injectJavascriptFileFromAsset( + {required String assetFilePath}) async { + String source = await rootBundle.loadString(assetFilePath); + return await evaluateJavascript(source: source); + } + + @override + Future injectCSSCode({required String source}) async { + Map args = {}; + args.putIfAbsent('source', () => source); + await channel?.invokeMethod('injectCSSCode', args); + } + + @override + Future injectCSSFileFromUrl( + {required WebUri urlFile, + CSSLinkHtmlTagAttributes? cssLinkHtmlTagAttributes}) async { + assert(urlFile.toString().isNotEmpty); + Map args = {}; + args.putIfAbsent('urlFile', () => urlFile.toString()); + args.putIfAbsent( + 'cssLinkHtmlTagAttributes', () => cssLinkHtmlTagAttributes?.toMap()); + await channel?.invokeMethod('injectCSSFileFromUrl', args); + } + + @override + Future injectCSSFileFromAsset({required String assetFilePath}) async { + String source = await rootBundle.loadString(assetFilePath); + await injectCSSCode(source: source); + } + + @override + void addJavaScriptHandler( + {required String handlerName, + required JavaScriptHandlerCallback callback}) { + assert(!_JAVASCRIPT_HANDLER_FORBIDDEN_NAMES.contains(handlerName), + '"$handlerName" is a forbidden name!'); + this._javaScriptHandlersMap[handlerName] = (callback); + } + + @override + JavaScriptHandlerCallback? removeJavaScriptHandler( + {required String handlerName}) { + return this._javaScriptHandlersMap.remove(handlerName); + } + + @override + bool hasJavaScriptHandler({required String handlerName}) { + return this._javaScriptHandlersMap.containsKey(handlerName); + } + + @override + Future takeScreenshot( + {ScreenshotConfiguration? screenshotConfiguration}) async { + Map args = {}; + args.putIfAbsent( + 'screenshotConfiguration', () => screenshotConfiguration?.toMap()); + return await channel?.invokeMethod('takeScreenshot', args); + } + + @override + @Deprecated('Use setSettings instead') + Future setOptions({required InAppWebViewGroupOptions options}) async { + InAppWebViewSettings settings = + InAppWebViewSettings.fromMap(options.toMap()) ?? InAppWebViewSettings(); + await setSettings(settings: settings); + } + + @override + @Deprecated('Use getSettings instead') + Future getOptions() async { + InAppWebViewSettings? settings = await getSettings(); + + Map? options = settings?.toMap(); + if (options != null) { + options = options.cast(); + return InAppWebViewGroupOptions.fromMap(options as Map); + } + + return null; + } + + @override + Future setSettings({required InAppWebViewSettings settings}) async { + Map args = {}; + + args.putIfAbsent('settings', () => settings.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + Future getSettings() async { + Map args = {}; + + Map? settings = + await channel?.invokeMethod('getSettings', args); + if (settings != null) { + settings = settings.cast(); + return InAppWebViewSettings.fromMap(settings as Map); + } + + return null; + } + + @override + Future getCopyBackForwardList() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('getCopyBackForwardList', args)) + ?.cast(); + return WebHistory.fromMap(result); + } + + @override + Future clearCache() async { + Map args = {}; + await channel?.invokeMethod('clearCache', args); + } + + @override + @Deprecated("Use FindInteractionController.findAll instead") + Future findAllAsync({required String find}) async { + Map args = {}; + args.putIfAbsent('find', () => find); + await channel?.invokeMethod('findAll', args); + } + + @override + @Deprecated("Use FindInteractionController.findNext instead") + Future findNext({required bool forward}) async { + Map args = {}; + args.putIfAbsent('forward', () => forward); + await channel?.invokeMethod('findNext', args); + } + + @override + @Deprecated("Use FindInteractionController.clearMatches instead") + Future clearMatches() async { + Map args = {}; + await channel?.invokeMethod('clearMatches', args); + } + + @override + @Deprecated("Use tRexRunnerHtml instead") + Future getTRexRunnerHtml() async { + return await tRexRunnerHtml; + } + + @override + @Deprecated("Use tRexRunnerCss instead") + Future getTRexRunnerCss() async { + return await tRexRunnerCss; + } + + @override + Future scrollTo( + {required int x, required int y, bool animated = false}) async { + Map args = {}; + args.putIfAbsent('x', () => x); + args.putIfAbsent('y', () => y); + args.putIfAbsent('animated', () => animated); + await channel?.invokeMethod('scrollTo', args); + } + + @override + Future scrollBy( + {required int x, required int y, bool animated = false}) async { + Map args = {}; + args.putIfAbsent('x', () => x); + args.putIfAbsent('y', () => y); + args.putIfAbsent('animated', () => animated); + await channel?.invokeMethod('scrollBy', args); + } + + @override + Future pauseTimers() async { + Map args = {}; + await channel?.invokeMethod('pauseTimers', args); + } + + @override + Future resumeTimers() async { + Map args = {}; + await channel?.invokeMethod('resumeTimers', args); + } + + @override + Future printCurrentPage( + {PrintJobSettings? settings}) async { + Map args = {}; + args.putIfAbsent("settings", () => settings?.toMap()); + String? jobId = + await channel?.invokeMethod('printCurrentPage', args); + if (jobId != null) { + return AndroidPrintJobController( + PlatformPrintJobControllerCreationParams(id: jobId)); + } + return null; + } + + @override + Future getContentHeight() async { + Map args = {}; + var height = await channel?.invokeMethod('getContentHeight', args); + if (height == null || height == 0) { + // try to use javascript + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollHeight;"); + if (scrollHeight != null && scrollHeight is num) { + height = scrollHeight.toInt(); + } + } + return height; + } + + @override + Future getContentWidth() async { + Map args = {}; + var height = await channel?.invokeMethod('getContentWidth', args); + if (height == null || height == 0) { + // try to use javascript + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollWidth;"); + if (scrollHeight != null && scrollHeight is num) { + height = scrollHeight.toInt(); + } + } + return height; + } + + @override + Future zoomBy( + {required double zoomFactor, + @Deprecated('Use animated instead') bool? iosAnimated, + bool animated = false}) async { + assert(zoomFactor > 0.01 && zoomFactor <= 100.0); + + Map args = {}; + args.putIfAbsent('zoomFactor', () => zoomFactor); + args.putIfAbsent('animated', () => iosAnimated ?? animated); + return await channel?.invokeMethod('zoomBy', args); + } + + @override + Future getOriginalUrl() async { + Map args = {}; + String? url = await channel?.invokeMethod('getOriginalUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + Future getZoomScale() async { + Map args = {}; + return await channel?.invokeMethod('getZoomScale', args); + } + + @override + @Deprecated('Use getZoomScale instead') + Future getScale() async { + return await getZoomScale(); + } + + @override + Future getSelectedText() async { + Map args = {}; + return await channel?.invokeMethod('getSelectedText', args); + } + + @override + Future getHitTestResult() async { + Map args = {}; + Map? hitTestResultMap = + await channel?.invokeMethod('getHitTestResult', args); + + if (hitTestResultMap == null) { + return null; + } + + hitTestResultMap = hitTestResultMap.cast(); + + InAppWebViewHitTestResultType? type = + InAppWebViewHitTestResultType.fromNativeValue( + hitTestResultMap["type"]?.toInt()); + String? extra = hitTestResultMap["extra"]; + return InAppWebViewHitTestResult(type: type, extra: extra); + } + + @override + Future clearFocus() async { + Map args = {}; + return await channel?.invokeMethod('clearFocus', args); + } + + @override + Future setContextMenu(ContextMenu? contextMenu) async { + Map args = {}; + args.putIfAbsent("contextMenu", () => contextMenu?.toMap()); + await channel?.invokeMethod('setContextMenu', args); + _inAppBrowser?.setContextMenu(contextMenu); + } + + @override + Future requestFocusNodeHref() async { + Map args = {}; + Map? result = + await channel?.invokeMethod('requestFocusNodeHref', args); + return result != null + ? RequestFocusNodeHrefResult( + url: result['url'] != null ? WebUri(result['url']) : null, + title: result['title'], + src: result['src'], + ) + : null; + } + + @override + Future requestImageRef() async { + Map args = {}; + Map? result = + await channel?.invokeMethod('requestImageRef', args); + return result != null + ? RequestImageRefResult( + url: result['url'] != null ? WebUri(result['url']) : null, + ) + : null; + } + + @override + Future> getMetaTags() async { + List metaTags = []; + + List>? metaTagList = + (await evaluateJavascript(source: """ +(function() { + var metaTags = []; + var metaTagNodes = document.head.getElementsByTagName('meta'); + for (var i = 0; i < metaTagNodes.length; i++) { + var metaTagNode = metaTagNodes[i]; + + var otherAttributes = metaTagNode.getAttributeNames(); + var nameIndex = otherAttributes.indexOf("name"); + if (nameIndex !== -1) otherAttributes.splice(nameIndex, 1); + var contentIndex = otherAttributes.indexOf("content"); + if (contentIndex !== -1) otherAttributes.splice(contentIndex, 1); + + var attrs = []; + for (var j = 0; j < otherAttributes.length; j++) { + var otherAttribute = otherAttributes[j]; + attrs.push( + { + name: otherAttribute, + value: metaTagNode.getAttribute(otherAttribute) + } + ); + } + + metaTags.push( + { + name: metaTagNode.name, + content: metaTagNode.content, + attrs: attrs + } + ); + } + return metaTags; +})(); + """))?.cast>(); + + if (metaTagList == null) { + return metaTags; + } + + for (var metaTag in metaTagList) { + var attrs = []; + + for (var metaTagAttr in metaTag["attrs"]) { + attrs.add(MetaTagAttribute( + name: metaTagAttr["name"], value: metaTagAttr["value"])); + } + + metaTags.add(MetaTag( + name: metaTag["name"], content: metaTag["content"], attrs: attrs)); + } + + return metaTags; + } + + @override + Future getMetaThemeColor() async { + Color? themeColor; + + try { + Map args = {}; + themeColor = UtilColor.fromStringRepresentation( + await channel?.invokeMethod('getMetaThemeColor', args)); + return themeColor; + } catch (e) { + // not implemented + } + + // try using javascript + var metaTags = await getMetaTags(); + MetaTag? metaTagThemeColor; + + for (var metaTag in metaTags) { + if (metaTag.name == "theme-color") { + metaTagThemeColor = metaTag; + break; + } + } + + if (metaTagThemeColor == null) { + return null; + } + + var colorValue = metaTagThemeColor.content; + + themeColor = colorValue != null + ? UtilColor.fromStringRepresentation(colorValue) + : null; + + return themeColor; + } + + @override + Future getScrollX() async { + Map args = {}; + return await channel?.invokeMethod('getScrollX', args); + } + + @override + Future getScrollY() async { + Map args = {}; + return await channel?.invokeMethod('getScrollY', args); + } + + @override + Future getCertificate() async { + Map args = {}; + Map? sslCertificateMap = + (await channel?.invokeMethod('getCertificate', args)) + ?.cast(); + return SslCertificate.fromMap(sslCertificateMap); + } + + @override + Future addUserScript({required UserScript userScript}) async { + Map args = {}; + args.putIfAbsent('userScript', () => userScript.toMap()); + if (!(_userScripts[userScript.injectionTime]?.contains(userScript) ?? + false)) { + _userScripts[userScript.injectionTime]?.add(userScript); + await channel?.invokeMethod('addUserScript', args); + } + } + + @override + Future addUserScripts({required List userScripts}) async { + for (var i = 0; i < userScripts.length; i++) { + await addUserScript(userScript: userScripts[i]); + } + } + + @override + Future removeUserScript({required UserScript userScript}) async { + var index = _userScripts[userScript.injectionTime]?.indexOf(userScript); + if (index == null || index == -1) { + return false; + } + + _userScripts[userScript.injectionTime]?.remove(userScript); + Map args = {}; + args.putIfAbsent('userScript', () => userScript.toMap()); + args.putIfAbsent('index', () => index); + await channel?.invokeMethod('removeUserScript', args); + + return true; + } + + @override + Future removeUserScriptsByGroupName({required String groupName}) async { + final List userScriptsAtDocumentStart = List.from( + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] ?? []); + for (final userScript in userScriptsAtDocumentStart) { + if (userScript.groupName == groupName) { + _userScripts[userScript.injectionTime]?.remove(userScript); + } + } + + final List userScriptsAtDocumentEnd = + List.from(_userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] ?? []); + for (final userScript in userScriptsAtDocumentEnd) { + if (userScript.groupName == groupName) { + _userScripts[userScript.injectionTime]?.remove(userScript); + } + } + + Map args = {}; + args.putIfAbsent('groupName', () => groupName); + await channel?.invokeMethod('removeUserScriptsByGroupName', args); + } + + @override + Future removeUserScripts( + {required List userScripts}) async { + for (final userScript in userScripts) { + await removeUserScript(userScript: userScript); + } + } + + @override + Future removeAllUserScripts() async { + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_START]?.clear(); + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_END]?.clear(); + + Map args = {}; + await channel?.invokeMethod('removeAllUserScripts', args); + } + + @override + bool hasUserScript({required UserScript userScript}) { + return _userScripts[userScript.injectionTime]?.contains(userScript) ?? + false; + } + + @override + Future callAsyncJavaScript( + {required String functionBody, + Map arguments = const {}, + ContentWorld? contentWorld}) async { + Map args = {}; + args.putIfAbsent('functionBody', () => functionBody); + args.putIfAbsent('arguments', () => arguments); + args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); + var data = await channel?.invokeMethod('callAsyncJavaScript', args); + if (data == null) { + return null; + } + data = json.decode(data); + return CallAsyncJavaScriptResult( + value: data["value"], error: data["error"]); + } + + @override + Future saveWebArchive( + {required String filePath, bool autoname = false}) async { + if (!autoname) { + assert(filePath.endsWith("." + WebArchiveFormat.MHT.toNativeValue())); + } + + Map args = {}; + args.putIfAbsent("filePath", () => filePath); + args.putIfAbsent("autoname", () => autoname); + return await channel?.invokeMethod('saveWebArchive', args); + } + + @override + Future isSecureContext() async { + Map args = {}; + return await channel?.invokeMethod('isSecureContext', args) ?? false; + } + + @override + Future createWebMessageChannel() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('createWebMessageChannel', args)) + ?.cast(); + final webMessageChannel = AndroidWebMessageChannel.static().fromMap(result); + if (webMessageChannel != null) { + _webMessageChannels.add(webMessageChannel); + } + return webMessageChannel; + } + + @override + Future postWebMessage( + {required WebMessage message, WebUri? targetOrigin}) async { + if (targetOrigin == null) { + targetOrigin = WebUri(''); + } + Map args = {}; + args.putIfAbsent('message', () => message.toMap()); + args.putIfAbsent('targetOrigin', () => targetOrigin.toString()); + await channel?.invokeMethod('postWebMessage', args); + } + + @override + Future addWebMessageListener( + PlatformWebMessageListener webMessageListener) async { + assert(!_webMessageListeners.contains(webMessageListener), + "${webMessageListener} was already added."); + assert( + !_webMessageListenerObjNames + .contains(webMessageListener.params.jsObjectName), + "jsObjectName ${webMessageListener.params.jsObjectName} was already added."); + _webMessageListeners.add(webMessageListener as AndroidWebMessageListener); + _webMessageListenerObjNames.add(webMessageListener.params.jsObjectName); + + Map args = {}; + args.putIfAbsent('webMessageListener', () => webMessageListener.toMap()); + await channel?.invokeMethod('addWebMessageListener', args); + } + + @override + bool hasWebMessageListener(PlatformWebMessageListener webMessageListener) { + return _webMessageListeners.contains(webMessageListener) || + _webMessageListenerObjNames + .contains(webMessageListener.params.jsObjectName); + } + + @override + Future canScrollVertically() async { + Map args = {}; + return await channel?.invokeMethod('canScrollVertically', args) ?? + false; + } + + @override + Future canScrollHorizontally() async { + Map args = {}; + return await channel?.invokeMethod('canScrollHorizontally', args) ?? + false; + } + + @override + Future startSafeBrowsing() async { + Map args = {}; + return await channel?.invokeMethod('startSafeBrowsing', args) ?? + false; + } + + @override + Future clearSslPreferences() async { + Map args = {}; + await channel?.invokeMethod('clearSslPreferences', args); + } + + @override + Future pause() async { + Map args = {}; + await channel?.invokeMethod('pause', args); + } + + @override + Future resume() async { + Map args = {}; + await channel?.invokeMethod('resume', args); + } + + @override + Future pageDown({required bool bottom}) async { + Map args = {}; + args.putIfAbsent("bottom", () => bottom); + return await channel?.invokeMethod('pageDown', args) ?? false; + } + + @override + Future pageUp({required bool top}) async { + Map args = {}; + args.putIfAbsent("top", () => top); + return await channel?.invokeMethod('pageUp', args) ?? false; + } + + @override + Future zoomIn() async { + Map args = {}; + return await channel?.invokeMethod('zoomIn', args) ?? false; + } + + @override + Future zoomOut() async { + Map args = {}; + return await channel?.invokeMethod('zoomOut', args) ?? false; + } + + @override + Future clearHistory() async { + Map args = {}; + return await channel?.invokeMethod('clearHistory', args); + } + + @override + Future isInFullscreen() async { + Map args = {}; + return await channel?.invokeMethod('isInFullscreen', args) ?? false; + } + + @override + Future getDefaultUserAgent() async { + Map args = {}; + return await _staticChannel.invokeMethod( + 'getDefaultUserAgent', args) ?? + ''; + } + + @override + Future clearClientCertPreferences() async { + Map args = {}; + await _staticChannel.invokeMethod('clearClientCertPreferences', args); + } + + @override + Future getSafeBrowsingPrivacyPolicyUrl() async { + Map args = {}; + String? url = await _staticChannel.invokeMethod( + 'getSafeBrowsingPrivacyPolicyUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + @Deprecated("Use setSafeBrowsingAllowlist instead") + Future setSafeBrowsingWhitelist({required List hosts}) async { + return await setSafeBrowsingAllowlist(hosts: hosts); + } + + @override + Future setSafeBrowsingAllowlist({required List hosts}) async { + Map args = {}; + args.putIfAbsent('hosts', () => hosts); + return await _staticChannel.invokeMethod( + 'setSafeBrowsingAllowlist', args) ?? + false; + } + + @override + Future getCurrentWebViewPackage() async { + Map args = {}; + Map? packageInfo = + (await _staticChannel.invokeMethod('getCurrentWebViewPackage', args)) + ?.cast(); + return WebViewPackageInfo.fromMap(packageInfo); + } + + @override + Future setWebContentsDebuggingEnabled(bool debuggingEnabled) async { + Map args = {}; + args.putIfAbsent('debuggingEnabled', () => debuggingEnabled); + return await _staticChannel.invokeMethod( + 'setWebContentsDebuggingEnabled', args); + } + + @override + Future getVariationsHeader() async { + Map args = {}; + return await _staticChannel.invokeMethod( + 'getVariationsHeader', args); + } + + @override + Future isMultiProcessEnabled() async { + Map args = {}; + return await _staticChannel.invokeMethod( + 'isMultiProcessEnabled', args) ?? + false; + } + + @override + Future disableWebView() async { + Map args = {}; + await _staticChannel.invokeMethod('disableWebView', args); + } + + @override + Future disposeKeepAlive(InAppWebViewKeepAlive keepAlive) async { + Map args = {}; + args.putIfAbsent('keepAliveId', () => keepAlive.id); + await _staticChannel.invokeMethod('disposeKeepAlive', args); + _keepAliveMap[keepAlive] = null; + } + + @override + Future get tRexRunnerHtml async => await rootBundle.loadString( + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html'); + + @override + Future get tRexRunnerCss async => await rootBundle.loadString( + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css'); + + @override + dynamic getViewId() { + return id; + } + + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: !isKeepAlive); + _inAppBrowser = null; + webStorage.dispose(); + if (!isKeepAlive) { + _controllerFromPlatform = null; + _javaScriptHandlersMap.clear(); + _userScripts.clear(); + _webMessageListenerObjNames.clear(); + _injectedScriptsFromURL.clear(); + for (final webMessageChannel in _webMessageChannels) { + webMessageChannel.dispose(); + } + _webMessageChannels.clear(); + for (final webMessageListener in _webMessageListeners) { + webMessageListener.dispose(); + } + _webMessageListeners.clear(); + } + } +} + +extension InternalInAppWebViewController on AndroidInAppWebViewController { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_android/lib/src/in_app_webview/main.dart b/flutter_inappwebview_android/lib/src/in_app_webview/main.dart new file mode 100644 index 00000000..b83b0611 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/in_app_webview/main.dart @@ -0,0 +1,3 @@ +export 'in_app_webview_controller.dart' hide InternalInAppWebViewController; +export 'in_app_webview.dart'; +export 'headless_in_app_webview.dart' hide InternalHeadlessInAppWebView; diff --git a/flutter_inappwebview_android/lib/src/inappwebview_platform.dart b/flutter_inappwebview_android/lib/src/inappwebview_platform.dart new file mode 100644 index 00000000..5d11fca8 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/inappwebview_platform.dart @@ -0,0 +1,365 @@ +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'chrome_safari_browser/chrome_safari_browser.dart'; +import 'cookie_manager.dart'; +import 'http_auth_credentials_database.dart'; +import 'find_interaction/main.dart'; +import 'in_app_browser/in_app_browser.dart'; +import 'in_app_webview/main.dart'; +import 'print_job/main.dart'; +import 'pull_to_refresh/main.dart'; +import 'web_message/main.dart'; +import 'web_storage/main.dart'; +import 'process_global_config.dart'; +import 'proxy_controller.dart'; +import 'service_worker_controller.dart'; +import 'tracing_controller.dart'; +import 'webview_asset_loader.dart'; +import 'webview_feature.dart' as wv; + +/// Implementation of [InAppWebViewPlatform] using the WebView API. +class AndroidInAppWebViewPlatform extends InAppWebViewPlatform { + /// Registers this class as the default instance of [InAppWebViewPlatform]. + static void registerWith() { + InAppWebViewPlatform.instance = AndroidInAppWebViewPlatform(); + } + + /// Creates a new [AndroidCookieManager]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [CookieManager] in `flutter_inappwebview` instead. + @override + AndroidCookieManager createPlatformCookieManager( + PlatformCookieManagerCreationParams params, + ) { + return AndroidCookieManager(params); + } + + /// Creates a new [AndroidInAppWebViewController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebViewController] in `flutter_inappwebview` instead. + @override + AndroidInAppWebViewController createPlatformInAppWebViewController( + PlatformInAppWebViewControllerCreationParams params, + ) { + return AndroidInAppWebViewController(params); + } + + /// Creates a new empty [AndroidInAppWebViewController] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebViewController] in `flutter_inappwebview` instead. + @override + AndroidInAppWebViewController createPlatformInAppWebViewControllerStatic() { + return AndroidInAppWebViewController.static(); + } + + /// Creates a new [AndroidInAppWebViewWidget]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebView] in `flutter_inappwebview` instead. + @override + AndroidInAppWebViewWidget createPlatformInAppWebViewWidget( + PlatformInAppWebViewWidgetCreationParams params, + ) { + return AndroidInAppWebViewWidget(params); + } + + /// Creates a new [AndroidFindInteractionController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [FindInteractionController] in `flutter_inappwebview` instead. + @override + AndroidFindInteractionController createPlatformFindInteractionController( + PlatformFindInteractionControllerCreationParams params, + ) { + return AndroidFindInteractionController(params); + } + + /// Creates a new [AndroidPrintJobController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PrintJobController] in `flutter_inappwebview` instead. + @override + AndroidPrintJobController createPlatformPrintJobController( + PlatformPrintJobControllerCreationParams params, + ) { + return AndroidPrintJobController(params); + } + + /// Creates a new [AndroidPullToRefreshController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PullToRefreshController] in `flutter_inappwebview` instead. + @override + AndroidPullToRefreshController createPlatformPullToRefreshController( + PlatformPullToRefreshControllerCreationParams params, + ) { + return AndroidPullToRefreshController(params); + } + + /// Creates a new [AndroidWebMessageChannel]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessageChannel] in `flutter_inappwebview` instead. + @override + AndroidWebMessageChannel createPlatformWebMessageChannel( + PlatformWebMessageChannelCreationParams params, + ) { + return AndroidWebMessageChannel(params); + } + + /// Creates a new empty [AndroidWebMessageChannel] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessageChannel] in `flutter_inappwebview` instead. + @override + AndroidWebMessageChannel createPlatformWebMessageChannelStatic() { + return AndroidWebMessageChannel.static(); + } + + /// Creates a new [AndroidWebMessageListener]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessageListener] in `flutter_inappwebview` instead. + @override + AndroidWebMessageListener createPlatformWebMessageListener( + PlatformWebMessageListenerCreationParams params, + ) { + return AndroidWebMessageListener(params); + } + + /// Creates a new [AndroidJavaScriptReplyProxy]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [JavaScriptReplyProxy] in `flutter_inappwebview` instead. + @override + AndroidJavaScriptReplyProxy createPlatformJavaScriptReplyProxy( + PlatformJavaScriptReplyProxyCreationParams params, + ) { + return AndroidJavaScriptReplyProxy(params); + } + + /// Creates a new [AndroidWebMessagePort]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessagePort] in `flutter_inappwebview` instead. + @override + AndroidWebMessagePort createPlatformWebMessagePort( + PlatformWebMessagePortCreationParams params, + ) { + return AndroidWebMessagePort(params); + } + + /// Creates a new [AndroidWebStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [AndroidWebStorage] in `flutter_inappwebview` instead. + @override + AndroidWebStorage createPlatformWebStorage( + PlatformWebStorageCreationParams params, + ) { + return AndroidWebStorage(params); + } + + /// Creates a new [AndroidLocalStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [AndroidLocalStorage] in `flutter_inappwebview` instead. + @override + AndroidLocalStorage createPlatformLocalStorage( + PlatformLocalStorageCreationParams params, + ) { + return AndroidLocalStorage(params); + } + + /// Creates a new [AndroidSessionStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PlatformSessionStorage] in `flutter_inappwebview` instead. + @override + AndroidSessionStorage createPlatformSessionStorage( + PlatformSessionStorageCreationParams params, + ) { + return AndroidSessionStorage(params); + } + + /// Creates a new [AndroidHeadlessInAppWebView]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [HeadlessInAppWebView] in `flutter_inappwebview` instead. + @override + AndroidHeadlessInAppWebView createPlatformHeadlessInAppWebView( + PlatformHeadlessInAppWebViewCreationParams params, + ) { + return AndroidHeadlessInAppWebView(params); + } + + /// Creates a new [AndroidHttpAuthCredentialDatabase]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [HttpAuthCredentialDatabase] in `flutter_inappwebview` instead. + @override + AndroidHttpAuthCredentialDatabase createPlatformHttpAuthCredentialDatabase( + PlatformHttpAuthCredentialDatabaseCreationParams params, + ) { + return AndroidHttpAuthCredentialDatabase(params); + } + + /// Creates a new [AndroidInAppBrowser]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppBrowser] in `flutter_inappwebview` instead. + @override + AndroidInAppBrowser createPlatformInAppBrowser( + PlatformInAppBrowserCreationParams params, + ) { + return AndroidInAppBrowser(params); + } + + /// Creates a new empty [AndroidInAppBrowser] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppBrowser] in `flutter_inappwebview` instead. + @override + AndroidInAppBrowser createPlatformInAppBrowserStatic() { + return AndroidInAppBrowser.static(); + } + + /// Creates a new [AndroidProcessGlobalConfig]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [ProcessGlobalConfig] in `flutter_inappwebview` instead. + @override + AndroidProcessGlobalConfig createPlatformProcessGlobalConfig( + PlatformProcessGlobalConfigCreationParams params, + ) { + return AndroidProcessGlobalConfig(params); + } + + /// Creates a new [AndroidProxyController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [ProxyController] in `flutter_inappwebview` instead. + @override + AndroidProxyController createPlatformProxyController( + PlatformProxyControllerCreationParams params, + ) { + return AndroidProxyController(params); + } + + /// Creates a new [AndroidServiceWorkerController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [ServiceWorkerController] in `flutter_inappwebview` instead. + @override + AndroidServiceWorkerController createPlatformServiceWorkerController( + PlatformServiceWorkerControllerCreationParams params, + ) { + return AndroidServiceWorkerController(params); + } + + /// Creates a new empty [AndroidServiceWorkerController] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [ServiceWorkerController] in `flutter_inappwebview` instead. + @override + AndroidServiceWorkerController createPlatformServiceWorkerControllerStatic() { + return AndroidServiceWorkerController.static(); + } + + /// Creates a new [AndroidTracingController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [TracingController] in `flutter_inappwebview` instead. + @override + AndroidTracingController createPlatformTracingController( + PlatformTracingControllerCreationParams params, + ) { + return AndroidTracingController(params); + } + + /// Creates a new [AndroidAssetsPathHandler]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [AssetsPathHandler] in `flutter_inappwebview` instead. + @override + AndroidAssetsPathHandler createPlatformAssetsPathHandler( + PlatformAssetsPathHandlerCreationParams params, + ) { + return AndroidAssetsPathHandler(params); + } + + /// Creates a new [AndroidResourcesPathHandler]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [ResourcesPathHandler] in `flutter_inappwebview` instead. + @override + AndroidResourcesPathHandler createPlatformResourcesPathHandler( + PlatformResourcesPathHandlerCreationParams params, + ) { + return AndroidResourcesPathHandler(params); + } + + /// Creates a new [AndroidInternalStoragePathHandler]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InternalStoragePathHandler] in `flutter_inappwebview` instead. + @override + AndroidInternalStoragePathHandler createPlatformInternalStoragePathHandler( + PlatformInternalStoragePathHandlerCreationParams params, + ) { + return AndroidInternalStoragePathHandler(params); + } + + /// Creates a new [wv.AndroidWebViewFeature]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewFeature] in `flutter_inappwebview` instead. + @override + wv.AndroidWebViewFeature createPlatformWebViewFeature( + PlatformWebViewFeatureCreationParams params, + ) { + return wv.AndroidWebViewFeature(params); + } + + /// Creates a new empty [wv.AndroidWebViewFeature] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewFeature] in `flutter_inappwebview` instead. + @override + wv.AndroidWebViewFeature createPlatformWebViewFeatureStatic() { + return wv.AndroidWebViewFeature.static(); + } + + /// Creates a new [AndroidChromeSafariBrowser]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [ChromeSafariBrowser] in `flutter_inappwebview` instead. + @override + AndroidChromeSafariBrowser createPlatformChromeSafariBrowser( + PlatformChromeSafariBrowserCreationParams params, + ) { + return AndroidChromeSafariBrowser(params); + } + + /// Creates a new empty [AndroidChromeSafariBrowser] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [ChromeSafariBrowser] in `flutter_inappwebview` instead. + @override + AndroidChromeSafariBrowser createPlatformChromeSafariBrowserStatic() { + return AndroidChromeSafariBrowser.static(); + } + + /// Creates a new empty [AndroidWebStorageManager] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebStorageManager] in `flutter_inappwebview` instead. + @override + AndroidWebStorageManager createPlatformWebStorageManager( + PlatformWebStorageManagerCreationParams params) { + return AndroidWebStorageManager(params); + } +} diff --git a/flutter_inappwebview_android/lib/src/main.dart b/flutter_inappwebview_android/lib/src/main.dart new file mode 100644 index 00000000..b49714ad --- /dev/null +++ b/flutter_inappwebview_android/lib/src/main.dart @@ -0,0 +1,18 @@ +export 'inappwebview_platform.dart'; +export 'in_app_webview/main.dart'; +export 'in_app_browser/main.dart'; +export 'chrome_safari_browser/main.dart'; +export 'web_storage/main.dart'; +export 'cookie_manager.dart' hide InternalCookieManager; +export 'http_auth_credentials_database.dart' + hide InternalHttpAuthCredentialDatabase; +export 'pull_to_refresh/main.dart'; +export 'web_message/main.dart'; +export 'print_job/main.dart'; +export 'find_interaction/main.dart'; +export 'service_worker_controller.dart'; +export 'webview_feature.dart' hide InternalWebViewFeature; +export 'proxy_controller.dart' hide InternalProxyController; +export 'webview_asset_loader.dart'; +export 'tracing_controller.dart' hide InternalTracingController; +export 'process_global_config.dart' hide InternalProcessGlobalConfig; diff --git a/lib/src/platform_util.dart b/flutter_inappwebview_android/lib/src/platform_util.dart similarity index 93% rename from lib/src/platform_util.dart rename to flutter_inappwebview_android/lib/src/platform_util.dart index 71250cb8..27e96478 100644 --- a/lib/src/platform_util.dart +++ b/flutter_inappwebview_android/lib/src/platform_util.dart @@ -3,8 +3,8 @@ import 'package:flutter/services.dart'; ///Platform native utilities class PlatformUtil { static PlatformUtil? _instance; - static const MethodChannel _channel = const MethodChannel( - 'com.pichillilorenzo/flutter_inappwebview_platformutil'); + static const MethodChannel _channel = + MethodChannel('com.pichillilorenzo/flutter_inappwebview_platformutil'); PlatformUtil._(); diff --git a/flutter_inappwebview_android/lib/src/print_job/main.dart b/flutter_inappwebview_android/lib/src/print_job/main.dart new file mode 100644 index 00000000..4e70ad94 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/print_job/main.dart @@ -0,0 +1 @@ +export 'print_job_controller.dart'; diff --git a/flutter_inappwebview_android/lib/src/print_job/print_job_controller.dart b/flutter_inappwebview_android/lib/src/print_job/print_job_controller.dart new file mode 100644 index 00000000..76831154 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/print_job/print_job_controller.dart @@ -0,0 +1,77 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidPrintJobController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformPrintJobControllerCreationParams] for +/// more information. +@immutable +class AndroidPrintJobControllerCreationParams + extends PlatformPrintJobControllerCreationParams { + /// Creates a new [AndroidPrintJobControllerCreationParams] instance. + const AndroidPrintJobControllerCreationParams( + {required super.id, super.onComplete}); + + /// Creates a [AndroidPrintJobControllerCreationParams] instance based on [PlatformPrintJobControllerCreationParams]. + factory AndroidPrintJobControllerCreationParams.fromPlatformPrintJobControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformPrintJobControllerCreationParams params) { + return AndroidPrintJobControllerCreationParams( + id: params.id, onComplete: params.onComplete); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformPrintJobController} +class AndroidPrintJobController extends PlatformPrintJobController + with ChannelController { + /// Constructs a [AndroidPrintJobController]. + AndroidPrintJobController(PlatformPrintJobControllerCreationParams params) + : super.implementation( + params is AndroidPrintJobControllerCreationParams + ? params + : AndroidPrintJobControllerCreationParams + .fromPlatformPrintJobControllerCreationParams(params), + ) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_printjobcontroller_${params.id}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + @override + Future cancel() async { + Map args = {}; + await channel?.invokeMethod('cancel', args); + } + + @override + Future restart() async { + Map args = {}; + await channel?.invokeMethod('restart', args); + } + + @override + Future getInfo() async { + Map args = {}; + Map? infoMap = + (await channel?.invokeMethod('getInfo', args))?.cast(); + return PrintJobInfo.fromMap(infoMap); + } + + @override + Future dispose() async { + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + } +} diff --git a/flutter_inappwebview_android/lib/src/process_global_config.dart b/flutter_inappwebview_android/lib/src/process_global_config.dart new file mode 100644 index 00000000..1f4d6fe0 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/process_global_config.dart @@ -0,0 +1,76 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidProcessGlobalConfig]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformProcessGlobalConfigCreationParams] for +/// more information. +@immutable +class AndroidProcessGlobalConfigCreationParams + extends PlatformProcessGlobalConfigCreationParams { + /// Creates a new [AndroidProcessGlobalConfigCreationParams] instance. + const AndroidProcessGlobalConfigCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformProcessGlobalConfigCreationParams params, + ) : super(); + + /// Creates a [AndroidProcessGlobalConfigCreationParams] instance based on [PlatformProcessGlobalConfigCreationParams]. + factory AndroidProcessGlobalConfigCreationParams.fromPlatformProcessGlobalConfigCreationParams( + PlatformProcessGlobalConfigCreationParams params) { + return AndroidProcessGlobalConfigCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformProcessGlobalConfig} +class AndroidProcessGlobalConfig extends PlatformProcessGlobalConfig + with ChannelController { + /// Creates a new [AndroidProcessGlobalConfig]. + AndroidProcessGlobalConfig(PlatformProcessGlobalConfigCreationParams params) + : super.implementation( + params is AndroidProcessGlobalConfigCreationParams + ? params + : AndroidProcessGlobalConfigCreationParams + .fromPlatformProcessGlobalConfigCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_processglobalconfig'); + handler = handleMethod; + initMethodCallHandler(); + } + + static AndroidProcessGlobalConfig? _instance; + + ///Gets the [AndroidProcessGlobalConfig] shared instance. + static AndroidProcessGlobalConfig instance() { + return (_instance != null) ? _instance! : _init(); + } + + static AndroidProcessGlobalConfig _init() { + _instance = AndroidProcessGlobalConfig( + AndroidProcessGlobalConfigCreationParams( + const PlatformProcessGlobalConfigCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future apply({required ProcessGlobalConfigSettings settings}) async { + Map args = {}; + args.putIfAbsent("settings", () => settings.toMap()); + await channel?.invokeMethod('apply', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalProcessGlobalConfig on AndroidProcessGlobalConfig { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_android/lib/src/proxy_controller.dart b/flutter_inappwebview_android/lib/src/proxy_controller.dart new file mode 100644 index 00000000..07649ac8 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/proxy_controller.dart @@ -0,0 +1,81 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidProxyController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformProxyControllerCreationParams] for +/// more information. +@immutable +class AndroidProxyControllerCreationParams + extends PlatformProxyControllerCreationParams { + /// Creates a new [AndroidProxyControllerCreationParams] instance. + const AndroidProxyControllerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformProxyControllerCreationParams params, + ) : super(); + + /// Creates a [AndroidProxyControllerCreationParams] instance based on [PlatformProxyControllerCreationParams]. + factory AndroidProxyControllerCreationParams.fromPlatformProxyControllerCreationParams( + PlatformProxyControllerCreationParams params) { + return AndroidProxyControllerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformProxyController} +class AndroidProxyController extends PlatformProxyController + with ChannelController { + /// Creates a new [AndroidProxyController]. + AndroidProxyController(PlatformProxyControllerCreationParams params) + : super.implementation( + params is AndroidProxyControllerCreationParams + ? params + : AndroidProxyControllerCreationParams + .fromPlatformProxyControllerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_proxycontroller'); + handler = handleMethod; + initMethodCallHandler(); + } + + static AndroidProxyController? _instance; + + ///Gets the [AndroidProxyController] shared instance. + static AndroidProxyController instance() { + return (_instance != null) ? _instance! : _init(); + } + + static AndroidProxyController _init() { + _instance = AndroidProxyController(AndroidProxyControllerCreationParams( + const PlatformProxyControllerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future setProxyOverride({required ProxySettings settings}) async { + Map args = {}; + args.putIfAbsent("settings", () => settings.toMap()); + await channel?.invokeMethod('setProxyOverride', args); + } + + @override + Future clearProxyOverride() async { + Map args = {}; + await channel?.invokeMethod('clearProxyOverride', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalProxyController on AndroidProxyController { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_android/lib/src/pull_to_refresh/main.dart b/flutter_inappwebview_android/lib/src/pull_to_refresh/main.dart new file mode 100644 index 00000000..586af9d8 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/pull_to_refresh/main.dart @@ -0,0 +1 @@ +export 'pull_to_refresh_controller.dart' hide InternalPullToRefreshController; diff --git a/flutter_inappwebview_android/lib/src/pull_to_refresh/pull_to_refresh_controller.dart b/flutter_inappwebview_android/lib/src/pull_to_refresh/pull_to_refresh_controller.dart new file mode 100644 index 00000000..693c98a1 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/pull_to_refresh/pull_to_refresh_controller.dart @@ -0,0 +1,163 @@ +import 'dart:ui'; + +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidPullToRefreshController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformPullToRefreshControllerCreationParams] for +/// more information. +class AndroidPullToRefreshControllerCreationParams + extends PlatformPullToRefreshControllerCreationParams { + /// Creates a new [AndroidPullToRefreshControllerCreationParams] instance. + AndroidPullToRefreshControllerCreationParams( + {super.onRefresh, super.options, super.settings}); + + /// Creates a [AndroidPullToRefreshControllerCreationParams] instance based on [PlatformPullToRefreshControllerCreationParams]. + factory AndroidPullToRefreshControllerCreationParams.fromPlatformPullToRefreshControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformPullToRefreshControllerCreationParams params) { + return AndroidPullToRefreshControllerCreationParams( + onRefresh: params.onRefresh, + options: params.options, + settings: params.settings); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformPullToRefreshController} +class AndroidPullToRefreshController extends PlatformPullToRefreshController + with ChannelController { + /// Constructs a [AndroidPullToRefreshController]. + AndroidPullToRefreshController( + PlatformPullToRefreshControllerCreationParams params) + : super.implementation( + params is AndroidPullToRefreshControllerCreationParams + ? params + : AndroidPullToRefreshControllerCreationParams + .fromPlatformPullToRefreshControllerCreationParams(params), + ); + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + debugLoggingSettings: + PlatformPullToRefreshController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + _debugLog(call.method, call.arguments); + + switch (call.method) { + case "onRefresh": + if (params.onRefresh != null) params.onRefresh!(); + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + Future setEnabled(bool enabled) async { + Map args = {}; + args.putIfAbsent('enabled', () => enabled); + await channel?.invokeMethod('setEnabled', args); + } + + @override + Future isEnabled() async { + Map args = {}; + return await channel?.invokeMethod('isEnabled', args) ?? false; + } + + Future _setRefreshing(bool refreshing) async { + Map args = {}; + args.putIfAbsent('refreshing', () => refreshing); + await channel?.invokeMethod('setRefreshing', args); + } + + @override + Future beginRefreshing() async { + return await _setRefreshing(true); + } + + @override + Future endRefreshing() async { + await _setRefreshing(false); + } + + @override + Future isRefreshing() async { + Map args = {}; + return await channel?.invokeMethod('isRefreshing', args) ?? false; + } + + @override + Future setColor(Color color) async { + Map args = {}; + args.putIfAbsent('color', () => color.toHex()); + await channel?.invokeMethod('setColor', args); + } + + @override + Future setBackgroundColor(Color color) async { + Map args = {}; + args.putIfAbsent('color', () => color.toHex()); + await channel?.invokeMethod('setBackgroundColor', args); + } + + @override + Future setDistanceToTriggerSync(int distanceToTriggerSync) async { + Map args = {}; + args.putIfAbsent('distanceToTriggerSync', () => distanceToTriggerSync); + await channel?.invokeMethod('setDistanceToTriggerSync', args); + } + + @override + Future setSlingshotDistance(int slingshotDistance) async { + Map args = {}; + args.putIfAbsent('slingshotDistance', () => slingshotDistance); + await channel?.invokeMethod('setSlingshotDistance', args); + } + + @override + Future getDefaultSlingshotDistance() async { + Map args = {}; + return await channel?.invokeMethod( + 'getDefaultSlingshotDistance', args) ?? + 0; + } + + @Deprecated("Use setIndicatorSize instead") + @override + Future setSize(AndroidPullToRefreshSize size) async { + Map args = {}; + args.putIfAbsent('size', () => size.toNativeValue()); + await channel?.invokeMethod('setSize', args); + } + + @override + Future setIndicatorSize(PullToRefreshSize size) async { + Map args = {}; + args.putIfAbsent('size', () => size.toNativeValue()); + await channel?.invokeMethod('setSize', args); + } + + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: !isKeepAlive); + } +} + +extension InternalPullToRefreshController on AndroidPullToRefreshController { + void init(dynamic id) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } +} diff --git a/flutter_inappwebview_android/lib/src/service_worker_controller.dart b/flutter_inappwebview_android/lib/src/service_worker_controller.dart new file mode 100644 index 00000000..646b958a --- /dev/null +++ b/flutter_inappwebview_android/lib/src/service_worker_controller.dart @@ -0,0 +1,161 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidServiceWorkerController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformServiceWorkerControllerCreationParams] for +/// more information. +@immutable +class AndroidServiceWorkerControllerCreationParams + extends PlatformServiceWorkerControllerCreationParams { + /// Creates a new [AndroidServiceWorkerControllerCreationParams] instance. + const AndroidServiceWorkerControllerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformServiceWorkerControllerCreationParams params, + ) : super(); + + /// Creates a [AndroidServiceWorkerControllerCreationParams] instance based on [PlatformServiceWorkerControllerCreationParams]. + factory AndroidServiceWorkerControllerCreationParams.fromPlatformServiceWorkerControllerCreationParams( + PlatformServiceWorkerControllerCreationParams params) { + return AndroidServiceWorkerControllerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformServiceWorkerController} +class AndroidServiceWorkerController extends PlatformServiceWorkerController + with ChannelController { + /// Creates a new [AndroidServiceWorkerController]. + AndroidServiceWorkerController( + PlatformServiceWorkerControllerCreationParams params) + : super.implementation( + params is AndroidServiceWorkerControllerCreationParams + ? params + : AndroidServiceWorkerControllerCreationParams + .fromPlatformServiceWorkerControllerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller'); + handler = handleMethod; + initMethodCallHandler(); + } + + factory AndroidServiceWorkerController.static() { + return instance(); + } + + static AndroidServiceWorkerController? _instance; + + ///Gets the [AndroidServiceWorkerController] shared instance. + static AndroidServiceWorkerController instance() { + return (_instance != null) ? _instance! : _init(); + } + + static AndroidServiceWorkerController _init() { + _instance = AndroidServiceWorkerController( + AndroidServiceWorkerControllerCreationParams( + const PlatformServiceWorkerControllerCreationParams())); + return _instance!; + } + + ServiceWorkerClient? _serviceWorkerClient; + + @override + ServiceWorkerClient? get serviceWorkerClient => _serviceWorkerClient; + + @override + Future setServiceWorkerClient(ServiceWorkerClient? value) async { + Map args = {}; + args.putIfAbsent('isNull', () => value == null); + await channel?.invokeMethod("setServiceWorkerClient", args); + _serviceWorkerClient = value; + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "shouldInterceptRequest": + if (serviceWorkerClient != null && + serviceWorkerClient!.shouldInterceptRequest != null) { + Map arguments = + call.arguments.cast(); + WebResourceRequest request = WebResourceRequest.fromMap(arguments)!; + + return (await serviceWorkerClient!.shouldInterceptRequest!(request)) + ?.toMap(); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + + return null; + } + + @override + Future getAllowContentAccess() async { + Map args = {}; + return await channel?.invokeMethod('getAllowContentAccess', args) ?? + false; + } + + @override + Future getAllowFileAccess() async { + Map args = {}; + return await channel?.invokeMethod('getAllowFileAccess', args) ?? + false; + } + + @override + Future getBlockNetworkLoads() async { + Map args = {}; + return await channel?.invokeMethod('getBlockNetworkLoads', args) ?? + false; + } + + @override + Future getCacheMode() async { + Map args = {}; + return CacheMode.fromNativeValue( + await channel?.invokeMethod('getCacheMode', args)); + } + + @override + Future setAllowContentAccess(bool allow) async { + Map args = {}; + args.putIfAbsent("allow", () => allow); + await channel?.invokeMethod('setAllowContentAccess', args); + } + + @override + Future setAllowFileAccess(bool allow) async { + Map args = {}; + args.putIfAbsent("allow", () => allow); + await channel?.invokeMethod('setAllowFileAccess', args); + } + + @override + Future setBlockNetworkLoads(bool flag) async { + Map args = {}; + args.putIfAbsent("flag", () => flag); + await channel?.invokeMethod('setBlockNetworkLoads', args); + } + + @override + Future setCacheMode(CacheMode mode) async { + Map args = {}; + args.putIfAbsent("mode", () => mode.toNativeValue()); + await channel?.invokeMethod('setCacheMode', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalServiceWorkerController on AndroidServiceWorkerController { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_android/lib/src/tracing_controller.dart b/flutter_inappwebview_android/lib/src/tracing_controller.dart new file mode 100644 index 00000000..9d619f07 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/tracing_controller.dart @@ -0,0 +1,88 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidTracingController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformTracingControllerCreationParams] for +/// more information. +@immutable +class AndroidTracingControllerCreationParams + extends PlatformTracingControllerCreationParams { + /// Creates a new [AndroidTracingControllerCreationParams] instance. + const AndroidTracingControllerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformTracingControllerCreationParams params, + ) : super(); + + /// Creates a [AndroidTracingControllerCreationParams] instance based on [PlatformTracingControllerCreationParams]. + factory AndroidTracingControllerCreationParams.fromPlatformTracingControllerCreationParams( + PlatformTracingControllerCreationParams params) { + return AndroidTracingControllerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformTracingController} +class AndroidTracingController extends PlatformTracingController + with ChannelController { + /// Creates a new [AndroidTracingController]. + AndroidTracingController(PlatformTracingControllerCreationParams params) + : super.implementation( + params is AndroidTracingControllerCreationParams + ? params + : AndroidTracingControllerCreationParams + .fromPlatformTracingControllerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_tracingcontroller'); + handler = handleMethod; + initMethodCallHandler(); + } + + static AndroidTracingController? _instance; + + ///Gets the [AndroidTracingController] shared instance. + static AndroidTracingController instance() { + return (_instance != null) ? _instance! : _init(); + } + + static AndroidTracingController _init() { + _instance = AndroidTracingController(AndroidTracingControllerCreationParams( + const PlatformTracingControllerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future start({required TracingSettings settings}) async { + Map args = {}; + args.putIfAbsent("settings", () => settings.toMap()); + await channel?.invokeMethod('start', args); + } + + @override + Future stop({String? filePath}) async { + Map args = {}; + args.putIfAbsent("filePath", () => filePath); + return await channel?.invokeMethod('stop', args) ?? false; + } + + @override + Future isTracing() async { + Map args = {}; + return await channel?.invokeMethod('isTracing', args) ?? false; + } + + @override + void dispose() { + // empty + } +} + +extension InternalTracingController on AndroidTracingController { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_android/lib/src/web_message/main.dart b/flutter_inappwebview_android/lib/src/web_message/main.dart new file mode 100644 index 00000000..d41e30c7 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/web_message/main.dart @@ -0,0 +1,3 @@ +export 'web_message_port.dart' hide InternalWebMessagePort; +export 'web_message_channel.dart' hide InternalWebMessageChannel; +export 'web_message_listener.dart'; diff --git a/flutter_inappwebview_android/lib/src/web_message/web_message_channel.dart b/flutter_inappwebview_android/lib/src/web_message/web_message_channel.dart new file mode 100644 index 00000000..9af87309 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/web_message/web_message_channel.dart @@ -0,0 +1,120 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'web_message_port.dart'; + +/// Object specifying creation parameters for creating a [AndroidWebMessageChannel]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessageChannelCreationParams] for +/// more information. +@immutable +class AndroidWebMessageChannelCreationParams + extends PlatformWebMessageChannelCreationParams { + /// Creates a new [AndroidWebMessageChannelCreationParams] instance. + const AndroidWebMessageChannelCreationParams( + {required super.id, required super.port1, required super.port2}); + + /// Creates a [AndroidWebMessageChannelCreationParams] instance based on [PlatformWebMessageChannelCreationParams]. + factory AndroidWebMessageChannelCreationParams.fromPlatformWebMessageChannelCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessageChannelCreationParams params) { + return AndroidWebMessageChannelCreationParams( + id: params.id, port1: params.port1, port2: params.port2); + } + + @override + String toString() { + return 'AndroidWebMessageChannelCreationParams{id: $id, port1: $port1, port2: $port2}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageChannel} +class AndroidWebMessageChannel extends PlatformWebMessageChannel + with ChannelController { + /// Constructs a [AndroidWebMessageChannel]. + AndroidWebMessageChannel(PlatformWebMessageChannelCreationParams params) + : super.implementation( + params is AndroidWebMessageChannelCreationParams + ? params + : AndroidWebMessageChannelCreationParams + .fromPlatformWebMessageChannelCreationParams(params), + ) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_web_message_channel_${params.id}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + static final AndroidWebMessageChannel _staticValue = AndroidWebMessageChannel( + AndroidWebMessageChannelCreationParams( + id: '', + port1: AndroidWebMessagePort( + AndroidWebMessagePortCreationParams(index: 0)), + port2: AndroidWebMessagePort( + AndroidWebMessagePortCreationParams(index: 1)))); + + /// Provide static access. + factory AndroidWebMessageChannel.static() { + return _staticValue; + } + + AndroidWebMessagePort get _androidPort1 => port1 as AndroidWebMessagePort; + + AndroidWebMessagePort get _androidPort2 => port2 as AndroidWebMessagePort; + + static AndroidWebMessageChannel? _fromMap(Map? map) { + if (map == null) { + return null; + } + var webMessageChannel = AndroidWebMessageChannel( + AndroidWebMessageChannelCreationParams( + id: map["id"], + port1: AndroidWebMessagePort( + AndroidWebMessagePortCreationParams(index: 0)), + port2: AndroidWebMessagePort( + AndroidWebMessagePortCreationParams(index: 1)))); + webMessageChannel._androidPort1.webMessageChannel = webMessageChannel; + webMessageChannel._androidPort2.webMessageChannel = webMessageChannel; + return webMessageChannel; + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onMessage": + int index = call.arguments["index"]; + var port = index == 0 ? _androidPort1 : _androidPort2; + if (port.onMessage != null) { + WebMessage? message = call.arguments["message"] != null + ? WebMessage.fromMap( + call.arguments["message"].cast()) + : null; + port.onMessage!(message); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + AndroidWebMessageChannel? fromMap(Map? map) { + return _fromMap(map); + } + + @override + void dispose() { + disposeChannel(); + } + + @override + String toString() { + return 'AndroidWebMessageChannel{id: $id, port1: $port1, port2: $port2}'; + } +} + +extension InternalWebMessageChannel on AndroidWebMessageChannel { + MethodChannel? get internalChannel => channel; +} diff --git a/flutter_inappwebview_android/lib/src/web_message/web_message_listener.dart b/flutter_inappwebview_android/lib/src/web_message/web_message_listener.dart new file mode 100644 index 00000000..00307c9a --- /dev/null +++ b/flutter_inappwebview_android/lib/src/web_message/web_message_listener.dart @@ -0,0 +1,164 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidWebMessageListener]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessageListenerCreationParams] for +/// more information. +@immutable +class AndroidWebMessageListenerCreationParams + extends PlatformWebMessageListenerCreationParams { + /// Creates a new [AndroidWebMessageListenerCreationParams] instance. + const AndroidWebMessageListenerCreationParams( + {required this.allowedOriginRules, + required super.jsObjectName, + super.onPostMessage}); + + /// Creates a [AndroidWebMessageListenerCreationParams] instance based on [PlatformWebMessageListenerCreationParams]. + factory AndroidWebMessageListenerCreationParams.fromPlatformWebMessageListenerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessageListenerCreationParams params) { + return AndroidWebMessageListenerCreationParams( + allowedOriginRules: params.allowedOriginRules ?? Set.from(["*"]), + jsObjectName: params.jsObjectName, + onPostMessage: params.onPostMessage); + } + + @override + final Set allowedOriginRules; + + @override + String toString() { + return 'AndroidWebMessageListenerCreationParams{jsObjectName: $jsObjectName, allowedOriginRules: $allowedOriginRules, onPostMessage: $onPostMessage}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageListener} +class AndroidWebMessageListener extends PlatformWebMessageListener + with ChannelController { + /// Constructs a [AndroidWebMessageListener]. + AndroidWebMessageListener(PlatformWebMessageListenerCreationParams params) + : super.implementation( + params is AndroidWebMessageListenerCreationParams + ? params + : AndroidWebMessageListenerCreationParams + .fromPlatformWebMessageListenerCreationParams(params), + ) { + assert(!this._androidParams.allowedOriginRules.contains(""), + "allowedOriginRules cannot contain empty strings"); + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_web_message_listener_${_id}_${params.jsObjectName}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + ///Message Listener ID used internally. + final String _id = IdGenerator.generate(); + + AndroidJavaScriptReplyProxy? _replyProxy; + + AndroidWebMessageListenerCreationParams get _androidParams => + params as AndroidWebMessageListenerCreationParams; + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onPostMessage": + if (_replyProxy == null) { + _replyProxy = AndroidJavaScriptReplyProxy( + PlatformJavaScriptReplyProxyCreationParams( + webMessageListener: this)); + } + if (onPostMessage != null) { + WebMessage? message = call.arguments["message"] != null + ? WebMessage.fromMap( + call.arguments["message"].cast()) + : null; + WebUri? sourceOrigin = call.arguments["sourceOrigin"] != null + ? WebUri(call.arguments["sourceOrigin"]) + : null; + bool isMainFrame = call.arguments["isMainFrame"]; + onPostMessage!(message, sourceOrigin, isMainFrame, _replyProxy!); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + void dispose() { + disposeChannel(); + } + + @override + Map toMap() { + return { + "id": _id, + "jsObjectName": params.jsObjectName, + "allowedOriginRules": _androidParams.allowedOriginRules.toList(), + }; + } + + @override + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return 'AndroidWebMessageListener{id: ${_id}, jsObjectName: ${params.jsObjectName}, allowedOriginRules: ${params.allowedOriginRules}, replyProxy: $_replyProxy}'; + } +} + +/// Object specifying creation parameters for creating a [AndroidJavaScriptReplyProxy]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformJavaScriptReplyProxyCreationParams] for +/// more information. +@immutable +class AndroidJavaScriptReplyProxyCreationParams + extends PlatformJavaScriptReplyProxyCreationParams { + /// Creates a new [AndroidJavaScriptReplyProxyCreationParams] instance. + const AndroidJavaScriptReplyProxyCreationParams( + {required super.webMessageListener}); + + /// Creates a [AndroidJavaScriptReplyProxyCreationParams] instance based on [PlatformJavaScriptReplyProxyCreationParams]. + factory AndroidJavaScriptReplyProxyCreationParams.fromPlatformJavaScriptReplyProxyCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformJavaScriptReplyProxyCreationParams params) { + return AndroidJavaScriptReplyProxyCreationParams( + webMessageListener: params.webMessageListener); + } +} + +///{@macro flutter_inappwebview_platform_interface.JavaScriptReplyProxy} +class AndroidJavaScriptReplyProxy extends PlatformJavaScriptReplyProxy { + /// Constructs a [AndroidWebMessageListener]. + AndroidJavaScriptReplyProxy(PlatformJavaScriptReplyProxyCreationParams params) + : super.implementation( + params is AndroidJavaScriptReplyProxyCreationParams + ? params + : AndroidJavaScriptReplyProxyCreationParams + .fromPlatformJavaScriptReplyProxyCreationParams(params), + ); + + AndroidWebMessageListener get _androidWebMessageListener => + params.webMessageListener as AndroidWebMessageListener; + + @override + Future postMessage(WebMessage message) async { + Map args = {}; + args.putIfAbsent('message', () => message.toMap()); + await _androidWebMessageListener.channel?.invokeMethod('postMessage', args); + } + + @override + String toString() { + return 'AndroidJavaScriptReplyProxy{}'; + } +} diff --git a/flutter_inappwebview_android/lib/src/web_message/web_message_port.dart b/flutter_inappwebview_android/lib/src/web_message/web_message_port.dart new file mode 100644 index 00000000..f8979f5c --- /dev/null +++ b/flutter_inappwebview_android/lib/src/web_message/web_message_port.dart @@ -0,0 +1,95 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'web_message_channel.dart'; + +/// Object specifying creation parameters for creating a [AndroidWebMessagePort]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessagePortCreationParams] for +/// more information. +@immutable +class AndroidWebMessagePortCreationParams + extends PlatformWebMessagePortCreationParams { + /// Creates a new [AndroidWebMessagePortCreationParams] instance. + const AndroidWebMessagePortCreationParams({required super.index}); + + /// Creates a [AndroidWebMessagePortCreationParams] instance based on [PlatformWebMessagePortCreationParams]. + factory AndroidWebMessagePortCreationParams.fromPlatformWebMessagePortCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessagePortCreationParams params) { + return AndroidWebMessagePortCreationParams(index: params.index); + } + + @override + String toString() { + return 'AndroidWebMessagePortCreationParams{index: $index}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessagePort} +class AndroidWebMessagePort extends PlatformWebMessagePort { + WebMessageCallback? _onMessage; + late AndroidWebMessageChannel _webMessageChannel; + + /// Constructs a [AndroidWebMessagePort]. + AndroidWebMessagePort(PlatformWebMessagePortCreationParams params) + : super.implementation( + params is AndroidWebMessagePortCreationParams + ? params + : AndroidWebMessagePortCreationParams + .fromPlatformWebMessagePortCreationParams(params), + ); + + @override + Future setWebMessageCallback(WebMessageCallback? onMessage) async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + await _webMessageChannel.internalChannel + ?.invokeMethod('setWebMessageCallback', args); + this._onMessage = onMessage; + } + + @override + Future postMessage(WebMessage message) async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + args.putIfAbsent('message', () => message.toMap()); + await _webMessageChannel.internalChannel?.invokeMethod('postMessage', args); + } + + @override + Future close() async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + await _webMessageChannel.internalChannel?.invokeMethod('close', args); + } + + @override + Map toMap() { + return { + "index": params.index, + "webMessageChannelId": this._webMessageChannel.params.id + }; + } + + @override + Map toJson() { + return toMap(); + } + + @override + String toString() { + return 'AndroidWebMessagePort{index: ${params.index}}'; + } +} + +extension InternalWebMessagePort on AndroidWebMessagePort { + WebMessageCallback? get onMessage => _onMessage; + void set onMessage(WebMessageCallback? value) => _onMessage = value; + + AndroidWebMessageChannel get webMessageChannel => _webMessageChannel; + void set webMessageChannel(AndroidWebMessageChannel value) => + _webMessageChannel = value; +} diff --git a/flutter_inappwebview_android/lib/src/web_storage/main.dart b/flutter_inappwebview_android/lib/src/web_storage/main.dart new file mode 100644 index 00000000..7265ae52 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/web_storage/main.dart @@ -0,0 +1,2 @@ +export 'web_storage.dart'; +export 'web_storage_manager.dart' hide InternalWebStorageManager; diff --git a/flutter_inappwebview_android/lib/src/web_storage/web_storage.dart b/flutter_inappwebview_android/lib/src/web_storage/web_storage.dart new file mode 100644 index 00000000..fdcefd6f --- /dev/null +++ b/flutter_inappwebview_android/lib/src/web_storage/web_storage.dart @@ -0,0 +1,257 @@ +import 'dart:convert'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../in_app_webview/in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [AndroidWebStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebStorageCreationParams] for +/// more information. +class AndroidWebStorageCreationParams extends PlatformWebStorageCreationParams { + /// Creates a new [AndroidWebStorageCreationParams] instance. + AndroidWebStorageCreationParams( + {required super.localStorage, required super.sessionStorage}); + + /// Creates a [AndroidWebStorageCreationParams] instance based on [PlatformWebStorageCreationParams]. + factory AndroidWebStorageCreationParams.fromPlatformWebStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebStorageCreationParams params) { + return AndroidWebStorageCreationParams( + localStorage: params.localStorage, + sessionStorage: params.sessionStorage); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebStorage} +class AndroidWebStorage extends PlatformWebStorage { + /// Constructs a [AndroidWebStorage]. + AndroidWebStorage(PlatformWebStorageCreationParams params) + : super.implementation( + params is AndroidWebStorageCreationParams + ? params + : AndroidWebStorageCreationParams + .fromPlatformWebStorageCreationParams(params), + ); + + @override + PlatformLocalStorage get localStorage => params.localStorage; + + @override + PlatformSessionStorage get sessionStorage => params.sessionStorage; + + @override + void dispose() { + localStorage.dispose(); + sessionStorage.dispose(); + } +} + +/// Object specifying creation parameters for creating a [AndroidStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformStorageCreationParams] for +/// more information. +class AndroidStorageCreationParams extends PlatformStorageCreationParams { + /// Creates a new [AndroidStorageCreationParams] instance. + AndroidStorageCreationParams( + {required super.controller, required super.webStorageType}); + + /// Creates a [AndroidStorageCreationParams] instance based on [PlatformStorageCreationParams]. + factory AndroidStorageCreationParams.fromPlatformStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformStorageCreationParams params) { + return AndroidStorageCreationParams( + controller: params.controller, webStorageType: params.webStorageType); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformStorage} +abstract class AndroidStorage implements PlatformStorage { + @override + AndroidInAppWebViewController? controller; + + @override + Future length() async { + var result = await controller?.evaluateJavascript(source: """ + window.$webStorageType.length; + """); + return result != null ? int.parse(json.decode(result)) : null; + } + + @override + Future setItem({required String key, required dynamic value}) async { + var encodedValue = json.encode(value); + await controller?.evaluateJavascript(source: """ + window.$webStorageType.setItem("$key", ${value is String ? encodedValue : "JSON.stringify($encodedValue)"}); + """); + } + + @override + Future getItem({required String key}) async { + var itemValue = await controller?.evaluateJavascript(source: """ + window.$webStorageType.getItem("$key"); + """); + + if (itemValue == null) { + return null; + } + + try { + return json.decode(itemValue); + } catch (e) {} + + return itemValue; + } + + @override + Future removeItem({required String key}) async { + await controller?.evaluateJavascript(source: """ + window.$webStorageType.removeItem("$key"); + """); + } + + @override + Future> getItems() async { + var webStorageItems = []; + + List>? items = + (await controller?.evaluateJavascript(source: """ +(function() { + var webStorageItems = []; + for(var i = 0; i < window.$webStorageType.length; i++){ + var key = window.$webStorageType.key(i); + webStorageItems.push( + { + key: key, + value: window.$webStorageType.getItem(key) + } + ); + } + return webStorageItems; +})(); + """))?.cast>(); + + if (items == null) { + return webStorageItems; + } + + for (var item in items) { + webStorageItems + .add(WebStorageItem(key: item["key"], value: item["value"])); + } + + return webStorageItems; + } + + @override + Future clear() async { + await controller?.evaluateJavascript(source: """ + window.$webStorageType.clear(); + """); + } + + @override + Future key({required int index}) async { + var result = await controller?.evaluateJavascript(source: """ + window.$webStorageType.key($index); + """); + return result != null ? json.decode(result) : null; + } + + @override + void dispose() { + controller = null; + } +} + +/// Object specifying creation parameters for creating a [AndroidLocalStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformLocalStorageCreationParams] for +/// more information. +class AndroidLocalStorageCreationParams + extends PlatformLocalStorageCreationParams { + /// Creates a new [AndroidLocalStorageCreationParams] instance. + AndroidLocalStorageCreationParams(super.params); + + /// Creates a [AndroidLocalStorageCreationParams] instance based on [PlatformLocalStorageCreationParams]. + factory AndroidLocalStorageCreationParams.fromPlatformLocalStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformLocalStorageCreationParams params) { + return AndroidLocalStorageCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformLocalStorage} +class AndroidLocalStorage extends PlatformLocalStorage with AndroidStorage { + /// Constructs a [AndroidLocalStorage]. + AndroidLocalStorage(PlatformLocalStorageCreationParams params) + : super.implementation( + params is AndroidLocalStorageCreationParams + ? params + : AndroidLocalStorageCreationParams + .fromPlatformLocalStorageCreationParams(params), + ); + + /// Default storage + factory AndroidLocalStorage.defaultStorage( + {required PlatformInAppWebViewController? controller}) { + return AndroidLocalStorage(AndroidLocalStorageCreationParams( + PlatformLocalStorageCreationParams(PlatformStorageCreationParams( + controller: controller, + webStorageType: WebStorageType.LOCAL_STORAGE)))); + } + + @override + AndroidInAppWebViewController? get controller => + params.controller as AndroidInAppWebViewController?; +} + +/// Object specifying creation parameters for creating a [AndroidSessionStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformSessionStorageCreationParams] for +/// more information. +class AndroidSessionStorageCreationParams + extends PlatformSessionStorageCreationParams { + /// Creates a new [AndroidSessionStorageCreationParams] instance. + AndroidSessionStorageCreationParams(super.params); + + /// Creates a [AndroidSessionStorageCreationParams] instance based on [PlatformSessionStorageCreationParams]. + factory AndroidSessionStorageCreationParams.fromPlatformSessionStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformSessionStorageCreationParams params) { + return AndroidSessionStorageCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformSessionStorage} +class AndroidSessionStorage extends PlatformSessionStorage with AndroidStorage { + /// Constructs a [AndroidSessionStorage]. + AndroidSessionStorage(PlatformSessionStorageCreationParams params) + : super.implementation( + params is AndroidSessionStorageCreationParams + ? params + : AndroidSessionStorageCreationParams + .fromPlatformSessionStorageCreationParams(params), + ); + + /// Default storage + factory AndroidSessionStorage.defaultStorage( + {required PlatformInAppWebViewController? controller}) { + return AndroidSessionStorage(AndroidSessionStorageCreationParams( + PlatformSessionStorageCreationParams(PlatformStorageCreationParams( + controller: controller, + webStorageType: WebStorageType.SESSION_STORAGE)))); + } + + @override + AndroidInAppWebViewController? get controller => + params.controller as AndroidInAppWebViewController?; +} diff --git a/flutter_inappwebview_android/lib/src/web_storage/web_storage_manager.dart b/flutter_inappwebview_android/lib/src/web_storage/web_storage_manager.dart new file mode 100755 index 00000000..d5fc4562 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/web_storage/web_storage_manager.dart @@ -0,0 +1,116 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidWebStorageManager]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebStorageManagerCreationParams] for +/// more information. +@immutable +class AndroidWebStorageManagerCreationParams + extends PlatformWebStorageManagerCreationParams { + /// Creates a new [AndroidWebStorageManagerCreationParams] instance. + const AndroidWebStorageManagerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformWebStorageManagerCreationParams params, + ) : super(); + + /// Creates a [AndroidWebStorageManagerCreationParams] instance based on [PlatformWebStorageManagerCreationParams]. + factory AndroidWebStorageManagerCreationParams.fromPlatformWebStorageManagerCreationParams( + PlatformWebStorageManagerCreationParams params) { + return AndroidWebStorageManagerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebStorageManager} +class AndroidWebStorageManager extends PlatformWebStorageManager + with ChannelController { + /// Creates a new [AndroidWebStorageManager]. + AndroidWebStorageManager(PlatformWebStorageManagerCreationParams params) + : super.implementation( + params is AndroidWebStorageManagerCreationParams + ? params + : AndroidWebStorageManagerCreationParams + .fromPlatformWebStorageManagerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_webstoragemanager'); + handler = handleMethod; + initMethodCallHandler(); + } + + static AndroidWebStorageManager? _instance; + + ///Gets the WebStorage manager shared instance. + static AndroidWebStorageManager instance() { + return (_instance != null) ? _instance! : _init(); + } + + static AndroidWebStorageManager _init() { + _instance = AndroidWebStorageManager(AndroidWebStorageManagerCreationParams( + const PlatformWebStorageManagerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future> getOrigins() async { + List originsList = []; + + Map args = {}; + List> origins = + (await channel?.invokeMethod('getOrigins', args)) + ?.cast>() ?? + []; + + for (var origin in origins) { + originsList.add(WebStorageOrigin( + origin: origin["origin"], + quota: origin["quota"], + usage: origin["usage"])); + } + + return originsList; + } + + @override + Future deleteAllData() async { + Map args = {}; + await channel?.invokeMethod('deleteAllData', args); + } + + @override + Future deleteOrigin({required String origin}) async { + Map args = {}; + args.putIfAbsent("origin", () => origin); + await channel?.invokeMethod('deleteOrigin', args); + } + + @override + Future getQuotaForOrigin({required String origin}) async { + Map args = {}; + args.putIfAbsent("origin", () => origin); + return await channel?.invokeMethod('getQuotaForOrigin', args) ?? 0; + } + + @override + Future getUsageForOrigin({required String origin}) async { + Map args = {}; + args.putIfAbsent("origin", () => origin); + return await channel?.invokeMethod('getUsageForOrigin', args) ?? 0; + } + + @override + void dispose() { + // empty + } +} + +extension InternalWebStorageManager on AndroidWebStorageManager { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_android/lib/src/webview_asset_loader.dart b/flutter_inappwebview_android/lib/src/webview_asset_loader.dart new file mode 100644 index 00000000..db56f1a5 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/webview_asset_loader.dart @@ -0,0 +1,200 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidPathHandler]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformPathHandlerCreationParams] for +/// more information. +@immutable +class AndroidPathHandlerCreationParams + extends PlatformPathHandlerCreationParams { + /// Creates a new [AndroidPathHandlerCreationParams] instance. + AndroidPathHandlerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformPathHandlerCreationParams params, + ) : super(path: params.path); + + /// Creates a [AndroidPathHandlerCreationParams] instance based on [PlatformPathHandlerCreationParams]. + factory AndroidPathHandlerCreationParams.fromPlatformPathHandlerCreationParams( + PlatformPathHandlerCreationParams params) { + return AndroidPathHandlerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformPathHandler} +abstract class AndroidPathHandler + implements ChannelController, PlatformPathHandler { + final String _id = IdGenerator.generate(); + + @override + late final PlatformPathHandlerEvents? eventHandler; + + @override + late final String path; + + void _init(PlatformPathHandlerCreationParams params) { + this.path = params.path; + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_custompathhandler_${_id}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "handle": + String path = call.arguments["path"]; + return (await eventHandler?.handle(path))?.toMap(); + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + @override + Map toMap() { + return {"path": path, "type": type, "id": _id}; + } + + @override + Map toJson() { + return toMap(); + } + + @override + String toString() { + return 'AndroidPathHandler{path: $path, type: $type}'; + } + + @override + void dispose() { + disposeChannel(); + eventHandler = null; + } +} + +/// Object specifying creation parameters for creating a [AndroidAssetsPathHandler]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformAssetsPathHandlerCreationParams] for +/// more information. +@immutable +class AndroidAssetsPathHandlerCreationParams + extends PlatformAssetsPathHandlerCreationParams { + /// Creates a new [AndroidAssetsPathHandlerCreationParams] instance. + AndroidAssetsPathHandlerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformAssetsPathHandlerCreationParams params, + ) : super(params); + + /// Creates a [AndroidAssetsPathHandlerCreationParams] instance based on [PlatformAssetsPathHandlerCreationParams]. + factory AndroidAssetsPathHandlerCreationParams.fromPlatformAssetsPathHandlerCreationParams( + PlatformAssetsPathHandlerCreationParams params) { + return AndroidAssetsPathHandlerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformAssetsPathHandler} +class AndroidAssetsPathHandler extends PlatformAssetsPathHandler + with AndroidPathHandler, ChannelController { + /// Constructs a [AndroidAssetsPathHandler]. + AndroidAssetsPathHandler(PlatformAssetsPathHandlerCreationParams params) + : super.implementation( + params is AndroidAssetsPathHandlerCreationParams + ? params + : AndroidAssetsPathHandlerCreationParams + .fromPlatformAssetsPathHandlerCreationParams(params), + ) { + _init(params); + } +} + +/// Object specifying creation parameters for creating a [AndroidResourcesPathHandler]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformResourcesPathHandlerCreationParams] for +/// more information. +@immutable +class AndroidResourcesPathHandlerCreationParams + extends PlatformResourcesPathHandlerCreationParams { + /// Creates a new [AndroidResourcesPathHandlerCreationParams] instance. + AndroidResourcesPathHandlerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformResourcesPathHandlerCreationParams params, + ) : super(params); + + /// Creates a [AndroidResourcesPathHandlerCreationParams] instance based on [PlatformResourcesPathHandlerCreationParams]. + factory AndroidResourcesPathHandlerCreationParams.fromPlatformResourcesPathHandlerCreationParams( + PlatformResourcesPathHandlerCreationParams params) { + return AndroidResourcesPathHandlerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformResourcesPathHandler} +class AndroidResourcesPathHandler extends PlatformResourcesPathHandler + with AndroidPathHandler, ChannelController { + /// Constructs a [AndroidResourcesPathHandler]. + AndroidResourcesPathHandler(PlatformResourcesPathHandlerCreationParams params) + : super.implementation( + params is AndroidResourcesPathHandlerCreationParams + ? params + : AndroidResourcesPathHandlerCreationParams + .fromPlatformResourcesPathHandlerCreationParams(params), + ) { + _init(params); + } +} + +/// Object specifying creation parameters for creating a [AndroidInternalStoragePathHandler]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInternalStoragePathHandlerCreationParams] for +/// more information. +@immutable +class AndroidInternalStoragePathHandlerCreationParams + extends PlatformInternalStoragePathHandlerCreationParams { + /// Creates a new [AndroidInternalStoragePathHandlerCreationParams] instance. + AndroidInternalStoragePathHandlerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformInternalStoragePathHandlerCreationParams params, + ) : super(params, directory: params.directory); + + /// Creates a [AndroidInternalStoragePathHandlerCreationParams] instance based on [PlatformInternalStoragePathHandlerCreationParams]. + factory AndroidInternalStoragePathHandlerCreationParams.fromPlatformInternalStoragePathHandlerCreationParams( + PlatformInternalStoragePathHandlerCreationParams params) { + return AndroidInternalStoragePathHandlerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInternalStoragePathHandler} +class AndroidInternalStoragePathHandler + extends PlatformInternalStoragePathHandler + with AndroidPathHandler, ChannelController { + /// Constructs a [AndroidInternalStoragePathHandler]. + AndroidInternalStoragePathHandler( + PlatformInternalStoragePathHandlerCreationParams params) + : super.implementation( + params is AndroidInternalStoragePathHandlerCreationParams + ? params + : AndroidInternalStoragePathHandlerCreationParams + .fromPlatformInternalStoragePathHandlerCreationParams(params), + ) { + _init(params); + } + + AndroidInternalStoragePathHandlerCreationParams get _internalParams => + params as AndroidInternalStoragePathHandlerCreationParams; + + @override + String get directory => _internalParams.directory; + + @override + Map toMap() { + return {...toMap(), 'directory': directory}; + } +} diff --git a/flutter_inappwebview_android/lib/src/webview_feature.dart b/flutter_inappwebview_android/lib/src/webview_feature.dart new file mode 100644 index 00000000..9b149510 --- /dev/null +++ b/flutter_inappwebview_android/lib/src/webview_feature.dart @@ -0,0 +1,89 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [AndroidWebViewFeature]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebViewFeatureCreationParams] for +/// more information. +@immutable +class AndroidWebViewFeatureCreationParams + extends PlatformWebViewFeatureCreationParams { + /// Creates a new [AndroidWebViewFeatureCreationParams] instance. + const AndroidWebViewFeatureCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformWebViewFeatureCreationParams params, + ) : super(); + + /// Creates a [AndroidWebViewFeatureCreationParams] instance based on [PlatformWebViewFeatureCreationParams]. + factory AndroidWebViewFeatureCreationParams.fromPlatformWebViewFeatureCreationParams( + PlatformWebViewFeatureCreationParams params) { + return AndroidWebViewFeatureCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebViewFeature} +class AndroidWebViewFeature extends PlatformWebViewFeature + with ChannelController { + /// Creates a new [AndroidWebViewFeature]. + AndroidWebViewFeature(PlatformWebViewFeatureCreationParams params) + : super.implementation( + params is AndroidWebViewFeatureCreationParams + ? params + : AndroidWebViewFeatureCreationParams + .fromPlatformWebViewFeatureCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_webviewfeature'); + handler = handleMethod; + initMethodCallHandler(); + } + + factory AndroidWebViewFeature.static() { + return instance(); + } + + static AndroidWebViewFeature? _instance; + + ///Gets the [AndroidWebViewFeature] shared instance. + static AndroidWebViewFeature instance() { + return (_instance != null) ? _instance! : _init(); + } + + static AndroidWebViewFeature _init() { + _instance = AndroidWebViewFeature(AndroidWebViewFeatureCreationParams( + const PlatformWebViewFeatureCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future isFeatureSupported(WebViewFeature feature) async { + Map args = {}; + args.putIfAbsent("feature", () => feature.toNativeValue()); + return await channel?.invokeMethod('isFeatureSupported', args) ?? + false; + } + + @override + Future isStartupFeatureSupported(WebViewFeature startupFeature) async { + Map args = {}; + args.putIfAbsent("startupFeature", () => startupFeature.toNativeValue()); + return await channel?.invokeMethod( + 'isStartupFeatureSupported', args) ?? + false; + } + + @override + void dispose() { + // empty + } +} + +extension InternalWebViewFeature on AndroidWebViewFeature { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_android/pubspec.yaml b/flutter_inappwebview_android/pubspec.yaml new file mode 100644 index 00000000..b0c2e745 --- /dev/null +++ b/flutter_inappwebview_android/pubspec.yaml @@ -0,0 +1,81 @@ +name: flutter_inappwebview_android +description: Android implementation of the flutter_inappwebview plugin. +version: 1.0.1 +homepage: https://inappwebview.dev/ +repository: https://github.com/pichillilorenzo/flutter_inappwebview/tree/master/flutter_inappwebview_android +issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues +topics: + - html + - webview + - webview-flutter + - inappwebview + - browser + +environment: + sdk: ">=2.17.0 <4.0.0" + flutter: ">=3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_inappwebview_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + plugin_platform_interface: ^2.0.2 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + implements: flutter_inappwebview + platforms: + android: + package: com.pichillilorenzo.flutter_inappwebview_android + pluginClass: InAppWebViewFlutterPlugin + dartPluginClass: AndroidInAppWebViewPlatform + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter_inappwebview_android/test/flutter_inappwebview_android_test.dart b/flutter_inappwebview_android/test/flutter_inappwebview_android_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_ios/.gitignore b/flutter_inappwebview_ios/.gitignore new file mode 100644 index 00000000..96486fd9 --- /dev/null +++ b/flutter_inappwebview_ios/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/flutter_inappwebview_ios/.metadata b/flutter_inappwebview_ios/.metadata new file mode 100644 index 00000000..1d00c64b --- /dev/null +++ b/flutter_inappwebview_ios/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e + base_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e + - platform: ios + create_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e + base_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/flutter_inappwebview_ios/CHANGELOG.md b/flutter_inappwebview_ios/CHANGELOG.md new file mode 100644 index 00000000..91460049 --- /dev/null +++ b/flutter_inappwebview_ios/CHANGELOG.md @@ -0,0 +1,7 @@ +## 1.0.1 + +- Updated README + +## 1.0.0 + +Initial release. diff --git a/flutter_inappwebview_ios/LICENSE b/flutter_inappwebview_ios/LICENSE new file mode 100644 index 00000000..4dada16d --- /dev/null +++ b/flutter_inappwebview_ios/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Lorenzo Pichilli + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/flutter_inappwebview_ios/README.md b/flutter_inappwebview_ios/README.md new file mode 100644 index 00000000..6121ff51 --- /dev/null +++ b/flutter_inappwebview_ios/README.md @@ -0,0 +1,13 @@ +# flutter\_inappwebview\_ios + +The Apple iOS WKWebView implementation of [`flutter_inappwebview`](https://pub.dev/packages/flutter_inappwebview). + +## Usage + +This package is [endorsed](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin), +which means you can simply use `flutter_inappwebview` +normally. This package will be automatically included in your app when you do, +so you do not need to add it to your `pubspec.yaml`. + +However, if you `import` this package to use any of its APIs directly, you +should add it to your `pubspec.yaml` as usual. \ No newline at end of file diff --git a/flutter_inappwebview_ios/analysis_options.yaml b/flutter_inappwebview_ios/analysis_options.yaml new file mode 100644 index 00000000..84964c30 --- /dev/null +++ b/flutter_inappwebview_ios/analysis_options.yaml @@ -0,0 +1,15 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + constant_identifier_names: ignore + deprecated_member_use_from_same_package: ignore + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options +analyzer: + errors: + deprecated_member_use: ignore + deprecated_member_use_from_same_package: ignore + unnecessary_cast: ignore + unnecessary_import: ignore diff --git a/flutter_inappwebview_ios/build.yaml b/flutter_inappwebview_ios/build.yaml new file mode 100644 index 00000000..e2b3acf3 --- /dev/null +++ b/flutter_inappwebview_ios/build.yaml @@ -0,0 +1,5 @@ +targets: + $default: + sources: + exclude: + - example/**.dart diff --git a/flutter_inappwebview_ios/example/.gitignore b/flutter_inappwebview_ios/example/.gitignore new file mode 100644 index 00000000..24476c5d --- /dev/null +++ b/flutter_inappwebview_ios/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/flutter_inappwebview_ios/example/README.md b/flutter_inappwebview_ios/example/README.md new file mode 100644 index 00000000..972be4ed --- /dev/null +++ b/flutter_inappwebview_ios/example/README.md @@ -0,0 +1,16 @@ +# flutter_inappwebview_ios_example + +Demonstrates how to use the flutter_inappwebview_ios plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter_inappwebview_ios/example/analysis_options.yaml b/flutter_inappwebview_ios/example/analysis_options.yaml new file mode 100644 index 00000000..0d290213 --- /dev/null +++ b/flutter_inappwebview_ios/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/flutter_inappwebview_ios/example/integration_test/plugin_integration_test.dart b/flutter_inappwebview_ios/example/integration_test/plugin_integration_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_ios/example/ios/.gitignore b/flutter_inappwebview_ios/example/ios/.gitignore new file mode 100644 index 00000000..7a7f9873 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/flutter_inappwebview_ios/example/ios/Flutter/AppFrameworkInfo.plist b/flutter_inappwebview_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..9625e105 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 11.0 + + diff --git a/flutter_inappwebview_ios/example/ios/Flutter/Debug.xcconfig b/flutter_inappwebview_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..ec97fc6f --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter_inappwebview_ios/example/ios/Flutter/Release.xcconfig b/flutter_inappwebview_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..c4855bfe --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter_inappwebview_ios/example/ios/Podfile b/flutter_inappwebview_ios/example/ios/Podfile new file mode 100644 index 00000000..fdcc671e --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '11.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.pbxproj b/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..123bf9e5 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,614 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807E294A63A400263BE5 /* Frameworks */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1430; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterInappwebviewIosExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterInappwebviewIosExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterInappwebviewIosExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterInappwebviewIosExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterInappwebviewIosExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterInappwebviewIosExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..87131a09 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_inappwebview_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/flutter_inappwebview_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter_inappwebview_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter_inappwebview_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter_inappwebview_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter_inappwebview_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter_inappwebview_ios/example/ios/Runner/AppDelegate.swift b/flutter_inappwebview_ios/example/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..70693e4a --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000..dc9ada47 Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..7353c41e Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..797d452e Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..6ed2d933 Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..4cd7b009 Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..fe730945 Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..321773cd Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..797d452e Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..502f463a Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..0ec30343 Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..0ec30343 Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..e9f5fea2 Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..84ac32ae Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..8953cba0 Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..0467bf12 Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..0bedcf2f --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/flutter_inappwebview_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/flutter_inappwebview_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_inappwebview_ios/example/ios/Runner/Base.lproj/Main.storyboard b/flutter_inappwebview_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_inappwebview_ios/example/ios/Runner/Info.plist b/flutter_inappwebview_ios/example/ios/Runner/Info.plist new file mode 100644 index 00000000..5aceee7b --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Flutter Inappwebview Ios + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + flutter_inappwebview_ios_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/flutter_inappwebview_ios/example/ios/Runner/Runner-Bridging-Header.h b/flutter_inappwebview_ios/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/flutter_inappwebview_ios/example/ios/RunnerTests/RunnerTests.swift b/flutter_inappwebview_ios/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..3fff3af3 --- /dev/null +++ b/flutter_inappwebview_ios/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,26 @@ +import Flutter +import UIKit +import XCTest + +@testable import flutter_inappwebview_ios + +// This demonstrates a simple unit test of the Swift portion of this plugin's implementation. +// +// See https://developer.apple.com/documentation/xctest for more information about using XCTest. + +class RunnerTests: XCTestCase { + + func testGetPlatformVersion() { + let plugin = FlutterInappwebviewIosPlugin() + + let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) + + let resultExpectation = expectation(description: "result block must be called.") + plugin.handle(call) { result in + XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion) + resultExpectation.fulfill() + } + waitForExpectations(timeout: 1) + } + +} diff --git a/flutter_inappwebview_ios/example/lib/main.dart b/flutter_inappwebview_ios/example/lib/main.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_ios/example/pubspec.lock b/flutter_inappwebview_ios/example/pubspec.lock new file mode 100644 index 00000000..e065f713 --- /dev/null +++ b/flutter_inappwebview_ios/example/pubspec.lock @@ -0,0 +1,283 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" + source: hosted + version: "1.17.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "5f80fd30e208ddded7dbbcd0d569e7995f9f63d45ea3f548d8dd4c0b473fb4c8" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter_inappwebview_ios: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "1.0.0" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: c221a6788c73421284208e3449cad07d8c65fdace120322483181a2b94d95697 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 + url: "https://pub.dev" + source: hosted + version: "2.1.7" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + url: "https://pub.dev" + source: hosted + version: "11.7.1" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + url: "https://pub.dev" + source: hosted + version: "3.0.2" +sdks: + dart: ">=3.1.4 <4.0.0" + flutter: ">=3.0.0" diff --git a/flutter_inappwebview_ios/example/pubspec.yaml b/flutter_inappwebview_ios/example/pubspec.yaml new file mode 100644 index 00000000..3eef0f0a --- /dev/null +++ b/flutter_inappwebview_ios/example/pubspec.yaml @@ -0,0 +1,85 @@ +name: flutter_inappwebview_ios_example +description: Demonstrates how to use the flutter_inappwebview_ios plugin. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=3.1.4 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + flutter_inappwebview_ios: + # When depending on this package from a real application you should use: + # flutter_inappwebview_ios: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + integration_test: + sdk: flutter + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter_inappwebview_ios/example/test/widget_test.dart b/flutter_inappwebview_ios/example/test/widget_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/ios/.gitignore b/flutter_inappwebview_ios/ios/.gitignore similarity index 100% rename from ios/.gitignore rename to flutter_inappwebview_ios/ios/.gitignore diff --git a/flutter_inappwebview_ios/ios/Assets/.gitkeep b/flutter_inappwebview_ios/ios/Assets/.gitkeep new file mode 100755 index 00000000..e69de29b diff --git a/ios/Classes/CredentialDatabase.swift b/flutter_inappwebview_ios/ios/Classes/CredentialDatabase.swift similarity index 100% rename from ios/Classes/CredentialDatabase.swift rename to flutter_inappwebview_ios/ios/Classes/CredentialDatabase.swift diff --git a/ios/Classes/FindInteraction/FindInteractionChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/FindInteraction/FindInteractionChannelDelegate.swift similarity index 100% rename from ios/Classes/FindInteraction/FindInteractionChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/FindInteraction/FindInteractionChannelDelegate.swift diff --git a/ios/Classes/FindInteraction/FindInteractionController.swift b/flutter_inappwebview_ios/ios/Classes/FindInteraction/FindInteractionController.swift similarity index 100% rename from ios/Classes/FindInteraction/FindInteractionController.swift rename to flutter_inappwebview_ios/ios/Classes/FindInteraction/FindInteractionController.swift diff --git a/ios/Classes/FindInteraction/FindInteractionSettings.swift b/flutter_inappwebview_ios/ios/Classes/FindInteraction/FindInteractionSettings.swift similarity index 100% rename from ios/Classes/FindInteraction/FindInteractionSettings.swift rename to flutter_inappwebview_ios/ios/Classes/FindInteraction/FindInteractionSettings.swift diff --git a/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift b/flutter_inappwebview_ios/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift similarity index 100% rename from ios/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift rename to flutter_inappwebview_ios/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift diff --git a/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift b/flutter_inappwebview_ios/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift similarity index 100% rename from ios/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift rename to flutter_inappwebview_ios/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift diff --git a/ios/Classes/HeadlessInAppWebView/HeadlessWebViewChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/HeadlessInAppWebView/HeadlessWebViewChannelDelegate.swift similarity index 100% rename from ios/Classes/HeadlessInAppWebView/HeadlessWebViewChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/HeadlessInAppWebView/HeadlessWebViewChannelDelegate.swift diff --git a/ios/Classes/ISettings.swift b/flutter_inappwebview_ios/ios/Classes/ISettings.swift similarity index 100% rename from ios/Classes/ISettings.swift rename to flutter_inappwebview_ios/ios/Classes/ISettings.swift diff --git a/ios/Classes/InAppBrowser/InAppBrowserChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserChannelDelegate.swift similarity index 100% rename from ios/Classes/InAppBrowser/InAppBrowserChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserChannelDelegate.swift diff --git a/ios/Classes/InAppBrowser/InAppBrowserDelegate.swift b/flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserDelegate.swift similarity index 100% rename from ios/Classes/InAppBrowser/InAppBrowserDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserDelegate.swift diff --git a/ios/Classes/InAppBrowser/InAppBrowserManager.swift b/flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserManager.swift similarity index 100% rename from ios/Classes/InAppBrowser/InAppBrowserManager.swift rename to flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserManager.swift diff --git a/ios/Classes/InAppBrowser/InAppBrowserNavigationController.swift b/flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserNavigationController.swift similarity index 100% rename from ios/Classes/InAppBrowser/InAppBrowserNavigationController.swift rename to flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserNavigationController.swift diff --git a/ios/Classes/InAppBrowser/InAppBrowserSettings.swift b/flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserSettings.swift similarity index 100% rename from ios/Classes/InAppBrowser/InAppBrowserSettings.swift rename to flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserSettings.swift diff --git a/ios/Classes/InAppBrowser/InAppBrowserWebViewController.swift b/flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserWebViewController.swift similarity index 100% rename from ios/Classes/InAppBrowser/InAppBrowserWebViewController.swift rename to flutter_inappwebview_ios/ios/Classes/InAppBrowser/InAppBrowserWebViewController.swift diff --git a/ios/Classes/InAppWebView/ContextMenuSettings.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/ContextMenuSettings.swift similarity index 100% rename from ios/Classes/InAppWebView/ContextMenuSettings.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/ContextMenuSettings.swift diff --git a/ios/Classes/InAppWebView/CustomSchemeHandler.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/CustomSchemeHandler.swift similarity index 100% rename from ios/Classes/InAppWebView/CustomSchemeHandler.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/CustomSchemeHandler.swift diff --git a/ios/Classes/InAppWebView/FlutterWebViewController.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/FlutterWebViewController.swift similarity index 100% rename from ios/Classes/InAppWebView/FlutterWebViewController.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/FlutterWebViewController.swift diff --git a/ios/Classes/InAppWebView/FlutterWebViewFactory.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/FlutterWebViewFactory.swift similarity index 100% rename from ios/Classes/InAppWebView/FlutterWebViewFactory.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/FlutterWebViewFactory.swift diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/InAppWebView.swift similarity index 100% rename from ios/Classes/InAppWebView/InAppWebView.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/InAppWebView.swift diff --git a/ios/Classes/InAppWebView/InAppWebViewManager.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/InAppWebViewManager.swift similarity index 100% rename from ios/Classes/InAppWebView/InAppWebViewManager.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/InAppWebViewManager.swift diff --git a/ios/Classes/InAppWebView/InAppWebViewSettings.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/InAppWebViewSettings.swift similarity index 100% rename from ios/Classes/InAppWebView/InAppWebViewSettings.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/InAppWebViewSettings.swift diff --git a/ios/Classes/InAppWebView/WebMessage/WebMessageChannel.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/WebMessage/WebMessageChannel.swift similarity index 100% rename from ios/Classes/InAppWebView/WebMessage/WebMessageChannel.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/WebMessage/WebMessageChannel.swift diff --git a/ios/Classes/InAppWebView/WebMessage/WebMessageChannelChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/WebMessage/WebMessageChannelChannelDelegate.swift similarity index 100% rename from ios/Classes/InAppWebView/WebMessage/WebMessageChannelChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/WebMessage/WebMessageChannelChannelDelegate.swift diff --git a/ios/Classes/InAppWebView/WebMessage/WebMessageListener.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/WebMessage/WebMessageListener.swift similarity index 100% rename from ios/Classes/InAppWebView/WebMessage/WebMessageListener.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/WebMessage/WebMessageListener.swift diff --git a/ios/Classes/InAppWebView/WebMessage/WebMessageListenerChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/WebMessage/WebMessageListenerChannelDelegate.swift similarity index 100% rename from ios/Classes/InAppWebView/WebMessage/WebMessageListenerChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/WebMessage/WebMessageListenerChannelDelegate.swift diff --git a/ios/Classes/InAppWebView/WebViewChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/WebViewChannelDelegate.swift similarity index 100% rename from ios/Classes/InAppWebView/WebViewChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/WebViewChannelDelegate.swift diff --git a/ios/Classes/InAppWebView/WebViewChannelDelegateMethods.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/WebViewChannelDelegateMethods.swift similarity index 100% rename from ios/Classes/InAppWebView/WebViewChannelDelegateMethods.swift rename to flutter_inappwebview_ios/ios/Classes/InAppWebView/WebViewChannelDelegateMethods.swift diff --git a/ios/Classes/InAppWebViewFlutterPlugin.h b/flutter_inappwebview_ios/ios/Classes/InAppWebViewFlutterPlugin.h similarity index 100% rename from ios/Classes/InAppWebViewFlutterPlugin.h rename to flutter_inappwebview_ios/ios/Classes/InAppWebViewFlutterPlugin.h diff --git a/ios/Classes/InAppWebViewFlutterPlugin.m b/flutter_inappwebview_ios/ios/Classes/InAppWebViewFlutterPlugin.m similarity index 86% rename from ios/Classes/InAppWebViewFlutterPlugin.m rename to flutter_inappwebview_ios/ios/Classes/InAppWebViewFlutterPlugin.m index c16dedb4..adcf1123 100755 --- a/ios/Classes/InAppWebViewFlutterPlugin.m +++ b/flutter_inappwebview_ios/ios/Classes/InAppWebViewFlutterPlugin.m @@ -16,13 +16,13 @@ */ #import "InAppWebViewFlutterPlugin.h" -#if __has_include() -#import +#if __has_include() +#import #else // Support project import fallback if the generated compatibility header // is not copied when this plugin is created as a library. // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 -#import "flutter_inappwebview-Swift.h" +#import "flutter_inappwebview_ios-Swift.h" #endif @implementation InAppWebViewFlutterPlugin : NSObject diff --git a/ios/Classes/LeakAvoider.swift b/flutter_inappwebview_ios/ios/Classes/LeakAvoider.swift similarity index 100% rename from ios/Classes/LeakAvoider.swift rename to flutter_inappwebview_ios/ios/Classes/LeakAvoider.swift diff --git a/ios/Classes/MyCookieManager.swift b/flutter_inappwebview_ios/ios/Classes/MyCookieManager.swift similarity index 100% rename from ios/Classes/MyCookieManager.swift rename to flutter_inappwebview_ios/ios/Classes/MyCookieManager.swift diff --git a/ios/Classes/MyWebStorageManager.swift b/flutter_inappwebview_ios/ios/Classes/MyWebStorageManager.swift similarity index 100% rename from ios/Classes/MyWebStorageManager.swift rename to flutter_inappwebview_ios/ios/Classes/MyWebStorageManager.swift diff --git a/ios/Classes/PlatformUtil.swift b/flutter_inappwebview_ios/ios/Classes/PlatformUtil.swift similarity index 100% rename from ios/Classes/PlatformUtil.swift rename to flutter_inappwebview_ios/ios/Classes/PlatformUtil.swift diff --git a/ios/Classes/PluginScriptsJS/CallAsyncJavaScriptBelowIOS14WrapperJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/CallAsyncJavaScriptBelowIOS14WrapperJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/CallAsyncJavaScriptBelowIOS14WrapperJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/CallAsyncJavaScriptBelowIOS14WrapperJS.swift diff --git a/ios/Classes/PluginScriptsJS/ConsoleLogJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/ConsoleLogJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/ConsoleLogJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/ConsoleLogJS.swift diff --git a/ios/Classes/PluginScriptsJS/EnableViewportScaleJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/EnableViewportScaleJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/EnableViewportScaleJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/EnableViewportScaleJS.swift diff --git a/ios/Classes/PluginScriptsJS/FindElementsAtPointJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/FindElementsAtPointJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/FindElementsAtPointJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/FindElementsAtPointJS.swift diff --git a/ios/Classes/PluginScriptsJS/FindTextHighlightJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/FindTextHighlightJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/FindTextHighlightJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/FindTextHighlightJS.swift diff --git a/ios/Classes/PluginScriptsJS/InterceptAjaxRequestJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/InterceptAjaxRequestJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/InterceptAjaxRequestJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/InterceptAjaxRequestJS.swift diff --git a/ios/Classes/PluginScriptsJS/InterceptFetchRequestJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/InterceptFetchRequestJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/InterceptFetchRequestJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/InterceptFetchRequestJS.swift diff --git a/ios/Classes/PluginScriptsJS/JavaScriptBridgeJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/JavaScriptBridgeJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/JavaScriptBridgeJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/JavaScriptBridgeJS.swift diff --git a/ios/Classes/PluginScriptsJS/LastTouchedAnchorOrImageJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/LastTouchedAnchorOrImageJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/LastTouchedAnchorOrImageJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/LastTouchedAnchorOrImageJS.swift diff --git a/ios/Classes/PluginScriptsJS/OnLoadResourceJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/OnLoadResourceJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/OnLoadResourceJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/OnLoadResourceJS.swift diff --git a/ios/Classes/PluginScriptsJS/OnWindowBlurEventJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/OnWindowBlurEventJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/OnWindowBlurEventJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/OnWindowBlurEventJS.swift diff --git a/ios/Classes/PluginScriptsJS/OnWindowFocusEventJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/OnWindowFocusEventJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/OnWindowFocusEventJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/OnWindowFocusEventJS.swift diff --git a/ios/Classes/PluginScriptsJS/OriginalViewPortMetaTagContentJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/OriginalViewPortMetaTagContentJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/OriginalViewPortMetaTagContentJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/OriginalViewPortMetaTagContentJS.swift diff --git a/ios/Classes/PluginScriptsJS/PluginScriptsUtil.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/PluginScriptsUtil.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/PluginScriptsUtil.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/PluginScriptsUtil.swift diff --git a/ios/Classes/PluginScriptsJS/PrintJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/PrintJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/PrintJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/PrintJS.swift diff --git a/ios/Classes/PluginScriptsJS/PromisePolyfillJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/PromisePolyfillJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/PromisePolyfillJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/PromisePolyfillJS.swift diff --git a/ios/Classes/PluginScriptsJS/SupportZoomJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/SupportZoomJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/SupportZoomJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/SupportZoomJS.swift diff --git a/ios/Classes/PluginScriptsJS/WebMessageChannelJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/WebMessageChannelJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/WebMessageChannelJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/WebMessageChannelJS.swift diff --git a/ios/Classes/PluginScriptsJS/WebMessageListenerJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/WebMessageListenerJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/WebMessageListenerJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/WebMessageListenerJS.swift diff --git a/ios/Classes/PluginScriptsJS/WindowIdJS.swift b/flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/WindowIdJS.swift similarity index 100% rename from ios/Classes/PluginScriptsJS/WindowIdJS.swift rename to flutter_inappwebview_ios/ios/Classes/PluginScriptsJS/WindowIdJS.swift diff --git a/ios/Classes/PrintJob/CustomUIPrintPageRenderer.swift b/flutter_inappwebview_ios/ios/Classes/PrintJob/CustomUIPrintPageRenderer.swift similarity index 100% rename from ios/Classes/PrintJob/CustomUIPrintPageRenderer.swift rename to flutter_inappwebview_ios/ios/Classes/PrintJob/CustomUIPrintPageRenderer.swift diff --git a/ios/Classes/PrintJob/PrintAttributes.swift b/flutter_inappwebview_ios/ios/Classes/PrintJob/PrintAttributes.swift similarity index 100% rename from ios/Classes/PrintJob/PrintAttributes.swift rename to flutter_inappwebview_ios/ios/Classes/PrintJob/PrintAttributes.swift diff --git a/ios/Classes/PrintJob/PrintJobChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/PrintJob/PrintJobChannelDelegate.swift similarity index 100% rename from ios/Classes/PrintJob/PrintJobChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/PrintJob/PrintJobChannelDelegate.swift diff --git a/ios/Classes/PrintJob/PrintJobController.swift b/flutter_inappwebview_ios/ios/Classes/PrintJob/PrintJobController.swift similarity index 100% rename from ios/Classes/PrintJob/PrintJobController.swift rename to flutter_inappwebview_ios/ios/Classes/PrintJob/PrintJobController.swift diff --git a/ios/Classes/PrintJob/PrintJobInfo.swift b/flutter_inappwebview_ios/ios/Classes/PrintJob/PrintJobInfo.swift similarity index 100% rename from ios/Classes/PrintJob/PrintJobInfo.swift rename to flutter_inappwebview_ios/ios/Classes/PrintJob/PrintJobInfo.swift diff --git a/ios/Classes/PrintJob/PrintJobManager.swift b/flutter_inappwebview_ios/ios/Classes/PrintJob/PrintJobManager.swift similarity index 100% rename from ios/Classes/PrintJob/PrintJobManager.swift rename to flutter_inappwebview_ios/ios/Classes/PrintJob/PrintJobManager.swift diff --git a/ios/Classes/PrintJob/PrintJobSettings.swift b/flutter_inappwebview_ios/ios/Classes/PrintJob/PrintJobSettings.swift similarity index 100% rename from ios/Classes/PrintJob/PrintJobSettings.swift rename to flutter_inappwebview_ios/ios/Classes/PrintJob/PrintJobSettings.swift diff --git a/ios/Classes/PullToRefresh/PullToRefreshChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/PullToRefresh/PullToRefreshChannelDelegate.swift similarity index 100% rename from ios/Classes/PullToRefresh/PullToRefreshChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/PullToRefresh/PullToRefreshChannelDelegate.swift diff --git a/ios/Classes/PullToRefresh/PullToRefreshControl.swift b/flutter_inappwebview_ios/ios/Classes/PullToRefresh/PullToRefreshControl.swift similarity index 100% rename from ios/Classes/PullToRefresh/PullToRefreshControl.swift rename to flutter_inappwebview_ios/ios/Classes/PullToRefresh/PullToRefreshControl.swift diff --git a/ios/Classes/PullToRefresh/PullToRefreshDelegate.swift b/flutter_inappwebview_ios/ios/Classes/PullToRefresh/PullToRefreshDelegate.swift similarity index 100% rename from ios/Classes/PullToRefresh/PullToRefreshDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/PullToRefresh/PullToRefreshDelegate.swift diff --git a/ios/Classes/PullToRefresh/PullToRefreshSettings.swift b/flutter_inappwebview_ios/ios/Classes/PullToRefresh/PullToRefreshSettings.swift similarity index 100% rename from ios/Classes/PullToRefresh/PullToRefreshSettings.swift rename to flutter_inappwebview_ios/ios/Classes/PullToRefresh/PullToRefreshSettings.swift diff --git a/ios/Classes/SafariViewController/ChromeSafariBrowserManager.swift b/flutter_inappwebview_ios/ios/Classes/SafariViewController/ChromeSafariBrowserManager.swift similarity index 100% rename from ios/Classes/SafariViewController/ChromeSafariBrowserManager.swift rename to flutter_inappwebview_ios/ios/Classes/SafariViewController/ChromeSafariBrowserManager.swift diff --git a/ios/Classes/SafariViewController/CustomUIActivity.swift b/flutter_inappwebview_ios/ios/Classes/SafariViewController/CustomUIActivity.swift similarity index 100% rename from ios/Classes/SafariViewController/CustomUIActivity.swift rename to flutter_inappwebview_ios/ios/Classes/SafariViewController/CustomUIActivity.swift diff --git a/ios/Classes/SafariViewController/SafariBrowserSettings.swift b/flutter_inappwebview_ios/ios/Classes/SafariViewController/SafariBrowserSettings.swift similarity index 100% rename from ios/Classes/SafariViewController/SafariBrowserSettings.swift rename to flutter_inappwebview_ios/ios/Classes/SafariViewController/SafariBrowserSettings.swift diff --git a/ios/Classes/SafariViewController/SafariViewController.swift b/flutter_inappwebview_ios/ios/Classes/SafariViewController/SafariViewController.swift similarity index 100% rename from ios/Classes/SafariViewController/SafariViewController.swift rename to flutter_inappwebview_ios/ios/Classes/SafariViewController/SafariViewController.swift diff --git a/ios/Classes/SafariViewController/SafariViewControllerChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/SafariViewController/SafariViewControllerChannelDelegate.swift similarity index 100% rename from ios/Classes/SafariViewController/SafariViewControllerChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/SafariViewController/SafariViewControllerChannelDelegate.swift diff --git a/ios/Classes/SwiftFlutterPlugin.swift b/flutter_inappwebview_ios/ios/Classes/SwiftFlutterPlugin.swift similarity index 100% rename from ios/Classes/SwiftFlutterPlugin.swift rename to flutter_inappwebview_ios/ios/Classes/SwiftFlutterPlugin.swift diff --git a/ios/Classes/Types/ActivityButton.swift b/flutter_inappwebview_ios/ios/Classes/Types/ActivityButton.swift similarity index 100% rename from ios/Classes/Types/ActivityButton.swift rename to flutter_inappwebview_ios/ios/Classes/Types/ActivityButton.swift diff --git a/ios/Classes/Types/BaseCallbackResult.swift b/flutter_inappwebview_ios/ios/Classes/Types/BaseCallbackResult.swift similarity index 100% rename from ios/Classes/Types/BaseCallbackResult.swift rename to flutter_inappwebview_ios/ios/Classes/Types/BaseCallbackResult.swift diff --git a/ios/Classes/Types/CGRect.swift b/flutter_inappwebview_ios/ios/Classes/Types/CGRect.swift similarity index 100% rename from ios/Classes/Types/CGRect.swift rename to flutter_inappwebview_ios/ios/Classes/Types/CGRect.swift diff --git a/ios/Classes/Types/CGSize.swift b/flutter_inappwebview_ios/ios/Classes/Types/CGSize.swift similarity index 100% rename from ios/Classes/Types/CGSize.swift rename to flutter_inappwebview_ios/ios/Classes/Types/CGSize.swift diff --git a/ios/Classes/Types/CallbackResult.swift b/flutter_inappwebview_ios/ios/Classes/Types/CallbackResult.swift similarity index 100% rename from ios/Classes/Types/CallbackResult.swift rename to flutter_inappwebview_ios/ios/Classes/Types/CallbackResult.swift diff --git a/ios/Classes/Types/ChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/Types/ChannelDelegate.swift similarity index 100% rename from ios/Classes/Types/ChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/Types/ChannelDelegate.swift diff --git a/ios/Classes/Types/ClientCertChallenge.swift b/flutter_inappwebview_ios/ios/Classes/Types/ClientCertChallenge.swift similarity index 100% rename from ios/Classes/Types/ClientCertChallenge.swift rename to flutter_inappwebview_ios/ios/Classes/Types/ClientCertChallenge.swift diff --git a/ios/Classes/Types/ClientCertResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/ClientCertResponse.swift similarity index 100% rename from ios/Classes/Types/ClientCertResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/ClientCertResponse.swift diff --git a/ios/Classes/Types/CreateWindowAction.swift b/flutter_inappwebview_ios/ios/Classes/Types/CreateWindowAction.swift similarity index 100% rename from ios/Classes/Types/CreateWindowAction.swift rename to flutter_inappwebview_ios/ios/Classes/Types/CreateWindowAction.swift diff --git a/ios/Classes/Types/CustomSchemeResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/CustomSchemeResponse.swift similarity index 100% rename from ios/Classes/Types/CustomSchemeResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/CustomSchemeResponse.swift diff --git a/ios/Classes/Types/Disposable.swift b/flutter_inappwebview_ios/ios/Classes/Types/Disposable.swift similarity index 100% rename from ios/Classes/Types/Disposable.swift rename to flutter_inappwebview_ios/ios/Classes/Types/Disposable.swift diff --git a/ios/Classes/Types/DownloadStartRequest.swift b/flutter_inappwebview_ios/ios/Classes/Types/DownloadStartRequest.swift similarity index 100% rename from ios/Classes/Types/DownloadStartRequest.swift rename to flutter_inappwebview_ios/ios/Classes/Types/DownloadStartRequest.swift diff --git a/ios/Classes/Types/FlutterMethodCallDelegate.swift b/flutter_inappwebview_ios/ios/Classes/Types/FlutterMethodCallDelegate.swift similarity index 100% rename from ios/Classes/Types/FlutterMethodCallDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/Types/FlutterMethodCallDelegate.swift diff --git a/ios/Classes/Types/FlutterMethodChannel.swift b/flutter_inappwebview_ios/ios/Classes/Types/FlutterMethodChannel.swift similarity index 100% rename from ios/Classes/Types/FlutterMethodChannel.swift rename to flutter_inappwebview_ios/ios/Classes/Types/FlutterMethodChannel.swift diff --git a/ios/Classes/Types/HitTestResult.swift b/flutter_inappwebview_ios/ios/Classes/Types/HitTestResult.swift similarity index 100% rename from ios/Classes/Types/HitTestResult.swift rename to flutter_inappwebview_ios/ios/Classes/Types/HitTestResult.swift diff --git a/ios/Classes/Types/HttpAuthResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/HttpAuthResponse.swift similarity index 100% rename from ios/Classes/Types/HttpAuthResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/HttpAuthResponse.swift diff --git a/ios/Classes/Types/HttpAuthenticationChallenge.swift b/flutter_inappwebview_ios/ios/Classes/Types/HttpAuthenticationChallenge.swift similarity index 100% rename from ios/Classes/Types/HttpAuthenticationChallenge.swift rename to flutter_inappwebview_ios/ios/Classes/Types/HttpAuthenticationChallenge.swift diff --git a/ios/Classes/Types/InAppBrowserMenuItem.swift b/flutter_inappwebview_ios/ios/Classes/Types/InAppBrowserMenuItem.swift similarity index 100% rename from ios/Classes/Types/InAppBrowserMenuItem.swift rename to flutter_inappwebview_ios/ios/Classes/Types/InAppBrowserMenuItem.swift diff --git a/ios/Classes/Types/JsAlertResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/JsAlertResponse.swift similarity index 100% rename from ios/Classes/Types/JsAlertResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/JsAlertResponse.swift diff --git a/ios/Classes/Types/JsConfirmResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/JsConfirmResponse.swift similarity index 100% rename from ios/Classes/Types/JsConfirmResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/JsConfirmResponse.swift diff --git a/ios/Classes/Types/JsPromptResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/JsPromptResponse.swift similarity index 100% rename from ios/Classes/Types/JsPromptResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/JsPromptResponse.swift diff --git a/ios/Classes/Types/MethodChannelResult.swift b/flutter_inappwebview_ios/ios/Classes/Types/MethodChannelResult.swift similarity index 100% rename from ios/Classes/Types/MethodChannelResult.swift rename to flutter_inappwebview_ios/ios/Classes/Types/MethodChannelResult.swift diff --git a/ios/Classes/Types/NSAttributedString.swift b/flutter_inappwebview_ios/ios/Classes/Types/NSAttributedString.swift similarity index 100% rename from ios/Classes/Types/NSAttributedString.swift rename to flutter_inappwebview_ios/ios/Classes/Types/NSAttributedString.swift diff --git a/ios/Classes/Types/PermissionRequest.swift b/flutter_inappwebview_ios/ios/Classes/Types/PermissionRequest.swift similarity index 100% rename from ios/Classes/Types/PermissionRequest.swift rename to flutter_inappwebview_ios/ios/Classes/Types/PermissionRequest.swift diff --git a/ios/Classes/Types/PermissionResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/PermissionResponse.swift similarity index 100% rename from ios/Classes/Types/PermissionResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/PermissionResponse.swift diff --git a/ios/Classes/Types/PluginScript.swift b/flutter_inappwebview_ios/ios/Classes/Types/PluginScript.swift similarity index 100% rename from ios/Classes/Types/PluginScript.swift rename to flutter_inappwebview_ios/ios/Classes/Types/PluginScript.swift diff --git a/ios/Classes/Types/SecCertificate.swift b/flutter_inappwebview_ios/ios/Classes/Types/SecCertificate.swift similarity index 100% rename from ios/Classes/Types/SecCertificate.swift rename to flutter_inappwebview_ios/ios/Classes/Types/SecCertificate.swift diff --git a/ios/Classes/Types/ServerTrustAuthResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/ServerTrustAuthResponse.swift similarity index 100% rename from ios/Classes/Types/ServerTrustAuthResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/ServerTrustAuthResponse.swift diff --git a/ios/Classes/Types/ServerTrustChallenge.swift b/flutter_inappwebview_ios/ios/Classes/Types/ServerTrustChallenge.swift similarity index 100% rename from ios/Classes/Types/ServerTrustChallenge.swift rename to flutter_inappwebview_ios/ios/Classes/Types/ServerTrustChallenge.swift diff --git a/ios/Classes/Types/Size2D.swift b/flutter_inappwebview_ios/ios/Classes/Types/Size2D.swift similarity index 100% rename from ios/Classes/Types/Size2D.swift rename to flutter_inappwebview_ios/ios/Classes/Types/Size2D.swift diff --git a/ios/Classes/Types/SslCertificate.swift b/flutter_inappwebview_ios/ios/Classes/Types/SslCertificate.swift similarity index 100% rename from ios/Classes/Types/SslCertificate.swift rename to flutter_inappwebview_ios/ios/Classes/Types/SslCertificate.swift diff --git a/ios/Classes/Types/SslError.swift b/flutter_inappwebview_ios/ios/Classes/Types/SslError.swift similarity index 100% rename from ios/Classes/Types/SslError.swift rename to flutter_inappwebview_ios/ios/Classes/Types/SslError.swift diff --git a/ios/Classes/Types/StringOrInt.swift b/flutter_inappwebview_ios/ios/Classes/Types/StringOrInt.swift similarity index 100% rename from ios/Classes/Types/StringOrInt.swift rename to flutter_inappwebview_ios/ios/Classes/Types/StringOrInt.swift diff --git a/ios/Classes/Types/UIColor.swift b/flutter_inappwebview_ios/ios/Classes/Types/UIColor.swift similarity index 100% rename from ios/Classes/Types/UIColor.swift rename to flutter_inappwebview_ios/ios/Classes/Types/UIColor.swift diff --git a/ios/Classes/Types/UIEdgeInsets.swift b/flutter_inappwebview_ios/ios/Classes/Types/UIEdgeInsets.swift similarity index 100% rename from ios/Classes/Types/UIEdgeInsets.swift rename to flutter_inappwebview_ios/ios/Classes/Types/UIEdgeInsets.swift diff --git a/ios/Classes/Types/UIEventAttribution.swift b/flutter_inappwebview_ios/ios/Classes/Types/UIEventAttribution.swift similarity index 100% rename from ios/Classes/Types/UIEventAttribution.swift rename to flutter_inappwebview_ios/ios/Classes/Types/UIEventAttribution.swift diff --git a/ios/Classes/Types/UIFindSession.swift b/flutter_inappwebview_ios/ios/Classes/Types/UIFindSession.swift similarity index 100% rename from ios/Classes/Types/UIFindSession.swift rename to flutter_inappwebview_ios/ios/Classes/Types/UIFindSession.swift diff --git a/ios/Classes/Types/UIImage.swift b/flutter_inappwebview_ios/ios/Classes/Types/UIImage.swift similarity index 100% rename from ios/Classes/Types/UIImage.swift rename to flutter_inappwebview_ios/ios/Classes/Types/UIImage.swift diff --git a/ios/Classes/Types/URLAuthenticationChallenge.swift b/flutter_inappwebview_ios/ios/Classes/Types/URLAuthenticationChallenge.swift similarity index 100% rename from ios/Classes/Types/URLAuthenticationChallenge.swift rename to flutter_inappwebview_ios/ios/Classes/Types/URLAuthenticationChallenge.swift diff --git a/ios/Classes/Types/URLCredential.swift b/flutter_inappwebview_ios/ios/Classes/Types/URLCredential.swift similarity index 100% rename from ios/Classes/Types/URLCredential.swift rename to flutter_inappwebview_ios/ios/Classes/Types/URLCredential.swift diff --git a/ios/Classes/Types/URLProtectionSpace.swift b/flutter_inappwebview_ios/ios/Classes/Types/URLProtectionSpace.swift similarity index 100% rename from ios/Classes/Types/URLProtectionSpace.swift rename to flutter_inappwebview_ios/ios/Classes/Types/URLProtectionSpace.swift diff --git a/ios/Classes/Types/URLRequest.swift b/flutter_inappwebview_ios/ios/Classes/Types/URLRequest.swift similarity index 100% rename from ios/Classes/Types/URLRequest.swift rename to flutter_inappwebview_ios/ios/Classes/Types/URLRequest.swift diff --git a/ios/Classes/Types/URLResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/URLResponse.swift similarity index 100% rename from ios/Classes/Types/URLResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/URLResponse.swift diff --git a/ios/Classes/Types/UserScript.swift b/flutter_inappwebview_ios/ios/Classes/Types/UserScript.swift similarity index 100% rename from ios/Classes/Types/UserScript.swift rename to flutter_inappwebview_ios/ios/Classes/Types/UserScript.swift diff --git a/ios/Classes/Types/WKContentWorld.swift b/flutter_inappwebview_ios/ios/Classes/Types/WKContentWorld.swift similarity index 100% rename from ios/Classes/Types/WKContentWorld.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WKContentWorld.swift diff --git a/ios/Classes/Types/WKFrameInfo.swift b/flutter_inappwebview_ios/ios/Classes/Types/WKFrameInfo.swift similarity index 100% rename from ios/Classes/Types/WKFrameInfo.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WKFrameInfo.swift diff --git a/ios/Classes/Types/WKNavigationAction.swift b/flutter_inappwebview_ios/ios/Classes/Types/WKNavigationAction.swift similarity index 100% rename from ios/Classes/Types/WKNavigationAction.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WKNavigationAction.swift diff --git a/ios/Classes/Types/WKNavigationResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/WKNavigationResponse.swift similarity index 100% rename from ios/Classes/Types/WKNavigationResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WKNavigationResponse.swift diff --git a/ios/Classes/Types/WKSecurityOrigin.swift b/flutter_inappwebview_ios/ios/Classes/Types/WKSecurityOrigin.swift similarity index 100% rename from ios/Classes/Types/WKSecurityOrigin.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WKSecurityOrigin.swift diff --git a/ios/Classes/Types/WKUserContentController.swift b/flutter_inappwebview_ios/ios/Classes/Types/WKUserContentController.swift similarity index 100% rename from ios/Classes/Types/WKUserContentController.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WKUserContentController.swift diff --git a/ios/Classes/Types/WKWindowFeatures.swift b/flutter_inappwebview_ios/ios/Classes/Types/WKWindowFeatures.swift similarity index 100% rename from ios/Classes/Types/WKWindowFeatures.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WKWindowFeatures.swift diff --git a/ios/Classes/Types/WebMessage.swift b/flutter_inappwebview_ios/ios/Classes/Types/WebMessage.swift similarity index 100% rename from ios/Classes/Types/WebMessage.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WebMessage.swift diff --git a/ios/Classes/Types/WebMessagePort.swift b/flutter_inappwebview_ios/ios/Classes/Types/WebMessagePort.swift similarity index 100% rename from ios/Classes/Types/WebMessagePort.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WebMessagePort.swift diff --git a/ios/Classes/Types/WebResourceError.swift b/flutter_inappwebview_ios/ios/Classes/Types/WebResourceError.swift similarity index 100% rename from ios/Classes/Types/WebResourceError.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WebResourceError.swift diff --git a/ios/Classes/Types/WebResourceRequest.swift b/flutter_inappwebview_ios/ios/Classes/Types/WebResourceRequest.swift similarity index 100% rename from ios/Classes/Types/WebResourceRequest.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WebResourceRequest.swift diff --git a/ios/Classes/Types/WebResourceResponse.swift b/flutter_inappwebview_ios/ios/Classes/Types/WebResourceResponse.swift similarity index 100% rename from ios/Classes/Types/WebResourceResponse.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WebResourceResponse.swift diff --git a/ios/Classes/Types/WebViewTransport.swift b/flutter_inappwebview_ios/ios/Classes/Types/WebViewTransport.swift similarity index 100% rename from ios/Classes/Types/WebViewTransport.swift rename to flutter_inappwebview_ios/ios/Classes/Types/WebViewTransport.swift diff --git a/ios/Classes/UIApplication/VisibleViewController.swift b/flutter_inappwebview_ios/ios/Classes/UIApplication/VisibleViewController.swift similarity index 100% rename from ios/Classes/UIApplication/VisibleViewController.swift rename to flutter_inappwebview_ios/ios/Classes/UIApplication/VisibleViewController.swift diff --git a/ios/Classes/Util.swift b/flutter_inappwebview_ios/ios/Classes/Util.swift similarity index 100% rename from ios/Classes/Util.swift rename to flutter_inappwebview_ios/ios/Classes/Util.swift diff --git a/ios/Classes/WKProcessPoolManager.swift b/flutter_inappwebview_ios/ios/Classes/WKProcessPoolManager.swift similarity index 100% rename from ios/Classes/WKProcessPoolManager.swift rename to flutter_inappwebview_ios/ios/Classes/WKProcessPoolManager.swift diff --git a/ios/Classes/WebAuthenticationSession/WebAuthenticationSession.swift b/flutter_inappwebview_ios/ios/Classes/WebAuthenticationSession/WebAuthenticationSession.swift similarity index 100% rename from ios/Classes/WebAuthenticationSession/WebAuthenticationSession.swift rename to flutter_inappwebview_ios/ios/Classes/WebAuthenticationSession/WebAuthenticationSession.swift diff --git a/ios/Classes/WebAuthenticationSession/WebAuthenticationSessionChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/WebAuthenticationSession/WebAuthenticationSessionChannelDelegate.swift similarity index 100% rename from ios/Classes/WebAuthenticationSession/WebAuthenticationSessionChannelDelegate.swift rename to flutter_inappwebview_ios/ios/Classes/WebAuthenticationSession/WebAuthenticationSessionChannelDelegate.swift diff --git a/ios/Classes/WebAuthenticationSession/WebAuthenticationSessionManager.swift b/flutter_inappwebview_ios/ios/Classes/WebAuthenticationSession/WebAuthenticationSessionManager.swift similarity index 100% rename from ios/Classes/WebAuthenticationSession/WebAuthenticationSessionManager.swift rename to flutter_inappwebview_ios/ios/Classes/WebAuthenticationSession/WebAuthenticationSessionManager.swift diff --git a/ios/Classes/WebAuthenticationSession/WebAuthenticationSessionSettings.swift b/flutter_inappwebview_ios/ios/Classes/WebAuthenticationSession/WebAuthenticationSessionSettings.swift similarity index 100% rename from ios/Classes/WebAuthenticationSession/WebAuthenticationSessionSettings.swift rename to flutter_inappwebview_ios/ios/Classes/WebAuthenticationSession/WebAuthenticationSessionSettings.swift diff --git a/ios/Storyboards/WebView.storyboard b/flutter_inappwebview_ios/ios/Storyboards/WebView.storyboard similarity index 95% rename from ios/Storyboards/WebView.storyboard rename to flutter_inappwebview_ios/ios/Storyboards/WebView.storyboard index 9c71b050..51a8d4f4 100755 --- a/ios/Storyboards/WebView.storyboard +++ b/flutter_inappwebview_ios/ios/Storyboards/WebView.storyboard @@ -11,7 +11,7 @@ - + @@ -24,7 +24,7 @@ - + diff --git a/ios/flutter_inappwebview.podspec b/flutter_inappwebview_ios/ios/flutter_inappwebview_ios.podspec similarity index 96% rename from ios/flutter_inappwebview.podspec rename to flutter_inappwebview_ios/ios/flutter_inappwebview_ios.podspec index 03e2e86c..a08fa7fb 100755 --- a/ios/flutter_inappwebview.podspec +++ b/flutter_inappwebview_ios/ios/flutter_inappwebview_ios.podspec @@ -3,7 +3,7 @@ # Run `pod lib lint flutterplugintest.podspec' to validate before publishing. # Pod::Spec.new do |s| - s.name = 'flutter_inappwebview' + s.name = 'flutter_inappwebview_ios' s.version = '0.0.1' s.summary = 'A new Flutter plugin.' s.description = <<-DESC diff --git a/flutter_inappwebview_ios/lib/flutter_inappwebview_ios.dart b/flutter_inappwebview_ios/lib/flutter_inappwebview_ios.dart new file mode 100644 index 00000000..9390a49d --- /dev/null +++ b/flutter_inappwebview_ios/lib/flutter_inappwebview_ios.dart @@ -0,0 +1,3 @@ +library flutter_inappwebview_ios; + +export 'src/main.dart'; diff --git a/flutter_inappwebview_ios/lib/src/chrome_safari_browser/chrome_safari_browser.dart b/flutter_inappwebview_ios/lib/src/chrome_safari_browser/chrome_safari_browser.dart new file mode 100755 index 00000000..c377ec0e --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/chrome_safari_browser/chrome_safari_browser.dart @@ -0,0 +1,218 @@ +import 'dart:async'; +import 'dart:collection'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [IOSChromeSafariBrowser]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformChromeSafariBrowserCreationParams] for +/// more information. +@immutable +class IOSChromeSafariBrowserCreationParams + extends PlatformChromeSafariBrowserCreationParams { + /// Creates a new [IOSChromeSafariBrowserCreationParams] instance. + const IOSChromeSafariBrowserCreationParams(); + + /// Creates a [IOSChromeSafariBrowserCreationParams] instance based on [PlatformChromeSafariBrowserCreationParams]. + factory IOSChromeSafariBrowserCreationParams.fromPlatformChromeSafariBrowserCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformChromeSafariBrowserCreationParams params) { + return IOSChromeSafariBrowserCreationParams(); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser} +class IOSChromeSafariBrowser extends PlatformChromeSafariBrowser + with ChannelController { + @override + final String id = IdGenerator.generate(); + + /// Constructs a [IOSChromeSafariBrowser]. + IOSChromeSafariBrowser(PlatformChromeSafariBrowserCreationParams params) + : super.implementation( + params is IOSChromeSafariBrowserCreationParams + ? params + : IOSChromeSafariBrowserCreationParams + .fromPlatformChromeSafariBrowserCreationParams(params), + ); + + static final IOSChromeSafariBrowser _staticValue = + IOSChromeSafariBrowser(IOSChromeSafariBrowserCreationParams()); + + /// Provide static access. + factory IOSChromeSafariBrowser.static() { + return _staticValue; + } + + Map _menuItems = new HashMap(); + bool _isOpened = false; + static const MethodChannel _staticChannel = + const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser'); + + _init() { + channel = + MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + id: id, + debugLoggingSettings: PlatformChromeSafariBrowser.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + _debugLog(call.method, call.arguments); + + switch (call.method) { + case "onOpened": + eventHandler?.onOpened(); + break; + case "onCompletedInitialLoad": + final bool? didLoadSuccessfully = call.arguments["didLoadSuccessfully"]; + eventHandler?.onCompletedInitialLoad(didLoadSuccessfully); + break; + case "onInitialLoadDidRedirect": + final String? url = call.arguments["url"]; + final WebUri? uri = url != null ? WebUri(url) : null; + eventHandler?.onInitialLoadDidRedirect(uri); + break; + case "onWillOpenInBrowser": + eventHandler?.onWillOpenInBrowser(); + break; + case "onClosed": + _isOpened = false; + final onClosed = eventHandler?.onClosed; + dispose(); + onClosed?.call(); + break; + case "onItemActionPerform": + String url = call.arguments["url"]; + String title = call.arguments["title"]; + int id = call.arguments["id"].toInt(); + if (this._menuItems[id] != null) { + if (this._menuItems[id]?.action != null) { + this._menuItems[id]?.action!(url, title); + } + if (this._menuItems[id]?.onClick != null) { + this._menuItems[id]?.onClick!(WebUri(url), title); + } + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + @override + Future open( + {WebUri? url, + Map? headers, + List? otherLikelyURLs, + WebUri? referrer, + @Deprecated('Use settings instead') + // ignore: deprecated_member_use_from_same_package + ChromeSafariBrowserClassOptions? options, + ChromeSafariBrowserSettings? settings}) async { + assert(!_isOpened, 'The browser is already opened.'); + _isOpened = true; + + assert(url != null, 'The specified URL must not be null on iOS.'); + assert(['http', 'https'].contains(url!.scheme), + 'The specified URL has an unsupported scheme. Only HTTP and HTTPS URLs are supported on iOS.'); + if (url != null) { + assert(url.toString().isNotEmpty, 'The specified URL must not be empty.'); + } + + _init(); + + List> menuItemList = []; + _menuItems.forEach((key, value) { + menuItemList.add(value.toMap()); + }); + + var initialSettings = settings?.toMap() ?? + options?.toMap() ?? + ChromeSafariBrowserSettings().toMap(); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent('url', () => url?.toString()); + args.putIfAbsent('headers', () => headers); + args.putIfAbsent('otherLikelyURLs', + () => otherLikelyURLs?.map((e) => e.toString()).toList()); + args.putIfAbsent('referrer', () => referrer?.toString()); + args.putIfAbsent('settings', () => initialSettings); + args.putIfAbsent('menuItemList', () => menuItemList); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future close() async { + Map args = {}; + await channel?.invokeMethod("close", args); + } + + @override + void addMenuItem(ChromeSafariBrowserMenuItem menuItem) { + this._menuItems[menuItem.id] = menuItem; + } + + @override + void addMenuItems(List menuItems) { + menuItems.forEach((menuItem) { + this._menuItems[menuItem.id] = menuItem; + }); + } + + @override + Future isAvailable() async { + Map args = {}; + return await _staticChannel.invokeMethod("isAvailable", args) ?? + false; + } + + @override + Future clearWebsiteData() async { + Map args = {}; + await _staticChannel.invokeMethod("clearWebsiteData", args); + } + + @override + Future prewarmConnections(List URLs) async { + Map args = {}; + args.putIfAbsent('URLs', () => URLs.map((e) => e.toString()).toList()); + Map? result = + (await _staticChannel.invokeMethod("prewarmConnections", args)) + ?.cast(); + return PrewarmingToken.fromMap(result); + } + + @override + Future invalidatePrewarmingToken( + PrewarmingToken prewarmingToken) async { + Map args = {}; + args.putIfAbsent('prewarmingToken', () => prewarmingToken.toMap()); + await _staticChannel.invokeMethod("invalidatePrewarmingToken", args); + } + + @override + bool isOpened() { + return _isOpened; + } + + @override + @mustCallSuper + void dispose() { + disposeChannel(); + eventHandler = null; + } +} diff --git a/flutter_inappwebview_ios/lib/src/chrome_safari_browser/main.dart b/flutter_inappwebview_ios/lib/src/chrome_safari_browser/main.dart new file mode 100644 index 00000000..9a6238b7 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/chrome_safari_browser/main.dart @@ -0,0 +1 @@ +export 'chrome_safari_browser.dart'; diff --git a/flutter_inappwebview_ios/lib/src/cookie_manager.dart b/flutter_inappwebview_ios/lib/src/cookie_manager.dart new file mode 100755 index 00000000..70d016d2 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/cookie_manager.dart @@ -0,0 +1,434 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'in_app_webview/headless_in_app_webview.dart'; +import 'platform_util.dart'; + +/// Object specifying creation parameters for creating a [IOSCookieManager]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformCookieManagerCreationParams] for +/// more information. +@immutable +class IOSCookieManagerCreationParams + extends PlatformCookieManagerCreationParams { + /// Creates a new [IOSCookieManagerCreationParams] instance. + const IOSCookieManagerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformCookieManagerCreationParams params, + ) : super(); + + /// Creates a [IOSCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams]. + factory IOSCookieManagerCreationParams.fromPlatformCookieManagerCreationParams( + PlatformCookieManagerCreationParams params) { + return IOSCookieManagerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager} +class IOSCookieManager extends PlatformCookieManager + with ChannelController { + /// Creates a new [IOSCookieManager]. + IOSCookieManager(PlatformCookieManagerCreationParams params) + : super.implementation( + params is IOSCookieManagerCreationParams + ? params + : IOSCookieManagerCreationParams + .fromPlatformCookieManagerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_cookiemanager'); + handler = handleMethod; + initMethodCallHandler(); + } + + static IOSCookieManager? _instance; + + ///Gets the [IOSCookieManager] shared instance. + static IOSCookieManager instance() { + return (_instance != null) ? _instance! : _init(); + } + + static IOSCookieManager _init() { + _instance = IOSCookieManager(IOSCookieManagerCreationParams( + const PlatformCookieManagerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future setCookie( + {required WebUri url, + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + bool? isHttpOnly, + HTTPCookieSameSitePolicy? sameSite, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + webViewController = webViewController ?? iosBelow11WebViewController; + + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + assert(value.isNotEmpty); + assert(path.isNotEmpty); + + if (await _shouldUseJavascript()) { + await _setCookieWithJavaScript( + url: url, + name: name, + value: value, + domain: domain, + path: path, + expiresDate: expiresDate, + maxAge: maxAge, + isSecure: isSecure, + sameSite: sameSite, + webViewController: webViewController); + return true; + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('name', () => name); + args.putIfAbsent('value', () => value); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + args.putIfAbsent('expiresDate', () => expiresDate?.toString()); + args.putIfAbsent('maxAge', () => maxAge); + args.putIfAbsent('isSecure', () => isSecure); + args.putIfAbsent('isHttpOnly', () => isHttpOnly); + args.putIfAbsent('sameSite', () => sameSite?.toNativeValue()); + + return await channel?.invokeMethod('setCookie', args) ?? false; + } + + Future _setCookieWithJavaScript( + {required WebUri url, + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + HTTPCookieSameSitePolicy? sameSite, + PlatformInAppWebViewController? webViewController}) async { + var cookieValue = name + "=" + value + "; Path=" + path; + + if (domain != null) cookieValue += "; Domain=" + domain; + + if (expiresDate != null) + cookieValue += "; Expires=" + await _getCookieExpirationDate(expiresDate); + + if (maxAge != null) cookieValue += "; Max-Age=" + maxAge.toString(); + + if (isSecure != null && isSecure) cookieValue += "; Secure"; + + if (sameSite != null) + cookieValue += "; SameSite=" + sameSite.toNativeValue(); + + cookieValue += ";"; + + if (webViewController != null) { + final javaScriptEnabled = + (await webViewController.getSettings())?.javaScriptEnabled ?? false; + if (javaScriptEnabled) { + await webViewController.evaluateJavascript( + source: 'document.cookie="$cookieValue"'); + return; + } + } + + final setCookieCompleter = Completer(); + final headlessWebView = + IOSHeadlessInAppWebView(IOSHeadlessInAppWebViewCreationParams( + initialUrlRequest: URLRequest(url: url), + onLoadStop: (controller, url) async { + await controller.evaluateJavascript( + source: 'document.cookie="$cookieValue"'); + setCookieCompleter.complete(); + })); + await headlessWebView.run(); + await setCookieCompleter.future; + await headlessWebView.dispose(); + } + + @override + Future> getCookies( + {required WebUri url, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + return await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + } + + List cookies = []; + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + List cookieListMap = + await channel?.invokeMethod('getCookies', args) ?? []; + cookieListMap = cookieListMap.cast>(); + + cookieListMap.forEach((cookieMap) { + cookies.add(Cookie( + name: cookieMap["name"], + value: cookieMap["value"], + expiresDate: cookieMap["expiresDate"], + isSessionOnly: cookieMap["isSessionOnly"], + domain: cookieMap["domain"], + sameSite: + HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), + isSecure: cookieMap["isSecure"], + isHttpOnly: cookieMap["isHttpOnly"], + path: cookieMap["path"])); + }); + return cookies; + } + + Future> _getCookiesWithJavaScript( + {required WebUri url, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + List cookies = []; + + if (webViewController != null) { + final javaScriptEnabled = + (await webViewController.getSettings())?.javaScriptEnabled ?? false; + if (javaScriptEnabled) { + List documentCookies = (await webViewController + .evaluateJavascript(source: 'document.cookie') as String) + .split(';') + .map((documentCookie) => documentCookie.trim()) + .toList(); + documentCookies.forEach((documentCookie) { + List cookie = documentCookie.split('='); + if (cookie.length > 1) { + cookies.add(Cookie( + name: cookie[0], + value: cookie[1], + )); + } + }); + return cookies; + } + } + + final pageLoaded = Completer(); + final headlessWebView = + IOSHeadlessInAppWebView(IOSHeadlessInAppWebViewCreationParams( + initialUrlRequest: URLRequest(url: url), + onLoadStop: (controller, url) async { + pageLoaded.complete(); + }, + )); + await headlessWebView.run(); + await pageLoaded.future; + + List documentCookies = (await headlessWebView.webViewController! + .evaluateJavascript(source: 'document.cookie') as String) + .split(';') + .map((documentCookie) => documentCookie.trim()) + .toList(); + documentCookies.forEach((documentCookie) { + List cookie = documentCookie.split('='); + if (cookie.length > 1) { + cookies.add(Cookie( + name: cookie[0], + value: cookie[1], + )); + } + }); + await headlessWebView.dispose(); + return cookies; + } + + @override + Future getCookie( + {required WebUri url, + required String name, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + List cookies = await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + return cookies + .cast() + .firstWhere((cookie) => cookie!.name == name, orElse: () => null); + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + List cookies = + await channel?.invokeMethod('getCookies', args) ?? []; + cookies = cookies.cast>(); + for (var i = 0; i < cookies.length; i++) { + cookies[i] = cookies[i].cast(); + if (cookies[i]["name"] == name) + return Cookie( + name: cookies[i]["name"], + value: cookies[i]["value"], + expiresDate: cookies[i]["expiresDate"], + isSessionOnly: cookies[i]["isSessionOnly"], + domain: cookies[i]["domain"], + sameSite: HTTPCookieSameSitePolicy.fromNativeValue( + cookies[i]["sameSite"]), + isSecure: cookies[i]["isSecure"], + isHttpOnly: cookies[i]["isHttpOnly"], + path: cookies[i]["path"]); + } + return null; + } + + @override + Future deleteCookie( + {required WebUri url, + required String name, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + await _setCookieWithJavaScript( + url: url, + name: name, + value: "", + path: path, + domain: domain, + maxAge: -1, + webViewController: webViewController); + return; + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('name', () => name); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + await channel?.invokeMethod('deleteCookie', args); + } + + @override + Future deleteCookies( + {required WebUri url, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + List cookies = await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + for (var i = 0; i < cookies.length; i++) { + await _setCookieWithJavaScript( + url: url, + name: cookies[i].name, + value: "", + path: path, + domain: domain, + maxAge: -1, + webViewController: webViewController); + } + return; + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + await channel?.invokeMethod('deleteCookies', args); + } + + @override + Future deleteAllCookies() async { + Map args = {}; + await channel?.invokeMethod('deleteAllCookies', args); + } + + @override + Future> getAllCookies() async { + List cookies = []; + + Map args = {}; + List cookieListMap = + await channel?.invokeMethod('getAllCookies', args) ?? []; + cookieListMap = cookieListMap.cast>(); + + cookieListMap.forEach((cookieMap) { + cookies.add(Cookie( + name: cookieMap["name"], + value: cookieMap["value"], + expiresDate: cookieMap["expiresDate"], + isSessionOnly: cookieMap["isSessionOnly"], + domain: cookieMap["domain"], + sameSite: + HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), + isSecure: cookieMap["isSecure"], + isHttpOnly: cookieMap["isHttpOnly"], + path: cookieMap["path"])); + }); + return cookies; + } + + Future _getCookieExpirationDate(int expiresDate) async { + var platformUtil = PlatformUtil.instance(); + var dateTime = DateTime.fromMillisecondsSinceEpoch(expiresDate).toUtc(); + return !kIsWeb + ? await platformUtil.formatDate( + date: dateTime, + format: 'EEE, dd MMM yyyy hh:mm:ss z', + locale: 'en_US', + timezone: 'GMT') + : await platformUtil.getWebCookieExpirationDate(date: dateTime); + } + + Future _shouldUseJavascript() async { + final platformUtil = PlatformUtil.instance(); + final systemVersion = await platformUtil.getSystemVersion(); + return systemVersion.compareTo("10.13") == -1; + } + + @override + void dispose() { + // empty + } +} + +extension InternalCookieManager on IOSCookieManager { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_ios/lib/src/find_interaction/find_interaction_controller.dart b/flutter_inappwebview_ios/lib/src/find_interaction/find_interaction_controller.dart new file mode 100644 index 00000000..83ea52b5 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/find_interaction/find_interaction_controller.dart @@ -0,0 +1,149 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [IOSFindInteractionController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformFindInteractionControllerCreationParams] for +/// more information. +@immutable +class IOSFindInteractionControllerCreationParams + extends PlatformFindInteractionControllerCreationParams { + /// Creates a new [IOSFindInteractionControllerCreationParams] instance. + const IOSFindInteractionControllerCreationParams( + {super.onFindResultReceived}); + + /// Creates a [IOSFindInteractionControllerCreationParams] instance based on [PlatformFindInteractionControllerCreationParams]. + factory IOSFindInteractionControllerCreationParams.fromPlatformFindInteractionControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformFindInteractionControllerCreationParams params) { + return IOSFindInteractionControllerCreationParams( + onFindResultReceived: params.onFindResultReceived); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController} +class IOSFindInteractionController extends PlatformFindInteractionController + with ChannelController { + /// Constructs a [IOSFindInteractionController]. + IOSFindInteractionController( + PlatformFindInteractionControllerCreationParams params) + : super.implementation( + params is IOSFindInteractionControllerCreationParams + ? params + : IOSFindInteractionControllerCreationParams + .fromPlatformFindInteractionControllerCreationParams(params), + ); + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + debugLoggingSettings: + PlatformFindInteractionController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + _debugLog(call.method, call.arguments); + + switch (call.method) { + case "onFindResultReceived": + if (onFindResultReceived != null) { + int activeMatchOrdinal = call.arguments["activeMatchOrdinal"]; + int numberOfMatches = call.arguments["numberOfMatches"]; + bool isDoneCounting = call.arguments["isDoneCounting"]; + onFindResultReceived!( + this, activeMatchOrdinal, numberOfMatches, isDoneCounting); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.findAll} + Future findAll({String? find}) async { + Map args = {}; + args.putIfAbsent('find', () => find); + await channel?.invokeMethod('findAll', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.findNext} + Future findNext({bool forward = true}) async { + Map args = {}; + args.putIfAbsent('forward', () => forward); + await channel?.invokeMethod('findNext', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.clearMatches} + Future clearMatches() async { + Map args = {}; + await channel?.invokeMethod('clearMatches', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.setSearchText} + Future setSearchText(String? searchText) async { + Map args = {}; + args.putIfAbsent('searchText', () => searchText); + await channel?.invokeMethod('setSearchText', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.getSearchText} + Future getSearchText() async { + Map args = {}; + return await channel?.invokeMethod('getSearchText', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.isFindNavigatorVisible} + Future isFindNavigatorVisible() async { + Map args = {}; + return await channel?.invokeMethod('isFindNavigatorVisible', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.updateResultCount} + Future updateResultCount() async { + Map args = {}; + await channel?.invokeMethod('updateResultCount', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.presentFindNavigator} + Future presentFindNavigator() async { + Map args = {}; + await channel?.invokeMethod('presentFindNavigator', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.dismissFindNavigator} + Future dismissFindNavigator() async { + Map args = {}; + await channel?.invokeMethod('dismissFindNavigator', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.getActiveFindSession} + Future getActiveFindSession() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('getActiveFindSession', args)) + ?.cast(); + return FindSession.fromMap(result); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.dispose} + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: !isKeepAlive); + } +} + +extension InternalFindInteractionController + on IOSFindInteractionController { + void init(dynamic id) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } +} diff --git a/flutter_inappwebview_ios/lib/src/find_interaction/main.dart b/flutter_inappwebview_ios/lib/src/find_interaction/main.dart new file mode 100644 index 00000000..a7adaacf --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/find_interaction/main.dart @@ -0,0 +1,2 @@ +export 'find_interaction_controller.dart' + hide InternalFindInteractionController; diff --git a/flutter_inappwebview_ios/lib/src/http_auth_credentials_database.dart b/flutter_inappwebview_ios/lib/src/http_auth_credentials_database.dart new file mode 100755 index 00000000..4de0fb6c --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/http_auth_credentials_database.dart @@ -0,0 +1,155 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [IOSHttpAuthCredentialDatabase]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformHttpAuthCredentialDatabaseCreationParams] for +/// more information. +@immutable +class IOSHttpAuthCredentialDatabaseCreationParams + extends PlatformHttpAuthCredentialDatabaseCreationParams { + /// Creates a new [IOSHttpAuthCredentialDatabaseCreationParams] instance. + const IOSHttpAuthCredentialDatabaseCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformHttpAuthCredentialDatabaseCreationParams params, + ) : super(); + + /// Creates a [IOSHttpAuthCredentialDatabaseCreationParams] instance based on [PlatformHttpAuthCredentialDatabaseCreationParams]. + factory IOSHttpAuthCredentialDatabaseCreationParams.fromPlatformHttpAuthCredentialDatabaseCreationParams( + PlatformHttpAuthCredentialDatabaseCreationParams params) { + return IOSHttpAuthCredentialDatabaseCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformHttpAuthCredentialDatabase} +class IOSHttpAuthCredentialDatabase + extends PlatformHttpAuthCredentialDatabase with ChannelController { + /// Creates a new [IOSHttpAuthCredentialDatabase]. + IOSHttpAuthCredentialDatabase( + PlatformHttpAuthCredentialDatabaseCreationParams params) + : super.implementation( + params is IOSHttpAuthCredentialDatabaseCreationParams + ? params + : IOSHttpAuthCredentialDatabaseCreationParams + .fromPlatformHttpAuthCredentialDatabaseCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_credential_database'); + handler = handleMethod; + initMethodCallHandler(); + } + + static IOSHttpAuthCredentialDatabase? _instance; + + ///Gets the database shared instance. + static IOSHttpAuthCredentialDatabase instance() { + return (_instance != null) ? _instance! : _init(); + } + + static IOSHttpAuthCredentialDatabase _init() { + _instance = IOSHttpAuthCredentialDatabase( + IOSHttpAuthCredentialDatabaseCreationParams( + const PlatformHttpAuthCredentialDatabaseCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future> + getAllAuthCredentials() async { + Map args = {}; + List allCredentials = + await channel?.invokeMethod('getAllAuthCredentials', args) ?? []; + + List result = []; + + for (Map map in allCredentials) { + var element = URLProtectionSpaceHttpAuthCredentials.fromMap( + map.cast()); + if (element != null) { + result.add(element); + } + } + return result; + } + + @override + Future> getHttpAuthCredentials( + {required URLProtectionSpace protectionSpace}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + List credentialList = + await channel?.invokeMethod('getHttpAuthCredentials', args) ?? []; + List credentials = []; + for (Map map in credentialList) { + var credential = URLCredential.fromMap(map.cast()); + if (credential != null) { + credentials.add(credential); + } + } + return credentials; + } + + @override + Future setHttpAuthCredential( + {required URLProtectionSpace protectionSpace, + required URLCredential credential}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + args.putIfAbsent("username", () => credential.username); + args.putIfAbsent("password", () => credential.password); + await channel?.invokeMethod('setHttpAuthCredential', args); + } + + @override + Future removeHttpAuthCredential( + {required URLProtectionSpace protectionSpace, + required URLCredential credential}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + args.putIfAbsent("username", () => credential.username); + args.putIfAbsent("password", () => credential.password); + await channel?.invokeMethod('removeHttpAuthCredential', args); + } + + @override + Future removeHttpAuthCredentials( + {required URLProtectionSpace protectionSpace}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + await channel?.invokeMethod('removeHttpAuthCredentials', args); + } + + @override + Future clearAllAuthCredentials() async { + Map args = {}; + await channel?.invokeMethod('clearAllAuthCredentials', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalHttpAuthCredentialDatabase + on IOSHttpAuthCredentialDatabase { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_ios/lib/src/in_app_browser/in_app_browser.dart b/flutter_inappwebview_ios/lib/src/in_app_browser/in_app_browser.dart new file mode 100755 index 00000000..fd4a6adf --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/in_app_browser/in_app_browser.dart @@ -0,0 +1,375 @@ +import 'dart:async'; +import 'dart:collection'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../find_interaction/find_interaction_controller.dart'; +import '../in_app_webview/in_app_webview_controller.dart'; +import '../pull_to_refresh/pull_to_refresh_controller.dart'; + +/// Object specifying creation parameters for creating a [IOSInAppBrowser]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInAppBrowserCreationParams] for +/// more information. +class IOSInAppBrowserCreationParams + extends PlatformInAppBrowserCreationParams { + /// Creates a new [IOSInAppBrowserCreationParams] instance. + IOSInAppBrowserCreationParams( + {super.contextMenu, + this.pullToRefreshController, + this.findInteractionController, + super.initialUserScripts, + super.windowId}); + + /// Creates a [IOSInAppBrowserCreationParams] instance based on [PlatformInAppBrowserCreationParams]. + factory IOSInAppBrowserCreationParams.fromPlatformInAppBrowserCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformInAppBrowserCreationParams params) { + return IOSInAppBrowserCreationParams( + contextMenu: params.contextMenu, + pullToRefreshController: + params.pullToRefreshController as IOSPullToRefreshController?, + findInteractionController: params.findInteractionController + as IOSFindInteractionController?, + initialUserScripts: params.initialUserScripts, + windowId: params.windowId); + } + + @override + final IOSFindInteractionController? findInteractionController; + + @override + final IOSPullToRefreshController? pullToRefreshController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser} +class IOSInAppBrowser extends PlatformInAppBrowser with ChannelController { + @override + final String id = IdGenerator.generate(); + + /// Constructs a [IOSInAppBrowser]. + IOSInAppBrowser(PlatformInAppBrowserCreationParams params) + : super.implementation( + params is IOSInAppBrowserCreationParams + ? params + : IOSInAppBrowserCreationParams + .fromPlatformInAppBrowserCreationParams(params), + ) { + _contextMenu = params.contextMenu; + } + + static final IOSInAppBrowser _staticValue = + IOSInAppBrowser(IOSInAppBrowserCreationParams()); + + /// Provide static access. + factory IOSInAppBrowser.static() { + return _staticValue; + } + + IOSInAppBrowserCreationParams get _iosParams => + params as IOSInAppBrowserCreationParams; + + static const MethodChannel _staticChannel = + const MethodChannel('com.pichillilorenzo/flutter_inappbrowser'); + + ContextMenu? _contextMenu; + + @override + ContextMenu? get contextMenu => _contextMenu; + + Map _menuItems = HashMap(); + bool _isOpened = false; + IOSInAppWebViewController? _webViewController; + + @override + IOSInAppWebViewController? get webViewController { + return _isOpened ? _webViewController : null; + } + + _init() { + channel = MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$id'); + handler = _handleMethod; + initMethodCallHandler(); + + _webViewController = IOSInAppWebViewController.fromInAppBrowser( + IOSInAppWebViewControllerCreationParams(id: id), + channel!, + this, + this.initialUserScripts); + _iosParams.pullToRefreshController?.init(id); + _iosParams.findInteractionController?.init(id); + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + id: id, + debugLoggingSettings: PlatformInAppBrowser.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onBrowserCreated": + _debugLog(call.method, call.arguments); + eventHandler?.onBrowserCreated(); + break; + case "onMenuItemClicked": + _debugLog(call.method, call.arguments); + int id = call.arguments["id"].toInt(); + if (this._menuItems[id] != null) { + if (this._menuItems[id]?.onClick != null) { + this._menuItems[id]?.onClick!(); + } + } + break; + case "onExit": + _debugLog(call.method, call.arguments); + _isOpened = false; + final onExit = eventHandler?.onExit; + dispose(); + onExit?.call(); + break; + default: + return _webViewController?.handleMethod(call); + } + } + + Map _prepareOpenRequest( + {@Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) { + assert(!_isOpened, 'The browser is already opened.'); + _isOpened = true; + _init(); + + var initialSettings = settings?.toMap() ?? + options?.toMap() ?? + InAppBrowserClassSettings().toMap(); + + Map pullToRefreshSettings = + pullToRefreshController?.settings.toMap() ?? + pullToRefreshController?.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + List> menuItemList = []; + _menuItems.forEach((key, value) { + menuItemList.add(value.toMap()); + }); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent('settings', () => initialSettings); + args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); + args.putIfAbsent('windowId', () => windowId); + args.putIfAbsent('initialUserScripts', + () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []); + args.putIfAbsent('pullToRefreshSettings', () => pullToRefreshSettings); + args.putIfAbsent('menuItems', () => menuItemList); + return args; + } + + @override + Future openUrlRequest( + {required URLRequest urlRequest, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); + + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openFile( + {required String assetFilePath, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + assert(assetFilePath.isNotEmpty); + + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('assetFilePath', () => assetFilePath); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openData( + {required String data, + String mimeType = "text/html", + String encoding = "utf8", + WebUri? baseUrl, + @Deprecated("Use historyUrl instead") Uri? androidHistoryUrl, + WebUri? historyUrl, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('data', () => data); + args.putIfAbsent('mimeType', () => mimeType); + args.putIfAbsent('encoding', () => encoding); + args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); + args.putIfAbsent('historyUrl', + () => (historyUrl ?? androidHistoryUrl)?.toString() ?? "about:blank"); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openWithSystemBrowser({required WebUri url}) async { + assert(url.toString().isNotEmpty); + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + return await _staticChannel.invokeMethod('openWithSystemBrowser', args); + } + + @override + void addMenuItem(InAppBrowserMenuItem menuItem) { + _menuItems[menuItem.id] = menuItem; + } + + @override + void addMenuItems(List menuItems) { + menuItems.forEach((menuItem) { + _menuItems[menuItem.id] = menuItem; + }); + } + + @override + bool removeMenuItem(InAppBrowserMenuItem menuItem) { + return _menuItems.remove(menuItem.id) != null; + } + + @override + void removeMenuItems(List menuItems) { + for (final menuItem in menuItems) { + removeMenuItem(menuItem); + } + } + + @override + void removeAllMenuItem() { + _menuItems.clear(); + } + + @override + bool hasMenuItem(InAppBrowserMenuItem menuItem) { + return _menuItems.containsKey(menuItem.id); + } + + @override + Future show() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('show', args); + } + + @override + Future hide() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('hide', args); + } + + @override + Future close() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('close', args); + } + + @override + Future isHidden() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + return await channel?.invokeMethod('isHidden', args) ?? false; + } + + @override + @Deprecated('Use setSettings instead') + Future setOptions({required InAppBrowserClassOptions options}) async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + args.putIfAbsent('settings', () => options.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + @Deprecated('Use getSettings instead') + Future getOptions() async { + assert(_isOpened, 'The browser is not opened.'); + Map args = {}; + + Map? options = + await channel?.invokeMethod('getSettings', args); + if (options != null) { + options = options.cast(); + return InAppBrowserClassOptions.fromMap(options as Map); + } + + return null; + } + + @override + Future setSettings( + {required InAppBrowserClassSettings settings}) async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + args.putIfAbsent('settings', () => settings.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + Future getSettings() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + + Map? settings = + await channel?.invokeMethod('getSettings', args); + if (settings != null) { + settings = settings.cast(); + return InAppBrowserClassSettings.fromMap( + settings as Map); + } + + return null; + } + + @override + bool isOpened() { + return this._isOpened; + } + + @override + @mustCallSuper + void dispose() { + disposeChannel(); + _webViewController?.dispose(); + _webViewController = null; + pullToRefreshController?.dispose(); + findInteractionController?.dispose(); + eventHandler = null; + } +} + +extension InternalInAppBrowser on IOSInAppBrowser { + void setContextMenu(ContextMenu? contextMenu) { + _contextMenu = contextMenu; + } +} diff --git a/flutter_inappwebview_ios/lib/src/in_app_browser/main.dart b/flutter_inappwebview_ios/lib/src/in_app_browser/main.dart new file mode 100644 index 00000000..e11eb8b1 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/in_app_browser/main.dart @@ -0,0 +1 @@ +export 'in_app_browser.dart' hide InternalInAppBrowser; diff --git a/flutter_inappwebview_ios/lib/src/in_app_webview/_static_channel.dart b/flutter_inappwebview_ios/lib/src/in_app_webview/_static_channel.dart new file mode 100644 index 00000000..beb7de70 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/in_app_webview/_static_channel.dart @@ -0,0 +1,4 @@ +import 'package:flutter/services.dart'; + +const IN_APP_WEBVIEW_STATIC_CHANNEL = + MethodChannel('com.pichillilorenzo/flutter_inappwebview_manager'); diff --git a/flutter_inappwebview_ios/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview_ios/lib/src/in_app_webview/headless_in_app_webview.dart new file mode 100644 index 00000000..bf514787 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/in_app_webview/headless_in_app_webview.dart @@ -0,0 +1,440 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import '../find_interaction/find_interaction_controller.dart'; +import '../pull_to_refresh/pull_to_refresh_controller.dart'; +import 'in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [IOSHeadlessInAppWebView]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformHeadlessInAppWebViewCreationParams] for +/// more information. +@immutable +class IOSHeadlessInAppWebViewCreationParams + extends PlatformHeadlessInAppWebViewCreationParams { + /// Creates a new [IOSHeadlessInAppWebViewCreationParams] instance. + IOSHeadlessInAppWebViewCreationParams( + {super.controllerFromPlatform, + super.initialSize, + super.windowId, + super.onWebViewCreated, + super.onLoadStart, + super.onLoadStop, + @Deprecated('Use onReceivedError instead') super.onLoadError, + super.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError, + super.onReceivedHttpError, + super.onProgressChanged, + super.onConsoleMessage, + super.shouldOverrideUrlLoading, + super.onLoadResource, + super.onScrollChanged, + @Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart, + super.onDownloadStartRequest, + @Deprecated('Use onLoadResourceWithCustomScheme instead') + super.onLoadResourceCustomScheme, + super.onLoadResourceWithCustomScheme, + super.onCreateWindow, + super.onCloseWindow, + super.onJsAlert, + super.onJsConfirm, + super.onJsPrompt, + super.onReceivedHttpAuthRequest, + super.onReceivedServerTrustAuthRequest, + super.onReceivedClientCertRequest, + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + super.onFindResultReceived, + super.shouldInterceptAjaxRequest, + super.onAjaxReadyStateChange, + super.onAjaxProgress, + super.shouldInterceptFetchRequest, + super.onUpdateVisitedHistory, + @Deprecated("Use onPrintRequest instead") super.onPrint, + super.onPrintRequest, + super.onLongPressHitTestResult, + super.onEnterFullscreen, + super.onExitFullscreen, + super.onPageCommitVisible, + super.onTitleChanged, + super.onWindowFocus, + super.onWindowBlur, + super.onOverScrolled, + super.onZoomScaleChanged, + @Deprecated('Use onSafeBrowsingHit instead') + super.androidOnSafeBrowsingHit, + super.onSafeBrowsingHit, + @Deprecated('Use onPermissionRequest instead') + super.androidOnPermissionRequest, + super.onPermissionRequest, + @Deprecated('Use onGeolocationPermissionsShowPrompt instead') + super.androidOnGeolocationPermissionsShowPrompt, + super.onGeolocationPermissionsShowPrompt, + @Deprecated('Use onGeolocationPermissionsHidePrompt instead') + super.androidOnGeolocationPermissionsHidePrompt, + super.onGeolocationPermissionsHidePrompt, + @Deprecated('Use shouldInterceptRequest instead') + super.androidShouldInterceptRequest, + super.shouldInterceptRequest, + @Deprecated('Use onRenderProcessGone instead') + super.androidOnRenderProcessGone, + super.onRenderProcessGone, + @Deprecated('Use onRenderProcessResponsive instead') + super.androidOnRenderProcessResponsive, + super.onRenderProcessResponsive, + @Deprecated('Use onRenderProcessUnresponsive instead') + super.androidOnRenderProcessUnresponsive, + super.onRenderProcessUnresponsive, + @Deprecated('Use onFormResubmission instead') + super.androidOnFormResubmission, + super.onFormResubmission, + @Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged, + @Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon, + super.onReceivedIcon, + @Deprecated('Use onReceivedTouchIconUrl instead') + super.androidOnReceivedTouchIconUrl, + super.onReceivedTouchIconUrl, + @Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload, + super.onJsBeforeUnload, + @Deprecated('Use onReceivedLoginRequest instead') + super.androidOnReceivedLoginRequest, + super.onReceivedLoginRequest, + super.onPermissionRequestCanceled, + super.onRequestFocus, + @Deprecated('Use onWebContentProcessDidTerminate instead') + super.iosOnWebContentProcessDidTerminate, + super.onWebContentProcessDidTerminate, + @Deprecated( + 'Use onDidReceiveServerRedirectForProvisionalNavigation instead') + super.iosOnDidReceiveServerRedirectForProvisionalNavigation, + super.onDidReceiveServerRedirectForProvisionalNavigation, + @Deprecated('Use onNavigationResponse instead') + super.iosOnNavigationResponse, + super.onNavigationResponse, + @Deprecated('Use shouldAllowDeprecatedTLS instead') + super.iosShouldAllowDeprecatedTLS, + super.shouldAllowDeprecatedTLS, + super.onCameraCaptureStateChanged, + super.onMicrophoneCaptureStateChanged, + super.onContentSizeChanged, + super.initialUrlRequest, + super.initialFile, + super.initialData, + @Deprecated('Use initialSettings instead') super.initialOptions, + super.initialSettings, + super.contextMenu, + super.initialUserScripts, + this.pullToRefreshController, + this.findInteractionController}); + + /// Creates a [IOSHeadlessInAppWebViewCreationParams] instance based on [PlatformHeadlessInAppWebViewCreationParams]. + IOSHeadlessInAppWebViewCreationParams.fromPlatformHeadlessInAppWebViewCreationParams( + PlatformHeadlessInAppWebViewCreationParams params) + : this( + controllerFromPlatform: params.controllerFromPlatform, + initialSize: params.initialSize, + windowId: params.windowId, + onWebViewCreated: params.onWebViewCreated, + onLoadStart: params.onLoadStart, + onLoadStop: params.onLoadStop, + onLoadError: params.onLoadError, + onReceivedError: params.onReceivedError, + onLoadHttpError: params.onLoadHttpError, + onReceivedHttpError: params.onReceivedHttpError, + onProgressChanged: params.onProgressChanged, + onConsoleMessage: params.onConsoleMessage, + shouldOverrideUrlLoading: params.shouldOverrideUrlLoading, + onLoadResource: params.onLoadResource, + onScrollChanged: params.onScrollChanged, + onDownloadStart: params.onDownloadStart, + onDownloadStartRequest: params.onDownloadStartRequest, + onLoadResourceCustomScheme: params.onLoadResourceCustomScheme, + onLoadResourceWithCustomScheme: + params.onLoadResourceWithCustomScheme, + onCreateWindow: params.onCreateWindow, + onCloseWindow: params.onCloseWindow, + onJsAlert: params.onJsAlert, + onJsConfirm: params.onJsConfirm, + onJsPrompt: params.onJsPrompt, + onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest, + onReceivedServerTrustAuthRequest: + params.onReceivedServerTrustAuthRequest, + onReceivedClientCertRequest: params.onReceivedClientCertRequest, + onFindResultReceived: params.onFindResultReceived, + shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest, + onAjaxReadyStateChange: params.onAjaxReadyStateChange, + onAjaxProgress: params.onAjaxProgress, + shouldInterceptFetchRequest: params.shouldInterceptFetchRequest, + onUpdateVisitedHistory: params.onUpdateVisitedHistory, + onPrint: params.onPrint, + onPrintRequest: params.onPrintRequest, + onLongPressHitTestResult: params.onLongPressHitTestResult, + onEnterFullscreen: params.onEnterFullscreen, + onExitFullscreen: params.onExitFullscreen, + onPageCommitVisible: params.onPageCommitVisible, + onTitleChanged: params.onTitleChanged, + onWindowFocus: params.onWindowFocus, + onWindowBlur: params.onWindowBlur, + onOverScrolled: params.onOverScrolled, + onZoomScaleChanged: params.onZoomScaleChanged, + androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit, + onSafeBrowsingHit: params.onSafeBrowsingHit, + androidOnPermissionRequest: params.androidOnPermissionRequest, + onPermissionRequest: params.onPermissionRequest, + androidOnGeolocationPermissionsShowPrompt: + params.androidOnGeolocationPermissionsShowPrompt, + onGeolocationPermissionsShowPrompt: + params.onGeolocationPermissionsShowPrompt, + androidOnGeolocationPermissionsHidePrompt: + params.androidOnGeolocationPermissionsHidePrompt, + onGeolocationPermissionsHidePrompt: + params.onGeolocationPermissionsHidePrompt, + androidShouldInterceptRequest: params.androidShouldInterceptRequest, + shouldInterceptRequest: params.shouldInterceptRequest, + androidOnRenderProcessGone: params.androidOnRenderProcessGone, + onRenderProcessGone: params.onRenderProcessGone, + androidOnRenderProcessResponsive: + params.androidOnRenderProcessResponsive, + onRenderProcessResponsive: params.onRenderProcessResponsive, + androidOnRenderProcessUnresponsive: + params.androidOnRenderProcessUnresponsive, + onRenderProcessUnresponsive: params.onRenderProcessUnresponsive, + androidOnFormResubmission: params.androidOnFormResubmission, + onFormResubmission: params.onFormResubmission, + androidOnScaleChanged: params.androidOnScaleChanged, + androidOnReceivedIcon: params.androidOnReceivedIcon, + onReceivedIcon: params.onReceivedIcon, + androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl, + onReceivedTouchIconUrl: params.onReceivedTouchIconUrl, + androidOnJsBeforeUnload: params.androidOnJsBeforeUnload, + onJsBeforeUnload: params.onJsBeforeUnload, + androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest, + onReceivedLoginRequest: params.onReceivedLoginRequest, + onPermissionRequestCanceled: params.onPermissionRequestCanceled, + onRequestFocus: params.onRequestFocus, + iosOnWebContentProcessDidTerminate: + params.iosOnWebContentProcessDidTerminate, + onWebContentProcessDidTerminate: + params.onWebContentProcessDidTerminate, + iosOnDidReceiveServerRedirectForProvisionalNavigation: + params.iosOnDidReceiveServerRedirectForProvisionalNavigation, + onDidReceiveServerRedirectForProvisionalNavigation: + params.onDidReceiveServerRedirectForProvisionalNavigation, + iosOnNavigationResponse: params.iosOnNavigationResponse, + onNavigationResponse: params.onNavigationResponse, + iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS, + shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS, + onCameraCaptureStateChanged: params.onCameraCaptureStateChanged, + onMicrophoneCaptureStateChanged: + params.onMicrophoneCaptureStateChanged, + onContentSizeChanged: params.onContentSizeChanged, + initialUrlRequest: params.initialUrlRequest, + initialFile: params.initialFile, + initialData: params.initialData, + initialOptions: params.initialOptions, + initialSettings: params.initialSettings, + contextMenu: params.contextMenu, + initialUserScripts: params.initialUserScripts, + pullToRefreshController: params.pullToRefreshController + as IOSPullToRefreshController?, + findInteractionController: params.findInteractionController + as IOSFindInteractionController?); + + @override + final IOSFindInteractionController? findInteractionController; + + @override + final IOSPullToRefreshController? pullToRefreshController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView} +class IOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView + with ChannelController { + @override + late final String id; + + bool _started = false; + bool _running = false; + + static const MethodChannel _sharedChannel = + const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview'); + + IOSInAppWebViewController? _webViewController; + + /// Constructs a [IOSHeadlessInAppWebView]. + IOSHeadlessInAppWebView(PlatformHeadlessInAppWebViewCreationParams params) + : super.implementation( + params is IOSHeadlessInAppWebViewCreationParams + ? params + : IOSHeadlessInAppWebViewCreationParams + .fromPlatformHeadlessInAppWebViewCreationParams(params), + ) { + id = IdGenerator.generate(); + } + + @override + IOSInAppWebViewController? get webViewController => _webViewController; + + dynamic _controllerFromPlatform; + + IOSHeadlessInAppWebViewCreationParams get _iosParams => + params as IOSHeadlessInAppWebViewCreationParams; + + _init() { + _webViewController = IOSInAppWebViewController( + IOSInAppWebViewControllerCreationParams( + id: id, webviewParams: params), + ); + _controllerFromPlatform = + params.controllerFromPlatform?.call(_webViewController!) ?? + _webViewController!; + _iosParams.pullToRefreshController?.init(id); + _iosParams.findInteractionController?.init(id); + channel = + MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onWebViewCreated": + if (params.onWebViewCreated != null && _webViewController != null) { + params.onWebViewCreated!(_controllerFromPlatform); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + Future run() async { + if (_started) { + return; + } + _started = true; + _init(); + + final initialSettings = params.initialSettings ?? InAppWebViewSettings(); + _inferInitialSettings(initialSettings); + + Map settingsMap = + (params.initialSettings != null ? initialSettings.toMap() : null) ?? + params.initialOptions?.toMap() ?? + initialSettings.toMap(); + + Map pullToRefreshSettings = + _iosParams.pullToRefreshController?.params.settings.toMap() ?? + _iosParams.pullToRefreshController?.params.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent( + 'params', + () => { + 'initialUrlRequest': params.initialUrlRequest?.toMap(), + 'initialFile': params.initialFile, + 'initialData': params.initialData?.toMap(), + 'initialSettings': settingsMap, + 'contextMenu': params.contextMenu?.toMap() ?? {}, + 'windowId': params.windowId, + 'initialUserScripts': + params.initialUserScripts?.map((e) => e.toMap()).toList() ?? + [], + 'pullToRefreshSettings': pullToRefreshSettings, + 'initialSize': params.initialSize.toMap() + }); + await _sharedChannel.invokeMethod('run', args); + _running = true; + } + + void _inferInitialSettings(InAppWebViewSettings settings) { + if (params.shouldOverrideUrlLoading != null && + settings.useShouldOverrideUrlLoading == null) { + settings.useShouldOverrideUrlLoading = true; + } + if (params.onLoadResource != null && settings.useOnLoadResource == null) { + settings.useOnLoadResource = true; + } + if (params.onDownloadStartRequest != null && + settings.useOnDownloadStart == null) { + settings.useOnDownloadStart = true; + } + if (params.shouldInterceptAjaxRequest != null && + settings.useShouldInterceptAjaxRequest == null) { + settings.useShouldInterceptAjaxRequest = true; + } + if (params.shouldInterceptFetchRequest != null && + settings.useShouldInterceptFetchRequest == null) { + settings.useShouldInterceptFetchRequest = true; + } + if (params.shouldInterceptRequest != null && + settings.useShouldInterceptRequest == null) { + settings.useShouldInterceptRequest = true; + } + if (params.onRenderProcessGone != null && + settings.useOnRenderProcessGone == null) { + settings.useOnRenderProcessGone = true; + } + if (params.onNavigationResponse != null && + settings.useOnNavigationResponse == null) { + settings.useOnNavigationResponse = true; + } + } + + @override + bool isRunning() { + return _running; + } + + @override + Future setSize(Size size) async { + if (!_running) { + return; + } + + Map args = {}; + args.putIfAbsent('size', () => size.toMap()); + await channel?.invokeMethod('setSize', args); + } + + @override + Future getSize() async { + if (!_running) { + return null; + } + + Map args = {}; + Map sizeMap = + (await channel?.invokeMethod('getSize', args))?.cast(); + return MapSize.fromMap(sizeMap); + } + + @override + Future dispose() async { + if (!_running) { + return; + } + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + _started = false; + _running = false; + _webViewController?.dispose(); + _webViewController = null; + _controllerFromPlatform = null; + _iosParams.pullToRefreshController?.dispose(); + _iosParams.findInteractionController?.dispose(); + } +} + +extension InternalHeadlessInAppWebView on IOSHeadlessInAppWebView { + Future internalDispose() async { + _started = false; + _running = false; + } +} diff --git a/flutter_inappwebview_ios/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_ios/lib/src/in_app_webview/in_app_webview.dart new file mode 100755 index 00000000..a9bcdd82 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/in_app_webview/in_app_webview.dart @@ -0,0 +1,417 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'headless_in_app_webview.dart'; + +import '../find_interaction/find_interaction_controller.dart'; +import 'in_app_webview_controller.dart'; +import '../pull_to_refresh/main.dart'; +import '../pull_to_refresh/pull_to_refresh_controller.dart'; + +/// Object specifying creation parameters for creating a [PlatformInAppWebViewWidget]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +class IOSInAppWebViewWidgetCreationParams + extends PlatformInAppWebViewWidgetCreationParams { + IOSInAppWebViewWidgetCreationParams( + {super.controllerFromPlatform, + super.key, + super.layoutDirection, + super.gestureRecognizers, + super.headlessWebView, + super.keepAlive, + super.preventGestureDelay, + super.windowId, + super.onWebViewCreated, + super.onLoadStart, + super.onLoadStop, + @Deprecated('Use onReceivedError instead') super.onLoadError, + super.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError, + super.onReceivedHttpError, + super.onProgressChanged, + super.onConsoleMessage, + super.shouldOverrideUrlLoading, + super.onLoadResource, + super.onScrollChanged, + @Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart, + super.onDownloadStartRequest, + @Deprecated('Use onLoadResourceWithCustomScheme instead') + super.onLoadResourceCustomScheme, + super.onLoadResourceWithCustomScheme, + super.onCreateWindow, + super.onCloseWindow, + super.onJsAlert, + super.onJsConfirm, + super.onJsPrompt, + super.onReceivedHttpAuthRequest, + super.onReceivedServerTrustAuthRequest, + super.onReceivedClientCertRequest, + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + super.onFindResultReceived, + super.shouldInterceptAjaxRequest, + super.onAjaxReadyStateChange, + super.onAjaxProgress, + super.shouldInterceptFetchRequest, + super.onUpdateVisitedHistory, + @Deprecated("Use onPrintRequest instead") super.onPrint, + super.onPrintRequest, + super.onLongPressHitTestResult, + super.onEnterFullscreen, + super.onExitFullscreen, + super.onPageCommitVisible, + super.onTitleChanged, + super.onWindowFocus, + super.onWindowBlur, + super.onOverScrolled, + super.onZoomScaleChanged, + @Deprecated('Use onSafeBrowsingHit instead') + super.androidOnSafeBrowsingHit, + super.onSafeBrowsingHit, + @Deprecated('Use onPermissionRequest instead') + super.androidOnPermissionRequest, + super.onPermissionRequest, + @Deprecated('Use onGeolocationPermissionsShowPrompt instead') + super.androidOnGeolocationPermissionsShowPrompt, + super.onGeolocationPermissionsShowPrompt, + @Deprecated('Use onGeolocationPermissionsHidePrompt instead') + super.androidOnGeolocationPermissionsHidePrompt, + super.onGeolocationPermissionsHidePrompt, + @Deprecated('Use shouldInterceptRequest instead') + super.androidShouldInterceptRequest, + super.shouldInterceptRequest, + @Deprecated('Use onRenderProcessGone instead') + super.androidOnRenderProcessGone, + super.onRenderProcessGone, + @Deprecated('Use onRenderProcessResponsive instead') + super.androidOnRenderProcessResponsive, + super.onRenderProcessResponsive, + @Deprecated('Use onRenderProcessUnresponsive instead') + super.androidOnRenderProcessUnresponsive, + super.onRenderProcessUnresponsive, + @Deprecated('Use onFormResubmission instead') + super.androidOnFormResubmission, + super.onFormResubmission, + @Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged, + @Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon, + super.onReceivedIcon, + @Deprecated('Use onReceivedTouchIconUrl instead') + super.androidOnReceivedTouchIconUrl, + super.onReceivedTouchIconUrl, + @Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload, + super.onJsBeforeUnload, + @Deprecated('Use onReceivedLoginRequest instead') + super.androidOnReceivedLoginRequest, + super.onReceivedLoginRequest, + super.onPermissionRequestCanceled, + super.onRequestFocus, + @Deprecated('Use onWebContentProcessDidTerminate instead') + super.iosOnWebContentProcessDidTerminate, + super.onWebContentProcessDidTerminate, + @Deprecated( + 'Use onDidReceiveServerRedirectForProvisionalNavigation instead') + super.iosOnDidReceiveServerRedirectForProvisionalNavigation, + super.onDidReceiveServerRedirectForProvisionalNavigation, + @Deprecated('Use onNavigationResponse instead') + super.iosOnNavigationResponse, + super.onNavigationResponse, + @Deprecated('Use shouldAllowDeprecatedTLS instead') + super.iosShouldAllowDeprecatedTLS, + super.shouldAllowDeprecatedTLS, + super.onCameraCaptureStateChanged, + super.onMicrophoneCaptureStateChanged, + super.onContentSizeChanged, + super.initialUrlRequest, + super.initialFile, + super.initialData, + @Deprecated('Use initialSettings instead') super.initialOptions, + super.initialSettings, + super.contextMenu, + super.initialUserScripts, + this.pullToRefreshController, + this.findInteractionController}); + + /// Constructs a [IOSInAppWebViewWidgetCreationParams] using a + /// [PlatformInAppWebViewWidgetCreationParams]. + IOSInAppWebViewWidgetCreationParams.fromPlatformInAppWebViewWidgetCreationParams( + PlatformInAppWebViewWidgetCreationParams params) + : this( + controllerFromPlatform: params.controllerFromPlatform, + key: params.key, + layoutDirection: params.layoutDirection, + gestureRecognizers: params.gestureRecognizers, + headlessWebView: params.headlessWebView, + keepAlive: params.keepAlive, + preventGestureDelay: params.preventGestureDelay, + windowId: params.windowId, + onWebViewCreated: params.onWebViewCreated, + onLoadStart: params.onLoadStart, + onLoadStop: params.onLoadStop, + onLoadError: params.onLoadError, + onReceivedError: params.onReceivedError, + onLoadHttpError: params.onLoadHttpError, + onReceivedHttpError: params.onReceivedHttpError, + onProgressChanged: params.onProgressChanged, + onConsoleMessage: params.onConsoleMessage, + shouldOverrideUrlLoading: params.shouldOverrideUrlLoading, + onLoadResource: params.onLoadResource, + onScrollChanged: params.onScrollChanged, + onDownloadStart: params.onDownloadStart, + onDownloadStartRequest: params.onDownloadStartRequest, + onLoadResourceCustomScheme: params.onLoadResourceCustomScheme, + onLoadResourceWithCustomScheme: + params.onLoadResourceWithCustomScheme, + onCreateWindow: params.onCreateWindow, + onCloseWindow: params.onCloseWindow, + onJsAlert: params.onJsAlert, + onJsConfirm: params.onJsConfirm, + onJsPrompt: params.onJsPrompt, + onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest, + onReceivedServerTrustAuthRequest: + params.onReceivedServerTrustAuthRequest, + onReceivedClientCertRequest: params.onReceivedClientCertRequest, + onFindResultReceived: params.onFindResultReceived, + shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest, + onAjaxReadyStateChange: params.onAjaxReadyStateChange, + onAjaxProgress: params.onAjaxProgress, + shouldInterceptFetchRequest: params.shouldInterceptFetchRequest, + onUpdateVisitedHistory: params.onUpdateVisitedHistory, + onPrint: params.onPrint, + onPrintRequest: params.onPrintRequest, + onLongPressHitTestResult: params.onLongPressHitTestResult, + onEnterFullscreen: params.onEnterFullscreen, + onExitFullscreen: params.onExitFullscreen, + onPageCommitVisible: params.onPageCommitVisible, + onTitleChanged: params.onTitleChanged, + onWindowFocus: params.onWindowFocus, + onWindowBlur: params.onWindowBlur, + onOverScrolled: params.onOverScrolled, + onZoomScaleChanged: params.onZoomScaleChanged, + androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit, + onSafeBrowsingHit: params.onSafeBrowsingHit, + androidOnPermissionRequest: params.androidOnPermissionRequest, + onPermissionRequest: params.onPermissionRequest, + androidOnGeolocationPermissionsShowPrompt: + params.androidOnGeolocationPermissionsShowPrompt, + onGeolocationPermissionsShowPrompt: + params.onGeolocationPermissionsShowPrompt, + androidOnGeolocationPermissionsHidePrompt: + params.androidOnGeolocationPermissionsHidePrompt, + onGeolocationPermissionsHidePrompt: + params.onGeolocationPermissionsHidePrompt, + androidShouldInterceptRequest: params.androidShouldInterceptRequest, + shouldInterceptRequest: params.shouldInterceptRequest, + androidOnRenderProcessGone: params.androidOnRenderProcessGone, + onRenderProcessGone: params.onRenderProcessGone, + androidOnRenderProcessResponsive: + params.androidOnRenderProcessResponsive, + onRenderProcessResponsive: params.onRenderProcessResponsive, + androidOnRenderProcessUnresponsive: + params.androidOnRenderProcessUnresponsive, + onRenderProcessUnresponsive: params.onRenderProcessUnresponsive, + androidOnFormResubmission: params.androidOnFormResubmission, + onFormResubmission: params.onFormResubmission, + androidOnScaleChanged: params.androidOnScaleChanged, + androidOnReceivedIcon: params.androidOnReceivedIcon, + onReceivedIcon: params.onReceivedIcon, + androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl, + onReceivedTouchIconUrl: params.onReceivedTouchIconUrl, + androidOnJsBeforeUnload: params.androidOnJsBeforeUnload, + onJsBeforeUnload: params.onJsBeforeUnload, + androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest, + onReceivedLoginRequest: params.onReceivedLoginRequest, + onPermissionRequestCanceled: params.onPermissionRequestCanceled, + onRequestFocus: params.onRequestFocus, + iosOnWebContentProcessDidTerminate: + params.iosOnWebContentProcessDidTerminate, + onWebContentProcessDidTerminate: + params.onWebContentProcessDidTerminate, + iosOnDidReceiveServerRedirectForProvisionalNavigation: + params.iosOnDidReceiveServerRedirectForProvisionalNavigation, + onDidReceiveServerRedirectForProvisionalNavigation: + params.onDidReceiveServerRedirectForProvisionalNavigation, + iosOnNavigationResponse: params.iosOnNavigationResponse, + onNavigationResponse: params.onNavigationResponse, + iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS, + shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS, + onCameraCaptureStateChanged: params.onCameraCaptureStateChanged, + onMicrophoneCaptureStateChanged: + params.onMicrophoneCaptureStateChanged, + onContentSizeChanged: params.onContentSizeChanged, + initialUrlRequest: params.initialUrlRequest, + initialFile: params.initialFile, + initialData: params.initialData, + initialOptions: params.initialOptions, + initialSettings: params.initialSettings, + contextMenu: params.contextMenu, + initialUserScripts: params.initialUserScripts, + pullToRefreshController: params.pullToRefreshController + as IOSPullToRefreshController?, + findInteractionController: params.findInteractionController + as IOSFindInteractionController?); + + @override + final IOSFindInteractionController? findInteractionController; + + @override + final IOSPullToRefreshController? pullToRefreshController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} +class IOSInAppWebViewWidget extends PlatformInAppWebViewWidget { + /// Constructs a [IOSInAppWebViewWidget]. + /// + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} + IOSInAppWebViewWidget(PlatformInAppWebViewWidgetCreationParams params) + : super.implementation( + params is IOSInAppWebViewWidgetCreationParams + ? params + : IOSInAppWebViewWidgetCreationParams + .fromPlatformInAppWebViewWidgetCreationParams(params), + ); + + IOSInAppWebViewWidgetCreationParams get _iosParams => + params as IOSInAppWebViewWidgetCreationParams; + + IOSInAppWebViewController? _controller; + + IOSHeadlessInAppWebView? get _iosHeadlessInAppWebView => + _iosParams.headlessWebView as IOSHeadlessInAppWebView?; + + @override + Widget build(BuildContext context) { + final initialSettings = + _iosParams.initialSettings ?? InAppWebViewSettings(); + _inferInitialSettings(initialSettings); + + Map settingsMap = (_iosParams.initialSettings != null + ? initialSettings.toMap() + : null) ?? + // ignore: deprecated_member_use_from_same_package + _iosParams.initialOptions?.toMap() ?? + initialSettings.toMap(); + + Map pullToRefreshSettings = + _iosParams.pullToRefreshController?.params.settings.toMap() ?? + // ignore: deprecated_member_use_from_same_package + _iosParams.pullToRefreshController?.params.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + if ((_iosParams.headlessWebView?.isRunning() ?? false) && + _iosParams.keepAlive != null) { + final headlessId = _iosParams.headlessWebView?.id; + if (headlessId != null) { + // force keep alive id to match headless webview id + _iosParams.keepAlive?.id = headlessId; + } + } + + return UiKitView( + viewType: 'com.pichillilorenzo/flutter_inappwebview', + onPlatformViewCreated: _onPlatformViewCreated, + gestureRecognizers: _iosParams.gestureRecognizers, + creationParams: { + 'initialUrlRequest': _iosParams.initialUrlRequest?.toMap(), + 'initialFile': _iosParams.initialFile, + 'initialData': _iosParams.initialData?.toMap(), + 'initialSettings': settingsMap, + 'contextMenu': _iosParams.contextMenu?.toMap() ?? {}, + 'windowId': _iosParams.windowId, + 'headlessWebViewId': _iosParams.headlessWebView?.isRunning() ?? false + ? _iosParams.headlessWebView?.id + : null, + 'initialUserScripts': + _iosParams.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], + 'pullToRefreshSettings': pullToRefreshSettings, + 'keepAliveId': _iosParams.keepAlive?.id, + 'preventGestureDelay': _iosParams.preventGestureDelay + }, + creationParamsCodec: const StandardMessageCodec(), + ); + } + + void _onPlatformViewCreated(int id) { + dynamic viewId = id; + if (_iosParams.headlessWebView?.isRunning() ?? false) { + viewId = _iosParams.headlessWebView?.id; + } + viewId = _iosParams.keepAlive?.id ?? viewId ?? id; + _iosHeadlessInAppWebView?.internalDispose(); + _controller = IOSInAppWebViewController( + PlatformInAppWebViewControllerCreationParams( + id: viewId, webviewParams: params)); + _iosParams.pullToRefreshController?.init(viewId); + _iosParams.findInteractionController?.init(viewId); + debugLog( + className: runtimeType.toString(), + id: viewId?.toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: "onWebViewCreated", + args: []); + if (_iosParams.onWebViewCreated != null) { + _iosParams.onWebViewCreated!( + params.controllerFromPlatform?.call(_controller!) ?? _controller!); + } + } + + void _inferInitialSettings(InAppWebViewSettings settings) { + if (_iosParams.shouldOverrideUrlLoading != null && + settings.useShouldOverrideUrlLoading == null) { + settings.useShouldOverrideUrlLoading = true; + } + if (_iosParams.onLoadResource != null && + settings.useOnLoadResource == null) { + settings.useOnLoadResource = true; + } + if (_iosParams.onDownloadStartRequest != null && + settings.useOnDownloadStart == null) { + settings.useOnDownloadStart = true; + } + if (_iosParams.shouldInterceptAjaxRequest != null && + settings.useShouldInterceptAjaxRequest == null) { + settings.useShouldInterceptAjaxRequest = true; + } + if (_iosParams.shouldInterceptFetchRequest != null && + settings.useShouldInterceptFetchRequest == null) { + settings.useShouldInterceptFetchRequest = true; + } + if (_iosParams.shouldInterceptRequest != null && + settings.useShouldInterceptRequest == null) { + settings.useShouldInterceptRequest = true; + } + if (_iosParams.onRenderProcessGone != null && + settings.useOnRenderProcessGone == null) { + settings.useOnRenderProcessGone = true; + } + if (_iosParams.onNavigationResponse != null && + settings.useOnNavigationResponse == null) { + settings.useOnNavigationResponse = true; + } + } + + @override + void dispose() { + dynamic viewId = _controller?.getViewId(); + debugLog( + className: runtimeType.toString(), + id: viewId?.toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: "dispose", + args: []); + final isKeepAlive = _iosParams.keepAlive != null; + _controller?.dispose(isKeepAlive: isKeepAlive); + _controller = null; + _iosParams.pullToRefreshController?.dispose(isKeepAlive: isKeepAlive); + _iosParams.findInteractionController?.dispose(isKeepAlive: isKeepAlive); + } + + @override + T controllerFromPlatform(PlatformInAppWebViewController controller) { + // unused + throw UnimplementedError(); + } +} diff --git a/flutter_inappwebview_ios/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_ios/lib/src/in_app_webview/in_app_webview_controller.dart new file mode 100644 index 00000000..e3b4925f --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/in_app_webview/in_app_webview_controller.dart @@ -0,0 +1,2746 @@ +import 'dart:io'; +import 'dart:collection'; +import 'dart:convert'; +import 'dart:core'; +import 'dart:developer' as developer; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../web_message/main.dart'; + +import '../in_app_browser/in_app_browser.dart'; +import '../web_storage/web_storage.dart'; + +import 'headless_in_app_webview.dart'; +import '_static_channel.dart'; + +import '../print_job/main.dart'; + +///List of forbidden names for JavaScript handlers. +// ignore: non_constant_identifier_names +final _JAVASCRIPT_HANDLER_FORBIDDEN_NAMES = UnmodifiableListView([ + "onLoadResource", + "shouldInterceptAjaxRequest", + "onAjaxReadyStateChange", + "onAjaxProgress", + "shouldInterceptFetchRequest", + "onPrintRequest", + "onWindowFocus", + "onWindowBlur", + "callAsyncJavaScript", + "evaluateJavaScriptWithContentWorld" +]); + +/// Object specifying creation parameters for creating a [IOSInAppWebViewController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInAppWebViewControllerCreationParams] for +/// more information. +@immutable +class IOSInAppWebViewControllerCreationParams + extends PlatformInAppWebViewControllerCreationParams { + /// Creates a new [IOSInAppWebViewControllerCreationParams] instance. + const IOSInAppWebViewControllerCreationParams( + {required super.id, super.webviewParams}); + + /// Creates a [IOSInAppWebViewControllerCreationParams] instance based on [PlatformInAppWebViewControllerCreationParams]. + factory IOSInAppWebViewControllerCreationParams.fromPlatformInAppWebViewControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformInAppWebViewControllerCreationParams params) { + return IOSInAppWebViewControllerCreationParams( + id: params.id, webviewParams: params.webviewParams); + } +} + +///Controls a WebView, such as an [InAppWebView] widget instance, a [IOSHeadlessInAppWebView] instance or [IOSInAppBrowser] WebView instance. +/// +///If you are using the [InAppWebView] widget, an [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated] +///callback. Instead, if you are using an [IOSInAppBrowser] instance, you can get it through the [IOSInAppBrowser.webViewController] attribute. +class IOSInAppWebViewController extends PlatformInAppWebViewController + with ChannelController { + static final MethodChannel _staticChannel = IN_APP_WEBVIEW_STATIC_CHANNEL; + + // List of properties to be saved and restored for keep alive feature + Map _javaScriptHandlersMap = + HashMap(); + Map> _userScripts = { + UserScriptInjectionTime.AT_DOCUMENT_START: [], + UserScriptInjectionTime.AT_DOCUMENT_END: [] + }; + Set _webMessageListenerObjNames = Set(); + Map _injectedScriptsFromURL = {}; + Set _webMessageChannels = Set(); + Set _webMessageListeners = Set(); + + // static map that contains the properties to be saved and restored for keep alive feature + static final Map + _keepAliveMap = {}; + + IOSInAppBrowser? _inAppBrowser; + + PlatformInAppBrowserEvents? get _inAppBrowserEventHandler => + _inAppBrowser?.eventHandler; + + dynamic _controllerFromPlatform; + + @override + late IOSWebStorage webStorage; + + IOSInAppWebViewController( + PlatformInAppWebViewControllerCreationParams params) + : super.implementation(params + is IOSInAppWebViewControllerCreationParams + ? params + : IOSInAppWebViewControllerCreationParams + .fromPlatformInAppWebViewControllerCreationParams(params)) { + channel = MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id'); + handler = handleMethod; + initMethodCallHandler(); + + final initialUserScripts = webviewParams?.initialUserScripts; + if (initialUserScripts != null) { + for (final userScript in initialUserScripts) { + if (userScript.injectionTime == + UserScriptInjectionTime.AT_DOCUMENT_START) { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] + ?.add(userScript); + } else { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] + ?.add(userScript); + } + } + } + + this._init(params); + } + + static final IOSInAppWebViewController _staticValue = + IOSInAppWebViewController( + IOSInAppWebViewControllerCreationParams(id: null)); + + factory IOSInAppWebViewController.static() { + return _staticValue; + } + + IOSInAppWebViewController.fromInAppBrowser( + PlatformInAppWebViewControllerCreationParams params, + MethodChannel channel, + IOSInAppBrowser inAppBrowser, + UnmodifiableListView? initialUserScripts) + : super.implementation( + params is IOSInAppWebViewControllerCreationParams + ? params + : IOSInAppWebViewControllerCreationParams + .fromPlatformInAppWebViewControllerCreationParams(params)) { + this.channel = channel; + this._inAppBrowser = inAppBrowser; + + if (initialUserScripts != null) { + for (final userScript in initialUserScripts) { + if (userScript.injectionTime == + UserScriptInjectionTime.AT_DOCUMENT_START) { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] + ?.add(userScript); + } else { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] + ?.add(userScript); + } + } + } + this._init(params); + } + + void _init(PlatformInAppWebViewControllerCreationParams params) { + _controllerFromPlatform = + params.webviewParams?.controllerFromPlatform?.call(this) ?? this; + + webStorage = IOSWebStorage(IOSWebStorageCreationParams( + localStorage: IOSLocalStorage.defaultStorage(controller: this), + sessionStorage: + IOSSessionStorage.defaultStorage(controller: this))); + + if (params.webviewParams is PlatformInAppWebViewWidgetCreationParams) { + final keepAlive = + (params.webviewParams as PlatformInAppWebViewWidgetCreationParams) + .keepAlive; + if (keepAlive != null) { + InAppWebViewControllerKeepAliveProps? props = _keepAliveMap[keepAlive]; + if (props == null) { + // save controller properties to restore it later + _keepAliveMap[keepAlive] = InAppWebViewControllerKeepAliveProps( + injectedScriptsFromURL: _injectedScriptsFromURL, + javaScriptHandlersMap: _javaScriptHandlersMap, + userScripts: _userScripts, + webMessageListenerObjNames: _webMessageListenerObjNames, + webMessageChannels: _webMessageChannels, + webMessageListeners: _webMessageListeners); + } else { + // restore controller properties + _injectedScriptsFromURL = props.injectedScriptsFromURL; + _javaScriptHandlersMap = props.javaScriptHandlersMap; + _userScripts = props.userScripts; + _webMessageListenerObjNames = props.webMessageListenerObjNames; + _webMessageChannels = + props.webMessageChannels as Set; + _webMessageListeners = + props.webMessageListeners as Set; + } + } + } + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + name: _inAppBrowser == null + ? "WebView" + : _inAppBrowser.runtimeType.toString(), + id: (getViewId() ?? _inAppBrowser?.id).toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + if (PlatformInAppWebViewController.debugLoggingSettings.enabled && + call.method != "onCallJsHandler") { + _debugLog(call.method, call.arguments); + } + + switch (call.method) { + case "onLoadStart": + _injectedScriptsFromURL.clear(); + if ((webviewParams != null && webviewParams!.onLoadStart != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && webviewParams!.onLoadStart != null) + webviewParams!.onLoadStart!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onLoadStart(uri); + } + break; + case "onLoadStop": + if ((webviewParams != null && webviewParams!.onLoadStop != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && webviewParams!.onLoadStop != null) + webviewParams!.onLoadStop!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onLoadStop(uri); + } + break; + case "onReceivedError": + if ((webviewParams != null && + (webviewParams!.onReceivedError != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadError != null)) || + _inAppBrowserEventHandler != null) { + WebResourceRequest request = WebResourceRequest.fromMap( + call.arguments["request"].cast())!; + WebResourceError error = WebResourceError.fromMap( + call.arguments["error"].cast())!; + var isForMainFrame = request.isForMainFrame ?? false; + + if (webviewParams != null) { + if (webviewParams!.onReceivedError != null) + webviewParams!.onReceivedError!( + _controllerFromPlatform, request, error); + else if (isForMainFrame) { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadError!(_controllerFromPlatform, request.url, + error.type.toNativeValue() ?? -1, error.description); + } + } else { + if (isForMainFrame) { + _inAppBrowserEventHandler!.onLoadError(request.url, + error.type.toNativeValue() ?? -1, error.description); + } + _inAppBrowserEventHandler!.onReceivedError(request, error); + } + } + break; + case "onReceivedHttpError": + if ((webviewParams != null && + (webviewParams!.onReceivedHttpError != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadHttpError != null)) || + _inAppBrowserEventHandler != null) { + WebResourceRequest request = WebResourceRequest.fromMap( + call.arguments["request"].cast())!; + WebResourceResponse errorResponse = WebResourceResponse.fromMap( + call.arguments["errorResponse"].cast())!; + var isForMainFrame = request.isForMainFrame ?? false; + + if (webviewParams != null) { + if (webviewParams!.onReceivedHttpError != null) + webviewParams!.onReceivedHttpError!( + _controllerFromPlatform, request, errorResponse); + else if (isForMainFrame) { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadHttpError!( + _controllerFromPlatform, + request.url, + errorResponse.statusCode ?? -1, + errorResponse.reasonPhrase ?? ''); + } + } else { + if (isForMainFrame) { + _inAppBrowserEventHandler!.onLoadHttpError( + request.url, + errorResponse.statusCode ?? -1, + errorResponse.reasonPhrase ?? ''); + } + _inAppBrowserEventHandler! + .onReceivedHttpError(request, errorResponse); + } + } + break; + case "onProgressChanged": + if ((webviewParams != null && + webviewParams!.onProgressChanged != null) || + _inAppBrowserEventHandler != null) { + int progress = call.arguments["progress"]; + if (webviewParams != null && webviewParams!.onProgressChanged != null) + webviewParams!.onProgressChanged!( + _controllerFromPlatform, progress); + else + _inAppBrowserEventHandler!.onProgressChanged(progress); + } + break; + case "shouldOverrideUrlLoading": + if ((webviewParams != null && + webviewParams!.shouldOverrideUrlLoading != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + NavigationAction navigationAction = + NavigationAction.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldOverrideUrlLoading != null) + return (await webviewParams!.shouldOverrideUrlLoading!( + _controllerFromPlatform, navigationAction)) + ?.toNativeValue(); + return (await _inAppBrowserEventHandler! + .shouldOverrideUrlLoading(navigationAction)) + ?.toNativeValue(); + } + break; + case "onConsoleMessage": + if ((webviewParams != null && + webviewParams!.onConsoleMessage != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ConsoleMessage consoleMessage = ConsoleMessage.fromMap(arguments)!; + if (webviewParams != null && webviewParams!.onConsoleMessage != null) + webviewParams!.onConsoleMessage!( + _controllerFromPlatform, consoleMessage); + else + _inAppBrowserEventHandler!.onConsoleMessage(consoleMessage); + } + break; + case "onScrollChanged": + if ((webviewParams != null && webviewParams!.onScrollChanged != null) || + _inAppBrowserEventHandler != null) { + int x = call.arguments["x"]; + int y = call.arguments["y"]; + if (webviewParams != null && webviewParams!.onScrollChanged != null) + webviewParams!.onScrollChanged!(_controllerFromPlatform, x, y); + else + _inAppBrowserEventHandler!.onScrollChanged(x, y); + } + break; + case "onDownloadStartRequest": + if ((webviewParams != null && + // ignore: deprecated_member_use_from_same_package + (webviewParams!.onDownloadStart != null || + webviewParams!.onDownloadStartRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + DownloadStartRequest downloadStartRequest = + DownloadStartRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onDownloadStartRequest != null) + webviewParams!.onDownloadStartRequest!( + _controllerFromPlatform, downloadStartRequest); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onDownloadStart!( + _controllerFromPlatform, downloadStartRequest.url); + } + } else { + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .onDownloadStart(downloadStartRequest.url); + _inAppBrowserEventHandler! + .onDownloadStartRequest(downloadStartRequest); + } + } + break; + case "onLoadResourceWithCustomScheme": + if ((webviewParams != null && + (webviewParams!.onLoadResourceWithCustomScheme != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadResourceCustomScheme != null)) || + _inAppBrowserEventHandler != null) { + Map requestMap = + call.arguments["request"].cast(); + WebResourceRequest request = WebResourceRequest.fromMap(requestMap)!; + + if (webviewParams != null) { + if (webviewParams!.onLoadResourceWithCustomScheme != null) + return (await webviewParams!.onLoadResourceWithCustomScheme!( + _controllerFromPlatform, request)) + ?.toMap(); + else { + return (await params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .onLoadResourceCustomScheme!( + _controllerFromPlatform, request.url)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onLoadResourceWithCustomScheme(request)) ?? + (await _inAppBrowserEventHandler! + .onLoadResourceCustomScheme(request.url))) + ?.toMap(); + } + } + break; + case "onCreateWindow": + if ((webviewParams != null && webviewParams!.onCreateWindow != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + CreateWindowAction createWindowAction = + CreateWindowAction.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onCreateWindow != null) + return await webviewParams!.onCreateWindow!( + _controllerFromPlatform, createWindowAction); + else + return await _inAppBrowserEventHandler! + .onCreateWindow(createWindowAction); + } + break; + case "onCloseWindow": + if (webviewParams != null && webviewParams!.onCloseWindow != null) + webviewParams!.onCloseWindow!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onCloseWindow(); + break; + case "onTitleChanged": + if ((webviewParams != null && webviewParams!.onTitleChanged != null) || + _inAppBrowserEventHandler != null) { + String? title = call.arguments["title"]; + if (webviewParams != null && webviewParams!.onTitleChanged != null) + webviewParams!.onTitleChanged!(_controllerFromPlatform, title); + else + _inAppBrowserEventHandler!.onTitleChanged(title); + } + break; + case "onGeolocationPermissionsShowPrompt": + if ((webviewParams != null && + (webviewParams!.onGeolocationPermissionsShowPrompt != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsShowPrompt != + null)) || + _inAppBrowserEventHandler != null) { + String origin = call.arguments["origin"]; + + if (webviewParams != null) { + if (webviewParams!.onGeolocationPermissionsShowPrompt != null) + return (await webviewParams!.onGeolocationPermissionsShowPrompt!( + _controllerFromPlatform, origin)) + ?.toMap(); + else { + return (await params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .androidOnGeolocationPermissionsShowPrompt!( + _controllerFromPlatform, origin)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onGeolocationPermissionsShowPrompt(origin)) ?? + (await _inAppBrowserEventHandler! + .androidOnGeolocationPermissionsShowPrompt(origin))) + ?.toMap(); + } + } + break; + case "onGeolocationPermissionsHidePrompt": + if (webviewParams != null && + (webviewParams!.onGeolocationPermissionsHidePrompt != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsHidePrompt != + null)) { + if (webviewParams!.onGeolocationPermissionsHidePrompt != null) + webviewParams! + .onGeolocationPermissionsHidePrompt!(_controllerFromPlatform); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsHidePrompt!( + _controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onGeolocationPermissionsHidePrompt(); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnGeolocationPermissionsHidePrompt(); + } + break; + case "shouldInterceptRequest": + if ((webviewParams != null && + (webviewParams!.shouldInterceptRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidShouldInterceptRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + WebResourceRequest request = WebResourceRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.shouldInterceptRequest != null) + return (await webviewParams!.shouldInterceptRequest!( + _controllerFromPlatform, request)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidShouldInterceptRequest!( + _controllerFromPlatform, request)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .shouldInterceptRequest(request)) ?? + (await _inAppBrowserEventHandler! + .androidShouldInterceptRequest(request))) + ?.toMap(); + } + } + break; + case "onRenderProcessUnresponsive": + if ((webviewParams != null && + (webviewParams!.onRenderProcessUnresponsive != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessUnresponsive != + null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessUnresponsive != null) + return (await webviewParams!.onRenderProcessUnresponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnRenderProcessUnresponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onRenderProcessUnresponsive(uri)) ?? + (await _inAppBrowserEventHandler! + .androidOnRenderProcessUnresponsive(uri))) + ?.toNativeValue(); + } + } + break; + case "onRenderProcessResponsive": + if ((webviewParams != null && + (webviewParams!.onRenderProcessResponsive != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessResponsive != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessResponsive != null) + return (await webviewParams!.onRenderProcessResponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnRenderProcessResponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onRenderProcessResponsive(uri)) ?? + (await _inAppBrowserEventHandler! + .androidOnRenderProcessResponsive(uri))) + ?.toNativeValue(); + } + } + break; + case "onRenderProcessGone": + if ((webviewParams != null && + (webviewParams!.onRenderProcessGone != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessGone != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + RenderProcessGoneDetail detail = + RenderProcessGoneDetail.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessGone != null) + webviewParams!.onRenderProcessGone!( + _controllerFromPlatform, detail); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessGone!( + _controllerFromPlatform, detail); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onRenderProcessGone(detail); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.androidOnRenderProcessGone(detail); + } + } + break; + case "onFormResubmission": + if ((webviewParams != null && + (webviewParams!.onFormResubmission != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnFormResubmission != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onFormResubmission != null) + return (await webviewParams!.onFormResubmission!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnFormResubmission!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onFormResubmission(uri)) ?? + // ignore: deprecated_member_use_from_same_package + (await _inAppBrowserEventHandler! + .androidOnFormResubmission(uri))) + ?.toNativeValue(); + } + } + break; + case "onZoomScaleChanged": + if ((webviewParams != null && + // ignore: deprecated_member_use_from_same_package + (webviewParams!.androidOnScaleChanged != null || + webviewParams!.onZoomScaleChanged != null)) || + _inAppBrowserEventHandler != null) { + double oldScale = call.arguments["oldScale"]; + double newScale = call.arguments["newScale"]; + + if (webviewParams != null) { + if (webviewParams!.onZoomScaleChanged != null) + webviewParams!.onZoomScaleChanged!( + _controllerFromPlatform, oldScale, newScale); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnScaleChanged!( + _controllerFromPlatform, oldScale, newScale); + } + } else { + _inAppBrowserEventHandler!.onZoomScaleChanged(oldScale, newScale); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnScaleChanged(oldScale, newScale); + } + } + break; + case "onReceivedIcon": + if ((webviewParams != null && + (webviewParams!.onReceivedIcon != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedIcon != null)) || + _inAppBrowserEventHandler != null) { + Uint8List icon = + Uint8List.fromList(call.arguments["icon"].cast()); + + if (webviewParams != null) { + if (webviewParams!.onReceivedIcon != null) + webviewParams!.onReceivedIcon!(_controllerFromPlatform, icon); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedIcon!( + _controllerFromPlatform, icon); + } + } else { + _inAppBrowserEventHandler!.onReceivedIcon(icon); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.androidOnReceivedIcon(icon); + } + } + break; + case "onReceivedTouchIconUrl": + if ((webviewParams != null && + (webviewParams!.onReceivedTouchIconUrl != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedTouchIconUrl != null)) || + _inAppBrowserEventHandler != null) { + String url = call.arguments["url"]; + bool precomposed = call.arguments["precomposed"]; + WebUri uri = WebUri(url); + + if (webviewParams != null) { + if (webviewParams!.onReceivedTouchIconUrl != null) + webviewParams!.onReceivedTouchIconUrl!( + _controllerFromPlatform, uri, precomposed); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedTouchIconUrl!( + _controllerFromPlatform, uri, precomposed); + } + } else { + _inAppBrowserEventHandler!.onReceivedTouchIconUrl(uri, precomposed); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnReceivedTouchIconUrl(uri, precomposed); + } + } + break; + case "onJsAlert": + if ((webviewParams != null && webviewParams!.onJsAlert != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsAlertRequest jsAlertRequest = JsAlertRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsAlert != null) + return (await webviewParams!.onJsAlert!( + _controllerFromPlatform, jsAlertRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler!.onJsAlert(jsAlertRequest)) + ?.toMap(); + } + break; + case "onJsConfirm": + if ((webviewParams != null && webviewParams!.onJsConfirm != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsConfirmRequest jsConfirmRequest = + JsConfirmRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsConfirm != null) + return (await webviewParams!.onJsConfirm!( + _controllerFromPlatform, jsConfirmRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onJsConfirm(jsConfirmRequest)) + ?.toMap(); + } + break; + case "onJsPrompt": + if ((webviewParams != null && webviewParams!.onJsPrompt != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsPromptRequest jsPromptRequest = JsPromptRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsPrompt != null) + return (await webviewParams!.onJsPrompt!( + _controllerFromPlatform, jsPromptRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onJsPrompt(jsPromptRequest)) + ?.toMap(); + } + break; + case "onJsBeforeUnload": + if ((webviewParams != null && + (webviewParams!.onJsBeforeUnload != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnJsBeforeUnload != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsBeforeUnloadRequest jsBeforeUnloadRequest = + JsBeforeUnloadRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onJsBeforeUnload != null) + return (await webviewParams!.onJsBeforeUnload!( + _controllerFromPlatform, jsBeforeUnloadRequest)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnJsBeforeUnload!( + _controllerFromPlatform, jsBeforeUnloadRequest)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onJsBeforeUnload(jsBeforeUnloadRequest)) ?? + (await _inAppBrowserEventHandler! + .androidOnJsBeforeUnload(jsBeforeUnloadRequest))) + ?.toMap(); + } + } + break; + case "onSafeBrowsingHit": + if ((webviewParams != null && + (webviewParams!.onSafeBrowsingHit != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnSafeBrowsingHit != null)) || + _inAppBrowserEventHandler != null) { + String url = call.arguments["url"]; + SafeBrowsingThreat? threatType = + SafeBrowsingThreat.fromNativeValue(call.arguments["threatType"]); + WebUri uri = WebUri(url); + + if (webviewParams != null) { + if (webviewParams!.onSafeBrowsingHit != null) + return (await webviewParams!.onSafeBrowsingHit!( + _controllerFromPlatform, uri, threatType)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnSafeBrowsingHit!( + _controllerFromPlatform, uri, threatType)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onSafeBrowsingHit(uri, threatType)) ?? + (await _inAppBrowserEventHandler! + .androidOnSafeBrowsingHit(uri, threatType))) + ?.toMap(); + } + } + break; + case "onReceivedLoginRequest": + if ((webviewParams != null && + (webviewParams!.onReceivedLoginRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedLoginRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + LoginRequest loginRequest = LoginRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onReceivedLoginRequest != null) + webviewParams!.onReceivedLoginRequest!( + _controllerFromPlatform, loginRequest); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedLoginRequest!( + _controllerFromPlatform, loginRequest); + } + } else { + _inAppBrowserEventHandler!.onReceivedLoginRequest(loginRequest); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnReceivedLoginRequest(loginRequest); + } + } + break; + case "onPermissionRequestCanceled": + if ((webviewParams != null && + webviewParams!.onPermissionRequestCanceled != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + PermissionRequest permissionRequest = + PermissionRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onPermissionRequestCanceled != null) + webviewParams!.onPermissionRequestCanceled!( + _controllerFromPlatform, permissionRequest); + else + _inAppBrowserEventHandler! + .onPermissionRequestCanceled(permissionRequest); + } + break; + case "onRequestFocus": + if ((webviewParams != null && webviewParams!.onRequestFocus != null) || + _inAppBrowserEventHandler != null) { + if (webviewParams != null && webviewParams!.onRequestFocus != null) + webviewParams!.onRequestFocus!(_controllerFromPlatform); + else + _inAppBrowserEventHandler!.onRequestFocus(); + } + break; + case "onReceivedHttpAuthRequest": + if ((webviewParams != null && + webviewParams!.onReceivedHttpAuthRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + HttpAuthenticationChallenge challenge = + HttpAuthenticationChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedHttpAuthRequest != null) + return (await webviewParams!.onReceivedHttpAuthRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedHttpAuthRequest(challenge)) + ?.toMap(); + } + break; + case "onReceivedServerTrustAuthRequest": + if ((webviewParams != null && + webviewParams!.onReceivedServerTrustAuthRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ServerTrustChallenge challenge = + ServerTrustChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedServerTrustAuthRequest != null) + return (await webviewParams!.onReceivedServerTrustAuthRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedServerTrustAuthRequest(challenge)) + ?.toMap(); + } + break; + case "onReceivedClientCertRequest": + if ((webviewParams != null && + webviewParams!.onReceivedClientCertRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ClientCertChallenge challenge = + ClientCertChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedClientCertRequest != null) + return (await webviewParams!.onReceivedClientCertRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedClientCertRequest(challenge)) + ?.toMap(); + } + break; + case "onFindResultReceived": + if ((webviewParams != null && + (webviewParams!.onFindResultReceived != null || + (webviewParams!.findInteractionController != null && + webviewParams!.findInteractionController!.params + .onFindResultReceived != + null))) || + _inAppBrowserEventHandler != null) { + int activeMatchOrdinal = call.arguments["activeMatchOrdinal"]; + int numberOfMatches = call.arguments["numberOfMatches"]; + bool isDoneCounting = call.arguments["isDoneCounting"]; + if (webviewParams != null) { + if (webviewParams!.findInteractionController != null && + webviewParams!.findInteractionController!.params + .onFindResultReceived != + null) + webviewParams! + .findInteractionController!.params.onFindResultReceived!( + webviewParams!.findInteractionController!, + activeMatchOrdinal, + numberOfMatches, + isDoneCounting); + else + webviewParams!.onFindResultReceived!(_controllerFromPlatform, + activeMatchOrdinal, numberOfMatches, isDoneCounting); + } else { + if (_inAppBrowser!.findInteractionController != null && + _inAppBrowser! + .findInteractionController!.onFindResultReceived != + null) + _inAppBrowser!.findInteractionController!.onFindResultReceived!( + webviewParams!.findInteractionController!, + activeMatchOrdinal, + numberOfMatches, + isDoneCounting); + else + _inAppBrowserEventHandler!.onFindResultReceived( + activeMatchOrdinal, numberOfMatches, isDoneCounting); + } + } + break; + case "onPermissionRequest": + if ((webviewParams != null && + (webviewParams!.onPermissionRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnPermissionRequest != null)) || + _inAppBrowserEventHandler != null) { + String origin = call.arguments["origin"]; + List resources = call.arguments["resources"].cast(); + + Map arguments = + call.arguments.cast(); + PermissionRequest permissionRequest = + PermissionRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onPermissionRequest != null) + return (await webviewParams!.onPermissionRequest!( + _controllerFromPlatform, permissionRequest)) + ?.toMap(); + else { + return (await webviewParams!.androidOnPermissionRequest!( + _controllerFromPlatform, origin, resources)) + ?.toMap(); + } + } else { + return (await _inAppBrowserEventHandler! + .onPermissionRequest(permissionRequest)) + ?.toMap() ?? + (await _inAppBrowserEventHandler! + .androidOnPermissionRequest(origin, resources)) + ?.toMap(); + } + } + break; + case "onUpdateVisitedHistory": + if ((webviewParams != null && + webviewParams!.onUpdateVisitedHistory != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + bool? isReload = call.arguments["isReload"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && + webviewParams!.onUpdateVisitedHistory != null) + webviewParams!.onUpdateVisitedHistory!( + _controllerFromPlatform, uri, isReload); + else + _inAppBrowserEventHandler!.onUpdateVisitedHistory(uri, isReload); + } + break; + case "onWebContentProcessDidTerminate": + if (webviewParams != null && + (webviewParams!.onWebContentProcessDidTerminate != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosOnWebContentProcessDidTerminate != null)) { + if (webviewParams!.onWebContentProcessDidTerminate != null) + webviewParams! + .onWebContentProcessDidTerminate!(_controllerFromPlatform); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams! + .iosOnWebContentProcessDidTerminate!(_controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onWebContentProcessDidTerminate(); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.iosOnWebContentProcessDidTerminate(); + } + break; + case "onPageCommitVisible": + if ((webviewParams != null && + webviewParams!.onPageCommitVisible != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && + webviewParams!.onPageCommitVisible != null) + webviewParams!.onPageCommitVisible!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onPageCommitVisible(uri); + } + break; + case "onDidReceiveServerRedirectForProvisionalNavigation": + if (webviewParams != null && + (webviewParams! + .onDidReceiveServerRedirectForProvisionalNavigation != + null || + params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .iosOnDidReceiveServerRedirectForProvisionalNavigation != + null)) { + if (webviewParams! + .onDidReceiveServerRedirectForProvisionalNavigation != + null) + webviewParams!.onDidReceiveServerRedirectForProvisionalNavigation!( + _controllerFromPlatform); + else { + params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .iosOnDidReceiveServerRedirectForProvisionalNavigation!( + _controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler! + .onDidReceiveServerRedirectForProvisionalNavigation(); + _inAppBrowserEventHandler! + .iosOnDidReceiveServerRedirectForProvisionalNavigation(); + } + break; + case "onNavigationResponse": + if ((webviewParams != null && + (webviewParams!.onNavigationResponse != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosOnNavigationResponse != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + // ignore: deprecated_member_use_from_same_package + IOSWKNavigationResponse iosOnNavigationResponse = + // ignore: deprecated_member_use_from_same_package + IOSWKNavigationResponse.fromMap(arguments)!; + + NavigationResponse navigationResponse = + NavigationResponse.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onNavigationResponse != null) + return (await webviewParams!.onNavigationResponse!( + _controllerFromPlatform, navigationResponse)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.iosOnNavigationResponse!( + _controllerFromPlatform, iosOnNavigationResponse)) + ?.toNativeValue(); + } + } else { + return (await _inAppBrowserEventHandler! + .onNavigationResponse(navigationResponse)) + ?.toNativeValue() ?? + (await _inAppBrowserEventHandler! + .iosOnNavigationResponse(iosOnNavigationResponse)) + ?.toNativeValue(); + } + } + break; + case "shouldAllowDeprecatedTLS": + if ((webviewParams != null && + (webviewParams!.shouldAllowDeprecatedTLS != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosShouldAllowDeprecatedTLS != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + URLAuthenticationChallenge challenge = + URLAuthenticationChallenge.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.shouldAllowDeprecatedTLS != null) + return (await webviewParams!.shouldAllowDeprecatedTLS!( + _controllerFromPlatform, challenge)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.iosShouldAllowDeprecatedTLS!( + _controllerFromPlatform, challenge)) + ?.toNativeValue(); + } + } else { + return (await _inAppBrowserEventHandler! + .shouldAllowDeprecatedTLS(challenge)) + ?.toNativeValue() ?? + // ignore: deprecated_member_use_from_same_package + (await _inAppBrowserEventHandler! + .iosShouldAllowDeprecatedTLS(challenge)) + ?.toNativeValue(); + } + } + break; + case "onLongPressHitTestResult": + if ((webviewParams != null && + webviewParams!.onLongPressHitTestResult != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + InAppWebViewHitTestResult hitTestResult = + InAppWebViewHitTestResult.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onLongPressHitTestResult != null) + webviewParams!.onLongPressHitTestResult!( + _controllerFromPlatform, hitTestResult); + else + _inAppBrowserEventHandler!.onLongPressHitTestResult(hitTestResult); + } + break; + case "onCreateContextMenu": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null && contextMenu.onCreateContextMenu != null) { + Map arguments = + call.arguments.cast(); + InAppWebViewHitTestResult hitTestResult = + InAppWebViewHitTestResult.fromMap(arguments)!; + + contextMenu.onCreateContextMenu!(hitTestResult); + } + break; + case "onHideContextMenu": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null && contextMenu.onHideContextMenu != null) { + contextMenu.onHideContextMenu!(); + } + break; + case "onContextMenuActionItemClicked": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null) { + int? androidId = call.arguments["androidId"]; + String? iosId = call.arguments["iosId"]; + dynamic id = call.arguments["id"]; + String title = call.arguments["title"]; + + ContextMenuItem menuItemClicked = ContextMenuItem( + id: id, + // ignore: deprecated_member_use_from_same_package + androidId: androidId, + // ignore: deprecated_member_use_from_same_package + iosId: iosId, + title: title, + action: null); + + for (var menuItem in contextMenu.menuItems) { + if (menuItem.id == id) { + menuItemClicked = menuItem; + if (menuItem.action != null) { + menuItem.action!(); + } + break; + } + } + + if (contextMenu.onContextMenuActionItemClicked != null) { + contextMenu.onContextMenuActionItemClicked!(menuItemClicked); + } + } + break; + case "onEnterFullscreen": + if (webviewParams != null && webviewParams!.onEnterFullscreen != null) + webviewParams!.onEnterFullscreen!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onEnterFullscreen(); + break; + case "onExitFullscreen": + if (webviewParams != null && webviewParams!.onExitFullscreen != null) + webviewParams!.onExitFullscreen!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onExitFullscreen(); + break; + case "onOverScrolled": + if ((webviewParams != null && webviewParams!.onOverScrolled != null) || + _inAppBrowserEventHandler != null) { + int x = call.arguments["x"]; + int y = call.arguments["y"]; + bool clampedX = call.arguments["clampedX"]; + bool clampedY = call.arguments["clampedY"]; + + if (webviewParams != null && webviewParams!.onOverScrolled != null) + webviewParams!.onOverScrolled!( + _controllerFromPlatform, x, y, clampedX, clampedY); + else + _inAppBrowserEventHandler!.onOverScrolled(x, y, clampedX, clampedY); + } + break; + case "onWindowFocus": + if (webviewParams != null && webviewParams!.onWindowFocus != null) + webviewParams!.onWindowFocus!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowFocus(); + break; + case "onWindowBlur": + if (webviewParams != null && webviewParams!.onWindowBlur != null) + webviewParams!.onWindowBlur!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowBlur(); + break; + case "onPrintRequest": + if ((webviewParams != null && + (webviewParams!.onPrintRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onPrint != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + String? printJobId = call.arguments["printJobId"]; + WebUri? uri = url != null ? WebUri(url) : null; + IOSPrintJobController? printJob = printJobId != null + ? IOSPrintJobController( + IOSPrintJobControllerCreationParams(id: printJobId)) + : null; + + if (webviewParams != null) { + if (webviewParams!.onPrintRequest != null) + return await webviewParams!.onPrintRequest!( + _controllerFromPlatform, uri, printJob); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onPrint!(_controllerFromPlatform, uri); + return false; + } + } else { + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.onPrint(uri); + return await _inAppBrowserEventHandler! + .onPrintRequest(uri, printJob); + } + } + break; + case "onInjectedScriptLoaded": + String id = call.arguments[0]; + var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onLoadCallback != null) { + onLoadCallback(); + } + break; + case "onInjectedScriptError": + String id = call.arguments[0]; + var onErrorCallback = _injectedScriptsFromURL[id]?.onError; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onErrorCallback != null) { + onErrorCallback(); + } + break; + case "onCameraCaptureStateChanged": + if ((webviewParams != null && + webviewParams!.onCameraCaptureStateChanged != null) || + _inAppBrowserEventHandler != null) { + var oldState = + MediaCaptureState.fromNativeValue(call.arguments["oldState"]); + var newState = + MediaCaptureState.fromNativeValue(call.arguments["newState"]); + + if (webviewParams != null && + webviewParams!.onCameraCaptureStateChanged != null) + webviewParams!.onCameraCaptureStateChanged!( + _controllerFromPlatform, oldState, newState); + else + _inAppBrowserEventHandler! + .onCameraCaptureStateChanged(oldState, newState); + } + break; + case "onMicrophoneCaptureStateChanged": + if ((webviewParams != null && + webviewParams!.onMicrophoneCaptureStateChanged != null) || + _inAppBrowserEventHandler != null) { + var oldState = + MediaCaptureState.fromNativeValue(call.arguments["oldState"]); + var newState = + MediaCaptureState.fromNativeValue(call.arguments["newState"]); + + if (webviewParams != null && + webviewParams!.onMicrophoneCaptureStateChanged != null) + webviewParams!.onMicrophoneCaptureStateChanged!( + _controllerFromPlatform, oldState, newState); + else + _inAppBrowserEventHandler! + .onMicrophoneCaptureStateChanged(oldState, newState); + } + break; + case "onContentSizeChanged": + if ((webviewParams != null && + webviewParams!.onContentSizeChanged != null) || + _inAppBrowserEventHandler != null) { + var oldContentSize = MapSize.fromMap( + call.arguments["oldContentSize"]?.cast())!; + var newContentSize = MapSize.fromMap( + call.arguments["newContentSize"]?.cast())!; + + if (webviewParams != null && + webviewParams!.onContentSizeChanged != null) + webviewParams!.onContentSizeChanged!( + _controllerFromPlatform, oldContentSize, newContentSize); + else + _inAppBrowserEventHandler! + .onContentSizeChanged(oldContentSize, newContentSize); + } + break; + case "onCallJsHandler": + String handlerName = call.arguments["handlerName"]; + // decode args to json + List args = jsonDecode(call.arguments["args"]); + + _debugLog(handlerName, args); + + switch (handlerName) { + case "onLoadResource": + if ((webviewParams != null && + webviewParams!.onLoadResource != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + arguments["startTime"] = arguments["startTime"] is int + ? arguments["startTime"].toDouble() + : arguments["startTime"]; + arguments["duration"] = arguments["duration"] is int + ? arguments["duration"].toDouble() + : arguments["duration"]; + + var response = LoadedResource.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onLoadResource != null) + webviewParams!.onLoadResource!( + _controllerFromPlatform, response); + else + _inAppBrowserEventHandler!.onLoadResource(response); + } + return null; + case "shouldInterceptAjaxRequest": + if ((webviewParams != null && + webviewParams!.shouldInterceptAjaxRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldInterceptAjaxRequest != null) + return jsonEncode( + await params.webviewParams!.shouldInterceptAjaxRequest!( + _controllerFromPlatform, request)); + else + return jsonEncode(await _inAppBrowserEventHandler! + .shouldInterceptAjaxRequest(request)); + } + return null; + case "onAjaxReadyStateChange": + if ((webviewParams != null && + webviewParams!.onAjaxReadyStateChange != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onAjaxReadyStateChange != null) + return (await webviewParams!.onAjaxReadyStateChange!( + _controllerFromPlatform, request)) + ?.toNativeValue(); + else + return (await _inAppBrowserEventHandler! + .onAjaxReadyStateChange(request)) + ?.toNativeValue(); + } + return null; + case "onAjaxProgress": + if ((webviewParams != null && + webviewParams!.onAjaxProgress != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onAjaxProgress != null) + return (await webviewParams!.onAjaxProgress!( + _controllerFromPlatform, request)) + ?.toNativeValue(); + else + return (await _inAppBrowserEventHandler! + .onAjaxProgress(request)) + ?.toNativeValue(); + } + return null; + case "shouldInterceptFetchRequest": + if ((webviewParams != null && + webviewParams!.shouldInterceptFetchRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + FetchRequest request = FetchRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldInterceptFetchRequest != null) + return jsonEncode( + await webviewParams!.shouldInterceptFetchRequest!( + _controllerFromPlatform, request)); + else + return jsonEncode(await _inAppBrowserEventHandler! + .shouldInterceptFetchRequest(request)); + } + return null; + case "onWindowFocus": + if (webviewParams != null && webviewParams!.onWindowFocus != null) + webviewParams!.onWindowFocus!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowFocus(); + return null; + case "onWindowBlur": + if (webviewParams != null && webviewParams!.onWindowBlur != null) + webviewParams!.onWindowBlur!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowBlur(); + return null; + case "onInjectedScriptLoaded": + String id = args[0]; + var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onLoadCallback != null) { + onLoadCallback(); + } + return null; + case "onInjectedScriptError": + String id = args[0]; + var onErrorCallback = _injectedScriptsFromURL[id]?.onError; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onErrorCallback != null) { + onErrorCallback(); + } + return null; + } + + if (_javaScriptHandlersMap.containsKey(handlerName)) { + // convert result to json + try { + return jsonEncode(await _javaScriptHandlersMap[handlerName]!(args)); + } catch (error, stacktrace) { + developer.log(error.toString() + '\n' + stacktrace.toString(), + name: 'JavaScript Handler "$handlerName"'); + throw Exception(error.toString().replaceFirst('Exception: ', '')); + } + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + Future getUrl() async { + Map args = {}; + String? url = await channel?.invokeMethod('getUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + Future getTitle() async { + Map args = {}; + return await channel?.invokeMethod('getTitle', args); + } + + @override + Future getProgress() async { + Map args = {}; + return await channel?.invokeMethod('getProgress', args); + } + + @override + Future getHtml() async { + String? html; + + InAppWebViewSettings? settings = await getSettings(); + if (settings != null && settings.javaScriptEnabled == true) { + html = await evaluateJavascript( + source: "window.document.getElementsByTagName('html')[0].outerHTML;"); + if (html != null && html.isNotEmpty) return html; + } + + var webviewUrl = await getUrl(); + if (webviewUrl == null) { + return html; + } + + if (webviewUrl.isScheme("file")) { + var assetPathSplitted = webviewUrl.toString().split("/flutter_assets/"); + var assetPath = assetPathSplitted[assetPathSplitted.length - 1]; + try { + var bytes = await rootBundle.load(assetPath); + html = utf8.decode(bytes.buffer.asUint8List()); + } catch (e) {} + } else { + try { + HttpClient client = HttpClient(); + var htmlRequest = await client.getUrl(webviewUrl); + html = + await (await htmlRequest.close()).transform(Utf8Decoder()).join(); + } catch (e) { + developer.log(e.toString(), name: this.runtimeType.toString()); + } + } + + return html; + } + + @override + Future> getFavicons() async { + List favicons = []; + + var webviewUrl = await getUrl(); + + if (webviewUrl == null) { + return favicons; + } + + String? manifestUrl; + + var html = await getHtml(); + if (html == null || html.isEmpty) { + return favicons; + } + var assetPathBase; + + if (webviewUrl.isScheme("file")) { + var assetPathSplitted = webviewUrl.toString().split("/flutter_assets/"); + assetPathBase = assetPathSplitted[0] + "/flutter_assets/"; + } + + InAppWebViewSettings? settings = await getSettings(); + if (settings != null && settings.javaScriptEnabled == true) { + List> links = (await evaluateJavascript(source: """ +(function() { + var linkNodes = document.head.getElementsByTagName("link"); + var links = []; + for (var i = 0; i < linkNodes.length; i++) { + var linkNode = linkNodes[i]; + if (linkNode.rel === 'manifest') { + links.push( + { + rel: linkNode.rel, + href: linkNode.href, + sizes: null + } + ); + } else if (linkNode.rel != null && linkNode.rel.indexOf('icon') >= 0) { + links.push( + { + rel: linkNode.rel, + href: linkNode.href, + sizes: linkNode.sizes != null && linkNode.sizes.value != "" ? linkNode.sizes.value : null + } + ); + } + } + return links; +})(); +"""))?.cast>() ?? []; + for (var link in links) { + if (link["rel"] == "manifest") { + manifestUrl = link["href"]; + if (!_isUrlAbsolute(manifestUrl!)) { + if (manifestUrl.startsWith("/")) { + manifestUrl = manifestUrl.substring(1); + } + manifestUrl = ((assetPathBase == null) + ? webviewUrl.scheme + "://" + webviewUrl.host + "/" + : assetPathBase) + + manifestUrl; + } + continue; + } + favicons.addAll(_createFavicons(webviewUrl, assetPathBase, link["href"], + link["rel"], link["sizes"], false)); + } + } + + // try to get /favicon.ico + try { + HttpClient client = HttpClient(); + var faviconUrl = + webviewUrl.scheme + "://" + webviewUrl.host + "/favicon.ico"; + var faviconUri = WebUri(faviconUrl); + var headRequest = await client.headUrl(faviconUri); + var headResponse = await headRequest.close(); + if (headResponse.statusCode == 200) { + favicons.add(Favicon(url: faviconUri, rel: "shortcut icon")); + } + } catch (e) { + developer.log("/favicon.ico file not found: " + e.toString(), + name: this.runtimeType.toString()); + } + + // try to get the manifest file + HttpClientRequest? manifestRequest; + HttpClientResponse? manifestResponse; + bool manifestFound = false; + if (manifestUrl == null) { + manifestUrl = + webviewUrl.scheme + "://" + webviewUrl.host + "/manifest.json"; + } + try { + HttpClient client = HttpClient(); + manifestRequest = await client.getUrl(Uri.parse(manifestUrl)); + manifestResponse = await manifestRequest.close(); + manifestFound = manifestResponse.statusCode == 200 && + manifestResponse.headers.contentType?.mimeType == "application/json"; + } catch (e) { + developer.log("Manifest file not found: " + e.toString(), + name: this.runtimeType.toString()); + } + + if (manifestFound) { + try { + Map manifest = json + .decode(await manifestResponse!.transform(Utf8Decoder()).join()); + if (manifest.containsKey("icons")) { + for (Map icon in manifest["icons"]) { + favicons.addAll(_createFavicons(webviewUrl, assetPathBase, + icon["src"], icon["rel"], icon["sizes"], true)); + } + } + } on FormatException catch (_) { + /// The [manifestResponse] might not has a valid JSON string, catch and + /// ignore the error + } + } + + return favicons; + } + + bool _isUrlAbsolute(String url) { + return url.startsWith("http://") || url.startsWith("https://"); + } + + List _createFavicons(WebUri url, String? assetPathBase, + String urlIcon, String? rel, String? sizes, bool isManifest) { + List favicons = []; + + List urlSplitted = urlIcon.split("/"); + if (!_isUrlAbsolute(urlIcon)) { + if (urlIcon.startsWith("/")) { + urlIcon = urlIcon.substring(1); + } + urlIcon = ((assetPathBase == null) + ? url.scheme + "://" + url.host + "/" + : assetPathBase) + + urlIcon; + } + if (isManifest) { + rel = (sizes != null) + ? urlSplitted[urlSplitted.length - 1] + .replaceFirst("-" + sizes, "") + .split(" ")[0] + .split(".")[0] + : null; + } + if (sizes != null && sizes.isNotEmpty && sizes != "any") { + List sizesSplitted = sizes.split(" "); + for (String size in sizesSplitted) { + int width = int.parse(size.split("x")[0]); + int height = int.parse(size.split("x")[1]); + favicons.add(Favicon( + url: WebUri(urlIcon), rel: rel, width: width, height: height)); + } + } else { + favicons.add( + Favicon(url: WebUri(urlIcon), rel: rel, width: null, height: null)); + } + + return favicons; + } + + @override + Future loadUrl( + {required URLRequest urlRequest, + @Deprecated('Use allowingReadAccessTo instead') + Uri? iosAllowingReadAccessTo, + WebUri? allowingReadAccessTo}) async { + assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); + assert( + allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); + assert(iosAllowingReadAccessTo == null || + iosAllowingReadAccessTo.isScheme("file")); + + Map args = {}; + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + args.putIfAbsent( + 'allowingReadAccessTo', + () => + allowingReadAccessTo?.toString() ?? + iosAllowingReadAccessTo?.toString()); + await channel?.invokeMethod('loadUrl', args); + } + + @override + Future postUrl( + {required WebUri url, required Uint8List postData}) async { + assert(url.toString().isNotEmpty); + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('postData', () => postData); + await channel?.invokeMethod('postUrl', args); + } + + @override + Future loadData( + {required String data, + String mimeType = "text/html", + String encoding = "utf8", + WebUri? baseUrl, + @Deprecated('Use historyUrl instead') Uri? androidHistoryUrl, + WebUri? historyUrl, + @Deprecated('Use allowingReadAccessTo instead') + Uri? iosAllowingReadAccessTo, + WebUri? allowingReadAccessTo}) async { + assert( + allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); + assert(iosAllowingReadAccessTo == null || + iosAllowingReadAccessTo.isScheme("file")); + + Map args = {}; + args.putIfAbsent('data', () => data); + args.putIfAbsent('mimeType', () => mimeType); + args.putIfAbsent('encoding', () => encoding); + args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); + args.putIfAbsent( + 'historyUrl', + () => + historyUrl?.toString() ?? + androidHistoryUrl?.toString() ?? + "about:blank"); + args.putIfAbsent( + 'allowingReadAccessTo', + () => + allowingReadAccessTo?.toString() ?? + iosAllowingReadAccessTo?.toString()); + await channel?.invokeMethod('loadData', args); + } + + @override + Future loadFile({required String assetFilePath}) async { + assert(assetFilePath.isNotEmpty); + Map args = {}; + args.putIfAbsent('assetFilePath', () => assetFilePath); + await channel?.invokeMethod('loadFile', args); + } + + @override + Future reload() async { + Map args = {}; + await channel?.invokeMethod('reload', args); + } + + @override + Future goBack() async { + Map args = {}; + await channel?.invokeMethod('goBack', args); + } + + @override + Future canGoBack() async { + Map args = {}; + return await channel?.invokeMethod('canGoBack', args) ?? false; + } + + @override + Future goForward() async { + Map args = {}; + await channel?.invokeMethod('goForward', args); + } + + @override + Future canGoForward() async { + Map args = {}; + return await channel?.invokeMethod('canGoForward', args) ?? false; + } + + @override + Future goBackOrForward({required int steps}) async { + Map args = {}; + args.putIfAbsent('steps', () => steps); + await channel?.invokeMethod('goBackOrForward', args); + } + + @override + Future canGoBackOrForward({required int steps}) async { + Map args = {}; + args.putIfAbsent('steps', () => steps); + return await channel?.invokeMethod('canGoBackOrForward', args) ?? + false; + } + + @override + Future goTo({required WebHistoryItem historyItem}) async { + var steps = historyItem.offset; + if (steps != null) { + await goBackOrForward(steps: steps); + } + } + + @override + Future isLoading() async { + Map args = {}; + return await channel?.invokeMethod('isLoading', args) ?? false; + } + + @override + Future stopLoading() async { + Map args = {}; + await channel?.invokeMethod('stopLoading', args); + } + + @override + Future evaluateJavascript( + {required String source, ContentWorld? contentWorld}) async { + Map args = {}; + args.putIfAbsent('source', () => source); + args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); + var data = await channel?.invokeMethod('evaluateJavascript', args); + return data; + } + + @override + Future injectJavascriptFileFromUrl( + {required WebUri urlFile, + ScriptHtmlTagAttributes? scriptHtmlTagAttributes}) async { + assert(urlFile.toString().isNotEmpty); + var id = scriptHtmlTagAttributes?.id; + if (scriptHtmlTagAttributes != null && id != null) { + _injectedScriptsFromURL[id] = scriptHtmlTagAttributes; + } + Map args = {}; + args.putIfAbsent('urlFile', () => urlFile.toString()); + args.putIfAbsent( + 'scriptHtmlTagAttributes', () => scriptHtmlTagAttributes?.toMap()); + await channel?.invokeMethod('injectJavascriptFileFromUrl', args); + } + + @override + Future injectJavascriptFileFromAsset( + {required String assetFilePath}) async { + String source = await rootBundle.loadString(assetFilePath); + return await evaluateJavascript(source: source); + } + + @override + Future injectCSSCode({required String source}) async { + Map args = {}; + args.putIfAbsent('source', () => source); + await channel?.invokeMethod('injectCSSCode', args); + } + + @override + Future injectCSSFileFromUrl( + {required WebUri urlFile, + CSSLinkHtmlTagAttributes? cssLinkHtmlTagAttributes}) async { + assert(urlFile.toString().isNotEmpty); + Map args = {}; + args.putIfAbsent('urlFile', () => urlFile.toString()); + args.putIfAbsent( + 'cssLinkHtmlTagAttributes', () => cssLinkHtmlTagAttributes?.toMap()); + await channel?.invokeMethod('injectCSSFileFromUrl', args); + } + + @override + Future injectCSSFileFromAsset({required String assetFilePath}) async { + String source = await rootBundle.loadString(assetFilePath); + await injectCSSCode(source: source); + } + + @override + void addJavaScriptHandler( + {required String handlerName, + required JavaScriptHandlerCallback callback}) { + assert(!_JAVASCRIPT_HANDLER_FORBIDDEN_NAMES.contains(handlerName), + '"$handlerName" is a forbidden name!'); + this._javaScriptHandlersMap[handlerName] = (callback); + } + + @override + JavaScriptHandlerCallback? removeJavaScriptHandler( + {required String handlerName}) { + return this._javaScriptHandlersMap.remove(handlerName); + } + + @override + bool hasJavaScriptHandler({required String handlerName}) { + return this._javaScriptHandlersMap.containsKey(handlerName); + } + + @override + Future takeScreenshot( + {ScreenshotConfiguration? screenshotConfiguration}) async { + Map args = {}; + args.putIfAbsent( + 'screenshotConfiguration', () => screenshotConfiguration?.toMap()); + return await channel?.invokeMethod('takeScreenshot', args); + } + + @override + @Deprecated('Use setSettings instead') + Future setOptions({required InAppWebViewGroupOptions options}) async { + InAppWebViewSettings settings = + InAppWebViewSettings.fromMap(options.toMap()) ?? InAppWebViewSettings(); + await setSettings(settings: settings); + } + + @override + @Deprecated('Use getSettings instead') + Future getOptions() async { + InAppWebViewSettings? settings = await getSettings(); + + Map? options = settings?.toMap(); + if (options != null) { + options = options.cast(); + return InAppWebViewGroupOptions.fromMap(options as Map); + } + + return null; + } + + @override + Future setSettings({required InAppWebViewSettings settings}) async { + Map args = {}; + + args.putIfAbsent('settings', () => settings.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + Future getSettings() async { + Map args = {}; + + Map? settings = + await channel?.invokeMethod('getSettings', args); + if (settings != null) { + settings = settings.cast(); + return InAppWebViewSettings.fromMap(settings as Map); + } + + return null; + } + + @override + Future getCopyBackForwardList() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('getCopyBackForwardList', args)) + ?.cast(); + return WebHistory.fromMap(result); + } + + @override + Future clearCache() async { + Map args = {}; + await channel?.invokeMethod('clearCache', args); + } + + @override + @Deprecated("Use FindInteractionController.findAll instead") + Future findAllAsync({required String find}) async { + Map args = {}; + args.putIfAbsent('find', () => find); + await channel?.invokeMethod('findAll', args); + } + + @override + @Deprecated("Use FindInteractionController.findNext instead") + Future findNext({required bool forward}) async { + Map args = {}; + args.putIfAbsent('forward', () => forward); + await channel?.invokeMethod('findNext', args); + } + + @override + @Deprecated("Use FindInteractionController.clearMatches instead") + Future clearMatches() async { + Map args = {}; + await channel?.invokeMethod('clearMatches', args); + } + + @override + @Deprecated("Use tRexRunnerHtml instead") + Future getTRexRunnerHtml() async { + return await tRexRunnerHtml; + } + + @override + @Deprecated("Use tRexRunnerCss instead") + Future getTRexRunnerCss() async { + return await tRexRunnerCss; + } + + @override + Future scrollTo( + {required int x, required int y, bool animated = false}) async { + Map args = {}; + args.putIfAbsent('x', () => x); + args.putIfAbsent('y', () => y); + args.putIfAbsent('animated', () => animated); + await channel?.invokeMethod('scrollTo', args); + } + + @override + Future scrollBy( + {required int x, required int y, bool animated = false}) async { + Map args = {}; + args.putIfAbsent('x', () => x); + args.putIfAbsent('y', () => y); + args.putIfAbsent('animated', () => animated); + await channel?.invokeMethod('scrollBy', args); + } + + @override + Future pauseTimers() async { + Map args = {}; + await channel?.invokeMethod('pauseTimers', args); + } + + @override + Future resumeTimers() async { + Map args = {}; + await channel?.invokeMethod('resumeTimers', args); + } + + @override + Future printCurrentPage( + {PrintJobSettings? settings}) async { + Map args = {}; + args.putIfAbsent("settings", () => settings?.toMap()); + String? jobId = + await channel?.invokeMethod('printCurrentPage', args); + if (jobId != null) { + return IOSPrintJobController( + PlatformPrintJobControllerCreationParams(id: jobId)); + } + return null; + } + + @override + Future getContentHeight() async { + Map args = {}; + var height = await channel?.invokeMethod('getContentHeight', args); + if (height == null || height == 0) { + // try to use javascript + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollHeight;"); + if (scrollHeight != null && scrollHeight is num) { + height = scrollHeight.toInt(); + } + } + return height; + } + + @override + Future getContentWidth() async { + Map args = {}; + var height = await channel?.invokeMethod('getContentWidth', args); + if (height == null || height == 0) { + // try to use javascript + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollWidth;"); + if (scrollHeight != null && scrollHeight is num) { + height = scrollHeight.toInt(); + } + } + return height; + } + + @override + Future zoomBy( + {required double zoomFactor, + @Deprecated('Use animated instead') bool? iosAnimated, + bool animated = false}) async { + Map args = {}; + args.putIfAbsent('zoomFactor', () => zoomFactor); + args.putIfAbsent('animated', () => iosAnimated ?? animated); + return await channel?.invokeMethod('zoomBy', args); + } + + @override + Future getOriginalUrl() async { + Map args = {}; + String? url = await channel?.invokeMethod('getOriginalUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + Future getZoomScale() async { + Map args = {}; + return await channel?.invokeMethod('getZoomScale', args); + } + + @override + @Deprecated('Use getZoomScale instead') + Future getScale() async { + return await getZoomScale(); + } + + @override + Future getSelectedText() async { + Map args = {}; + return await channel?.invokeMethod('getSelectedText', args); + } + + @override + Future getHitTestResult() async { + Map args = {}; + Map? hitTestResultMap = + await channel?.invokeMethod('getHitTestResult', args); + + if (hitTestResultMap == null) { + return null; + } + + hitTestResultMap = hitTestResultMap.cast(); + + InAppWebViewHitTestResultType? type = + InAppWebViewHitTestResultType.fromNativeValue( + hitTestResultMap["type"]?.toInt()); + String? extra = hitTestResultMap["extra"]; + return InAppWebViewHitTestResult(type: type, extra: extra); + } + + @override + Future clearFocus() async { + Map args = {}; + return await channel?.invokeMethod('clearFocus', args); + } + + @override + Future setContextMenu(ContextMenu? contextMenu) async { + Map args = {}; + args.putIfAbsent("contextMenu", () => contextMenu?.toMap()); + await channel?.invokeMethod('setContextMenu', args); + _inAppBrowser?.setContextMenu(contextMenu); + } + + @override + Future requestFocusNodeHref() async { + Map args = {}; + Map? result = + await channel?.invokeMethod('requestFocusNodeHref', args); + return result != null + ? RequestFocusNodeHrefResult( + url: result['url'] != null ? WebUri(result['url']) : null, + title: result['title'], + src: result['src'], + ) + : null; + } + + @override + Future requestImageRef() async { + Map args = {}; + Map? result = + await channel?.invokeMethod('requestImageRef', args); + return result != null + ? RequestImageRefResult( + url: result['url'] != null ? WebUri(result['url']) : null, + ) + : null; + } + + @override + Future> getMetaTags() async { + List metaTags = []; + + List>? metaTagList = + (await evaluateJavascript(source: """ +(function() { + var metaTags = []; + var metaTagNodes = document.head.getElementsByTagName('meta'); + for (var i = 0; i < metaTagNodes.length; i++) { + var metaTagNode = metaTagNodes[i]; + + var otherAttributes = metaTagNode.getAttributeNames(); + var nameIndex = otherAttributes.indexOf("name"); + if (nameIndex !== -1) otherAttributes.splice(nameIndex, 1); + var contentIndex = otherAttributes.indexOf("content"); + if (contentIndex !== -1) otherAttributes.splice(contentIndex, 1); + + var attrs = []; + for (var j = 0; j < otherAttributes.length; j++) { + var otherAttribute = otherAttributes[j]; + attrs.push( + { + name: otherAttribute, + value: metaTagNode.getAttribute(otherAttribute) + } + ); + } + + metaTags.push( + { + name: metaTagNode.name, + content: metaTagNode.content, + attrs: attrs + } + ); + } + return metaTags; +})(); + """))?.cast>(); + + if (metaTagList == null) { + return metaTags; + } + + for (var metaTag in metaTagList) { + var attrs = []; + + for (var metaTagAttr in metaTag["attrs"]) { + attrs.add(MetaTagAttribute( + name: metaTagAttr["name"], value: metaTagAttr["value"])); + } + + metaTags.add(MetaTag( + name: metaTag["name"], content: metaTag["content"], attrs: attrs)); + } + + return metaTags; + } + + @override + Future getMetaThemeColor() async { + Color? themeColor; + + try { + Map args = {}; + themeColor = UtilColor.fromStringRepresentation( + await channel?.invokeMethod('getMetaThemeColor', args)); + return themeColor; + } catch (e) { + // not implemented + } + + // try using javascript + var metaTags = await getMetaTags(); + MetaTag? metaTagThemeColor; + + for (var metaTag in metaTags) { + if (metaTag.name == "theme-color") { + metaTagThemeColor = metaTag; + break; + } + } + + if (metaTagThemeColor == null) { + return null; + } + + var colorValue = metaTagThemeColor.content; + + themeColor = colorValue != null + ? UtilColor.fromStringRepresentation(colorValue) + : null; + + return themeColor; + } + + @override + Future getScrollX() async { + Map args = {}; + return await channel?.invokeMethod('getScrollX', args); + } + + @override + Future getScrollY() async { + Map args = {}; + return await channel?.invokeMethod('getScrollY', args); + } + + @override + Future getCertificate() async { + Map args = {}; + Map? sslCertificateMap = + (await channel?.invokeMethod('getCertificate', args)) + ?.cast(); + return SslCertificate.fromMap(sslCertificateMap); + } + + @override + Future addUserScript({required UserScript userScript}) async { + assert(webviewParams?.windowId == null); + + Map args = {}; + args.putIfAbsent('userScript', () => userScript.toMap()); + if (!(_userScripts[userScript.injectionTime]?.contains(userScript) ?? + false)) { + _userScripts[userScript.injectionTime]?.add(userScript); + await channel?.invokeMethod('addUserScript', args); + } + } + + @override + Future addUserScripts({required List userScripts}) async { + assert(webviewParams?.windowId == null); + + for (var i = 0; i < userScripts.length; i++) { + await addUserScript(userScript: userScripts[i]); + } + } + + @override + Future removeUserScript({required UserScript userScript}) async { + assert(webviewParams?.windowId == null); + + var index = _userScripts[userScript.injectionTime]?.indexOf(userScript); + if (index == null || index == -1) { + return false; + } + + _userScripts[userScript.injectionTime]?.remove(userScript); + Map args = {}; + args.putIfAbsent('userScript', () => userScript.toMap()); + args.putIfAbsent('index', () => index); + await channel?.invokeMethod('removeUserScript', args); + + return true; + } + + @override + Future removeUserScriptsByGroupName({required String groupName}) async { + assert(webviewParams?.windowId == null); + + final List userScriptsAtDocumentStart = List.from( + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] ?? []); + for (final userScript in userScriptsAtDocumentStart) { + if (userScript.groupName == groupName) { + _userScripts[userScript.injectionTime]?.remove(userScript); + } + } + + final List userScriptsAtDocumentEnd = + List.from(_userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] ?? []); + for (final userScript in userScriptsAtDocumentEnd) { + if (userScript.groupName == groupName) { + _userScripts[userScript.injectionTime]?.remove(userScript); + } + } + + Map args = {}; + args.putIfAbsent('groupName', () => groupName); + await channel?.invokeMethod('removeUserScriptsByGroupName', args); + } + + @override + Future removeUserScripts( + {required List userScripts}) async { + assert(webviewParams?.windowId == null); + + for (final userScript in userScripts) { + await removeUserScript(userScript: userScript); + } + } + + @override + Future removeAllUserScripts() async { + assert(webviewParams?.windowId == null); + + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_START]?.clear(); + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_END]?.clear(); + + Map args = {}; + await channel?.invokeMethod('removeAllUserScripts', args); + } + + @override + bool hasUserScript({required UserScript userScript}) { + return _userScripts[userScript.injectionTime]?.contains(userScript) ?? + false; + } + + @override + Future callAsyncJavaScript( + {required String functionBody, + Map arguments = const {}, + ContentWorld? contentWorld}) async { + Map args = {}; + args.putIfAbsent('functionBody', () => functionBody); + args.putIfAbsent('arguments', () => arguments); + args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); + var data = await channel?.invokeMethod('callAsyncJavaScript', args); + if (data == null) { + return null; + } + return CallAsyncJavaScriptResult( + value: data["value"], error: data["error"]); + } + + @override + Future saveWebArchive( + {required String filePath, bool autoname = false}) async { + if (!autoname) { + assert(filePath + .endsWith("." + WebArchiveFormat.WEBARCHIVE.toNativeValue())); + } + + Map args = {}; + args.putIfAbsent("filePath", () => filePath); + args.putIfAbsent("autoname", () => autoname); + return await channel?.invokeMethod('saveWebArchive', args); + } + + @override + Future isSecureContext() async { + Map args = {}; + return await channel?.invokeMethod('isSecureContext', args) ?? false; + } + + @override + Future createWebMessageChannel() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('createWebMessageChannel', args)) + ?.cast(); + final webMessageChannel = IOSWebMessageChannel.static().fromMap(result); + if (webMessageChannel != null) { + _webMessageChannels.add(webMessageChannel); + } + return webMessageChannel; + } + + @override + Future postWebMessage( + {required WebMessage message, WebUri? targetOrigin}) async { + if (targetOrigin == null) { + targetOrigin = WebUri(''); + } + Map args = {}; + args.putIfAbsent('message', () => message.toMap()); + args.putIfAbsent('targetOrigin', () => targetOrigin.toString()); + await channel?.invokeMethod('postWebMessage', args); + } + + @override + Future addWebMessageListener( + PlatformWebMessageListener webMessageListener) async { + assert(!_webMessageListeners.contains(webMessageListener), + "${webMessageListener} was already added."); + assert( + !_webMessageListenerObjNames + .contains(webMessageListener.params.jsObjectName), + "jsObjectName ${webMessageListener.params.jsObjectName} was already added."); + _webMessageListeners.add(webMessageListener as IOSWebMessageListener); + _webMessageListenerObjNames.add(webMessageListener.params.jsObjectName); + + Map args = {}; + args.putIfAbsent('webMessageListener', () => webMessageListener.toMap()); + await channel?.invokeMethod('addWebMessageListener', args); + } + + @override + bool hasWebMessageListener(PlatformWebMessageListener webMessageListener) { + return _webMessageListeners.contains(webMessageListener) || + _webMessageListenerObjNames + .contains(webMessageListener.params.jsObjectName); + } + + @override + Future canScrollVertically() async { + Map args = {}; + return await channel?.invokeMethod('canScrollVertically', args) ?? + false; + } + + @override + Future canScrollHorizontally() async { + Map args = {}; + return await channel?.invokeMethod('canScrollHorizontally', args) ?? + false; + } + + @override + Future reloadFromOrigin() async { + Map args = {}; + await channel?.invokeMethod('reloadFromOrigin', args); + } + + @override + Future createPdf( + {@Deprecated("Use pdfConfiguration instead") + // ignore: deprecated_member_use_from_same_package + IOSWKPDFConfiguration? iosWKPdfConfiguration, + PDFConfiguration? pdfConfiguration}) async { + Map args = {}; + args.putIfAbsent('pdfConfiguration', + () => pdfConfiguration?.toMap() ?? iosWKPdfConfiguration?.toMap()); + return await channel?.invokeMethod('createPdf', args); + } + + @override + Future createWebArchiveData() async { + Map args = {}; + return await channel?.invokeMethod('createWebArchiveData', args); + } + + @override + Future hasOnlySecureContent() async { + Map args = {}; + return await channel?.invokeMethod('hasOnlySecureContent', args) ?? + false; + } + + @override + Future pauseAllMediaPlayback() async { + Map args = {}; + return await channel?.invokeMethod('pauseAllMediaPlayback', args); + } + + @override + Future setAllMediaPlaybackSuspended({required bool suspended}) async { + Map args = {}; + args.putIfAbsent("suspended", () => suspended); + return await channel?.invokeMethod('setAllMediaPlaybackSuspended', args); + } + + @override + Future closeAllMediaPresentations() async { + Map args = {}; + return await channel?.invokeMethod('closeAllMediaPresentations', args); + } + + @override + Future requestMediaPlaybackState() async { + Map args = {}; + return MediaPlaybackState.fromNativeValue( + await channel?.invokeMethod('requestMediaPlaybackState', args)); + } + + @override + Future isInFullscreen() async { + Map args = {}; + return await channel?.invokeMethod('isInFullscreen', args) ?? false; + } + + @override + Future getCameraCaptureState() async { + Map args = {}; + return MediaCaptureState.fromNativeValue( + await channel?.invokeMethod('getCameraCaptureState', args)); + } + + @override + Future setCameraCaptureState({required MediaCaptureState state}) async { + Map args = {}; + args.putIfAbsent('state', () => state.toNativeValue()); + await channel?.invokeMethod('setCameraCaptureState', args); + } + + @override + Future getMicrophoneCaptureState() async { + Map args = {}; + return MediaCaptureState.fromNativeValue( + await channel?.invokeMethod('getMicrophoneCaptureState', args)); + } + + @override + Future setMicrophoneCaptureState( + {required MediaCaptureState state}) async { + Map args = {}; + args.putIfAbsent('state', () => state.toNativeValue()); + await channel?.invokeMethod('setMicrophoneCaptureState', args); + } + + @override + Future loadSimulatedRequest( + {required URLRequest urlRequest, + required Uint8List data, + URLResponse? urlResponse}) async { + Map args = {}; + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + args.putIfAbsent('data', () => data); + args.putIfAbsent('urlResponse', () => urlResponse?.toMap()); + await channel?.invokeMethod('loadSimulatedRequest', args); + } + + @override + Future getDefaultUserAgent() async { + Map args = {}; + return await _staticChannel.invokeMethod( + 'getDefaultUserAgent', args) ?? + ''; + } + + @override + Future handlesURLScheme(String urlScheme) async { + Map args = {}; + args.putIfAbsent('urlScheme', () => urlScheme); + return await _staticChannel.invokeMethod('handlesURLScheme', args); + } + + @override + Future disposeKeepAlive(InAppWebViewKeepAlive keepAlive) async { + Map args = {}; + args.putIfAbsent('keepAliveId', () => keepAlive.id); + await _staticChannel.invokeMethod('disposeKeepAlive', args); + _keepAliveMap[keepAlive] = null; + } + + @override + Future get tRexRunnerHtml async => await rootBundle.loadString( + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html'); + + @override + Future get tRexRunnerCss async => await rootBundle.loadString( + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css'); + + @override + dynamic getViewId() { + return id; + } + + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: !isKeepAlive); + _inAppBrowser = null; + webStorage.dispose(); + if (!isKeepAlive) { + _controllerFromPlatform = null; + _javaScriptHandlersMap.clear(); + _userScripts.clear(); + _webMessageListenerObjNames.clear(); + _injectedScriptsFromURL.clear(); + for (final webMessageChannel in _webMessageChannels) { + webMessageChannel.dispose(); + } + _webMessageChannels.clear(); + for (final webMessageListener in _webMessageListeners) { + webMessageListener.dispose(); + } + _webMessageListeners.clear(); + } + } +} + +extension InternalInAppWebViewController on IOSInAppWebViewController { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_ios/lib/src/in_app_webview/main.dart b/flutter_inappwebview_ios/lib/src/in_app_webview/main.dart new file mode 100644 index 00000000..b83b0611 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/in_app_webview/main.dart @@ -0,0 +1,3 @@ +export 'in_app_webview_controller.dart' hide InternalInAppWebViewController; +export 'in_app_webview.dart'; +export 'headless_in_app_webview.dart' hide InternalHeadlessInAppWebView; diff --git a/flutter_inappwebview_ios/lib/src/inappwebview_platform.dart b/flutter_inappwebview_ios/lib/src/inappwebview_platform.dart new file mode 100644 index 00000000..504f1f1a --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/inappwebview_platform.dart @@ -0,0 +1,273 @@ +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'chrome_safari_browser/chrome_safari_browser.dart'; +import 'cookie_manager.dart'; +import 'http_auth_credentials_database.dart'; +import 'find_interaction/main.dart'; +import 'in_app_browser/in_app_browser.dart'; +import 'in_app_webview/main.dart'; +import 'print_job/main.dart'; +import 'pull_to_refresh/main.dart'; +import 'web_message/main.dart'; +import 'web_storage/main.dart'; +import 'web_authentication_session/main.dart'; + +/// Implementation of [InAppWebViewPlatform] using the WebKit API. +class IOSInAppWebViewPlatform extends InAppWebViewPlatform { + /// Registers this class as the default instance of [InAppWebViewPlatform]. + static void registerWith() { + InAppWebViewPlatform.instance = IOSInAppWebViewPlatform(); + } + + /// Creates a new [IOSCookieManager]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [CookieManager] in `flutter_inappwebview` instead. + @override + IOSCookieManager createPlatformCookieManager( + PlatformCookieManagerCreationParams params, + ) { + return IOSCookieManager(params); + } + + /// Creates a new [IOSInAppWebViewController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebViewController] in `flutter_inappwebview` instead. + @override + IOSInAppWebViewController createPlatformInAppWebViewController( + PlatformInAppWebViewControllerCreationParams params, + ) { + return IOSInAppWebViewController(params); + } + + /// Creates a new empty [IOSInAppWebViewController] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebViewController] in `flutter_inappwebview` instead. + @override + IOSInAppWebViewController createPlatformInAppWebViewControllerStatic() { + return IOSInAppWebViewController.static(); + } + + /// Creates a new [IOSInAppWebViewWidget]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebView] in `flutter_inappwebview` instead. + @override + IOSInAppWebViewWidget createPlatformInAppWebViewWidget( + PlatformInAppWebViewWidgetCreationParams params, + ) { + return IOSInAppWebViewWidget(params); + } + + /// Creates a new [IOSFindInteractionController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [FindInteractionController] in `flutter_inappwebview` instead. + @override + IOSFindInteractionController createPlatformFindInteractionController( + PlatformFindInteractionControllerCreationParams params, + ) { + return IOSFindInteractionController(params); + } + + /// Creates a new [IOSPrintJobController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PrintJobController] in `flutter_inappwebview` instead. + @override + IOSPrintJobController createPlatformPrintJobController( + PlatformPrintJobControllerCreationParams params, + ) { + return IOSPrintJobController(params); + } + + /// Creates a new [IOSPullToRefreshController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PullToRefreshController] in `flutter_inappwebview` instead. + @override + IOSPullToRefreshController createPlatformPullToRefreshController( + PlatformPullToRefreshControllerCreationParams params, + ) { + return IOSPullToRefreshController(params); + } + + /// Creates a new [IOSWebMessageChannel]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessageChannel] in `flutter_inappwebview` instead. + @override + IOSWebMessageChannel createPlatformWebMessageChannel( + PlatformWebMessageChannelCreationParams params, + ) { + return IOSWebMessageChannel(params); + } + + /// Creates a new empty [IOSWebMessageChannel] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessageChannel] in `flutter_inappwebview` instead. + @override + IOSWebMessageChannel createPlatformWebMessageChannelStatic() { + return IOSWebMessageChannel.static(); + } + + /// Creates a new [IOSWebMessageListener]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessageListener] in `flutter_inappwebview` instead. + @override + IOSWebMessageListener createPlatformWebMessageListener( + PlatformWebMessageListenerCreationParams params, + ) { + return IOSWebMessageListener(params); + } + + /// Creates a new [IOSJavaScriptReplyProxy]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [JavaScriptReplyProxy] in `flutter_inappwebview` instead. + @override + IOSJavaScriptReplyProxy createPlatformJavaScriptReplyProxy( + PlatformJavaScriptReplyProxyCreationParams params, + ) { + return IOSJavaScriptReplyProxy(params); + } + + /// Creates a new [IOSWebMessagePort]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessagePort] in `flutter_inappwebview` instead. + @override + IOSWebMessagePort createPlatformWebMessagePort( + PlatformWebMessagePortCreationParams params, + ) { + return IOSWebMessagePort(params); + } + + /// Creates a new [IOSWebStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [IOSWebStorage] in `flutter_inappwebview` instead. + @override + IOSWebStorage createPlatformWebStorage( + PlatformWebStorageCreationParams params, + ) { + return IOSWebStorage(params); + } + + /// Creates a new [IOSLocalStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [IOSLocalStorage] in `flutter_inappwebview` instead. + @override + IOSLocalStorage createPlatformLocalStorage( + PlatformLocalStorageCreationParams params, + ) { + return IOSLocalStorage(params); + } + + /// Creates a new [IOSSessionStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PlatformSessionStorage] in `flutter_inappwebview` instead. + @override + IOSSessionStorage createPlatformSessionStorage( + PlatformSessionStorageCreationParams params, + ) { + return IOSSessionStorage(params); + } + + /// Creates a new [IOSHeadlessInAppWebView]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [HeadlessInAppWebView] in `flutter_inappwebview` instead. + @override + IOSHeadlessInAppWebView createPlatformHeadlessInAppWebView( + PlatformHeadlessInAppWebViewCreationParams params, + ) { + return IOSHeadlessInAppWebView(params); + } + + /// Creates a new [IOSHttpAuthCredentialDatabase]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [HttpAuthCredentialDatabase] in `flutter_inappwebview` instead. + @override + IOSHttpAuthCredentialDatabase createPlatformHttpAuthCredentialDatabase( + PlatformHttpAuthCredentialDatabaseCreationParams params, + ) { + return IOSHttpAuthCredentialDatabase(params); + } + + /// Creates a new [IOSInAppBrowser]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppBrowser] in `flutter_inappwebview` instead. + @override + IOSInAppBrowser createPlatformInAppBrowser( + PlatformInAppBrowserCreationParams params, + ) { + return IOSInAppBrowser(params); + } + + /// Creates a new empty [IOSInAppBrowser] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppBrowser] in `flutter_inappwebview` instead. + @override + IOSInAppBrowser createPlatformInAppBrowserStatic() { + return IOSInAppBrowser.static(); + } + + /// Creates a new [IOSChromeSafariBrowser]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [ChromeSafariBrowser] in `flutter_inappwebview` instead. + @override + IOSChromeSafariBrowser createPlatformChromeSafariBrowser( + PlatformChromeSafariBrowserCreationParams params, + ) { + return IOSChromeSafariBrowser(params); + } + + /// Creates a new empty [IOSChromeSafariBrowser] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [ChromeSafariBrowser] in `flutter_inappwebview` instead. + @override + IOSChromeSafariBrowser createPlatformChromeSafariBrowserStatic() { + return IOSChromeSafariBrowser.static(); + } + + /// Creates a new empty [IOSWebStorageManager] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebStorageManager] in `flutter_inappwebview` instead. + @override + IOSWebStorageManager createPlatformWebStorageManager( + PlatformWebStorageManagerCreationParams params) { + return IOSWebStorageManager(params); + } + + /// Creates a new [IOSWebAuthenticationSession]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebAuthenticationSession] in `flutter_inappwebview` instead. + @override + IOSWebAuthenticationSession createPlatformWebAuthenticationSession( + PlatformWebAuthenticationSessionCreationParams params) { + return IOSWebAuthenticationSession(params); + } + + /// Creates a new empty [IOSWebAuthenticationSession] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebAuthenticationSession] in `flutter_inappwebview` instead. + @override + IOSWebAuthenticationSession createPlatformWebAuthenticationSessionStatic() { + return IOSWebAuthenticationSession.static(); + } +} diff --git a/flutter_inappwebview_ios/lib/src/main.dart b/flutter_inappwebview_ios/lib/src/main.dart new file mode 100644 index 00000000..17f32781 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/main.dart @@ -0,0 +1,13 @@ +export 'inappwebview_platform.dart'; +export 'in_app_webview/main.dart'; +export 'in_app_browser/main.dart'; +export 'chrome_safari_browser/main.dart'; +export 'web_storage/main.dart'; +export 'cookie_manager.dart' hide InternalCookieManager; +export 'http_auth_credentials_database.dart' + hide InternalHttpAuthCredentialDatabase; +export 'pull_to_refresh/main.dart'; +export 'web_message/main.dart'; +export 'print_job/main.dart'; +export 'find_interaction/main.dart'; +export 'web_authentication_session/main.dart'; diff --git a/flutter_inappwebview_ios/lib/src/platform_util.dart b/flutter_inappwebview_ios/lib/src/platform_util.dart new file mode 100644 index 00000000..27e96478 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/platform_util.dart @@ -0,0 +1,64 @@ +import 'package:flutter/services.dart'; + +///Platform native utilities +class PlatformUtil { + static PlatformUtil? _instance; + static const MethodChannel _channel = + MethodChannel('com.pichillilorenzo/flutter_inappwebview_platformutil'); + + PlatformUtil._(); + + ///Get [PlatformUtil] instance. + static PlatformUtil instance() { + return (_instance != null) ? _instance! : _init(); + } + + static PlatformUtil _init() { + _channel.setMethodCallHandler((call) async { + try { + return await _handleMethod(call); + } on Error catch (e) { + print(e); + print(e.stackTrace); + } + }); + _instance = PlatformUtil._(); + return _instance!; + } + + static Future _handleMethod(MethodCall call) async {} + + String? _cachedSystemVersion; + + ///Get current platform system version. + Future getSystemVersion() async { + if (_cachedSystemVersion != null) { + return _cachedSystemVersion!; + } + Map args = {}; + _cachedSystemVersion = + await _channel.invokeMethod('getSystemVersion', args); + return _cachedSystemVersion!; + } + + ///Format date. + Future formatDate( + {required DateTime date, + required String format, + String locale = "en_US", + String timezone = "UTC"}) async { + Map args = {}; + args.putIfAbsent('date', () => date.millisecondsSinceEpoch); + args.putIfAbsent('format', () => format); + args.putIfAbsent('locale', () => locale); + args.putIfAbsent('timezone', () => timezone); + return await _channel.invokeMethod('formatDate', args); + } + + ///Get cookie expiration date used by Web platform. + Future getWebCookieExpirationDate({required DateTime date}) async { + Map args = {}; + args.putIfAbsent('date', () => date.millisecondsSinceEpoch); + return await _channel.invokeMethod('getWebCookieExpirationDate', args); + } +} diff --git a/flutter_inappwebview_ios/lib/src/print_job/main.dart b/flutter_inappwebview_ios/lib/src/print_job/main.dart new file mode 100644 index 00000000..4e70ad94 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/print_job/main.dart @@ -0,0 +1 @@ +export 'print_job_controller.dart'; diff --git a/flutter_inappwebview_ios/lib/src/print_job/print_job_controller.dart b/flutter_inappwebview_ios/lib/src/print_job/print_job_controller.dart new file mode 100644 index 00000000..f56b826d --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/print_job/print_job_controller.dart @@ -0,0 +1,79 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [IOSPrintJobController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformPrintJobControllerCreationParams] for +/// more information. +@immutable +class IOSPrintJobControllerCreationParams + extends PlatformPrintJobControllerCreationParams { + /// Creates a new [IOSPrintJobControllerCreationParams] instance. + const IOSPrintJobControllerCreationParams( + {required super.id, super.onComplete}); + + /// Creates a [IOSPrintJobControllerCreationParams] instance based on [PlatformPrintJobControllerCreationParams]. + factory IOSPrintJobControllerCreationParams.fromPlatformPrintJobControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformPrintJobControllerCreationParams params) { + return IOSPrintJobControllerCreationParams( + id: params.id, onComplete: params.onComplete); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformPrintJobController} +class IOSPrintJobController extends PlatformPrintJobController + with ChannelController { + /// Constructs a [IOSPrintJobController]. + IOSPrintJobController(PlatformPrintJobControllerCreationParams params) + : super.implementation( + params is IOSPrintJobControllerCreationParams + ? params + : IOSPrintJobControllerCreationParams + .fromPlatformPrintJobControllerCreationParams(params), + ) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_printjobcontroller_${params.id}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onComplete": + bool completed = call.arguments["completed"]; + String? error = call.arguments["error"]; + if (params.onComplete != null) { + params.onComplete!(completed, error); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + @override + Future dismiss({bool animated = true}) async { + Map args = {}; + args.putIfAbsent("animated", () => animated); + await channel?.invokeMethod('dismiss', args); + } + + @override + Future getInfo() async { + Map args = {}; + Map? infoMap = + (await channel?.invokeMethod('getInfo', args))?.cast(); + return PrintJobInfo.fromMap(infoMap); + } + + @override + Future dispose() async { + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + } +} diff --git a/flutter_inappwebview_ios/lib/src/pull_to_refresh/main.dart b/flutter_inappwebview_ios/lib/src/pull_to_refresh/main.dart new file mode 100644 index 00000000..586af9d8 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/pull_to_refresh/main.dart @@ -0,0 +1 @@ +export 'pull_to_refresh_controller.dart' hide InternalPullToRefreshController; diff --git a/flutter_inappwebview_ios/lib/src/pull_to_refresh/pull_to_refresh_controller.dart b/flutter_inappwebview_ios/lib/src/pull_to_refresh/pull_to_refresh_controller.dart new file mode 100644 index 00000000..fae128c5 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/pull_to_refresh/pull_to_refresh_controller.dart @@ -0,0 +1,141 @@ +import 'dart:ui'; + +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [IOSPullToRefreshController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformPullToRefreshControllerCreationParams] for +/// more information. +class IOSPullToRefreshControllerCreationParams + extends PlatformPullToRefreshControllerCreationParams { + /// Creates a new [IOSPullToRefreshControllerCreationParams] instance. + IOSPullToRefreshControllerCreationParams( + {super.onRefresh, super.options, super.settings}); + + /// Creates a [IOSPullToRefreshControllerCreationParams] instance based on [PlatformPullToRefreshControllerCreationParams]. + factory IOSPullToRefreshControllerCreationParams.fromPlatformPullToRefreshControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformPullToRefreshControllerCreationParams params) { + return IOSPullToRefreshControllerCreationParams( + onRefresh: params.onRefresh, + options: params.options, + settings: params.settings); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformPullToRefreshController} +class IOSPullToRefreshController extends PlatformPullToRefreshController + with ChannelController { + /// Constructs a [IOSPullToRefreshController]. + IOSPullToRefreshController( + PlatformPullToRefreshControllerCreationParams params) + : super.implementation( + params is IOSPullToRefreshControllerCreationParams + ? params + : IOSPullToRefreshControllerCreationParams + .fromPlatformPullToRefreshControllerCreationParams(params), + ); + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + debugLoggingSettings: + PlatformPullToRefreshController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + _debugLog(call.method, call.arguments); + + switch (call.method) { + case "onRefresh": + if (params.onRefresh != null) params.onRefresh!(); + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + Future setEnabled(bool enabled) async { + Map args = {}; + args.putIfAbsent('enabled', () => enabled); + await channel?.invokeMethod('setEnabled', args); + } + + @override + Future isEnabled() async { + Map args = {}; + return await channel?.invokeMethod('isEnabled', args) ?? false; + } + + Future _setRefreshing(bool refreshing) async { + Map args = {}; + args.putIfAbsent('refreshing', () => refreshing); + await channel?.invokeMethod('setRefreshing', args); + } + + @override + Future beginRefreshing() async { + return await _setRefreshing(true); + } + + @override + Future endRefreshing() async { + await _setRefreshing(false); + } + + @override + Future isRefreshing() async { + Map args = {}; + return await channel?.invokeMethod('isRefreshing', args) ?? false; + } + + @override + Future setColor(Color color) async { + Map args = {}; + args.putIfAbsent('color', () => color.toHex()); + await channel?.invokeMethod('setColor', args); + } + + @override + Future setBackgroundColor(Color color) async { + Map args = {}; + args.putIfAbsent('color', () => color.toHex()); + await channel?.invokeMethod('setBackgroundColor', args); + } + + @Deprecated("Use setStyledTitle instead") + @override + Future setAttributedTitle(IOSNSAttributedString attributedTitle) async { + Map args = {}; + args.putIfAbsent('attributedTitle', () => attributedTitle.toMap()); + await channel?.invokeMethod('setStyledTitle', args); + } + + @override + Future setStyledTitle(AttributedString attributedTitle) async { + Map args = {}; + args.putIfAbsent('attributedTitle', () => attributedTitle.toMap()); + await channel?.invokeMethod('setStyledTitle', args); + } + + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: !isKeepAlive); + } +} + +extension InternalPullToRefreshController on IOSPullToRefreshController { + void init(dynamic id) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } +} diff --git a/flutter_inappwebview_ios/lib/src/web_authentication_session/main.dart b/flutter_inappwebview_ios/lib/src/web_authentication_session/main.dart new file mode 100644 index 00000000..37ca0c24 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/web_authentication_session/main.dart @@ -0,0 +1 @@ +export 'web_authenticate_session.dart'; diff --git a/flutter_inappwebview_ios/lib/src/web_authentication_session/web_authenticate_session.dart b/flutter_inappwebview_ios/lib/src/web_authentication_session/web_authenticate_session.dart new file mode 100755 index 00000000..8d2160b1 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/web_authentication_session/web_authenticate_session.dart @@ -0,0 +1,163 @@ +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [IOSWebAuthenticationSession]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebAuthenticationSessionCreationParams] for +/// more information. +class IOSWebAuthenticationSessionCreationParams + extends PlatformWebAuthenticationSessionCreationParams { + /// Creates a new [IOSWebAuthenticationSessionCreationParams] instance. + const IOSWebAuthenticationSessionCreationParams(); + + /// Creates a [IOSWebAuthenticationSessionCreationParams] instance based on [PlatformWebAuthenticationSessionCreationParams]. + factory IOSWebAuthenticationSessionCreationParams.fromPlatformWebAuthenticationSessionCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebAuthenticationSessionCreationParams params) { + return IOSWebAuthenticationSessionCreationParams(); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebAuthenticationSession} +class IOSWebAuthenticationSession extends PlatformWebAuthenticationSession + with ChannelController { + /// Constructs a [IOSWebAuthenticationSession]. + IOSWebAuthenticationSession( + PlatformWebAuthenticationSessionCreationParams params) + : super.implementation( + params is IOSWebAuthenticationSessionCreationParams + ? params + : IOSWebAuthenticationSessionCreationParams + .fromPlatformWebAuthenticationSessionCreationParams(params), + ); + + static final IOSWebAuthenticationSession _staticValue = + IOSWebAuthenticationSession(IOSWebAuthenticationSessionCreationParams()); + + /// Provide static access. + factory IOSWebAuthenticationSession.static() { + return _staticValue; + } + + @override + final String id = IdGenerator.generate(); + + @override + late final WebUri url; + + @override + late final String? callbackURLScheme; + + @override + late final WebAuthenticationSessionSettings? initialSettings; + + @override + late final WebAuthenticationSessionCompletionHandler onComplete; + + static const MethodChannel _staticChannel = const MethodChannel( + 'com.pichillilorenzo/flutter_webauthenticationsession'); + + @override + Future create( + {required WebUri url, + String? callbackURLScheme, + WebAuthenticationSessionCompletionHandler onComplete, + WebAuthenticationSessionSettings? initialSettings}) async { + var session = IOSWebAuthenticationSession._create( + url: url, + callbackURLScheme: callbackURLScheme, + onComplete: onComplete, + initialSettings: initialSettings); + initialSettings = + session.initialSettings ?? WebAuthenticationSessionSettings(); + Map args = {}; + args.putIfAbsent("id", () => session.id); + args.putIfAbsent("url", () => session.url.toString()); + args.putIfAbsent("callbackURLScheme", () => session.callbackURLScheme); + args.putIfAbsent("initialSettings", () => initialSettings?.toMap()); + await _staticChannel.invokeMethod('create', args); + return session; + } + + IOSWebAuthenticationSession._create( + {required this.url, + this.callbackURLScheme, + this.onComplete, + WebAuthenticationSessionSettings? initialSettings}) + : super.implementation(IOSWebAuthenticationSessionCreationParams()) { + assert(url.toString().isNotEmpty); + assert(['http', 'https'].contains(url.scheme), + 'The specified URL has an unsupported scheme. Only HTTP and HTTPS URLs are supported on iOS.'); + + this.initialSettings = + initialSettings ?? WebAuthenticationSessionSettings(); + channel = MethodChannel( + 'com.pichillilorenzo/flutter_webauthenticationsession_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + debugLoggingSettings: + PlatformWebAuthenticationSession.debugLoggingSettings, + id: id, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + _debugLog(call.method, call.arguments); + + switch (call.method) { + case "onComplete": + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + var error = WebAuthenticationSessionError.fromNativeValue( + call.arguments["errorCode"]); + if (onComplete != null) { + onComplete!(uri, error); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + @override + Future canStart() async { + Map args = {}; + return await channel?.invokeMethod('canStart', args) ?? false; + } + + @override + Future start() async { + Map args = {}; + return await channel?.invokeMethod('start', args) ?? false; + } + + @override + Future cancel() async { + Map args = {}; + await channel?.invokeMethod("cancel", args); + } + + @override + Future dispose() async { + Map args = {}; + await channel?.invokeMethod("dispose", args); + disposeChannel(); + } + + @override + Future isAvailable() async { + Map args = {}; + return await _staticChannel.invokeMethod("isAvailable", args) ?? + false; + } +} diff --git a/flutter_inappwebview_ios/lib/src/web_message/main.dart b/flutter_inappwebview_ios/lib/src/web_message/main.dart new file mode 100644 index 00000000..d41e30c7 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/web_message/main.dart @@ -0,0 +1,3 @@ +export 'web_message_port.dart' hide InternalWebMessagePort; +export 'web_message_channel.dart' hide InternalWebMessageChannel; +export 'web_message_listener.dart'; diff --git a/flutter_inappwebview_ios/lib/src/web_message/web_message_channel.dart b/flutter_inappwebview_ios/lib/src/web_message/web_message_channel.dart new file mode 100644 index 00000000..dec20653 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/web_message/web_message_channel.dart @@ -0,0 +1,120 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'web_message_port.dart'; + +/// Object specifying creation parameters for creating a [IOSWebMessageChannel]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessageChannelCreationParams] for +/// more information. +@immutable +class IOSWebMessageChannelCreationParams + extends PlatformWebMessageChannelCreationParams { + /// Creates a new [IOSWebMessageChannelCreationParams] instance. + const IOSWebMessageChannelCreationParams( + {required super.id, required super.port1, required super.port2}); + + /// Creates a [IOSWebMessageChannelCreationParams] instance based on [PlatformWebMessageChannelCreationParams]. + factory IOSWebMessageChannelCreationParams.fromPlatformWebMessageChannelCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessageChannelCreationParams params) { + return IOSWebMessageChannelCreationParams( + id: params.id, port1: params.port1, port2: params.port2); + } + + @override + String toString() { + return 'IOSWebMessageChannelCreationParams{id: $id, port1: $port1, port2: $port2}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageChannel} +class IOSWebMessageChannel extends PlatformWebMessageChannel + with ChannelController { + /// Constructs a [IOSWebMessageChannel]. + IOSWebMessageChannel(PlatformWebMessageChannelCreationParams params) + : super.implementation( + params is IOSWebMessageChannelCreationParams + ? params + : IOSWebMessageChannelCreationParams + .fromPlatformWebMessageChannelCreationParams(params), + ) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_web_message_channel_${params.id}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + static final IOSWebMessageChannel _staticValue = IOSWebMessageChannel( + IOSWebMessageChannelCreationParams( + id: '', + port1: IOSWebMessagePort( + IOSWebMessagePortCreationParams(index: 0)), + port2: IOSWebMessagePort( + IOSWebMessagePortCreationParams(index: 1)))); + + /// Provide static access. + factory IOSWebMessageChannel.static() { + return _staticValue; + } + + IOSWebMessagePort get _iosPort1 => port1 as IOSWebMessagePort; + + IOSWebMessagePort get _iosPort2 => port2 as IOSWebMessagePort; + + static IOSWebMessageChannel? _fromMap(Map? map) { + if (map == null) { + return null; + } + var webMessageChannel = IOSWebMessageChannel( + IOSWebMessageChannelCreationParams( + id: map["id"], + port1: IOSWebMessagePort( + IOSWebMessagePortCreationParams(index: 0)), + port2: IOSWebMessagePort( + IOSWebMessagePortCreationParams(index: 1)))); + webMessageChannel._iosPort1.webMessageChannel = webMessageChannel; + webMessageChannel._iosPort2.webMessageChannel = webMessageChannel; + return webMessageChannel; + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onMessage": + int index = call.arguments["index"]; + var port = index == 0 ? _iosPort1 : _iosPort2; + if (port.onMessage != null) { + WebMessage? message = call.arguments["message"] != null + ? WebMessage.fromMap( + call.arguments["message"].cast()) + : null; + port.onMessage!(message); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + IOSWebMessageChannel? fromMap(Map? map) { + return _fromMap(map); + } + + @override + void dispose() { + disposeChannel(); + } + + @override + String toString() { + return 'IOSWebMessageChannel{id: $id, port1: $port1, port2: $port2}'; + } +} + +extension InternalWebMessageChannel on IOSWebMessageChannel { + MethodChannel? get internalChannel => channel; +} diff --git a/flutter_inappwebview_ios/lib/src/web_message/web_message_listener.dart b/flutter_inappwebview_ios/lib/src/web_message/web_message_listener.dart new file mode 100644 index 00000000..38a27880 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/web_message/web_message_listener.dart @@ -0,0 +1,164 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [IOSWebMessageListener]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessageListenerCreationParams] for +/// more information. +@immutable +class IOSWebMessageListenerCreationParams + extends PlatformWebMessageListenerCreationParams { + /// Creates a new [IOSWebMessageListenerCreationParams] instance. + const IOSWebMessageListenerCreationParams( + {required this.allowedOriginRules, + required super.jsObjectName, + super.onPostMessage}); + + /// Creates a [IOSWebMessageListenerCreationParams] instance based on [PlatformWebMessageListenerCreationParams]. + factory IOSWebMessageListenerCreationParams.fromPlatformWebMessageListenerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessageListenerCreationParams params) { + return IOSWebMessageListenerCreationParams( + allowedOriginRules: params.allowedOriginRules ?? Set.from(["*"]), + jsObjectName: params.jsObjectName, + onPostMessage: params.onPostMessage); + } + + @override + final Set allowedOriginRules; + + @override + String toString() { + return 'IOSWebMessageListenerCreationParams{jsObjectName: $jsObjectName, allowedOriginRules: $allowedOriginRules, onPostMessage: $onPostMessage}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageListener} +class IOSWebMessageListener extends PlatformWebMessageListener + with ChannelController { + /// Constructs a [IOSWebMessageListener]. + IOSWebMessageListener(PlatformWebMessageListenerCreationParams params) + : super.implementation( + params is IOSWebMessageListenerCreationParams + ? params + : IOSWebMessageListenerCreationParams + .fromPlatformWebMessageListenerCreationParams(params), + ) { + assert(!this._iosParams.allowedOriginRules.contains(""), + "allowedOriginRules cannot contain empty strings"); + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_web_message_listener_${_id}_${params.jsObjectName}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + ///Message Listener ID used internally. + final String _id = IdGenerator.generate(); + + IOSJavaScriptReplyProxy? _replyProxy; + + IOSWebMessageListenerCreationParams get _iosParams => + params as IOSWebMessageListenerCreationParams; + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onPostMessage": + if (_replyProxy == null) { + _replyProxy = IOSJavaScriptReplyProxy( + PlatformJavaScriptReplyProxyCreationParams( + webMessageListener: this)); + } + if (onPostMessage != null) { + WebMessage? message = call.arguments["message"] != null + ? WebMessage.fromMap( + call.arguments["message"].cast()) + : null; + WebUri? sourceOrigin = call.arguments["sourceOrigin"] != null + ? WebUri(call.arguments["sourceOrigin"]) + : null; + bool isMainFrame = call.arguments["isMainFrame"]; + onPostMessage!(message, sourceOrigin, isMainFrame, _replyProxy!); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + void dispose() { + disposeChannel(); + } + + @override + Map toMap() { + return { + "id": _id, + "jsObjectName": params.jsObjectName, + "allowedOriginRules": _iosParams.allowedOriginRules.toList(), + }; + } + + @override + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return 'IOSWebMessageListener{id: ${_id}, jsObjectName: ${params.jsObjectName}, allowedOriginRules: ${params.allowedOriginRules}, replyProxy: $_replyProxy}'; + } +} + +/// Object specifying creation parameters for creating a [IOSJavaScriptReplyProxy]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformJavaScriptReplyProxyCreationParams] for +/// more information. +@immutable +class IOSJavaScriptReplyProxyCreationParams + extends PlatformJavaScriptReplyProxyCreationParams { + /// Creates a new [IOSJavaScriptReplyProxyCreationParams] instance. + const IOSJavaScriptReplyProxyCreationParams( + {required super.webMessageListener}); + + /// Creates a [IOSJavaScriptReplyProxyCreationParams] instance based on [PlatformJavaScriptReplyProxyCreationParams]. + factory IOSJavaScriptReplyProxyCreationParams.fromPlatformJavaScriptReplyProxyCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformJavaScriptReplyProxyCreationParams params) { + return IOSJavaScriptReplyProxyCreationParams( + webMessageListener: params.webMessageListener); + } +} + +///{@macro flutter_inappwebview_platform_interface.JavaScriptReplyProxy} +class IOSJavaScriptReplyProxy extends PlatformJavaScriptReplyProxy { + /// Constructs a [IOSWebMessageListener]. + IOSJavaScriptReplyProxy(PlatformJavaScriptReplyProxyCreationParams params) + : super.implementation( + params is IOSJavaScriptReplyProxyCreationParams + ? params + : IOSJavaScriptReplyProxyCreationParams + .fromPlatformJavaScriptReplyProxyCreationParams(params), + ); + + IOSWebMessageListener get _iosWebMessageListener => + params.webMessageListener as IOSWebMessageListener; + + @override + Future postMessage(WebMessage message) async { + Map args = {}; + args.putIfAbsent('message', () => message.toMap()); + await _iosWebMessageListener.channel?.invokeMethod('postMessage', args); + } + + @override + String toString() { + return 'IOSJavaScriptReplyProxy{}'; + } +} diff --git a/flutter_inappwebview_ios/lib/src/web_message/web_message_port.dart b/flutter_inappwebview_ios/lib/src/web_message/web_message_port.dart new file mode 100644 index 00000000..b16b3b75 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/web_message/web_message_port.dart @@ -0,0 +1,95 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'web_message_channel.dart'; + +/// Object specifying creation parameters for creating a [IOSWebMessagePort]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessagePortCreationParams] for +/// more information. +@immutable +class IOSWebMessagePortCreationParams + extends PlatformWebMessagePortCreationParams { + /// Creates a new [IOSWebMessagePortCreationParams] instance. + const IOSWebMessagePortCreationParams({required super.index}); + + /// Creates a [IOSWebMessagePortCreationParams] instance based on [PlatformWebMessagePortCreationParams]. + factory IOSWebMessagePortCreationParams.fromPlatformWebMessagePortCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessagePortCreationParams params) { + return IOSWebMessagePortCreationParams(index: params.index); + } + + @override + String toString() { + return 'IOSWebMessagePortCreationParams{index: $index}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessagePort} +class IOSWebMessagePort extends PlatformWebMessagePort { + WebMessageCallback? _onMessage; + late IOSWebMessageChannel _webMessageChannel; + + /// Constructs a [IOSWebMessagePort]. + IOSWebMessagePort(PlatformWebMessagePortCreationParams params) + : super.implementation( + params is IOSWebMessagePortCreationParams + ? params + : IOSWebMessagePortCreationParams + .fromPlatformWebMessagePortCreationParams(params), + ); + + @override + Future setWebMessageCallback(WebMessageCallback? onMessage) async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + await _webMessageChannel.internalChannel + ?.invokeMethod('setWebMessageCallback', args); + this._onMessage = onMessage; + } + + @override + Future postMessage(WebMessage message) async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + args.putIfAbsent('message', () => message.toMap()); + await _webMessageChannel.internalChannel?.invokeMethod('postMessage', args); + } + + @override + Future close() async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + await _webMessageChannel.internalChannel?.invokeMethod('close', args); + } + + @override + Map toMap() { + return { + "index": params.index, + "webMessageChannelId": this._webMessageChannel.params.id + }; + } + + @override + Map toJson() { + return toMap(); + } + + @override + String toString() { + return 'IOSWebMessagePort{index: ${params.index}}'; + } +} + +extension InternalWebMessagePort on IOSWebMessagePort { + WebMessageCallback? get onMessage => _onMessage; + void set onMessage(WebMessageCallback? value) => _onMessage = value; + + IOSWebMessageChannel get webMessageChannel => _webMessageChannel; + void set webMessageChannel(IOSWebMessageChannel value) => + _webMessageChannel = value; +} diff --git a/flutter_inappwebview_ios/lib/src/web_storage/main.dart b/flutter_inappwebview_ios/lib/src/web_storage/main.dart new file mode 100644 index 00000000..7265ae52 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/web_storage/main.dart @@ -0,0 +1,2 @@ +export 'web_storage.dart'; +export 'web_storage_manager.dart' hide InternalWebStorageManager; diff --git a/flutter_inappwebview_ios/lib/src/web_storage/web_storage.dart b/flutter_inappwebview_ios/lib/src/web_storage/web_storage.dart new file mode 100644 index 00000000..45fa637b --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/web_storage/web_storage.dart @@ -0,0 +1,257 @@ +import 'dart:convert'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../in_app_webview/in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [IOSWebStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebStorageCreationParams] for +/// more information. +class IOSWebStorageCreationParams extends PlatformWebStorageCreationParams { + /// Creates a new [IOSWebStorageCreationParams] instance. + IOSWebStorageCreationParams( + {required super.localStorage, required super.sessionStorage}); + + /// Creates a [IOSWebStorageCreationParams] instance based on [PlatformWebStorageCreationParams]. + factory IOSWebStorageCreationParams.fromPlatformWebStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebStorageCreationParams params) { + return IOSWebStorageCreationParams( + localStorage: params.localStorage, + sessionStorage: params.sessionStorage); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebStorage} +class IOSWebStorage extends PlatformWebStorage { + /// Constructs a [IOSWebStorage]. + IOSWebStorage(PlatformWebStorageCreationParams params) + : super.implementation( + params is IOSWebStorageCreationParams + ? params + : IOSWebStorageCreationParams + .fromPlatformWebStorageCreationParams(params), + ); + + @override + PlatformLocalStorage get localStorage => params.localStorage; + + @override + PlatformSessionStorage get sessionStorage => params.sessionStorage; + + @override + void dispose() { + localStorage.dispose(); + sessionStorage.dispose(); + } +} + +/// Object specifying creation parameters for creating a [IOSStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformStorageCreationParams] for +/// more information. +class IOSStorageCreationParams extends PlatformStorageCreationParams { + /// Creates a new [IOSStorageCreationParams] instance. + IOSStorageCreationParams( + {required super.controller, required super.webStorageType}); + + /// Creates a [IOSStorageCreationParams] instance based on [PlatformStorageCreationParams]. + factory IOSStorageCreationParams.fromPlatformStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformStorageCreationParams params) { + return IOSStorageCreationParams( + controller: params.controller, webStorageType: params.webStorageType); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformStorage} +abstract class IOSStorage implements PlatformStorage { + @override + IOSInAppWebViewController? controller; + + @override + Future length() async { + var result = await controller?.evaluateJavascript(source: """ + window.$webStorageType.length; + """); + return result != null ? int.parse(json.decode(result)) : null; + } + + @override + Future setItem({required String key, required dynamic value}) async { + var encodedValue = json.encode(value); + await controller?.evaluateJavascript(source: """ + window.$webStorageType.setItem("$key", ${value is String ? encodedValue : "JSON.stringify($encodedValue)"}); + """); + } + + @override + Future getItem({required String key}) async { + var itemValue = await controller?.evaluateJavascript(source: """ + window.$webStorageType.getItem("$key"); + """); + + if (itemValue == null) { + return null; + } + + try { + return json.decode(itemValue); + } catch (e) {} + + return itemValue; + } + + @override + Future removeItem({required String key}) async { + await controller?.evaluateJavascript(source: """ + window.$webStorageType.removeItem("$key"); + """); + } + + @override + Future> getItems() async { + var webStorageItems = []; + + List>? items = + (await controller?.evaluateJavascript(source: """ +(function() { + var webStorageItems = []; + for(var i = 0; i < window.$webStorageType.length; i++){ + var key = window.$webStorageType.key(i); + webStorageItems.push( + { + key: key, + value: window.$webStorageType.getItem(key) + } + ); + } + return webStorageItems; +})(); + """))?.cast>(); + + if (items == null) { + return webStorageItems; + } + + for (var item in items) { + webStorageItems + .add(WebStorageItem(key: item["key"], value: item["value"])); + } + + return webStorageItems; + } + + @override + Future clear() async { + await controller?.evaluateJavascript(source: """ + window.$webStorageType.clear(); + """); + } + + @override + Future key({required int index}) async { + var result = await controller?.evaluateJavascript(source: """ + window.$webStorageType.key($index); + """); + return result != null ? json.decode(result) : null; + } + + @override + void dispose() { + controller = null; + } +} + +/// Object specifying creation parameters for creating a [IOSLocalStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformLocalStorageCreationParams] for +/// more information. +class IOSLocalStorageCreationParams + extends PlatformLocalStorageCreationParams { + /// Creates a new [IOSLocalStorageCreationParams] instance. + IOSLocalStorageCreationParams(super.params); + + /// Creates a [IOSLocalStorageCreationParams] instance based on [PlatformLocalStorageCreationParams]. + factory IOSLocalStorageCreationParams.fromPlatformLocalStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformLocalStorageCreationParams params) { + return IOSLocalStorageCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformLocalStorage} +class IOSLocalStorage extends PlatformLocalStorage with IOSStorage { + /// Constructs a [IOSLocalStorage]. + IOSLocalStorage(PlatformLocalStorageCreationParams params) + : super.implementation( + params is IOSLocalStorageCreationParams + ? params + : IOSLocalStorageCreationParams + .fromPlatformLocalStorageCreationParams(params), + ); + + /// Default storage + factory IOSLocalStorage.defaultStorage( + {required PlatformInAppWebViewController? controller}) { + return IOSLocalStorage(IOSLocalStorageCreationParams( + PlatformLocalStorageCreationParams(PlatformStorageCreationParams( + controller: controller, + webStorageType: WebStorageType.LOCAL_STORAGE)))); + } + + @override + IOSInAppWebViewController? get controller => + params.controller as IOSInAppWebViewController?; +} + +/// Object specifying creation parameters for creating a [IOSSessionStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformSessionStorageCreationParams] for +/// more information. +class IOSSessionStorageCreationParams + extends PlatformSessionStorageCreationParams { + /// Creates a new [IOSSessionStorageCreationParams] instance. + IOSSessionStorageCreationParams(super.params); + + /// Creates a [IOSSessionStorageCreationParams] instance based on [PlatformSessionStorageCreationParams]. + factory IOSSessionStorageCreationParams.fromPlatformSessionStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformSessionStorageCreationParams params) { + return IOSSessionStorageCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformSessionStorage} +class IOSSessionStorage extends PlatformSessionStorage with IOSStorage { + /// Constructs a [IOSSessionStorage]. + IOSSessionStorage(PlatformSessionStorageCreationParams params) + : super.implementation( + params is IOSSessionStorageCreationParams + ? params + : IOSSessionStorageCreationParams + .fromPlatformSessionStorageCreationParams(params), + ); + + /// Default storage + factory IOSSessionStorage.defaultStorage( + {required PlatformInAppWebViewController? controller}) { + return IOSSessionStorage(IOSSessionStorageCreationParams( + PlatformSessionStorageCreationParams(PlatformStorageCreationParams( + controller: controller, + webStorageType: WebStorageType.SESSION_STORAGE)))); + } + + @override + IOSInAppWebViewController? get controller => + params.controller as IOSInAppWebViewController?; +} diff --git a/flutter_inappwebview_ios/lib/src/web_storage/web_storage_manager.dart b/flutter_inappwebview_ios/lib/src/web_storage/web_storage_manager.dart new file mode 100755 index 00000000..bb259cc3 --- /dev/null +++ b/flutter_inappwebview_ios/lib/src/web_storage/web_storage_manager.dart @@ -0,0 +1,134 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [IOSWebStorageManager]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebStorageManagerCreationParams] for +/// more information. +@immutable +class IOSWebStorageManagerCreationParams + extends PlatformWebStorageManagerCreationParams { + /// Creates a new [IOSWebStorageManagerCreationParams] instance. + const IOSWebStorageManagerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformWebStorageManagerCreationParams params, + ) : super(); + + /// Creates a [IOSWebStorageManagerCreationParams] instance based on [PlatformWebStorageManagerCreationParams]. + factory IOSWebStorageManagerCreationParams.fromPlatformWebStorageManagerCreationParams( + PlatformWebStorageManagerCreationParams params) { + return IOSWebStorageManagerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebStorageManager} +class IOSWebStorageManager extends PlatformWebStorageManager + with ChannelController { + /// Creates a new [IOSWebStorageManager]. + IOSWebStorageManager(PlatformWebStorageManagerCreationParams params) + : super.implementation( + params is IOSWebStorageManagerCreationParams + ? params + : IOSWebStorageManagerCreationParams + .fromPlatformWebStorageManagerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_webstoragemanager'); + handler = handleMethod; + initMethodCallHandler(); + } + + static IOSWebStorageManager? _instance; + + ///Gets the WebStorage manager shared instance. + static IOSWebStorageManager instance() { + return (_instance != null) ? _instance! : _init(); + } + + static IOSWebStorageManager _init() { + _instance = IOSWebStorageManager(IOSWebStorageManagerCreationParams( + const PlatformWebStorageManagerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future> fetchDataRecords( + {required Set dataTypes}) async { + List recordList = []; + List dataTypesList = []; + for (var dataType in dataTypes) { + dataTypesList.add(dataType.toNativeValue()); + } + Map args = {}; + args.putIfAbsent("dataTypes", () => dataTypesList); + List> records = + (await channel?.invokeMethod('fetchDataRecords', args)) + ?.cast>() ?? + []; + for (var record in records) { + List dataTypesString = record["dataTypes"].cast(); + Set dataTypes = Set(); + for (var dataTypeValue in dataTypesString) { + var dataType = WebsiteDataType.fromNativeValue(dataTypeValue); + if (dataType != null) { + dataTypes.add(dataType); + } + } + recordList.add(WebsiteDataRecord( + displayName: record["displayName"], dataTypes: dataTypes)); + } + return recordList; + } + + @override + Future removeDataFor( + {required Set dataTypes, + required List dataRecords}) async { + List dataTypesList = []; + for (var dataType in dataTypes) { + dataTypesList.add(dataType.toNativeValue()); + } + + List> recordList = []; + for (var record in dataRecords) { + recordList.add(record.toMap()); + } + + Map args = {}; + args.putIfAbsent("dataTypes", () => dataTypesList); + args.putIfAbsent("recordList", () => recordList); + await channel?.invokeMethod('removeDataFor', args); + } + + @override + Future removeDataModifiedSince( + {required Set dataTypes, required DateTime date}) async { + List dataTypesList = []; + for (var dataType in dataTypes) { + dataTypesList.add(dataType.toNativeValue()); + } + + var timestamp = date.millisecondsSinceEpoch; + + Map args = {}; + args.putIfAbsent("dataTypes", () => dataTypesList); + args.putIfAbsent("timestamp", () => timestamp); + await channel?.invokeMethod('removeDataModifiedSince', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalWebStorageManager on IOSWebStorageManager { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_ios/pubspec.yaml b/flutter_inappwebview_ios/pubspec.yaml new file mode 100644 index 00000000..3df35559 --- /dev/null +++ b/flutter_inappwebview_ios/pubspec.yaml @@ -0,0 +1,80 @@ +name: flutter_inappwebview_ios +description: iOS implementation of the flutter_inappwebview plugin. +version: 1.0.1 +homepage: https://inappwebview.dev/ +repository: https://github.com/pichillilorenzo/flutter_inappwebview/tree/master/flutter_inappwebview_ios +issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues +topics: + - html + - webview + - webview-flutter + - inappwebview + - browser + +environment: + sdk: ">=2.17.0 <4.0.0" + flutter: ">=3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_inappwebview_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + plugin_platform_interface: ^2.0.2 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + implements: flutter_inappwebview + platforms: + ios: + pluginClass: InAppWebViewFlutterPlugin + dartPluginClass: IOSInAppWebViewPlatform + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter_inappwebview_ios/test/flutter_inappwebview_ios_test.dart b/flutter_inappwebview_ios/test/flutter_inappwebview_ios_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_macos/.gitignore b/flutter_inappwebview_macos/.gitignore new file mode 100644 index 00000000..96486fd9 --- /dev/null +++ b/flutter_inappwebview_macos/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/flutter_inappwebview_macos/.metadata b/flutter_inappwebview_macos/.metadata new file mode 100644 index 00000000..650038ed --- /dev/null +++ b/flutter_inappwebview_macos/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e + base_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e + - platform: macos + create_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e + base_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/flutter_inappwebview_macos/CHANGELOG.md b/flutter_inappwebview_macos/CHANGELOG.md new file mode 100644 index 00000000..227f4433 --- /dev/null +++ b/flutter_inappwebview_macos/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +Initial release. diff --git a/flutter_inappwebview_macos/LICENSE b/flutter_inappwebview_macos/LICENSE new file mode 100644 index 00000000..4dada16d --- /dev/null +++ b/flutter_inappwebview_macos/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Lorenzo Pichilli + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/flutter_inappwebview_macos/README.md b/flutter_inappwebview_macos/README.md new file mode 100644 index 00000000..7af883d5 --- /dev/null +++ b/flutter_inappwebview_macos/README.md @@ -0,0 +1,13 @@ +# flutter\_inappwebview\_macos + +The Apple macOS WKWebView implementation of [`flutter_inappwebview`](https://pub.dev/packages/flutter_inappwebview). + +## Usage + +This package is [endorsed](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin), +which means you can simply use `flutter_inappwebview` +normally. This package will be automatically included in your app when you do, +so you do not need to add it to your `pubspec.yaml`. + +However, if you `import` this package to use any of its APIs directly, you +should add it to your `pubspec.yaml` as usual. \ No newline at end of file diff --git a/flutter_inappwebview_macos/analysis_options.yaml b/flutter_inappwebview_macos/analysis_options.yaml new file mode 100644 index 00000000..84964c30 --- /dev/null +++ b/flutter_inappwebview_macos/analysis_options.yaml @@ -0,0 +1,15 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + constant_identifier_names: ignore + deprecated_member_use_from_same_package: ignore + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options +analyzer: + errors: + deprecated_member_use: ignore + deprecated_member_use_from_same_package: ignore + unnecessary_cast: ignore + unnecessary_import: ignore diff --git a/flutter_inappwebview_macos/build.yaml b/flutter_inappwebview_macos/build.yaml new file mode 100644 index 00000000..e2b3acf3 --- /dev/null +++ b/flutter_inappwebview_macos/build.yaml @@ -0,0 +1,5 @@ +targets: + $default: + sources: + exclude: + - example/**.dart diff --git a/flutter_inappwebview_macos/example/.gitignore b/flutter_inappwebview_macos/example/.gitignore new file mode 100644 index 00000000..24476c5d --- /dev/null +++ b/flutter_inappwebview_macos/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/flutter_inappwebview_macos/example/README.md b/flutter_inappwebview_macos/example/README.md new file mode 100644 index 00000000..1e75fecb --- /dev/null +++ b/flutter_inappwebview_macos/example/README.md @@ -0,0 +1,16 @@ +# flutter_inappwebview_macos_example + +Demonstrates how to use the flutter_inappwebview_macos plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter_inappwebview_macos/example/analysis_options.yaml b/flutter_inappwebview_macos/example/analysis_options.yaml new file mode 100644 index 00000000..0d290213 --- /dev/null +++ b/flutter_inappwebview_macos/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/flutter_inappwebview_macos/example/integration_test/plugin_integration_test.dart b/flutter_inappwebview_macos/example/integration_test/plugin_integration_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_macos/example/lib/main.dart b/flutter_inappwebview_macos/example/lib/main.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_macos/example/macos/.gitignore b/flutter_inappwebview_macos/example/macos/.gitignore new file mode 100644 index 00000000..746adbb6 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/flutter_inappwebview_macos/example/macos/Flutter/Flutter-Debug.xcconfig b/flutter_inappwebview_macos/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..4b81f9b2 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/flutter_inappwebview_macos/example/macos/Flutter/Flutter-Release.xcconfig b/flutter_inappwebview_macos/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..5caa9d15 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/flutter_inappwebview_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift b/flutter_inappwebview_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..738fc0a4 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import flutter_inappwebview_macos + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) +} diff --git a/flutter_inappwebview_macos/example/macos/Podfile b/flutter_inappwebview_macos/example/macos/Podfile new file mode 100644 index 00000000..c795730d --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/flutter_inappwebview_macos/example/macos/Runner.xcodeproj/project.pbxproj b/flutter_inappwebview_macos/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..6c14ab4e --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,695 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* flutter_inappwebview_macos_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "flutter_inappwebview_macos_example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* flutter_inappwebview_macos_example.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* flutter_inappwebview_macos_example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1430; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterInappwebviewMacosExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/flutter_inappwebview_macos_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_inappwebview_macos_example"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterInappwebviewMacosExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/flutter_inappwebview_macos_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_inappwebview_macos_example"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterInappwebviewMacosExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/flutter_inappwebview_macos_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_inappwebview_macos_example"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/flutter_inappwebview_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter_inappwebview_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter_inappwebview_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter_inappwebview_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..f50c0304 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_inappwebview_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/flutter_inappwebview_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter_inappwebview_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter_inappwebview_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter_inappwebview_macos/example/macos/Runner/AppDelegate.swift b/flutter_inappwebview_macos/example/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..d53ef643 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a2ec33f1 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 00000000..82b6f9d9 Binary files /dev/null and b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 00000000..13b35eba Binary files /dev/null and b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 00000000..0a3f5fa4 Binary files /dev/null and b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 00000000..bdb57226 Binary files /dev/null and b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 00000000..f083318e Binary files /dev/null and b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 00000000..326c0e72 Binary files /dev/null and b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 00000000..2f1632cf Binary files /dev/null and b/flutter_inappwebview_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/flutter_inappwebview_macos/example/macos/Runner/Base.lproj/MainMenu.xib b/flutter_inappwebview_macos/example/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000..80e867a4 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_inappwebview_macos/example/macos/Runner/Configs/AppInfo.xcconfig b/flutter_inappwebview_macos/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..ce19f930 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = flutter_inappwebview_macos_example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutterInappwebviewMacosExample + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2023 com.pichillilorenzo. All rights reserved. diff --git a/flutter_inappwebview_macos/example/macos/Runner/Configs/Debug.xcconfig b/flutter_inappwebview_macos/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/flutter_inappwebview_macos/example/macos/Runner/Configs/Release.xcconfig b/flutter_inappwebview_macos/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/flutter_inappwebview_macos/example/macos/Runner/Configs/Warnings.xcconfig b/flutter_inappwebview_macos/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/flutter_inappwebview_macos/example/macos/Runner/DebugProfile.entitlements b/flutter_inappwebview_macos/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..dddb8a30 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/flutter_inappwebview_macos/example/macos/Runner/Info.plist b/flutter_inappwebview_macos/example/macos/Runner/Info.plist new file mode 100644 index 00000000..4789daa6 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/flutter_inappwebview_macos/example/macos/Runner/MainFlutterWindow.swift b/flutter_inappwebview_macos/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..3cc05eb2 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/flutter_inappwebview_macos/example/macos/Runner/Release.entitlements b/flutter_inappwebview_macos/example/macos/Runner/Release.entitlements new file mode 100644 index 00000000..852fa1a4 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/flutter_inappwebview_macos/example/macos/RunnerTests/RunnerTests.swift b/flutter_inappwebview_macos/example/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..fd897645 --- /dev/null +++ b/flutter_inappwebview_macos/example/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,27 @@ +import FlutterMacOS +import Cocoa +import XCTest + +@testable import flutter_inappwebview_macos + +// This demonstrates a simple unit test of the Swift portion of this plugin's implementation. +// +// See https://developer.apple.com/documentation/xctest for more information about using XCTest. + +class RunnerTests: XCTestCase { + + func testGetPlatformVersion() { + let plugin = FlutterInappwebviewMacosPlugin() + + let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) + + let resultExpectation = expectation(description: "result block must be called.") + plugin.handle(call) { result in + XCTAssertEqual(result as! String, + "macOS " + ProcessInfo.processInfo.operatingSystemVersionString) + resultExpectation.fulfill() + } + waitForExpectations(timeout: 1) + } + +} diff --git a/flutter_inappwebview_macos/example/pubspec.lock b/flutter_inappwebview_macos/example/pubspec.lock new file mode 100644 index 00000000..d0369033 --- /dev/null +++ b/flutter_inappwebview_macos/example/pubspec.lock @@ -0,0 +1,283 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" + source: hosted + version: "1.17.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "5f80fd30e208ddded7dbbcd0d569e7995f9f63d45ea3f548d8dd4c0b473fb4c8" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter_inappwebview_macos: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "1.0.0" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: c221a6788c73421284208e3449cad07d8c65fdace120322483181a2b94d95697 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 + url: "https://pub.dev" + source: hosted + version: "2.1.7" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + url: "https://pub.dev" + source: hosted + version: "11.7.1" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + url: "https://pub.dev" + source: hosted + version: "3.0.2" +sdks: + dart: ">=3.1.4 <4.0.0" + flutter: ">=3.0.0" diff --git a/flutter_inappwebview_macos/example/pubspec.yaml b/flutter_inappwebview_macos/example/pubspec.yaml new file mode 100644 index 00000000..95aef948 --- /dev/null +++ b/flutter_inappwebview_macos/example/pubspec.yaml @@ -0,0 +1,85 @@ +name: flutter_inappwebview_macos_example +description: Demonstrates how to use the flutter_inappwebview_macos plugin. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=3.1.4 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + flutter_inappwebview_macos: + # When depending on this package from a real application you should use: + # flutter_inappwebview_macos: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + integration_test: + sdk: flutter + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter_inappwebview_macos/example/test/widget_test.dart b/flutter_inappwebview_macos/example/test/widget_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_macos/lib/flutter_inappwebview_macos.dart b/flutter_inappwebview_macos/lib/flutter_inappwebview_macos.dart new file mode 100644 index 00000000..b82fc696 --- /dev/null +++ b/flutter_inappwebview_macos/lib/flutter_inappwebview_macos.dart @@ -0,0 +1,3 @@ +library flutter_inappwebview_macos; + +export 'src/main.dart'; diff --git a/flutter_inappwebview_macos/lib/src/cookie_manager.dart b/flutter_inappwebview_macos/lib/src/cookie_manager.dart new file mode 100755 index 00000000..3fbc69a0 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/cookie_manager.dart @@ -0,0 +1,434 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'in_app_webview/headless_in_app_webview.dart'; +import 'platform_util.dart'; + +/// Object specifying creation parameters for creating a [MacOSCookieManager]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformCookieManagerCreationParams] for +/// more information. +@immutable +class MacOSCookieManagerCreationParams + extends PlatformCookieManagerCreationParams { + /// Creates a new [MacOSCookieManagerCreationParams] instance. + const MacOSCookieManagerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformCookieManagerCreationParams params, + ) : super(); + + /// Creates a [MacOSCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams]. + factory MacOSCookieManagerCreationParams.fromPlatformCookieManagerCreationParams( + PlatformCookieManagerCreationParams params) { + return MacOSCookieManagerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager} +class MacOSCookieManager extends PlatformCookieManager + with ChannelController { + /// Creates a new [MacOSCookieManager]. + MacOSCookieManager(PlatformCookieManagerCreationParams params) + : super.implementation( + params is MacOSCookieManagerCreationParams + ? params + : MacOSCookieManagerCreationParams + .fromPlatformCookieManagerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_cookiemanager'); + handler = handleMethod; + initMethodCallHandler(); + } + + static MacOSCookieManager? _instance; + + ///Gets the [MacOSCookieManager] shared instance. + static MacOSCookieManager instance() { + return (_instance != null) ? _instance! : _init(); + } + + static MacOSCookieManager _init() { + _instance = MacOSCookieManager(MacOSCookieManagerCreationParams( + const PlatformCookieManagerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future setCookie( + {required WebUri url, + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + bool? isHttpOnly, + HTTPCookieSameSitePolicy? sameSite, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + webViewController = webViewController ?? iosBelow11WebViewController; + + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + assert(value.isNotEmpty); + assert(path.isNotEmpty); + + if (await _shouldUseJavascript()) { + await _setCookieWithJavaScript( + url: url, + name: name, + value: value, + domain: domain, + path: path, + expiresDate: expiresDate, + maxAge: maxAge, + isSecure: isSecure, + sameSite: sameSite, + webViewController: webViewController); + return true; + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('name', () => name); + args.putIfAbsent('value', () => value); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + args.putIfAbsent('expiresDate', () => expiresDate?.toString()); + args.putIfAbsent('maxAge', () => maxAge); + args.putIfAbsent('isSecure', () => isSecure); + args.putIfAbsent('isHttpOnly', () => isHttpOnly); + args.putIfAbsent('sameSite', () => sameSite?.toNativeValue()); + + return await channel?.invokeMethod('setCookie', args) ?? false; + } + + Future _setCookieWithJavaScript( + {required WebUri url, + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + HTTPCookieSameSitePolicy? sameSite, + PlatformInAppWebViewController? webViewController}) async { + var cookieValue = name + "=" + value + "; Path=" + path; + + if (domain != null) cookieValue += "; Domain=" + domain; + + if (expiresDate != null) + cookieValue += "; Expires=" + await _getCookieExpirationDate(expiresDate); + + if (maxAge != null) cookieValue += "; Max-Age=" + maxAge.toString(); + + if (isSecure != null && isSecure) cookieValue += "; Secure"; + + if (sameSite != null) + cookieValue += "; SameSite=" + sameSite.toNativeValue(); + + cookieValue += ";"; + + if (webViewController != null) { + final javaScriptEnabled = + (await webViewController.getSettings())?.javaScriptEnabled ?? false; + if (javaScriptEnabled) { + await webViewController.evaluateJavascript( + source: 'document.cookie="$cookieValue"'); + return; + } + } + + final setCookieCompleter = Completer(); + final headlessWebView = + MacOSHeadlessInAppWebView(MacOSHeadlessInAppWebViewCreationParams( + initialUrlRequest: URLRequest(url: url), + onLoadStop: (controller, url) async { + await controller.evaluateJavascript( + source: 'document.cookie="$cookieValue"'); + setCookieCompleter.complete(); + })); + await headlessWebView.run(); + await setCookieCompleter.future; + await headlessWebView.dispose(); + } + + @override + Future> getCookies( + {required WebUri url, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + return await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + } + + List cookies = []; + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + List cookieListMap = + await channel?.invokeMethod('getCookies', args) ?? []; + cookieListMap = cookieListMap.cast>(); + + cookieListMap.forEach((cookieMap) { + cookies.add(Cookie( + name: cookieMap["name"], + value: cookieMap["value"], + expiresDate: cookieMap["expiresDate"], + isSessionOnly: cookieMap["isSessionOnly"], + domain: cookieMap["domain"], + sameSite: + HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), + isSecure: cookieMap["isSecure"], + isHttpOnly: cookieMap["isHttpOnly"], + path: cookieMap["path"])); + }); + return cookies; + } + + Future> _getCookiesWithJavaScript( + {required WebUri url, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + List cookies = []; + + if (webViewController != null) { + final javaScriptEnabled = + (await webViewController.getSettings())?.javaScriptEnabled ?? false; + if (javaScriptEnabled) { + List documentCookies = (await webViewController + .evaluateJavascript(source: 'document.cookie') as String) + .split(';') + .map((documentCookie) => documentCookie.trim()) + .toList(); + documentCookies.forEach((documentCookie) { + List cookie = documentCookie.split('='); + if (cookie.length > 1) { + cookies.add(Cookie( + name: cookie[0], + value: cookie[1], + )); + } + }); + return cookies; + } + } + + final pageLoaded = Completer(); + final headlessWebView = + MacOSHeadlessInAppWebView(MacOSHeadlessInAppWebViewCreationParams( + initialUrlRequest: URLRequest(url: url), + onLoadStop: (controller, url) async { + pageLoaded.complete(); + }, + )); + await headlessWebView.run(); + await pageLoaded.future; + + List documentCookies = (await headlessWebView.webViewController! + .evaluateJavascript(source: 'document.cookie') as String) + .split(';') + .map((documentCookie) => documentCookie.trim()) + .toList(); + documentCookies.forEach((documentCookie) { + List cookie = documentCookie.split('='); + if (cookie.length > 1) { + cookies.add(Cookie( + name: cookie[0], + value: cookie[1], + )); + } + }); + await headlessWebView.dispose(); + return cookies; + } + + @override + Future getCookie( + {required WebUri url, + required String name, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + List cookies = await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + return cookies + .cast() + .firstWhere((cookie) => cookie!.name == name, orElse: () => null); + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + List cookies = + await channel?.invokeMethod('getCookies', args) ?? []; + cookies = cookies.cast>(); + for (var i = 0; i < cookies.length; i++) { + cookies[i] = cookies[i].cast(); + if (cookies[i]["name"] == name) + return Cookie( + name: cookies[i]["name"], + value: cookies[i]["value"], + expiresDate: cookies[i]["expiresDate"], + isSessionOnly: cookies[i]["isSessionOnly"], + domain: cookies[i]["domain"], + sameSite: HTTPCookieSameSitePolicy.fromNativeValue( + cookies[i]["sameSite"]), + isSecure: cookies[i]["isSecure"], + isHttpOnly: cookies[i]["isHttpOnly"], + path: cookies[i]["path"]); + } + return null; + } + + @override + Future deleteCookie( + {required WebUri url, + required String name, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + await _setCookieWithJavaScript( + url: url, + name: name, + value: "", + path: path, + domain: domain, + maxAge: -1, + webViewController: webViewController); + return; + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('name', () => name); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + await channel?.invokeMethod('deleteCookie', args); + } + + @override + Future deleteCookies( + {required WebUri url, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + List cookies = await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + for (var i = 0; i < cookies.length; i++) { + await _setCookieWithJavaScript( + url: url, + name: cookies[i].name, + value: "", + path: path, + domain: domain, + maxAge: -1, + webViewController: webViewController); + } + return; + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + await channel?.invokeMethod('deleteCookies', args); + } + + @override + Future deleteAllCookies() async { + Map args = {}; + await channel?.invokeMethod('deleteAllCookies', args); + } + + @override + Future> getAllCookies() async { + List cookies = []; + + Map args = {}; + List cookieListMap = + await channel?.invokeMethod('getAllCookies', args) ?? []; + cookieListMap = cookieListMap.cast>(); + + cookieListMap.forEach((cookieMap) { + cookies.add(Cookie( + name: cookieMap["name"], + value: cookieMap["value"], + expiresDate: cookieMap["expiresDate"], + isSessionOnly: cookieMap["isSessionOnly"], + domain: cookieMap["domain"], + sameSite: + HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), + isSecure: cookieMap["isSecure"], + isHttpOnly: cookieMap["isHttpOnly"], + path: cookieMap["path"])); + }); + return cookies; + } + + Future _getCookieExpirationDate(int expiresDate) async { + var platformUtil = PlatformUtil.instance(); + var dateTime = DateTime.fromMillisecondsSinceEpoch(expiresDate).toUtc(); + return !kIsWeb + ? await platformUtil.formatDate( + date: dateTime, + format: 'EEE, dd MMM yyyy hh:mm:ss z', + locale: 'en_US', + timezone: 'GMT') + : await platformUtil.getWebCookieExpirationDate(date: dateTime); + } + + Future _shouldUseJavascript() async { + final platformUtil = PlatformUtil.instance(); + final systemVersion = await platformUtil.getSystemVersion(); + return systemVersion.compareTo("11") == -1; + } + + @override + void dispose() { + // empty + } +} + +extension InternalCookieManager on MacOSCookieManager { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_macos/lib/src/find_interaction/find_interaction_controller.dart b/flutter_inappwebview_macos/lib/src/find_interaction/find_interaction_controller.dart new file mode 100644 index 00000000..e8810f26 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/find_interaction/find_interaction_controller.dart @@ -0,0 +1,125 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [MacOSFindInteractionController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformFindInteractionControllerCreationParams] for +/// more information. +@immutable +class MacOSFindInteractionControllerCreationParams + extends PlatformFindInteractionControllerCreationParams { + /// Creates a new [MacOSFindInteractionControllerCreationParams] instance. + const MacOSFindInteractionControllerCreationParams( + {super.onFindResultReceived}); + + /// Creates a [MacOSFindInteractionControllerCreationParams] instance based on [PlatformFindInteractionControllerCreationParams]. + factory MacOSFindInteractionControllerCreationParams.fromPlatformFindInteractionControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformFindInteractionControllerCreationParams params) { + return MacOSFindInteractionControllerCreationParams( + onFindResultReceived: params.onFindResultReceived); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController} +class MacOSFindInteractionController extends PlatformFindInteractionController + with ChannelController { + /// Constructs a [MacOSFindInteractionController]. + MacOSFindInteractionController( + PlatformFindInteractionControllerCreationParams params) + : super.implementation( + params is MacOSFindInteractionControllerCreationParams + ? params + : MacOSFindInteractionControllerCreationParams + .fromPlatformFindInteractionControllerCreationParams(params), + ); + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + debugLoggingSettings: + PlatformFindInteractionController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + _debugLog(call.method, call.arguments); + + switch (call.method) { + case "onFindResultReceived": + if (onFindResultReceived != null) { + int activeMatchOrdinal = call.arguments["activeMatchOrdinal"]; + int numberOfMatches = call.arguments["numberOfMatches"]; + bool isDoneCounting = call.arguments["isDoneCounting"]; + onFindResultReceived!( + this, activeMatchOrdinal, numberOfMatches, isDoneCounting); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.findAll} + Future findAll({String? find}) async { + Map args = {}; + args.putIfAbsent('find', () => find); + await channel?.invokeMethod('findAll', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.findNext} + Future findNext({bool forward = true}) async { + Map args = {}; + args.putIfAbsent('forward', () => forward); + await channel?.invokeMethod('findNext', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.clearMatches} + Future clearMatches() async { + Map args = {}; + await channel?.invokeMethod('clearMatches', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.setSearchText} + Future setSearchText(String? searchText) async { + Map args = {}; + args.putIfAbsent('searchText', () => searchText); + await channel?.invokeMethod('setSearchText', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.getSearchText} + Future getSearchText() async { + Map args = {}; + return await channel?.invokeMethod('getSearchText', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.getActiveFindSession} + Future getActiveFindSession() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('getActiveFindSession', args)) + ?.cast(); + return FindSession.fromMap(result); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.dispose} + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: !isKeepAlive); + } +} + +extension InternalFindInteractionController + on MacOSFindInteractionController { + void init(dynamic id) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } +} diff --git a/flutter_inappwebview_macos/lib/src/find_interaction/main.dart b/flutter_inappwebview_macos/lib/src/find_interaction/main.dart new file mode 100644 index 00000000..a7adaacf --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/find_interaction/main.dart @@ -0,0 +1,2 @@ +export 'find_interaction_controller.dart' + hide InternalFindInteractionController; diff --git a/flutter_inappwebview_macos/lib/src/http_auth_credentials_database.dart b/flutter_inappwebview_macos/lib/src/http_auth_credentials_database.dart new file mode 100755 index 00000000..4b07865c --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/http_auth_credentials_database.dart @@ -0,0 +1,155 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [MacOSHttpAuthCredentialDatabase]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformHttpAuthCredentialDatabaseCreationParams] for +/// more information. +@immutable +class MacOSHttpAuthCredentialDatabaseCreationParams + extends PlatformHttpAuthCredentialDatabaseCreationParams { + /// Creates a new [MacOSHttpAuthCredentialDatabaseCreationParams] instance. + const MacOSHttpAuthCredentialDatabaseCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformHttpAuthCredentialDatabaseCreationParams params, + ) : super(); + + /// Creates a [MacOSHttpAuthCredentialDatabaseCreationParams] instance based on [PlatformHttpAuthCredentialDatabaseCreationParams]. + factory MacOSHttpAuthCredentialDatabaseCreationParams.fromPlatformHttpAuthCredentialDatabaseCreationParams( + PlatformHttpAuthCredentialDatabaseCreationParams params) { + return MacOSHttpAuthCredentialDatabaseCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformHttpAuthCredentialDatabase} +class MacOSHttpAuthCredentialDatabase + extends PlatformHttpAuthCredentialDatabase with ChannelController { + /// Creates a new [MacOSHttpAuthCredentialDatabase]. + MacOSHttpAuthCredentialDatabase( + PlatformHttpAuthCredentialDatabaseCreationParams params) + : super.implementation( + params is MacOSHttpAuthCredentialDatabaseCreationParams + ? params + : MacOSHttpAuthCredentialDatabaseCreationParams + .fromPlatformHttpAuthCredentialDatabaseCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_credential_database'); + handler = handleMethod; + initMethodCallHandler(); + } + + static MacOSHttpAuthCredentialDatabase? _instance; + + ///Gets the database shared instance. + static MacOSHttpAuthCredentialDatabase instance() { + return (_instance != null) ? _instance! : _init(); + } + + static MacOSHttpAuthCredentialDatabase _init() { + _instance = MacOSHttpAuthCredentialDatabase( + MacOSHttpAuthCredentialDatabaseCreationParams( + const PlatformHttpAuthCredentialDatabaseCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future> + getAllAuthCredentials() async { + Map args = {}; + List allCredentials = + await channel?.invokeMethod('getAllAuthCredentials', args) ?? []; + + List result = []; + + for (Map map in allCredentials) { + var element = URLProtectionSpaceHttpAuthCredentials.fromMap( + map.cast()); + if (element != null) { + result.add(element); + } + } + return result; + } + + @override + Future> getHttpAuthCredentials( + {required URLProtectionSpace protectionSpace}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + List credentialList = + await channel?.invokeMethod('getHttpAuthCredentials', args) ?? []; + List credentials = []; + for (Map map in credentialList) { + var credential = URLCredential.fromMap(map.cast()); + if (credential != null) { + credentials.add(credential); + } + } + return credentials; + } + + @override + Future setHttpAuthCredential( + {required URLProtectionSpace protectionSpace, + required URLCredential credential}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + args.putIfAbsent("username", () => credential.username); + args.putIfAbsent("password", () => credential.password); + await channel?.invokeMethod('setHttpAuthCredential', args); + } + + @override + Future removeHttpAuthCredential( + {required URLProtectionSpace protectionSpace, + required URLCredential credential}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + args.putIfAbsent("username", () => credential.username); + args.putIfAbsent("password", () => credential.password); + await channel?.invokeMethod('removeHttpAuthCredential', args); + } + + @override + Future removeHttpAuthCredentials( + {required URLProtectionSpace protectionSpace}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + await channel?.invokeMethod('removeHttpAuthCredentials', args); + } + + @override + Future clearAllAuthCredentials() async { + Map args = {}; + await channel?.invokeMethod('clearAllAuthCredentials', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalHttpAuthCredentialDatabase + on MacOSHttpAuthCredentialDatabase { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_macos/lib/src/in_app_browser/in_app_browser.dart b/flutter_inappwebview_macos/lib/src/in_app_browser/in_app_browser.dart new file mode 100755 index 00000000..429f1273 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/in_app_browser/in_app_browser.dart @@ -0,0 +1,369 @@ +import 'dart:async'; +import 'dart:collection'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../find_interaction/find_interaction_controller.dart'; +import '../in_app_webview/in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [MacOSInAppBrowser]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInAppBrowserCreationParams] for +/// more information. +class MacOSInAppBrowserCreationParams + extends PlatformInAppBrowserCreationParams { + /// Creates a new [MacOSInAppBrowserCreationParams] instance. + MacOSInAppBrowserCreationParams( + {super.contextMenu, + super.pullToRefreshController, + this.findInteractionController, + super.initialUserScripts, + super.windowId}); + + /// Creates a [MacOSInAppBrowserCreationParams] instance based on [PlatformInAppBrowserCreationParams]. + factory MacOSInAppBrowserCreationParams.fromPlatformInAppBrowserCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformInAppBrowserCreationParams params) { + return MacOSInAppBrowserCreationParams( + contextMenu: params.contextMenu, + pullToRefreshController: params.pullToRefreshController, + findInteractionController: params.findInteractionController + as MacOSFindInteractionController?, + initialUserScripts: params.initialUserScripts, + windowId: params.windowId); + } + + @override + final MacOSFindInteractionController? findInteractionController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser} +class MacOSInAppBrowser extends PlatformInAppBrowser with ChannelController { + @override + final String id = IdGenerator.generate(); + + /// Constructs a [MacOSInAppBrowser]. + MacOSInAppBrowser(PlatformInAppBrowserCreationParams params) + : super.implementation( + params is MacOSInAppBrowserCreationParams + ? params + : MacOSInAppBrowserCreationParams + .fromPlatformInAppBrowserCreationParams(params), + ) { + _contextMenu = params.contextMenu; + } + + static final MacOSInAppBrowser _staticValue = + MacOSInAppBrowser(MacOSInAppBrowserCreationParams()); + + /// Provide static access. + factory MacOSInAppBrowser.static() { + return _staticValue; + } + + MacOSInAppBrowserCreationParams get _macosParams => + params as MacOSInAppBrowserCreationParams; + + static const MethodChannel _staticChannel = + const MethodChannel('com.pichillilorenzo/flutter_inappbrowser'); + + ContextMenu? _contextMenu; + + @override + ContextMenu? get contextMenu => _contextMenu; + + Map _menuItems = HashMap(); + bool _isOpened = false; + MacOSInAppWebViewController? _webViewController; + + @override + MacOSInAppWebViewController? get webViewController { + return _isOpened ? _webViewController : null; + } + + _init() { + channel = MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$id'); + handler = _handleMethod; + initMethodCallHandler(); + + _webViewController = MacOSInAppWebViewController.fromInAppBrowser( + MacOSInAppWebViewControllerCreationParams(id: id), + channel!, + this, + this.initialUserScripts); + _macosParams.findInteractionController?.init(id); + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + id: id, + debugLoggingSettings: PlatformInAppBrowser.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onBrowserCreated": + _debugLog(call.method, call.arguments); + eventHandler?.onBrowserCreated(); + break; + case "onMenuItemClicked": + _debugLog(call.method, call.arguments); + int id = call.arguments["id"].toInt(); + if (this._menuItems[id] != null) { + if (this._menuItems[id]?.onClick != null) { + this._menuItems[id]?.onClick!(); + } + } + break; + case "onExit": + _debugLog(call.method, call.arguments); + _isOpened = false; + final onExit = eventHandler?.onExit; + dispose(); + onExit?.call(); + break; + default: + return _webViewController?.handleMethod(call); + } + } + + Map _prepareOpenRequest( + {@Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) { + assert(!_isOpened, 'The browser is already opened.'); + _isOpened = true; + _init(); + + var initialSettings = settings?.toMap() ?? + options?.toMap() ?? + InAppBrowserClassSettings().toMap(); + + Map pullToRefreshSettings = + pullToRefreshController?.settings.toMap() ?? + pullToRefreshController?.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + List> menuItemList = []; + _menuItems.forEach((key, value) { + menuItemList.add(value.toMap()); + }); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent('settings', () => initialSettings); + args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); + args.putIfAbsent('windowId', () => windowId); + args.putIfAbsent('initialUserScripts', + () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []); + args.putIfAbsent('pullToRefreshSettings', () => pullToRefreshSettings); + args.putIfAbsent('menuItems', () => menuItemList); + return args; + } + + @override + Future openUrlRequest( + {required URLRequest urlRequest, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); + + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openFile( + {required String assetFilePath, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + assert(assetFilePath.isNotEmpty); + + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('assetFilePath', () => assetFilePath); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openData( + {required String data, + String mimeType = "text/html", + String encoding = "utf8", + WebUri? baseUrl, + @Deprecated("Use historyUrl instead") Uri? androidHistoryUrl, + WebUri? historyUrl, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('data', () => data); + args.putIfAbsent('mimeType', () => mimeType); + args.putIfAbsent('encoding', () => encoding); + args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); + args.putIfAbsent('historyUrl', + () => (historyUrl ?? androidHistoryUrl)?.toString() ?? "about:blank"); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openWithSystemBrowser({required WebUri url}) async { + assert(url.toString().isNotEmpty); + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + return await _staticChannel.invokeMethod('openWithSystemBrowser', args); + } + + @override + void addMenuItem(InAppBrowserMenuItem menuItem) { + _menuItems[menuItem.id] = menuItem; + } + + @override + void addMenuItems(List menuItems) { + menuItems.forEach((menuItem) { + _menuItems[menuItem.id] = menuItem; + }); + } + + @override + bool removeMenuItem(InAppBrowserMenuItem menuItem) { + return _menuItems.remove(menuItem.id) != null; + } + + @override + void removeMenuItems(List menuItems) { + for (final menuItem in menuItems) { + removeMenuItem(menuItem); + } + } + + @override + void removeAllMenuItem() { + _menuItems.clear(); + } + + @override + bool hasMenuItem(InAppBrowserMenuItem menuItem) { + return _menuItems.containsKey(menuItem.id); + } + + @override + Future show() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('show', args); + } + + @override + Future hide() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('hide', args); + } + + @override + Future close() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('close', args); + } + + @override + Future isHidden() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + return await channel?.invokeMethod('isHidden', args) ?? false; + } + + @override + @Deprecated('Use setSettings instead') + Future setOptions({required InAppBrowserClassOptions options}) async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + args.putIfAbsent('settings', () => options.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + @Deprecated('Use getSettings instead') + Future getOptions() async { + assert(_isOpened, 'The browser is not opened.'); + Map args = {}; + + Map? options = + await channel?.invokeMethod('getSettings', args); + if (options != null) { + options = options.cast(); + return InAppBrowserClassOptions.fromMap(options as Map); + } + + return null; + } + + @override + Future setSettings( + {required InAppBrowserClassSettings settings}) async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + args.putIfAbsent('settings', () => settings.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + Future getSettings() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + + Map? settings = + await channel?.invokeMethod('getSettings', args); + if (settings != null) { + settings = settings.cast(); + return InAppBrowserClassSettings.fromMap( + settings as Map); + } + + return null; + } + + @override + bool isOpened() { + return this._isOpened; + } + + @override + @mustCallSuper + void dispose() { + disposeChannel(); + _webViewController?.dispose(); + _webViewController = null; + pullToRefreshController?.dispose(); + findInteractionController?.dispose(); + eventHandler = null; + } +} + +extension InternalInAppBrowser on MacOSInAppBrowser { + void setContextMenu(ContextMenu? contextMenu) { + _contextMenu = contextMenu; + } +} diff --git a/flutter_inappwebview_macos/lib/src/in_app_browser/main.dart b/flutter_inappwebview_macos/lib/src/in_app_browser/main.dart new file mode 100644 index 00000000..e11eb8b1 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/in_app_browser/main.dart @@ -0,0 +1 @@ +export 'in_app_browser.dart' hide InternalInAppBrowser; diff --git a/flutter_inappwebview_macos/lib/src/in_app_webview/_static_channel.dart b/flutter_inappwebview_macos/lib/src/in_app_webview/_static_channel.dart new file mode 100644 index 00000000..beb7de70 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/in_app_webview/_static_channel.dart @@ -0,0 +1,4 @@ +import 'package:flutter/services.dart'; + +const IN_APP_WEBVIEW_STATIC_CHANNEL = + MethodChannel('com.pichillilorenzo/flutter_inappwebview_manager'); diff --git a/flutter_inappwebview_macos/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview_macos/lib/src/in_app_webview/headless_in_app_webview.dart new file mode 100644 index 00000000..cb7dfc3e --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/in_app_webview/headless_in_app_webview.dart @@ -0,0 +1,434 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import '../find_interaction/find_interaction_controller.dart'; +import 'in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [MacOSHeadlessInAppWebView]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformHeadlessInAppWebViewCreationParams] for +/// more information. +@immutable +class MacOSHeadlessInAppWebViewCreationParams + extends PlatformHeadlessInAppWebViewCreationParams { + /// Creates a new [MacOSHeadlessInAppWebViewCreationParams] instance. + MacOSHeadlessInAppWebViewCreationParams( + {super.controllerFromPlatform, + super.initialSize, + super.windowId, + super.onWebViewCreated, + super.onLoadStart, + super.onLoadStop, + @Deprecated('Use onReceivedError instead') super.onLoadError, + super.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError, + super.onReceivedHttpError, + super.onProgressChanged, + super.onConsoleMessage, + super.shouldOverrideUrlLoading, + super.onLoadResource, + super.onScrollChanged, + @Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart, + super.onDownloadStartRequest, + @Deprecated('Use onLoadResourceWithCustomScheme instead') + super.onLoadResourceCustomScheme, + super.onLoadResourceWithCustomScheme, + super.onCreateWindow, + super.onCloseWindow, + super.onJsAlert, + super.onJsConfirm, + super.onJsPrompt, + super.onReceivedHttpAuthRequest, + super.onReceivedServerTrustAuthRequest, + super.onReceivedClientCertRequest, + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + super.onFindResultReceived, + super.shouldInterceptAjaxRequest, + super.onAjaxReadyStateChange, + super.onAjaxProgress, + super.shouldInterceptFetchRequest, + super.onUpdateVisitedHistory, + @Deprecated("Use onPrintRequest instead") super.onPrint, + super.onPrintRequest, + super.onLongPressHitTestResult, + super.onEnterFullscreen, + super.onExitFullscreen, + super.onPageCommitVisible, + super.onTitleChanged, + super.onWindowFocus, + super.onWindowBlur, + super.onOverScrolled, + super.onZoomScaleChanged, + @Deprecated('Use onSafeBrowsingHit instead') + super.androidOnSafeBrowsingHit, + super.onSafeBrowsingHit, + @Deprecated('Use onPermissionRequest instead') + super.androidOnPermissionRequest, + super.onPermissionRequest, + @Deprecated('Use onGeolocationPermissionsShowPrompt instead') + super.androidOnGeolocationPermissionsShowPrompt, + super.onGeolocationPermissionsShowPrompt, + @Deprecated('Use onGeolocationPermissionsHidePrompt instead') + super.androidOnGeolocationPermissionsHidePrompt, + super.onGeolocationPermissionsHidePrompt, + @Deprecated('Use shouldInterceptRequest instead') + super.androidShouldInterceptRequest, + super.shouldInterceptRequest, + @Deprecated('Use onRenderProcessGone instead') + super.androidOnRenderProcessGone, + super.onRenderProcessGone, + @Deprecated('Use onRenderProcessResponsive instead') + super.androidOnRenderProcessResponsive, + super.onRenderProcessResponsive, + @Deprecated('Use onRenderProcessUnresponsive instead') + super.androidOnRenderProcessUnresponsive, + super.onRenderProcessUnresponsive, + @Deprecated('Use onFormResubmission instead') + super.androidOnFormResubmission, + super.onFormResubmission, + @Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged, + @Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon, + super.onReceivedIcon, + @Deprecated('Use onReceivedTouchIconUrl instead') + super.androidOnReceivedTouchIconUrl, + super.onReceivedTouchIconUrl, + @Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload, + super.onJsBeforeUnload, + @Deprecated('Use onReceivedLoginRequest instead') + super.androidOnReceivedLoginRequest, + super.onReceivedLoginRequest, + super.onPermissionRequestCanceled, + super.onRequestFocus, + @Deprecated('Use onWebContentProcessDidTerminate instead') + super.iosOnWebContentProcessDidTerminate, + super.onWebContentProcessDidTerminate, + @Deprecated( + 'Use onDidReceiveServerRedirectForProvisionalNavigation instead') + super.iosOnDidReceiveServerRedirectForProvisionalNavigation, + super.onDidReceiveServerRedirectForProvisionalNavigation, + @Deprecated('Use onNavigationResponse instead') + super.iosOnNavigationResponse, + super.onNavigationResponse, + @Deprecated('Use shouldAllowDeprecatedTLS instead') + super.iosShouldAllowDeprecatedTLS, + super.shouldAllowDeprecatedTLS, + super.onCameraCaptureStateChanged, + super.onMicrophoneCaptureStateChanged, + super.onContentSizeChanged, + super.initialUrlRequest, + super.initialFile, + super.initialData, + @Deprecated('Use initialSettings instead') super.initialOptions, + super.initialSettings, + super.contextMenu, + super.initialUserScripts, + super.pullToRefreshController, + this.findInteractionController}); + + /// Creates a [MacOSHeadlessInAppWebViewCreationParams] instance based on [PlatformHeadlessInAppWebViewCreationParams]. + MacOSHeadlessInAppWebViewCreationParams.fromPlatformHeadlessInAppWebViewCreationParams( + PlatformHeadlessInAppWebViewCreationParams params) + : this( + controllerFromPlatform: params.controllerFromPlatform, + initialSize: params.initialSize, + windowId: params.windowId, + onWebViewCreated: params.onWebViewCreated, + onLoadStart: params.onLoadStart, + onLoadStop: params.onLoadStop, + onLoadError: params.onLoadError, + onReceivedError: params.onReceivedError, + onLoadHttpError: params.onLoadHttpError, + onReceivedHttpError: params.onReceivedHttpError, + onProgressChanged: params.onProgressChanged, + onConsoleMessage: params.onConsoleMessage, + shouldOverrideUrlLoading: params.shouldOverrideUrlLoading, + onLoadResource: params.onLoadResource, + onScrollChanged: params.onScrollChanged, + onDownloadStart: params.onDownloadStart, + onDownloadStartRequest: params.onDownloadStartRequest, + onLoadResourceCustomScheme: params.onLoadResourceCustomScheme, + onLoadResourceWithCustomScheme: + params.onLoadResourceWithCustomScheme, + onCreateWindow: params.onCreateWindow, + onCloseWindow: params.onCloseWindow, + onJsAlert: params.onJsAlert, + onJsConfirm: params.onJsConfirm, + onJsPrompt: params.onJsPrompt, + onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest, + onReceivedServerTrustAuthRequest: + params.onReceivedServerTrustAuthRequest, + onReceivedClientCertRequest: params.onReceivedClientCertRequest, + onFindResultReceived: params.onFindResultReceived, + shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest, + onAjaxReadyStateChange: params.onAjaxReadyStateChange, + onAjaxProgress: params.onAjaxProgress, + shouldInterceptFetchRequest: params.shouldInterceptFetchRequest, + onUpdateVisitedHistory: params.onUpdateVisitedHistory, + onPrint: params.onPrint, + onPrintRequest: params.onPrintRequest, + onLongPressHitTestResult: params.onLongPressHitTestResult, + onEnterFullscreen: params.onEnterFullscreen, + onExitFullscreen: params.onExitFullscreen, + onPageCommitVisible: params.onPageCommitVisible, + onTitleChanged: params.onTitleChanged, + onWindowFocus: params.onWindowFocus, + onWindowBlur: params.onWindowBlur, + onOverScrolled: params.onOverScrolled, + onZoomScaleChanged: params.onZoomScaleChanged, + androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit, + onSafeBrowsingHit: params.onSafeBrowsingHit, + androidOnPermissionRequest: params.androidOnPermissionRequest, + onPermissionRequest: params.onPermissionRequest, + androidOnGeolocationPermissionsShowPrompt: + params.androidOnGeolocationPermissionsShowPrompt, + onGeolocationPermissionsShowPrompt: + params.onGeolocationPermissionsShowPrompt, + androidOnGeolocationPermissionsHidePrompt: + params.androidOnGeolocationPermissionsHidePrompt, + onGeolocationPermissionsHidePrompt: + params.onGeolocationPermissionsHidePrompt, + androidShouldInterceptRequest: params.androidShouldInterceptRequest, + shouldInterceptRequest: params.shouldInterceptRequest, + androidOnRenderProcessGone: params.androidOnRenderProcessGone, + onRenderProcessGone: params.onRenderProcessGone, + androidOnRenderProcessResponsive: + params.androidOnRenderProcessResponsive, + onRenderProcessResponsive: params.onRenderProcessResponsive, + androidOnRenderProcessUnresponsive: + params.androidOnRenderProcessUnresponsive, + onRenderProcessUnresponsive: params.onRenderProcessUnresponsive, + androidOnFormResubmission: params.androidOnFormResubmission, + onFormResubmission: params.onFormResubmission, + androidOnScaleChanged: params.androidOnScaleChanged, + androidOnReceivedIcon: params.androidOnReceivedIcon, + onReceivedIcon: params.onReceivedIcon, + androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl, + onReceivedTouchIconUrl: params.onReceivedTouchIconUrl, + androidOnJsBeforeUnload: params.androidOnJsBeforeUnload, + onJsBeforeUnload: params.onJsBeforeUnload, + androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest, + onReceivedLoginRequest: params.onReceivedLoginRequest, + onPermissionRequestCanceled: params.onPermissionRequestCanceled, + onRequestFocus: params.onRequestFocus, + iosOnWebContentProcessDidTerminate: + params.iosOnWebContentProcessDidTerminate, + onWebContentProcessDidTerminate: + params.onWebContentProcessDidTerminate, + iosOnDidReceiveServerRedirectForProvisionalNavigation: + params.iosOnDidReceiveServerRedirectForProvisionalNavigation, + onDidReceiveServerRedirectForProvisionalNavigation: + params.onDidReceiveServerRedirectForProvisionalNavigation, + iosOnNavigationResponse: params.iosOnNavigationResponse, + onNavigationResponse: params.onNavigationResponse, + iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS, + shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS, + onCameraCaptureStateChanged: params.onCameraCaptureStateChanged, + onMicrophoneCaptureStateChanged: + params.onMicrophoneCaptureStateChanged, + onContentSizeChanged: params.onContentSizeChanged, + initialUrlRequest: params.initialUrlRequest, + initialFile: params.initialFile, + initialData: params.initialData, + initialOptions: params.initialOptions, + initialSettings: params.initialSettings, + contextMenu: params.contextMenu, + initialUserScripts: params.initialUserScripts, + pullToRefreshController: params.pullToRefreshController, + findInteractionController: params.findInteractionController + as MacOSFindInteractionController?); + + @override + final MacOSFindInteractionController? findInteractionController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView} +class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView + with ChannelController { + @override + late final String id; + + bool _started = false; + bool _running = false; + + static const MethodChannel _sharedChannel = + const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview'); + + MacOSInAppWebViewController? _webViewController; + + /// Constructs a [MacOSHeadlessInAppWebView]. + MacOSHeadlessInAppWebView(PlatformHeadlessInAppWebViewCreationParams params) + : super.implementation( + params is MacOSHeadlessInAppWebViewCreationParams + ? params + : MacOSHeadlessInAppWebViewCreationParams + .fromPlatformHeadlessInAppWebViewCreationParams(params), + ) { + id = IdGenerator.generate(); + } + + @override + MacOSInAppWebViewController? get webViewController => _webViewController; + + dynamic _controllerFromPlatform; + + MacOSHeadlessInAppWebViewCreationParams get _macosParams => + params as MacOSHeadlessInAppWebViewCreationParams; + + _init() { + _webViewController = MacOSInAppWebViewController( + MacOSInAppWebViewControllerCreationParams( + id: id, webviewParams: params), + ); + _controllerFromPlatform = + params.controllerFromPlatform?.call(_webViewController!) ?? + _webViewController!; + _macosParams.findInteractionController?.init(id); + channel = + MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onWebViewCreated": + if (params.onWebViewCreated != null && _webViewController != null) { + params.onWebViewCreated!(_controllerFromPlatform); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + Future run() async { + if (_started) { + return; + } + _started = true; + _init(); + + final initialSettings = params.initialSettings ?? InAppWebViewSettings(); + _inferInitialSettings(initialSettings); + + Map settingsMap = + (params.initialSettings != null ? initialSettings.toMap() : null) ?? + params.initialOptions?.toMap() ?? + initialSettings.toMap(); + + Map pullToRefreshSettings = + _macosParams.pullToRefreshController?.params.settings.toMap() ?? + _macosParams.pullToRefreshController?.params.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent( + 'params', + () => { + 'initialUrlRequest': params.initialUrlRequest?.toMap(), + 'initialFile': params.initialFile, + 'initialData': params.initialData?.toMap(), + 'initialSettings': settingsMap, + 'contextMenu': params.contextMenu?.toMap() ?? {}, + 'windowId': params.windowId, + 'initialUserScripts': + params.initialUserScripts?.map((e) => e.toMap()).toList() ?? + [], + 'pullToRefreshSettings': pullToRefreshSettings, + 'initialSize': params.initialSize.toMap() + }); + await _sharedChannel.invokeMethod('run', args); + _running = true; + } + + void _inferInitialSettings(InAppWebViewSettings settings) { + if (params.shouldOverrideUrlLoading != null && + settings.useShouldOverrideUrlLoading == null) { + settings.useShouldOverrideUrlLoading = true; + } + if (params.onLoadResource != null && settings.useOnLoadResource == null) { + settings.useOnLoadResource = true; + } + if (params.onDownloadStartRequest != null && + settings.useOnDownloadStart == null) { + settings.useOnDownloadStart = true; + } + if (params.shouldInterceptAjaxRequest != null && + settings.useShouldInterceptAjaxRequest == null) { + settings.useShouldInterceptAjaxRequest = true; + } + if (params.shouldInterceptFetchRequest != null && + settings.useShouldInterceptFetchRequest == null) { + settings.useShouldInterceptFetchRequest = true; + } + if (params.shouldInterceptRequest != null && + settings.useShouldInterceptRequest == null) { + settings.useShouldInterceptRequest = true; + } + if (params.onRenderProcessGone != null && + settings.useOnRenderProcessGone == null) { + settings.useOnRenderProcessGone = true; + } + if (params.onNavigationResponse != null && + settings.useOnNavigationResponse == null) { + settings.useOnNavigationResponse = true; + } + } + + @override + bool isRunning() { + return _running; + } + + @override + Future setSize(Size size) async { + if (!_running) { + return; + } + + Map args = {}; + args.putIfAbsent('size', () => size.toMap()); + await channel?.invokeMethod('setSize', args); + } + + @override + Future getSize() async { + if (!_running) { + return null; + } + + Map args = {}; + Map sizeMap = + (await channel?.invokeMethod('getSize', args))?.cast(); + return MapSize.fromMap(sizeMap); + } + + @override + Future dispose() async { + if (!_running) { + return; + } + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + _started = false; + _running = false; + _webViewController?.dispose(); + _webViewController = null; + _controllerFromPlatform = null; + _macosParams.pullToRefreshController?.dispose(); + _macosParams.findInteractionController?.dispose(); + } +} + +extension InternalHeadlessInAppWebView on MacOSHeadlessInAppWebView { + Future internalDispose() async { + _started = false; + _running = false; + } +} diff --git a/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview.dart new file mode 100755 index 00000000..76627d16 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview.dart @@ -0,0 +1,410 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'headless_in_app_webview.dart'; + +import '../find_interaction/find_interaction_controller.dart'; +import 'in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [PlatformInAppWebViewWidget]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +class MacOSInAppWebViewWidgetCreationParams + extends PlatformInAppWebViewWidgetCreationParams { + MacOSInAppWebViewWidgetCreationParams( + {super.controllerFromPlatform, + super.key, + super.layoutDirection, + super.gestureRecognizers, + super.headlessWebView, + super.keepAlive, + super.preventGestureDelay, + super.windowId, + super.onWebViewCreated, + super.onLoadStart, + super.onLoadStop, + @Deprecated('Use onReceivedError instead') super.onLoadError, + super.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError, + super.onReceivedHttpError, + super.onProgressChanged, + super.onConsoleMessage, + super.shouldOverrideUrlLoading, + super.onLoadResource, + super.onScrollChanged, + @Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart, + super.onDownloadStartRequest, + @Deprecated('Use onLoadResourceWithCustomScheme instead') + super.onLoadResourceCustomScheme, + super.onLoadResourceWithCustomScheme, + super.onCreateWindow, + super.onCloseWindow, + super.onJsAlert, + super.onJsConfirm, + super.onJsPrompt, + super.onReceivedHttpAuthRequest, + super.onReceivedServerTrustAuthRequest, + super.onReceivedClientCertRequest, + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + super.onFindResultReceived, + super.shouldInterceptAjaxRequest, + super.onAjaxReadyStateChange, + super.onAjaxProgress, + super.shouldInterceptFetchRequest, + super.onUpdateVisitedHistory, + @Deprecated("Use onPrintRequest instead") super.onPrint, + super.onPrintRequest, + super.onLongPressHitTestResult, + super.onEnterFullscreen, + super.onExitFullscreen, + super.onPageCommitVisible, + super.onTitleChanged, + super.onWindowFocus, + super.onWindowBlur, + super.onOverScrolled, + super.onZoomScaleChanged, + @Deprecated('Use onSafeBrowsingHit instead') + super.androidOnSafeBrowsingHit, + super.onSafeBrowsingHit, + @Deprecated('Use onPermissionRequest instead') + super.androidOnPermissionRequest, + super.onPermissionRequest, + @Deprecated('Use onGeolocationPermissionsShowPrompt instead') + super.androidOnGeolocationPermissionsShowPrompt, + super.onGeolocationPermissionsShowPrompt, + @Deprecated('Use onGeolocationPermissionsHidePrompt instead') + super.androidOnGeolocationPermissionsHidePrompt, + super.onGeolocationPermissionsHidePrompt, + @Deprecated('Use shouldInterceptRequest instead') + super.androidShouldInterceptRequest, + super.shouldInterceptRequest, + @Deprecated('Use onRenderProcessGone instead') + super.androidOnRenderProcessGone, + super.onRenderProcessGone, + @Deprecated('Use onRenderProcessResponsive instead') + super.androidOnRenderProcessResponsive, + super.onRenderProcessResponsive, + @Deprecated('Use onRenderProcessUnresponsive instead') + super.androidOnRenderProcessUnresponsive, + super.onRenderProcessUnresponsive, + @Deprecated('Use onFormResubmission instead') + super.androidOnFormResubmission, + super.onFormResubmission, + @Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged, + @Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon, + super.onReceivedIcon, + @Deprecated('Use onReceivedTouchIconUrl instead') + super.androidOnReceivedTouchIconUrl, + super.onReceivedTouchIconUrl, + @Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload, + super.onJsBeforeUnload, + @Deprecated('Use onReceivedLoginRequest instead') + super.androidOnReceivedLoginRequest, + super.onReceivedLoginRequest, + super.onPermissionRequestCanceled, + super.onRequestFocus, + @Deprecated('Use onWebContentProcessDidTerminate instead') + super.iosOnWebContentProcessDidTerminate, + super.onWebContentProcessDidTerminate, + @Deprecated( + 'Use onDidReceiveServerRedirectForProvisionalNavigation instead') + super.iosOnDidReceiveServerRedirectForProvisionalNavigation, + super.onDidReceiveServerRedirectForProvisionalNavigation, + @Deprecated('Use onNavigationResponse instead') + super.iosOnNavigationResponse, + super.onNavigationResponse, + @Deprecated('Use shouldAllowDeprecatedTLS instead') + super.iosShouldAllowDeprecatedTLS, + super.shouldAllowDeprecatedTLS, + super.onCameraCaptureStateChanged, + super.onMicrophoneCaptureStateChanged, + super.onContentSizeChanged, + super.initialUrlRequest, + super.initialFile, + super.initialData, + @Deprecated('Use initialSettings instead') super.initialOptions, + super.initialSettings, + super.contextMenu, + super.initialUserScripts, + super.pullToRefreshController, + this.findInteractionController}); + + /// Constructs a [MacOSInAppWebViewWidgetCreationParams] using a + /// [PlatformInAppWebViewWidgetCreationParams]. + MacOSInAppWebViewWidgetCreationParams.fromPlatformInAppWebViewWidgetCreationParams( + PlatformInAppWebViewWidgetCreationParams params) + : this( + controllerFromPlatform: params.controllerFromPlatform, + key: params.key, + layoutDirection: params.layoutDirection, + gestureRecognizers: params.gestureRecognizers, + headlessWebView: params.headlessWebView, + keepAlive: params.keepAlive, + preventGestureDelay: params.preventGestureDelay, + windowId: params.windowId, + onWebViewCreated: params.onWebViewCreated, + onLoadStart: params.onLoadStart, + onLoadStop: params.onLoadStop, + onLoadError: params.onLoadError, + onReceivedError: params.onReceivedError, + onLoadHttpError: params.onLoadHttpError, + onReceivedHttpError: params.onReceivedHttpError, + onProgressChanged: params.onProgressChanged, + onConsoleMessage: params.onConsoleMessage, + shouldOverrideUrlLoading: params.shouldOverrideUrlLoading, + onLoadResource: params.onLoadResource, + onScrollChanged: params.onScrollChanged, + onDownloadStart: params.onDownloadStart, + onDownloadStartRequest: params.onDownloadStartRequest, + onLoadResourceCustomScheme: params.onLoadResourceCustomScheme, + onLoadResourceWithCustomScheme: + params.onLoadResourceWithCustomScheme, + onCreateWindow: params.onCreateWindow, + onCloseWindow: params.onCloseWindow, + onJsAlert: params.onJsAlert, + onJsConfirm: params.onJsConfirm, + onJsPrompt: params.onJsPrompt, + onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest, + onReceivedServerTrustAuthRequest: + params.onReceivedServerTrustAuthRequest, + onReceivedClientCertRequest: params.onReceivedClientCertRequest, + onFindResultReceived: params.onFindResultReceived, + shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest, + onAjaxReadyStateChange: params.onAjaxReadyStateChange, + onAjaxProgress: params.onAjaxProgress, + shouldInterceptFetchRequest: params.shouldInterceptFetchRequest, + onUpdateVisitedHistory: params.onUpdateVisitedHistory, + onPrint: params.onPrint, + onPrintRequest: params.onPrintRequest, + onLongPressHitTestResult: params.onLongPressHitTestResult, + onEnterFullscreen: params.onEnterFullscreen, + onExitFullscreen: params.onExitFullscreen, + onPageCommitVisible: params.onPageCommitVisible, + onTitleChanged: params.onTitleChanged, + onWindowFocus: params.onWindowFocus, + onWindowBlur: params.onWindowBlur, + onOverScrolled: params.onOverScrolled, + onZoomScaleChanged: params.onZoomScaleChanged, + androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit, + onSafeBrowsingHit: params.onSafeBrowsingHit, + androidOnPermissionRequest: params.androidOnPermissionRequest, + onPermissionRequest: params.onPermissionRequest, + androidOnGeolocationPermissionsShowPrompt: + params.androidOnGeolocationPermissionsShowPrompt, + onGeolocationPermissionsShowPrompt: + params.onGeolocationPermissionsShowPrompt, + androidOnGeolocationPermissionsHidePrompt: + params.androidOnGeolocationPermissionsHidePrompt, + onGeolocationPermissionsHidePrompt: + params.onGeolocationPermissionsHidePrompt, + androidShouldInterceptRequest: params.androidShouldInterceptRequest, + shouldInterceptRequest: params.shouldInterceptRequest, + androidOnRenderProcessGone: params.androidOnRenderProcessGone, + onRenderProcessGone: params.onRenderProcessGone, + androidOnRenderProcessResponsive: + params.androidOnRenderProcessResponsive, + onRenderProcessResponsive: params.onRenderProcessResponsive, + androidOnRenderProcessUnresponsive: + params.androidOnRenderProcessUnresponsive, + onRenderProcessUnresponsive: params.onRenderProcessUnresponsive, + androidOnFormResubmission: params.androidOnFormResubmission, + onFormResubmission: params.onFormResubmission, + androidOnScaleChanged: params.androidOnScaleChanged, + androidOnReceivedIcon: params.androidOnReceivedIcon, + onReceivedIcon: params.onReceivedIcon, + androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl, + onReceivedTouchIconUrl: params.onReceivedTouchIconUrl, + androidOnJsBeforeUnload: params.androidOnJsBeforeUnload, + onJsBeforeUnload: params.onJsBeforeUnload, + androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest, + onReceivedLoginRequest: params.onReceivedLoginRequest, + onPermissionRequestCanceled: params.onPermissionRequestCanceled, + onRequestFocus: params.onRequestFocus, + iosOnWebContentProcessDidTerminate: + params.iosOnWebContentProcessDidTerminate, + onWebContentProcessDidTerminate: + params.onWebContentProcessDidTerminate, + iosOnDidReceiveServerRedirectForProvisionalNavigation: + params.iosOnDidReceiveServerRedirectForProvisionalNavigation, + onDidReceiveServerRedirectForProvisionalNavigation: + params.onDidReceiveServerRedirectForProvisionalNavigation, + iosOnNavigationResponse: params.iosOnNavigationResponse, + onNavigationResponse: params.onNavigationResponse, + iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS, + shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS, + onCameraCaptureStateChanged: params.onCameraCaptureStateChanged, + onMicrophoneCaptureStateChanged: + params.onMicrophoneCaptureStateChanged, + onContentSizeChanged: params.onContentSizeChanged, + initialUrlRequest: params.initialUrlRequest, + initialFile: params.initialFile, + initialData: params.initialData, + initialOptions: params.initialOptions, + initialSettings: params.initialSettings, + contextMenu: params.contextMenu, + initialUserScripts: params.initialUserScripts, + pullToRefreshController: params.pullToRefreshController, + findInteractionController: params.findInteractionController + as MacOSFindInteractionController?); + + @override + final MacOSFindInteractionController? findInteractionController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} +class MacOSInAppWebViewWidget extends PlatformInAppWebViewWidget { + /// Constructs a [MacOSInAppWebViewWidget]. + /// + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} + MacOSInAppWebViewWidget(PlatformInAppWebViewWidgetCreationParams params) + : super.implementation( + params is MacOSInAppWebViewWidgetCreationParams + ? params + : MacOSInAppWebViewWidgetCreationParams + .fromPlatformInAppWebViewWidgetCreationParams(params), + ); + + MacOSInAppWebViewWidgetCreationParams get _macosParams => + params as MacOSInAppWebViewWidgetCreationParams; + + MacOSInAppWebViewController? _controller; + + MacOSHeadlessInAppWebView? get _macosHeadlessInAppWebView => + _macosParams.headlessWebView as MacOSHeadlessInAppWebView?; + + @override + Widget build(BuildContext context) { + final initialSettings = + _macosParams.initialSettings ?? InAppWebViewSettings(); + _inferInitialSettings(initialSettings); + + Map settingsMap = (_macosParams.initialSettings != null + ? initialSettings.toMap() + : null) ?? + // ignore: deprecated_member_use_from_same_package + _macosParams.initialOptions?.toMap() ?? + initialSettings.toMap(); + + Map pullToRefreshSettings = + _macosParams.pullToRefreshController?.params.settings.toMap() ?? + // ignore: deprecated_member_use_from_same_package + _macosParams.pullToRefreshController?.params.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + if ((_macosParams.headlessWebView?.isRunning() ?? false) && + _macosParams.keepAlive != null) { + final headlessId = _macosParams.headlessWebView?.id; + if (headlessId != null) { + // force keep alive id to match headless webview id + _macosParams.keepAlive?.id = headlessId; + } + } + + return UiKitView( + viewType: 'com.pichillilorenzo/flutter_inappwebview', + onPlatformViewCreated: _onPlatformViewCreated, + gestureRecognizers: _macosParams.gestureRecognizers, + creationParams: { + 'initialUrlRequest': _macosParams.initialUrlRequest?.toMap(), + 'initialFile': _macosParams.initialFile, + 'initialData': _macosParams.initialData?.toMap(), + 'initialSettings': settingsMap, + 'contextMenu': _macosParams.contextMenu?.toMap() ?? {}, + 'windowId': _macosParams.windowId, + 'headlessWebViewId': _macosParams.headlessWebView?.isRunning() ?? false + ? _macosParams.headlessWebView?.id + : null, + 'initialUserScripts': + _macosParams.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], + 'pullToRefreshSettings': pullToRefreshSettings, + 'keepAliveId': _macosParams.keepAlive?.id, + 'preventGestureDelay': _macosParams.preventGestureDelay + }, + creationParamsCodec: const StandardMessageCodec(), + ); + } + + void _onPlatformViewCreated(int id) { + dynamic viewId = id; + if (_macosParams.headlessWebView?.isRunning() ?? false) { + viewId = _macosParams.headlessWebView?.id; + } + viewId = _macosParams.keepAlive?.id ?? viewId ?? id; + _macosHeadlessInAppWebView?.internalDispose(); + _controller = MacOSInAppWebViewController( + PlatformInAppWebViewControllerCreationParams( + id: viewId, webviewParams: params)); + _macosParams.findInteractionController?.init(viewId); + debugLog( + className: runtimeType.toString(), + id: viewId?.toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: "onWebViewCreated", + args: []); + if (_macosParams.onWebViewCreated != null) { + _macosParams.onWebViewCreated!( + params.controllerFromPlatform?.call(_controller!) ?? _controller!); + } + } + + void _inferInitialSettings(InAppWebViewSettings settings) { + if (_macosParams.shouldOverrideUrlLoading != null && + settings.useShouldOverrideUrlLoading == null) { + settings.useShouldOverrideUrlLoading = true; + } + if (_macosParams.onLoadResource != null && + settings.useOnLoadResource == null) { + settings.useOnLoadResource = true; + } + if (_macosParams.onDownloadStartRequest != null && + settings.useOnDownloadStart == null) { + settings.useOnDownloadStart = true; + } + if (_macosParams.shouldInterceptAjaxRequest != null && + settings.useShouldInterceptAjaxRequest == null) { + settings.useShouldInterceptAjaxRequest = true; + } + if (_macosParams.shouldInterceptFetchRequest != null && + settings.useShouldInterceptFetchRequest == null) { + settings.useShouldInterceptFetchRequest = true; + } + if (_macosParams.shouldInterceptRequest != null && + settings.useShouldInterceptRequest == null) { + settings.useShouldInterceptRequest = true; + } + if (_macosParams.onRenderProcessGone != null && + settings.useOnRenderProcessGone == null) { + settings.useOnRenderProcessGone = true; + } + if (_macosParams.onNavigationResponse != null && + settings.useOnNavigationResponse == null) { + settings.useOnNavigationResponse = true; + } + } + + @override + void dispose() { + dynamic viewId = _controller?.getViewId(); + debugLog( + className: runtimeType.toString(), + id: viewId?.toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: "dispose", + args: []); + final isKeepAlive = _macosParams.keepAlive != null; + _controller?.dispose(isKeepAlive: isKeepAlive); + _controller = null; + _macosParams.pullToRefreshController?.dispose(isKeepAlive: isKeepAlive); + _macosParams.findInteractionController?.dispose(isKeepAlive: isKeepAlive); + } + + @override + T controllerFromPlatform(PlatformInAppWebViewController controller) { + // unused + throw UnimplementedError(); + } +} diff --git a/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview_controller.dart new file mode 100644 index 00000000..41332671 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview_controller.dart @@ -0,0 +1,2681 @@ +import 'dart:io'; +import 'dart:collection'; +import 'dart:convert'; +import 'dart:core'; +import 'dart:developer' as developer; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../web_message/main.dart'; + +import '../in_app_browser/in_app_browser.dart'; +import '../web_storage/web_storage.dart'; + +import 'headless_in_app_webview.dart'; +import '_static_channel.dart'; + +import '../print_job/main.dart'; + +///List of forbidden names for JavaScript handlers. +// ignore: non_constant_identifier_names +final _JAVASCRIPT_HANDLER_FORBIDDEN_NAMES = UnmodifiableListView([ + "onLoadResource", + "shouldInterceptAjaxRequest", + "onAjaxReadyStateChange", + "onAjaxProgress", + "shouldInterceptFetchRequest", + "onPrintRequest", + "onWindowFocus", + "onWindowBlur", + "callAsyncJavaScript", + "evaluateJavaScriptWithContentWorld" +]); + +/// Object specifying creation parameters for creating a [MacOSInAppWebViewController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInAppWebViewControllerCreationParams] for +/// more information. +@immutable +class MacOSInAppWebViewControllerCreationParams + extends PlatformInAppWebViewControllerCreationParams { + /// Creates a new [MacOSInAppWebViewControllerCreationParams] instance. + const MacOSInAppWebViewControllerCreationParams( + {required super.id, super.webviewParams}); + + /// Creates a [MacOSInAppWebViewControllerCreationParams] instance based on [PlatformInAppWebViewControllerCreationParams]. + factory MacOSInAppWebViewControllerCreationParams.fromPlatformInAppWebViewControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformInAppWebViewControllerCreationParams params) { + return MacOSInAppWebViewControllerCreationParams( + id: params.id, webviewParams: params.webviewParams); + } +} + +///Controls a WebView, such as an [InAppWebView] widget instance, a [MacOSHeadlessInAppWebView] instance or [MacOSInAppBrowser] WebView instance. +/// +///If you are using the [InAppWebView] widget, an [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated] +///callback. Instead, if you are using an [MacOSInAppBrowser] instance, you can get it through the [MacOSInAppBrowser.webViewController] attribute. +class MacOSInAppWebViewController extends PlatformInAppWebViewController + with ChannelController { + static final MethodChannel _staticChannel = IN_APP_WEBVIEW_STATIC_CHANNEL; + + // List of properties to be saved and restored for keep alive feature + Map _javaScriptHandlersMap = + HashMap(); + Map> _userScripts = { + UserScriptInjectionTime.AT_DOCUMENT_START: [], + UserScriptInjectionTime.AT_DOCUMENT_END: [] + }; + Set _webMessageListenerObjNames = Set(); + Map _injectedScriptsFromURL = {}; + Set _webMessageChannels = Set(); + Set _webMessageListeners = Set(); + + // static map that contains the properties to be saved and restored for keep alive feature + static final Map + _keepAliveMap = {}; + + MacOSInAppBrowser? _inAppBrowser; + + PlatformInAppBrowserEvents? get _inAppBrowserEventHandler => + _inAppBrowser?.eventHandler; + + dynamic _controllerFromPlatform; + + @override + late MacOSWebStorage webStorage; + + MacOSInAppWebViewController( + PlatformInAppWebViewControllerCreationParams params) + : super.implementation(params + is MacOSInAppWebViewControllerCreationParams + ? params + : MacOSInAppWebViewControllerCreationParams + .fromPlatformInAppWebViewControllerCreationParams(params)) { + channel = MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id'); + handler = handleMethod; + initMethodCallHandler(); + + final initialUserScripts = webviewParams?.initialUserScripts; + if (initialUserScripts != null) { + for (final userScript in initialUserScripts) { + if (userScript.injectionTime == + UserScriptInjectionTime.AT_DOCUMENT_START) { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] + ?.add(userScript); + } else { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] + ?.add(userScript); + } + } + } + + this._init(params); + } + + static final MacOSInAppWebViewController _staticValue = + MacOSInAppWebViewController( + MacOSInAppWebViewControllerCreationParams(id: null)); + + factory MacOSInAppWebViewController.static() { + return _staticValue; + } + + MacOSInAppWebViewController.fromInAppBrowser( + PlatformInAppWebViewControllerCreationParams params, + MethodChannel channel, + MacOSInAppBrowser inAppBrowser, + UnmodifiableListView? initialUserScripts) + : super.implementation( + params is MacOSInAppWebViewControllerCreationParams + ? params + : MacOSInAppWebViewControllerCreationParams + .fromPlatformInAppWebViewControllerCreationParams(params)) { + this.channel = channel; + this._inAppBrowser = inAppBrowser; + + if (initialUserScripts != null) { + for (final userScript in initialUserScripts) { + if (userScript.injectionTime == + UserScriptInjectionTime.AT_DOCUMENT_START) { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] + ?.add(userScript); + } else { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] + ?.add(userScript); + } + } + } + this._init(params); + } + + void _init(PlatformInAppWebViewControllerCreationParams params) { + _controllerFromPlatform = + params.webviewParams?.controllerFromPlatform?.call(this) ?? this; + + webStorage = MacOSWebStorage(MacOSWebStorageCreationParams( + localStorage: MacOSLocalStorage.defaultStorage(controller: this), + sessionStorage: + MacOSSessionStorage.defaultStorage(controller: this))); + + if (params.webviewParams is PlatformInAppWebViewWidgetCreationParams) { + final keepAlive = + (params.webviewParams as PlatformInAppWebViewWidgetCreationParams) + .keepAlive; + if (keepAlive != null) { + InAppWebViewControllerKeepAliveProps? props = _keepAliveMap[keepAlive]; + if (props == null) { + // save controller properties to restore it later + _keepAliveMap[keepAlive] = InAppWebViewControllerKeepAliveProps( + injectedScriptsFromURL: _injectedScriptsFromURL, + javaScriptHandlersMap: _javaScriptHandlersMap, + userScripts: _userScripts, + webMessageListenerObjNames: _webMessageListenerObjNames, + webMessageChannels: _webMessageChannels, + webMessageListeners: _webMessageListeners); + } else { + // restore controller properties + _injectedScriptsFromURL = props.injectedScriptsFromURL; + _javaScriptHandlersMap = props.javaScriptHandlersMap; + _userScripts = props.userScripts; + _webMessageListenerObjNames = props.webMessageListenerObjNames; + _webMessageChannels = + props.webMessageChannels as Set; + _webMessageListeners = + props.webMessageListeners as Set; + } + } + } + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + name: _inAppBrowser == null + ? "WebView" + : _inAppBrowser.runtimeType.toString(), + id: (getViewId() ?? _inAppBrowser?.id).toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + if (PlatformInAppWebViewController.debugLoggingSettings.enabled && + call.method != "onCallJsHandler") { + _debugLog(call.method, call.arguments); + } + + switch (call.method) { + case "onLoadStart": + _injectedScriptsFromURL.clear(); + if ((webviewParams != null && webviewParams!.onLoadStart != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && webviewParams!.onLoadStart != null) + webviewParams!.onLoadStart!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onLoadStart(uri); + } + break; + case "onLoadStop": + if ((webviewParams != null && webviewParams!.onLoadStop != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && webviewParams!.onLoadStop != null) + webviewParams!.onLoadStop!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onLoadStop(uri); + } + break; + case "onReceivedError": + if ((webviewParams != null && + (webviewParams!.onReceivedError != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadError != null)) || + _inAppBrowserEventHandler != null) { + WebResourceRequest request = WebResourceRequest.fromMap( + call.arguments["request"].cast())!; + WebResourceError error = WebResourceError.fromMap( + call.arguments["error"].cast())!; + var isForMainFrame = request.isForMainFrame ?? false; + + if (webviewParams != null) { + if (webviewParams!.onReceivedError != null) + webviewParams!.onReceivedError!( + _controllerFromPlatform, request, error); + else if (isForMainFrame) { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadError!(_controllerFromPlatform, request.url, + error.type.toNativeValue() ?? -1, error.description); + } + } else { + if (isForMainFrame) { + _inAppBrowserEventHandler!.onLoadError(request.url, + error.type.toNativeValue() ?? -1, error.description); + } + _inAppBrowserEventHandler!.onReceivedError(request, error); + } + } + break; + case "onReceivedHttpError": + if ((webviewParams != null && + (webviewParams!.onReceivedHttpError != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadHttpError != null)) || + _inAppBrowserEventHandler != null) { + WebResourceRequest request = WebResourceRequest.fromMap( + call.arguments["request"].cast())!; + WebResourceResponse errorResponse = WebResourceResponse.fromMap( + call.arguments["errorResponse"].cast())!; + var isForMainFrame = request.isForMainFrame ?? false; + + if (webviewParams != null) { + if (webviewParams!.onReceivedHttpError != null) + webviewParams!.onReceivedHttpError!( + _controllerFromPlatform, request, errorResponse); + else if (isForMainFrame) { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadHttpError!( + _controllerFromPlatform, + request.url, + errorResponse.statusCode ?? -1, + errorResponse.reasonPhrase ?? ''); + } + } else { + if (isForMainFrame) { + _inAppBrowserEventHandler!.onLoadHttpError( + request.url, + errorResponse.statusCode ?? -1, + errorResponse.reasonPhrase ?? ''); + } + _inAppBrowserEventHandler! + .onReceivedHttpError(request, errorResponse); + } + } + break; + case "onProgressChanged": + if ((webviewParams != null && + webviewParams!.onProgressChanged != null) || + _inAppBrowserEventHandler != null) { + int progress = call.arguments["progress"]; + if (webviewParams != null && webviewParams!.onProgressChanged != null) + webviewParams!.onProgressChanged!( + _controllerFromPlatform, progress); + else + _inAppBrowserEventHandler!.onProgressChanged(progress); + } + break; + case "shouldOverrideUrlLoading": + if ((webviewParams != null && + webviewParams!.shouldOverrideUrlLoading != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + NavigationAction navigationAction = + NavigationAction.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldOverrideUrlLoading != null) + return (await webviewParams!.shouldOverrideUrlLoading!( + _controllerFromPlatform, navigationAction)) + ?.toNativeValue(); + return (await _inAppBrowserEventHandler! + .shouldOverrideUrlLoading(navigationAction)) + ?.toNativeValue(); + } + break; + case "onConsoleMessage": + if ((webviewParams != null && + webviewParams!.onConsoleMessage != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ConsoleMessage consoleMessage = ConsoleMessage.fromMap(arguments)!; + if (webviewParams != null && webviewParams!.onConsoleMessage != null) + webviewParams!.onConsoleMessage!( + _controllerFromPlatform, consoleMessage); + else + _inAppBrowserEventHandler!.onConsoleMessage(consoleMessage); + } + break; + case "onScrollChanged": + if ((webviewParams != null && webviewParams!.onScrollChanged != null) || + _inAppBrowserEventHandler != null) { + int x = call.arguments["x"]; + int y = call.arguments["y"]; + if (webviewParams != null && webviewParams!.onScrollChanged != null) + webviewParams!.onScrollChanged!(_controllerFromPlatform, x, y); + else + _inAppBrowserEventHandler!.onScrollChanged(x, y); + } + break; + case "onDownloadStartRequest": + if ((webviewParams != null && + // ignore: deprecated_member_use_from_same_package + (webviewParams!.onDownloadStart != null || + webviewParams!.onDownloadStartRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + DownloadStartRequest downloadStartRequest = + DownloadStartRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onDownloadStartRequest != null) + webviewParams!.onDownloadStartRequest!( + _controllerFromPlatform, downloadStartRequest); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onDownloadStart!( + _controllerFromPlatform, downloadStartRequest.url); + } + } else { + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .onDownloadStart(downloadStartRequest.url); + _inAppBrowserEventHandler! + .onDownloadStartRequest(downloadStartRequest); + } + } + break; + case "onLoadResourceWithCustomScheme": + if ((webviewParams != null && + (webviewParams!.onLoadResourceWithCustomScheme != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadResourceCustomScheme != null)) || + _inAppBrowserEventHandler != null) { + Map requestMap = + call.arguments["request"].cast(); + WebResourceRequest request = WebResourceRequest.fromMap(requestMap)!; + + if (webviewParams != null) { + if (webviewParams!.onLoadResourceWithCustomScheme != null) + return (await webviewParams!.onLoadResourceWithCustomScheme!( + _controllerFromPlatform, request)) + ?.toMap(); + else { + return (await params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .onLoadResourceCustomScheme!( + _controllerFromPlatform, request.url)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onLoadResourceWithCustomScheme(request)) ?? + (await _inAppBrowserEventHandler! + .onLoadResourceCustomScheme(request.url))) + ?.toMap(); + } + } + break; + case "onCreateWindow": + if ((webviewParams != null && webviewParams!.onCreateWindow != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + CreateWindowAction createWindowAction = + CreateWindowAction.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onCreateWindow != null) + return await webviewParams!.onCreateWindow!( + _controllerFromPlatform, createWindowAction); + else + return await _inAppBrowserEventHandler! + .onCreateWindow(createWindowAction); + } + break; + case "onCloseWindow": + if (webviewParams != null && webviewParams!.onCloseWindow != null) + webviewParams!.onCloseWindow!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onCloseWindow(); + break; + case "onTitleChanged": + if ((webviewParams != null && webviewParams!.onTitleChanged != null) || + _inAppBrowserEventHandler != null) { + String? title = call.arguments["title"]; + if (webviewParams != null && webviewParams!.onTitleChanged != null) + webviewParams!.onTitleChanged!(_controllerFromPlatform, title); + else + _inAppBrowserEventHandler!.onTitleChanged(title); + } + break; + case "onGeolocationPermissionsShowPrompt": + if ((webviewParams != null && + (webviewParams!.onGeolocationPermissionsShowPrompt != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsShowPrompt != + null)) || + _inAppBrowserEventHandler != null) { + String origin = call.arguments["origin"]; + + if (webviewParams != null) { + if (webviewParams!.onGeolocationPermissionsShowPrompt != null) + return (await webviewParams!.onGeolocationPermissionsShowPrompt!( + _controllerFromPlatform, origin)) + ?.toMap(); + else { + return (await params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .androidOnGeolocationPermissionsShowPrompt!( + _controllerFromPlatform, origin)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onGeolocationPermissionsShowPrompt(origin)) ?? + (await _inAppBrowserEventHandler! + .androidOnGeolocationPermissionsShowPrompt(origin))) + ?.toMap(); + } + } + break; + case "onGeolocationPermissionsHidePrompt": + if (webviewParams != null && + (webviewParams!.onGeolocationPermissionsHidePrompt != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsHidePrompt != + null)) { + if (webviewParams!.onGeolocationPermissionsHidePrompt != null) + webviewParams! + .onGeolocationPermissionsHidePrompt!(_controllerFromPlatform); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsHidePrompt!( + _controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onGeolocationPermissionsHidePrompt(); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnGeolocationPermissionsHidePrompt(); + } + break; + case "shouldInterceptRequest": + if ((webviewParams != null && + (webviewParams!.shouldInterceptRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidShouldInterceptRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + WebResourceRequest request = WebResourceRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.shouldInterceptRequest != null) + return (await webviewParams!.shouldInterceptRequest!( + _controllerFromPlatform, request)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidShouldInterceptRequest!( + _controllerFromPlatform, request)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .shouldInterceptRequest(request)) ?? + (await _inAppBrowserEventHandler! + .androidShouldInterceptRequest(request))) + ?.toMap(); + } + } + break; + case "onRenderProcessUnresponsive": + if ((webviewParams != null && + (webviewParams!.onRenderProcessUnresponsive != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessUnresponsive != + null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessUnresponsive != null) + return (await webviewParams!.onRenderProcessUnresponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnRenderProcessUnresponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onRenderProcessUnresponsive(uri)) ?? + (await _inAppBrowserEventHandler! + .androidOnRenderProcessUnresponsive(uri))) + ?.toNativeValue(); + } + } + break; + case "onRenderProcessResponsive": + if ((webviewParams != null && + (webviewParams!.onRenderProcessResponsive != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessResponsive != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessResponsive != null) + return (await webviewParams!.onRenderProcessResponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnRenderProcessResponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onRenderProcessResponsive(uri)) ?? + (await _inAppBrowserEventHandler! + .androidOnRenderProcessResponsive(uri))) + ?.toNativeValue(); + } + } + break; + case "onRenderProcessGone": + if ((webviewParams != null && + (webviewParams!.onRenderProcessGone != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessGone != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + RenderProcessGoneDetail detail = + RenderProcessGoneDetail.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessGone != null) + webviewParams!.onRenderProcessGone!( + _controllerFromPlatform, detail); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessGone!( + _controllerFromPlatform, detail); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onRenderProcessGone(detail); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.androidOnRenderProcessGone(detail); + } + } + break; + case "onFormResubmission": + if ((webviewParams != null && + (webviewParams!.onFormResubmission != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnFormResubmission != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onFormResubmission != null) + return (await webviewParams!.onFormResubmission!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnFormResubmission!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onFormResubmission(uri)) ?? + // ignore: deprecated_member_use_from_same_package + (await _inAppBrowserEventHandler! + .androidOnFormResubmission(uri))) + ?.toNativeValue(); + } + } + break; + case "onZoomScaleChanged": + if ((webviewParams != null && + // ignore: deprecated_member_use_from_same_package + (webviewParams!.androidOnScaleChanged != null || + webviewParams!.onZoomScaleChanged != null)) || + _inAppBrowserEventHandler != null) { + double oldScale = call.arguments["oldScale"]; + double newScale = call.arguments["newScale"]; + + if (webviewParams != null) { + if (webviewParams!.onZoomScaleChanged != null) + webviewParams!.onZoomScaleChanged!( + _controllerFromPlatform, oldScale, newScale); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnScaleChanged!( + _controllerFromPlatform, oldScale, newScale); + } + } else { + _inAppBrowserEventHandler!.onZoomScaleChanged(oldScale, newScale); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnScaleChanged(oldScale, newScale); + } + } + break; + case "onReceivedIcon": + if ((webviewParams != null && + (webviewParams!.onReceivedIcon != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedIcon != null)) || + _inAppBrowserEventHandler != null) { + Uint8List icon = + Uint8List.fromList(call.arguments["icon"].cast()); + + if (webviewParams != null) { + if (webviewParams!.onReceivedIcon != null) + webviewParams!.onReceivedIcon!(_controllerFromPlatform, icon); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedIcon!( + _controllerFromPlatform, icon); + } + } else { + _inAppBrowserEventHandler!.onReceivedIcon(icon); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.androidOnReceivedIcon(icon); + } + } + break; + case "onReceivedTouchIconUrl": + if ((webviewParams != null && + (webviewParams!.onReceivedTouchIconUrl != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedTouchIconUrl != null)) || + _inAppBrowserEventHandler != null) { + String url = call.arguments["url"]; + bool precomposed = call.arguments["precomposed"]; + WebUri uri = WebUri(url); + + if (webviewParams != null) { + if (webviewParams!.onReceivedTouchIconUrl != null) + webviewParams!.onReceivedTouchIconUrl!( + _controllerFromPlatform, uri, precomposed); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedTouchIconUrl!( + _controllerFromPlatform, uri, precomposed); + } + } else { + _inAppBrowserEventHandler!.onReceivedTouchIconUrl(uri, precomposed); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnReceivedTouchIconUrl(uri, precomposed); + } + } + break; + case "onJsAlert": + if ((webviewParams != null && webviewParams!.onJsAlert != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsAlertRequest jsAlertRequest = JsAlertRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsAlert != null) + return (await webviewParams!.onJsAlert!( + _controllerFromPlatform, jsAlertRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler!.onJsAlert(jsAlertRequest)) + ?.toMap(); + } + break; + case "onJsConfirm": + if ((webviewParams != null && webviewParams!.onJsConfirm != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsConfirmRequest jsConfirmRequest = + JsConfirmRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsConfirm != null) + return (await webviewParams!.onJsConfirm!( + _controllerFromPlatform, jsConfirmRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onJsConfirm(jsConfirmRequest)) + ?.toMap(); + } + break; + case "onJsPrompt": + if ((webviewParams != null && webviewParams!.onJsPrompt != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsPromptRequest jsPromptRequest = JsPromptRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsPrompt != null) + return (await webviewParams!.onJsPrompt!( + _controllerFromPlatform, jsPromptRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onJsPrompt(jsPromptRequest)) + ?.toMap(); + } + break; + case "onJsBeforeUnload": + if ((webviewParams != null && + (webviewParams!.onJsBeforeUnload != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnJsBeforeUnload != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsBeforeUnloadRequest jsBeforeUnloadRequest = + JsBeforeUnloadRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onJsBeforeUnload != null) + return (await webviewParams!.onJsBeforeUnload!( + _controllerFromPlatform, jsBeforeUnloadRequest)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnJsBeforeUnload!( + _controllerFromPlatform, jsBeforeUnloadRequest)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onJsBeforeUnload(jsBeforeUnloadRequest)) ?? + (await _inAppBrowserEventHandler! + .androidOnJsBeforeUnload(jsBeforeUnloadRequest))) + ?.toMap(); + } + } + break; + case "onSafeBrowsingHit": + if ((webviewParams != null && + (webviewParams!.onSafeBrowsingHit != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnSafeBrowsingHit != null)) || + _inAppBrowserEventHandler != null) { + String url = call.arguments["url"]; + SafeBrowsingThreat? threatType = + SafeBrowsingThreat.fromNativeValue(call.arguments["threatType"]); + WebUri uri = WebUri(url); + + if (webviewParams != null) { + if (webviewParams!.onSafeBrowsingHit != null) + return (await webviewParams!.onSafeBrowsingHit!( + _controllerFromPlatform, uri, threatType)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnSafeBrowsingHit!( + _controllerFromPlatform, uri, threatType)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onSafeBrowsingHit(uri, threatType)) ?? + (await _inAppBrowserEventHandler! + .androidOnSafeBrowsingHit(uri, threatType))) + ?.toMap(); + } + } + break; + case "onReceivedLoginRequest": + if ((webviewParams != null && + (webviewParams!.onReceivedLoginRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedLoginRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + LoginRequest loginRequest = LoginRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onReceivedLoginRequest != null) + webviewParams!.onReceivedLoginRequest!( + _controllerFromPlatform, loginRequest); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedLoginRequest!( + _controllerFromPlatform, loginRequest); + } + } else { + _inAppBrowserEventHandler!.onReceivedLoginRequest(loginRequest); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnReceivedLoginRequest(loginRequest); + } + } + break; + case "onPermissionRequestCanceled": + if ((webviewParams != null && + webviewParams!.onPermissionRequestCanceled != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + PermissionRequest permissionRequest = + PermissionRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onPermissionRequestCanceled != null) + webviewParams!.onPermissionRequestCanceled!( + _controllerFromPlatform, permissionRequest); + else + _inAppBrowserEventHandler! + .onPermissionRequestCanceled(permissionRequest); + } + break; + case "onRequestFocus": + if ((webviewParams != null && webviewParams!.onRequestFocus != null) || + _inAppBrowserEventHandler != null) { + if (webviewParams != null && webviewParams!.onRequestFocus != null) + webviewParams!.onRequestFocus!(_controllerFromPlatform); + else + _inAppBrowserEventHandler!.onRequestFocus(); + } + break; + case "onReceivedHttpAuthRequest": + if ((webviewParams != null && + webviewParams!.onReceivedHttpAuthRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + HttpAuthenticationChallenge challenge = + HttpAuthenticationChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedHttpAuthRequest != null) + return (await webviewParams!.onReceivedHttpAuthRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedHttpAuthRequest(challenge)) + ?.toMap(); + } + break; + case "onReceivedServerTrustAuthRequest": + if ((webviewParams != null && + webviewParams!.onReceivedServerTrustAuthRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ServerTrustChallenge challenge = + ServerTrustChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedServerTrustAuthRequest != null) + return (await webviewParams!.onReceivedServerTrustAuthRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedServerTrustAuthRequest(challenge)) + ?.toMap(); + } + break; + case "onReceivedClientCertRequest": + if ((webviewParams != null && + webviewParams!.onReceivedClientCertRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ClientCertChallenge challenge = + ClientCertChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedClientCertRequest != null) + return (await webviewParams!.onReceivedClientCertRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedClientCertRequest(challenge)) + ?.toMap(); + } + break; + case "onFindResultReceived": + if ((webviewParams != null && + (webviewParams!.onFindResultReceived != null || + (webviewParams!.findInteractionController != null && + webviewParams!.findInteractionController!.params + .onFindResultReceived != + null))) || + _inAppBrowserEventHandler != null) { + int activeMatchOrdinal = call.arguments["activeMatchOrdinal"]; + int numberOfMatches = call.arguments["numberOfMatches"]; + bool isDoneCounting = call.arguments["isDoneCounting"]; + if (webviewParams != null) { + if (webviewParams!.findInteractionController != null && + webviewParams!.findInteractionController!.params + .onFindResultReceived != + null) + webviewParams! + .findInteractionController!.params.onFindResultReceived!( + webviewParams!.findInteractionController!, + activeMatchOrdinal, + numberOfMatches, + isDoneCounting); + else + webviewParams!.onFindResultReceived!(_controllerFromPlatform, + activeMatchOrdinal, numberOfMatches, isDoneCounting); + } else { + if (_inAppBrowser!.findInteractionController != null && + _inAppBrowser! + .findInteractionController!.onFindResultReceived != + null) + _inAppBrowser!.findInteractionController!.onFindResultReceived!( + webviewParams!.findInteractionController!, + activeMatchOrdinal, + numberOfMatches, + isDoneCounting); + else + _inAppBrowserEventHandler!.onFindResultReceived( + activeMatchOrdinal, numberOfMatches, isDoneCounting); + } + } + break; + case "onPermissionRequest": + if ((webviewParams != null && + (webviewParams!.onPermissionRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnPermissionRequest != null)) || + _inAppBrowserEventHandler != null) { + String origin = call.arguments["origin"]; + List resources = call.arguments["resources"].cast(); + + Map arguments = + call.arguments.cast(); + PermissionRequest permissionRequest = + PermissionRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onPermissionRequest != null) + return (await webviewParams!.onPermissionRequest!( + _controllerFromPlatform, permissionRequest)) + ?.toMap(); + else { + return (await webviewParams!.androidOnPermissionRequest!( + _controllerFromPlatform, origin, resources)) + ?.toMap(); + } + } else { + return (await _inAppBrowserEventHandler! + .onPermissionRequest(permissionRequest)) + ?.toMap() ?? + (await _inAppBrowserEventHandler! + .androidOnPermissionRequest(origin, resources)) + ?.toMap(); + } + } + break; + case "onUpdateVisitedHistory": + if ((webviewParams != null && + webviewParams!.onUpdateVisitedHistory != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + bool? isReload = call.arguments["isReload"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && + webviewParams!.onUpdateVisitedHistory != null) + webviewParams!.onUpdateVisitedHistory!( + _controllerFromPlatform, uri, isReload); + else + _inAppBrowserEventHandler!.onUpdateVisitedHistory(uri, isReload); + } + break; + case "onWebContentProcessDidTerminate": + if (webviewParams != null && + (webviewParams!.onWebContentProcessDidTerminate != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosOnWebContentProcessDidTerminate != null)) { + if (webviewParams!.onWebContentProcessDidTerminate != null) + webviewParams! + .onWebContentProcessDidTerminate!(_controllerFromPlatform); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams! + .iosOnWebContentProcessDidTerminate!(_controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onWebContentProcessDidTerminate(); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.iosOnWebContentProcessDidTerminate(); + } + break; + case "onPageCommitVisible": + if ((webviewParams != null && + webviewParams!.onPageCommitVisible != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && + webviewParams!.onPageCommitVisible != null) + webviewParams!.onPageCommitVisible!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onPageCommitVisible(uri); + } + break; + case "onDidReceiveServerRedirectForProvisionalNavigation": + if (webviewParams != null && + (webviewParams! + .onDidReceiveServerRedirectForProvisionalNavigation != + null || + params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .iosOnDidReceiveServerRedirectForProvisionalNavigation != + null)) { + if (webviewParams! + .onDidReceiveServerRedirectForProvisionalNavigation != + null) + webviewParams!.onDidReceiveServerRedirectForProvisionalNavigation!( + _controllerFromPlatform); + else { + params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .iosOnDidReceiveServerRedirectForProvisionalNavigation!( + _controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler! + .onDidReceiveServerRedirectForProvisionalNavigation(); + _inAppBrowserEventHandler! + .iosOnDidReceiveServerRedirectForProvisionalNavigation(); + } + break; + case "onNavigationResponse": + if ((webviewParams != null && + (webviewParams!.onNavigationResponse != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosOnNavigationResponse != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + // ignore: deprecated_member_use_from_same_package + IOSWKNavigationResponse iosOnNavigationResponse = + // ignore: deprecated_member_use_from_same_package + IOSWKNavigationResponse.fromMap(arguments)!; + + NavigationResponse navigationResponse = + NavigationResponse.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onNavigationResponse != null) + return (await webviewParams!.onNavigationResponse!( + _controllerFromPlatform, navigationResponse)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.iosOnNavigationResponse!( + _controllerFromPlatform, iosOnNavigationResponse)) + ?.toNativeValue(); + } + } else { + return (await _inAppBrowserEventHandler! + .onNavigationResponse(navigationResponse)) + ?.toNativeValue() ?? + (await _inAppBrowserEventHandler! + .iosOnNavigationResponse(iosOnNavigationResponse)) + ?.toNativeValue(); + } + } + break; + case "shouldAllowDeprecatedTLS": + if ((webviewParams != null && + (webviewParams!.shouldAllowDeprecatedTLS != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosShouldAllowDeprecatedTLS != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + URLAuthenticationChallenge challenge = + URLAuthenticationChallenge.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.shouldAllowDeprecatedTLS != null) + return (await webviewParams!.shouldAllowDeprecatedTLS!( + _controllerFromPlatform, challenge)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.iosShouldAllowDeprecatedTLS!( + _controllerFromPlatform, challenge)) + ?.toNativeValue(); + } + } else { + return (await _inAppBrowserEventHandler! + .shouldAllowDeprecatedTLS(challenge)) + ?.toNativeValue() ?? + // ignore: deprecated_member_use_from_same_package + (await _inAppBrowserEventHandler! + .iosShouldAllowDeprecatedTLS(challenge)) + ?.toNativeValue(); + } + } + break; + case "onLongPressHitTestResult": + if ((webviewParams != null && + webviewParams!.onLongPressHitTestResult != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + InAppWebViewHitTestResult hitTestResult = + InAppWebViewHitTestResult.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onLongPressHitTestResult != null) + webviewParams!.onLongPressHitTestResult!( + _controllerFromPlatform, hitTestResult); + else + _inAppBrowserEventHandler!.onLongPressHitTestResult(hitTestResult); + } + break; + case "onCreateContextMenu": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null && contextMenu.onCreateContextMenu != null) { + Map arguments = + call.arguments.cast(); + InAppWebViewHitTestResult hitTestResult = + InAppWebViewHitTestResult.fromMap(arguments)!; + + contextMenu.onCreateContextMenu!(hitTestResult); + } + break; + case "onHideContextMenu": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null && contextMenu.onHideContextMenu != null) { + contextMenu.onHideContextMenu!(); + } + break; + case "onContextMenuActionItemClicked": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null) { + int? androidId = call.arguments["androidId"]; + String? iosId = call.arguments["iosId"]; + dynamic id = call.arguments["id"]; + String title = call.arguments["title"]; + + ContextMenuItem menuItemClicked = ContextMenuItem( + id: id, + // ignore: deprecated_member_use_from_same_package + androidId: androidId, + // ignore: deprecated_member_use_from_same_package + iosId: iosId, + title: title, + action: null); + + for (var menuItem in contextMenu.menuItems) { + if (menuItem.id == id) { + menuItemClicked = menuItem; + if (menuItem.action != null) { + menuItem.action!(); + } + break; + } + } + + if (contextMenu.onContextMenuActionItemClicked != null) { + contextMenu.onContextMenuActionItemClicked!(menuItemClicked); + } + } + break; + case "onEnterFullscreen": + if (webviewParams != null && webviewParams!.onEnterFullscreen != null) + webviewParams!.onEnterFullscreen!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onEnterFullscreen(); + break; + case "onExitFullscreen": + if (webviewParams != null && webviewParams!.onExitFullscreen != null) + webviewParams!.onExitFullscreen!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onExitFullscreen(); + break; + case "onOverScrolled": + if ((webviewParams != null && webviewParams!.onOverScrolled != null) || + _inAppBrowserEventHandler != null) { + int x = call.arguments["x"]; + int y = call.arguments["y"]; + bool clampedX = call.arguments["clampedX"]; + bool clampedY = call.arguments["clampedY"]; + + if (webviewParams != null && webviewParams!.onOverScrolled != null) + webviewParams!.onOverScrolled!( + _controllerFromPlatform, x, y, clampedX, clampedY); + else + _inAppBrowserEventHandler!.onOverScrolled(x, y, clampedX, clampedY); + } + break; + case "onWindowFocus": + if (webviewParams != null && webviewParams!.onWindowFocus != null) + webviewParams!.onWindowFocus!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowFocus(); + break; + case "onWindowBlur": + if (webviewParams != null && webviewParams!.onWindowBlur != null) + webviewParams!.onWindowBlur!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowBlur(); + break; + case "onPrintRequest": + if ((webviewParams != null && + (webviewParams!.onPrintRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onPrint != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + String? printJobId = call.arguments["printJobId"]; + WebUri? uri = url != null ? WebUri(url) : null; + MacOSPrintJobController? printJob = printJobId != null + ? MacOSPrintJobController( + MacOSPrintJobControllerCreationParams(id: printJobId)) + : null; + + if (webviewParams != null) { + if (webviewParams!.onPrintRequest != null) + return await webviewParams!.onPrintRequest!( + _controllerFromPlatform, uri, printJob); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onPrint!(_controllerFromPlatform, uri); + return false; + } + } else { + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.onPrint(uri); + return await _inAppBrowserEventHandler! + .onPrintRequest(uri, printJob); + } + } + break; + case "onInjectedScriptLoaded": + String id = call.arguments[0]; + var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onLoadCallback != null) { + onLoadCallback(); + } + break; + case "onInjectedScriptError": + String id = call.arguments[0]; + var onErrorCallback = _injectedScriptsFromURL[id]?.onError; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onErrorCallback != null) { + onErrorCallback(); + } + break; + case "onCameraCaptureStateChanged": + if ((webviewParams != null && + webviewParams!.onCameraCaptureStateChanged != null) || + _inAppBrowserEventHandler != null) { + var oldState = + MediaCaptureState.fromNativeValue(call.arguments["oldState"]); + var newState = + MediaCaptureState.fromNativeValue(call.arguments["newState"]); + + if (webviewParams != null && + webviewParams!.onCameraCaptureStateChanged != null) + webviewParams!.onCameraCaptureStateChanged!( + _controllerFromPlatform, oldState, newState); + else + _inAppBrowserEventHandler! + .onCameraCaptureStateChanged(oldState, newState); + } + break; + case "onMicrophoneCaptureStateChanged": + if ((webviewParams != null && + webviewParams!.onMicrophoneCaptureStateChanged != null) || + _inAppBrowserEventHandler != null) { + var oldState = + MediaCaptureState.fromNativeValue(call.arguments["oldState"]); + var newState = + MediaCaptureState.fromNativeValue(call.arguments["newState"]); + + if (webviewParams != null && + webviewParams!.onMicrophoneCaptureStateChanged != null) + webviewParams!.onMicrophoneCaptureStateChanged!( + _controllerFromPlatform, oldState, newState); + else + _inAppBrowserEventHandler! + .onMicrophoneCaptureStateChanged(oldState, newState); + } + break; + case "onContentSizeChanged": + if ((webviewParams != null && + webviewParams!.onContentSizeChanged != null) || + _inAppBrowserEventHandler != null) { + var oldContentSize = MapSize.fromMap( + call.arguments["oldContentSize"]?.cast())!; + var newContentSize = MapSize.fromMap( + call.arguments["newContentSize"]?.cast())!; + + if (webviewParams != null && + webviewParams!.onContentSizeChanged != null) + webviewParams!.onContentSizeChanged!( + _controllerFromPlatform, oldContentSize, newContentSize); + else + _inAppBrowserEventHandler! + .onContentSizeChanged(oldContentSize, newContentSize); + } + break; + case "onCallJsHandler": + String handlerName = call.arguments["handlerName"]; + // decode args to json + List args = jsonDecode(call.arguments["args"]); + + _debugLog(handlerName, args); + + switch (handlerName) { + case "onLoadResource": + if ((webviewParams != null && + webviewParams!.onLoadResource != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + arguments["startTime"] = arguments["startTime"] is int + ? arguments["startTime"].toDouble() + : arguments["startTime"]; + arguments["duration"] = arguments["duration"] is int + ? arguments["duration"].toDouble() + : arguments["duration"]; + + var response = LoadedResource.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onLoadResource != null) + webviewParams!.onLoadResource!( + _controllerFromPlatform, response); + else + _inAppBrowserEventHandler!.onLoadResource(response); + } + return null; + case "shouldInterceptAjaxRequest": + if ((webviewParams != null && + webviewParams!.shouldInterceptAjaxRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldInterceptAjaxRequest != null) + return jsonEncode( + await params.webviewParams!.shouldInterceptAjaxRequest!( + _controllerFromPlatform, request)); + else + return jsonEncode(await _inAppBrowserEventHandler! + .shouldInterceptAjaxRequest(request)); + } + return null; + case "onAjaxReadyStateChange": + if ((webviewParams != null && + webviewParams!.onAjaxReadyStateChange != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onAjaxReadyStateChange != null) + return (await webviewParams!.onAjaxReadyStateChange!( + _controllerFromPlatform, request)) + ?.toNativeValue(); + else + return (await _inAppBrowserEventHandler! + .onAjaxReadyStateChange(request)) + ?.toNativeValue(); + } + return null; + case "onAjaxProgress": + if ((webviewParams != null && + webviewParams!.onAjaxProgress != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onAjaxProgress != null) + return (await webviewParams!.onAjaxProgress!( + _controllerFromPlatform, request)) + ?.toNativeValue(); + else + return (await _inAppBrowserEventHandler! + .onAjaxProgress(request)) + ?.toNativeValue(); + } + return null; + case "shouldInterceptFetchRequest": + if ((webviewParams != null && + webviewParams!.shouldInterceptFetchRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + FetchRequest request = FetchRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldInterceptFetchRequest != null) + return jsonEncode( + await webviewParams!.shouldInterceptFetchRequest!( + _controllerFromPlatform, request)); + else + return jsonEncode(await _inAppBrowserEventHandler! + .shouldInterceptFetchRequest(request)); + } + return null; + case "onWindowFocus": + if (webviewParams != null && webviewParams!.onWindowFocus != null) + webviewParams!.onWindowFocus!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowFocus(); + return null; + case "onWindowBlur": + if (webviewParams != null && webviewParams!.onWindowBlur != null) + webviewParams!.onWindowBlur!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowBlur(); + return null; + case "onInjectedScriptLoaded": + String id = args[0]; + var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onLoadCallback != null) { + onLoadCallback(); + } + return null; + case "onInjectedScriptError": + String id = args[0]; + var onErrorCallback = _injectedScriptsFromURL[id]?.onError; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onErrorCallback != null) { + onErrorCallback(); + } + return null; + } + + if (_javaScriptHandlersMap.containsKey(handlerName)) { + // convert result to json + try { + return jsonEncode(await _javaScriptHandlersMap[handlerName]!(args)); + } catch (error, stacktrace) { + developer.log(error.toString() + '\n' + stacktrace.toString(), + name: 'JavaScript Handler "$handlerName"'); + throw Exception(error.toString().replaceFirst('Exception: ', '')); + } + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + Future getUrl() async { + Map args = {}; + String? url = await channel?.invokeMethod('getUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + Future getTitle() async { + Map args = {}; + return await channel?.invokeMethod('getTitle', args); + } + + @override + Future getProgress() async { + Map args = {}; + return await channel?.invokeMethod('getProgress', args); + } + + @override + Future getHtml() async { + String? html; + + InAppWebViewSettings? settings = await getSettings(); + if (settings != null && settings.javaScriptEnabled == true) { + html = await evaluateJavascript( + source: "window.document.getElementsByTagName('html')[0].outerHTML;"); + if (html != null && html.isNotEmpty) return html; + } + + var webviewUrl = await getUrl(); + if (webviewUrl == null) { + return html; + } + + if (webviewUrl.isScheme("file")) { + var assetPathSplitted = webviewUrl.toString().split("/flutter_assets/"); + var assetPath = assetPathSplitted[assetPathSplitted.length - 1]; + try { + var bytes = await rootBundle.load(assetPath); + html = utf8.decode(bytes.buffer.asUint8List()); + } catch (e) {} + } else { + try { + HttpClient client = HttpClient(); + var htmlRequest = await client.getUrl(webviewUrl); + html = + await (await htmlRequest.close()).transform(Utf8Decoder()).join(); + } catch (e) { + developer.log(e.toString(), name: this.runtimeType.toString()); + } + } + + return html; + } + + @override + Future> getFavicons() async { + List favicons = []; + + var webviewUrl = await getUrl(); + + if (webviewUrl == null) { + return favicons; + } + + String? manifestUrl; + + var html = await getHtml(); + if (html == null || html.isEmpty) { + return favicons; + } + var assetPathBase; + + if (webviewUrl.isScheme("file")) { + var assetPathSplitted = webviewUrl.toString().split("/flutter_assets/"); + assetPathBase = assetPathSplitted[0] + "/flutter_assets/"; + } + + InAppWebViewSettings? settings = await getSettings(); + if (settings != null && settings.javaScriptEnabled == true) { + List> links = (await evaluateJavascript(source: """ +(function() { + var linkNodes = document.head.getElementsByTagName("link"); + var links = []; + for (var i = 0; i < linkNodes.length; i++) { + var linkNode = linkNodes[i]; + if (linkNode.rel === 'manifest') { + links.push( + { + rel: linkNode.rel, + href: linkNode.href, + sizes: null + } + ); + } else if (linkNode.rel != null && linkNode.rel.indexOf('icon') >= 0) { + links.push( + { + rel: linkNode.rel, + href: linkNode.href, + sizes: linkNode.sizes != null && linkNode.sizes.value != "" ? linkNode.sizes.value : null + } + ); + } + } + return links; +})(); +"""))?.cast>() ?? []; + for (var link in links) { + if (link["rel"] == "manifest") { + manifestUrl = link["href"]; + if (!_isUrlAbsolute(manifestUrl!)) { + if (manifestUrl.startsWith("/")) { + manifestUrl = manifestUrl.substring(1); + } + manifestUrl = ((assetPathBase == null) + ? webviewUrl.scheme + "://" + webviewUrl.host + "/" + : assetPathBase) + + manifestUrl; + } + continue; + } + favicons.addAll(_createFavicons(webviewUrl, assetPathBase, link["href"], + link["rel"], link["sizes"], false)); + } + } + + // try to get /favicon.ico + try { + HttpClient client = HttpClient(); + var faviconUrl = + webviewUrl.scheme + "://" + webviewUrl.host + "/favicon.ico"; + var faviconUri = WebUri(faviconUrl); + var headRequest = await client.headUrl(faviconUri); + var headResponse = await headRequest.close(); + if (headResponse.statusCode == 200) { + favicons.add(Favicon(url: faviconUri, rel: "shortcut icon")); + } + } catch (e) { + developer.log("/favicon.ico file not found: " + e.toString(), + name: this.runtimeType.toString()); + } + + // try to get the manifest file + HttpClientRequest? manifestRequest; + HttpClientResponse? manifestResponse; + bool manifestFound = false; + if (manifestUrl == null) { + manifestUrl = + webviewUrl.scheme + "://" + webviewUrl.host + "/manifest.json"; + } + try { + HttpClient client = HttpClient(); + manifestRequest = await client.getUrl(Uri.parse(manifestUrl)); + manifestResponse = await manifestRequest.close(); + manifestFound = manifestResponse.statusCode == 200 && + manifestResponse.headers.contentType?.mimeType == "application/json"; + } catch (e) { + developer.log("Manifest file not found: " + e.toString(), + name: this.runtimeType.toString()); + } + + if (manifestFound) { + try { + Map manifest = json + .decode(await manifestResponse!.transform(Utf8Decoder()).join()); + if (manifest.containsKey("icons")) { + for (Map icon in manifest["icons"]) { + favicons.addAll(_createFavicons(webviewUrl, assetPathBase, + icon["src"], icon["rel"], icon["sizes"], true)); + } + } + } on FormatException catch (_) { + /// The [manifestResponse] might not has a valid JSON string, catch and + /// ignore the error + } + } + + return favicons; + } + + bool _isUrlAbsolute(String url) { + return url.startsWith("http://") || url.startsWith("https://"); + } + + List _createFavicons(WebUri url, String? assetPathBase, + String urlIcon, String? rel, String? sizes, bool isManifest) { + List favicons = []; + + List urlSplitted = urlIcon.split("/"); + if (!_isUrlAbsolute(urlIcon)) { + if (urlIcon.startsWith("/")) { + urlIcon = urlIcon.substring(1); + } + urlIcon = ((assetPathBase == null) + ? url.scheme + "://" + url.host + "/" + : assetPathBase) + + urlIcon; + } + if (isManifest) { + rel = (sizes != null) + ? urlSplitted[urlSplitted.length - 1] + .replaceFirst("-" + sizes, "") + .split(" ")[0] + .split(".")[0] + : null; + } + if (sizes != null && sizes.isNotEmpty && sizes != "any") { + List sizesSplitted = sizes.split(" "); + for (String size in sizesSplitted) { + int width = int.parse(size.split("x")[0]); + int height = int.parse(size.split("x")[1]); + favicons.add(Favicon( + url: WebUri(urlIcon), rel: rel, width: width, height: height)); + } + } else { + favicons.add( + Favicon(url: WebUri(urlIcon), rel: rel, width: null, height: null)); + } + + return favicons; + } + + @override + Future loadUrl( + {required URLRequest urlRequest, + @Deprecated('Use allowingReadAccessTo instead') + Uri? iosAllowingReadAccessTo, + WebUri? allowingReadAccessTo}) async { + assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); + assert( + allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); + assert(iosAllowingReadAccessTo == null || + iosAllowingReadAccessTo.isScheme("file")); + + Map args = {}; + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + args.putIfAbsent( + 'allowingReadAccessTo', + () => + allowingReadAccessTo?.toString() ?? + iosAllowingReadAccessTo?.toString()); + await channel?.invokeMethod('loadUrl', args); + } + + @override + Future postUrl( + {required WebUri url, required Uint8List postData}) async { + assert(url.toString().isNotEmpty); + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('postData', () => postData); + await channel?.invokeMethod('postUrl', args); + } + + @override + Future loadData( + {required String data, + String mimeType = "text/html", + String encoding = "utf8", + WebUri? baseUrl, + @Deprecated('Use historyUrl instead') Uri? androidHistoryUrl, + WebUri? historyUrl, + @Deprecated('Use allowingReadAccessTo instead') + Uri? iosAllowingReadAccessTo, + WebUri? allowingReadAccessTo}) async { + assert( + allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); + assert(iosAllowingReadAccessTo == null || + iosAllowingReadAccessTo.isScheme("file")); + + Map args = {}; + args.putIfAbsent('data', () => data); + args.putIfAbsent('mimeType', () => mimeType); + args.putIfAbsent('encoding', () => encoding); + args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); + args.putIfAbsent( + 'historyUrl', + () => + historyUrl?.toString() ?? + androidHistoryUrl?.toString() ?? + "about:blank"); + args.putIfAbsent( + 'allowingReadAccessTo', + () => + allowingReadAccessTo?.toString() ?? + iosAllowingReadAccessTo?.toString()); + await channel?.invokeMethod('loadData', args); + } + + @override + Future loadFile({required String assetFilePath}) async { + assert(assetFilePath.isNotEmpty); + Map args = {}; + args.putIfAbsent('assetFilePath', () => assetFilePath); + await channel?.invokeMethod('loadFile', args); + } + + @override + Future reload() async { + Map args = {}; + await channel?.invokeMethod('reload', args); + } + + @override + Future goBack() async { + Map args = {}; + await channel?.invokeMethod('goBack', args); + } + + @override + Future canGoBack() async { + Map args = {}; + return await channel?.invokeMethod('canGoBack', args) ?? false; + } + + @override + Future goForward() async { + Map args = {}; + await channel?.invokeMethod('goForward', args); + } + + @override + Future canGoForward() async { + Map args = {}; + return await channel?.invokeMethod('canGoForward', args) ?? false; + } + + @override + Future goBackOrForward({required int steps}) async { + Map args = {}; + args.putIfAbsent('steps', () => steps); + await channel?.invokeMethod('goBackOrForward', args); + } + + @override + Future canGoBackOrForward({required int steps}) async { + Map args = {}; + args.putIfAbsent('steps', () => steps); + return await channel?.invokeMethod('canGoBackOrForward', args) ?? + false; + } + + @override + Future goTo({required WebHistoryItem historyItem}) async { + var steps = historyItem.offset; + if (steps != null) { + await goBackOrForward(steps: steps); + } + } + + @override + Future isLoading() async { + Map args = {}; + return await channel?.invokeMethod('isLoading', args) ?? false; + } + + @override + Future stopLoading() async { + Map args = {}; + await channel?.invokeMethod('stopLoading', args); + } + + @override + Future evaluateJavascript( + {required String source, ContentWorld? contentWorld}) async { + Map args = {}; + args.putIfAbsent('source', () => source); + args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); + var data = await channel?.invokeMethod('evaluateJavascript', args); + return data; + } + + @override + Future injectJavascriptFileFromUrl( + {required WebUri urlFile, + ScriptHtmlTagAttributes? scriptHtmlTagAttributes}) async { + assert(urlFile.toString().isNotEmpty); + var id = scriptHtmlTagAttributes?.id; + if (scriptHtmlTagAttributes != null && id != null) { + _injectedScriptsFromURL[id] = scriptHtmlTagAttributes; + } + Map args = {}; + args.putIfAbsent('urlFile', () => urlFile.toString()); + args.putIfAbsent( + 'scriptHtmlTagAttributes', () => scriptHtmlTagAttributes?.toMap()); + await channel?.invokeMethod('injectJavascriptFileFromUrl', args); + } + + @override + Future injectJavascriptFileFromAsset( + {required String assetFilePath}) async { + String source = await rootBundle.loadString(assetFilePath); + return await evaluateJavascript(source: source); + } + + @override + Future injectCSSCode({required String source}) async { + Map args = {}; + args.putIfAbsent('source', () => source); + await channel?.invokeMethod('injectCSSCode', args); + } + + @override + Future injectCSSFileFromUrl( + {required WebUri urlFile, + CSSLinkHtmlTagAttributes? cssLinkHtmlTagAttributes}) async { + assert(urlFile.toString().isNotEmpty); + Map args = {}; + args.putIfAbsent('urlFile', () => urlFile.toString()); + args.putIfAbsent( + 'cssLinkHtmlTagAttributes', () => cssLinkHtmlTagAttributes?.toMap()); + await channel?.invokeMethod('injectCSSFileFromUrl', args); + } + + @override + Future injectCSSFileFromAsset({required String assetFilePath}) async { + String source = await rootBundle.loadString(assetFilePath); + await injectCSSCode(source: source); + } + + @override + void addJavaScriptHandler( + {required String handlerName, + required JavaScriptHandlerCallback callback}) { + assert(!_JAVASCRIPT_HANDLER_FORBIDDEN_NAMES.contains(handlerName), + '"$handlerName" is a forbidden name!'); + this._javaScriptHandlersMap[handlerName] = (callback); + } + + @override + JavaScriptHandlerCallback? removeJavaScriptHandler( + {required String handlerName}) { + return this._javaScriptHandlersMap.remove(handlerName); + } + + @override + bool hasJavaScriptHandler({required String handlerName}) { + return this._javaScriptHandlersMap.containsKey(handlerName); + } + + @override + Future takeScreenshot( + {ScreenshotConfiguration? screenshotConfiguration}) async { + Map args = {}; + args.putIfAbsent( + 'screenshotConfiguration', () => screenshotConfiguration?.toMap()); + return await channel?.invokeMethod('takeScreenshot', args); + } + + @override + @Deprecated('Use setSettings instead') + Future setOptions({required InAppWebViewGroupOptions options}) async { + InAppWebViewSettings settings = + InAppWebViewSettings.fromMap(options.toMap()) ?? InAppWebViewSettings(); + await setSettings(settings: settings); + } + + @override + @Deprecated('Use getSettings instead') + Future getOptions() async { + InAppWebViewSettings? settings = await getSettings(); + + Map? options = settings?.toMap(); + if (options != null) { + options = options.cast(); + return InAppWebViewGroupOptions.fromMap(options as Map); + } + + return null; + } + + @override + Future setSettings({required InAppWebViewSettings settings}) async { + Map args = {}; + + args.putIfAbsent('settings', () => settings.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + Future getSettings() async { + Map args = {}; + + Map? settings = + await channel?.invokeMethod('getSettings', args); + if (settings != null) { + settings = settings.cast(); + return InAppWebViewSettings.fromMap(settings as Map); + } + + return null; + } + + @override + Future getCopyBackForwardList() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('getCopyBackForwardList', args)) + ?.cast(); + return WebHistory.fromMap(result); + } + + @override + Future clearCache() async { + Map args = {}; + await channel?.invokeMethod('clearCache', args); + } + + @override + @Deprecated("Use FindInteractionController.findAll instead") + Future findAllAsync({required String find}) async { + Map args = {}; + args.putIfAbsent('find', () => find); + await channel?.invokeMethod('findAll', args); + } + + @override + @Deprecated("Use FindInteractionController.findNext instead") + Future findNext({required bool forward}) async { + Map args = {}; + args.putIfAbsent('forward', () => forward); + await channel?.invokeMethod('findNext', args); + } + + @override + @Deprecated("Use FindInteractionController.clearMatches instead") + Future clearMatches() async { + Map args = {}; + await channel?.invokeMethod('clearMatches', args); + } + + @override + @Deprecated("Use tRexRunnerHtml instead") + Future getTRexRunnerHtml() async { + return await tRexRunnerHtml; + } + + @override + @Deprecated("Use tRexRunnerCss instead") + Future getTRexRunnerCss() async { + return await tRexRunnerCss; + } + + @override + Future scrollTo( + {required int x, required int y, bool animated = false}) async { + Map args = {}; + args.putIfAbsent('x', () => x); + args.putIfAbsent('y', () => y); + args.putIfAbsent('animated', () => animated); + await channel?.invokeMethod('scrollTo', args); + } + + @override + Future scrollBy( + {required int x, required int y, bool animated = false}) async { + Map args = {}; + args.putIfAbsent('x', () => x); + args.putIfAbsent('y', () => y); + args.putIfAbsent('animated', () => animated); + await channel?.invokeMethod('scrollBy', args); + } + + @override + Future pauseTimers() async { + Map args = {}; + await channel?.invokeMethod('pauseTimers', args); + } + + @override + Future resumeTimers() async { + Map args = {}; + await channel?.invokeMethod('resumeTimers', args); + } + + @override + Future printCurrentPage( + {PrintJobSettings? settings}) async { + Map args = {}; + args.putIfAbsent("settings", () => settings?.toMap()); + String? jobId = + await channel?.invokeMethod('printCurrentPage', args); + if (jobId != null) { + return MacOSPrintJobController( + PlatformPrintJobControllerCreationParams(id: jobId)); + } + return null; + } + + @override + Future getContentHeight() async { + Map args = {}; + var height = await channel?.invokeMethod('getContentHeight', args); + if (height == null || height == 0) { + // try to use javascript + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollHeight;"); + if (scrollHeight != null && scrollHeight is num) { + height = scrollHeight.toInt(); + } + } + return height; + } + + @override + Future getContentWidth() async { + Map args = {}; + var height = await channel?.invokeMethod('getContentWidth', args); + if (height == null || height == 0) { + // try to use javascript + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollWidth;"); + if (scrollHeight != null && scrollHeight is num) { + height = scrollHeight.toInt(); + } + } + return height; + } + + @override + Future zoomBy( + {required double zoomFactor, + @Deprecated('Use animated instead') bool? iosAnimated, + bool animated = false}) async { + Map args = {}; + args.putIfAbsent('zoomFactor', () => zoomFactor); + args.putIfAbsent('animated', () => iosAnimated ?? animated); + return await channel?.invokeMethod('zoomBy', args); + } + + @override + Future getOriginalUrl() async { + Map args = {}; + String? url = await channel?.invokeMethod('getOriginalUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + @Deprecated('Use getZoomScale instead') + Future getScale() async { + return await getZoomScale(); + } + + @override + Future getSelectedText() async { + Map args = {}; + return await channel?.invokeMethod('getSelectedText', args); + } + + @override + Future> getMetaTags() async { + List metaTags = []; + + List>? metaTagList = + (await evaluateJavascript(source: """ +(function() { + var metaTags = []; + var metaTagNodes = document.head.getElementsByTagName('meta'); + for (var i = 0; i < metaTagNodes.length; i++) { + var metaTagNode = metaTagNodes[i]; + + var otherAttributes = metaTagNode.getAttributeNames(); + var nameIndex = otherAttributes.indexOf("name"); + if (nameIndex !== -1) otherAttributes.splice(nameIndex, 1); + var contentIndex = otherAttributes.indexOf("content"); + if (contentIndex !== -1) otherAttributes.splice(contentIndex, 1); + + var attrs = []; + for (var j = 0; j < otherAttributes.length; j++) { + var otherAttribute = otherAttributes[j]; + attrs.push( + { + name: otherAttribute, + value: metaTagNode.getAttribute(otherAttribute) + } + ); + } + + metaTags.push( + { + name: metaTagNode.name, + content: metaTagNode.content, + attrs: attrs + } + ); + } + return metaTags; +})(); + """))?.cast>(); + + if (metaTagList == null) { + return metaTags; + } + + for (var metaTag in metaTagList) { + var attrs = []; + + for (var metaTagAttr in metaTag["attrs"]) { + attrs.add(MetaTagAttribute( + name: metaTagAttr["name"], value: metaTagAttr["value"])); + } + + metaTags.add(MetaTag( + name: metaTag["name"], content: metaTag["content"], attrs: attrs)); + } + + return metaTags; + } + + @override + Future getMetaThemeColor() async { + Color? themeColor; + + try { + Map args = {}; + themeColor = UtilColor.fromStringRepresentation( + await channel?.invokeMethod('getMetaThemeColor', args)); + return themeColor; + } catch (e) { + // not implemented + } + + // try using javascript + var metaTags = await getMetaTags(); + MetaTag? metaTagThemeColor; + + for (var metaTag in metaTags) { + if (metaTag.name == "theme-color") { + metaTagThemeColor = metaTag; + break; + } + } + + if (metaTagThemeColor == null) { + return null; + } + + var colorValue = metaTagThemeColor.content; + + themeColor = colorValue != null + ? UtilColor.fromStringRepresentation(colorValue) + : null; + + return themeColor; + } + + @override + Future getScrollX() async { + Map args = {}; + return await channel?.invokeMethod('getScrollX', args); + } + + @override + Future getScrollY() async { + Map args = {}; + return await channel?.invokeMethod('getScrollY', args); + } + + @override + Future getCertificate() async { + Map args = {}; + Map? sslCertificateMap = + (await channel?.invokeMethod('getCertificate', args)) + ?.cast(); + return SslCertificate.fromMap(sslCertificateMap); + } + + @override + Future addUserScript({required UserScript userScript}) async { + assert(webviewParams?.windowId == null); + + Map args = {}; + args.putIfAbsent('userScript', () => userScript.toMap()); + if (!(_userScripts[userScript.injectionTime]?.contains(userScript) ?? + false)) { + _userScripts[userScript.injectionTime]?.add(userScript); + await channel?.invokeMethod('addUserScript', args); + } + } + + @override + Future addUserScripts({required List userScripts}) async { + assert(webviewParams?.windowId == null); + + for (var i = 0; i < userScripts.length; i++) { + await addUserScript(userScript: userScripts[i]); + } + } + + @override + Future removeUserScript({required UserScript userScript}) async { + assert(webviewParams?.windowId == null); + + var index = _userScripts[userScript.injectionTime]?.indexOf(userScript); + if (index == null || index == -1) { + return false; + } + + _userScripts[userScript.injectionTime]?.remove(userScript); + Map args = {}; + args.putIfAbsent('userScript', () => userScript.toMap()); + args.putIfAbsent('index', () => index); + await channel?.invokeMethod('removeUserScript', args); + + return true; + } + + @override + Future removeUserScriptsByGroupName({required String groupName}) async { + assert(webviewParams?.windowId == null); + + final List userScriptsAtDocumentStart = List.from( + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] ?? []); + for (final userScript in userScriptsAtDocumentStart) { + if (userScript.groupName == groupName) { + _userScripts[userScript.injectionTime]?.remove(userScript); + } + } + + final List userScriptsAtDocumentEnd = + List.from(_userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] ?? []); + for (final userScript in userScriptsAtDocumentEnd) { + if (userScript.groupName == groupName) { + _userScripts[userScript.injectionTime]?.remove(userScript); + } + } + + Map args = {}; + args.putIfAbsent('groupName', () => groupName); + await channel?.invokeMethod('removeUserScriptsByGroupName', args); + } + + @override + Future removeUserScripts( + {required List userScripts}) async { + assert(webviewParams?.windowId == null); + + for (final userScript in userScripts) { + await removeUserScript(userScript: userScript); + } + } + + @override + Future removeAllUserScripts() async { + assert(webviewParams?.windowId == null); + + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_START]?.clear(); + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_END]?.clear(); + + Map args = {}; + await channel?.invokeMethod('removeAllUserScripts', args); + } + + @override + bool hasUserScript({required UserScript userScript}) { + return _userScripts[userScript.injectionTime]?.contains(userScript) ?? + false; + } + + @override + Future callAsyncJavaScript( + {required String functionBody, + Map arguments = const {}, + ContentWorld? contentWorld}) async { + Map args = {}; + args.putIfAbsent('functionBody', () => functionBody); + args.putIfAbsent('arguments', () => arguments); + args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); + var data = await channel?.invokeMethod('callAsyncJavaScript', args); + if (data == null) { + return null; + } + return CallAsyncJavaScriptResult( + value: data["value"], error: data["error"]); + } + + @override + Future saveWebArchive( + {required String filePath, bool autoname = false}) async { + if (!autoname) { + assert(filePath + .endsWith("." + WebArchiveFormat.WEBARCHIVE.toNativeValue())); + } + + Map args = {}; + args.putIfAbsent("filePath", () => filePath); + args.putIfAbsent("autoname", () => autoname); + return await channel?.invokeMethod('saveWebArchive', args); + } + + @override + Future isSecureContext() async { + Map args = {}; + return await channel?.invokeMethod('isSecureContext', args) ?? false; + } + + @override + Future createWebMessageChannel() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('createWebMessageChannel', args)) + ?.cast(); + final webMessageChannel = MacOSWebMessageChannel.static().fromMap(result); + if (webMessageChannel != null) { + _webMessageChannels.add(webMessageChannel); + } + return webMessageChannel; + } + + @override + Future postWebMessage( + {required WebMessage message, WebUri? targetOrigin}) async { + if (targetOrigin == null) { + targetOrigin = WebUri(''); + } + Map args = {}; + args.putIfAbsent('message', () => message.toMap()); + args.putIfAbsent('targetOrigin', () => targetOrigin.toString()); + await channel?.invokeMethod('postWebMessage', args); + } + + @override + Future addWebMessageListener( + PlatformWebMessageListener webMessageListener) async { + assert(!_webMessageListeners.contains(webMessageListener), + "${webMessageListener} was already added."); + assert( + !_webMessageListenerObjNames + .contains(webMessageListener.params.jsObjectName), + "jsObjectName ${webMessageListener.params.jsObjectName} was already added."); + _webMessageListeners.add(webMessageListener as MacOSWebMessageListener); + _webMessageListenerObjNames.add(webMessageListener.params.jsObjectName); + + Map args = {}; + args.putIfAbsent('webMessageListener', () => webMessageListener.toMap()); + await channel?.invokeMethod('addWebMessageListener', args); + } + + @override + bool hasWebMessageListener(PlatformWebMessageListener webMessageListener) { + return _webMessageListeners.contains(webMessageListener) || + _webMessageListenerObjNames + .contains(webMessageListener.params.jsObjectName); + } + + @override + Future canScrollVertically() async { + Map args = {}; + return await channel?.invokeMethod('canScrollVertically', args) ?? + false; + } + + @override + Future canScrollHorizontally() async { + Map args = {}; + return await channel?.invokeMethod('canScrollHorizontally', args) ?? + false; + } + + @override + Future reloadFromOrigin() async { + Map args = {}; + await channel?.invokeMethod('reloadFromOrigin', args); + } + + @override + Future createPdf( + {@Deprecated("Use pdfConfiguration instead") + // ignore: deprecated_member_use_from_same_package + IOSWKPDFConfiguration? iosWKPdfConfiguration, + PDFConfiguration? pdfConfiguration}) async { + Map args = {}; + args.putIfAbsent('pdfConfiguration', + () => pdfConfiguration?.toMap() ?? iosWKPdfConfiguration?.toMap()); + return await channel?.invokeMethod('createPdf', args); + } + + @override + Future createWebArchiveData() async { + Map args = {}; + return await channel?.invokeMethod('createWebArchiveData', args); + } + + @override + Future hasOnlySecureContent() async { + Map args = {}; + return await channel?.invokeMethod('hasOnlySecureContent', args) ?? + false; + } + + @override + Future pauseAllMediaPlayback() async { + Map args = {}; + return await channel?.invokeMethod('pauseAllMediaPlayback', args); + } + + @override + Future setAllMediaPlaybackSuspended({required bool suspended}) async { + Map args = {}; + args.putIfAbsent("suspended", () => suspended); + return await channel?.invokeMethod('setAllMediaPlaybackSuspended', args); + } + + @override + Future closeAllMediaPresentations() async { + Map args = {}; + return await channel?.invokeMethod('closeAllMediaPresentations', args); + } + + @override + Future requestMediaPlaybackState() async { + Map args = {}; + return MediaPlaybackState.fromNativeValue( + await channel?.invokeMethod('requestMediaPlaybackState', args)); + } + + @override + Future isInFullscreen() async { + Map args = {}; + return await channel?.invokeMethod('isInFullscreen', args) ?? false; + } + + @override + Future getCameraCaptureState() async { + Map args = {}; + return MediaCaptureState.fromNativeValue( + await channel?.invokeMethod('getCameraCaptureState', args)); + } + + @override + Future setCameraCaptureState({required MediaCaptureState state}) async { + Map args = {}; + args.putIfAbsent('state', () => state.toNativeValue()); + await channel?.invokeMethod('setCameraCaptureState', args); + } + + @override + Future getMicrophoneCaptureState() async { + Map args = {}; + return MediaCaptureState.fromNativeValue( + await channel?.invokeMethod('getMicrophoneCaptureState', args)); + } + + @override + Future setMicrophoneCaptureState( + {required MediaCaptureState state}) async { + Map args = {}; + args.putIfAbsent('state', () => state.toNativeValue()); + await channel?.invokeMethod('setMicrophoneCaptureState', args); + } + + @override + Future loadSimulatedRequest( + {required URLRequest urlRequest, + required Uint8List data, + URLResponse? urlResponse}) async { + Map args = {}; + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + args.putIfAbsent('data', () => data); + args.putIfAbsent('urlResponse', () => urlResponse?.toMap()); + await channel?.invokeMethod('loadSimulatedRequest', args); + } + + @override + Future getDefaultUserAgent() async { + Map args = {}; + return await _staticChannel.invokeMethod( + 'getDefaultUserAgent', args) ?? + ''; + } + + @override + Future handlesURLScheme(String urlScheme) async { + Map args = {}; + args.putIfAbsent('urlScheme', () => urlScheme); + return await _staticChannel.invokeMethod('handlesURLScheme', args); + } + + @override + Future disposeKeepAlive(InAppWebViewKeepAlive keepAlive) async { + Map args = {}; + args.putIfAbsent('keepAliveId', () => keepAlive.id); + await _staticChannel.invokeMethod('disposeKeepAlive', args); + _keepAliveMap[keepAlive] = null; + } + + @override + Future get tRexRunnerHtml async => await rootBundle.loadString( + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html'); + + @override + Future get tRexRunnerCss async => await rootBundle.loadString( + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css'); + + @override + dynamic getViewId() { + return id; + } + + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: !isKeepAlive); + _inAppBrowser = null; + webStorage.dispose(); + if (!isKeepAlive) { + _controllerFromPlatform = null; + _javaScriptHandlersMap.clear(); + _userScripts.clear(); + _webMessageListenerObjNames.clear(); + _injectedScriptsFromURL.clear(); + for (final webMessageChannel in _webMessageChannels) { + webMessageChannel.dispose(); + } + _webMessageChannels.clear(); + for (final webMessageListener in _webMessageListeners) { + webMessageListener.dispose(); + } + _webMessageListeners.clear(); + } + } +} + +extension InternalInAppWebViewController on MacOSInAppWebViewController { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_macos/lib/src/in_app_webview/main.dart b/flutter_inappwebview_macos/lib/src/in_app_webview/main.dart new file mode 100644 index 00000000..b83b0611 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/in_app_webview/main.dart @@ -0,0 +1,3 @@ +export 'in_app_webview_controller.dart' hide InternalInAppWebViewController; +export 'in_app_webview.dart'; +export 'headless_in_app_webview.dart' hide InternalHeadlessInAppWebView; diff --git a/flutter_inappwebview_macos/lib/src/inappwebview_platform.dart b/flutter_inappwebview_macos/lib/src/inappwebview_platform.dart new file mode 100644 index 00000000..3e13e4f5 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/inappwebview_platform.dart @@ -0,0 +1,241 @@ +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'cookie_manager.dart'; +import 'http_auth_credentials_database.dart'; +import 'find_interaction/main.dart'; +import 'in_app_browser/in_app_browser.dart'; +import 'in_app_webview/main.dart'; +import 'print_job/main.dart'; +import 'web_message/main.dart'; +import 'web_storage/main.dart'; +import 'web_authentication_session/main.dart'; + +/// Implementation of [InAppWebViewPlatform] using the WebKit API. +class MacOSInAppWebViewPlatform extends InAppWebViewPlatform { + /// Registers this class as the default instance of [InAppWebViewPlatform]. + static void registerWith() { + InAppWebViewPlatform.instance = MacOSInAppWebViewPlatform(); + } + + /// Creates a new [MacOSCookieManager]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [CookieManager] in `flutter_inappwebview` instead. + @override + MacOSCookieManager createPlatformCookieManager( + PlatformCookieManagerCreationParams params, + ) { + return MacOSCookieManager(params); + } + + /// Creates a new [MacOSInAppWebViewController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebViewController] in `flutter_inappwebview` instead. + @override + MacOSInAppWebViewController createPlatformInAppWebViewController( + PlatformInAppWebViewControllerCreationParams params, + ) { + return MacOSInAppWebViewController(params); + } + + /// Creates a new empty [MacOSInAppWebViewController] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebViewController] in `flutter_inappwebview` instead. + @override + MacOSInAppWebViewController createPlatformInAppWebViewControllerStatic() { + return MacOSInAppWebViewController.static(); + } + + // TODO: unhide when Flutter official PlatformView for macOS is available + // /// Creates a new [MacOSInAppWebViewWidget]. + // /// + // /// This function should only be called by the app-facing package. + // /// Look at using [InAppWebView] in `flutter_inappwebview` instead. + // @override + // MacOSInAppWebViewWidget createPlatformInAppWebViewWidget( + // PlatformInAppWebViewWidgetCreationParams params, + // ) { + // return MacOSInAppWebViewWidget(params); + // } + + /// Creates a new [MacOSFindInteractionController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [FindInteractionController] in `flutter_inappwebview` instead. + @override + MacOSFindInteractionController createPlatformFindInteractionController( + PlatformFindInteractionControllerCreationParams params, + ) { + return MacOSFindInteractionController(params); + } + + /// Creates a new [MacOSPrintJobController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PrintJobController] in `flutter_inappwebview` instead. + @override + MacOSPrintJobController createPlatformPrintJobController( + PlatformPrintJobControllerCreationParams params, + ) { + return MacOSPrintJobController(params); + } + + /// Creates a new [MacOSWebMessageChannel]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessageChannel] in `flutter_inappwebview` instead. + @override + MacOSWebMessageChannel createPlatformWebMessageChannel( + PlatformWebMessageChannelCreationParams params, + ) { + return MacOSWebMessageChannel(params); + } + + /// Creates a new empty [MacOSWebMessageChannel] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessageChannel] in `flutter_inappwebview` instead. + @override + MacOSWebMessageChannel createPlatformWebMessageChannelStatic() { + return MacOSWebMessageChannel.static(); + } + + /// Creates a new [MacOSWebMessageListener]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessageListener] in `flutter_inappwebview` instead. + @override + MacOSWebMessageListener createPlatformWebMessageListener( + PlatformWebMessageListenerCreationParams params, + ) { + return MacOSWebMessageListener(params); + } + + /// Creates a new [MacOSJavaScriptReplyProxy]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [JavaScriptReplyProxy] in `flutter_inappwebview` instead. + @override + MacOSJavaScriptReplyProxy createPlatformJavaScriptReplyProxy( + PlatformJavaScriptReplyProxyCreationParams params, + ) { + return MacOSJavaScriptReplyProxy(params); + } + + /// Creates a new [MacOSWebMessagePort]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebMessagePort] in `flutter_inappwebview` instead. + @override + MacOSWebMessagePort createPlatformWebMessagePort( + PlatformWebMessagePortCreationParams params, + ) { + return MacOSWebMessagePort(params); + } + + /// Creates a new [MacOSWebStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [MacOSWebStorage] in `flutter_inappwebview` instead. + @override + MacOSWebStorage createPlatformWebStorage( + PlatformWebStorageCreationParams params, + ) { + return MacOSWebStorage(params); + } + + /// Creates a new [MacOSLocalStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [MacOSLocalStorage] in `flutter_inappwebview` instead. + @override + MacOSLocalStorage createPlatformLocalStorage( + PlatformLocalStorageCreationParams params, + ) { + return MacOSLocalStorage(params); + } + + /// Creates a new [MacOSSessionStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PlatformSessionStorage] in `flutter_inappwebview` instead. + @override + MacOSSessionStorage createPlatformSessionStorage( + PlatformSessionStorageCreationParams params, + ) { + return MacOSSessionStorage(params); + } + + /// Creates a new [MacOSHeadlessInAppWebView]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [HeadlessInAppWebView] in `flutter_inappwebview` instead. + @override + MacOSHeadlessInAppWebView createPlatformHeadlessInAppWebView( + PlatformHeadlessInAppWebViewCreationParams params, + ) { + return MacOSHeadlessInAppWebView(params); + } + + /// Creates a new [MacOSHttpAuthCredentialDatabase]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [HttpAuthCredentialDatabase] in `flutter_inappwebview` instead. + @override + MacOSHttpAuthCredentialDatabase createPlatformHttpAuthCredentialDatabase( + PlatformHttpAuthCredentialDatabaseCreationParams params, + ) { + return MacOSHttpAuthCredentialDatabase(params); + } + + /// Creates a new [MacOSInAppBrowser]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppBrowser] in `flutter_inappwebview` instead. + @override + MacOSInAppBrowser createPlatformInAppBrowser( + PlatformInAppBrowserCreationParams params, + ) { + return MacOSInAppBrowser(params); + } + + /// Creates a new empty [MacOSInAppBrowser] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppBrowser] in `flutter_inappwebview` instead. + @override + MacOSInAppBrowser createPlatformInAppBrowserStatic() { + return MacOSInAppBrowser.static(); + } + + /// Creates a new empty [MacOSWebStorageManager] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebStorageManager] in `flutter_inappwebview` instead. + @override + MacOSWebStorageManager createPlatformWebStorageManager( + PlatformWebStorageManagerCreationParams params) { + return MacOSWebStorageManager(params); + } + + /// Creates a new [MacOSWebAuthenticationSession]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebAuthenticationSession] in `flutter_inappwebview` instead. + @override + MacOSWebAuthenticationSession createPlatformWebAuthenticationSession( + PlatformWebAuthenticationSessionCreationParams params) { + return MacOSWebAuthenticationSession(params); + } + + /// Creates a new empty [MacOSWebAuthenticationSession] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebAuthenticationSession] in `flutter_inappwebview` instead. + @override + MacOSWebAuthenticationSession createPlatformWebAuthenticationSessionStatic() { + return MacOSWebAuthenticationSession.static(); + } +} diff --git a/flutter_inappwebview_macos/lib/src/main.dart b/flutter_inappwebview_macos/lib/src/main.dart new file mode 100644 index 00000000..e58414ae --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/main.dart @@ -0,0 +1,11 @@ +export 'inappwebview_platform.dart'; +export 'in_app_webview/main.dart'; +export 'in_app_browser/main.dart'; +export 'web_storage/main.dart'; +export 'cookie_manager.dart' hide InternalCookieManager; +export 'http_auth_credentials_database.dart' + hide InternalHttpAuthCredentialDatabase; +export 'web_message/main.dart'; +export 'print_job/main.dart'; +export 'find_interaction/main.dart'; +export 'web_authentication_session/main.dart'; diff --git a/flutter_inappwebview_macos/lib/src/platform_util.dart b/flutter_inappwebview_macos/lib/src/platform_util.dart new file mode 100644 index 00000000..27e96478 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/platform_util.dart @@ -0,0 +1,64 @@ +import 'package:flutter/services.dart'; + +///Platform native utilities +class PlatformUtil { + static PlatformUtil? _instance; + static const MethodChannel _channel = + MethodChannel('com.pichillilorenzo/flutter_inappwebview_platformutil'); + + PlatformUtil._(); + + ///Get [PlatformUtil] instance. + static PlatformUtil instance() { + return (_instance != null) ? _instance! : _init(); + } + + static PlatformUtil _init() { + _channel.setMethodCallHandler((call) async { + try { + return await _handleMethod(call); + } on Error catch (e) { + print(e); + print(e.stackTrace); + } + }); + _instance = PlatformUtil._(); + return _instance!; + } + + static Future _handleMethod(MethodCall call) async {} + + String? _cachedSystemVersion; + + ///Get current platform system version. + Future getSystemVersion() async { + if (_cachedSystemVersion != null) { + return _cachedSystemVersion!; + } + Map args = {}; + _cachedSystemVersion = + await _channel.invokeMethod('getSystemVersion', args); + return _cachedSystemVersion!; + } + + ///Format date. + Future formatDate( + {required DateTime date, + required String format, + String locale = "en_US", + String timezone = "UTC"}) async { + Map args = {}; + args.putIfAbsent('date', () => date.millisecondsSinceEpoch); + args.putIfAbsent('format', () => format); + args.putIfAbsent('locale', () => locale); + args.putIfAbsent('timezone', () => timezone); + return await _channel.invokeMethod('formatDate', args); + } + + ///Get cookie expiration date used by Web platform. + Future getWebCookieExpirationDate({required DateTime date}) async { + Map args = {}; + args.putIfAbsent('date', () => date.millisecondsSinceEpoch); + return await _channel.invokeMethod('getWebCookieExpirationDate', args); + } +} diff --git a/flutter_inappwebview_macos/lib/src/print_job/main.dart b/flutter_inappwebview_macos/lib/src/print_job/main.dart new file mode 100644 index 00000000..4e70ad94 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/print_job/main.dart @@ -0,0 +1 @@ +export 'print_job_controller.dart'; diff --git a/flutter_inappwebview_macos/lib/src/print_job/print_job_controller.dart b/flutter_inappwebview_macos/lib/src/print_job/print_job_controller.dart new file mode 100644 index 00000000..b1a95ce2 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/print_job/print_job_controller.dart @@ -0,0 +1,72 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [MacOSPrintJobController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformPrintJobControllerCreationParams] for +/// more information. +@immutable +class MacOSPrintJobControllerCreationParams + extends PlatformPrintJobControllerCreationParams { + /// Creates a new [MacOSPrintJobControllerCreationParams] instance. + const MacOSPrintJobControllerCreationParams( + {required super.id, super.onComplete}); + + /// Creates a [MacOSPrintJobControllerCreationParams] instance based on [PlatformPrintJobControllerCreationParams]. + factory MacOSPrintJobControllerCreationParams.fromPlatformPrintJobControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformPrintJobControllerCreationParams params) { + return MacOSPrintJobControllerCreationParams( + id: params.id, onComplete: params.onComplete); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformPrintJobController} +class MacOSPrintJobController extends PlatformPrintJobController + with ChannelController { + /// Constructs a [MacOSPrintJobController]. + MacOSPrintJobController(PlatformPrintJobControllerCreationParams params) + : super.implementation( + params is MacOSPrintJobControllerCreationParams + ? params + : MacOSPrintJobControllerCreationParams + .fromPlatformPrintJobControllerCreationParams(params), + ) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_printjobcontroller_${params.id}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onComplete": + bool completed = call.arguments["completed"]; + String? error = call.arguments["error"]; + if (params.onComplete != null) { + params.onComplete!(completed, error); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + @override + Future getInfo() async { + Map args = {}; + Map? infoMap = + (await channel?.invokeMethod('getInfo', args))?.cast(); + return PrintJobInfo.fromMap(infoMap); + } + + @override + Future dispose() async { + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + } +} diff --git a/flutter_inappwebview_macos/lib/src/web_authentication_session/main.dart b/flutter_inappwebview_macos/lib/src/web_authentication_session/main.dart new file mode 100644 index 00000000..37ca0c24 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/web_authentication_session/main.dart @@ -0,0 +1 @@ +export 'web_authenticate_session.dart'; diff --git a/flutter_inappwebview_macos/lib/src/web_authentication_session/web_authenticate_session.dart b/flutter_inappwebview_macos/lib/src/web_authentication_session/web_authenticate_session.dart new file mode 100755 index 00000000..33a59daa --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/web_authentication_session/web_authenticate_session.dart @@ -0,0 +1,163 @@ +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [MacOSWebAuthenticationSession]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebAuthenticationSessionCreationParams] for +/// more information. +class MacOSWebAuthenticationSessionCreationParams + extends PlatformWebAuthenticationSessionCreationParams { + /// Creates a new [MacOSWebAuthenticationSessionCreationParams] instance. + const MacOSWebAuthenticationSessionCreationParams(); + + /// Creates a [MacOSWebAuthenticationSessionCreationParams] instance based on [PlatformWebAuthenticationSessionCreationParams]. + factory MacOSWebAuthenticationSessionCreationParams.fromPlatformWebAuthenticationSessionCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebAuthenticationSessionCreationParams params) { + return MacOSWebAuthenticationSessionCreationParams(); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebAuthenticationSession} +class MacOSWebAuthenticationSession extends PlatformWebAuthenticationSession + with ChannelController { + /// Constructs a [MacOSWebAuthenticationSession]. + MacOSWebAuthenticationSession( + PlatformWebAuthenticationSessionCreationParams params) + : super.implementation( + params is MacOSWebAuthenticationSessionCreationParams + ? params + : MacOSWebAuthenticationSessionCreationParams + .fromPlatformWebAuthenticationSessionCreationParams(params), + ); + + static final MacOSWebAuthenticationSession _staticValue = + MacOSWebAuthenticationSession(MacOSWebAuthenticationSessionCreationParams()); + + /// Provide static access. + factory MacOSWebAuthenticationSession.static() { + return _staticValue; + } + + @override + final String id = IdGenerator.generate(); + + @override + late final WebUri url; + + @override + late final String? callbackURLScheme; + + @override + late final WebAuthenticationSessionSettings? initialSettings; + + @override + late final WebAuthenticationSessionCompletionHandler onComplete; + + static const MethodChannel _staticChannel = const MethodChannel( + 'com.pichillilorenzo/flutter_webauthenticationsession'); + + @override + Future create( + {required WebUri url, + String? callbackURLScheme, + WebAuthenticationSessionCompletionHandler onComplete, + WebAuthenticationSessionSettings? initialSettings}) async { + var session = MacOSWebAuthenticationSession._create( + url: url, + callbackURLScheme: callbackURLScheme, + onComplete: onComplete, + initialSettings: initialSettings); + initialSettings = + session.initialSettings ?? WebAuthenticationSessionSettings(); + Map args = {}; + args.putIfAbsent("id", () => session.id); + args.putIfAbsent("url", () => session.url.toString()); + args.putIfAbsent("callbackURLScheme", () => session.callbackURLScheme); + args.putIfAbsent("initialSettings", () => initialSettings?.toMap()); + await _staticChannel.invokeMethod('create', args); + return session; + } + + MacOSWebAuthenticationSession._create( + {required this.url, + this.callbackURLScheme, + this.onComplete, + WebAuthenticationSessionSettings? initialSettings}) + : super.implementation(MacOSWebAuthenticationSessionCreationParams()) { + assert(url.toString().isNotEmpty); + assert(['http', 'https'].contains(url.scheme), + 'The specified URL has an unsupported scheme. Only HTTP and HTTPS URLs are supported on iOS.'); + + this.initialSettings = + initialSettings ?? WebAuthenticationSessionSettings(); + channel = MethodChannel( + 'com.pichillilorenzo/flutter_webauthenticationsession_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + debugLoggingSettings: + PlatformWebAuthenticationSession.debugLoggingSettings, + id: id, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + _debugLog(call.method, call.arguments); + + switch (call.method) { + case "onComplete": + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + var error = WebAuthenticationSessionError.fromNativeValue( + call.arguments["errorCode"]); + if (onComplete != null) { + onComplete!(uri, error); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + @override + Future canStart() async { + Map args = {}; + return await channel?.invokeMethod('canStart', args) ?? false; + } + + @override + Future start() async { + Map args = {}; + return await channel?.invokeMethod('start', args) ?? false; + } + + @override + Future cancel() async { + Map args = {}; + await channel?.invokeMethod("cancel", args); + } + + @override + Future dispose() async { + Map args = {}; + await channel?.invokeMethod("dispose", args); + disposeChannel(); + } + + @override + Future isAvailable() async { + Map args = {}; + return await _staticChannel.invokeMethod("isAvailable", args) ?? + false; + } +} diff --git a/flutter_inappwebview_macos/lib/src/web_message/main.dart b/flutter_inappwebview_macos/lib/src/web_message/main.dart new file mode 100644 index 00000000..d41e30c7 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/web_message/main.dart @@ -0,0 +1,3 @@ +export 'web_message_port.dart' hide InternalWebMessagePort; +export 'web_message_channel.dart' hide InternalWebMessageChannel; +export 'web_message_listener.dart'; diff --git a/flutter_inappwebview_macos/lib/src/web_message/web_message_channel.dart b/flutter_inappwebview_macos/lib/src/web_message/web_message_channel.dart new file mode 100644 index 00000000..80e16008 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/web_message/web_message_channel.dart @@ -0,0 +1,120 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'web_message_port.dart'; + +/// Object specifying creation parameters for creating a [MacOSWebMessageChannel]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessageChannelCreationParams] for +/// more information. +@immutable +class MacOSWebMessageChannelCreationParams + extends PlatformWebMessageChannelCreationParams { + /// Creates a new [MacOSWebMessageChannelCreationParams] instance. + const MacOSWebMessageChannelCreationParams( + {required super.id, required super.port1, required super.port2}); + + /// Creates a [MacOSWebMessageChannelCreationParams] instance based on [PlatformWebMessageChannelCreationParams]. + factory MacOSWebMessageChannelCreationParams.fromPlatformWebMessageChannelCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessageChannelCreationParams params) { + return MacOSWebMessageChannelCreationParams( + id: params.id, port1: params.port1, port2: params.port2); + } + + @override + String toString() { + return 'MacOSWebMessageChannelCreationParams{id: $id, port1: $port1, port2: $port2}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageChannel} +class MacOSWebMessageChannel extends PlatformWebMessageChannel + with ChannelController { + /// Constructs a [MacOSWebMessageChannel]. + MacOSWebMessageChannel(PlatformWebMessageChannelCreationParams params) + : super.implementation( + params is MacOSWebMessageChannelCreationParams + ? params + : MacOSWebMessageChannelCreationParams + .fromPlatformWebMessageChannelCreationParams(params), + ) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_web_message_channel_${params.id}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + static final MacOSWebMessageChannel _staticValue = MacOSWebMessageChannel( + MacOSWebMessageChannelCreationParams( + id: '', + port1: MacOSWebMessagePort( + MacOSWebMessagePortCreationParams(index: 0)), + port2: MacOSWebMessagePort( + MacOSWebMessagePortCreationParams(index: 1)))); + + /// Provide static access. + factory MacOSWebMessageChannel.static() { + return _staticValue; + } + + MacOSWebMessagePort get _macosPort1 => port1 as MacOSWebMessagePort; + + MacOSWebMessagePort get _macosPort2 => port2 as MacOSWebMessagePort; + + static MacOSWebMessageChannel? _fromMap(Map? map) { + if (map == null) { + return null; + } + var webMessageChannel = MacOSWebMessageChannel( + MacOSWebMessageChannelCreationParams( + id: map["id"], + port1: MacOSWebMessagePort( + MacOSWebMessagePortCreationParams(index: 0)), + port2: MacOSWebMessagePort( + MacOSWebMessagePortCreationParams(index: 1)))); + webMessageChannel._macosPort1.webMessageChannel = webMessageChannel; + webMessageChannel._macosPort2.webMessageChannel = webMessageChannel; + return webMessageChannel; + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onMessage": + int index = call.arguments["index"]; + var port = index == 0 ? _macosPort1 : _macosPort2; + if (port.onMessage != null) { + WebMessage? message = call.arguments["message"] != null + ? WebMessage.fromMap( + call.arguments["message"].cast()) + : null; + port.onMessage!(message); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + MacOSWebMessageChannel? fromMap(Map? map) { + return _fromMap(map); + } + + @override + void dispose() { + disposeChannel(); + } + + @override + String toString() { + return 'MacOSWebMessageChannel{id: $id, port1: $port1, port2: $port2}'; + } +} + +extension InternalWebMessageChannel on MacOSWebMessageChannel { + MethodChannel? get internalChannel => channel; +} diff --git a/flutter_inappwebview_macos/lib/src/web_message/web_message_listener.dart b/flutter_inappwebview_macos/lib/src/web_message/web_message_listener.dart new file mode 100644 index 00000000..b2791eba --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/web_message/web_message_listener.dart @@ -0,0 +1,164 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [MacOSWebMessageListener]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessageListenerCreationParams] for +/// more information. +@immutable +class MacOSWebMessageListenerCreationParams + extends PlatformWebMessageListenerCreationParams { + /// Creates a new [MacOSWebMessageListenerCreationParams] instance. + const MacOSWebMessageListenerCreationParams( + {required this.allowedOriginRules, + required super.jsObjectName, + super.onPostMessage}); + + /// Creates a [MacOSWebMessageListenerCreationParams] instance based on [PlatformWebMessageListenerCreationParams]. + factory MacOSWebMessageListenerCreationParams.fromPlatformWebMessageListenerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessageListenerCreationParams params) { + return MacOSWebMessageListenerCreationParams( + allowedOriginRules: params.allowedOriginRules ?? Set.from(["*"]), + jsObjectName: params.jsObjectName, + onPostMessage: params.onPostMessage); + } + + @override + final Set allowedOriginRules; + + @override + String toString() { + return 'MacOSWebMessageListenerCreationParams{jsObjectName: $jsObjectName, allowedOriginRules: $allowedOriginRules, onPostMessage: $onPostMessage}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageListener} +class MacOSWebMessageListener extends PlatformWebMessageListener + with ChannelController { + /// Constructs a [MacOSWebMessageListener]. + MacOSWebMessageListener(PlatformWebMessageListenerCreationParams params) + : super.implementation( + params is MacOSWebMessageListenerCreationParams + ? params + : MacOSWebMessageListenerCreationParams + .fromPlatformWebMessageListenerCreationParams(params), + ) { + assert(!this._macosParams.allowedOriginRules.contains(""), + "allowedOriginRules cannot contain empty strings"); + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_web_message_listener_${_id}_${params.jsObjectName}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + ///Message Listener ID used internally. + final String _id = IdGenerator.generate(); + + MacOSJavaScriptReplyProxy? _replyProxy; + + MacOSWebMessageListenerCreationParams get _macosParams => + params as MacOSWebMessageListenerCreationParams; + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onPostMessage": + if (_replyProxy == null) { + _replyProxy = MacOSJavaScriptReplyProxy( + PlatformJavaScriptReplyProxyCreationParams( + webMessageListener: this)); + } + if (onPostMessage != null) { + WebMessage? message = call.arguments["message"] != null + ? WebMessage.fromMap( + call.arguments["message"].cast()) + : null; + WebUri? sourceOrigin = call.arguments["sourceOrigin"] != null + ? WebUri(call.arguments["sourceOrigin"]) + : null; + bool isMainFrame = call.arguments["isMainFrame"]; + onPostMessage!(message, sourceOrigin, isMainFrame, _replyProxy!); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + void dispose() { + disposeChannel(); + } + + @override + Map toMap() { + return { + "id": _id, + "jsObjectName": params.jsObjectName, + "allowedOriginRules": _macosParams.allowedOriginRules.toList(), + }; + } + + @override + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return 'MacOSWebMessageListener{id: ${_id}, jsObjectName: ${params.jsObjectName}, allowedOriginRules: ${params.allowedOriginRules}, replyProxy: $_replyProxy}'; + } +} + +/// Object specifying creation parameters for creating a [MacOSJavaScriptReplyProxy]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformJavaScriptReplyProxyCreationParams] for +/// more information. +@immutable +class MacOSJavaScriptReplyProxyCreationParams + extends PlatformJavaScriptReplyProxyCreationParams { + /// Creates a new [MacOSJavaScriptReplyProxyCreationParams] instance. + const MacOSJavaScriptReplyProxyCreationParams( + {required super.webMessageListener}); + + /// Creates a [MacOSJavaScriptReplyProxyCreationParams] instance based on [PlatformJavaScriptReplyProxyCreationParams]. + factory MacOSJavaScriptReplyProxyCreationParams.fromPlatformJavaScriptReplyProxyCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformJavaScriptReplyProxyCreationParams params) { + return MacOSJavaScriptReplyProxyCreationParams( + webMessageListener: params.webMessageListener); + } +} + +///{@macro flutter_inappwebview_platform_interface.JavaScriptReplyProxy} +class MacOSJavaScriptReplyProxy extends PlatformJavaScriptReplyProxy { + /// Constructs a [MacOSWebMessageListener]. + MacOSJavaScriptReplyProxy(PlatformJavaScriptReplyProxyCreationParams params) + : super.implementation( + params is MacOSJavaScriptReplyProxyCreationParams + ? params + : MacOSJavaScriptReplyProxyCreationParams + .fromPlatformJavaScriptReplyProxyCreationParams(params), + ); + + MacOSWebMessageListener get _macosWebMessageListener => + params.webMessageListener as MacOSWebMessageListener; + + @override + Future postMessage(WebMessage message) async { + Map args = {}; + args.putIfAbsent('message', () => message.toMap()); + await _macosWebMessageListener.channel?.invokeMethod('postMessage', args); + } + + @override + String toString() { + return 'MacOSJavaScriptReplyProxy{}'; + } +} diff --git a/flutter_inappwebview_macos/lib/src/web_message/web_message_port.dart b/flutter_inappwebview_macos/lib/src/web_message/web_message_port.dart new file mode 100644 index 00000000..c0682298 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/web_message/web_message_port.dart @@ -0,0 +1,95 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'web_message_channel.dart'; + +/// Object specifying creation parameters for creating a [MacOSWebMessagePort]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessagePortCreationParams] for +/// more information. +@immutable +class MacOSWebMessagePortCreationParams + extends PlatformWebMessagePortCreationParams { + /// Creates a new [MacOSWebMessagePortCreationParams] instance. + const MacOSWebMessagePortCreationParams({required super.index}); + + /// Creates a [MacOSWebMessagePortCreationParams] instance based on [PlatformWebMessagePortCreationParams]. + factory MacOSWebMessagePortCreationParams.fromPlatformWebMessagePortCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessagePortCreationParams params) { + return MacOSWebMessagePortCreationParams(index: params.index); + } + + @override + String toString() { + return 'MacOSWebMessagePortCreationParams{index: $index}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessagePort} +class MacOSWebMessagePort extends PlatformWebMessagePort { + WebMessageCallback? _onMessage; + late MacOSWebMessageChannel _webMessageChannel; + + /// Constructs a [MacOSWebMessagePort]. + MacOSWebMessagePort(PlatformWebMessagePortCreationParams params) + : super.implementation( + params is MacOSWebMessagePortCreationParams + ? params + : MacOSWebMessagePortCreationParams + .fromPlatformWebMessagePortCreationParams(params), + ); + + @override + Future setWebMessageCallback(WebMessageCallback? onMessage) async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + await _webMessageChannel.internalChannel + ?.invokeMethod('setWebMessageCallback', args); + this._onMessage = onMessage; + } + + @override + Future postMessage(WebMessage message) async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + args.putIfAbsent('message', () => message.toMap()); + await _webMessageChannel.internalChannel?.invokeMethod('postMessage', args); + } + + @override + Future close() async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + await _webMessageChannel.internalChannel?.invokeMethod('close', args); + } + + @override + Map toMap() { + return { + "index": params.index, + "webMessageChannelId": this._webMessageChannel.params.id + }; + } + + @override + Map toJson() { + return toMap(); + } + + @override + String toString() { + return 'MacOSWebMessagePort{index: ${params.index}}'; + } +} + +extension InternalWebMessagePort on MacOSWebMessagePort { + WebMessageCallback? get onMessage => _onMessage; + void set onMessage(WebMessageCallback? value) => _onMessage = value; + + MacOSWebMessageChannel get webMessageChannel => _webMessageChannel; + void set webMessageChannel(MacOSWebMessageChannel value) => + _webMessageChannel = value; +} diff --git a/flutter_inappwebview_macos/lib/src/web_storage/main.dart b/flutter_inappwebview_macos/lib/src/web_storage/main.dart new file mode 100644 index 00000000..7265ae52 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/web_storage/main.dart @@ -0,0 +1,2 @@ +export 'web_storage.dart'; +export 'web_storage_manager.dart' hide InternalWebStorageManager; diff --git a/flutter_inappwebview_macos/lib/src/web_storage/web_storage.dart b/flutter_inappwebview_macos/lib/src/web_storage/web_storage.dart new file mode 100644 index 00000000..25299efb --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/web_storage/web_storage.dart @@ -0,0 +1,257 @@ +import 'dart:convert'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../in_app_webview/in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [MacOSWebStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebStorageCreationParams] for +/// more information. +class MacOSWebStorageCreationParams extends PlatformWebStorageCreationParams { + /// Creates a new [MacOSWebStorageCreationParams] instance. + MacOSWebStorageCreationParams( + {required super.localStorage, required super.sessionStorage}); + + /// Creates a [MacOSWebStorageCreationParams] instance based on [PlatformWebStorageCreationParams]. + factory MacOSWebStorageCreationParams.fromPlatformWebStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebStorageCreationParams params) { + return MacOSWebStorageCreationParams( + localStorage: params.localStorage, + sessionStorage: params.sessionStorage); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebStorage} +class MacOSWebStorage extends PlatformWebStorage { + /// Constructs a [MacOSWebStorage]. + MacOSWebStorage(PlatformWebStorageCreationParams params) + : super.implementation( + params is MacOSWebStorageCreationParams + ? params + : MacOSWebStorageCreationParams + .fromPlatformWebStorageCreationParams(params), + ); + + @override + PlatformLocalStorage get localStorage => params.localStorage; + + @override + PlatformSessionStorage get sessionStorage => params.sessionStorage; + + @override + void dispose() { + localStorage.dispose(); + sessionStorage.dispose(); + } +} + +/// Object specifying creation parameters for creating a [MacOSStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformStorageCreationParams] for +/// more information. +class MacOSStorageCreationParams extends PlatformStorageCreationParams { + /// Creates a new [MacOSStorageCreationParams] instance. + MacOSStorageCreationParams( + {required super.controller, required super.webStorageType}); + + /// Creates a [MacOSStorageCreationParams] instance based on [PlatformStorageCreationParams]. + factory MacOSStorageCreationParams.fromPlatformStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformStorageCreationParams params) { + return MacOSStorageCreationParams( + controller: params.controller, webStorageType: params.webStorageType); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformStorage} +abstract class MacOSStorage implements PlatformStorage { + @override + MacOSInAppWebViewController? controller; + + @override + Future length() async { + var result = await controller?.evaluateJavascript(source: """ + window.$webStorageType.length; + """); + return result != null ? int.parse(json.decode(result)) : null; + } + + @override + Future setItem({required String key, required dynamic value}) async { + var encodedValue = json.encode(value); + await controller?.evaluateJavascript(source: """ + window.$webStorageType.setItem("$key", ${value is String ? encodedValue : "JSON.stringify($encodedValue)"}); + """); + } + + @override + Future getItem({required String key}) async { + var itemValue = await controller?.evaluateJavascript(source: """ + window.$webStorageType.getItem("$key"); + """); + + if (itemValue == null) { + return null; + } + + try { + return json.decode(itemValue); + } catch (e) {} + + return itemValue; + } + + @override + Future removeItem({required String key}) async { + await controller?.evaluateJavascript(source: """ + window.$webStorageType.removeItem("$key"); + """); + } + + @override + Future> getItems() async { + var webStorageItems = []; + + List>? items = + (await controller?.evaluateJavascript(source: """ +(function() { + var webStorageItems = []; + for(var i = 0; i < window.$webStorageType.length; i++){ + var key = window.$webStorageType.key(i); + webStorageItems.push( + { + key: key, + value: window.$webStorageType.getItem(key) + } + ); + } + return webStorageItems; +})(); + """))?.cast>(); + + if (items == null) { + return webStorageItems; + } + + for (var item in items) { + webStorageItems + .add(WebStorageItem(key: item["key"], value: item["value"])); + } + + return webStorageItems; + } + + @override + Future clear() async { + await controller?.evaluateJavascript(source: """ + window.$webStorageType.clear(); + """); + } + + @override + Future key({required int index}) async { + var result = await controller?.evaluateJavascript(source: """ + window.$webStorageType.key($index); + """); + return result != null ? json.decode(result) : null; + } + + @override + void dispose() { + controller = null; + } +} + +/// Object specifying creation parameters for creating a [MacOSLocalStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformLocalStorageCreationParams] for +/// more information. +class MacOSLocalStorageCreationParams + extends PlatformLocalStorageCreationParams { + /// Creates a new [MacOSLocalStorageCreationParams] instance. + MacOSLocalStorageCreationParams(super.params); + + /// Creates a [MacOSLocalStorageCreationParams] instance based on [PlatformLocalStorageCreationParams]. + factory MacOSLocalStorageCreationParams.fromPlatformLocalStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformLocalStorageCreationParams params) { + return MacOSLocalStorageCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformLocalStorage} +class MacOSLocalStorage extends PlatformLocalStorage with MacOSStorage { + /// Constructs a [MacOSLocalStorage]. + MacOSLocalStorage(PlatformLocalStorageCreationParams params) + : super.implementation( + params is MacOSLocalStorageCreationParams + ? params + : MacOSLocalStorageCreationParams + .fromPlatformLocalStorageCreationParams(params), + ); + + /// Default storage + factory MacOSLocalStorage.defaultStorage( + {required PlatformInAppWebViewController? controller}) { + return MacOSLocalStorage(MacOSLocalStorageCreationParams( + PlatformLocalStorageCreationParams(PlatformStorageCreationParams( + controller: controller, + webStorageType: WebStorageType.LOCAL_STORAGE)))); + } + + @override + MacOSInAppWebViewController? get controller => + params.controller as MacOSInAppWebViewController?; +} + +/// Object specifying creation parameters for creating a [MacOSSessionStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformSessionStorageCreationParams] for +/// more information. +class MacOSSessionStorageCreationParams + extends PlatformSessionStorageCreationParams { + /// Creates a new [MacOSSessionStorageCreationParams] instance. + MacOSSessionStorageCreationParams(super.params); + + /// Creates a [MacOSSessionStorageCreationParams] instance based on [PlatformSessionStorageCreationParams]. + factory MacOSSessionStorageCreationParams.fromPlatformSessionStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformSessionStorageCreationParams params) { + return MacOSSessionStorageCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformSessionStorage} +class MacOSSessionStorage extends PlatformSessionStorage with MacOSStorage { + /// Constructs a [MacOSSessionStorage]. + MacOSSessionStorage(PlatformSessionStorageCreationParams params) + : super.implementation( + params is MacOSSessionStorageCreationParams + ? params + : MacOSSessionStorageCreationParams + .fromPlatformSessionStorageCreationParams(params), + ); + + /// Default storage + factory MacOSSessionStorage.defaultStorage( + {required PlatformInAppWebViewController? controller}) { + return MacOSSessionStorage(MacOSSessionStorageCreationParams( + PlatformSessionStorageCreationParams(PlatformStorageCreationParams( + controller: controller, + webStorageType: WebStorageType.SESSION_STORAGE)))); + } + + @override + MacOSInAppWebViewController? get controller => + params.controller as MacOSInAppWebViewController?; +} diff --git a/flutter_inappwebview_macos/lib/src/web_storage/web_storage_manager.dart b/flutter_inappwebview_macos/lib/src/web_storage/web_storage_manager.dart new file mode 100755 index 00000000..b281f0d7 --- /dev/null +++ b/flutter_inappwebview_macos/lib/src/web_storage/web_storage_manager.dart @@ -0,0 +1,134 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [MacOSWebStorageManager]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebStorageManagerCreationParams] for +/// more information. +@immutable +class MacOSWebStorageManagerCreationParams + extends PlatformWebStorageManagerCreationParams { + /// Creates a new [MacOSWebStorageManagerCreationParams] instance. + const MacOSWebStorageManagerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformWebStorageManagerCreationParams params, + ) : super(); + + /// Creates a [MacOSWebStorageManagerCreationParams] instance based on [PlatformWebStorageManagerCreationParams]. + factory MacOSWebStorageManagerCreationParams.fromPlatformWebStorageManagerCreationParams( + PlatformWebStorageManagerCreationParams params) { + return MacOSWebStorageManagerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebStorageManager} +class MacOSWebStorageManager extends PlatformWebStorageManager + with ChannelController { + /// Creates a new [MacOSWebStorageManager]. + MacOSWebStorageManager(PlatformWebStorageManagerCreationParams params) + : super.implementation( + params is MacOSWebStorageManagerCreationParams + ? params + : MacOSWebStorageManagerCreationParams + .fromPlatformWebStorageManagerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_webstoragemanager'); + handler = handleMethod; + initMethodCallHandler(); + } + + static MacOSWebStorageManager? _instance; + + ///Gets the WebStorage manager shared instance. + static MacOSWebStorageManager instance() { + return (_instance != null) ? _instance! : _init(); + } + + static MacOSWebStorageManager _init() { + _instance = MacOSWebStorageManager(MacOSWebStorageManagerCreationParams( + const PlatformWebStorageManagerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future> fetchDataRecords( + {required Set dataTypes}) async { + List recordList = []; + List dataTypesList = []; + for (var dataType in dataTypes) { + dataTypesList.add(dataType.toNativeValue()); + } + Map args = {}; + args.putIfAbsent("dataTypes", () => dataTypesList); + List> records = + (await channel?.invokeMethod('fetchDataRecords', args)) + ?.cast>() ?? + []; + for (var record in records) { + List dataTypesString = record["dataTypes"].cast(); + Set dataTypes = Set(); + for (var dataTypeValue in dataTypesString) { + var dataType = WebsiteDataType.fromNativeValue(dataTypeValue); + if (dataType != null) { + dataTypes.add(dataType); + } + } + recordList.add(WebsiteDataRecord( + displayName: record["displayName"], dataTypes: dataTypes)); + } + return recordList; + } + + @override + Future removeDataFor( + {required Set dataTypes, + required List dataRecords}) async { + List dataTypesList = []; + for (var dataType in dataTypes) { + dataTypesList.add(dataType.toNativeValue()); + } + + List> recordList = []; + for (var record in dataRecords) { + recordList.add(record.toMap()); + } + + Map args = {}; + args.putIfAbsent("dataTypes", () => dataTypesList); + args.putIfAbsent("recordList", () => recordList); + await channel?.invokeMethod('removeDataFor', args); + } + + @override + Future removeDataModifiedSince( + {required Set dataTypes, required DateTime date}) async { + List dataTypesList = []; + for (var dataType in dataTypes) { + dataTypesList.add(dataType.toNativeValue()); + } + + var timestamp = date.millisecondsSinceEpoch; + + Map args = {}; + args.putIfAbsent("dataTypes", () => dataTypesList); + args.putIfAbsent("timestamp", () => timestamp); + await channel?.invokeMethod('removeDataModifiedSince', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalWebStorageManager on MacOSWebStorageManager { + get handleMethod => _handleMethod; +} diff --git a/macos/Classes/CredentialDatabase.swift b/flutter_inappwebview_macos/macos/Classes/CredentialDatabase.swift similarity index 100% rename from macos/Classes/CredentialDatabase.swift rename to flutter_inappwebview_macos/macos/Classes/CredentialDatabase.swift diff --git a/macos/Classes/FindInteraction/FindInteractionChannelDelegate.swift b/flutter_inappwebview_macos/macos/Classes/FindInteraction/FindInteractionChannelDelegate.swift similarity index 100% rename from macos/Classes/FindInteraction/FindInteractionChannelDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/FindInteraction/FindInteractionChannelDelegate.swift diff --git a/macos/Classes/FindInteraction/FindInteractionController.swift b/flutter_inappwebview_macos/macos/Classes/FindInteraction/FindInteractionController.swift similarity index 100% rename from macos/Classes/FindInteraction/FindInteractionController.swift rename to flutter_inappwebview_macos/macos/Classes/FindInteraction/FindInteractionController.swift diff --git a/macos/Classes/FindInteraction/FindInteractionSettings.swift b/flutter_inappwebview_macos/macos/Classes/FindInteraction/FindInteractionSettings.swift similarity index 100% rename from macos/Classes/FindInteraction/FindInteractionSettings.swift rename to flutter_inappwebview_macos/macos/Classes/FindInteraction/FindInteractionSettings.swift diff --git a/macos/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift b/flutter_inappwebview_macos/macos/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift similarity index 100% rename from macos/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift rename to flutter_inappwebview_macos/macos/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift diff --git a/macos/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift b/flutter_inappwebview_macos/macos/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift similarity index 100% rename from macos/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift rename to flutter_inappwebview_macos/macos/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift diff --git a/macos/Classes/HeadlessInAppWebView/HeadlessWebViewChannelDelegate.swift b/flutter_inappwebview_macos/macos/Classes/HeadlessInAppWebView/HeadlessWebViewChannelDelegate.swift similarity index 100% rename from macos/Classes/HeadlessInAppWebView/HeadlessWebViewChannelDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/HeadlessInAppWebView/HeadlessWebViewChannelDelegate.swift diff --git a/macos/Classes/ISettings.swift b/flutter_inappwebview_macos/macos/Classes/ISettings.swift similarity index 100% rename from macos/Classes/ISettings.swift rename to flutter_inappwebview_macos/macos/Classes/ISettings.swift diff --git a/macos/Classes/InAppBrowser/InAppBrowserChannelDelegate.swift b/flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserChannelDelegate.swift similarity index 100% rename from macos/Classes/InAppBrowser/InAppBrowserChannelDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserChannelDelegate.swift diff --git a/macos/Classes/InAppBrowser/InAppBrowserDelegate.swift b/flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserDelegate.swift similarity index 100% rename from macos/Classes/InAppBrowser/InAppBrowserDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserDelegate.swift diff --git a/macos/Classes/InAppBrowser/InAppBrowserManager.swift b/flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserManager.swift similarity index 100% rename from macos/Classes/InAppBrowser/InAppBrowserManager.swift rename to flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserManager.swift diff --git a/macos/Classes/InAppBrowser/InAppBrowserSettings.swift b/flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserSettings.swift similarity index 100% rename from macos/Classes/InAppBrowser/InAppBrowserSettings.swift rename to flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserSettings.swift diff --git a/macos/Classes/InAppBrowser/InAppBrowserWebViewController.swift b/flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserWebViewController.swift similarity index 100% rename from macos/Classes/InAppBrowser/InAppBrowserWebViewController.swift rename to flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserWebViewController.swift diff --git a/macos/Classes/InAppBrowser/InAppBrowserWindow.swift b/flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserWindow.swift similarity index 100% rename from macos/Classes/InAppBrowser/InAppBrowserWindow.swift rename to flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserWindow.swift diff --git a/macos/Classes/InAppWebView/ContextMenuSettings.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/ContextMenuSettings.swift similarity index 100% rename from macos/Classes/InAppWebView/ContextMenuSettings.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/ContextMenuSettings.swift diff --git a/macos/Classes/InAppWebView/CustomSchemeHandler.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/CustomSchemeHandler.swift similarity index 100% rename from macos/Classes/InAppWebView/CustomSchemeHandler.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/CustomSchemeHandler.swift diff --git a/macos/Classes/InAppWebView/FlutterWebViewController.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/FlutterWebViewController.swift similarity index 100% rename from macos/Classes/InAppWebView/FlutterWebViewController.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/FlutterWebViewController.swift diff --git a/macos/Classes/InAppWebView/FlutterWebViewFactory.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/FlutterWebViewFactory.swift similarity index 100% rename from macos/Classes/InAppWebView/FlutterWebViewFactory.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/FlutterWebViewFactory.swift diff --git a/macos/Classes/InAppWebView/InAppWebView.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/InAppWebView.swift similarity index 100% rename from macos/Classes/InAppWebView/InAppWebView.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/InAppWebView.swift diff --git a/macos/Classes/InAppWebView/InAppWebViewManager.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/InAppWebViewManager.swift similarity index 100% rename from macos/Classes/InAppWebView/InAppWebViewManager.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/InAppWebViewManager.swift diff --git a/macos/Classes/InAppWebView/InAppWebViewSettings.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/InAppWebViewSettings.swift similarity index 100% rename from macos/Classes/InAppWebView/InAppWebViewSettings.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/InAppWebViewSettings.swift diff --git a/macos/Classes/InAppWebView/WebMessage/WebMessageChannel.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebMessage/WebMessageChannel.swift similarity index 100% rename from macos/Classes/InAppWebView/WebMessage/WebMessageChannel.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/WebMessage/WebMessageChannel.swift diff --git a/macos/Classes/InAppWebView/WebMessage/WebMessageChannelChannelDelegate.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebMessage/WebMessageChannelChannelDelegate.swift similarity index 100% rename from macos/Classes/InAppWebView/WebMessage/WebMessageChannelChannelDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/WebMessage/WebMessageChannelChannelDelegate.swift diff --git a/macos/Classes/InAppWebView/WebMessage/WebMessageListener.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebMessage/WebMessageListener.swift similarity index 100% rename from macos/Classes/InAppWebView/WebMessage/WebMessageListener.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/WebMessage/WebMessageListener.swift diff --git a/macos/Classes/InAppWebView/WebMessage/WebMessageListenerChannelDelegate.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebMessage/WebMessageListenerChannelDelegate.swift similarity index 100% rename from macos/Classes/InAppWebView/WebMessage/WebMessageListenerChannelDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/WebMessage/WebMessageListenerChannelDelegate.swift diff --git a/macos/Classes/InAppWebView/WebViewChannelDelegate.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegate.swift similarity index 100% rename from macos/Classes/InAppWebView/WebViewChannelDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegate.swift diff --git a/macos/Classes/InAppWebView/WebViewChannelDelegateMethods.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegateMethods.swift similarity index 100% rename from macos/Classes/InAppWebView/WebViewChannelDelegateMethods.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegateMethods.swift diff --git a/macos/Classes/InAppWebViewFlutterPlugin.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebViewFlutterPlugin.swift similarity index 100% rename from macos/Classes/InAppWebViewFlutterPlugin.swift rename to flutter_inappwebview_macos/macos/Classes/InAppWebViewFlutterPlugin.swift diff --git a/macos/Classes/LeakAvoider.swift b/flutter_inappwebview_macos/macos/Classes/LeakAvoider.swift similarity index 100% rename from macos/Classes/LeakAvoider.swift rename to flutter_inappwebview_macos/macos/Classes/LeakAvoider.swift diff --git a/macos/Classes/MyCookieManager.swift b/flutter_inappwebview_macos/macos/Classes/MyCookieManager.swift similarity index 100% rename from macos/Classes/MyCookieManager.swift rename to flutter_inappwebview_macos/macos/Classes/MyCookieManager.swift diff --git a/macos/Classes/MyWebStorageManager.swift b/flutter_inappwebview_macos/macos/Classes/MyWebStorageManager.swift similarity index 100% rename from macos/Classes/MyWebStorageManager.swift rename to flutter_inappwebview_macos/macos/Classes/MyWebStorageManager.swift diff --git a/macos/Classes/PlatformUtil.swift b/flutter_inappwebview_macos/macos/Classes/PlatformUtil.swift similarity index 100% rename from macos/Classes/PlatformUtil.swift rename to flutter_inappwebview_macos/macos/Classes/PlatformUtil.swift diff --git a/macos/Classes/PluginScriptsJS/CallAsyncJavaScriptBelowIOS14WrapperJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/CallAsyncJavaScriptBelowIOS14WrapperJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/CallAsyncJavaScriptBelowIOS14WrapperJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/CallAsyncJavaScriptBelowIOS14WrapperJS.swift diff --git a/macos/Classes/PluginScriptsJS/ConsoleLogJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/ConsoleLogJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/ConsoleLogJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/ConsoleLogJS.swift diff --git a/macos/Classes/PluginScriptsJS/EnableViewportScaleJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/EnableViewportScaleJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/EnableViewportScaleJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/EnableViewportScaleJS.swift diff --git a/macos/Classes/PluginScriptsJS/FindElementsAtPointJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/FindElementsAtPointJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/FindElementsAtPointJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/FindElementsAtPointJS.swift diff --git a/macos/Classes/PluginScriptsJS/FindTextHighlightJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/FindTextHighlightJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/FindTextHighlightJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/FindTextHighlightJS.swift diff --git a/macos/Classes/PluginScriptsJS/InterceptAjaxRequestJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/InterceptAjaxRequestJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/InterceptAjaxRequestJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/InterceptAjaxRequestJS.swift diff --git a/macos/Classes/PluginScriptsJS/InterceptFetchRequestJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/InterceptFetchRequestJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/InterceptFetchRequestJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/InterceptFetchRequestJS.swift diff --git a/macos/Classes/PluginScriptsJS/JavaScriptBridgeJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/JavaScriptBridgeJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/JavaScriptBridgeJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/JavaScriptBridgeJS.swift diff --git a/macos/Classes/PluginScriptsJS/OnLoadResourceJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/OnLoadResourceJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/OnLoadResourceJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/OnLoadResourceJS.swift diff --git a/macos/Classes/PluginScriptsJS/OnScrollChangedJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/OnScrollChangedJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/OnScrollChangedJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/OnScrollChangedJS.swift diff --git a/macos/Classes/PluginScriptsJS/OnWindowBlurEventJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/OnWindowBlurEventJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/OnWindowBlurEventJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/OnWindowBlurEventJS.swift diff --git a/macos/Classes/PluginScriptsJS/OnWindowFocusEventJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/OnWindowFocusEventJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/OnWindowFocusEventJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/OnWindowFocusEventJS.swift diff --git a/macos/Classes/PluginScriptsJS/OriginalViewPortMetaTagContentJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/OriginalViewPortMetaTagContentJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/OriginalViewPortMetaTagContentJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/OriginalViewPortMetaTagContentJS.swift diff --git a/macos/Classes/PluginScriptsJS/PluginScriptsUtil.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/PluginScriptsUtil.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/PluginScriptsUtil.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/PluginScriptsUtil.swift diff --git a/macos/Classes/PluginScriptsJS/PrintJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/PrintJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/PrintJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/PrintJS.swift diff --git a/macos/Classes/PluginScriptsJS/PromisePolyfillJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/PromisePolyfillJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/PromisePolyfillJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/PromisePolyfillJS.swift diff --git a/macos/Classes/PluginScriptsJS/SupportZoomJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/SupportZoomJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/SupportZoomJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/SupportZoomJS.swift diff --git a/macos/Classes/PluginScriptsJS/WebMessageChannelJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/WebMessageChannelJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/WebMessageChannelJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/WebMessageChannelJS.swift diff --git a/macos/Classes/PluginScriptsJS/WebMessageListenerJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/WebMessageListenerJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/WebMessageListenerJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/WebMessageListenerJS.swift diff --git a/macos/Classes/PluginScriptsJS/WindowIdJS.swift b/flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/WindowIdJS.swift similarity index 100% rename from macos/Classes/PluginScriptsJS/WindowIdJS.swift rename to flutter_inappwebview_macos/macos/Classes/PluginScriptsJS/WindowIdJS.swift diff --git a/macos/Classes/PrintJob/CustomUIPrintPageRenderer.swift b/flutter_inappwebview_macos/macos/Classes/PrintJob/CustomUIPrintPageRenderer.swift similarity index 100% rename from macos/Classes/PrintJob/CustomUIPrintPageRenderer.swift rename to flutter_inappwebview_macos/macos/Classes/PrintJob/CustomUIPrintPageRenderer.swift diff --git a/macos/Classes/PrintJob/PrintAttributes.swift b/flutter_inappwebview_macos/macos/Classes/PrintJob/PrintAttributes.swift similarity index 100% rename from macos/Classes/PrintJob/PrintAttributes.swift rename to flutter_inappwebview_macos/macos/Classes/PrintJob/PrintAttributes.swift diff --git a/macos/Classes/PrintJob/PrintJobChannelDelegate.swift b/flutter_inappwebview_macos/macos/Classes/PrintJob/PrintJobChannelDelegate.swift similarity index 100% rename from macos/Classes/PrintJob/PrintJobChannelDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/PrintJob/PrintJobChannelDelegate.swift diff --git a/macos/Classes/PrintJob/PrintJobController.swift b/flutter_inappwebview_macos/macos/Classes/PrintJob/PrintJobController.swift similarity index 100% rename from macos/Classes/PrintJob/PrintJobController.swift rename to flutter_inappwebview_macos/macos/Classes/PrintJob/PrintJobController.swift diff --git a/macos/Classes/PrintJob/PrintJobInfo.swift b/flutter_inappwebview_macos/macos/Classes/PrintJob/PrintJobInfo.swift similarity index 100% rename from macos/Classes/PrintJob/PrintJobInfo.swift rename to flutter_inappwebview_macos/macos/Classes/PrintJob/PrintJobInfo.swift diff --git a/macos/Classes/PrintJob/PrintJobManager.swift b/flutter_inappwebview_macos/macos/Classes/PrintJob/PrintJobManager.swift similarity index 100% rename from macos/Classes/PrintJob/PrintJobManager.swift rename to flutter_inappwebview_macos/macos/Classes/PrintJob/PrintJobManager.swift diff --git a/macos/Classes/PrintJob/PrintJobSettings.swift b/flutter_inappwebview_macos/macos/Classes/PrintJob/PrintJobSettings.swift similarity index 100% rename from macos/Classes/PrintJob/PrintJobSettings.swift rename to flutter_inappwebview_macos/macos/Classes/PrintJob/PrintJobSettings.swift diff --git a/macos/Classes/Types/BaseCallbackResult.swift b/flutter_inappwebview_macos/macos/Classes/Types/BaseCallbackResult.swift similarity index 100% rename from macos/Classes/Types/BaseCallbackResult.swift rename to flutter_inappwebview_macos/macos/Classes/Types/BaseCallbackResult.swift diff --git a/macos/Classes/Types/CGRect.swift b/flutter_inappwebview_macos/macos/Classes/Types/CGRect.swift similarity index 100% rename from macos/Classes/Types/CGRect.swift rename to flutter_inappwebview_macos/macos/Classes/Types/CGRect.swift diff --git a/macos/Classes/Types/CallbackResult.swift b/flutter_inappwebview_macos/macos/Classes/Types/CallbackResult.swift similarity index 100% rename from macos/Classes/Types/CallbackResult.swift rename to flutter_inappwebview_macos/macos/Classes/Types/CallbackResult.swift diff --git a/macos/Classes/Types/ChannelDelegate.swift b/flutter_inappwebview_macos/macos/Classes/Types/ChannelDelegate.swift similarity index 100% rename from macos/Classes/Types/ChannelDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/Types/ChannelDelegate.swift diff --git a/macos/Classes/Types/ClientCertChallenge.swift b/flutter_inappwebview_macos/macos/Classes/Types/ClientCertChallenge.swift similarity index 100% rename from macos/Classes/Types/ClientCertChallenge.swift rename to flutter_inappwebview_macos/macos/Classes/Types/ClientCertChallenge.swift diff --git a/macos/Classes/Types/ClientCertResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/ClientCertResponse.swift similarity index 100% rename from macos/Classes/Types/ClientCertResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/ClientCertResponse.swift diff --git a/macos/Classes/Types/CreateWindowAction.swift b/flutter_inappwebview_macos/macos/Classes/Types/CreateWindowAction.swift similarity index 100% rename from macos/Classes/Types/CreateWindowAction.swift rename to flutter_inappwebview_macos/macos/Classes/Types/CreateWindowAction.swift diff --git a/macos/Classes/Types/CustomSchemeResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/CustomSchemeResponse.swift similarity index 100% rename from macos/Classes/Types/CustomSchemeResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/CustomSchemeResponse.swift diff --git a/macos/Classes/Types/Disposable.swift b/flutter_inappwebview_macos/macos/Classes/Types/Disposable.swift similarity index 100% rename from macos/Classes/Types/Disposable.swift rename to flutter_inappwebview_macos/macos/Classes/Types/Disposable.swift diff --git a/macos/Classes/Types/DownloadStartRequest.swift b/flutter_inappwebview_macos/macos/Classes/Types/DownloadStartRequest.swift similarity index 100% rename from macos/Classes/Types/DownloadStartRequest.swift rename to flutter_inappwebview_macos/macos/Classes/Types/DownloadStartRequest.swift diff --git a/macos/Classes/Types/FindSession.swift b/flutter_inappwebview_macos/macos/Classes/Types/FindSession.swift similarity index 100% rename from macos/Classes/Types/FindSession.swift rename to flutter_inappwebview_macos/macos/Classes/Types/FindSession.swift diff --git a/macos/Classes/Types/FlutterMethodCallDelegate.swift b/flutter_inappwebview_macos/macos/Classes/Types/FlutterMethodCallDelegate.swift similarity index 100% rename from macos/Classes/Types/FlutterMethodCallDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/Types/FlutterMethodCallDelegate.swift diff --git a/macos/Classes/Types/FlutterMethodChannel.swift b/flutter_inappwebview_macos/macos/Classes/Types/FlutterMethodChannel.swift similarity index 100% rename from macos/Classes/Types/FlutterMethodChannel.swift rename to flutter_inappwebview_macos/macos/Classes/Types/FlutterMethodChannel.swift diff --git a/macos/Classes/Types/HitTestResult.swift b/flutter_inappwebview_macos/macos/Classes/Types/HitTestResult.swift similarity index 100% rename from macos/Classes/Types/HitTestResult.swift rename to flutter_inappwebview_macos/macos/Classes/Types/HitTestResult.swift diff --git a/macos/Classes/Types/HttpAuthResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/HttpAuthResponse.swift similarity index 100% rename from macos/Classes/Types/HttpAuthResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/HttpAuthResponse.swift diff --git a/macos/Classes/Types/HttpAuthenticationChallenge.swift b/flutter_inappwebview_macos/macos/Classes/Types/HttpAuthenticationChallenge.swift similarity index 100% rename from macos/Classes/Types/HttpAuthenticationChallenge.swift rename to flutter_inappwebview_macos/macos/Classes/Types/HttpAuthenticationChallenge.swift diff --git a/macos/Classes/Types/InAppBrowserMenuItem.swift b/flutter_inappwebview_macos/macos/Classes/Types/InAppBrowserMenuItem.swift similarity index 100% rename from macos/Classes/Types/InAppBrowserMenuItem.swift rename to flutter_inappwebview_macos/macos/Classes/Types/InAppBrowserMenuItem.swift diff --git a/macos/Classes/Types/InAppBrowserWindowType.swift b/flutter_inappwebview_macos/macos/Classes/Types/InAppBrowserWindowType.swift similarity index 100% rename from macos/Classes/Types/InAppBrowserWindowType.swift rename to flutter_inappwebview_macos/macos/Classes/Types/InAppBrowserWindowType.swift diff --git a/macos/Classes/Types/JsAlertResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/JsAlertResponse.swift similarity index 100% rename from macos/Classes/Types/JsAlertResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/JsAlertResponse.swift diff --git a/macos/Classes/Types/JsConfirmResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/JsConfirmResponse.swift similarity index 100% rename from macos/Classes/Types/JsConfirmResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/JsConfirmResponse.swift diff --git a/macos/Classes/Types/JsPromptResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/JsPromptResponse.swift similarity index 100% rename from macos/Classes/Types/JsPromptResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/JsPromptResponse.swift diff --git a/macos/Classes/Types/MethodChannelResult.swift b/flutter_inappwebview_macos/macos/Classes/Types/MethodChannelResult.swift similarity index 100% rename from macos/Classes/Types/MethodChannelResult.swift rename to flutter_inappwebview_macos/macos/Classes/Types/MethodChannelResult.swift diff --git a/macos/Classes/Types/NSAttributedString.swift b/flutter_inappwebview_macos/macos/Classes/Types/NSAttributedString.swift similarity index 100% rename from macos/Classes/Types/NSAttributedString.swift rename to flutter_inappwebview_macos/macos/Classes/Types/NSAttributedString.swift diff --git a/macos/Classes/Types/NSColor.swift b/flutter_inappwebview_macos/macos/Classes/Types/NSColor.swift similarity index 100% rename from macos/Classes/Types/NSColor.swift rename to flutter_inappwebview_macos/macos/Classes/Types/NSColor.swift diff --git a/macos/Classes/Types/NSEdgeInsets.swift b/flutter_inappwebview_macos/macos/Classes/Types/NSEdgeInsets.swift similarity index 100% rename from macos/Classes/Types/NSEdgeInsets.swift rename to flutter_inappwebview_macos/macos/Classes/Types/NSEdgeInsets.swift diff --git a/macos/Classes/Types/NSImage.swift b/flutter_inappwebview_macos/macos/Classes/Types/NSImage.swift similarity index 100% rename from macos/Classes/Types/NSImage.swift rename to flutter_inappwebview_macos/macos/Classes/Types/NSImage.swift diff --git a/macos/Classes/Types/NSPrinter.swift b/flutter_inappwebview_macos/macos/Classes/Types/NSPrinter.swift similarity index 100% rename from macos/Classes/Types/NSPrinter.swift rename to flutter_inappwebview_macos/macos/Classes/Types/NSPrinter.swift diff --git a/macos/Classes/Types/PermissionRequest.swift b/flutter_inappwebview_macos/macos/Classes/Types/PermissionRequest.swift similarity index 100% rename from macos/Classes/Types/PermissionRequest.swift rename to flutter_inappwebview_macos/macos/Classes/Types/PermissionRequest.swift diff --git a/macos/Classes/Types/PermissionResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/PermissionResponse.swift similarity index 100% rename from macos/Classes/Types/PermissionResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/PermissionResponse.swift diff --git a/macos/Classes/Types/PluginScript.swift b/flutter_inappwebview_macos/macos/Classes/Types/PluginScript.swift similarity index 100% rename from macos/Classes/Types/PluginScript.swift rename to flutter_inappwebview_macos/macos/Classes/Types/PluginScript.swift diff --git a/macos/Classes/Types/SecCertificate.swift b/flutter_inappwebview_macos/macos/Classes/Types/SecCertificate.swift similarity index 100% rename from macos/Classes/Types/SecCertificate.swift rename to flutter_inappwebview_macos/macos/Classes/Types/SecCertificate.swift diff --git a/macos/Classes/Types/ServerTrustAuthResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/ServerTrustAuthResponse.swift similarity index 100% rename from macos/Classes/Types/ServerTrustAuthResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/ServerTrustAuthResponse.swift diff --git a/macos/Classes/Types/ServerTrustChallenge.swift b/flutter_inappwebview_macos/macos/Classes/Types/ServerTrustChallenge.swift similarity index 100% rename from macos/Classes/Types/ServerTrustChallenge.swift rename to flutter_inappwebview_macos/macos/Classes/Types/ServerTrustChallenge.swift diff --git a/macos/Classes/Types/Size2D.swift b/flutter_inappwebview_macos/macos/Classes/Types/Size2D.swift similarity index 100% rename from macos/Classes/Types/Size2D.swift rename to flutter_inappwebview_macos/macos/Classes/Types/Size2D.swift diff --git a/macos/Classes/Types/SslCertificate.swift b/flutter_inappwebview_macos/macos/Classes/Types/SslCertificate.swift similarity index 100% rename from macos/Classes/Types/SslCertificate.swift rename to flutter_inappwebview_macos/macos/Classes/Types/SslCertificate.swift diff --git a/macos/Classes/Types/SslError.swift b/flutter_inappwebview_macos/macos/Classes/Types/SslError.swift similarity index 100% rename from macos/Classes/Types/SslError.swift rename to flutter_inappwebview_macos/macos/Classes/Types/SslError.swift diff --git a/macos/Classes/Types/StringOrInt.swift b/flutter_inappwebview_macos/macos/Classes/Types/StringOrInt.swift similarity index 100% rename from macos/Classes/Types/StringOrInt.swift rename to flutter_inappwebview_macos/macos/Classes/Types/StringOrInt.swift diff --git a/macos/Classes/Types/URLAuthenticationChallenge.swift b/flutter_inappwebview_macos/macos/Classes/Types/URLAuthenticationChallenge.swift similarity index 100% rename from macos/Classes/Types/URLAuthenticationChallenge.swift rename to flutter_inappwebview_macos/macos/Classes/Types/URLAuthenticationChallenge.swift diff --git a/macos/Classes/Types/URLCredential.swift b/flutter_inappwebview_macos/macos/Classes/Types/URLCredential.swift similarity index 100% rename from macos/Classes/Types/URLCredential.swift rename to flutter_inappwebview_macos/macos/Classes/Types/URLCredential.swift diff --git a/macos/Classes/Types/URLProtectionSpace.swift b/flutter_inappwebview_macos/macos/Classes/Types/URLProtectionSpace.swift similarity index 100% rename from macos/Classes/Types/URLProtectionSpace.swift rename to flutter_inappwebview_macos/macos/Classes/Types/URLProtectionSpace.swift diff --git a/macos/Classes/Types/URLRequest.swift b/flutter_inappwebview_macos/macos/Classes/Types/URLRequest.swift similarity index 100% rename from macos/Classes/Types/URLRequest.swift rename to flutter_inappwebview_macos/macos/Classes/Types/URLRequest.swift diff --git a/macos/Classes/Types/URLResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/URLResponse.swift similarity index 100% rename from macos/Classes/Types/URLResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/URLResponse.swift diff --git a/macos/Classes/Types/UserScript.swift b/flutter_inappwebview_macos/macos/Classes/Types/UserScript.swift similarity index 100% rename from macos/Classes/Types/UserScript.swift rename to flutter_inappwebview_macos/macos/Classes/Types/UserScript.swift diff --git a/macos/Classes/Types/WKContentWorld.swift b/flutter_inappwebview_macos/macos/Classes/Types/WKContentWorld.swift similarity index 100% rename from macos/Classes/Types/WKContentWorld.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WKContentWorld.swift diff --git a/macos/Classes/Types/WKFrameInfo.swift b/flutter_inappwebview_macos/macos/Classes/Types/WKFrameInfo.swift similarity index 100% rename from macos/Classes/Types/WKFrameInfo.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WKFrameInfo.swift diff --git a/macos/Classes/Types/WKNavigationAction.swift b/flutter_inappwebview_macos/macos/Classes/Types/WKNavigationAction.swift similarity index 100% rename from macos/Classes/Types/WKNavigationAction.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WKNavigationAction.swift diff --git a/macos/Classes/Types/WKNavigationResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/WKNavigationResponse.swift similarity index 100% rename from macos/Classes/Types/WKNavigationResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WKNavigationResponse.swift diff --git a/macos/Classes/Types/WKSecurityOrigin.swift b/flutter_inappwebview_macos/macos/Classes/Types/WKSecurityOrigin.swift similarity index 100% rename from macos/Classes/Types/WKSecurityOrigin.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WKSecurityOrigin.swift diff --git a/macos/Classes/Types/WKUserContentController.swift b/flutter_inappwebview_macos/macos/Classes/Types/WKUserContentController.swift similarity index 100% rename from macos/Classes/Types/WKUserContentController.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WKUserContentController.swift diff --git a/macos/Classes/Types/WKWindowFeatures.swift b/flutter_inappwebview_macos/macos/Classes/Types/WKWindowFeatures.swift similarity index 100% rename from macos/Classes/Types/WKWindowFeatures.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WKWindowFeatures.swift diff --git a/macos/Classes/Types/WebMessage.swift b/flutter_inappwebview_macos/macos/Classes/Types/WebMessage.swift similarity index 100% rename from macos/Classes/Types/WebMessage.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WebMessage.swift diff --git a/macos/Classes/Types/WebMessagePort.swift b/flutter_inappwebview_macos/macos/Classes/Types/WebMessagePort.swift similarity index 100% rename from macos/Classes/Types/WebMessagePort.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WebMessagePort.swift diff --git a/macos/Classes/Types/WebResourceError.swift b/flutter_inappwebview_macos/macos/Classes/Types/WebResourceError.swift similarity index 100% rename from macos/Classes/Types/WebResourceError.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WebResourceError.swift diff --git a/macos/Classes/Types/WebResourceRequest.swift b/flutter_inappwebview_macos/macos/Classes/Types/WebResourceRequest.swift similarity index 100% rename from macos/Classes/Types/WebResourceRequest.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WebResourceRequest.swift diff --git a/macos/Classes/Types/WebResourceResponse.swift b/flutter_inappwebview_macos/macos/Classes/Types/WebResourceResponse.swift similarity index 100% rename from macos/Classes/Types/WebResourceResponse.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WebResourceResponse.swift diff --git a/macos/Classes/Types/WebViewTransport.swift b/flutter_inappwebview_macos/macos/Classes/Types/WebViewTransport.swift similarity index 100% rename from macos/Classes/Types/WebViewTransport.swift rename to flutter_inappwebview_macos/macos/Classes/Types/WebViewTransport.swift diff --git a/macos/Classes/Util.swift b/flutter_inappwebview_macos/macos/Classes/Util.swift similarity index 100% rename from macos/Classes/Util.swift rename to flutter_inappwebview_macos/macos/Classes/Util.swift diff --git a/macos/Classes/WKProcessPoolManager.swift b/flutter_inappwebview_macos/macos/Classes/WKProcessPoolManager.swift similarity index 100% rename from macos/Classes/WKProcessPoolManager.swift rename to flutter_inappwebview_macos/macos/Classes/WKProcessPoolManager.swift diff --git a/macos/Classes/WebAuthenticationSession/WebAuthenticationSession.swift b/flutter_inappwebview_macos/macos/Classes/WebAuthenticationSession/WebAuthenticationSession.swift similarity index 100% rename from macos/Classes/WebAuthenticationSession/WebAuthenticationSession.swift rename to flutter_inappwebview_macos/macos/Classes/WebAuthenticationSession/WebAuthenticationSession.swift diff --git a/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionChannelDelegate.swift b/flutter_inappwebview_macos/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionChannelDelegate.swift similarity index 94% rename from macos/Classes/WebAuthenticationSession/WebAuthenticationSessionChannelDelegate.swift rename to flutter_inappwebview_macos/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionChannelDelegate.swift index 7c476a6a..6cdf5dbe 100644 --- a/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionChannelDelegate.swift +++ b/flutter_inappwebview_macos/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionChannelDelegate.swift @@ -60,7 +60,9 @@ public class WebAuthenticationSessionChannelDelegate : ChannelDelegate { "url": url?.absoluteString, "errorCode": errorCode ] - channel?.invokeMethod("onComplete", arguments: arguments) + DispatchQueue.main.async { [weak self] in + self?.channel?.invokeMethod("onComplete", arguments: arguments) + } } public override func dispose() { diff --git a/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionManager.swift b/flutter_inappwebview_macos/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionManager.swift similarity index 100% rename from macos/Classes/WebAuthenticationSession/WebAuthenticationSessionManager.swift rename to flutter_inappwebview_macos/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionManager.swift diff --git a/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionSettings.swift b/flutter_inappwebview_macos/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionSettings.swift similarity index 100% rename from macos/Classes/WebAuthenticationSession/WebAuthenticationSessionSettings.swift rename to flutter_inappwebview_macos/macos/Classes/WebAuthenticationSession/WebAuthenticationSessionSettings.swift diff --git a/macos/flutter_inappwebview.podspec b/flutter_inappwebview_macos/macos/flutter_inappwebview_macos.podspec similarity index 85% rename from macos/flutter_inappwebview.podspec rename to flutter_inappwebview_macos/macos/flutter_inappwebview_macos.podspec index 97601f6f..3f43bea6 100644 --- a/macos/flutter_inappwebview.podspec +++ b/flutter_inappwebview_macos/macos/flutter_inappwebview_macos.podspec @@ -1,9 +1,9 @@ # # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint flutter_inappwebview.podspec` to validate before publishing. +# Run `pod lib lint flutter_inappwebview_macos.podspec` to validate before publishing. # Pod::Spec.new do |s| - s.name = 'flutter_inappwebview' + s.name = 'flutter_inappwebview_macos' s.version = '0.0.1' s.summary = 'A new Flutter plugin project.' s.description = <<-DESC diff --git a/flutter_inappwebview_macos/pubspec.yaml b/flutter_inappwebview_macos/pubspec.yaml new file mode 100644 index 00000000..eaaf0a90 --- /dev/null +++ b/flutter_inappwebview_macos/pubspec.yaml @@ -0,0 +1,80 @@ +name: flutter_inappwebview_macos +description: macOS implementation of the flutter_inappwebview plugin. +version: 1.0.0 +homepage: https://inappwebview.dev/ +repository: https://github.com/pichillilorenzo/flutter_inappwebview/tree/master/flutter_inappwebview_macos +issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues +topics: + - html + - webview + - webview-flutter + - inappwebview + - browser + +environment: + sdk: ">=2.17.0 <4.0.0" + flutter: ">=3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_inappwebview_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + plugin_platform_interface: ^2.0.2 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + implements: flutter_inappwebview + platforms: + macos: + pluginClass: InAppWebViewFlutterPlugin + dartPluginClass: MacOSInAppWebViewPlatform + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter_inappwebview_macos/test/flutter_inappwebview_macos_test.dart b/flutter_inappwebview_macos/test/flutter_inappwebview_macos_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_platform_interface/.gitignore b/flutter_inappwebview_platform_interface/.gitignore new file mode 100644 index 00000000..96486fd9 --- /dev/null +++ b/flutter_inappwebview_platform_interface/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/flutter_inappwebview_platform_interface/.metadata b/flutter_inappwebview_platform_interface/.metadata new file mode 100644 index 00000000..5c536bce --- /dev/null +++ b/flutter_inappwebview_platform_interface/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e" + channel: "stable" + +project_type: package diff --git a/flutter_inappwebview_platform_interface/CHANGELOG.md b/flutter_inappwebview_platform_interface/CHANGELOG.md new file mode 100644 index 00000000..16477d94 --- /dev/null +++ b/flutter_inappwebview_platform_interface/CHANGELOG.md @@ -0,0 +1,7 @@ +## 1.0.1 + +- Updated README + +## 1.0.0 + +Initial release. diff --git a/flutter_inappwebview_platform_interface/LICENSE b/flutter_inappwebview_platform_interface/LICENSE new file mode 100644 index 00000000..4dada16d --- /dev/null +++ b/flutter_inappwebview_platform_interface/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Lorenzo Pichilli + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/flutter_inappwebview_platform_interface/README.md b/flutter_inappwebview_platform_interface/README.md new file mode 100644 index 00000000..c7e1d40d --- /dev/null +++ b/flutter_inappwebview_platform_interface/README.md @@ -0,0 +1,23 @@ +# flutter\_inappwebview\_platform\_interface + +A common platform interface for the [`flutter_inappwebview`](https://pub.dev/packages/flutter_inappwebview) plugin. + +This interface allows platform-specific implementations of the `flutter_inappwebview` +plugin, as well as the plugin itself, to ensure they are supporting the +same interface. + +# Usage + +To implement a new platform-specific implementation of `flutter_inappwebview`, extend +[`InAppWebViewPlatform`](lib/src/inappwebview_platform.dart) with an implementation that performs the +platform-specific behavior, and when you register your plugin, set the default +`InAppWebViewPlatform` by calling +`InAppWebViewPlatform.instance = MyPlatformWebview()`. + +# Note on breaking changes + +Strongly prefer non-breaking changes (such as adding a method to the interface) +over breaking changes for this package. + +See https://flutter.dev/go/platform-interface-breaking-changes for a discussion +on why a less-clean interface is preferable to a breaking change. \ No newline at end of file diff --git a/flutter_inappwebview_platform_interface/analysis_options.yaml b/flutter_inappwebview_platform_interface/analysis_options.yaml new file mode 100644 index 00000000..84964c30 --- /dev/null +++ b/flutter_inappwebview_platform_interface/analysis_options.yaml @@ -0,0 +1,15 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + constant_identifier_names: ignore + deprecated_member_use_from_same_package: ignore + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options +analyzer: + errors: + deprecated_member_use: ignore + deprecated_member_use_from_same_package: ignore + unnecessary_cast: ignore + unnecessary_import: ignore diff --git a/flutter_inappwebview_platform_interface/build.yaml b/flutter_inappwebview_platform_interface/build.yaml new file mode 100644 index 00000000..e2b3acf3 --- /dev/null +++ b/flutter_inappwebview_platform_interface/build.yaml @@ -0,0 +1,5 @@ +targets: + $default: + sources: + exclude: + - example/**.dart diff --git a/flutter_inappwebview_platform_interface/lib/flutter_inappwebview_platform_interface.dart b/flutter_inappwebview_platform_interface/lib/flutter_inappwebview_platform_interface.dart new file mode 100644 index 00000000..2367b0f2 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/flutter_inappwebview_platform_interface.dart @@ -0,0 +1,3 @@ +library flutter_inappwebview_platform_interface; + +export 'src/main.dart'; diff --git a/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart similarity index 97% rename from lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart index 7b43dab9..3803d311 100755 --- a/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart @@ -4,11 +4,11 @@ import '../../util.dart'; import '../../types/main.dart'; import '../chrome_safari_browser_settings.dart'; -import '../chrome_safari_browser.dart'; +import '../platform_chrome_safari_browser.dart'; import '../../in_app_webview/android/in_app_webview_options.dart'; -///This class represents all the Android-only [ChromeSafariBrowser] options available. +///This class represents all the Android-only [PlatformChromeSafariBrowser] options available. ///Use [ChromeSafariBrowserSettings] instead. @Deprecated('Use ChromeSafariBrowserSettings instead') class AndroidChromeCustomTabsOptions diff --git a/lib/src/chrome_safari_browser/android/main.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/android/main.dart similarity index 100% rename from lib/src/chrome_safari_browser/android/main.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/android/main.dart diff --git a/lib/src/chrome_safari_browser/apple/main.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/apple/main.dart similarity index 100% rename from lib/src/chrome_safari_browser/apple/main.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/apple/main.dart diff --git a/lib/src/chrome_safari_browser/apple/safari_options.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/apple/safari_options.dart similarity index 96% rename from lib/src/chrome_safari_browser/apple/safari_options.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/apple/safari_options.dart index 9559ac6d..9eb397b4 100755 --- a/lib/src/chrome_safari_browser/apple/safari_options.dart +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/apple/safari_options.dart @@ -4,11 +4,11 @@ import '../../util.dart'; import '../../types/main.dart'; import '../chrome_safari_browser_settings.dart'; -import '../chrome_safari_browser.dart'; +import '../platform_chrome_safari_browser.dart'; import '../../in_app_webview/apple/in_app_webview_options.dart'; -///This class represents all the iOS-only [ChromeSafariBrowser] options available. +///This class represents all the iOS-only [PlatformChromeSafariBrowser] options available. ///Use [ChromeSafariBrowserSettings] instead. @Deprecated('Use ChromeSafariBrowserSettings instead') class IOSSafariOptions implements ChromeSafariBrowserOptions, IosOptions { diff --git a/lib/src/chrome_safari_browser/chrome_safari_action_button.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_action_button.dart similarity index 89% rename from lib/src/chrome_safari_browser/chrome_safari_action_button.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_action_button.dart index 7f2a56bf..400296a1 100644 --- a/lib/src/chrome_safari_browser/chrome_safari_action_button.dart +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_action_button.dart @@ -2,13 +2,13 @@ import 'dart:typed_data'; import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; -import 'chrome_safari_browser.dart'; +import 'platform_chrome_safari_browser.dart'; import 'chrome_safari_browser_menu_item.dart'; import '../web_uri.dart'; part 'chrome_safari_action_button.g.dart'; -///Class that represents a custom action button for a [ChromeSafariBrowser] instance. +///Class that represents a custom action button for a [PlatformChromeSafariBrowser] instance. @SupportedPlatforms(platforms: [ AndroidPlatform(note: 'Not available in an Android Trusted Web Activity.'), ]) diff --git a/lib/src/chrome_safari_browser/chrome_safari_action_button.g.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_action_button.g.dart similarity index 91% rename from lib/src/chrome_safari_browser/chrome_safari_action_button.g.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_action_button.g.dart index 7d9e3c23..ef981f4b 100644 --- a/lib/src/chrome_safari_browser/chrome_safari_action_button.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_action_button.g.dart @@ -6,11 +6,11 @@ part of 'chrome_safari_action_button.dart'; // ExchangeableObjectGenerator // ************************************************************************** -///Class that represents a custom action button for a [ChromeSafariBrowser] instance. +///Class that represents a custom action button for a [PlatformChromeSafariBrowser] instance. /// ///**NOTE for Android native WebView**: Not available in an Android Trusted Web Activity. /// -///**Supported Platforms/Implementations**: +///**Officially Supported Platforms/Implementations**: ///- Android native WebView class ChromeSafariBrowserActionButton { ///Use onClick instead. @@ -35,7 +35,7 @@ class ChromeSafariBrowserActionButton { /// ///**NOTE for Android native WebView**: Not available in an Android Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ChromeSafariBrowserActionButton( {required this.id, diff --git a/lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.dart similarity index 88% rename from lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.dart index a5ac4599..f3a62579 100644 --- a/lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.dart +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.dart @@ -1,12 +1,12 @@ import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; import '../types/ui_image.dart'; -import 'chrome_safari_browser.dart'; +import 'platform_chrome_safari_browser.dart'; import '../web_uri.dart'; part 'chrome_safari_browser_menu_item.g.dart'; -///Class that represents a custom menu item for a [ChromeSafariBrowser] instance. +///Class that represents a custom menu item for a [PlatformChromeSafariBrowser] instance. @SupportedPlatforms(platforms: [ AndroidPlatform(note: 'Not available in an Android Trusted Web Activity.'), IOSPlatform() diff --git a/lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.g.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.g.dart similarity index 90% rename from lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.g.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.g.dart index 014ad3de..9bb9f017 100644 --- a/lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_menu_item.g.dart @@ -6,11 +6,11 @@ part of 'chrome_safari_browser_menu_item.dart'; // ExchangeableObjectGenerator // ************************************************************************** -///Class that represents a custom menu item for a [ChromeSafariBrowser] instance. +///Class that represents a custom menu item for a [PlatformChromeSafariBrowser] instance. /// ///**NOTE for Android native WebView**: Not available in an Android Trusted Web Activity. /// -///**Supported Platforms/Implementations**: +///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS class ChromeSafariBrowserMenuItem { @@ -33,7 +33,7 @@ class ChromeSafariBrowserMenuItem { /// ///**NOTE for Android native WebView**: Not available in an Android Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ChromeSafariBrowserMenuItem( diff --git a/lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.dart similarity index 94% rename from lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.dart index 9dfe02c8..6404b07a 100644 --- a/lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.dart +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.dart @@ -2,7 +2,7 @@ import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_i import '../types/android_resource.dart'; import '../web_uri.dart'; -import 'chrome_safari_browser.dart'; +import 'platform_chrome_safari_browser.dart'; part 'chrome_safari_browser_secondary_toolbar.g.dart'; @@ -29,7 +29,7 @@ class ChromeSafariBrowserSecondaryToolbar_ { {required this.layout, this.clickableIDs = const []}); } -///Class that represents a clickable ID item of the secondary toolbar for a [ChromeSafariBrowser] instance. +///Class that represents a clickable ID item of the secondary toolbar for a [PlatformChromeSafariBrowser] instance. @SupportedPlatforms(platforms: [ AndroidPlatform(note: 'Not available in an Android Trusted Web Activity.') ]) diff --git a/lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.g.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.g.dart similarity index 93% rename from lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.g.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.g.dart index 20185c2f..7b1a7a3e 100644 --- a/lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_secondary_toolbar.g.dart @@ -17,7 +17,7 @@ part of 'chrome_safari_browser_secondary_toolbar.dart'; /// ///**NOTE for Android native WebView**: Not available in an Android Trusted Web Activity. /// -///**Supported Platforms/Implementations**: +///**Officially Supported Platforms/Implementations**: ///- Android native WebView class ChromeSafariBrowserSecondaryToolbar { ///The IDs of clickable views. The `onClick` event of these views will be handled by custom tabs. @@ -29,7 +29,7 @@ class ChromeSafariBrowserSecondaryToolbar { /// ///**NOTE for Android native WebView**: Not available in an Android Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ChromeSafariBrowserSecondaryToolbar( {this.clickableIDs = const [], required this.layout}); @@ -70,11 +70,11 @@ class ChromeSafariBrowserSecondaryToolbar { } } -///Class that represents a clickable ID item of the secondary toolbar for a [ChromeSafariBrowser] instance. +///Class that represents a clickable ID item of the secondary toolbar for a [PlatformChromeSafariBrowser] instance. /// ///**NOTE for Android native WebView**: Not available in an Android Trusted Web Activity. /// -///**Supported Platforms/Implementations**: +///**Officially Supported Platforms/Implementations**: ///- Android native WebView class ChromeSafariBrowserSecondaryToolbarClickableID { ///The android id resource @@ -86,7 +86,7 @@ class ChromeSafariBrowserSecondaryToolbarClickableID { /// ///**NOTE for Android native WebView**: Not available in an Android Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ChromeSafariBrowserSecondaryToolbarClickableID( {required this.id, this.onClick}); diff --git a/lib/src/chrome_safari_browser/chrome_safari_browser_settings.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_settings.dart similarity index 86% rename from lib/src/chrome_safari_browser/chrome_safari_browser_settings.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_settings.dart index 6ba48813..b8d6610c 100755 --- a/lib/src/chrome_safari_browser/chrome_safari_browser_settings.dart +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_settings.dart @@ -62,7 +62,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: Not available in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android CustomTabsShareState_? shareState; @@ -70,31 +70,31 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: Not available in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? showTitle; ///Set the custom background color of the toolbar. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android Color_? toolbarBackgroundColor; ///Sets the navigation bar color. Has no effect on Android API versions below L. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android Color_? navigationBarColor; ///Sets the navigation bar divider color. Has no effect on Android API versions below P. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android Color_? navigationBarDividerColor; ///Sets the color of the secondary toolbar. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android Color_? secondaryToolbarColor; @@ -102,7 +102,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: Not available in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? enableUrlBarHiding; @@ -110,7 +110,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: Not available in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? instantAppsEnabled; @@ -120,31 +120,31 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { ///If non-null, the Intent can only match the components in the given ///application package. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android String? packageName; ///Set to `true` to enable Keep Alive. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? keepAliveEnabled; ///Set to `true` to launch the Android activity in `singleInstance` mode. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? isSingleInstance; ///Set to `true` to launch the Android intent with the flag `FLAG_ACTIVITY_NO_HISTORY`. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? noHistory; ///Set to `true` to launch the Custom Tab as a Trusted Web Activity. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? isTrustedWebActivity; @@ -152,7 +152,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: Available only in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android List? additionalTrustedOrigins; @@ -160,7 +160,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: Available only in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android @ExchangeableObjectProperty(deserializer: _deserializeDisplayMode) TrustedWebActivityDisplayMode_? displayMode; @@ -169,7 +169,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: Available only in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android TrustedWebActivityScreenOrientation_? screenOrientation; @@ -177,7 +177,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { ///It must contain 2 [AndroidResource], where the first one represents the "enter" animation for the browser ///and the second one represents the "exit" animation for the application. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android List? startAnimations; @@ -185,7 +185,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { ///It must contain 2 [AndroidResource], where the first one represents the "enter" animation for the application ///and the second one represents the "exit" animation for the browser. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android List? exitAnimations; @@ -194,19 +194,19 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { ///Calling this with an intent will override any custom tabs related customizations. ///The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? alwaysUseBrowserUI; ///Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? entersReaderIfAvailable; ///Set to `true` to enable bar collapsing. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? barCollapsingEnabled; @@ -214,7 +214,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: available on iOS 11.0+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS DismissButtonStyle_? dismissButtonStyle; @@ -222,7 +222,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: available on iOS 10.0+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color_? preferredBarTintColor; @@ -230,19 +230,19 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: available on iOS 10.0+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color_? preferredControlTintColor; ///Set the custom modal presentation style when presenting the WebView. The default value is [ModalPresentationStyle.FULL_SCREEN]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ModalPresentationStyle_? presentationStyle; ///Set to the custom transition style when presenting the WebView. The default value is [ModalTransitionStyle.COVER_VERTICAL]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ModalTransitionStyle_? transitionStyle; @@ -251,7 +251,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: available on iOS 15.0+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ActivityButton_? activityButton; @@ -260,7 +260,7 @@ class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions { /// ///**NOTE**: available on iOS 15.2+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS UIEventAttribution_? eventAttribution; diff --git a/lib/src/chrome_safari_browser/chrome_safari_browser_settings.g.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_settings.g.dart similarity index 89% rename from lib/src/chrome_safari_browser/chrome_safari_browser_settings.g.dart rename to flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_settings.g.dart index 165d6b29..4ec95401 100644 --- a/lib/src/chrome_safari_browser/chrome_safari_browser_settings.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/chrome_safari_browser_settings.g.dart @@ -13,7 +13,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: available on iOS 15.0+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ActivityButton? activityButton; @@ -21,7 +21,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: Available only in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android List? additionalTrustedOrigins; @@ -30,13 +30,13 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { ///Calling this with an intent will override any custom tabs related customizations. ///The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? alwaysUseBrowserUI; ///Set to `true` to enable bar collapsing. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? barCollapsingEnabled; @@ -44,7 +44,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: available on iOS 11.0+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS DismissButtonStyle? dismissButtonStyle; @@ -52,7 +52,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: Available only in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android TrustedWebActivityDisplayMode? displayMode; @@ -60,13 +60,13 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: Not available in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? enableUrlBarHiding; ///Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? entersReaderIfAvailable; @@ -75,7 +75,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: available on iOS 15.2+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS UIEventAttribution? eventAttribution; @@ -83,7 +83,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { ///It must contain 2 [AndroidResource], where the first one represents the "enter" animation for the application ///and the second one represents the "exit" animation for the browser. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android List? exitAnimations; @@ -91,43 +91,43 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: Not available in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? instantAppsEnabled; ///Set to `true` to launch the Android activity in `singleInstance` mode. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? isSingleInstance; ///Set to `true` to launch the Custom Tab as a Trusted Web Activity. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? isTrustedWebActivity; ///Set to `true` to enable Keep Alive. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? keepAliveEnabled; ///Sets the navigation bar color. Has no effect on Android API versions below L. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android Color? navigationBarColor; ///Sets the navigation bar divider color. Has no effect on Android API versions below P. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android Color? navigationBarDividerColor; ///Set to `true` to launch the Android intent with the flag `FLAG_ACTIVITY_NO_HISTORY`. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? noHistory; @@ -137,7 +137,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { ///If non-null, the Intent can only match the components in the given ///application package. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android String? packageName; @@ -145,7 +145,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: available on iOS 10.0+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color? preferredBarTintColor; @@ -153,13 +153,13 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: available on iOS 10.0+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color? preferredControlTintColor; ///Set the custom modal presentation style when presenting the WebView. The default value is [ModalPresentationStyle.FULL_SCREEN]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ModalPresentationStyle? presentationStyle; @@ -167,13 +167,13 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: Available only in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android TrustedWebActivityScreenOrientation? screenOrientation; ///Sets the color of the secondary toolbar. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android Color? secondaryToolbarColor; @@ -181,7 +181,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: Not available in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android CustomTabsShareState? shareState; @@ -189,7 +189,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { /// ///**NOTE**: Not available in a Trusted Web Activity. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android bool? showTitle; @@ -197,19 +197,19 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { ///It must contain 2 [AndroidResource], where the first one represents the "enter" animation for the browser ///and the second one represents the "exit" animation for the application. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android List? startAnimations; ///Set the custom background color of the toolbar. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android Color? toolbarBackgroundColor; ///Set to the custom transition style when presenting the WebView. The default value is [ModalTransitionStyle.COVER_VERTICAL]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ModalTransitionStyle? transitionStyle; ChromeSafariBrowserSettings( diff --git a/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/main.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/main.dart new file mode 100644 index 00000000..a2c764ce --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/main.dart @@ -0,0 +1,14 @@ +export 'platform_chrome_safari_browser.dart'; +export 'chrome_safari_browser_settings.dart' + show + ChromeSafariBrowserOptions, + ChromeSafariBrowserSettings, + ChromeSafariBrowserClassOptions; +export 'android/main.dart'; +export 'apple/main.dart'; +export 'chrome_safari_action_button.dart' show ChromeSafariBrowserActionButton; +export 'chrome_safari_browser_menu_item.dart' show ChromeSafariBrowserMenuItem; +export 'chrome_safari_browser_secondary_toolbar.dart' + show + ChromeSafariBrowserSecondaryToolbar, + ChromeSafariBrowserSecondaryToolbarClickableID; diff --git a/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/platform_chrome_safari_browser.dart b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/platform_chrome_safari_browser.dart new file mode 100755 index 00000000..c221d1cd --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/chrome_safari_browser/platform_chrome_safari_browser.dart @@ -0,0 +1,614 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [PlatformChromeSafariBrowser]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +@immutable +class PlatformChromeSafariBrowserCreationParams { + /// Used by the platform implementation to create a new [PlatformChromeSafariBrowser]. + const PlatformChromeSafariBrowserCreationParams(); +} + +///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser} +///Class that provides a visible standard interface for browsing the web. +///It presents a self-contained web interface inside your app. +/// +///If you need to customize or interact with the web content, use the `InAppWebView` widget. +/// +///This class uses native [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android +///and [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS. +/// +///**NOTE**: 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). +/// +///**Officially Supported Platforms/Implementations**: +///- Android +///- iOS +///{@endtemplate} +abstract class PlatformChromeSafariBrowser extends PlatformInterface + implements Disposable { + ///Debug settings. + static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings(); + + /// Event handler object that handles the [PlatformChromeSafariBrowser] events. + PlatformChromeSafariBrowserEvents? eventHandler; + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.id} + ///View ID used internally. + ///@{endtemplate} + String get id { + throw UnimplementedError('id is not implemented on the current platform'); + } + + /// Creates a new [PlatformChromeSafariBrowser] + factory PlatformChromeSafariBrowser( + PlatformChromeSafariBrowserCreationParams params) { + assert( + InAppWebViewPlatform.instance != null, + 'A platform implementation for `flutter_inappwebview` has not been set. Please ' + 'ensure that an implementation of `InAppWebViewPlatform` has been set to ' + '`InAppWebViewPlatform.instance` before use. For unit testing, ' + '`InAppWebViewPlatform.instance` can be set with your own test implementation.', + ); + final PlatformChromeSafariBrowser chromeSafariBrowser = InAppWebViewPlatform + .instance! + .createPlatformChromeSafariBrowser(params); + PlatformInterface.verify(chromeSafariBrowser, _token); + return chromeSafariBrowser; + } + + /// Creates a new [PlatformChromeSafariBrowser] to access static methods. + factory PlatformChromeSafariBrowser.static() { + assert( + InAppWebViewPlatform.instance != null, + 'A platform implementation for `flutter_inappwebview` has not been set. Please ' + 'ensure that an implementation of `InAppWebViewPlatform` has been set to ' + '`InAppWebViewPlatform.instance` before use. For unit testing, ' + '`InAppWebViewPlatform.instance` can be set with your own test implementation.', + ); + final PlatformChromeSafariBrowser chromeSafariBrowserStatic = + InAppWebViewPlatform.instance! + .createPlatformChromeSafariBrowserStatic(); + PlatformInterface.verify(chromeSafariBrowserStatic, _token); + return chromeSafariBrowserStatic; + } + + /// Used by the platform implementation to create a new [PlatformChromeSafariBrowser]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformChromeSafariBrowser.implementation(this.params) + : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformChromeSafariBrowser]. + final PlatformChromeSafariBrowserCreationParams params; + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.open} + ///Opens the [PlatformChromeSafariBrowser] instance with an [url]. + /// + ///[url] - The [url] to load. On iOS, the [url] is required and must use the `http` or `https` scheme. + /// + ///[headers] (Supported only on Android) - [whitelisted](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) cross-origin request headers. + ///It is possible to attach non-whitelisted headers to cross-origin requests, when the server and client are related using a + ///[digital asset link](https://developers.google.com/digital-asset-links/v1/getting-started). + /// + ///[otherLikelyURLs] - Other likely destinations, sorted in decreasing likelihood order. Supported only on Android. + /// + ///[referrer] - referrer header. Supported only on Android. + /// + ///[options] - Deprecated. Use `settings` instead. + /// + ///[settings] - Settings for the [PlatformChromeSafariBrowser]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS + ///{@endtemplate} + Future open( + {WebUri? url, + Map? headers, + List? otherLikelyURLs, + WebUri? referrer, + @Deprecated('Use settings instead') + // ignore: deprecated_member_use_from_same_package + ChromeSafariBrowserClassOptions? options, + ChromeSafariBrowserSettings? settings}) { + throw UnimplementedError('open is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.launchUrl} + ///Tells the browser to launch with [url]. + /// + ///[url] - initial url. + /// + ///[headers] (Supported only on Android) - [whitelisted](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) cross-origin request headers. + ///It is possible to attach non-whitelisted headers to cross-origin requests, when the server and client are related using a + ///[digital asset link](https://developers.google.com/digital-asset-links/v1/getting-started). + /// + ///[otherLikelyURLs] - Other likely destinations, sorted in decreasing likelihood order. + /// + ///[referrer] - referrer header. Supported only on Android. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///{@endtemplate} + Future launchUrl({ + required WebUri url, + Map? headers, + List? otherLikelyURLs, + WebUri? referrer, + }) { + throw UnimplementedError( + 'launchUrl is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.mayLaunchUrl} + ///Tells the browser of a likely future navigation to a URL. + ///The most likely URL has to be specified first. + ///Optionally, a list of other likely URLs can be provided. + ///They are treated as less likely than the first one, and have to be sorted in decreasing priority order. + ///These additional URLs may be ignored. All previous calls to this method will be deprioritized. + /// + ///[url] - Most likely URL, may be null if otherLikelyBundles is provided. + /// + ///[otherLikelyURLs] - Other likely destinations, sorted in decreasing likelihood order. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsSession.mayLaunchUrl](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#mayLaunchUrl(android.net.Uri,android.os.Bundle,java.util.List%3Candroid.os.Bundle%3E))) + ///{@endtemplate} + Future mayLaunchUrl({WebUri? url, List? otherLikelyURLs}) { + throw UnimplementedError( + 'mayLaunchUrl is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.validateRelationship} + ///Requests to validate a relationship between the application and an origin. + /// + ///See [here](https://developers.google.com/digital-asset-links/v1/getting-started) for documentation about Digital Asset Links. + ///This methods requests the browser to verify a relation with the calling application, to grant the associated rights. + /// + ///If this method returns `true`, the validation result will be provided through [PlatformChromeSafariBrowserEvents.onRelationshipValidationResult]. + ///Otherwise the request didn't succeed. + /// + ///[relation] – Relation to check, must be one of the [CustomTabsRelationType] constants. + /// + ///[origin] – Origin. + /// + ///[extras] – Reserved for future use. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsSession.validateRelationship](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#validateRelationship(int,android.net.Uri,android.os.Bundle))) + ///{@endtemplate} + Future validateRelationship( + {required CustomTabsRelationType relation, required WebUri origin}) { + throw UnimplementedError( + 'validateRelationship is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.close} + ///Closes the [PlatformChromeSafariBrowser] instance. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS + ///{@endtemplate} + Future close() { + throw UnimplementedError( + 'close is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.setActionButton} + ///Set a custom action button. + /// + ///**NOTE**: Not available in a Trusted Web Activity. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsIntent.Builder.setActionButton](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsIntent.Builder#setActionButton(android.graphics.Bitmap,%20java.lang.String,%20android.app.PendingIntent,%20boolean))) + ///{@endtemplate} + void setActionButton(ChromeSafariBrowserActionButton actionButton) { + throw UnimplementedError( + 'setActionButton is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.updateActionButton} + ///Updates the [ChromeSafariBrowserActionButton.icon] and [ChromeSafariBrowserActionButton.description]. + /// + ///**NOTE**: Not available in a Trusted Web Activity. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsSession.setActionButton](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#setActionButton(android.graphics.Bitmap,java.lang.String))) + ///{@endtemplate} + Future updateActionButton( + {required Uint8List icon, required String description}) { + throw UnimplementedError( + 'updateActionButton is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.setSecondaryToolbar} + ///Sets the remote views displayed in the secondary toolbar in a custom tab. + /// + ///**NOTE**: Not available in a Trusted Web Activity. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsIntent.Builder.setSecondaryToolbarViews](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsIntent.Builder#setSecondaryToolbarViews(android.widget.RemoteViews,int[],android.app.PendingIntent))) + ///{@endtemplate} + void setSecondaryToolbar( + ChromeSafariBrowserSecondaryToolbar secondaryToolbar) { + throw UnimplementedError( + 'setSecondaryToolbar is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.updateSecondaryToolbar} + ///Sets or updates (if already present) the Remote Views of the secondary toolbar in an existing custom tab session. + /// + ///**NOTE**: Not available in a Trusted Web Activity. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsSession.setSecondaryToolbarViews](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#setSecondaryToolbarViews(android.widget.RemoteViews,int[],android.app.PendingIntent))) + ///{@endtemplate} + Future updateSecondaryToolbar( + ChromeSafariBrowserSecondaryToolbar secondaryToolbar) { + throw UnimplementedError( + 'updateSecondaryToolbar is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.addMenuItem} + ///Adds a [ChromeSafariBrowserMenuItem] to the menu. + /// + ///**NOTE**: Not available in an Android Trusted Web Activity. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS + ///{@endtemplate} + void addMenuItem(ChromeSafariBrowserMenuItem menuItem) { + throw UnimplementedError( + 'addMenuItem is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.addMenuItems} + ///Adds a list of [ChromeSafariBrowserMenuItem] to the menu. + /// + ///**NOTE**: Not available in an Android Trusted Web Activity. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS + ///{@endtemplate} + void addMenuItems(List menuItems) { + throw UnimplementedError( + 'addMenuItems is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.requestPostMessageChannel} + ///Sends a request to create a two way postMessage channel between the client + ///and the browser. + ///If you want to specifying the target origin to communicate with, set the [targetOrigin]. + /// + ///[sourceOrigin] - A origin that the client is requesting to be + ///identified as during the postMessage communication. + ///It has to either start with http or https. + /// + ///[targetOrigin] - The target Origin to establish the postMessage communication with. + ///This can be the app's package name, it has to either start with http or https. + /// + ///Returns whether the implementation accepted the request. + ///Note that returning true here doesn't mean an origin has already been + ///assigned as the validation is asynchronous. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsSession.requestPostMessageChannel](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#requestPostMessageChannel(android.net.Uri,android.net.Uri,android.os.Bundle))) + ///{@endtemplate} + Future requestPostMessageChannel( + {required WebUri sourceOrigin, WebUri? targetOrigin}) { + throw UnimplementedError( + 'requestPostMessageChannel is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.postMessage} + ///Sends a postMessage request using the origin communicated via [requestPostMessageChannel]. + ///Fails when called before [PlatformChromeSafariBrowserEvents.onMessageChannelReady] event. + /// + ///[message] – The message that is being sent. + /// + ///Returns an integer constant about the postMessage request result. + ///Will return CustomTabsService.RESULT_SUCCESS if successful. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsSession.postMessage](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#postMessage(java.lang.String,android.os.Bundle))) + ///{@endtemplate} + Future postMessage(String message) { + throw UnimplementedError( + 'postMessage is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.isEngagementSignalsApiAvailable} + ///Returns whether the Engagement Signals API is available. + ///The availability of the Engagement Signals API may change at runtime. + ///If an EngagementSignalsCallback has been set, an [PlatformChromeSafariBrowserEvents.onSessionEnded] + ///signal will be sent if the API becomes unavailable later. + /// + ///Returns whether the Engagement Signals API is available. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsSession.isEngagementSignalsApiAvailable](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#isEngagementSignalsApiAvailable(android.os.Bundle))) + ///{@endtemplate} + Future isEngagementSignalsApiAvailable() { + throw UnimplementedError( + 'isEngagementSignalsApiAvailable is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.isOpened} + ///Returns `true` if the [PlatformChromeSafariBrowser] instance is opened, otherwise `false`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS + ///{@endtemplate} + bool isOpened() { + throw UnimplementedError( + 'isOpened is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.isAvailable} + ///On Android, returns `true` if Chrome Custom Tabs is available. + ///On iOS, returns `true` if SFSafariViewController is available. + ///Otherwise returns `false`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS + ///{@endtemplate} + Future isAvailable() { + throw UnimplementedError( + 'isAvailable is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.getMaxToolbarItems} + ///The maximum number of allowed secondary toolbar items. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///{@endtemplate} + Future getMaxToolbarItems() { + throw UnimplementedError( + 'getMaxToolbarItems is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.getPackageName} + ///Returns the preferred package to use for Custom Tabs. + ///The preferred package name is the default VIEW intent handler as long as it supports Custom Tabs. + ///To modify this preferred behavior, set [ignoreDefault] to `true` and give a + ///non empty list of package names in packages. + ///This method queries the `PackageManager` to determine which packages support the Custom Tabs API. + ///On apps that target Android 11 and above, this requires adding the following + ///package visibility elements to your manifest. + /// + ///[packages] – Ordered list of packages to test for Custom Tabs support, in decreasing order of priority. + /// + ///[ignoreDefault] – If set, the default VIEW handler won't get priority over other browsers. + /// + ///Returns the preferred package name for handling Custom Tabs, or null. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsClient.getPackageName](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsClient#getPackageName(android.content.Context,java.util.List%3Cjava.lang.String%3E,boolean)))) + ///{@endtemplate} + Future getPackageName( + {List? packages, bool ignoreDefault = false}) { + throw UnimplementedError( + 'getPackageName is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.clearWebsiteData} + ///Clear associated website data accrued from browsing activity within your app. + ///This includes all local storage, cached resources, and cookies. + /// + ///**NOTE for iOS**: available on iOS 16.0+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - SFSafariViewController.DataStore.clearWebsiteData](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller/datastore/3981117-clearwebsitedata)) + ///{@endtemplate} + Future clearWebsiteData() { + throw UnimplementedError( + 'clearWebsiteData is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.prewarmConnections} + ///Prewarms a connection to each URL. SFSafariViewController will automatically use a + ///prewarmed connection if possible when loading its initial URL. + /// + ///Returns a token object that corresponds to the requested URLs. You must keep a strong + ///reference to this token as long as you expect the prewarmed connections to remain open. If the same + ///server is requested in multiple calls to this method, all of the corresponding tokens must be + ///invalidated or released to end the prewarmed connection to that server. + /// + ///This method uses a best-effort approach to prewarming connections, but may delay + ///or drop requests based on the volume of requests made by your app. Use this method when you expect + ///to present the browser soon. Many HTTP servers time out connections after a few minutes. + ///After a timeout, prewarming delivers less performance benefit. + /// + ///[URLs] - the URLs of servers that the browser should prewarm connections to. + ///Only supports URLs with `http://` or `https://` schemes. + /// + ///**NOTE for iOS**: available on iOS 15.0+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - SFSafariViewController.prewarmConnections](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller/3752133-prewarmconnections)) + ///{@endtemplate} + Future prewarmConnections(List URLs) { + throw UnimplementedError( + 'prewarmConnections is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.invalidatePrewarmingToken} + ///Ends all prewarmed connections associated with the token, except for connections that are also kept alive by other tokens. + /// + ///**NOTE for iOS**: available on iOS 15.0+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - SFSafariViewController.prewarmConnections](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller/3752133-prewarmconnections)) + ///{@endtemplate} + Future invalidatePrewarmingToken(PrewarmingToken prewarmingToken) { + throw UnimplementedError( + 'invalidatePrewarmingToken is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.dispose} + ///Disposes the channel and event handler. + ///{@endtemplate} + @override + void dispose() { + throw UnimplementedError( + 'dispose is not implemented on the current platform'); + } +} + +abstract class PlatformChromeSafariBrowserEvents { + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onServiceConnected} + ///Event fired when the when connecting from Android Custom Tabs Service. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///{@endtemplate} + void onServiceConnected() {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onOpened} + ///Event fired when the [PlatformChromeSafariBrowser] is opened. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS + ///{@endtemplate} + void onOpened() {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onCompletedInitialLoad} + ///Event fired when the initial URL load is complete. + /// + ///[didLoadSuccessfully] - `true` if loading completed successfully; otherwise, `false`. Supported only on iOS. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS ([Official API - SFSafariViewControllerDelegate.safariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontrollerdelegate/1621215-safariviewcontroller)) + ///{@endtemplate} + void onCompletedInitialLoad(bool? didLoadSuccessfully) {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onInitialLoadDidRedirect} + ///Event fired when the initial URL load is complete. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - SFSafariViewControllerDelegate.safariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontrollerdelegate/2923545-safariviewcontroller)) + ///{@endtemplate} + void onInitialLoadDidRedirect(WebUri? url) {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onNavigationEvent} + ///Event fired when a navigation event happens. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsCallback.onNavigationEvent](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsCallback#onNavigationEvent(int,android.os.Bundle))) + ///{@endtemplate} + void onNavigationEvent(CustomTabsNavigationEventType? navigationEvent) {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onRelationshipValidationResult} + ///Event fired when a relationship validation result is available. + /// + ///[relation] - Relation for which the result is available. Value previously passed to [PlatformChromeSafariBrowser.validateRelationship]. + /// + ///[requestedOrigin] - Origin requested. Value previously passed to [PlatformChromeSafariBrowser.validateRelationship]. + /// + ///[result] - Whether the relation was validated. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsCallback.onRelationshipValidationResult](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsCallback#onRelationshipValidationResult(int,android.net.Uri,boolean,android.os.Bundle))) + ///{@endtemplate} + void onRelationshipValidationResult( + CustomTabsRelationType? relation, WebUri? requestedOrigin, bool result) {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onWillOpenInBrowser} + ///Event fired when the user opens the current page in the default browser by tapping the toolbar button. + /// + ///**NOTE for iOS**: available on iOS 14.0+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - SFSafariViewControllerDelegate.safariViewControllerWillOpenInBrowser](https://developer.apple.com/documentation/safariservices/sfsafariviewcontrollerdelegate/3650426-safariviewcontrollerwillopeninbr)) + ///{@endtemplate} + void onWillOpenInBrowser() {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onMessageChannelReady} + ///Called when the [PlatformChromeSafariBrowser] has requested a postMessage channel through + ///[PlatformChromeSafariBrowser.requestPostMessageChannel] and the channel is ready for sending and receiving messages on both ends. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsCallback.onMessageChannelReady](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsCallback#onMessageChannelReady(android.os.Bundle))) + ///{@endtemplate} + void onMessageChannelReady() {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onPostMessage} + ///Called when a tab controlled by this [PlatformChromeSafariBrowser] has sent a postMessage. + ///If [PlatformChromeSafariBrowser.postMessage] is called from a single thread, then the messages will be posted in the same order. + ///When received on the client side, it is the client's responsibility to preserve the ordering further. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsCallback.onPostMessage](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsCallback#onPostMessage(java.lang.String,android.os.Bundle))) + ///{@endtemplate} + void onPostMessage(String message) {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onVerticalScrollEvent} + ///Called when a user scrolls the tab. + /// + ///[isDirectionUp] - `false` when the user scrolls farther down the page, + ///and `true` when the user scrolls back up toward the top of the page. + /// + ///**NOTE**: available only if [PlatformChromeSafariBrowser.isEngagementSignalsApiAvailable] returns `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - EngagementSignalsCallback.onVerticalScrollEvent](https://developer.android.com/reference/androidx/browser/customtabs/EngagementSignalsCallback#onVerticalScrollEvent(boolean,android.os.Bundle))) + ///{@endtemplate} + void onVerticalScrollEvent(bool isDirectionUp) {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onGreatestScrollPercentageIncreased} + ///Called when a user has reached a greater scroll percentage on the page. The greatest scroll + ///percentage is reset if the user navigates to a different page. If the current page's total + ///height changes, this method will be called again only if the scroll progress reaches a + ///higher percentage based on the new and current height of the page. + /// + ///[scrollPercentage] - An integer indicating the percent of scrollable progress + ///the user hasmade down the current page. + /// + ///**NOTE**: available only if [PlatformChromeSafariBrowser.isEngagementSignalsApiAvailable] returns `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - EngagementSignalsCallback.onGreatestScrollPercentageIncreased](https://developer.android.com/reference/androidx/browser/customtabs/EngagementSignalsCallback#onGreatestScrollPercentageIncreased(int,android.os.Bundle))) + ///{@endtemplate} + void onGreatestScrollPercentageIncreased(int scrollPercentage) {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onSessionEnded} + ///Called when a `CustomTabsSession` is ending or when no further Engagement Signals + ///callbacks are expected to report whether any user action has occurred during the session. + /// + ///[didUserInteract] - Whether the user has interacted with the page in any way, e.g. scrolling. + /// + ///**NOTE**: available only if [PlatformChromeSafariBrowser.isEngagementSignalsApiAvailable] returns `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android ([Official API - EngagementSignalsCallback.onSessionEnded](https://developer.android.com/reference/androidx/browser/customtabs/EngagementSignalsCallback#onSessionEnded(boolean,android.os.Bundle))) + ///{@endtemplate} + void onSessionEnded(bool didUserInteract) {} + + ///{@template flutter_inappwebview_platform_interface.PlatformChromeSafariBrowser.onClosed} + ///Event fired when the [PlatformChromeSafariBrowser] is closed. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS + ///{@endtemplate} + void onClosed() {} +} diff --git a/lib/src/content_blocker.dart b/flutter_inappwebview_platform_interface/lib/src/content_blocker.dart similarity index 95% rename from lib/src/content_blocker.dart rename to flutter_inappwebview_platform_interface/lib/src/content_blocker.dart index f4f34be0..6d6a6eb7 100755 --- a/lib/src/content_blocker.dart +++ b/flutter_inappwebview_platform_interface/lib/src/content_blocker.dart @@ -44,7 +44,7 @@ class ContentBlockerTrigger { ///A list of regular expressions to match iframes URL against. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ///- MacOS List ifFrameUrl; @@ -52,7 +52,7 @@ class ContentBlockerTrigger { ///A Boolean value indicating if the URL matching should be case-sensitive. ///The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -62,7 +62,7 @@ class ContentBlockerTrigger { ///(how the browser intends to use the resource) that the rule should match. ///If not specified, the rule matches all resource types. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -72,7 +72,7 @@ class ContentBlockerTrigger { ///Values must be lowercase ASCII, or punycode for non-ASCII. ///Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.unlessDomain]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -82,7 +82,7 @@ class ContentBlockerTrigger { ///Values must be lowercase ASCII, or punycode for non-ASCII. ///Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.ifDomain]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -91,7 +91,7 @@ class ContentBlockerTrigger { ///A list of [ContentBlockerTriggerLoadType] that can include one of two mutually exclusive values. ///If not specified, the rule matches all load types. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -100,7 +100,7 @@ class ContentBlockerTrigger { ///A list of strings matched to the entire main document URL; limits the action to a specific list of URL patterns. ///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.unlessTopUrl]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -109,7 +109,7 @@ class ContentBlockerTrigger { ///An array of strings matched to the entire main document URL; acts on any site except URL patterns in provided list. ///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.ifTopUrl]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -117,7 +117,7 @@ class ContentBlockerTrigger { ///An array of strings that specify loading contexts. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ///- MacOS List loadContext; diff --git a/lib/src/context_menu/context_menu.dart b/flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu.dart similarity index 92% rename from lib/src/context_menu/context_menu.dart rename to flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu.dart index 9af6cf0b..6e790c4d 100644 --- a/lib/src/context_menu/context_menu.dart +++ b/flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu.dart @@ -1,13 +1,13 @@ import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; -import '../in_app_webview/webview.dart'; +import '../in_app_webview/platform_webview.dart'; import '../types/in_app_webview_hit_test_result.dart'; import 'context_menu_item.dart'; import 'context_menu_settings.dart'; part 'context_menu.g.dart'; -///Class that represents the WebView context menu. It used by [WebView.contextMenu]. +///Class that represents the WebView context menu. It used by [PlatformWebViewCreationParams.contextMenu]. @SupportedPlatforms(platforms: [ AndroidPlatform( note: diff --git a/lib/src/context_menu/context_menu.g.dart b/flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu.g.dart similarity index 92% rename from lib/src/context_menu/context_menu.g.dart rename to flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu.g.dart index 01e57939..84e63405 100644 --- a/lib/src/context_menu/context_menu.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu.g.dart @@ -6,11 +6,11 @@ part of 'context_menu.dart'; // ExchangeableObjectGenerator // ************************************************************************** -///Class that represents the WebView context menu. It used by [WebView.contextMenu]. +///Class that represents the WebView context menu. It used by [PlatformWebViewCreationParams.contextMenu]. /// ///**NOTE for Android native WebView**: To make it work properly on Android, JavaScript should be enabled! /// -///**Supported Platforms/Implementations**: +///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS class ContextMenu { @@ -40,7 +40,7 @@ class ContextMenu { /// ///**NOTE for Android native WebView**: To make it work properly on Android, JavaScript should be enabled! /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ContextMenu( diff --git a/lib/src/context_menu/context_menu_item.dart b/flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu_item.dart similarity index 100% rename from lib/src/context_menu/context_menu_item.dart rename to flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu_item.dart diff --git a/lib/src/context_menu/context_menu_item.g.dart b/flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu_item.g.dart similarity index 100% rename from lib/src/context_menu/context_menu_item.g.dart rename to flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu_item.g.dart diff --git a/lib/src/context_menu/context_menu_settings.dart b/flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu_settings.dart similarity index 100% rename from lib/src/context_menu/context_menu_settings.dart rename to flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu_settings.dart diff --git a/lib/src/context_menu/context_menu_settings.g.dart b/flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu_settings.g.dart similarity index 100% rename from lib/src/context_menu/context_menu_settings.g.dart rename to flutter_inappwebview_platform_interface/lib/src/context_menu/context_menu_settings.g.dart diff --git a/lib/src/context_menu/main.dart b/flutter_inappwebview_platform_interface/lib/src/context_menu/main.dart similarity index 100% rename from lib/src/context_menu/main.dart rename to flutter_inappwebview_platform_interface/lib/src/context_menu/main.dart diff --git a/lib/src/debug_logging_settings.dart b/flutter_inappwebview_platform_interface/lib/src/debug_logging_settings.dart similarity index 100% rename from lib/src/debug_logging_settings.dart rename to flutter_inappwebview_platform_interface/lib/src/debug_logging_settings.dart diff --git a/flutter_inappwebview_platform_interface/lib/src/find_interaction/main.dart b/flutter_inappwebview_platform_interface/lib/src/find_interaction/main.dart new file mode 100644 index 00000000..59ad6cde --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/find_interaction/main.dart @@ -0,0 +1 @@ +export 'platform_find_interaction_controller.dart'; diff --git a/flutter_inappwebview_platform_interface/lib/src/find_interaction/platform_find_interaction_controller.dart b/flutter_inappwebview_platform_interface/lib/src/find_interaction/platform_find_interaction_controller.dart new file mode 100644 index 00000000..cad92e44 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/find_interaction/platform_find_interaction_controller.dart @@ -0,0 +1,254 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_inappwebview_platform_interface/src/types/disposable.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import '../in_app_webview/in_app_webview_settings.dart'; +import '../debug_logging_settings.dart'; +import '../inappwebview_platform.dart'; +import '../types/main.dart'; + +/// Object specifying creation parameters for creating a [PlatformFindInteractionController]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +@immutable +class PlatformFindInteractionControllerCreationParams { + /// Used by the platform implementation to create a new [PlatformFindInteractionController]. + const PlatformFindInteractionControllerCreationParams( + {this.onFindResultReceived}); + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.onFindResultReceived} + final void Function( + PlatformFindInteractionController controller, + int activeMatchOrdinal, + int numberOfMatches, + bool isDoneCounting)? onFindResultReceived; +} + +///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController} +///This class represents the controller used by the `WebView` to add +///text-finding capabilities, such as the "Find on page" feature. +/// +///**Officially Supported Platforms/Implementations**: +///- Android native WebView +///- iOS +///- MacOS +///{@endtemplate} +abstract class PlatformFindInteractionController extends PlatformInterface + implements Disposable { + ///Debug settings. + static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings(); + + /// Creates a new [PlatformFindInteractionController] + factory PlatformFindInteractionController( + PlatformFindInteractionControllerCreationParams params) { + assert( + InAppWebViewPlatform.instance != null, + 'A platform implementation for `flutter_inappwebview` has not been set. Please ' + 'ensure that an implementation of `InAppWebViewPlatform` has been set to ' + '`InAppWebViewPlatform.instance` before use. For unit testing, ' + '`InAppWebViewPlatform.instance` can be set with your own test implementation.', + ); + final PlatformFindInteractionController webViewControllerDelegate = + InAppWebViewPlatform.instance! + .createPlatformFindInteractionController(params); + PlatformInterface.verify(webViewControllerDelegate, _token); + return webViewControllerDelegate; + } + + /// Used by the platform implementation to create a new [PlatformFindInteractionController]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformFindInteractionController.implementation(this.params) + : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformFindInteractionController]. + final PlatformFindInteractionControllerCreationParams params; + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.onFindResultReceived} + ///Event fired as find-on-page operations progress. + ///The listener may be notified multiple times while the operation is underway, and the [numberOfMatches] value should not be considered final unless [isDoneCounting] is true. + /// + ///[activeMatchOrdinal] represents the zero-based ordinal of the currently selected match. + /// + ///[numberOfMatches] represents how many matches have been found. + /// + ///[isDoneCounting] whether the find operation has actually completed. + /// + ///**NOTE**: on iOS, if [InAppWebViewSettings.isFindInteractionEnabled] is `true`, this event will not be called. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.FindListener.onFindResultReceived](https://developer.android.com/reference/android/webkit/WebView.FindListener#onFindResultReceived(int,%20int,%20boolean))) + ///- iOS + ///- MacOS + ///{@endtemplate} + void Function(PlatformFindInteractionController controller, + int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting)? + get onFindResultReceived => params.onFindResultReceived; + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.findAll} + ///Finds all instances of find on the page and highlights them. Notifies [PlatformFindInteractionController.onFindResultReceived] listener. + /// + ///[find] represents the string to find. + /// + ///**NOTE**: on Android native WebView, it finds all instances asynchronously. Successive calls to this will cancel any pending searches. + /// + ///**NOTE**: on iOS, if [InAppWebViewSettings.isFindInteractionEnabled] is `true`, + ///it uses the built-in find interaction native UI, + ///otherwise this is implemented using CSS and Javascript. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.findAllAsync](https://developer.android.com/reference/android/webkit/WebView#findAllAsync(java.lang.String))) + ///- iOS (if [InAppWebViewSettings.isFindInteractionEnabled] is `true`: [Official API - UIFindInteraction.presentFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975832-presentfindnavigator?changes=_2) with [Official API - UIFindInteraction.searchText](https://developer.apple.com/documentation/uikit/uifindinteraction/3975834-searchtext?changes=_2)) + ///- MacOS + ///{@endtemplate} + Future findAll({String? find}) { + throw UnimplementedError( + 'findAll is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.findNext} + ///Highlights and scrolls to the next match found by [findAll]. Notifies [PlatformFindInteractionController.onFindResultReceived] listener. + /// + ///[forward] represents the direction to search. The default value is `true`, meaning forward. + /// + ///**NOTE**: on iOS, if [InAppWebViewSettings.isFindInteractionEnabled] is `true`, + ///it uses the built-in find interaction native UI, + ///otherwise this is implemented using CSS and Javascript. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.findNext](https://developer.android.com/reference/android/webkit/WebView#findNext(boolean))) + ///- iOS (if [InAppWebViewSettings.isFindInteractionEnabled] is `true`: [Official API - UIFindInteraction.findNext](https://developer.apple.com/documentation/uikit/uifindinteraction/3975829-findnext?changes=_2) and ([Official API - UIFindInteraction.findPrevious](https://developer.apple.com/documentation/uikit/uifindinteraction/3975830-findprevious?changes=_2))) + ///- MacOS + ///{@endtemplate} + Future findNext({bool forward = true}) { + throw UnimplementedError( + 'findNext is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.clearMatches} + ///Clears the highlighting surrounding text matches created by [findAll]. + /// + ///**NOTE**: on iOS, if [InAppWebViewSettings.isFindInteractionEnabled] is `true`, + ///it uses the built-in find interaction native UI, + ///otherwise this is implemented using CSS and Javascript. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.clearMatches](https://developer.android.com/reference/android/webkit/WebView#clearMatches())) + ///- iOS (if [InAppWebViewSettings.isFindInteractionEnabled] is `true`: [Official API - UIFindInteraction.dismissFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975827-dismissfindnavigator?changes=_2)) + ///- MacOS + ///{@endtemplate} + Future clearMatches() { + throw UnimplementedError( + 'clearMatches is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.setSearchText} + ///Pre-populate the search text to be used. + /// + ///On iOS, if [InAppWebViewSettings.isFindInteractionEnabled] is `true, + ///it will pre-populate the system find panel's search text field with a search query. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ([Official API - UIFindInteraction.searchText](https://developer.apple.com/documentation/uikit/uifindinteraction/3975834-searchtext?changes=_2)) + ///- MacOS + ///{@endtemplate} + Future setSearchText(String? searchText) { + throw UnimplementedError( + 'setSearchText is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.getSearchText} + ///Get the search text used. + /// + ///On iOS, if [InAppWebViewSettings.isFindInteractionEnabled] is `true, + ///it will get the system find panel's search text field value. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ([Official API - UIFindInteraction.searchText](https://developer.apple.com/documentation/uikit/uifindinteraction/3975834-searchtext?changes=_2)) + ///- MacOS + ///{@endtemplate} + Future getSearchText() { + throw UnimplementedError( + 'getSearchText is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.isFindNavigatorVisible} + ///A Boolean value that indicates when the find panel displays onscreen. + /// + ///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - UIFindInteraction.isFindNavigatorVisible](https://developer.apple.com/documentation/uikit/uifindinteraction/3975828-isfindnavigatorvisible?changes=_2)) + ///{@endtemplate} + Future isFindNavigatorVisible() { + throw UnimplementedError( + 'isFindNavigatorVisible is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.updateResultCount} + ///Updates the results the interface displays for the active search. + /// + ///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - UIFindInteraction.updateResultCount](https://developer.apple.com/documentation/uikit/uifindinteraction/3975835-updateresultcount?changes=_2)) + ///{@endtemplate} + Future updateResultCount() { + throw UnimplementedError( + 'updateResultCount is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.presentFindNavigator} + ///Begins a search, displaying the find panel. + /// + ///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - UIFindInteraction.presentFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975832-presentfindnavigator?changes=_2)) + ///{@endtemplate} + Future presentFindNavigator() { + throw UnimplementedError( + 'presentFindNavigator is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.dismissFindNavigator} + ///Dismisses the find panel, if present. + /// + ///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - UIFindInteraction.dismissFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975827-dismissfindnavigator?changes=_2)) + ///{@endtemplate} + Future dismissFindNavigator() { + throw UnimplementedError( + 'dismissFindNavigator is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.getActiveFindSession} + ///If there's a currently active find session, returns the active find session. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ([Official API - UIFindInteraction.activeFindSession](https://developer.apple.com/documentation/uikit/uifindinteraction/3975825-activefindsession?changes=_7____4_8&language=objc)) + ///- MacOS + ///{@endtemplate} + Future getActiveFindSession() { + throw UnimplementedError( + 'getActiveFindSession is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformFindInteractionController.dispose} + ///Disposes the controller. + ///{@endtemplate} + @override + void dispose({bool isKeepAlive = false}) { + throw UnimplementedError( + 'dispose is not implemented on the current platform'); + } +} diff --git a/lib/src/in_app_browser/android/in_app_browser_options.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/android/in_app_browser_options.dart similarity index 94% rename from lib/src/in_app_browser/android/in_app_browser_options.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_browser/android/in_app_browser_options.dart index 1ab5ba24..700f37dc 100755 --- a/lib/src/in_app_browser/android/in_app_browser_options.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/android/in_app_browser_options.dart @@ -1,9 +1,9 @@ import '../../in_app_webview/android/in_app_webview_options.dart'; import '../in_app_browser_settings.dart'; -import '../in_app_browser.dart'; +import '../platform_in_app_browser.dart'; -///This class represents all the Android-only [InAppBrowser] options available. +///This class represents all the Android-only [PlatformInAppBrowser] options available. ///Use [InAppBrowserSettings] instead. @Deprecated('Use InAppBrowserSettings instead') class AndroidInAppBrowserOptions implements BrowserOptions, AndroidOptions { diff --git a/lib/src/in_app_browser/android/main.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/android/main.dart similarity index 100% rename from lib/src/in_app_browser/android/main.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_browser/android/main.dart diff --git a/lib/src/in_app_browser/apple/in_app_browser_options.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/apple/in_app_browser_options.dart similarity index 96% rename from lib/src/in_app_browser/apple/in_app_browser_options.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_browser/apple/in_app_browser_options.dart index e34c1127..d5cdc540 100755 --- a/lib/src/in_app_browser/apple/in_app_browser_options.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/apple/in_app_browser_options.dart @@ -3,12 +3,12 @@ import 'dart:ui'; import '../../in_app_webview/apple/in_app_webview_options.dart'; import '../in_app_browser_settings.dart'; -import '../in_app_browser.dart'; +import '../platform_in_app_browser.dart'; import '../../types/main.dart'; import '../../util.dart'; -///This class represents all the iOS-only [InAppBrowser] options available. +///This class represents all the iOS-only [PlatformInAppBrowser] options available. ///Use [InAppBrowserSettings] instead. @Deprecated('Use InAppBrowserSettings instead') class IOSInAppBrowserOptions implements BrowserOptions, IosOptions { diff --git a/lib/src/in_app_browser/apple/main.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/apple/main.dart similarity index 100% rename from lib/src/in_app_browser/apple/main.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_browser/apple/main.dart diff --git a/lib/src/in_app_browser/in_app_browser_menu_item.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_menu_item.dart similarity index 93% rename from lib/src/in_app_browser/in_app_browser_menu_item.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_menu_item.dart index a9e7d239..5f082e7a 100644 --- a/lib/src/in_app_browser/in_app_browser_menu_item.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_menu_item.dart @@ -5,7 +5,7 @@ import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_i import '../util.dart'; -import 'in_app_browser.dart'; +import 'platform_in_app_browser.dart'; import '../types/main.dart'; part 'in_app_browser_menu_item.g.dart'; @@ -30,7 +30,7 @@ dynamic _deserializeIcon(dynamic icon) { return null; } -///Class that represents a custom menu item for a [InAppBrowser] instance. +///Class that represents a custom menu item for a [PlatformInAppBrowser] instance. @SupportedPlatforms(platforms: [ AndroidPlatform(), IOSPlatform(), diff --git a/lib/src/in_app_browser/in_app_browser_menu_item.g.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_menu_item.g.dart similarity index 89% rename from lib/src/in_app_browser/in_app_browser_menu_item.g.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_menu_item.g.dart index e7382b27..90620b74 100644 --- a/lib/src/in_app_browser/in_app_browser_menu_item.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_menu_item.g.dart @@ -6,9 +6,9 @@ part of 'in_app_browser_menu_item.dart'; // ExchangeableObjectGenerator // ************************************************************************** -///Class that represents a custom menu item for a [InAppBrowser] instance. +///Class that represents a custom menu item for a [PlatformInAppBrowser] instance. /// -///**Supported Platforms/Implementations**: +///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -18,7 +18,7 @@ class InAppBrowserMenuItem { ///Icon color. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS 13.0+ ///- MacOS @@ -40,7 +40,7 @@ class InAppBrowserMenuItem { String title; /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS diff --git a/lib/src/in_app_browser/in_app_browser_settings.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_settings.dart similarity index 89% rename from lib/src/in_app_browser/in_app_browser_settings.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_settings.dart index d8d61dff..d75cafb8 100755 --- a/lib/src/in_app_browser/in_app_browser_settings.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_settings.dart @@ -1,7 +1,5 @@ import 'dart:ui'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_inappwebview/src/types/main.dart'; import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; import '../types/in_app_webview_rect.dart'; @@ -101,7 +99,7 @@ class InAppBrowserSettings_ ///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`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -109,7 +107,7 @@ class InAppBrowserSettings_ ///Set to `true` to hide the toolbar at the top of the WebView. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -117,7 +115,7 @@ class InAppBrowserSettings_ ///Set the custom background color of the toolbar at the top. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -125,7 +123,7 @@ class InAppBrowserSettings_ ///Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -133,7 +131,7 @@ class InAppBrowserSettings_ ///Set to `true` to hide the progress bar when the WebView is loading a page. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -141,137 +139,137 @@ class InAppBrowserSettings_ ///Set to `true` to hide the default menu items. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS bool? hideDefaultMenuItems; ///Set to `true` if you want the title should be displayed. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView bool? hideTitleBar; ///Set the action bar's title. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- MacOS String? toolbarTopFixedTitle; ///Set to `false` to not close the InAppBrowser when the user click on the Android back button and the WebView cannot go back to the history. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView bool? closeOnCannotGoBack; ///Set to `false` to block the InAppBrowser WebView going back when the user click on the Android back button. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView bool? allowGoBackWithBackButton; ///Set to `true` to close the InAppBrowser when the user click on the Android back button. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView bool? shouldCloseOnBackButtonPressed; ///Set to `true` to set the toolbar at the top translucent. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? toolbarTopTranslucent; ///Set the tint color to apply to the navigation bar background. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color_? toolbarTopBarTintColor; ///Set the tint color to apply to the navigation items and bar button items. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color_? toolbarTopTintColor; ///Set to `true` to hide the toolbar at the bottom of the WebView. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? hideToolbarBottom; ///Set the custom background color of the toolbar at the bottom. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color_? toolbarBottomBackgroundColor; ///Set the tint color to apply to the bar button items. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color_? toolbarBottomTintColor; ///Set to `true` to set the toolbar at the bottom translucent. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? toolbarBottomTranslucent; ///Set the custom text for the close button. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS String? closeButtonCaption; ///Set the custom color for the close button. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color_? closeButtonColor; ///Set to `true` to hide the close button. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? hideCloseButton; ///Set the custom color for the menu button. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color_? menuButtonColor; ///Set the custom modal presentation style when presenting the WebView. The default value is [ModalPresentationStyle.FULL_SCREEN]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ModalPresentationStyle_? presentationStyle; ///Set to the custom transition style when presenting the WebView. The default value is [ModalTransitionStyle.COVER_VERTICAL]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ModalTransitionStyle_? transitionStyle; ///How the browser window should be added to the main window. ///The default value is [WindowType.CHILD]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- MacOS WindowType_? windowType; ///The window’s alpha value. ///The default value is `1.0`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- MacOS double? windowAlphaValue; ///Flags that describe the window’s current style, such as if it’s resizable or in full-screen mode. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- MacOS WindowStyleMask_? windowStyleMask; @@ -279,14 +277,14 @@ class InAppBrowserSettings_ /// ///**NOTE for MacOS**: available on MacOS 11.0+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- MacOS WindowTitlebarSeparatorStyle_? windowTitlebarSeparatorStyle; ///Sets the origin and size of the window’s frame rectangle according to a given frame rectangle, ///thereby setting its position and size onscreen. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- MacOS InAppWebViewRect_? windowFrame; diff --git a/lib/src/in_app_browser/in_app_browser_settings.g.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_settings.g.dart similarity index 88% rename from lib/src/in_app_browser/in_app_browser_settings.g.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_settings.g.dart index 9d18bc54..4c2fe0f6 100644 --- a/lib/src/in_app_browser/in_app_browser_settings.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_settings.g.dart @@ -11,32 +11,32 @@ class InAppBrowserSettings implements BrowserOptions, AndroidOptions, IosOptions { ///Set to `false` to block the InAppBrowser WebView going back when the user click on the Android back button. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView bool? allowGoBackWithBackButton; ///Set the custom text for the close button. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS String? closeButtonCaption; ///Set the custom color for the close button. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color? closeButtonColor; ///Set to `false` to not close the InAppBrowser when the user click on the Android back button and the WebView cannot go back to the history. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView bool? closeOnCannotGoBack; ///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`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -44,20 +44,20 @@ class InAppBrowserSettings ///Set to `true` to hide the close button. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? hideCloseButton; ///Set to `true` to hide the default menu items. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS bool? hideDefaultMenuItems; ///Set to `true` to hide the progress bar when the WebView is loading a page. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -65,19 +65,19 @@ class InAppBrowserSettings ///Set to `true` if you want the title should be displayed. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView bool? hideTitleBar; ///Set to `true` to hide the toolbar at the bottom of the WebView. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? hideToolbarBottom; ///Set to `true` to hide the toolbar at the top of the WebView. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -85,7 +85,7 @@ class InAppBrowserSettings ///Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -93,43 +93,43 @@ class InAppBrowserSettings ///Set the custom color for the menu button. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color? menuButtonColor; ///Set the custom modal presentation style when presenting the WebView. The default value is [ModalPresentationStyle.FULL_SCREEN]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ModalPresentationStyle? presentationStyle; ///Set to `true` to close the InAppBrowser when the user click on the Android back button. The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView bool? shouldCloseOnBackButtonPressed; ///Set the custom background color of the toolbar at the bottom. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color? toolbarBottomBackgroundColor; ///Set the tint color to apply to the bar button items. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color? toolbarBottomTintColor; ///Set to `true` to set the toolbar at the bottom translucent. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? toolbarBottomTranslucent; ///Set the custom background color of the toolbar at the top. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ///- MacOS @@ -137,52 +137,52 @@ class InAppBrowserSettings ///Set the tint color to apply to the navigation bar background. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color? toolbarTopBarTintColor; ///Set the action bar's title. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ///- MacOS String? toolbarTopFixedTitle; ///Set the tint color to apply to the navigation items and bar button items. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS Color? toolbarTopTintColor; ///Set to `true` to set the toolbar at the top translucent. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS bool? toolbarTopTranslucent; ///Set to the custom transition style when presenting the WebView. The default value is [ModalTransitionStyle.COVER_VERTICAL]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ModalTransitionStyle? transitionStyle; ///The window’s alpha value. ///The default value is `1.0`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- MacOS double? windowAlphaValue; ///Sets the origin and size of the window’s frame rectangle according to a given frame rectangle, ///thereby setting its position and size onscreen. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- MacOS InAppWebViewRect? windowFrame; ///Flags that describe the window’s current style, such as if it’s resizable or in full-screen mode. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- MacOS WindowStyleMask? windowStyleMask; @@ -190,14 +190,14 @@ class InAppBrowserSettings /// ///**NOTE for MacOS**: available on MacOS 11.0+. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- MacOS WindowTitlebarSeparatorStyle? windowTitlebarSeparatorStyle; ///How the browser window should be added to the main window. ///The default value is [WindowType.CHILD]. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- MacOS WindowType? windowType; InAppBrowserSettings( diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/main.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/main.dart new file mode 100644 index 00000000..1b7b1bec --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/main.dart @@ -0,0 +1,11 @@ +export 'platform_in_app_browser.dart'; +export 'in_app_browser_settings.dart' + show + InAppBrowserClassSettings, + BrowserOptions, + InAppBrowserSettings, + InAppBrowserClassOptions, + InAppBrowserOptions; +export 'android/main.dart'; +export 'apple/main.dart'; +export 'in_app_browser_menu_item.dart' show InAppBrowserMenuItem; diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart new file mode 100755 index 00000000..ae8f8860 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart @@ -0,0 +1,1362 @@ +import 'dart:async'; +import 'dart:collection'; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/src/types/disposable.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import '../context_menu/context_menu.dart'; +import '../find_interaction/platform_find_interaction_controller.dart'; +import '../inappwebview_platform.dart'; +import '../pull_to_refresh/main.dart'; +import '../types/main.dart'; + +import '../in_app_webview/platform_inappwebview_controller.dart'; +import '../in_app_webview/in_app_webview_settings.dart'; + +import '../print_job/main.dart'; +import '../web_uri.dart'; +import 'in_app_browser_menu_item.dart'; +import 'in_app_browser_settings.dart'; +import '../debug_logging_settings.dart'; +import '../pull_to_refresh/platform_pull_to_refresh_controller.dart'; + +/// Object specifying creation parameters for creating a [PlatformInAppBrowser]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +@immutable +class PlatformInAppBrowserCreationParams { + /// Used by the platform implementation to create a new [PlatformInAppBrowser]. + const PlatformInAppBrowserCreationParams( + {this.contextMenu, + this.pullToRefreshController, + this.findInteractionController, + this.initialUserScripts, + this.windowId}); + + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser.contextMenu} + final ContextMenu? contextMenu; + + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser.pullToRefreshController} + final PlatformPullToRefreshController? pullToRefreshController; + + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser.findInteractionController} + final PlatformFindInteractionController? findInteractionController; + + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser.initialUserScripts} + final UnmodifiableListView? initialUserScripts; + + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser.windowId} + final int? windowId; +} + +///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser} +///This class represents a native WebView displayed on top of the Flutter App, +///so it's not integrated into the Flutter widget tree. +///It uses the native WebView of the platform. +///The [webViewController] field can be used to access the [PlatformInAppWebViewController] API. +/// +///**Officially Supported Platforms/Implementations**: +///- Android native WebView +///- iOS +///- MacOS +///@{endtemplate} +abstract class PlatformInAppBrowser extends PlatformInterface + implements Disposable { + ///Debug settings. + static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings(); + + /// Event handler object that handles the [PlatformInAppBrowser] events. + PlatformInAppBrowserEvents? eventHandler; + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.id} + ///View ID used internally. + ///@{endtemplate} + String get id { + throw UnimplementedError('id is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.contextMenu} + ///Context menu used by the browser. It should be set before opening the browser. + ///@{endtemplate} + ContextMenu? get contextMenu => params.contextMenu; + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.pullToRefreshController} + ///Represents the pull-to-refresh feature controller. + ///@{endtemplate} + PlatformPullToRefreshController? get pullToRefreshController => + params.pullToRefreshController; + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.findInteractionController} + ///Represents the find interaction feature controller. + ///@{endtemplate} + PlatformFindInteractionController? get findInteractionController => + params.findInteractionController; + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.initialUserScripts} + ///Initial list of user scripts to be loaded at start or end of a page loading. + ///@{endtemplate} + UnmodifiableListView? get initialUserScripts => + params.initialUserScripts; + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.windowId} + ///The window id of a [CreateWindowAction.windowId]. + ///@{endtemplate} + int? get windowId => params.windowId; + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.webViewController} + ///WebView Controller that can be used to access the [PlatformInAppWebViewController] API. + ///When [onExit] is fired, this will be `null` and cannot be used anymore. + ///@{endtemplate} + PlatformInAppWebViewController? get webViewController { + throw UnimplementedError( + 'webViewController is not implemented on the current platform'); + } + + /// Creates a new [PlatformInAppBrowser] + factory PlatformInAppBrowser(PlatformInAppBrowserCreationParams params) { + assert( + InAppWebViewPlatform.instance != null, + 'A platform implementation for `flutter_inappwebview` has not been set. Please ' + 'ensure that an implementation of `InAppWebViewPlatform` has been set to ' + '`InAppWebViewPlatform.instance` before use. For unit testing, ' + '`InAppWebViewPlatform.instance` can be set with your own test implementation.', + ); + final PlatformInAppBrowser inAppBrowser = + InAppWebViewPlatform.instance!.createPlatformInAppBrowser(params); + PlatformInterface.verify(inAppBrowser, _token); + return inAppBrowser; + } + + /// Creates a new [PlatformInAppBrowser] to access static methods. + factory PlatformInAppBrowser.static() { + assert( + InAppWebViewPlatform.instance != null, + 'A platform implementation for `flutter_inappwebview` has not been set. Please ' + 'ensure that an implementation of `InAppWebViewPlatform` has been set to ' + '`InAppWebViewPlatform.instance` before use. For unit testing, ' + '`InAppWebViewPlatform.instance` can be set with your own test implementation.', + ); + final PlatformInAppBrowser inAppBrowserStatic = + InAppWebViewPlatform.instance!.createPlatformInAppBrowserStatic(); + PlatformInterface.verify(inAppBrowserStatic, _token); + return inAppBrowserStatic; + } + + /// Used by the platform implementation to create a new [PlatformInAppBrowser]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformInAppBrowser.implementation(this.params) : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformInAppBrowser]. + final PlatformInAppBrowserCreationParams params; + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.openUrlRequest} + ///Opens the [PlatformInAppBrowser] instance with an [urlRequest]. + /// + ///[urlRequest]: The [urlRequest] to load. + /// + ///[options]: Options for the [PlatformInAppBrowser]. + /// + ///[settings]: Settings for the [PlatformInAppBrowser]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + Future openUrlRequest( + {required URLRequest urlRequest, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) { + throw UnimplementedError( + 'openUrlRequest is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.openFile} + ///Opens the [PlatformInAppBrowser] instance with the given [assetFilePath] file. + /// + ///[options]: Options for the [PlatformInAppBrowser]. + /// + ///To be able to load your local files (assets, js, css, etc.), you need to add them in the `assets` section of the `pubspec.yaml` file, otherwise they cannot be found! + /// + ///Example of a `pubspec.yaml` file: + ///```yaml + ///... + /// + ///# The following section is specific to Flutter. + ///flutter: + /// + /// # The following line ensures that the Material Icons font is + /// # included with your application, so that you can use the icons in + /// # the material Icons class. + /// uses-material-design: true + /// + /// assets: + /// - assets/index.html + /// - assets/css/ + /// - assets/images/ + /// + ///... + ///``` + ///Example of a `main.dart` file: + ///```dart + ///... + ///inAppBrowser.openFile(assetFilePath: "assets/index.html"); + ///... + ///``` + /// + ///[headers]: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value. + /// + ///[options]: Options for the [PlatformInAppBrowser]. + /// + ///[settings]: Settings for the [PlatformInAppBrowser]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + Future openFile( + {required String assetFilePath, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) { + throw UnimplementedError( + 'openFile is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.openData} + ///Opens the [PlatformInAppBrowser] instance with [data] as a content, using [baseUrl] as the base URL for it. + /// + ///The [mimeType] parameter specifies the format of the data. The default value is `"text/html"`. + /// + ///The [encoding] parameter specifies the encoding of the data. The default value is `"utf8"`. + /// + ///The [androidHistoryUrl] parameter is the URL to use as the history entry. The default value is `about:blank`. If non-null, this must be a valid URL. This parameter is used only on Android. + /// + ///The [options] parameter specifies the options for the [PlatformInAppBrowser]. + /// + ///[settings]: Settings for the [PlatformInAppBrowser]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + Future openData( + {required String data, + String mimeType = "text/html", + String encoding = "utf8", + WebUri? baseUrl, + @Deprecated("Use historyUrl instead") Uri? androidHistoryUrl, + WebUri? historyUrl, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) { + throw UnimplementedError( + 'openData is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.openWithSystemBrowser} + ///This is a static method that opens an [url] in the system browser. You wont be able to use the [PlatformInAppBrowser] methods here! + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + Future openWithSystemBrowser({required WebUri url}) { + throw UnimplementedError( + 'openWithSystemBrowser is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.addMenuItem} + ///Adds a [InAppBrowserMenuItem] to the menu. + ///If the browser is already open, + ///it will take effect the next time it is opened. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS 14.0+ + ///{@endtemplate} + void addMenuItem(InAppBrowserMenuItem menuItem) { + throw UnimplementedError( + 'addMenuItem is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.addMenuItems} + ///Adds a list of [InAppBrowserMenuItem] to the menu. + ///If the browser is already open, + ///it will take effect the next time it is opened. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS 14.0+ + ///{@endtemplate} + void addMenuItems(List menuItems) { + throw UnimplementedError( + 'addMenuItems is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.removeMenuItem} + ///Removes the [menuItem] from the list. + ///Returns `true` if it was in the list, `false` otherwise. + ///If the browser is already open, + ///it will take effect the next time it is opened. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS 14.0+ + ///{@endtemplate} + bool removeMenuItem(InAppBrowserMenuItem menuItem) { + throw UnimplementedError( + 'removeMenuItem is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.removeMenuItems} + ///Removes a list of [menuItems] from the list. + ///If the browser is already open, + ///it will take effect the next time it is opened. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS 14.0+ + ///{@endtemplate} + void removeMenuItems(List menuItems) { + throw UnimplementedError( + 'removeMenuItems is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.removeAllMenuItem} + ///Removes all the menu items from the list. + ///If the browser is already open, + ///it will take effect the next time it is opened. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android + ///- iOS 14.0+ + ///{@endtemplate} + void removeAllMenuItem() { + throw UnimplementedError( + 'removeAllMenuItem is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.hasMenuItem} + ///Returns `true` if the [menuItem] has been already added, otherwise `false`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS 14.0+ + ///{@endtemplate} + bool hasMenuItem(InAppBrowserMenuItem menuItem) { + throw UnimplementedError( + 'hasMenuItem is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.show} + ///Displays a [PlatformInAppBrowser] window that was opened hidden. Calling this has no effect if the [PlatformInAppBrowser] was already visible. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + Future show() { + throw UnimplementedError('show is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.hide} + ///Hides the [PlatformInAppBrowser] window. Calling this has no effect if the [PlatformInAppBrowser] was already hidden. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + Future hide() { + throw UnimplementedError('hide is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.close} + ///Closes the [PlatformInAppBrowser] window. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + Future close() { + throw UnimplementedError( + 'close is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.isHidden} + ///Check if the Web View of the [PlatformInAppBrowser] instance is hidden. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + Future isHidden() { + throw UnimplementedError( + 'isHidden is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.setOptions} + ///Use [setSettings] instead. + ///{@endtemplate} + @Deprecated('Use setSettings instead') + Future setOptions({required InAppBrowserClassOptions options}) { + throw UnimplementedError( + 'setOptions is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.getOptions} + ///Use [getSettings] instead. + ///{@endtemplate} + @Deprecated('Use getSettings instead') + Future getOptions() { + throw UnimplementedError( + 'getOptions is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.setSettings} + ///Sets the [PlatformInAppBrowser] settings with the new [settings] and evaluates them. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + Future setSettings({required InAppBrowserClassSettings settings}) { + throw UnimplementedError( + 'setSettings is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.getSettings} + ///Gets the current [PlatformInAppBrowser] settings. Returns `null` if it wasn't able to get them. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + Future getSettings() { + throw UnimplementedError( + 'getSettings is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.isOpened} + ///Returns `true` if the [PlatformInAppBrowser] instance is opened, otherwise `false`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///{@endtemplate} + bool isOpened() { + throw UnimplementedError( + 'isOpened is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.dispose} + ///Disposes the channel and controllers. + ///{@endtemplate} + @override + void dispose() { + throw UnimplementedError( + 'dispose is not implemented on the current platform'); + } +} + +abstract class PlatformInAppBrowserEvents { + ///Event fired when the [PlatformInAppBrowser] is created. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + void onBrowserCreated() {} + + ///Event fired when the [PlatformInAppBrowser] window is closed. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + void onExit() {} + + ///Event fired when the [PlatformInAppBrowser] starts to load an [url]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onPageStarted](https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) + void onLoadStart(WebUri? url) {} + + ///Event fired when the [PlatformInAppBrowser] finishes loading an [url]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onPageFinished](https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) + void onLoadStop(WebUri? url) {} + + ///Use [onReceivedError] instead. + @Deprecated("Use onReceivedError instead") + void onLoadError(Uri? url, int code, String message) {} + + ///Event fired when the [PlatformInAppBrowser] encounters an [error] loading a [request]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onReceivedError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceError))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) + void onReceivedError(WebResourceRequest request, WebResourceError error) {} + + ///Use [onReceivedHttpError] instead. + @Deprecated("Use onReceivedHttpError instead") + void onLoadHttpError(Uri? url, int statusCode, String description) {} + + ///Event fired when the [PlatformInAppBrowser] receives an HTTP error. + /// + ///[request] represents the originating request. + /// + ///[errorResponse] represents the information about the error occurred. + /// + ///**NOTE**: available on Android 23+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onReceivedHttpError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) + void onReceivedHttpError( + WebResourceRequest request, WebResourceResponse errorResponse) {} + + ///Event fired when the current [progress] (range 0-100) of loading a page is changed. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onProgressChanged](https://developer.android.com/reference/android/webkit/WebChromeClient#onProgressChanged(android.webkit.WebView,%20int))) + ///- iOS + ///- MacOS + void onProgressChanged(int progress) {} + + ///Event fired when the [PlatformInAppBrowser] webview receives a [ConsoleMessage]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onConsoleMessage](https://developer.android.com/reference/android/webkit/WebChromeClient#onConsoleMessage(android.webkit.ConsoleMessage))) + ///- iOS + ///- MacOS + void onConsoleMessage(ConsoleMessage consoleMessage) {} + + ///Give the host application a chance to take control when a URL is about to be loaded in the current WebView. This event is not called on the initial load of the WebView. + /// + ///Note that on Android there isn't any way to load an URL for a frame that is not the main frame, so if the request is not for the main frame, the navigation is allowed by default. + ///However, if you want to cancel requests for subframes, you can use the [InAppWebViewSettings.regexToCancelSubFramesLoading] setting + ///to write a Regular Expression that, if the url request of a subframe matches, then the request of that subframe is canceled. + /// + ///Also, on Android, this method is not called for POST requests. + /// + ///[navigationAction] represents an object that contains information about an action that causes navigation to occur. + /// + ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewSettings.useShouldOverrideUrlLoading] setting to `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.shouldOverrideUrlLoading](https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20java.lang.String))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview)) + Future? shouldOverrideUrlLoading( + NavigationAction navigationAction) { + return null; + } + + ///Event fired when the [PlatformInAppBrowser] webview loads a resource. + /// + ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewSettings.useOnLoadResource] and [InAppWebViewSettings.javaScriptEnabled] setting to `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + void onLoadResource(LoadedResource resource) {} + + ///Event fired when the [PlatformInAppBrowser] webview scrolls. + /// + ///[x] represents the current horizontal scroll origin in pixels. + /// + ///[y] represents the current vertical scroll origin in pixels. + /// + ///**NOTE for MacOS**: this method is implemented with using JavaScript. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.onScrollChanged](https://developer.android.com/reference/android/webkit/WebView#onScrollChanged(int,%20int,%20int,%20int))) + ///- iOS ([Official API - UIScrollViewDelegate.scrollViewDidScroll](https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619392-scrollviewdidscroll)) + ///- MacOS + void onScrollChanged(int x, int y) {} + + ///Use [onDownloadStartRequest] instead + @Deprecated('Use onDownloadStartRequest instead') + void onDownloadStart(Uri url) {} + + ///Event fired when `WebView` recognizes a downloadable file. + ///To download the file, you can use the [flutter_downloader](https://pub.dev/packages/flutter_downloader) plugin. + /// + ///[downloadStartRequest] represents the request of the file to download. + /// + ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewSettings.useOnDownloadStart] setting to `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.setDownloadListener](https://developer.android.com/reference/android/webkit/WebView#setDownloadListener(android.webkit.DownloadListener))) + ///- iOS + ///- MacOS + void onDownloadStartRequest(DownloadStartRequest downloadStartRequest) {} + + ///Use [onLoadResourceWithCustomScheme] instead. + @Deprecated('Use onLoadResourceWithCustomScheme instead') + Future? onLoadResourceCustomScheme(Uri url) { + return null; + } + + ///Event fired when the [PlatformInAppBrowser] webview finds the `custom-scheme` while loading a resource. + ///Here you can handle the url [request] and return a [CustomSchemeResponse] to load a specific resource encoded to `base64`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ([Official API - WKURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkurlschemehandler)) + ///- MacOS ([Official API - WKURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkurlschemehandler)) + Future? onLoadResourceWithCustomScheme( + WebResourceRequest request) { + return null; + } + + ///Event fired when the [PlatformInAppBrowser] webview requests the host application to create a new window, + ///for example when trying to open a link with `target="_blank"` or when `window.open()` is called by JavaScript side. + ///If the host application chooses to honor this request, it should return `true` from this method, create a new WebView to host the window. + ///If the host application chooses not to honor the request, it should return `false` from this method. + ///The default implementation of this method does nothing and hence returns `false`. + /// + ///[createWindowAction] represents the request. + /// + ///**NOTE**: to allow JavaScript to open windows, you need to set [InAppWebViewSettings.javaScriptCanOpenWindowsAutomatically] setting to `true`. + /// + ///**NOTE**: on Android you need to set [InAppWebViewSettings.supportMultipleWindows] setting to `true`. + /// + ///**NOTE**: on iOS and MacOS, setting these initial settings: [InAppWebViewSettings.supportZoom], [InAppWebViewSettings.useOnLoadResource], [InAppWebViewSettings.useShouldInterceptAjaxRequest], + ///[InAppWebViewSettings.useShouldInterceptFetchRequest], [InAppWebViewSettings.applicationNameForUserAgent], [InAppWebViewSettings.javaScriptCanOpenWindowsAutomatically], + ///[InAppWebViewSettings.javaScriptEnabled], [InAppWebViewSettings.minimumFontSize], [InAppWebViewSettings.preferredContentMode], [InAppWebViewSettings.incognito], + ///[InAppWebViewSettings.cacheEnabled], [InAppWebViewSettings.mediaPlaybackRequiresUserGesture], + ///[InAppWebViewSettings.resourceCustomSchemes], [InAppWebViewSettings.sharedCookiesEnabled], + ///[InAppWebViewSettings.enableViewportScale], [InAppWebViewSettings.allowsAirPlayForMediaPlayback], + ///[InAppWebViewSettings.allowsPictureInPictureMediaPlayback], [InAppWebViewSettings.isFraudulentWebsiteWarningEnabled], + ///[InAppWebViewSettings.allowsInlineMediaPlayback], [InAppWebViewSettings.suppressesIncrementalRendering], [InAppWebViewSettings.selectionGranularity], + ///[InAppWebViewSettings.ignoresViewportScaleLimits], [InAppWebViewSettings.limitsNavigationsToAppBoundDomains], + ///[InAppWebViewSettings.upgradeKnownHostsToHTTPS], + ///will have no effect due to a `WKWebView` limitation when creating a new window WebView: it's impossible to return a new `WKWebView` + ///with a different `WKWebViewConfiguration` instance (see https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview). + ///So, these options will be inherited from the caller WebView. + ///Also, note that calling [PlatformInAppWebViewController.setSettings] method using the controller of the new created WebView, + ///it will update also the WebView options of the caller WebView. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onCreateWindow](https://developer.android.com/reference/android/webkit/WebChromeClient#onCreateWindow(android.webkit.WebView,%20boolean,%20boolean,%20android.os.Message))) + ///- iOS ([Official API - WKUIDelegate.webView](https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview)) + ///- MacOS ([Official API - WKUIDelegate.webView](https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview)) + Future? onCreateWindow(CreateWindowAction createWindowAction) { + return null; + } + + ///Event fired when the host application should close the given WebView and remove it from the view system if necessary. + ///At this point, WebCore has stopped any loading in this window and has removed any cross-scripting ability in javascript. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onCloseWindow](https://developer.android.com/reference/android/webkit/WebChromeClient#onCloseWindow(android.webkit.WebView))) + ///- iOS ([Official API - WKUIDelegate.webViewDidClose](https://developer.apple.com/documentation/webkit/wkuidelegate/1537390-webviewdidclose)) + ///- MacOS ([Official API - WKUIDelegate.webViewDidClose](https://developer.apple.com/documentation/webkit/wkuidelegate/1537390-webviewdidclose)) + void onCloseWindow() {} + + ///Event fired when the JavaScript `window` object of the WebView has received focus. + ///This is the result of the `focus` javascript event applied to the `window` object. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + void onWindowFocus() {} + + ///Event fired when the JavaScript `window` object of the WebView has lost focus. + ///This is the result of the `blur` javascript event applied to the `window` object. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + void onWindowBlur() {} + + ///Event fired when javascript calls the `alert()` method to display an alert dialog. + ///If [JsAlertResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog. + /// + ///[jsAlertRequest] contains the message to be displayed in the alert dialog and the of the page requesting the dialog. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onJsAlert](https://developer.android.com/reference/android/webkit/WebChromeClient#onJsAlert(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult))) + ///- iOS ([Official API - WKUIDelegate.webView](https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview)) + ///- MacOS ([Official API - WKUIDelegate.webView](https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview)) + Future? onJsAlert(JsAlertRequest jsAlertRequest) { + return null; + } + + ///Event fired when javascript calls the `confirm()` method to display a confirm dialog. + ///If [JsConfirmResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog. + /// + ///[jsConfirmRequest] contains the message to be displayed in the confirm dialog and the of the page requesting the dialog. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onJsConfirm](https://developer.android.com/reference/android/webkit/WebChromeClient#onJsConfirm(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult))) + ///- iOS ([Official API - WKUIDelegate.webView](https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview)) + ///- MacOS ([Official API - WKUIDelegate.webView](https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview)) + Future? onJsConfirm(JsConfirmRequest jsConfirmRequest) { + return null; + } + + ///Event fired when javascript calls the `prompt()` method to display a prompt dialog. + ///If [JsPromptResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog. + /// + ///[jsPromptRequest] contains the message to be displayed in the prompt dialog, the default value displayed in the prompt dialog, and the of the page requesting the dialog. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onJsPrompt](https://developer.android.com/reference/android/webkit/WebChromeClient#onJsPrompt(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20android.webkit.JsPromptResult))) + ///- iOS ([Official API - WKUIDelegate.webView](https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview)) + ///- MacOS ([Official API - WKUIDelegate.webView](https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview)) + Future? onJsPrompt(JsPromptRequest jsPromptRequest) { + return null; + } + + ///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. + /// + ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [URLAuthenticationChallenge]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onReceivedHttpAuthRequest](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview)) + Future? onReceivedHttpAuthRequest( + URLAuthenticationChallenge challenge) { + return null; + } + + ///Event fired when the WebView need to perform server trust authentication (certificate validation). + ///The host application must return either [ServerTrustAuthResponse] instance with [ServerTrustAuthResponseAction.CANCEL] or [ServerTrustAuthResponseAction.PROCEED]. + /// + ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ServerTrustChallenge]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onReceivedSslError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview)) + Future? onReceivedServerTrustAuthRequest( + URLAuthenticationChallenge challenge) { + return null; + } + + ///Notify the host application to handle an SSL client certificate request. + ///Webview stores the response in memory (for the life of the application) if [ClientCertResponseAction.PROCEED] or [ClientCertResponseAction.CANCEL] + ///is called and does not call [onReceivedClientCertRequest] again for the same host and port pair. + ///Note that, multiple layers in chromium network stack might be caching the responses. + /// + ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ClientCertChallenge]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onReceivedClientCertRequest](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedClientCertRequest(android.webkit.WebView,%20android.webkit.ClientCertRequest))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview)) + Future? onReceivedClientCertRequest( + URLAuthenticationChallenge challenge) { + return null; + } + + ///Use [FindInteractionController.onFindResultReceived] instead. + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + void onFindResultReceived( + int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) {} + + ///Event fired when an `XMLHttpRequest` is sent to a server. + ///It gives the host application a chance to take control over the request before sending it. + /// + ///[ajaxRequest] represents the `XMLHttpRequest`. + /// + ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewSettings.useShouldInterceptAjaxRequest] setting to `true`. + ///Also, unlike iOS that has [WKUserScript](https://developer.apple.com/documentation/webkit/wkuserscript) that + ///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code + ///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms). + ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + Future? shouldInterceptAjaxRequest(AjaxRequest ajaxRequest) { + return null; + } + + ///Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes. + ///It gives the host application a chance to abort the request. + /// + ///[ajaxRequest] represents the [XMLHttpRequest]. + /// + ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewSettings.useShouldInterceptAjaxRequest] setting to `true`. + ///Also, unlike iOS that has [WKUserScript](https://developer.apple.com/documentation/webkit/wkuserscript) that + ///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code + ///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms). + ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + Future? onAjaxReadyStateChange(AjaxRequest ajaxRequest) { + return null; + } + + ///Event fired as an `XMLHttpRequest` progress. + ///It gives the host application a chance to abort the request. + /// + ///[ajaxRequest] represents the [XMLHttpRequest]. + /// + ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewSettings.useShouldInterceptAjaxRequest] setting to `true`. + ///Also, unlike iOS that has [WKUserScript](https://developer.apple.com/documentation/webkit/wkuserscript) that + ///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code + ///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms). + ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + Future? onAjaxProgress(AjaxRequest ajaxRequest) { + return null; + } + + ///Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API). + ///It gives the host application a chance to take control over the request before sending it. + /// + ///[fetchRequest] represents a resource request. + /// + ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewSettings.useShouldInterceptFetchRequest] setting to `true`. + ///Also, unlike iOS that has [WKUserScript](https://developer.apple.com/documentation/webkit/wkuserscript) that + ///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code + ///used to intercept fetch requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms). + ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the fetch requests will be intercept for sure. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + Future? shouldInterceptFetchRequest( + FetchRequest fetchRequest) { + return null; + } + + ///Event fired when the host application updates its visited links database. + ///This event is also fired when the navigation state of the [InAppWebView] changes through the usage of + ///javascript **[History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API)** functions (`pushState()`, `replaceState()`) and `onpopstate` event + ///or, also, when the javascript `window.location` changes without reloading the webview (for example appending or modifying an hash to the url). + /// + ///[url] represents the url being visited. + /// + ///[isReload] indicates if this url is being reloaded. Available only on Android. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.doUpdateVisitedHistory](https://developer.android.com/reference/android/webkit/WebViewClient#doUpdateVisitedHistory(android.webkit.WebView,%20java.lang.String,%20boolean))) + ///- iOS + ///- MacOS + void onUpdateVisitedHistory(WebUri? url, bool? isReload) {} + + ///Use [onPrintRequest] instead + @Deprecated("Use onPrintRequest instead") + void onPrint(Uri? url) {} + + ///Event fired when `window.print()` is called from JavaScript side. + ///Return `true` if you want to handle the print job. + ///Otherwise return `false`, so the [PlatformPrintJobController] will be handled and disposed automatically by the system. + /// + ///[url] represents the url on which is called. + /// + ///[printJobController] represents the controller of the print job created. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + Future? onPrintRequest( + WebUri? url, PlatformPrintJobController? printJobController) { + return null; + } + + ///Event fired when an HTML element of the webview has been clicked and held. + /// + ///[hitTestResult] represents the hit result for hitting an HTML elements. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - View.setOnLongClickListener](https://developer.android.com/reference/android/view/View#setOnLongClickListener(android.view.View.OnLongClickListener))) + ///- iOS ([Official API - UILongPressGestureRecognizer](https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer)) + void onLongPressHitTestResult(InAppWebViewHitTestResult hitTestResult) {} + + ///Event fired when the current page has entered full screen mode. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onShowCustomView](https://developer.android.com/reference/android/webkit/WebChromeClient#onShowCustomView(android.view.View,%20android.webkit.WebChromeClient.CustomViewCallback))) + ///- iOS ([Official API - UIWindow.didBecomeVisibleNotification](https://developer.apple.com/documentation/uikit/uiwindow/1621621-didbecomevisiblenotification)) + ///- MacOS ([Official API - NSWindow.didEnterFullScreenNotification](https://developer.apple.com/documentation/appkit/nswindow/1419651-didenterfullscreennotification)) + void onEnterFullscreen() {} + + ///Event fired when the current page has exited full screen mode. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onHideCustomView](https://developer.android.com/reference/android/webkit/WebChromeClient#onHideCustomView())) + ///- iOS ([Official API - UIWindow.didBecomeHiddenNotification](https://developer.apple.com/documentation/uikit/uiwindow/1621617-didbecomehiddennotification)) + ///- MacOS ([Official API - NSWindow.didExitFullScreenNotification](https://developer.apple.com/documentation/appkit/nswindow/1419177-didexitfullscreennotification)) + void onExitFullscreen() {} + + ///Called when the web view begins to receive web content. + /// + ///This event occurs early in the document loading process, and as such + ///you should expect that linked resources (for example, CSS and images) may not be available. + /// + ///[url] represents the URL corresponding to the page navigation that triggered this callback. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onPageCommitVisible](https://developer.android.com/reference/android/webkit/WebViewClient#onPageCommitVisible(android.webkit.WebView,%20java.lang.String))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455635-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455635-webview)) + void onPageCommitVisible(WebUri? url) {} + + ///Event fired when a change in the document title occurred. + /// + ///[title] represents the string containing the new title of the document. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onReceivedTitle](https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTitle(android.webkit.WebView,%20java.lang.String))) + ///- iOS + ///- MacOS + void onTitleChanged(String? title) {} + + ///Event fired to respond to the results of an over-scroll operation. + /// + ///[x] represents the new X scroll value in pixels. + /// + ///[y] represents the new Y scroll value in pixels. + /// + ///[clampedX] is `true` if [x] was clamped to an over-scroll boundary. + /// + ///[clampedY] is `true` if [y] was clamped to an over-scroll boundary. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.onOverScrolled](https://developer.android.com/reference/android/webkit/WebView#onOverScrolled(int,%20int,%20boolean,%20boolean))) + ///- iOS + void onOverScrolled(int x, int y, bool clampedX, bool clampedY) {} + + ///Event fired when the zoom scale of the WebView has changed. + /// + ///[oldScale] The old zoom scale factor. + /// + ///[newScale] The new zoom scale factor.ì + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onScaleChanged](https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float))) + ///- iOS ([Official API - UIScrollViewDelegate.scrollViewDidZoom](https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619409-scrollviewdidzoom)) + void onZoomScaleChanged(double oldScale, double newScale) {} + + ///Use [onSafeBrowsingHit] instead. + @Deprecated("Use onSafeBrowsingHit instead") + Future? androidOnSafeBrowsingHit( + Uri url, SafeBrowsingThreat? threatType) { + return null; + } + + ///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing. + ///The default behavior is to show an interstitial to the user, with the reporting checkbox visible. + /// + ///[url] represents the url of the request. + /// + ///[threatType] represents the reason the resource was caught by Safe Browsing, corresponding to a [SafeBrowsingThreat]. + /// + ///**NOTE**: available only on Android 27+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onSafeBrowsingHit](https://developer.android.com/reference/android/webkit/WebViewClient#onSafeBrowsingHit(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20int,%20android.webkit.SafeBrowsingResponse))) + Future? onSafeBrowsingHit( + WebUri url, SafeBrowsingThreat? threatType) { + return null; + } + + ///Use [onPermissionRequest] instead. + @Deprecated("Use onPermissionRequest instead") + Future? androidOnPermissionRequest( + String origin, List resources) { + return null; + } + + ///Event fired when the WebView is requesting permission to access the specified resources and the permission currently isn't granted or denied. + /// + ///[permissionRequest] represents the permission request with an array of resources the web content wants to access + ///and the origin of the web page which is trying to access the restricted resources. + /// + ///**NOTE for Android**: available only on Android 21+. + /// + ///**NOTE for iOS**: available only on iOS 15.0+. The default [PermissionResponse.action] is [PermissionResponseAction.PROMPT]. + /// + ///**NOTE for MacOS**: available only on iOS 12.0+. The default [PermissionResponse.action] is [PermissionResponseAction.PROMPT]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest))) + ///- iOS + ///- MacOS + Future? onPermissionRequest( + PermissionRequest permissionRequest) { + return null; + } + + ///Use [onGeolocationPermissionsShowPrompt] instead. + @Deprecated("Use onGeolocationPermissionsShowPrompt instead") + Future? + androidOnGeolocationPermissionsShowPrompt(String origin) { + return null; + } + + ///Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin. + ///Note that for applications targeting Android N and later SDKs (API level > `Build.VERSION_CODES.M`) this method is only called for requests originating from secure origins such as https. + ///On non-secure origins geolocation requests are automatically denied. + /// + ///[origin] represents the origin of the web content attempting to use the Geolocation API. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onGeolocationPermissionsShowPrompt](https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsShowPrompt(java.lang.String,%20android.webkit.GeolocationPermissions.Callback))) + Future? + onGeolocationPermissionsShowPrompt(String origin) { + return null; + } + + ///Use [onGeolocationPermissionsHidePrompt] instead. + @Deprecated("Use onGeolocationPermissionsHidePrompt instead") + void androidOnGeolocationPermissionsHidePrompt() {} + + ///Notify the host application that a request for Geolocation permissions, made with a previous call to [onGeolocationPermissionsShowPrompt] has been canceled. + ///Any related UI should therefore be hidden. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onGeolocationPermissionsHidePrompt](https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsHidePrompt())) + void onGeolocationPermissionsHidePrompt() {} + + ///Use [shouldInterceptRequest] instead. + @Deprecated("Use shouldInterceptRequest instead") + Future? androidShouldInterceptRequest( + WebResourceRequest request) { + return null; + } + + ///Notify the host application of a resource request and allow the application to return the data. + ///If the return value is `null`, the WebView will continue to load the resource as usual. + ///Otherwise, the return response and data will be used. + /// + ///This callback is invoked for a variety of URL schemes (e.g., `http(s):`, `data:`, `file:`, etc.), + ///not only those schemes which send requests over the network. + ///This is not called for `javascript:` URLs, `blob:` URLs, or for assets accessed via `file:///android_asset/` or `file:///android_res/` URLs. + /// + ///In the case of redirects, this is only called for the initial resource URL, not any subsequent redirect URLs. + /// + ///[request] Object containing the details of the request. + /// + ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewSettings.useShouldInterceptRequest] option to `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.shouldInterceptRequest](https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20android.webkit.WebResourceRequest))) + Future? shouldInterceptRequest( + WebResourceRequest request) { + return null; + } + + ///Use [onRenderProcessUnresponsive] instead. + @Deprecated("Use onRenderProcessUnresponsive instead") + Future? androidOnRenderProcessUnresponsive( + Uri? url) { + return null; + } + + ///Event called when the renderer currently associated with the WebView becomes unresponsive as a result of a long running blocking task such as the execution of JavaScript. + /// + ///If a WebView fails to process an input event, or successfully navigate to a new URL within a reasonable time frame, the renderer is considered to be unresponsive, and this callback will be called. + /// + ///This callback will continue to be called at regular intervals as long as the renderer remains unresponsive. + ///If the renderer becomes responsive again, [onRenderProcessResponsive] will be called once, + ///and this method will not subsequently be called unless another period of unresponsiveness is detected. + /// + ///The minimum interval between successive calls to [onRenderProcessUnresponsive] is 5 seconds. + /// + ///No action is taken by WebView as a result of this method call. + ///Applications may choose to terminate the associated renderer via the object that is passed to this callback, + ///if in multiprocess mode, however this must be accompanied by correctly handling [onRenderProcessGone] for this WebView, + ///and all other WebViews associated with the same renderer. Failure to do so will result in application termination. + /// + ///**NOTE**: available only on Android 29+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewRenderProcessClient.onRenderProcessUnresponsive](https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessUnresponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess))) + Future? onRenderProcessUnresponsive( + WebUri? url) { + return null; + } + + ///Use [onRenderProcessResponsive] instead. + @Deprecated("Use onRenderProcessResponsive instead") + Future? androidOnRenderProcessResponsive( + Uri? url) { + return null; + } + + ///Event called once when an unresponsive renderer currently associated with the WebView becomes responsive. + /// + ///After a WebView renderer becomes unresponsive, which is notified to the application by [onRenderProcessUnresponsive], + ///it is possible for the blocking renderer task to complete, returning the renderer to a responsive state. + ///In that case, this method is called once to indicate responsiveness. + /// + ///No action is taken by WebView as a result of this method call. + /// + ///**NOTE**: available only on Android 29+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewRenderProcessClient.onRenderProcessResponsive](https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessResponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess))) + Future? onRenderProcessResponsive(WebUri? url) { + return null; + } + + ///Use [onRenderProcessGone] instead. + @Deprecated("Use onRenderProcessGone instead") + void androidOnRenderProcessGone(RenderProcessGoneDetail detail) {} + + ///Event fired when the given WebView's render process has exited. + ///The application's implementation of this callback should only attempt to clean up the WebView. + ///The WebView should be removed from the view hierarchy, all references to it should be cleaned up. + /// + ///[detail] the reason why it exited. + /// + ///**NOTE**: available only on Android 26+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onRenderProcessGone](https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail))) + void onRenderProcessGone(RenderProcessGoneDetail detail) {} + + ///Use [onFormResubmission] instead. + @Deprecated('Use onFormResubmission instead') + Future? androidOnFormResubmission(Uri? url) { + return null; + } + + ///As the host application if the browser should resend data as the requested page was a result of a POST. The default is to not resend the data. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onFormResubmission](https://developer.android.com/reference/android/webkit/WebViewClient#onFormResubmission(android.webkit.WebView,%20android.os.Message,%20android.os.Message))) + Future? onFormResubmission(WebUri? url) { + return null; + } + + ///Use [onZoomScaleChanged] instead. + @Deprecated('Use onZoomScaleChanged instead') + void androidOnScaleChanged(double oldScale, double newScale) {} + + ///Use [onReceivedIcon] instead. + @Deprecated('Use onReceivedIcon instead') + void androidOnReceivedIcon(Uint8List icon) {} + + ///Event fired when there is new favicon for the current page. + /// + ///[icon] represents the favicon for the current page. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onReceivedIcon](https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedIcon(android.webkit.WebView,%20android.graphics.Bitmap))) + void onReceivedIcon(Uint8List icon) {} + + ///Use [onReceivedTouchIconUrl] instead. + @Deprecated('Use onReceivedTouchIconUrl instead') + void androidOnReceivedTouchIconUrl(Uri url, bool precomposed) {} + + ///Event fired when there is an url for an apple-touch-icon. + /// + ///[url] represents the icon url. + /// + ///[precomposed] is `true` if the url is for a precomposed touch icon. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onReceivedTouchIconUrl](https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTouchIconUrl(android.webkit.WebView,%20java.lang.String,%20boolean))) + void onReceivedTouchIconUrl(WebUri url, bool precomposed) {} + + ///Use [onJsBeforeUnload] instead. + @Deprecated('Use onJsBeforeUnload instead') + Future? androidOnJsBeforeUnload( + JsBeforeUnloadRequest jsBeforeUnloadRequest) { + return null; + } + + ///Event fired when the client should display a dialog to confirm navigation away from the current page. + ///This is the result of the `onbeforeunload` javascript event. + ///If [JsBeforeUnloadResponse.handledByClient] is `true`, WebView will assume that the client will handle the confirm dialog. + ///If [JsBeforeUnloadResponse.handledByClient] is `false`, a default value of `true` will be returned to javascript to accept navigation away from the current page. + ///The default behavior is to return `false`. + ///Setting the [JsBeforeUnloadResponse.action] to [JsBeforeUnloadResponseAction.CONFIRM] will navigate away from the current page, + ///[JsBeforeUnloadResponseAction.CANCEL] will cancel the navigation. + /// + ///[jsBeforeUnloadRequest] contains the message to be displayed in the alert dialog and the of the page requesting the dialog. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onJsBeforeUnload](https://developer.android.com/reference/android/webkit/WebChromeClient#onJsBeforeUnload(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult))) + Future? onJsBeforeUnload( + JsBeforeUnloadRequest jsBeforeUnloadRequest) { + return null; + } + + ///Use [onReceivedLoginRequest] instead. + @Deprecated('Use onReceivedLoginRequest instead') + void androidOnReceivedLoginRequest(LoginRequest loginRequest) {} + + ///Event fired when a request to automatically log in the user has been processed. + /// + ///[loginRequest] contains the realm, account and args of the login request. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onReceivedLoginRequest](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedLoginRequest(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String))) + void onReceivedLoginRequest(LoginRequest loginRequest) {} + + ///Notify the host application that the given permission request has been canceled. Any related UI should therefore be hidden. + /// + ///[permissionRequest] represents the permission request that needs be canceled + ///with an array of resources the web content wants to access + ///and the origin of the web page which is trying to access the restricted resources. + /// + ///**NOTE for Android**: available only on Android 21+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onPermissionRequestCanceled](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequestCanceled(android.webkit.PermissionRequest))) + void onPermissionRequestCanceled(PermissionRequest permissionRequest) {} + + ///Request display and focus for this WebView. + ///This may happen due to another WebView opening a link in this WebView and requesting that this WebView be displayed. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onRequestFocus](https://developer.android.com/reference/android/webkit/WebChromeClient#onRequestFocus(android.webkit.WebView))) + void onRequestFocus() {} + + ///Use [onWebContentProcessDidTerminate] instead. + @Deprecated('Use onWebContentProcessDidTerminate instead') + void iosOnWebContentProcessDidTerminate() {} + + ///Invoked when the web view's web content process is terminated. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - WKNavigationDelegate.webViewWebContentProcessDidTerminate](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455639-webviewwebcontentprocessdidtermi)) + ///- MacOS ([Official API - WKNavigationDelegate.webViewWebContentProcessDidTerminate](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455639-webviewwebcontentprocessdidtermi)) + void onWebContentProcessDidTerminate() {} + + ///Use [onDidReceiveServerRedirectForProvisionalNavigation] instead. + @Deprecated('Use onDidReceiveServerRedirectForProvisionalNavigation instead') + void iosOnDidReceiveServerRedirectForProvisionalNavigation() {} + + ///Called when a web view receives a server redirect. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455627-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455627-webview)) + void onDidReceiveServerRedirectForProvisionalNavigation() {} + + ///Use [onNavigationResponse] instead. + @Deprecated('Use onNavigationResponse instead') + Future? iosOnNavigationResponse( + IOSWKNavigationResponse navigationResponse) { + return null; + } + + ///Called when a web view asks for permission to navigate to new content after the response to the navigation request is known. + /// + ///[navigationResponse] represents the navigation response. + /// + ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewSettings.useOnNavigationResponse] setting to `true`. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) + Future? onNavigationResponse( + NavigationResponse navigationResponse) { + return null; + } + + ///Use [shouldAllowDeprecatedTLS] instead. + @Deprecated('Use shouldAllowDeprecatedTLS instead') + Future? iosShouldAllowDeprecatedTLS( + URLAuthenticationChallenge challenge) { + return null; + } + + ///Called when a web view asks whether to continue with a connection that uses a deprecated version of TLS (v1.0 and v1.1). + /// + ///[challenge] represents the authentication challenge. + /// + ///**NOTE for iOS**: available only on iOS 14.0+. + /// + ///**NOTE for MacOS**: available only on MacOS 11.0+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/3601237-webview)) + ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/3601237-webview)) + Future? shouldAllowDeprecatedTLS( + URLAuthenticationChallenge challenge) { + return null; + } + + ///Event fired when a change in the camera capture state occurred. + /// + ///**NOTE for iOS**: available only on iOS 15.0+. + /// + ///**NOTE for MacOS**: available only on MacOS 12.0+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS + ///- MacOS + void onCameraCaptureStateChanged( + MediaCaptureState? oldState, + MediaCaptureState? newState, + ) {} + + ///Event fired when a change in the microphone capture state occurred. + ///Event fired when a change in the microphone capture state occurred. + /// + ///**NOTE for iOS**: available only on iOS 15.0+. + /// + ///**NOTE for MacOS**: available only on MacOS 12.0+. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS + ///- MacOS + void onMicrophoneCaptureStateChanged( + MediaCaptureState? oldState, + MediaCaptureState? newState, + ) {} + + ///Event fired when the content size of the `WebView` changes. + /// + ///[oldContentSize] represents the old content size value. + /// + ///[newContentSize] represents the new content size value. + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS + void onContentSizeChanged(Size oldContentSize, Size newContentSize) {} +} diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_localhost_server.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_localhost_server.dart new file mode 100755 index 00000000..fff3757d --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_localhost_server.dart @@ -0,0 +1,160 @@ +import 'dart:io'; +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart' show rootBundle; + +import 'mime_type_resolver.dart'; +import 'platform_in_app_localhost_server.dart'; + +/// Object specifying creation parameters for creating a [DefaultInAppLocalhostServer]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInAppLocalhostServerCreationParams] for +/// more information. +@immutable +class DefaultInAppLocalhostServerCreationParams + extends PlatformInAppLocalhostServerCreationParams { + /// Creates a new [DefaultInAppLocalhostServerCreationParams] instance. + const DefaultInAppLocalhostServerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformInAppLocalhostServerCreationParams params, + ) : super(); + + /// Creates a [DefaultInAppLocalhostServerCreationParams] instance based on [PlatformInAppLocalhostServerCreationParams]. + factory DefaultInAppLocalhostServerCreationParams.fromPlatformInAppLocalhostServerCreationParams( + PlatformInAppLocalhostServerCreationParams params) { + return DefaultInAppLocalhostServerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInAppLocalhostServer} +class DefaultInAppLocalhostServer extends PlatformInAppLocalhostServer { + bool _started = false; + HttpServer? _server; + int _port = 8080; + bool _shared = false; + String _directoryIndex = 'index.html'; + String _documentRoot = './'; + + /// Creates a new [DefaultInAppLocalhostServer]. + DefaultInAppLocalhostServer(PlatformInAppLocalhostServerCreationParams params) + : super.implementation( + params is DefaultInAppLocalhostServerCreationParams + ? params + : DefaultInAppLocalhostServerCreationParams + .fromPlatformInAppLocalhostServerCreationParams(params), + ) { + this._port = params.port; + this._directoryIndex = params.directoryIndex; + this._documentRoot = (params.documentRoot.endsWith('/')) + ? params.documentRoot + : '${params.documentRoot}/'; + this._shared = params.shared; + } + + @override + int get port => _port; + + @override + String get directoryIndex => _directoryIndex; + + @override + String get documentRoot => _documentRoot; + + @override + bool get shared => _shared; + + @override + Future start() async { + if (this._started) { + throw Exception('Server already started on http://localhost:$_port'); + } + this._started = true; + + var completer = Completer(); + + runZonedGuarded(() { + HttpServer.bind('127.0.0.1', _port, shared: _shared).then((server) { + print('Server running on http://localhost:' + _port.toString()); + + this._server = server; + + server.listen((HttpRequest request) async { + Uint8List body = Uint8List(0); + + var path = request.requestedUri.path; + path = (path.startsWith('/')) ? path.substring(1) : path; + path += (path.endsWith('/')) ? _directoryIndex : ''; + if (path == '') { + // if the path still empty, try to load the index file + path = _directoryIndex; + } + path = _documentRoot + path; + + try { + body = (await rootBundle.load(Uri.decodeFull(path))) + .buffer + .asUint8List(); + } catch (e) { + print(Uri.decodeFull(path)); + print(e.toString()); + request.response.close(); + return; + } + + var contentType = ContentType('text', 'html', charset: 'utf-8'); + if (!request.requestedUri.path.endsWith('/') && + request.requestedUri.pathSegments.isNotEmpty) { + var mimeType = MimeTypeResolver.lookup(request.requestedUri.path); + if (mimeType != null) { + contentType = _getContentTypeFromMimeType(mimeType); + } + } + + request.response.headers.contentType = contentType; + request.response.add(body); + request.response.close(); + }); + + completer.complete(); + }); + }, (e, stackTrace) => print('Error: $e $stackTrace')); + + return completer.future; + } + + @override + Future close() async { + if (this._server == null) { + return; + } + await this._server!.close(force: true); + print('Server running on http://localhost:$_port closed'); + this._started = false; + this._server = null; + } + + @override + bool isRunning() { + return this._server != null; + } + + ContentType _getContentTypeFromMimeType(String mimeType) { + final contentType = mimeType.split('/'); + String? charset; + + if (_isTextFile(mimeType)) { + charset = 'utf-8'; + } + + return ContentType(contentType[0], contentType[1], charset: charset); + } + + bool _isTextFile(String mimeType) { + final textFile = RegExp(r'^text\/|^application\/(javascript|json)'); + return textFile.hasMatch(mimeType); + } +} diff --git a/lib/src/in_app_webview/android/in_app_webview_options.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/android/in_app_webview_options.dart similarity index 97% rename from lib/src/in_app_webview/android/in_app_webview_options.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_webview/android/in_app_webview_options.dart index 18e4e958..dc956e13 100755 --- a/lib/src/in_app_webview/android/in_app_webview_options.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/android/in_app_webview_options.dart @@ -4,7 +4,6 @@ import '../../util.dart'; import '../../types/main.dart'; import '../../in_app_browser/in_app_browser_settings.dart'; import '../in_app_webview_settings.dart'; -import '../webview.dart'; class AndroidOptions {} @@ -159,10 +158,10 @@ class AndroidInAppWebViewOptions bool hardwareAcceleration; ///Sets whether the WebView supports multiple windows. - ///If set to `true`, [WebView.onCreateWindow] event must be implemented by the host application. The default value is `false`. + ///If set to `true`, [PlatformWebViewCreationParams.onCreateWindow] event must be implemented by the host application. The default value is `false`. bool supportMultipleWindows; - ///Regular expression used by [WebView.shouldOverrideUrlLoading] event to cancel navigation requests for frames that are not the main frame. + ///Regular expression used by [PlatformWebViewCreationParams.shouldOverrideUrlLoading] event to cancel navigation requests 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. String? regexToCancelSubFramesLoading; @@ -173,10 +172,10 @@ class AndroidInAppWebViewOptions ///as it can cause framerate drops on animations in Android 9 and lower (see [Hybrid-Composition#performance](https://github.com/flutter/flutter/wiki/Hybrid-Composition#performance)). bool useHybridComposition; - ///Set to `true` to be able to listen at the [WebView.androidShouldInterceptRequest] event. The default value is `false`. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.androidShouldInterceptRequest] event. The default value is `false`. bool useShouldInterceptRequest; - ///Set to `true` to be able to listen at the [WebView.androidOnRenderProcessGone] event. The default value is `false`. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.androidOnRenderProcessGone] event. The default value is `false`. bool useOnRenderProcessGone; ///Sets the WebView's over-scroll mode. diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/android/main.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/android/main.dart new file mode 100644 index 00000000..da217a86 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/android/main.dart @@ -0,0 +1 @@ +export 'in_app_webview_options.dart'; diff --git a/lib/src/in_app_webview/apple/in_app_webview_options.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/apple/in_app_webview_options.dart similarity index 85% rename from lib/src/in_app_webview/apple/in_app_webview_options.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_webview/apple/in_app_webview_options.dart index 63a1caf7..e656a4ba 100755 --- a/lib/src/in_app_webview/apple/in_app_webview_options.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/apple/in_app_webview_options.dart @@ -3,8 +3,7 @@ import '../../types/main.dart'; import '../../in_app_browser/in_app_browser_settings.dart'; import '../in_app_webview_settings.dart'; -import '../webview.dart'; -import '../in_app_webview_controller.dart'; +import '../platform_inappwebview_controller.dart'; class IosOptions {} @@ -153,42 +152,42 @@ class IOSInAppWebViewOptions ///**NOTE**: available on iOS 14.0+. bool limitsNavigationsToAppBoundDomains; - ///Set to `true` to be able to listen to the [WebView.iosOnNavigationResponse] event. The default value is `false`. + ///Set to `true` to be able to listen to the [PlatformWebViewCreationParams.iosOnNavigationResponse] event. The default value is `false`. bool useOnNavigationResponse; - ///Set to `true` to enable Apple Pay API for the [WebView] at its first page load or on the next page load (using [InAppWebViewController.setOptions]). The default value is `false`. + ///Set to `true` to enable Apple Pay API for the `WebView` at its first page load or on the next page load (using [PlatformInAppWebViewController.setOptions]). The default value is `false`. /// ///**IMPORTANT NOTE**: As written in the official [Safari 13 Release Notes](https://developer.apple.com/documentation/safari-release-notes/safari-13-release-notes#Payment-Request-API), - ///it won't work if any script injection APIs are used (such as [InAppWebViewController.evaluateJavascript] or [UserScript]). + ///it won't work if any script injection APIs are used (such as [PlatformInAppWebViewController.evaluateJavascript] or [UserScript]). ///So, when this attribute is `true`, all the methods, options, and events implemented using JavaScript won't be called or won't do anything and the result will always be `null`. /// ///Methods affected: - ///- [InAppWebViewController.addUserScript] - ///- [InAppWebViewController.addUserScripts] - ///- [InAppWebViewController.removeUserScript] - ///- [InAppWebViewController.removeUserScripts] - ///- [InAppWebViewController.removeAllUserScripts] - ///- [InAppWebViewController.evaluateJavascript] - ///- [InAppWebViewController.callAsyncJavaScript] - ///- [InAppWebViewController.injectJavascriptFileFromUrl] - ///- [InAppWebViewController.injectJavascriptFileFromAsset] - ///- [InAppWebViewController.injectCSSCode] - ///- [InAppWebViewController.injectCSSFileFromUrl] - ///- [InAppWebViewController.injectCSSFileFromAsset] - ///- [InAppWebViewController.findAllAsync] - ///- [InAppWebViewController.findNext] - ///- [InAppWebViewController.clearMatches] - ///- [InAppWebViewController.pauseTimers] - ///- [InAppWebViewController.getSelectedText] - ///- [InAppWebViewController.getHitTestResult] - ///- [InAppWebViewController.requestFocusNodeHref] - ///- [InAppWebViewController.requestImageRef] - ///- [InAppWebViewController.postWebMessage] - ///- [InAppWebViewController.createWebMessageChannel] - ///- [InAppWebViewController.addWebMessageListener] + ///- [PlatformInAppWebViewController.addUserScript] + ///- [PlatformInAppWebViewController.addUserScripts] + ///- [PlatformInAppWebViewController.removeUserScript] + ///- [PlatformInAppWebViewController.removeUserScripts] + ///- [PlatformInAppWebViewController.removeAllUserScripts] + ///- [PlatformInAppWebViewController.evaluateJavascript] + ///- [PlatformInAppWebViewController.callAsyncJavaScript] + ///- [PlatformInAppWebViewController.injectJavascriptFileFromUrl] + ///- [PlatformInAppWebViewController.injectJavascriptFileFromAsset] + ///- [PlatformInAppWebViewController.injectCSSCode] + ///- [PlatformInAppWebViewController.injectCSSFileFromUrl] + ///- [PlatformInAppWebViewController.injectCSSFileFromAsset] + ///- [PlatformInAppWebViewController.findAllAsync] + ///- [PlatformInAppWebViewController.findNext] + ///- [PlatformInAppWebViewController.clearMatches] + ///- [PlatformInAppWebViewController.pauseTimers] + ///- [PlatformInAppWebViewController.getSelectedText] + ///- [PlatformInAppWebViewController.getHitTestResult] + ///- [PlatformInAppWebViewController.requestFocusNodeHref] + ///- [PlatformInAppWebViewController.requestImageRef] + ///- [PlatformInAppWebViewController.postWebMessage] + ///- [PlatformInAppWebViewController.createWebMessageChannel] + ///- [PlatformInAppWebViewController.addWebMessageListener] /// ///Options affected: - ///- [WebView.initialUserScripts] + ///- [PlatformWebViewCreationParams.initialUserScripts] ///- [InAppWebViewOptions.supportZoom] ///- [InAppWebViewOptions.useOnLoadResource] ///- [InAppWebViewOptions.useShouldInterceptAjaxRequest] @@ -196,26 +195,26 @@ class IOSInAppWebViewOptions ///- [IOSInAppWebViewOptions.enableViewportScale] /// ///Events affected: - ///- the `hitTestResult` argument of [WebView.onLongPressHitTestResult] will be empty + ///- the `hitTestResult` argument of [PlatformWebViewCreationParams.onLongPressHitTestResult] will be empty ///- the `hitTestResult` argument of [ContextMenu.onCreateContextMenu] will be empty - ///- [WebView.onLoadResource] - ///- [WebView.shouldInterceptAjaxRequest] - ///- [WebView.onAjaxReadyStateChange] - ///- [WebView.onAjaxProgress] - ///- [WebView.shouldInterceptFetchRequest] - ///- [WebView.onConsoleMessage] - ///- [WebView.onPrint] - ///- [WebView.onWindowFocus] - ///- [WebView.onWindowBlur] - ///- [WebView.onFindResultReceived] + ///- [PlatformWebViewCreationParams.onLoadResource] + ///- [PlatformWebViewCreationParams.shouldInterceptAjaxRequest] + ///- [PlatformWebViewCreationParams.onAjaxReadyStateChange] + ///- [PlatformWebViewCreationParams.onAjaxProgress] + ///- [PlatformWebViewCreationParams.shouldInterceptFetchRequest] + ///- [PlatformWebViewCreationParams.onConsoleMessage] + ///- [PlatformWebViewCreationParams.onPrint] + ///- [PlatformWebViewCreationParams.onWindowFocus] + ///- [PlatformWebViewCreationParams.onWindowBlur] + ///- [PlatformWebViewCreationParams.onFindResultReceived] /// ///**NOTE**: available on iOS 13.0+. bool applePayAPIEnabled; - ///Used in combination with [WebView.initialUrlRequest] or [WebView.initialData] (using the `file://` scheme), it represents the URL from which to read the web content. + ///Used in combination with [PlatformWebViewCreationParams.initialUrlRequest] or [PlatformWebViewCreationParams.initialData] (using the `file://` scheme), it represents the URL from which to read the web content. ///This URL must be a file-based URL (using the `file://` scheme). - ///Specify the same value as the [URLRequest.url] if you are using it with the [WebView.initialUrlRequest] parameter or - ///the [InAppWebViewInitialData.baseUrl] if you are using it with the [WebView.initialData] parameter to prevent WebView from reading any other content. + ///Specify the same value as the [URLRequest.url] if you are using it with the [PlatformWebViewCreationParams.initialUrlRequest] parameter or + ///the [InAppWebViewInitialData.baseUrl] if you are using it with the [PlatformWebViewCreationParams.initialData] parameter to prevent WebView from reading any other content. ///Specify a directory to give WebView permission to read additional files in the specified directory. Uri? allowingReadAccessTo; diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/apple/main.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/apple/main.dart new file mode 100644 index 00000000..da217a86 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/apple/main.dart @@ -0,0 +1 @@ +export 'in_app_webview_options.dart'; diff --git a/lib/src/in_app_webview/in_app_webview_keep_alive.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart similarity index 64% rename from lib/src/in_app_webview/in_app_webview_keep_alive.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart index ccd752b1..e026022f 100644 --- a/lib/src/in_app_webview/in_app_webview_keep_alive.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart @@ -1,33 +1,33 @@ import '../types/main.dart'; import '../util.dart'; -import '../web_message/web_message_channel.dart'; -import '../web_message/web_message_listener.dart'; -import 'in_app_webview.dart'; -import 'in_app_webview_controller.dart'; +import '../web_message/platform_web_message_channel.dart'; +import '../web_message/platform_web_message_listener.dart'; +import 'platform_inappwebview_widget.dart'; +import 'platform_inappwebview_controller.dart'; -///Class used to keep alive a [InAppWebView]. +///Class used to keep alive a [PlatformInAppWebViewWidget]. class InAppWebViewKeepAlive { String _id = IdGenerator.generate(); } ///Used internally extension InternalInAppWebViewKeepAlive on InAppWebViewKeepAlive { + String get id => _id; + set id(String id) { _id = id; } - - String get id => _id; } -///Used internally to save and restore [InAppWebViewController] properties +///Used internally to save and restore [PlatformInAppWebViewController] properties ///for the keep alive feature. class InAppWebViewControllerKeepAliveProps { Map javaScriptHandlersMap; Map> userScripts; Set webMessageListenerObjNames; Map injectedScriptsFromURL; - Set webMessageChannels = Set(); - Set webMessageListeners = Set(); + Set webMessageChannels = Set(); + Set webMessageListeners = Set(); InAppWebViewControllerKeepAliveProps( {required this.javaScriptHandlersMap, diff --git a/lib/src/in_app_webview/in_app_webview_settings.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.dart similarity index 91% rename from lib/src/in_app_webview/in_app_webview_settings.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.dart index 74be44c0..7599b599 100755 --- a/lib/src/in_app_webview/in_app_webview_settings.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.dart @@ -1,10 +1,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_inappwebview/src/types/user_preferred_content_mode.dart'; import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; import 'dart:typed_data'; -import '../android/webview_asset_loader.dart'; +import '../platform_webview_asset_loader.dart'; import '../types/action_mode_menu_item.dart'; import '../types/cache_mode.dart'; import '../types/data_detector_types.dart'; @@ -20,6 +19,7 @@ import '../types/scrollbar_style.dart'; import '../types/scrollview_content_inset_adjustment_behavior.dart'; import '../types/scrollview_deceleration_rate.dart'; import '../types/selection_granularity.dart'; +import '../types/user_preferred_content_mode.dart'; import '../types/vertical_scrollbar_position.dart'; import '../web_uri.dart'; import 'android/in_app_webview_options.dart'; @@ -28,11 +28,11 @@ import '../content_blocker.dart'; import '../types/main.dart'; import '../util.dart'; import '../in_app_browser/in_app_browser_settings.dart'; -import 'webview.dart'; -import '../android/webview_feature.dart'; -import '../in_app_webview/in_app_webview_controller.dart'; +import '../platform_webview_feature.dart'; +import '../in_app_webview/platform_inappwebview_controller.dart'; import '../context_menu/context_menu.dart'; -import '../in_app_browser/in_app_browser.dart'; +import '../in_app_browser/platform_in_app_browser.dart'; +import 'platform_webview.dart'; part 'in_app_webview_settings.g.dart'; @@ -52,29 +52,29 @@ List _deserializeContentBlockers( ///This class represents all the WebView settings available. @ExchangeableObject(copyMethod: true) class InAppWebViewSettings_ { - ///Set to `true` to be able to listen at the [WebView.shouldOverrideUrlLoading] event. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.shouldOverrideUrlLoading] event. /// - ///If the [WebView.shouldOverrideUrlLoading] event is implemented and this value is `null`, + ///If the [PlatformWebViewCreationParams.shouldOverrideUrlLoading] event is implemented and this value is `null`, ///it will be automatically inferred as `true`, otherwise, the default value is `false`. - ///This logic will not be applied for [InAppBrowser], where you must set the value manually. + ///This logic will not be applied for [PlatformInAppBrowser], where you must set the value manually. @SupportedPlatforms( platforms: [AndroidPlatform(), IOSPlatform(), MacOSPlatform()]) bool? useShouldOverrideUrlLoading; - ///Set to `true` to be able to listen at the [WebView.onLoadResource] event. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.onLoadResource] event. /// - ///If the [WebView.onLoadResource] event is implemented and this value is `null`, + ///If the [PlatformWebViewCreationParams.onLoadResource] event is implemented and this value is `null`, ///it will be automatically inferred as `true`, otherwise, the default value is `false`. - ///This logic will not be applied for [InAppBrowser], where you must set the value manually. + ///This logic will not be applied for [PlatformInAppBrowser], where you must set the value manually. @SupportedPlatforms( platforms: [AndroidPlatform(), IOSPlatform(), MacOSPlatform()]) bool? useOnLoadResource; - ///Set to `true` to be able to listen at the [WebView.onDownloadStartRequest] event. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.onDownloadStartRequest] event. /// - ///If the [WebView.onDownloadStartRequest] event is implemented and this value is `null`, + ///If the [PlatformWebViewCreationParams.onDownloadStartRequest] event is implemented and this value is `null`, ///it will be automatically inferred as `true`, otherwise, the default value is `false`. - ///This logic will not be applied for [InAppBrowser], where you must set the value manually. + ///This logic will not be applied for [PlatformInAppBrowser], where you must set the value manually. @SupportedPlatforms( platforms: [AndroidPlatform(), IOSPlatform(), MacOSPlatform()]) bool? useOnDownloadStart; @@ -220,7 +220,7 @@ class InAppWebViewSettings_ { ]) bool? horizontalScrollBarEnabled; - ///List of custom schemes that the WebView must handle. Use the [WebView.onLoadResourceWithCustomScheme] event to intercept resource requests with custom scheme. + ///List of custom schemes that the WebView must handle. Use the [PlatformWebViewCreationParams.onLoadResourceWithCustomScheme] event to intercept resource requests with custom scheme. @SupportedPlatforms(platforms: [ AndroidPlatform(), IOSPlatform(available: "11.0"), @@ -253,20 +253,20 @@ class InAppWebViewSettings_ { ]) UserPreferredContentMode_? preferredContentMode; - ///Set to `true` to be able to listen at the [WebView.shouldInterceptAjaxRequest] event. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.shouldInterceptAjaxRequest] event. /// - ///If the [WebView.shouldInterceptAjaxRequest] event is implemented and this value is `null`, + ///If the [PlatformWebViewCreationParams.shouldInterceptAjaxRequest] event is implemented and this value is `null`, ///it will be automatically inferred as `true`, otherwise, the default value is `false`. - ///This logic will not be applied for [InAppBrowser], where you must set the value manually. + ///This logic will not be applied for [PlatformInAppBrowser], where you must set the value manually. @SupportedPlatforms( platforms: [AndroidPlatform(), IOSPlatform(), MacOSPlatform()]) bool? useShouldInterceptAjaxRequest; - ///Set to `true` to be able to listen at the [WebView.shouldInterceptFetchRequest] event. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.shouldInterceptFetchRequest] event. /// - ///If the [WebView.shouldInterceptFetchRequest] event is implemented and this value is `null`, + ///If the [PlatformWebViewCreationParams.shouldInterceptFetchRequest] event is implemented and this value is `null`, ///it will be automatically inferred as `true`, otherwise, the default value is `false`. - ///This logic will not be applied for [InAppBrowser], where you must set the value manually. + ///This logic will not be applied for [PlatformInAppBrowser], where you must set the value manually. @SupportedPlatforms( platforms: [AndroidPlatform(), IOSPlatform(), MacOSPlatform()]) bool? useShouldInterceptFetchRequest; @@ -361,7 +361,7 @@ because there isn't any way to make the website data store non-persistent for th ///Set to `true` to allow audio playing when the app goes in background or the screen is locked or another app is opened. ///However, there will be no controls in the notification bar or on the lockscreen. - ///Also, make sure to not call [InAppWebViewController.pause], otherwise it will stop audio playing. + ///Also, make sure to not call [PlatformInAppWebViewController.pause], otherwise it will stop audio playing. ///The default value is `false`. /// ///**IMPORTANT NOTE**: if you use this setting, your app could be rejected by the Google Play Store. @@ -606,7 +606,7 @@ because there isn't any way to make the website data store non-persistent for th note: """Please note that in order for the Geolocation API to be usable by a page in the WebView, the following requirements must be met: - an application must have permission to access the device location, see [Manifest.permission.ACCESS_COARSE_LOCATION](https://developer.android.com/reference/android/Manifest.permission#ACCESS_COARSE_LOCATION), [Manifest.permission.ACCESS_FINE_LOCATION](https://developer.android.com/reference/android/Manifest.permission#ACCESS_FINE_LOCATION); -- an application must provide an implementation of the [WebView.onGeolocationPermissionsShowPrompt] callback to receive notifications that a page is requesting access to location via the JavaScript Geolocation API.""") +- an application must provide an implementation of the [PlatformWebViewCreationParams.onGeolocationPermissionsShowPrompt] callback to receive notifications that a page is requesting access to location via the JavaScript Geolocation API.""") ]) bool? geolocationEnabled; @@ -746,7 +746,7 @@ because there isn't any way to make the website data store non-persistent for th bool? hardwareAcceleration; ///Sets whether the WebView supports multiple windows. - ///If set to `true`, [WebView.onCreateWindow] event must be implemented by the host application. The default value is `false`. + ///If set to `true`, [PlatformWebViewCreationParams.onCreateWindow] event must be implemented by the host application. The default value is `false`. @SupportedPlatforms(platforms: [ AndroidPlatform( apiName: "WebSettings.setSupportMultipleWindows", @@ -755,7 +755,7 @@ because there isn't any way to make the website data store non-persistent for th ]) bool? supportMultipleWindows; - ///Regular expression used by [WebView.shouldOverrideUrlLoading] event to cancel navigation requests for frames that are not the main frame. + ///Regular expression used by [PlatformWebViewCreationParams.shouldOverrideUrlLoading] event to cancel navigation requests 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. @SupportedPlatforms(platforms: [AndroidPlatform()]) String? regexToCancelSubFramesLoading; @@ -770,19 +770,19 @@ as it can cause framerate drops on animations in Android 9 and lower (see [Hybri ]) bool? useHybridComposition; - ///Set to `true` to be able to listen at the [WebView.shouldInterceptRequest] event. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.shouldInterceptRequest] event. /// - ///If the [WebView.shouldInterceptRequest] event is implemented and this value is `null`, + ///If the [PlatformWebViewCreationParams.shouldInterceptRequest] event is implemented and this value is `null`, ///it will be automatically inferred as `true`, otherwise, the default value is `false`. - ///This logic will not be applied for [InAppBrowser], where you must set the value manually. + ///This logic will not be applied for [PlatformInAppBrowser], where you must set the value manually. @SupportedPlatforms(platforms: [AndroidPlatform()]) bool? useShouldInterceptRequest; - ///Set to `true` to be able to listen at the [WebView.onRenderProcessGone] event. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.onRenderProcessGone] event. /// - ///If the [WebView.onRenderProcessGone] event is implemented and this value is `null`, + ///If the [PlatformWebViewCreationParams.onRenderProcessGone] event is implemented and this value is `null`, ///it will be automatically inferred as `true`, otherwise, the default value is `false`. - ///This logic will not be applied for [InAppBrowser], where you must set the value manually. + ///This logic will not be applied for [PlatformInAppBrowser], where you must set the value manually. @SupportedPlatforms(platforms: [AndroidPlatform()]) bool? useOnRenderProcessGone; @@ -963,7 +963,7 @@ as it can cause framerate drops on animations in Android 9 and lower (see [Hybri /// ///Apps can use this method to restore the legacy behavior for servers that still rely on the deprecated header, but it should not be used to identify the webview to first-party servers under the control of the app developer. /// - ///The format of the strings in the allow-list follows the origin rules of [InAppWebViewController.addWebMessageListener]. + ///The format of the strings in the allow-list follows the origin rules of [PlatformInAppWebViewController.addWebMessageListener]. @SupportedPlatforms(platforms: [ AndroidPlatform( apiName: "WebSettingsCompat.setRequestedWithHeaderOriginAllowList", @@ -1289,57 +1289,57 @@ as it can cause framerate drops on animations in Android 9 and lower (see [Hybri ]) bool? limitsNavigationsToAppBoundDomains; - ///Set to `true` to be able to listen to the [WebView.onNavigationResponse] event. + ///Set to `true` to be able to listen to the [PlatformWebViewCreationParams.onNavigationResponse] event. /// - ///If the [WebView.onNavigationResponse] event is implemented and this value is `null`, + ///If the [PlatformWebViewCreationParams.onNavigationResponse] event is implemented and this value is `null`, ///it will be automatically inferred as `true`, otherwise, the default value is `false`. - ///This logic will not be applied for [InAppBrowser], where you must set the value manually. + ///This logic will not be applied for [PlatformInAppBrowser], where you must set the value manually. @SupportedPlatforms(platforms: [IOSPlatform(), MacOSPlatform()]) bool? useOnNavigationResponse; - ///Set to `true` to enable Apple Pay API for the [WebView] at its first page load or on the next page load (using [InAppWebViewController.setOptions]). The default value is `false`. + ///Set to `true` to enable Apple Pay API for the `WebView` at its first page load or on the next page load (using [PlatformInAppWebViewController.setOptions]). The default value is `false`. /// ///**IMPORTANT NOTE**: As written in the official [Safari 13 Release Notes](https://developer.apple.com/documentation/safari-release-notes/safari-13-release-notes#Payment-Request-API), - ///it won't work if any script injection APIs are used (such as [InAppWebViewController.evaluateJavascript] or [UserScript]). + ///it won't work if any script injection APIs are used (such as [PlatformInAppWebViewController.evaluateJavascript] or [UserScript]). ///So, when this attribute is `true`, all the methods, options, and events implemented using JavaScript won't be called or won't do anything and the result will always be `null`. /// ///Methods affected: - ///- [InAppWebViewController.addUserScript] - ///- [InAppWebViewController.addUserScripts] - ///- [InAppWebViewController.removeUserScript] - ///- [InAppWebViewController.removeUserScripts] - ///- [InAppWebViewController.removeAllUserScripts] - ///- [InAppWebViewController.evaluateJavascript] - ///- [InAppWebViewController.callAsyncJavaScript] - ///- [InAppWebViewController.injectJavascriptFileFromUrl] - ///- [InAppWebViewController.injectJavascriptFileFromAsset] - ///- [InAppWebViewController.injectCSSCode] - ///- [InAppWebViewController.injectCSSFileFromUrl] - ///- [InAppWebViewController.injectCSSFileFromAsset] - ///- [InAppWebViewController.findAllAsync] - ///- [InAppWebViewController.findNext] - ///- [InAppWebViewController.clearMatches] - ///- [InAppWebViewController.pauseTimers] - ///- [InAppWebViewController.getSelectedText] - ///- [InAppWebViewController.getHitTestResult] - ///- [InAppWebViewController.requestFocusNodeHref] - ///- [InAppWebViewController.requestImageRef] - ///- [InAppWebViewController.postWebMessage] - ///- [InAppWebViewController.createWebMessageChannel] - ///- [InAppWebViewController.addWebMessageListener] + ///- [PlatformInAppWebViewController.addUserScript] + ///- [PlatformInAppWebViewController.addUserScripts] + ///- [PlatformInAppWebViewController.removeUserScript] + ///- [PlatformInAppWebViewController.removeUserScripts] + ///- [PlatformInAppWebViewController.removeAllUserScripts] + ///- [PlatformInAppWebViewController.evaluateJavascript] + ///- [PlatformInAppWebViewController.callAsyncJavaScript] + ///- [PlatformInAppWebViewController.injectJavascriptFileFromUrl] + ///- [PlatformInAppWebViewController.injectJavascriptFileFromAsset] + ///- [PlatformInAppWebViewController.injectCSSCode] + ///- [PlatformInAppWebViewController.injectCSSFileFromUrl] + ///- [PlatformInAppWebViewController.injectCSSFileFromAsset] + ///- [PlatformInAppWebViewController.findAllAsync] + ///- [PlatformInAppWebViewController.findNext] + ///- [PlatformInAppWebViewController.clearMatches] + ///- [PlatformInAppWebViewController.pauseTimers] + ///- [PlatformInAppWebViewController.getSelectedText] + ///- [PlatformInAppWebViewController.getHitTestResult] + ///- [PlatformInAppWebViewController.requestFocusNodeHref] + ///- [PlatformInAppWebViewController.requestImageRef] + ///- [PlatformInAppWebViewController.postWebMessage] + ///- [PlatformInAppWebViewController.createWebMessageChannel] + ///- [PlatformInAppWebViewController.addWebMessageListener] /// ///Also, on MacOS: - ///- [InAppWebViewController.getScrollX] - ///- [InAppWebViewController.getScrollY] - ///- [InAppWebViewController.scrollTo] - ///- [InAppWebViewController.scrollBy] - ///- [InAppWebViewController.getContentHeight] - ///- [InAppWebViewController.getContentWidth] - ///- [InAppWebViewController.canScrollVertically] - ///- [InAppWebViewController.canScrollHorizontally] + ///- [PlatformInAppWebViewController.getScrollX] + ///- [PlatformInAppWebViewController.getScrollY] + ///- [PlatformInAppWebViewController.scrollTo] + ///- [PlatformInAppWebViewController.scrollBy] + ///- [PlatformInAppWebViewController.getContentHeight] + ///- [PlatformInAppWebViewController.getContentWidth] + ///- [PlatformInAppWebViewController.canScrollVertically] + ///- [PlatformInAppWebViewController.canScrollHorizontally] /// ///Settings affected: - ///- [WebView.initialUserScripts] + ///- [PlatformWebViewCreationParams.initialUserScripts] ///- [InAppWebViewSettings.supportZoom] ///- [InAppWebViewSettings.useOnLoadResource] ///- [InAppWebViewSettings.useShouldInterceptAjaxRequest] @@ -1347,32 +1347,32 @@ as it can cause framerate drops on animations in Android 9 and lower (see [Hybri ///- [InAppWebViewSettings.enableViewportScale] /// ///Events affected: - ///- the `hitTestResult` argument of [WebView.onLongPressHitTestResult] will be empty + ///- the `hitTestResult` argument of [PlatformWebViewCreationParams.onLongPressHitTestResult] will be empty ///- the `hitTestResult` argument of [ContextMenu.onCreateContextMenu] will be empty - ///- [WebView.onLoadResource] - ///- [WebView.shouldInterceptAjaxRequest] - ///- [WebView.onAjaxReadyStateChange] - ///- [WebView.onAjaxProgress] - ///- [WebView.shouldInterceptFetchRequest] - ///- [WebView.onConsoleMessage] - ///- [WebView.onPrintRequest] - ///- [WebView.onWindowFocus] - ///- [WebView.onWindowBlur] - ///- [WebView.onFindResultReceived] + ///- [PlatformWebViewCreationParams.onLoadResource] + ///- [PlatformWebViewCreationParams.shouldInterceptAjaxRequest] + ///- [PlatformWebViewCreationParams.onAjaxReadyStateChange] + ///- [PlatformWebViewCreationParams.onAjaxProgress] + ///- [PlatformWebViewCreationParams.shouldInterceptFetchRequest] + ///- [PlatformWebViewCreationParams.onConsoleMessage] + ///- [PlatformWebViewCreationParams.onPrintRequest] + ///- [PlatformWebViewCreationParams.onWindowFocus] + ///- [PlatformWebViewCreationParams.onWindowBlur] + ///- [PlatformWebViewCreationParams.onFindResultReceived] ///- [FindInteractionController.onFindResultReceived] /// ///Also, on MacOS: - ///- [WebView.onScrollChanged] + ///- [PlatformWebViewCreationParams.onScrollChanged] @SupportedPlatforms(platforms: [ IOSPlatform(available: "13.0"), MacOSPlatform(available: "10.15") ]) bool? applePayAPIEnabled; - ///Used in combination with [WebView.initialUrlRequest] or [WebView.initialData] (using the `file://` scheme), it represents the URL from which to read the web content. + ///Used in combination with [PlatformWebViewCreationParams.initialUrlRequest] or [PlatformWebViewCreationParams.initialData] (using the `file://` scheme), it represents the URL from which to read the web content. ///This URL must be a file-based URL (using the `file://` scheme). - ///Specify the same value as the [URLRequest.url] if you are using it with the [WebView.initialUrlRequest] parameter or - ///the [InAppWebViewInitialData.baseUrl] if you are using it with the [WebView.initialData] parameter to prevent WebView from reading any other content. + ///Specify the same value as the [URLRequest.url] if you are using it with the [PlatformWebViewCreationParams.initialUrlRequest] parameter or + ///the [InAppWebViewInitialData.baseUrl] if you are using it with the [PlatformWebViewCreationParams.initialData] parameter to prevent WebView from reading any other content. ///Specify a directory to give WebView permission to read additional files in the specified directory. @SupportedPlatforms(platforms: [IOSPlatform(), MacOSPlatform()]) WebUri? allowingReadAccessTo; @@ -1767,7 +1767,7 @@ as it can cause framerate drops on animations in Android 9 and lower (see [Hybri } } -///Class that represents the options that can be used for a [WebView]. +///Class that represents the options that can be used for a `WebView`. ///Use [InAppWebViewSettings] instead. @Deprecated('Use InAppWebViewSettings instead') class InAppWebViewGroupOptions { @@ -1855,13 +1855,13 @@ class WebViewOptions { @Deprecated('Use InAppWebViewSettings instead') class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOptions, IosOptions { - ///Set to `true` to be able to listen at the [WebView.shouldOverrideUrlLoading] event. The default value is `false`. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.shouldOverrideUrlLoading] event. The default value is `false`. bool useShouldOverrideUrlLoading; - ///Set to `true` to be able to listen at the [WebView.onLoadResource] event. The default value is `false`. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.onLoadResource] event. The default value is `false`. bool useOnLoadResource; - ///Set to `true` to be able to listen at the [WebView.onDownloadStart] event. The default value is `false`. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.onDownloadStart] event. The default value is `false`. bool useOnDownloadStart; ///Set to `true` to have all the browser's cache cleared before the new WebView is opened. The default value is `false`. @@ -1897,7 +1897,7 @@ class InAppWebViewOptions ///Define whether the horizontal scrollbar should be drawn or not. The default value is `true`. bool horizontalScrollBarEnabled; - ///List of custom schemes that the WebView must handle. Use the [WebView.onLoadResourceCustomScheme] event to intercept resource requests with custom scheme. + ///List of custom schemes that the WebView must handle. Use the [PlatformWebViewCreationParams.onLoadResourceCustomScheme] event to intercept resource requests with custom scheme. /// ///**NOTE**: available on iOS 11.0+. List resourceCustomSchemes; @@ -1912,10 +1912,10 @@ class InAppWebViewOptions ///**NOTE**: available on iOS 13.0+. UserPreferredContentMode? preferredContentMode; - ///Set to `true` to be able to listen at the [WebView.shouldInterceptAjaxRequest] event. The default value is `false`. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.shouldInterceptAjaxRequest] event. The default value is `false`. bool useShouldInterceptAjaxRequest; - ///Set to `true` to be able to listen at the [WebView.shouldInterceptFetchRequest] event. The default value is `false`. + ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.shouldInterceptFetchRequest] event. The default value is `false`. bool useShouldInterceptFetchRequest; ///Set to `true` to open a browser window with incognito mode. The default value is `false`. diff --git a/lib/src/in_app_webview/in_app_webview_settings.g.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.g.dart similarity index 86% rename from lib/src/in_app_webview/in_app_webview_settings.g.dart rename to flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.g.dart index b0fbad4f..d436b37c 100644 --- a/lib/src/in_app_webview/in_app_webview_settings.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.g.dart @@ -11,7 +11,7 @@ class InAppWebViewSettings { ///A Boolean value indicating whether the WebView ignores an accessibility request to invert its colors. ///The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS 11.0+ ([Official API - UIView.accessibilityIgnoresInvertColors](https://developer.apple.com/documentation/uikit/uiview/2865843-accessibilityignoresinvertcolors)) bool? accessibilityIgnoresInvertColors; @@ -29,32 +29,32 @@ class InAppWebViewSettings { /// ///**NOTE for Android native WebView**: available on Android only if [WebViewFeature.ALGORITHMIC_DARKENING] feature is supported. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView 29+ ([Official API - WebSettingsCompat.setAlgorithmicDarkeningAllowed](https://developer.android.com/reference/androidx/webkit/WebSettingsCompat#setAlgorithmicDarkeningAllowed(android.webkit.WebSettings,boolean))) bool? algorithmicDarkeningAllowed; ///Set to `true` to allow audio playing when the app goes in background or the screen is locked or another app is opened. ///However, there will be no controls in the notification bar or on the lockscreen. - ///Also, make sure to not call [InAppWebViewController.pause], otherwise it will stop audio playing. + ///Also, make sure to not call [PlatformInAppWebViewController.pause], otherwise it will stop audio playing. ///The default value is `false`. /// ///**IMPORTANT NOTE**: if you use this setting, your app could be rejected by the Google Play Store. ///For example, if you allow background playing of YouTube videos, which is a violation of the YouTube API Terms of Service. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView bool? allowBackgroundAudioPlaying; ///Enables or disables content URL access within WebView. Content URL access allows WebView to load content from a content provider installed in the system. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebSettings.setAllowContentAccess](https://developer.android.com/reference/android/webkit/WebSettings?hl=en#setAllowContentAccess(boolean))) bool? allowContentAccess; ///Enables or disables file access within WebView. Note that this enables or disables file system access only. ///Assets and resources are still accessible using `file:///android_asset` and `file:///android_res`. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebSettings.setAllowFileAccess](https://developer.android.com/reference/android/webkit/WebSettings?hl=en#setAllowFileAccess(boolean))) bool? allowFileAccess; @@ -68,7 +68,7 @@ class InAppWebViewSettings { /// ///The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebSettings.setAllowFileAccessFromFileURLs](https://developer.android.com/reference/android/webkit/WebSettings?hl=en#setAllowFileAccessFromFileURLs(boolean))) ///- iOS ///- MacOS @@ -84,33 +84,33 @@ class InAppWebViewSettings { /// ///The default value is `false`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebSettings.setAllowUniversalAccessFromFileURLs](https://developer.android.com/reference/android/webkit/WebSettings?hl=en#setAllowUniversalAccessFromFileURLs(boolean))) ///- iOS ///- MacOS bool? allowUniversalAccessFromFileURLs; - ///Used in combination with [WebView.initialUrlRequest] or [WebView.initialData] (using the `file://` scheme), it represents the URL from which to read the web content. + ///Used in combination with [PlatformWebViewCreationParams.initialUrlRequest] or [PlatformWebViewCreationParams.initialData] (using the `file://` scheme), it represents the URL from which to read the web content. ///This URL must be a file-based URL (using the `file://` scheme). - ///Specify the same value as the [URLRequest.url] if you are using it with the [WebView.initialUrlRequest] parameter or - ///the [InAppWebViewInitialData.baseUrl] if you are using it with the [WebView.initialData] parameter to prevent WebView from reading any other content. + ///Specify the same value as the [URLRequest.url] if you are using it with the [PlatformWebViewCreationParams.initialUrlRequest] parameter or + ///the [InAppWebViewInitialData.baseUrl] if you are using it with the [PlatformWebViewCreationParams.initialData] parameter to prevent WebView from reading any other content. ///Specify a directory to give WebView permission to read additional files in the specified directory. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ///- MacOS WebUri? allowingReadAccessTo; ///Set to `true` to allow AirPlay. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ([Official API - WKWebViewConfiguration.allowsAirPlayForMediaPlayback](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1395673-allowsairplayformediaplayback)) ///- MacOS ([Official API - WKWebViewConfiguration.allowsAirPlayForMediaPlayback](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1395673-allowsairplayformediaplayback)) bool? allowsAirPlayForMediaPlayback; ///Set to `true` to allow the horizontal swipe gestures trigger back-forward list navigations. The default value is `true`. /// - ///**Supported Platforms/Implementations**: + ///**Officially Supported Platforms/Implementations**: ///- iOS ([Official API - WKWebView.allowsBackForwardNavigationGestures](https://developer.apple.com/documentation/webkit/wkwebview/1414995-allowsbackforwardnavigationgestu)) ///- MacOS ([Official API - WKWebView.allowsBackForwardNavigationGestures](https://developer.apple.com/documentation/webkit/wkwebview/1414995-allowsbackforwardnavigationgestu)) bool? allowsBackForwardNavigationGestures; @@ -118,20 +118,20 @@ class InAppWebViewSettings { ///Set to `true` to allow HTML5 media playback to appear inline within the screen layout, using browser-supplied controls rather than native controls. ///For this to work, add the `webkit-playsinline` attribute to any `