From 37fa32b31e647063804642fdb2db4e6fa4c1f470 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Thu, 4 Feb 2021 21:54:09 +0100 Subject: [PATCH] added AndroidWebViewFeature, AndroidServiceWorkerController, AndroidServiceWorkerClient classes --- CHANGELOG.md | 3 +- .../InAppWebView/InAppWebView.java | 12 +- .../InAppWebViewFlutterPlugin.java | 15 ++ .../RequestPermissionHandler.java | 1 - .../ServiceWorkerManager.java | 156 +++++++++++++ .../WebViewFeatureManager.java | 34 +++ example/.flutter-plugins-dependencies | 2 +- example/assets/images/flutter-logo.jpg | Bin 0 -> 4044 bytes example/assets/images/flutter-logo.png | Bin 0 -> 1216 bytes example/ios/Runner.xcodeproj/project.pbxproj | 4 +- example/lib/in_app_webiew_example.screen.dart | 4 +- example/lib/main.dart | 12 + example/test_assets/images/flutter-logo.jpg | Bin 0 -> 4044 bytes example/test_assets/images/flutter-logo.png | Bin 0 -> 1216 bytes lib/flutter_inappwebview.dart | 4 +- .../android/service_worker_controller.dart | 179 +++++++++++++++ lib/src/android/webview_feature.dart | 214 ++++++++++++++++++ lib/src/in_app_webview_controller.dart | 1 + lib/src/types.dart | 4 +- 19 files changed, 626 insertions(+), 19 deletions(-) create mode 100755 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java create mode 100755 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/WebViewFeatureManager.java create mode 100644 example/assets/images/flutter-logo.jpg create mode 100644 example/assets/images/flutter-logo.png create mode 100644 example/test_assets/images/flutter-logo.jpg create mode 100644 example/test_assets/images/flutter-logo.png create mode 100644 lib/src/android/service_worker_controller.dart create mode 100644 lib/src/android/webview_feature.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ea592b..584c8225 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,11 @@ - Added `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options also for iOS (also thanks to [liranhao](https://github.com/liranhao)) - Added limited cookies support on iOS below 11.0 using JavaScript - Added `IOSCookieManager` class and `CookieManager.instance().ios.getAllCookies` iOS-specific method -- Added `UserScript` and `UserScriptInjectionTime` classes +- Added `UserScript`, `UserScriptInjectionTime`, `ContentWorld`, `AndroidWebViewFeature`, `AndroidServiceWorkerController`, `AndroidServiceWorkerClient` classes - Added `initialUserScripts` WebView option - Added `addUserScript`, `addUserScripts`, `removeUserScript`, `removeUserScripts`, `removeAllUserScripts` WebView methods - Added `isDirectionalLockEnabled`, `mediaType`, `pageZoom`, `limitsNavigationsToAppBoundDomains` iOS-specific webview options - Added `handlesURLScheme` iOS-specific webview method -- Added `ContentWorld` class - Updated integration tests - Merge "Upgraded appcompat to 1.2.0-rc-02" [#465](https://github.com/pichillilorenzo/flutter_inappwebview/pull/465) (thanks to [andreidiaconu](https://github.com/andreidiaconu)) - Merge "Added missing field 'headers' which returned by WebResourceResponse.toMap()" [#490](https://github.com/pichillilorenzo/flutter_inappwebview/pull/490) (thanks to [Doflatango](https://github.com/Doflatango)) diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java index 383bfdb2..cf1bdd96 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java @@ -35,20 +35,16 @@ import android.webkit.CookieManager; import android.webkit.DownloadListener; import android.webkit.ValueCallback; import android.webkit.WebBackForwardList; -import android.webkit.WebChromeClient; import android.webkit.WebHistoryItem; import android.webkit.WebSettings; import android.webkit.WebStorage; -import android.webkit.WebViewClient; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import android.widget.TextView; -import androidx.annotation.Keep; import androidx.annotation.RequiresApi; import androidx.webkit.WebViewCompat; import androidx.webkit.WebViewFeature; -import androidx.webkit.WebViewRenderProcessClient; import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlocker; import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerAction; @@ -63,9 +59,6 @@ import com.pichillilorenzo.flutter_inappwebview.Util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashMap; @@ -131,10 +124,11 @@ final public class InAppWebView extends InputAwareWebView { "}"; static final String contentWorldWrapperJS = "(function() {" + - " var iframe = document.getElementById('$CONTENT_WORLD_NAME');" + + " var iframeId = '" + JavaScriptBridgeInterface.name + "_$CONTENT_WORLD_NAME';" + + " var iframe = document.getElementById(iframeId);" + " if (iframe == null) {" + " iframe = document.createElement('iframe');" + - " iframe.id = '$CONTENT_WORLD_NAME';" + + " iframe.id = iframeId;" + " iframe.style = 'display: none; z-index: 0; position: absolute; width: 0px; height: 0px';" + " document.body.append(iframe);" + " }" + diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewFlutterPlugin.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewFlutterPlugin.java index 7bbd4f5d..4af050b7 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewFlutterPlugin.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewFlutterPlugin.java @@ -29,6 +29,8 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware { public static MyCookieManager myCookieManager; public static CredentialDatabaseHandler credentialDatabaseHandler; public static MyWebStorage myWebStorage; + public static ServiceWorkerManager serviceWorkerManager; + public static WebViewFeatureManager webViewFeatureManager; public static ValueCallback filePathCallbackLegacy; public static ValueCallback filePathCallback; @@ -65,12 +67,17 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware { platformViewRegistry.registerViewFactory( "com.pichillilorenzo/flutter_inappwebview", new FlutterWebViewFactory(messenger, flutterView)); + inAppWebViewStatic = new InAppWebViewStatic(messenger); myCookieManager = new MyCookieManager(messenger); myWebStorage = new MyWebStorage(messenger); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + serviceWorkerManager = new ServiceWorkerManager(messenger); + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { credentialDatabaseHandler = new CredentialDatabaseHandler(messenger); } + webViewFeatureManager = new WebViewFeatureManager(messenger); } @Override @@ -103,6 +110,14 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware { inAppWebViewStatic.dispose(); inAppWebViewStatic = null; } + if (serviceWorkerManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + serviceWorkerManager.dispose(); + serviceWorkerManager = null; + } + if (webViewFeatureManager != null) { + webViewFeatureManager.dispose(); + webViewFeatureManager = null; + } filePathCallbackLegacy = null; filePathCallback = null; } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/RequestPermissionHandler.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/RequestPermissionHandler.java index bcb1117e..c1ca8fe3 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/RequestPermissionHandler.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/RequestPermissionHandler.java @@ -34,7 +34,6 @@ public abstract class RequestPermissionHandler implements ActivityCompat.OnReque @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { - Log.d("asdasd", "\n\na asd asd \n\n"); if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { List callbacks = actionDictionary.get(requestCode); for (Runnable runnable : callbacks) { diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java new file mode 100755 index 00000000..4ca04b7e --- /dev/null +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java @@ -0,0 +1,156 @@ +package com.pichillilorenzo.flutter_inappwebview; + +import android.os.Build; +import android.util.Log; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.webkit.ServiceWorkerClientCompat; +import androidx.webkit.ServiceWorkerControllerCompat; +import androidx.webkit.ServiceWorkerWebSettingsCompat; +import androidx.webkit.WebViewFeature; + +import java.io.ByteArrayInputStream; +import java.util.HashMap; +import java.util.Map; + +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + +@RequiresApi(api = Build.VERSION_CODES.N) +public class ServiceWorkerManager implements MethodChannel.MethodCallHandler { + + static final String LOG_TAG = "ServiceWorkerManager"; + + public static MethodChannel channel; + @Nullable + public static ServiceWorkerControllerCompat serviceWorkerController; + + public ServiceWorkerManager(BinaryMessenger messenger) { + channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_android_serviceworkercontroller"); + channel.setMethodCallHandler(this); + if (WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BASIC_USAGE)) { + serviceWorkerController = ServiceWorkerControllerCompat.getInstance(); + serviceWorkerController.setServiceWorkerClient(new ServiceWorkerClientCompat() { + @Nullable + @Override + public WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request) { + final Map obj = new HashMap<>(); + obj.put("url", request.getUrl().toString()); + obj.put("method", request.getMethod()); + obj.put("headers", request.getRequestHeaders()); + obj.put("isForMainFrame", request.isForMainFrame()); + obj.put("hasGesture", request.hasGesture()); + obj.put("isRedirect", request.isRedirect()); + + Util.WaitFlutterResult flutterResult; + try { + flutterResult = Util.invokeMethodAndWait(channel, "shouldInterceptRequest", obj); + } catch (InterruptedException e) { + e.printStackTrace(); + return null; + } + + if (flutterResult.error != null) { + Log.e(LOG_TAG, flutterResult.error); + } + else if (flutterResult.result != null) { + Map res = (Map) flutterResult.result; + String contentType = (String) res.get("contentType"); + String contentEncoding = (String) res.get("contentEncoding"); + byte[] data = (byte[]) res.get("data"); + Map responseHeaders = (Map) res.get("headers"); + Integer statusCode = (Integer) res.get("statusCode"); + String reasonPhrase = (String) res.get("reasonPhrase"); + + ByteArrayInputStream inputStream = (data != null) ? new ByteArrayInputStream(data) : null; + + if ((responseHeaders == null && statusCode == null && reasonPhrase == null) || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return new WebResourceResponse(contentType, contentEncoding, inputStream); + } else { + return new WebResourceResponse(contentType, contentEncoding, statusCode, reasonPhrase, responseHeaders, inputStream); + } + } + + return null; + } + }); + } else { + serviceWorkerController = null; + } + } + + @Override + public void onMethodCall(MethodCall call, MethodChannel.Result result) { + ServiceWorkerWebSettingsCompat serviceWorkerWebSettings = (serviceWorkerController != null) ? serviceWorkerController.getServiceWorkerWebSettings() : null; + + switch (call.method) { + case "getAllowContentAccess": + if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS)) { + result.success(serviceWorkerWebSettings.getAllowContentAccess()); + } else { + result.success(false); + } + break; + case "getAllowFileAccess": + if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_FILE_ACCESS)) { + result.success(serviceWorkerWebSettings.getAllowFileAccess()); + } else { + result.success(false); + } + break; + case "getBlockNetworkLoads": + if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS)) { + result.success(serviceWorkerWebSettings.getBlockNetworkLoads()); + } else { + result.success(false); + } + break; + case "getCacheMode": + if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CACHE_MODE)) { + result.success(serviceWorkerWebSettings.getCacheMode()); + } else { + result.success(null); + } + break; + case "setAllowContentAccess": + if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS)) { + Boolean allow = (Boolean) call.argument("allow"); + serviceWorkerWebSettings.setAllowContentAccess(allow); + } + result.success(true); + break; + case "setAllowFileAccess": + if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_FILE_ACCESS)) { + Boolean allow = (Boolean) call.argument("allow"); + serviceWorkerWebSettings.setAllowFileAccess(allow); + } + result.success(true); + break; + case "setBlockNetworkLoads": + if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS)) { + Boolean flag = (Boolean) call.argument("flag"); + serviceWorkerWebSettings.setBlockNetworkLoads(flag); + } + result.success(true); + break; + case "setCacheMode": + if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CACHE_MODE)) { + Integer mode = (Integer) call.argument("mode"); + serviceWorkerWebSettings.setCacheMode(mode); + } + result.success(true); + break; + default: + result.notImplemented(); + } + } + + public void dispose() { + channel.setMethodCallHandler(null); + } +} diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/WebViewFeatureManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/WebViewFeatureManager.java new file mode 100755 index 00000000..17637d80 --- /dev/null +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/WebViewFeatureManager.java @@ -0,0 +1,34 @@ +package com.pichillilorenzo.flutter_inappwebview; + +import androidx.webkit.WebViewFeature; + +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + +public class WebViewFeatureManager implements MethodChannel.MethodCallHandler { + + static final String LOG_TAG = "WebViewFeatureManager"; + + public static MethodChannel channel; + + public WebViewFeatureManager(BinaryMessenger messenger) { + channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_android_webviewfeature"); + channel.setMethodCallHandler(this); + } + + @Override + public void onMethodCall(MethodCall call, MethodChannel.Result result) { + switch (call.method) { + case "isFeatureSupported": + String feature = (String) call.argument("feature"); + result.success(WebViewFeature.isFeatureSupported(feature)); + default: + result.notImplemented(); + } + } + + public void dispose() { + channel.setMethodCallHandler(null); + } +} diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index b053648b..69410532 100644 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-02-04 01:10:32.198308","version":"1.26.0-18.0.pre.90"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-02-04 21:51:34.603760","version":"1.26.0-18.0.pre.90"} \ No newline at end of file diff --git a/example/assets/images/flutter-logo.jpg b/example/assets/images/flutter-logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6d17a4da4a4e2943374deb1d77d2208d0005e249 GIT binary patch literal 4044 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 literal 0 HcmV?d00001 diff --git a/example/assets/images/flutter-logo.png b/example/assets/images/flutter-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e4cb3f825fd2f3b53cd6fb305003962ba1f28091 GIT binary patch literal 1216 zcmaJ=YfMuI7`?Z*%6Jq?@c}DJl~n~YVJ-n7&{pLktQITN1Oh7}Qb)<`7HCY}u%6f`IZU6lpQa1TN*>0fpYXl4XDP!;_QmJ0~Y6`Tl(8Ls$U> zvro+c03ZY&^koA8Xp|uE31nQnhY=j(Vudg{egSDrqx?VCtrd+3RV;-lmP7v|7%pVB z=-7&7V}w6?i=$Wqj|l&rQ1FHI^ki$Ex0f;7l)+|&0^u%I=s>CIblx9k^~EWVHXM+L z&n(py|LAWln+N*t<7BI}D)YRXZnodY*zl{8tun}Z3>(9Rdsc^EJ?J{xl$VMyoHRn> zfwB0tvpM#fAa%;FZwxQb1$_}Aqhy6G~?QhSdR6B3emXo{R|J`LH7h!6*^#d;=Rl^qw zl`|%DS=8Q>Ls*nLb~Ss}#b_B}hc-KQ3QPFzrwl{G?TXrEuR&C0%Z(c`j5cY=aeI^)NrioI zY@oz4WBdiJTXGq9>N>VwB*{W$b=~8lJp9Q1)O>Yfd=$8X=wzB>82>nI(Kx$IH9(3T z#2(bV6nuNB$=MxF!X4JixK>2xG|~7xlUoS9!*55Tcf;(B+u|~S-|?|Xv>Pm@KF45tVbC?}xRrwX=8A^2pg+8#B)`U_`_Wk4o*U>Jsu~S_T8%os6PnLyXd@v$miU?0b zfm%B6Fz0&GB2F3K{0dwswF|?_0i5_vUkT9qghzR1@>xH9Cp@k#=cM;T4UJhAl*g#& zae0QlhZ3@V1Q)q1uKo$O_S2D;B9|RoDzns?d;Ck9I~MLm7coI!B^`M+@Bk2rTXK+z zX5H=fnweeT9sW9E6lM9%djeO&cP(%Hc7Cf8$t$v}D;8IgO&*TxnR8TPP$ez@FM|AO zle21PR`?C6s9K=z_fk$y`V2U6R_t3X{Rxi20dBRM6ZpVTK_#l6PEZ5+cRl;wC|6`I zUwEFXxHP{az4F_pm*PkPuG^cOp#}3vzj8C?+D1-(En7T}qkEq>_a6HEP)uiVRzTjP z#D0#Sz)4c@gkZnV(j$K9yWYr3RH$dXm^wG#*krkZIWqFZw6{X28zb$t@6u^ro7{Fi z^KLdfA|2P}4ojnWxKzXGuA8KRshfJ_N*Hw}^TGZ8YjA-$kU|6sU>d_A;xO(8O}>|_ iIji>g_{XlLY0(&X%~z7kDZ52p^MQUW-`f4$v;P1odMJJX literal 0 HcmV?d00001 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 440922a3..d6eb46d1 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ 020EF14E4245221B2C22ACE5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B0FC2CF7A6002799890B3102 /* Pods_Runner.framework */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 25A517508F43E58C47090625 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; + 25A517508F43E58C47090625 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 61FF730023634CA10069C557 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 61FF72FF23634CA10069C557 /* libsqlite3.tbd */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; @@ -46,7 +46,7 @@ buildActionMask = 2147483647; files = ( 61FF730023634CA10069C557 /* libsqlite3.tbd in Frameworks */, - 25A517508F43E58C47090625 /* BuildFile in Frameworks */, + 25A517508F43E58C47090625 /* (null) in Frameworks */, 020EF14E4245221B2C22ACE5 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 09470cac..cd331cd8 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -81,7 +81,7 @@ class _InAppWebViewExampleScreenState extends State { BoxDecoration(border: Border.all(color: Colors.blueAccent)), child: InAppWebView( // contextMenu: contextMenu, - initialUrl: "https://flutter.dev/", + initialUrl: "https://flutter.dev", // initialFile: "assets/index.html", initialHeaders: {}, initialUserScripts: UnmodifiableListView([ @@ -91,12 +91,14 @@ class _InAppWebViewExampleScreenState extends State { crossPlatform: InAppWebViewOptions( useShouldOverrideUrlLoading: false, mediaPlaybackRequiresUserGesture: false, + clearCache: true ), android: AndroidInAppWebViewOptions( useHybridComposition: true, ), ios: IOSInAppWebViewOptions( allowsInlineMediaPlayback: true, + // limitsNavigationsToAppBoundDomains: true // adds Service Worker API on iOS 14.0+ ) ), onWebViewCreated: (controller) { diff --git a/example/lib/main.dart b/example/lib/main.dart index 9e6a49a9..210c78ac 100755 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -12,6 +12,8 @@ import 'package:flutter_inappwebview_example/in_app_browser_example.screen.dart' // InAppLocalhostServer localhostServer = new InAppLocalhostServer(); +AndroidServiceWorkerController serviceWorkerController = AndroidServiceWorkerController.instance(); + Future main() async { WidgetsFlutterBinding.ensureInitialized(); // await Permission.camera.request(); @@ -19,6 +21,16 @@ Future main() async { if (Platform.isAndroid) { await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true); } + + if (await AndroidWebViewFeature.isFeatureSupported(AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST)) { + serviceWorkerController.serviceWorkerClient = AndroidServiceWorkerClient( + shouldInterceptRequest: (request) async { + print(request); + return null; + }, + ); + } + runApp(MyApp()); } diff --git a/example/test_assets/images/flutter-logo.jpg b/example/test_assets/images/flutter-logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6d17a4da4a4e2943374deb1d77d2208d0005e249 GIT binary patch literal 4044 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 literal 0 HcmV?d00001 diff --git a/example/test_assets/images/flutter-logo.png b/example/test_assets/images/flutter-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e4cb3f825fd2f3b53cd6fb305003962ba1f28091 GIT binary patch literal 1216 zcmaJ=YfMuI7`?Z*%6Jq?@c}DJl~n~YVJ-n7&{pLktQITN1Oh7}Qb)<`7HCY}u%6f`IZU6lpQa1TN*>0fpYXl4XDP!;_QmJ0~Y6`Tl(8Ls$U> zvro+c03ZY&^koA8Xp|uE31nQnhY=j(Vudg{egSDrqx?VCtrd+3RV;-lmP7v|7%pVB z=-7&7V}w6?i=$Wqj|l&rQ1FHI^ki$Ex0f;7l)+|&0^u%I=s>CIblx9k^~EWVHXM+L z&n(py|LAWln+N*t<7BI}D)YRXZnodY*zl{8tun}Z3>(9Rdsc^EJ?J{xl$VMyoHRn> zfwB0tvpM#fAa%;FZwxQb1$_}Aqhy6G~?QhSdR6B3emXo{R|J`LH7h!6*^#d;=Rl^qw zl`|%DS=8Q>Ls*nLb~Ss}#b_B}hc-KQ3QPFzrwl{G?TXrEuR&C0%Z(c`j5cY=aeI^)NrioI zY@oz4WBdiJTXGq9>N>VwB*{W$b=~8lJp9Q1)O>Yfd=$8X=wzB>82>nI(Kx$IH9(3T z#2(bV6nuNB$=MxF!X4JixK>2xG|~7xlUoS9!*55Tcf;(B+u|~S-|?|Xv>Pm@KF45tVbC?}xRrwX=8A^2pg+8#B)`U_`_Wk4o*U>Jsu~S_T8%os6PnLyXd@v$miU?0b zfm%B6Fz0&GB2F3K{0dwswF|?_0i5_vUkT9qghzR1@>xH9Cp@k#=cM;T4UJhAl*g#& zae0QlhZ3@V1Q)q1uKo$O_S2D;B9|RoDzns?d;Ck9I~MLm7coI!B^`M+@Bk2rTXK+z zX5H=fnweeT9sW9E6lM9%djeO&cP(%Hc7Cf8$t$v}D;8IgO&*TxnR8TPP$ez@FM|AO zle21PR`?C6s9K=z_fk$y`V2U6R_t3X{Rxi20dBRM6ZpVTK_#l6PEZ5+cRl;wC|6`I zUwEFXxHP{az4F_pm*PkPuG^cOp#}3vzj8C?+D1-(En7T}qkEq>_a6HEP)uiVRzTjP z#D0#Sz)4c@gkZnV(j$K9yWYr3RH$dXm^wG#*krkZIWqFZw6{X28zb$t@6u^ro7{Fi z^KLdfA|2P}4ojnWxKzXGuA8KRshfJ_N*Hw}^TGZ8YjA-$kU|6sU>d_A;xO(8O}>|_ iIji>g_{XlLY0(&X%~z7kDZ52p^MQUW-`f4$v;P1odMJJX literal 0 HcmV?d00001 diff --git a/lib/flutter_inappwebview.dart b/lib/flutter_inappwebview.dart index 2c95f52f..f5df52f7 100755 --- a/lib/flutter_inappwebview.dart +++ b/lib/flutter_inappwebview.dart @@ -37,4 +37,6 @@ export 'src/http_auth_credentials_database.dart'; export 'src/web_storage_manager.dart'; export 'src/context_menu.dart'; export 'src/web_storage.dart'; -export 'src/X509Certificate/main.dart'; \ No newline at end of file +export 'src/X509Certificate/main.dart'; +export 'src/android/service_worker_controller.dart'; +export 'src/android/webview_feature.dart'; \ No newline at end of file diff --git a/lib/src/android/service_worker_controller.dart b/lib/src/android/service_worker_controller.dart new file mode 100644 index 00000000..cdb85a16 --- /dev/null +++ b/lib/src/android/service_worker_controller.dart @@ -0,0 +1,179 @@ +import 'dart:async'; +import 'package:flutter/services.dart'; +import 'webview_feature.dart'; +import '../types.dart'; + +///Class that represents an Android-specific class that manages Service Workers used by [WebView]. +/// +///**NOTE**: available on Android 24+. +/// +///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerControllerCompat +class AndroidServiceWorkerController { + static AndroidServiceWorkerController? _instance; + static const MethodChannel _channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_android_serviceworkercontroller'); + + AndroidServiceWorkerClient? serviceWorkerClient; + + ///Gets the [AndroidServiceWorkerController] shared instance. + static AndroidServiceWorkerController instance() { + return (_instance != null) ? _instance! : _init(); + } + + static AndroidServiceWorkerController _init() { + _channel.setMethodCallHandler(_handleMethod); + _instance = AndroidServiceWorkerController(); + return _instance!; + } + + static Future _handleMethod(MethodCall call) async { + AndroidServiceWorkerController controller = AndroidServiceWorkerController.instance(); + AndroidServiceWorkerClient? serviceWorkerClient = controller.serviceWorkerClient; + + switch (call.method) { + case "shouldInterceptRequest": + String url = call.arguments["url"]; + String method = call.arguments["method"]; + Map? headers = + call.arguments["headers"]?.cast(); + bool isForMainFrame = call.arguments["isForMainFrame"]; + bool hasGesture = call.arguments["hasGesture"]; + bool isRedirect = call.arguments["isRedirect"]; + + var request = new WebResourceRequest( + url: url, + method: method, + headers: headers, + isForMainFrame: isForMainFrame, + hasGesture: hasGesture, + isRedirect: isRedirect); + + if (serviceWorkerClient != null && serviceWorkerClient.shouldInterceptRequest != null) { + return (await serviceWorkerClient.shouldInterceptRequest!(request)) + ?.toMap(); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + + return null; + } + + ///Gets whether Service Workers support content URL access. + ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_CONTENT_ACCESS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getAllowContentAccess() + static Future getAllowContentAccess() async { + Map args = {}; + return await _channel.invokeMethod('getAllowContentAccess', args); + } + + ///Gets whether Service Workers support file access. + ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_FILE_ACCESS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getAllowFileAccess() + static Future getAllowFileAccess() async { + Map args = {}; + return await _channel.invokeMethod('getAllowFileAccess', args); + } + + ///Gets whether Service Workers are prohibited from loading any resources from the network. + ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getBlockNetworkLoads() + static Future getBlockNetworkLoads() async { + Map args = {}; + return await _channel.invokeMethod('getBlockNetworkLoads', args); + } + + ///Gets the current setting for overriding the cache mode. + ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_CACHE_MODE]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getCacheMode() + static Future getCacheMode() async { + Map args = {}; + return AndroidCacheMode.fromValue(await _channel.invokeMethod('getCacheMode', args)); + } + + ///Enables or disables content URL access from Service Workers. + ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_CONTENT_ACCESS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setAllowContentAccess(boolean) + static Future setAllowContentAccess(bool allow) async { + Map args = {}; + args.putIfAbsent("allow", () => allow); + await _channel.invokeMethod('setAllowContentAccess', args); + } + + ///Enables or disables file access within Service Workers. + ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_FILE_ACCESS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setAllowFileAccess(boolean) + static Future setAllowFileAccess(bool allow) async { + Map args = {}; + args.putIfAbsent("allow", () => allow); + await _channel.invokeMethod('setAllowFileAccess', args); + } + + ///Sets whether Service Workers should not load resources from the network. + ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setBlockNetworkLoads(boolean) + static Future setBlockNetworkLoads(bool flag) async { + Map args = {}; + args.putIfAbsent("flag", () => flag); + await _channel.invokeMethod('setBlockNetworkLoads', args); + } + + ///Overrides the way the cache is used. + ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_CACHE_MODE]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setCacheMode(int) + static Future setCacheMode(AndroidCacheMode mode) async { + Map args = {}; + args.putIfAbsent("mode", () => mode.toValue()); + await _channel.invokeMethod('setCacheMode', args); + } +} + +///Class that represents an Android-specific class for clients to capture Service Worker related callbacks. +/// +///**NOTE**: available on Android 24+. +/// +///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerClientCompat +class AndroidServiceWorkerClient { + + ///Notify the host application of a resource request and allow the application to return the data. + ///If the return value is `null`, the Service Worker will continue to load the resource as usual. + ///Otherwise, the return response and data will be used. + /// + ///This method is called only if [AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST] is supported. + ///You can check whether that flag is supported using [AndroidWebViewFeature.isFeatureSupported]. + /// + ///[request] represents an object containing the details of the request. + /// + ///**NOTE**: available on Android 24+. + final Future Function(WebResourceRequest request)? + shouldInterceptRequest; + + AndroidServiceWorkerClient({ + this.shouldInterceptRequest + }); +} \ No newline at end of file diff --git a/lib/src/android/webview_feature.dart b/lib/src/android/webview_feature.dart new file mode 100644 index 00000000..d30e7bf0 --- /dev/null +++ b/lib/src/android/webview_feature.dart @@ -0,0 +1,214 @@ +import 'dart:async'; +import 'package:flutter/services.dart'; + +///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device. +class AndroidWebViewFeature { + static const MethodChannel _channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_android_webviewfeature'); + + static Future _handleMethod(MethodCall call) async {} + + final String _value; + + const AndroidWebViewFeature._internal(this._value); + + static final Set values = [ + AndroidWebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, + AndroidWebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, + AndroidWebViewFeature.FORCE_DARK, + AndroidWebViewFeature.FORCE_DARK_STRATEGY, + AndroidWebViewFeature.GET_WEB_CHROME_CLIENT, + AndroidWebViewFeature.GET_WEB_VIEW_CLIENT, + AndroidWebViewFeature.GET_WEB_VIEW_RENDERER, + AndroidWebViewFeature.MULTI_PROCESS, + AndroidWebViewFeature.OFF_SCREEN_PRERASTER, + AndroidWebViewFeature.POST_WEB_MESSAGE, + AndroidWebViewFeature.PROXY_OVERRIDE, + AndroidWebViewFeature.RECEIVE_HTTP_ERROR, + AndroidWebViewFeature.RECEIVE_WEB_RESOURCE_ERROR, + AndroidWebViewFeature.SAFE_BROWSING_ALLOWLIST, + AndroidWebViewFeature.SAFE_BROWSING_ENABLE, + AndroidWebViewFeature.SAFE_BROWSING_HIT, + AndroidWebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, + AndroidWebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, + AndroidWebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, + AndroidWebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, + AndroidWebViewFeature.SAFE_BROWSING_WHITELIST, + AndroidWebViewFeature.SERVICE_WORKER_BASIC_USAGE, + AndroidWebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, + AndroidWebViewFeature.SERVICE_WORKER_CACHE_MODE, + AndroidWebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, + AndroidWebViewFeature.SERVICE_WORKER_FILE_ACCESS, + AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST, + AndroidWebViewFeature.SHOULD_OVERRIDE_WITH_REDIRECTS, + AndroidWebViewFeature.START_SAFE_BROWSING, + AndroidWebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, + AndroidWebViewFeature.VISUAL_STATE_CALLBACK, + AndroidWebViewFeature.WEB_MESSAGE_CALLBACK_ON_MESSAGE, + AndroidWebViewFeature.WEB_MESSAGE_LISTENER, + AndroidWebViewFeature.WEB_MESSAGE_PORT_CLOSE, + AndroidWebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, + AndroidWebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, + AndroidWebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, + AndroidWebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, + AndroidWebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, + AndroidWebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, + AndroidWebViewFeature.WEB_VIEW_RENDERER_TERMINATE, + ].toSet(); + + static AndroidWebViewFeature? fromValue(String? value) { + if (value != null) { + try { + return AndroidWebViewFeature.values.firstWhere( + (element) => element.toValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + String toValue() => _value; + + @override + String toString() => _value; + + /// + static const CREATE_WEB_MESSAGE_CHANNEL = const AndroidWebViewFeature._internal("CREATE_WEB_MESSAGE_CHANNEL"); + + /// + static const DISABLED_ACTION_MODE_MENU_ITEMS = const AndroidWebViewFeature._internal("DISABLED_ACTION_MODE_MENU_ITEMS"); + + /// + static const FORCE_DARK = const AndroidWebViewFeature._internal("FORCE_DARK"); + + /// + static const FORCE_DARK_STRATEGY = const AndroidWebViewFeature._internal("FORCE_DARK_STRATEGY"); + + /// + static const GET_WEB_CHROME_CLIENT = const AndroidWebViewFeature._internal("GET_WEB_CHROME_CLIENT"); + + /// + static const GET_WEB_VIEW_CLIENT = const AndroidWebViewFeature._internal("GET_WEB_VIEW_CLIENT"); + + /// + static const GET_WEB_VIEW_RENDERER = const AndroidWebViewFeature._internal("GET_WEB_VIEW_RENDERER"); + + /// + static const MULTI_PROCESS = const AndroidWebViewFeature._internal("MULTI_PROCESS"); + + /// + static const OFF_SCREEN_PRERASTER = const AndroidWebViewFeature._internal("OFF_SCREEN_PRERASTER"); + + /// + static const POST_WEB_MESSAGE = const AndroidWebViewFeature._internal("POST_WEB_MESSAGE"); + + /// + static const PROXY_OVERRIDE = const AndroidWebViewFeature._internal("PROXY_OVERRIDE"); + + /// + static const RECEIVE_HTTP_ERROR = const AndroidWebViewFeature._internal("RECEIVE_HTTP_ERROR"); + + /// + static const RECEIVE_WEB_RESOURCE_ERROR = const AndroidWebViewFeature._internal("RECEIVE_WEB_RESOURCE_ERROR"); + + /// + static const SAFE_BROWSING_ALLOWLIST = const AndroidWebViewFeature._internal("SAFE_BROWSING_ALLOWLIST"); + + /// + static const SAFE_BROWSING_ENABLE = const AndroidWebViewFeature._internal("SAFE_BROWSING_ENABLE"); + + /// + static const SAFE_BROWSING_HIT = const AndroidWebViewFeature._internal("SAFE_BROWSING_HIT"); + + /// + static const SAFE_BROWSING_PRIVACY_POLICY_URL = const AndroidWebViewFeature._internal("SAFE_BROWSING_PRIVACY_POLICY_URL"); + + /// + static const SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = const AndroidWebViewFeature._internal("SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY"); + + /// + static const SAFE_BROWSING_RESPONSE_PROCEED = const AndroidWebViewFeature._internal("SAFE_BROWSING_RESPONSE_PROCEED"); + + /// + static const SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = const AndroidWebViewFeature._internal("SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL"); + + /// + static const SAFE_BROWSING_WHITELIST = const AndroidWebViewFeature._internal("SAFE_BROWSING_WHITELIST"); + + /// + static const SERVICE_WORKER_BASIC_USAGE = const AndroidWebViewFeature._internal("SERVICE_WORKER_BASIC_USAGE"); + + /// + static const SERVICE_WORKER_BLOCK_NETWORK_LOADS = const AndroidWebViewFeature._internal("SERVICE_WORKER_BLOCK_NETWORK_LOADS"); + + /// + static const SERVICE_WORKER_CACHE_MODE = const AndroidWebViewFeature._internal("SERVICE_WORKER_CACHE_MODE"); + + /// + static const SERVICE_WORKER_CONTENT_ACCESS = const AndroidWebViewFeature._internal("SERVICE_WORKER_CONTENT_ACCESS"); + + /// + static const SERVICE_WORKER_FILE_ACCESS = const AndroidWebViewFeature._internal("SERVICE_WORKER_FILE_ACCESS"); + + /// + static const SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = const AndroidWebViewFeature._internal("SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST"); + + /// + static const SHOULD_OVERRIDE_WITH_REDIRECTS = const AndroidWebViewFeature._internal("SHOULD_OVERRIDE_WITH_REDIRECTS"); + + /// + static const START_SAFE_BROWSING = const AndroidWebViewFeature._internal("START_SAFE_BROWSING"); + + /// + static const TRACING_CONTROLLER_BASIC_USAGE = const AndroidWebViewFeature._internal("TRACING_CONTROLLER_BASIC_USAGE"); + + /// + static const VISUAL_STATE_CALLBACK = const AndroidWebViewFeature._internal("VISUAL_STATE_CALLBACK"); + + /// + static const WEB_MESSAGE_CALLBACK_ON_MESSAGE = const AndroidWebViewFeature._internal("WEB_MESSAGE_CALLBACK_ON_MESSAGE"); + + /// + static const WEB_MESSAGE_LISTENER = const AndroidWebViewFeature._internal("WEB_MESSAGE_LISTENER"); + + /// + static const WEB_MESSAGE_PORT_CLOSE = const AndroidWebViewFeature._internal("WEB_MESSAGE_PORT_CLOSE"); + + /// + static const WEB_MESSAGE_PORT_POST_MESSAGE = const AndroidWebViewFeature._internal("WEB_MESSAGE_PORT_POST_MESSAGE"); + + /// + static const WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = const AndroidWebViewFeature._internal("WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK"); + + /// + static const WEB_RESOURCE_ERROR_GET_CODE = const AndroidWebViewFeature._internal("WEB_RESOURCE_ERROR_GET_CODE"); + + /// + static const WEB_RESOURCE_ERROR_GET_DESCRIPTION = const AndroidWebViewFeature._internal("WEB_RESOURCE_ERROR_GET_DESCRIPTION"); + + /// + static const WEB_RESOURCE_REQUEST_IS_REDIRECT = const AndroidWebViewFeature._internal("WEB_RESOURCE_REQUEST_IS_REDIRECT"); + + /// + static const WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = const AndroidWebViewFeature._internal("WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE"); + + /// + static const WEB_VIEW_RENDERER_TERMINATE = const AndroidWebViewFeature._internal("WEB_VIEW_RENDERER_TERMINATE"); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; + + ///Return whether a feature is supported at run-time. On devices running Android version `Build.VERSION_CODES.LOLLIPOP` and higher, + ///this will check whether a feature is supported, depending on the combination of the desired feature, the Android version of device, + ///and the WebView APK on the device. If running on a device with a lower API level, this will always return `false`. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewFeature#isFeatureSupported(java.lang.String) + static Future isFeatureSupported(AndroidWebViewFeature feature) async { + Map args = {}; + args.putIfAbsent("feature", () => feature.toValue()); + return await _channel.invokeMethod('isFeatureSupported', args); + } +} \ No newline at end of file diff --git a/lib/src/in_app_webview_controller.dart b/lib/src/in_app_webview_controller.dart index 9441e618..9aefbe24 100644 --- a/lib/src/in_app_webview_controller.dart +++ b/lib/src/in_app_webview_controller.dart @@ -929,6 +929,7 @@ class InAppWebViewController { default: throw UnimplementedError("Unimplemented ${call.method} method"); } + return null; } ///Gets the URL for the current page. diff --git a/lib/src/types.dart b/lib/src/types.dart index 1555f30c..b966c252 100755 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -4601,7 +4601,7 @@ class UserScript { /// ///**NOTE for iOS 14.0+**: this class represents the native [WKContentWorld](https://developer.apple.com/documentation/webkit/wkcontentworld) class. /// -///**NOTE for Android**: it will create and append an `