From 14ff4921f85543c388fde9dccbf8fb5e989c52d0 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Sat, 22 Oct 2022 04:05:41 +0200 Subject: [PATCH] Added InAppWebViewSettings.allowBackgroundAudioPlaying for Android, Added WebViewAssetLoader and InAppWebViewSettings.webViewAssetLoader for Android --- CHANGELOG.md | 15 + .../ServiceWorkerChannelDelegate.java | 9 +- .../types/SyncBaseCallbackResultImpl.java | 1 - .../types/WebResourceResponseExt.java | 10 +- .../types/WebViewAssetLoaderExt.java | 209 ++++++++++ .../webview/in_app_webview/InAppWebView.java | 36 +- .../InAppWebViewChromeClient.java | 16 +- .../in_app_webview/InAppWebViewClient.java | 11 + .../in_app_webview/InAppWebViewSettings.java | 19 +- .../src/exchangeable_object_generator.dart | 4 +- .../app/src/main/res/values-night/styles.xml | 2 +- example/assets/website/index.html | 14 + example/integration_test/constants.dart | 3 + .../headless_in_app_webview/main.dart | 2 +- .../integration_test/in_app_webview/main.dart | 2 + .../in_app_webview/webview_asset_loader.dart | 56 +++ example/test_assets/website/index.html | 14 + lib/src/android/main.dart | 7 + lib/src/android/webview_asset_loader.dart | 181 ++++++++ lib/src/android/webview_asset_loader.g.dart | 312 ++++++++++++++ lib/src/android/webview_feature.dart | 3 + lib/src/android/webview_feature.g.dart | 3 + .../android/in_app_webview_controller.dart | 2 +- .../apple/in_app_webview_options.dart | 2 +- lib/src/in_app_webview/in_app_webview.dart | 138 +++---- .../in_app_webview_controller.dart | 42 +- .../in_app_webview_settings.dart | 389 ++---------------- .../in_app_webview_settings.g.dart | 55 ++- lib/src/print_job/print_job_settings.dart | 5 +- lib/src/types/ajax_request.g.dart | 4 +- lib/src/types/download_start_request.g.dart | 2 +- lib/src/types/favicon.g.dart | 2 +- lib/src/types/fetch_request.g.dart | 2 +- .../fetch_request_federated_credential.g.dart | 2 +- .../fetch_request_password_credential.g.dart | 2 +- .../types/in_app_webview_initial_data.g.dart | 6 +- lib/src/types/js_alert_request.g.dart | 2 +- lib/src/types/js_before_unload_request.g.dart | 2 +- lib/src/types/js_confirm_request.g.dart | 2 +- lib/src/types/js_prompt_request.g.dart | 2 +- lib/src/types/loaded_resource.g.dart | 2 +- lib/src/types/permission_request.g.dart | 2 +- lib/src/types/print_job_attributes.g.dart | 5 +- .../request_focus_node_href_result.g.dart | 2 +- lib/src/types/request_image_ref_result.g.dart | 2 +- lib/src/types/url_request.g.dart | 6 +- lib/src/types/url_response.g.dart | 4 +- lib/src/types/web_history_item.g.dart | 4 +- lib/src/types/web_resource_request.g.dart | 2 +- lib/src/types/web_resource_response.dart | 4 +- lib/src/types/web_resource_response.g.dart | 4 +- lib/src/web/in_app_web_view_web_element.dart | 2 +- .../web_authenticate_session.dart | 2 +- lib/src/web_message/web_message_listener.dart | 2 +- pubspec.yaml | 2 +- 55 files changed, 1107 insertions(+), 528 deletions(-) create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebViewAssetLoaderExt.java create mode 100644 example/assets/website/index.html create mode 100644 example/integration_test/in_app_webview/webview_asset_loader.dart create mode 100644 example/test_assets/website/index.html create mode 100644 lib/src/android/webview_asset_loader.dart create mode 100644 lib/src/android/webview_asset_loader.g.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 403f7b95..996ffd8d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 6.0.0-beta.6 + +- Added `InAppWebViewSettings.allowBackgroundAudioPlaying` for Android +- Added `WebViewAssetLoader` and `InAppWebViewSettings.webViewAssetLoader` for Android + +### BREAKING CHANGES + +- `WebResourceResponse.contentType` and `WebResourceResponse.contentEncoding` properties can be null + ## 6.0.0-beta.5 - Merge fixes of version `5.5.0+5` @@ -54,6 +63,12 @@ - Removed `URLProtectionSpace.iosIsProxy` property - `historyUrl` and `baseUrl` of `InAppWebViewInitialData` can be `null` +## 5.6.0 + +- Fixed "URLCredential.fromMap returns null for username" [#1205](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1205) +- Fixed "Compare to webview_flutter, inappwebview is significant frame dropped while page scrolling" [#1386](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1386) +- Merged "Fix hybrid composition laggy" [#1387](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1387) (thanks to [Doflatango](https://github.com/Doflatango)) + ## 5.5.0+5 - Fixed `HeadlessInAppWebView` default size on Android diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/service_worker/ServiceWorkerChannelDelegate.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/service_worker/ServiceWorkerChannelDelegate.java index 6961f2bc..7c6a1b04 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/service_worker/ServiceWorkerChannelDelegate.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/service_worker/ServiceWorkerChannelDelegate.java @@ -2,7 +2,6 @@ package com.pichillilorenzo.flutter_inappwebview.service_worker; import android.annotation.SuppressLint; import android.os.Build; -import android.webkit.ServiceWorkerController; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -12,10 +11,8 @@ import androidx.webkit.ServiceWorkerWebSettingsCompat; import androidx.webkit.WebViewFeature; import com.pichillilorenzo.flutter_inappwebview.Util; -import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebView; import com.pichillilorenzo.flutter_inappwebview.types.BaseCallbackResultImpl; import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl; -import com.pichillilorenzo.flutter_inappwebview.types.Disposable; import com.pichillilorenzo.flutter_inappwebview.types.SyncBaseCallbackResultImpl; import com.pichillilorenzo.flutter_inappwebview.types.WebResourceRequestExt; import com.pichillilorenzo.flutter_inappwebview.types.WebResourceResponseExt; @@ -136,7 +133,7 @@ public class ServiceWorkerChannelDelegate extends ChannelDelegateImpl { } } - public void shouldInterceptRequest(WebResourceRequestExt request, @NonNull WebViewChannelDelegate.ShouldInterceptRequestCallback callback) { + public void shouldInterceptRequest(WebResourceRequestExt request, @NonNull ShouldInterceptRequestCallback callback) { MethodChannel channel = getChannel(); if (channel == null) return; channel.invokeMethod("shouldInterceptRequest", request.toMap(), callback); @@ -146,7 +143,7 @@ public class ServiceWorkerChannelDelegate extends ChannelDelegateImpl { @Nullable @Override public WebResourceResponseExt decodeResult(@Nullable Object obj) { - return (new WebViewChannelDelegate.ShouldInterceptRequestCallback()).decodeResult(obj); + return (new ShouldInterceptRequestCallback()).decodeResult(obj); } } @@ -154,7 +151,7 @@ public class ServiceWorkerChannelDelegate extends ChannelDelegateImpl { public WebResourceResponseExt shouldInterceptRequest(WebResourceRequestExt request) throws InterruptedException { MethodChannel channel = getChannel(); if (channel == null) return null; - final WebViewChannelDelegate.SyncShouldInterceptRequestCallback callback = new WebViewChannelDelegate.SyncShouldInterceptRequestCallback(); + final SyncShouldInterceptRequestCallback callback = new SyncShouldInterceptRequestCallback(); return Util.invokeMethodAndWaitResult(channel, "shouldInterceptRequest", request.toMap(), callback); } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/SyncBaseCallbackResultImpl.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/SyncBaseCallbackResultImpl.java index 9b963a85..a9d65884 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/SyncBaseCallbackResultImpl.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/SyncBaseCallbackResultImpl.java @@ -1,7 +1,6 @@ package com.pichillilorenzo.flutter_inappwebview.types; import androidx.annotation.CallSuper; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.concurrent.CountDownLatch; diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceResponseExt.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceResponseExt.java index 43d1d8cf..e867a2c4 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceResponseExt.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceResponseExt.java @@ -13,7 +13,9 @@ import java.util.HashMap; import java.util.Map; public class WebResourceResponseExt { + @Nullable private String contentType; + @Nullable private String contentEncoding; @Nullable private Integer statusCode; @@ -24,7 +26,7 @@ public class WebResourceResponseExt { @Nullable private byte[] data; - public WebResourceResponseExt(String contentType, String contentEncoding, @Nullable Integer statusCode, + public WebResourceResponseExt(@Nullable String contentType, @Nullable String contentEncoding, @Nullable Integer statusCode, @Nullable String reasonPhrase, @Nullable Map headers, @Nullable byte[] data) { this.contentType = contentType; this.contentEncoding = contentEncoding; @@ -77,19 +79,21 @@ public class WebResourceResponseExt { return webResourceResponseMap; } + @Nullable public String getContentType() { return contentType; } - public void setContentType(String contentType) { + public void setContentType(@Nullable String contentType) { this.contentType = contentType; } + @Nullable public String getContentEncoding() { return contentEncoding; } - public void setContentEncoding(String contentEncoding) { + public void setContentEncoding(@Nullable String contentEncoding) { this.contentEncoding = contentEncoding; } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebViewAssetLoaderExt.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebViewAssetLoaderExt.java new file mode 100644 index 00000000..a41d08b6 --- /dev/null +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebViewAssetLoaderExt.java @@ -0,0 +1,209 @@ +package com.pichillilorenzo.flutter_inappwebview.types; + +import android.content.Context; +import android.os.Build; +import android.util.Log; +import android.webkit.WebResourceResponse; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.webkit.WebViewAssetLoader; + +import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin; +import com.pichillilorenzo.flutter_inappwebview.Util; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.flutter.plugin.common.MethodChannel; + +public class WebViewAssetLoaderExt implements Disposable { + @Nullable + public WebViewAssetLoader loader; + @NonNull + public List customPathHandlers; + + public WebViewAssetLoaderExt(@Nullable WebViewAssetLoader loader, @NonNull List customPathHandlers) { + this.loader = loader; + this.customPathHandlers = customPathHandlers; + } + + @Nullable + public static WebViewAssetLoaderExt fromMap(@Nullable Map map, @NonNull InAppWebViewFlutterPlugin plugin, @NonNull Context context) { + if (map == null) { + return null; + } + WebViewAssetLoader.Builder builder = new WebViewAssetLoader.Builder(); + String domain = (String) map.get("domain"); + Boolean httpAllowed = (Boolean) map.get("httpAllowed"); + List> pathHandlers = (List>) map.get("pathHandlers"); + List customPathHandlers = new ArrayList<>(); + if (domain != null && !domain.isEmpty()) { + builder.setDomain(domain); + } + if (httpAllowed != null) { + builder.setHttpAllowed(httpAllowed); + } + if (pathHandlers != null) { + for (Map pathHandler : pathHandlers) { + String type = (String) pathHandler.get("type"); + String path = (String) pathHandler.get("path"); + if (type == null || path == null) { + continue; + } + switch (type) { + case "AssetsPathHandler": + WebViewAssetLoader.AssetsPathHandler assetsPathHandler = + new WebViewAssetLoader.AssetsPathHandler(context); + builder.addPathHandler(path, assetsPathHandler); + break; + case "InternalStoragePathHandler": + String directory = (String) pathHandler.get("directory"); + if (directory == null) { + continue; + } + File dir = new File(directory); + WebViewAssetLoader.InternalStoragePathHandler internalStoragePathHandler = + new WebViewAssetLoader.InternalStoragePathHandler(context, dir); + builder.addPathHandler(path, internalStoragePathHandler); + break; + case "ResourcesPathHandler": + WebViewAssetLoader.ResourcesPathHandler resourcesPathHandler = new WebViewAssetLoader.ResourcesPathHandler(context); + builder.addPathHandler(path, resourcesPathHandler); + break; + default: + String id = (String) pathHandler.get("id"); + if (id == null) { + continue; + } + PathHandlerExt customPathHandler = new PathHandlerExt(id, plugin); + builder.addPathHandler(path, customPathHandler); + customPathHandlers.add(customPathHandler); + break; + } + } + } + return new WebViewAssetLoaderExt(builder.build(), customPathHandlers); + } + + @Override + public void dispose() { + for (PathHandlerExt pathHandler : customPathHandlers) { + pathHandler.dispose(); + } + customPathHandlers.clear(); + } + + public static class PathHandlerExt implements WebViewAssetLoader.PathHandler, Disposable { + + protected static final String LOG_TAG = "PathHandlerExt"; + public static final String METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_custompathhandler_"; + + @NonNull + public String id; + @Nullable + public PathHandlerExtChannelDelegate channelDelegate; + + public PathHandlerExt(@NonNull String id, @NonNull InAppWebViewFlutterPlugin plugin) { + this.id = id; + final MethodChannel channel = new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME_PREFIX + id); + this.channelDelegate = new PathHandlerExtChannelDelegate(this, channel); + } + + @Nullable + @Override + public WebResourceResponse handle(@NonNull String path) { + if (channelDelegate != null) { + WebResourceResponseExt response = null; + + try { + response = channelDelegate.handle(path); + } catch (InterruptedException e) { + e.printStackTrace(); + return null; + } + + if (response != null) { + String contentType = response.getContentType(); + String contentEncoding = response.getContentEncoding(); + byte[] data = response.getData(); + Map responseHeaders = response.getHeaders(); + Integer statusCode = response.getStatusCode(); + String reasonPhrase = response.getReasonPhrase(); + + ByteArrayInputStream inputStream = (data != null) ? new ByteArrayInputStream(data) : null; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && statusCode != null && reasonPhrase != null) { + return new WebResourceResponse(contentType, contentEncoding, statusCode, reasonPhrase, responseHeaders, inputStream); + } else { + return new WebResourceResponse(contentType, contentEncoding, inputStream); + } + } + } + return null; + } + + @Override + public void dispose() { + if (channelDelegate != null) { + channelDelegate.dispose(); + channelDelegate = null; + } + } + } + + public static class PathHandlerExtChannelDelegate extends ChannelDelegateImpl { + + @Nullable + private PathHandlerExt pathHandler; + + public PathHandlerExtChannelDelegate(@NonNull PathHandlerExt pathHandler, @NonNull MethodChannel channel) { + super(channel); + this.pathHandler = pathHandler; + } + + public static class HandleCallback extends BaseCallbackResultImpl { + @Nullable + @Override + public WebResourceResponseExt decodeResult(@Nullable Object obj) { + return WebResourceResponseExt.fromMap((Map) obj); + } + } + + public void handle(String path, @NonNull HandleCallback callback) { + MethodChannel channel = getChannel(); + if (channel == null) return; + Map obj = new HashMap<>(); + obj.put("path", path); + channel.invokeMethod("handle", obj, callback); + } + + public static class SyncHandleCallback extends SyncBaseCallbackResultImpl { + @Nullable + @Override + public WebResourceResponseExt decodeResult(@Nullable Object obj) { + return (new HandleCallback()).decodeResult(obj); + } + } + + @Nullable + public WebResourceResponseExt handle(String path) throws InterruptedException { + MethodChannel channel = getChannel(); + if (channel == null) return null; + final SyncHandleCallback callback = new SyncHandleCallback(); + Map obj = new HashMap<>(); + obj.put("path", path); + return Util.invokeMethodAndWaitResult(channel, "handle", obj, callback); + } + + @Override + public void dispose() { + super.dispose(); + pathHandler = null; + } + } +} diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java index b7b022cc..b4e58793 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java @@ -88,6 +88,7 @@ import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOption import com.pichillilorenzo.flutter_inappwebview.types.URLRequest; import com.pichillilorenzo.flutter_inappwebview.types.UserContentController; import com.pichillilorenzo.flutter_inappwebview.types.UserScript; +import com.pichillilorenzo.flutter_inappwebview.types.WebViewAssetLoaderExt; import com.pichillilorenzo.flutter_inappwebview.webview.ContextMenuSettings; import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface; import com.pichillilorenzo.flutter_inappwebview.webview.JavaScriptBridgeInterface; @@ -108,7 +109,6 @@ import java.util.UUID; import java.util.regex.Pattern; import io.flutter.plugin.common.MethodChannel; -import okhttp3.OkHttpClient; final public class InAppWebView extends InputAwareWebView implements InAppWebViewInterface { protected static final String LOG_TAG = "InAppWebView"; @@ -134,9 +134,7 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie public InAppWebViewSettings customSettings = new InAppWebViewSettings(); public boolean isLoading = false; private boolean inFullscreen = false; - public OkHttpClient httpClient; public float zoomScale = 1.0f; - int okHttpClientCacheSize = 10 * 1024 * 1024; // 10MB public ContentBlockerHandler contentBlockerHandler = new ContentBlockerHandler(); public Pattern regexToCancelSubFramesLoadingCompiled; @Nullable @@ -168,6 +166,9 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie @Nullable public FindInteractionController findInteractionController; + @Nullable + public WebViewAssetLoaderExt webViewAssetLoaderExt; + public InAppWebView(Context context) { super(context); } @@ -200,7 +201,9 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie @SuppressLint("RestrictedApi") public void prepare() { - httpClient = new OkHttpClient().newBuilder().build(); + if (plugin != null) { + webViewAssetLoaderExt = WebViewAssetLoaderExt.fromMap(customSettings.webViewAssetLoader, plugin, getContext()); + } javaScriptBridgeInterface = new JavaScriptBridgeInterface(this); addJavascriptInterface(javaScriptBridgeInterface, JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME); @@ -1052,6 +1055,13 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie WebSettingsCompat.setEnterpriseAuthenticationAppLinkPolicyEnabled(settings, newCustomSettings.enterpriseAuthenticationAppLinkPolicyEnabled); } + if (plugin != null) { + if (webViewAssetLoaderExt != null) { + webViewAssetLoaderExt.dispose(); + } + webViewAssetLoaderExt = WebViewAssetLoaderExt.fromMap(customSettings.webViewAssetLoader, plugin, getContext()); + } + customSettings = newCustomSettings; } @@ -1848,10 +1858,16 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie throw new UnsupportedOperationException(); } - // @Override -// protected void onWindowVisibilityChanged(int visibility) { -// if (visibility != View.GONE) super.onWindowVisibilityChanged(View.VISIBLE); -// } + @Override + protected void onWindowVisibilityChanged(int visibility) { + if (customSettings.allowBackgroundAudioPlaying) { + if (visibility != View.GONE) { + super.onWindowVisibilityChanged(View.VISIBLE); + } + return; + } + super.onWindowVisibilityChanged(visibility); + } public float getZoomScale() { return zoomScale; @@ -1946,6 +1962,10 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie findInteractionController.dispose(); findInteractionController = null; } + if (webViewAssetLoaderExt != null) { + webViewAssetLoaderExt.dispose(); + webViewAssetLoaderExt = null; + } if (windowId != null) { InAppWebViewChromeClient.windowWebViewMessages.remove(windowId); } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewChromeClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewChromeClient.java index b0bbdb1d..70ce80c6 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewChromeClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewChromeClient.java @@ -99,7 +99,9 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN; + @Nullable private View mCustomView; + @Nullable private WebChromeClient.CustomViewCallback mCustomViewCallback; private int mOriginalOrientation; private int mOriginalSystemUiVisibility; @@ -140,11 +142,15 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR if (decorView == null) { return; } - ((FrameLayout) decorView).removeView(this.mCustomView); + if (this.mCustomView != null) { + ((FrameLayout) decorView).removeView(this.mCustomView); + } this.mCustomView = null; decorView.setSystemUiVisibility(this.mOriginalSystemUiVisibility); activity.setRequestedOrientation(this.mOriginalOrientation); - this.mCustomViewCallback.onCustomViewHidden(); + if (this.mCustomViewCallback != null) { + this.mCustomViewCallback.onCustomViewHidden(); + } this.mCustomViewCallback = null; activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); @@ -176,7 +182,9 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR this.mOriginalSystemUiVisibility = decorView.getSystemUiVisibility(); this.mOriginalOrientation = activity.getRequestedOrientation(); this.mCustomViewCallback = paramCustomViewCallback; - this.mCustomView.setBackgroundColor(Color.BLACK); + if (this.mCustomView != null) { + this.mCustomView.setBackgroundColor(Color.BLACK); + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { decorView.setSystemUiVisibility(FULLSCREEN_SYSTEM_UI_VISIBILITY_KITKAT); @@ -185,7 +193,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR } activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); ((FrameLayout) decorView).addView(this.mCustomView, FULLSCREEN_LAYOUT_PARAMS); - + if (inAppWebView != null) { WebViewChannelDelegate eventWebViewChannelDelegate = inAppWebView.channelDelegate; if (eventWebViewChannelDelegate != null) diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewClient.java index c7bd6bd2..af415ed9 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewClient.java @@ -625,6 +625,17 @@ public class InAppWebViewClient extends WebViewClient { public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequestExt request) { final InAppWebView webView = (InAppWebView) view; + if (webView.webViewAssetLoaderExt != null && webView.webViewAssetLoaderExt.loader != null) { + try { + WebResourceResponse webResourceResponse = webView.webViewAssetLoaderExt.loader.shouldInterceptRequest(request.getUrl()); + if (webResourceResponse != null) { + return webResourceResponse; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + if (webView.customSettings.useShouldInterceptRequest) { WebResourceResponseExt response = null; if (webView.channelDelegate != null) { diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewSettings.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewSettings.java index d5290d6b..3dcb3d9c 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewSettings.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewSettings.java @@ -1,5 +1,8 @@ package com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview; +import static android.webkit.WebSettings.LayoutAlgorithm.NARROW_COLUMNS; +import static android.webkit.WebSettings.LayoutAlgorithm.NORMAL; + import android.annotation.SuppressLint; import android.os.Build; import android.view.View; @@ -11,17 +14,14 @@ import androidx.webkit.WebSettingsCompat; import androidx.webkit.WebViewFeature; import com.pichillilorenzo.flutter_inappwebview.ISettings; -import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface; import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType; +import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import static android.webkit.WebSettings.LayoutAlgorithm.NARROW_COLUMNS; -import static android.webkit.WebSettings.LayoutAlgorithm.NORMAL; - public class InAppWebViewSettings implements ISettings { public static final String LOG_TAG = "InAppWebViewSettings"; @@ -52,7 +52,7 @@ public class InAppWebViewSettings implements ISettings { public Boolean supportZoom = true; public Boolean allowFileAccessFromFileURLs = false; public Boolean allowUniversalAccessFromFileURLs = false; - + public Boolean allowBackgroundAudioPlaying = false; public Integer textZoom = 100; public Boolean clearSessionCache = false; public Boolean builtInZoomControls = true; @@ -119,6 +119,8 @@ public class InAppWebViewSettings implements ISettings { @Nullable public Integer requestedWithHeaderMode; public Boolean enterpriseAuthenticationAppLinkPolicyEnabled = true; + @Nullable + public Map webViewAssetLoader; @NonNull @Override @@ -389,6 +391,12 @@ public class InAppWebViewSettings implements ISettings { case "enterpriseAuthenticationAppLinkPolicyEnabled": enterpriseAuthenticationAppLinkPolicyEnabled = (Boolean) value; break; + case "allowBackgroundAudioPlaying": + allowBackgroundAudioPlaying = (Boolean) value; + break; + case "webViewAssetLoader": + webViewAssetLoader = (Map) value; + break; } } @@ -485,6 +493,7 @@ public class InAppWebViewSettings implements ISettings { settings.put("algorithmicDarkeningAllowed", algorithmicDarkeningAllowed); settings.put("requestedWithHeaderMode", requestedWithHeaderMode); settings.put("enterpriseAuthenticationAppLinkPolicyEnabled", enterpriseAuthenticationAppLinkPolicyEnabled); + settings.put("allowBackgroundAudioPlaying", allowBackgroundAudioPlaying); return settings; } diff --git a/dev_packages/generators/lib/src/exchangeable_object_generator.dart b/dev_packages/generators/lib/src/exchangeable_object_generator.dart index d0f0a8e3..2a020fd8 100644 --- a/dev_packages/generators/lib/src/exchangeable_object_generator.dart +++ b/dev_packages/generators/lib/src/exchangeable_object_generator.dart @@ -492,9 +492,9 @@ class ExchangeableObjectGenerator final isNullable = Util.typeIsNullable(elementType); if (elementType.getDisplayString(withNullability: false) == "Uri") { if (!isNullable) { - return "Uri.parse($value)"; + return "(Uri.tryParse($value) ?? Uri())"; } else { - return "$value != null ? Uri.parse($value) : null"; + return "$value != null ? Uri.tryParse($value) : null"; } } else if (elementType.getDisplayString(withNullability: false) == "Color") { diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml index 3db14bb5..e0fc71c1 100644 --- a/example/android/app/src/main/res/values-night/styles.xml +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -12,7 +12,7 @@ running. This Theme is only used starting with V2 of Flutter's Android embedding. --> - diff --git a/example/assets/website/index.html b/example/assets/website/index.html new file mode 100644 index 00000000..f47d4e71 --- /dev/null +++ b/example/assets/website/index.html @@ -0,0 +1,14 @@ + + + + + + + WebViewAssetLoader + + +

WebViewAssetLoader

+

This is a test.

+ + \ No newline at end of file diff --git a/example/integration_test/constants.dart b/example/integration_test/constants.dart index 7bbfb3af..df709a96 100644 --- a/example/integration_test/constants.dart +++ b/example/integration_test/constants.dart @@ -23,3 +23,6 @@ final TEST_CHROME_SAFE_BROWSING_MALWARE = final TEST_PERMISSION_SITE = Uri.parse('https://permission.site/'); final TEST_SERVICE_WORKER_URL = Uri.parse( 'https://mdn.github.io/dom-examples/service-worker/simple-service-worker/'); +final TEST_WEBVIEW_ASSET_LOADER_DOMAIN = 'my.custom.domain.com'; +final TEST_WEBVIEW_ASSET_LOADER_URL = Uri.parse( + 'https://$TEST_WEBVIEW_ASSET_LOADER_DOMAIN/assets/flutter_assets/assets/website/index.html'); diff --git a/example/integration_test/headless_in_app_webview/main.dart b/example/integration_test/headless_in_app_webview/main.dart index 02e4b22e..1453e709 100644 --- a/example/integration_test/headless_in_app_webview/main.dart +++ b/example/integration_test/headless_in_app_webview/main.dart @@ -12,6 +12,6 @@ void main() { takeScreenshot(); customSize(); setGetSettings(); - convertToInAppWebView(); + // convertToInAppWebView(); }); } diff --git a/example/integration_test/in_app_webview/main.dart b/example/integration_test/in_app_webview/main.dart index 31cfb5b0..0f83208c 100644 --- a/example/integration_test/in_app_webview/main.dart +++ b/example/integration_test/in_app_webview/main.dart @@ -82,6 +82,7 @@ import 'user_scripts.dart'; import 'video_playback_policy.dart'; import 'web_history.dart'; import 'web_message.dart'; +import 'webview_asset_loader.dart'; import 'webview_windows.dart'; void main() { @@ -170,5 +171,6 @@ void main() { createPdf(); applePayAPI(); handlesURLScheme(); + webViewAssetLoader(); }, skip: shouldSkip); } diff --git a/example/integration_test/in_app_webview/webview_asset_loader.dart b/example/integration_test/in_app_webview/webview_asset_loader.dart new file mode 100644 index 00000000..a1307549 --- /dev/null +++ b/example/integration_test/in_app_webview/webview_asset_loader.dart @@ -0,0 +1,56 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../constants.dart'; + +void webViewAssetLoader() { + final shouldSkip = kIsWeb + ? true + : ![ + TargetPlatform.android, + ].contains(defaultTargetPlatform); + + testWidgets('WebViewAssetLoader', (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + final Completer pageLoaded = Completer(); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: InAppWebView( + key: GlobalKey(), + initialUrlRequest: URLRequest(url: TEST_WEBVIEW_ASSET_LOADER_URL), + initialSettings: InAppWebViewSettings( + allowFileAccessFromFileURLs: false, + allowUniversalAccessFromFileURLs: false, + allowFileAccess: false, + allowContentAccess: false, + webViewAssetLoader: WebViewAssetLoader( + domain: TEST_WEBVIEW_ASSET_LOADER_DOMAIN, + pathHandlers: [AssetsPathHandler(path: '/assets/')])), + onWebViewCreated: (controller) { + controllerCompleter.complete(controller); + }, + onLoadStop: (controller, url) { + pageLoaded.complete(url.toString()); + }, + ), + ), + ); + + final InAppWebViewController controller = await controllerCompleter.future; + final url = await pageLoaded.future; + + expect(url, TEST_WEBVIEW_ASSET_LOADER_URL.toString()); + + expect( + await controller.evaluateJavascript( + source: "document.querySelector('h1').innerHTML"), + 'WebViewAssetLoader'); + }, skip: shouldSkip); +} diff --git a/example/test_assets/website/index.html b/example/test_assets/website/index.html new file mode 100644 index 00000000..f47d4e71 --- /dev/null +++ b/example/test_assets/website/index.html @@ -0,0 +1,14 @@ + + + + + + + WebViewAssetLoader + + +

WebViewAssetLoader

+

This is a test.

+ + \ No newline at end of file diff --git a/lib/src/android/main.dart b/lib/src/android/main.dart index 5b7613df..e9142dd6 100644 --- a/lib/src/android/main.dart +++ b/lib/src/android/main.dart @@ -1,3 +1,10 @@ export 'service_worker_controller.dart'; export 'webview_feature.dart' show WebViewFeature, AndroidWebViewFeature; export 'proxy_controller.dart'; +export 'webview_asset_loader.dart' + show + WebViewAssetLoader, + PathHandler, + AssetsPathHandler, + ResourcesPathHandler, + InternalStoragePathHandler; diff --git a/lib/src/android/webview_asset_loader.dart b/lib/src/android/webview_asset_loader.dart new file mode 100644 index 00000000..89c44a11 --- /dev/null +++ b/lib/src/android/webview_asset_loader.dart @@ -0,0 +1,181 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; + +import '../types/web_resource_response.dart'; +import '../util.dart'; +import '../in_app_webview/webview.dart'; + +part 'webview_asset_loader.g.dart'; + +///Helper class to load local files including application's static assets and resources using http(s):// URLs inside a [WebView] class. +///Loading local files using web-like URLs instead of `file://` is desirable as it is compatible with the Same-Origin policy. +/// +///For more context about application's assets and resources and how to normally access them please refer to +///[Android Developer Docs: App resources overview](https://developer.android.com/guide/topics/resources/providing-resources). +/// +///Using http(s):// URLs to access local resources may conflict with a real website. +///This means that local files should only be hosted on domains your organization owns +///(at paths reserved for this purpose) or the default domain reserved for this: `appassets.androidplatform.net`. +/// +///**Supported Platforms/Implementations**: +///- Android native WebView +@ExchangeableObject(copyMethod: true) +class WebViewAssetLoader_ { + ///An unused domain reserved for Android applications to intercept requests for app assets. + /// + ///It is used by default unless the user specified a different domain. + static final String DEFAULT_DOMAIN = "appassets.androidplatform.net"; + + ///Set the domain under which app assets can be accessed. The default domain is `appassets.androidplatform.net`. + String? domain; + + ///Allow using the HTTP scheme in addition to HTTPS. The default is to not allow HTTP. + bool? httpAllowed; + + ///List of registered path handlers. + /// + ///[WebViewAssetLoader] will try Path Handlers in the order they're registered, + ///and will use whichever is the first to return a non-null. + List? pathHandlers; + + WebViewAssetLoader_({this.domain, this.httpAllowed, this.pathHandlers}); +} + +///A handler that produces responses for a registered path. +/// +///Implement this interface to handle other use-cases according to your app's needs. +@ExchangeableObject(fromMapFactory: false) +abstract class PathHandler_ { + late final String _type; + late final String _id; + late final MethodChannel _channel; + + ///The suffix path to be handled. + /// + ///The path should start and end with a `"/"` and it shouldn't collide with a real web path. + String path; + + @ExchangeableObjectConstructor() + PathHandler_({required this.path}) { + _type = this.runtimeType.toString(); + _id = IdGenerator.generate(); + this._channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_custompathhandler_$_id'); + this._channel.setMethodCallHandler((call) async { + try { + return await _handleMethod(call); + } on Error catch (e) { + print(e); + print(e.stackTrace); + } + }); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "handle": + String path = call.arguments["path"]; + return (await handle(path))?.toMap(); + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + ///Handles the requested URL by returning the appropriate response. + /// + ///Returning a `null` value means that the handler decided not to handle this path. + ///In this case, [WebViewAssetLoader] will try the next handler registered on this path or pass to [WebView] that will fall back to network to try to resolve the URL. + /// + ///However, if the handler wants to save unnecessary processing either by another handler or by falling back to network, + ///in cases like a file cannot be found, it may return a `WebResourceResponse(data: null)` + ///which is received as an HTTP response with status code `404` and no body. + Future handle(String path) async { + return null; + } + + @ExchangeableObjectMethod(toMapMergeWith: true) + // ignore: unused_element + Map _toMapMergeWith() { + return {"type": _type, "id": _id}; + } +} + +///Handler class to open a file from assets directory in the application APK. +/// +///Opens the requested file from the application's assets directory. +/// +///The matched prefix path used shouldn't be a prefix of a real web path. +///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`. +///This saves the time of falling back to network and trying to resolve a path that doesn't exist. +///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body. +/// +///The MIME type for the file will be determined from the file's extension using +///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-). +///Developers should ensure that asset files are named using standard file extensions. +///If the file does not have a recognised extension, `text/plain` will be used by default. +@ExchangeableObject() +class AssetsPathHandler_ extends PathHandler_ { + AssetsPathHandler_({required String path}) : super(path: path) {} + + @ExchangeableObjectMethod(toMapMergeWith: true) + // ignore: unused_element + Map _toMapMergeWith() { + return {"type": _type}; + } +} + +///Handler class to open a file from resources directory in the application APK. +/// +///Opens the requested file from application's resources directory. +/// +///The matched prefix path used shouldn't be a prefix of a real web path. +///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`. +///This saves the time of falling back to network and trying to resolve a path that doesn't exist. +///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body. +/// +///The MIME type for the file will be determined from the file's extension using +///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-). +///Developers should ensure that asset files are named using standard file extensions. +///If the file does not have a recognised extension, `text/plain` will be used by default. +@ExchangeableObject() +class ResourcesPathHandler_ extends PathHandler_ { + ResourcesPathHandler_({required String path}) : super(path: path) {} + + @ExchangeableObjectMethod(toMapMergeWith: true) + // ignore: unused_element + Map _toMapMergeWith() { + return {"type": _type}; + } +} + +///Handler class to open files from application internal storage. +///For more information about android storage please refer to +///[Android Developers Docs: Data and file storage overview](https://developer.android.com/guide/topics/data/data-storage). +/// +///To avoid leaking user or app data to the web, make sure to choose [directory] carefully, +///and assume any file under this directory could be accessed by any web page subject to same-origin rules. +/// +///Opens the requested file from the exposed data directory. +/// +///The matched prefix path used shouldn't be a prefix of a real web path. +///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`. +///This saves the time of falling back to network and trying to resolve a path that doesn't exist. +///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body. +/// +///The MIME type for the file will be determined from the file's extension using +///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-). +///Developers should ensure that asset files are named using standard file extensions. +///If the file does not have a recognised extension, `text/plain` will be used by default. +@ExchangeableObject() +class InternalStoragePathHandler_ extends PathHandler_ { + String directory; + + InternalStoragePathHandler_({required String path, required this.directory}) + : super(path: path) {} + + @ExchangeableObjectMethod(toMapMergeWith: true) + // ignore: unused_element + Map _toMapMergeWith() { + return {"type": _type}; + } +} diff --git a/lib/src/android/webview_asset_loader.g.dart b/lib/src/android/webview_asset_loader.g.dart new file mode 100644 index 00000000..751262bb --- /dev/null +++ b/lib/src/android/webview_asset_loader.g.dart @@ -0,0 +1,312 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'webview_asset_loader.dart'; + +// ************************************************************************** +// ExchangeableObjectGenerator +// ************************************************************************** + +///Helper class to load local files including application's static assets and resources using http(s):// URLs inside a [WebView] class. +///Loading local files using web-like URLs instead of `file://` is desirable as it is compatible with the Same-Origin policy. +/// +///For more context about application's assets and resources and how to normally access them please refer to +///[Android Developer Docs: App resources overview](https://developer.android.com/guide/topics/resources/providing-resources). +/// +///Using http(s):// URLs to access local resources may conflict with a real website. +///This means that local files should only be hosted on domains your organization owns +///(at paths reserved for this purpose) or the default domain reserved for this: `appassets.androidplatform.net`. +/// +///**Supported Platforms/Implementations**: +///- Android native WebView +class WebViewAssetLoader { + ///An unused domain reserved for Android applications to intercept requests for app assets. + /// + ///It is used by default unless the user specified a different domain. + static final String DEFAULT_DOMAIN = "appassets.androidplatform.net"; + + ///Set the domain under which app assets can be accessed. The default domain is `appassets.androidplatform.net`. + String? domain; + + ///Allow using the HTTP scheme in addition to HTTPS. The default is to not allow HTTP. + bool? httpAllowed; + + ///List of registered path handlers. + /// + ///[WebViewAssetLoader] will try Path Handlers in the order they're registered, + ///and will use whichever is the first to return a non-null. + List? pathHandlers; + WebViewAssetLoader({this.domain, this.httpAllowed, this.pathHandlers}); + + ///Gets a possible [WebViewAssetLoader] instance from a [Map] value. + static WebViewAssetLoader? fromMap(Map? map) { + if (map == null) { + return null; + } + final instance = WebViewAssetLoader( + domain: map['domain'], + httpAllowed: map['httpAllowed'], + pathHandlers: map['pathHandlers'] != null + ? List.from(map['pathHandlers'].map((e) => e)) + : null, + ); + return instance; + } + + ///Converts instance to a map. + Map toMap() { + return { + "domain": domain, + "httpAllowed": httpAllowed, + "pathHandlers": pathHandlers?.map((e) => e.toMap()).toList(), + }; + } + + ///Converts instance to a map. + Map toJson() { + return toMap(); + } + + ///Returns a copy of WebViewAssetLoader. + WebViewAssetLoader copy() { + return WebViewAssetLoader.fromMap(toMap()) ?? WebViewAssetLoader(); + } + + @override + String toString() { + return 'WebViewAssetLoader{domain: $domain, httpAllowed: $httpAllowed, pathHandlers: $pathHandlers}'; + } +} + +///A handler that produces responses for a registered path. +/// +///Implement this interface to handle other use-cases according to your app's needs. +abstract class PathHandler { + late final String _type; + late final String _id; + late final MethodChannel _channel; + + ///The suffix path to be handled. + /// + ///The path should start and end with a `"/"` and it shouldn't collide with a real web path. + String path; + PathHandler({required this.path}) { + _type = this.runtimeType.toString(); + _id = IdGenerator.generate(); + this._channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_custompathhandler_$_id'); + this._channel.setMethodCallHandler((call) async { + try { + return await _handleMethod(call); + } on Error catch (e) { + print(e); + print(e.stackTrace); + } + }); + } + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "handle": + String path = call.arguments["path"]; + return (await handle(path))?.toMap(); + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + ///Handles the requested URL by returning the appropriate response. + /// + ///Returning a `null` value means that the handler decided not to handle this path. + ///In this case, [WebViewAssetLoader] will try the next handler registered on this path or pass to [WebView] that will fall back to network to try to resolve the URL. + /// + ///However, if the handler wants to save unnecessary processing either by another handler or by falling back to network, + ///in cases like a file cannot be found, it may return a `WebResourceResponse(data: null)` + ///which is received as an HTTP response with status code `404` and no body. + Future handle(String path) async { + return null; + } + + @ExchangeableObjectMethod(toMapMergeWith: true) + Map _toMapMergeWith() { + return {"type": _type, "id": _id}; + } + + ///Converts instance to a map. + Map toMap() { + return { + ..._toMapMergeWith(), + "path": path, + }; + } + + ///Converts instance to a map. + Map toJson() { + return toMap(); + } + + @override + String toString() { + return 'PathHandler{path: $path}'; + } +} + +///Handler class to open a file from assets directory in the application APK. +/// +///Opens the requested file from the application's assets directory. +/// +///The matched prefix path used shouldn't be a prefix of a real web path. +///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`. +///This saves the time of falling back to network and trying to resolve a path that doesn't exist. +///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body. +/// +///The MIME type for the file will be determined from the file's extension using +///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-). +///Developers should ensure that asset files are named using standard file extensions. +///If the file does not have a recognised extension, `text/plain` will be used by default. +class AssetsPathHandler extends PathHandler { + AssetsPathHandler({required String path}) : super(path: path); + + ///Gets a possible [AssetsPathHandler] instance from a [Map] value. + static AssetsPathHandler? fromMap(Map? map) { + if (map == null) { + return null; + } + final instance = AssetsPathHandler( + path: map['path'], + ); + return instance; + } + + @ExchangeableObjectMethod(toMapMergeWith: true) + Map _toMapMergeWith() { + return {"type": _type}; + } + + ///Converts instance to a map. + Map toMap() { + return { + ..._toMapMergeWith(), + "path": path, + }; + } + + ///Converts instance to a map. + Map toJson() { + return toMap(); + } + + @override + String toString() { + return 'AssetsPathHandler{path: $path}'; + } +} + +///Handler class to open a file from resources directory in the application APK. +/// +///Opens the requested file from application's resources directory. +/// +///The matched prefix path used shouldn't be a prefix of a real web path. +///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`. +///This saves the time of falling back to network and trying to resolve a path that doesn't exist. +///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body. +/// +///The MIME type for the file will be determined from the file's extension using +///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-). +///Developers should ensure that asset files are named using standard file extensions. +///If the file does not have a recognised extension, `text/plain` will be used by default. +class ResourcesPathHandler extends PathHandler { + ResourcesPathHandler({required String path}) : super(path: path); + + ///Gets a possible [ResourcesPathHandler] instance from a [Map] value. + static ResourcesPathHandler? fromMap(Map? map) { + if (map == null) { + return null; + } + final instance = ResourcesPathHandler( + path: map['path'], + ); + return instance; + } + + @ExchangeableObjectMethod(toMapMergeWith: true) + Map _toMapMergeWith() { + return {"type": _type}; + } + + ///Converts instance to a map. + Map toMap() { + return { + ..._toMapMergeWith(), + "path": path, + }; + } + + ///Converts instance to a map. + Map toJson() { + return toMap(); + } + + @override + String toString() { + return 'ResourcesPathHandler{path: $path}'; + } +} + +///Handler class to open files from application internal storage. +///For more information about android storage please refer to +///[Android Developers Docs: Data and file storage overview](https://developer.android.com/guide/topics/data/data-storage). +/// +///To avoid leaking user or app data to the web, make sure to choose [directory] carefully, +///and assume any file under this directory could be accessed by any web page subject to same-origin rules. +/// +///Opens the requested file from the exposed data directory. +/// +///The matched prefix path used shouldn't be a prefix of a real web path. +///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`. +///This saves the time of falling back to network and trying to resolve a path that doesn't exist. +///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body. +/// +///The MIME type for the file will be determined from the file's extension using +///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-). +///Developers should ensure that asset files are named using standard file extensions. +///If the file does not have a recognised extension, `text/plain` will be used by default. +class InternalStoragePathHandler extends PathHandler { + String directory; + InternalStoragePathHandler({required this.directory, required String path}) + : super(path: path); + + ///Gets a possible [InternalStoragePathHandler] instance from a [Map] value. + static InternalStoragePathHandler? fromMap(Map? map) { + if (map == null) { + return null; + } + final instance = InternalStoragePathHandler( + path: map['path'], + directory: map['directory'], + ); + return instance; + } + + @ExchangeableObjectMethod(toMapMergeWith: true) + Map _toMapMergeWith() { + return {"type": _type}; + } + + ///Converts instance to a map. + Map toMap() { + return { + ..._toMapMergeWith(), + "path": path, + "directory": directory, + }; + } + + ///Converts instance to a map. + Map toJson() { + return toMap(); + } + + @override + String toString() { + return 'InternalStoragePathHandler{path: $path, directory: $directory}'; + } +} diff --git a/lib/src/android/webview_feature.dart b/lib/src/android/webview_feature.dart index f57d6619..64510f7c 100644 --- a/lib/src/android/webview_feature.dart +++ b/lib/src/android/webview_feature.dart @@ -11,6 +11,9 @@ import '../types/user_script_injection_time.dart'; part 'webview_feature.g.dart'; ///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device. +/// +///**Supported Platforms/Implementations**: +///- Android native WebView @ExchangeableEnum() class WebViewFeature_ { @ExchangeableEnumCustomValue() diff --git a/lib/src/android/webview_feature.g.dart b/lib/src/android/webview_feature.g.dart index 1d3ce030..77964d4f 100644 --- a/lib/src/android/webview_feature.g.dart +++ b/lib/src/android/webview_feature.g.dart @@ -7,6 +7,9 @@ part of 'webview_feature.dart'; // ************************************************************************** ///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device. +/// +///**Supported Platforms/Implementations**: +///- Android native WebView class WebViewFeature { final String _value; final String _nativeValue; diff --git a/lib/src/in_app_webview/android/in_app_webview_controller.dart b/lib/src/in_app_webview/android/in_app_webview_controller.dart index 39e69e21..985ac143 100644 --- a/lib/src/in_app_webview/android/in_app_webview_controller.dart +++ b/lib/src/in_app_webview/android/in_app_webview_controller.dart @@ -121,6 +121,6 @@ class AndroidInAppWebViewController { Future getOriginalUrl() async { Map args = {}; String? url = await _channel.invokeMethod('getOriginalUrl', args); - return url != null ? Uri.parse(url) : null; + return url != null ? Uri.tryParse(url) : null; } } diff --git a/lib/src/in_app_webview/apple/in_app_webview_options.dart b/lib/src/in_app_webview/apple/in_app_webview_options.dart index 44787d77..63a1caf7 100755 --- a/lib/src/in_app_webview/apple/in_app_webview_options.dart +++ b/lib/src/in_app_webview/apple/in_app_webview_options.dart @@ -368,7 +368,7 @@ class IOSInAppWebViewOptions instance.useOnNavigationResponse = map["useOnNavigationResponse"]; instance.applePayAPIEnabled = map["applePayAPIEnabled"]; instance.allowingReadAccessTo = map["allowingReadAccessTo"] != null - ? Uri.parse(map["allowingReadAccessTo"]) + ? Uri.tryParse(map["allowingReadAccessTo"]) : null; instance.disableLongPressContextMenuOnLinks = map["disableLongPressContextMenuOnLinks"]; diff --git a/lib/src/in_app_webview/in_app_webview.dart b/lib/src/in_app_webview/in_app_webview.dart index 35d233d3..a3e1a35f 100755 --- a/lib/src/in_app_webview/in_app_webview.dart +++ b/lib/src/in_app_webview/in_app_webview.dart @@ -646,77 +646,52 @@ class _InAppWebViewState extends State { if (!useHybridComposition && widget.pullToRefreshController != null) { throw new Exception( - "To use the pull-to-refresh feature, useHybridComposition Android-specific option MUST be true!"); + "To use the pull-to-refresh feature, InAppWebViewSettings.useHybridComposition setting MUST be true!"); } - if (useHybridComposition) { - return PlatformViewLink( - viewType: 'com.pichillilorenzo/flutter_inappwebview', - surfaceFactory: ( - BuildContext context, - PlatformViewController controller, - ) { - return AndroidViewSurface( - controller: controller as AndroidViewController, - gestureRecognizers: widget.gestureRecognizers ?? - const >{}, - hitTestBehavior: PlatformViewHitTestBehavior.opaque, - ); - }, - onCreatePlatformView: (PlatformViewCreationParams params) { - return PlatformViewsService.initSurfaceAndroidView( - id: params.id, - viewType: 'com.pichillilorenzo/flutter_inappwebview', - layoutDirection: TextDirection.rtl, - creationParams: { - 'initialUrlRequest': widget.initialUrlRequest?.toMap(), - 'initialFile': widget.initialFile, - 'initialData': widget.initialData?.toMap(), - 'initialSettings': initialSettings, - 'contextMenu': widget.contextMenu?.toMap() ?? {}, - 'windowId': widget.windowId, - 'headlessWebViewId': - widget.headlessWebView?.isRunning() ?? false - ? widget.headlessWebView?.id - : null, - 'implementation': widget.implementation.toNativeValue(), - 'initialUserScripts': - widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? - [], - 'pullToRefreshSettings': pullToRefreshSettings - }, - creationParamsCodec: const StandardMessageCodec(), - ) - ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) - ..addOnPlatformViewCreatedListener( - (id) => _onPlatformViewCreated(id)) - ..create(); - }, - ); - } else { - return AndroidView( - viewType: 'com.pichillilorenzo/flutter_inappwebview', - onPlatformViewCreated: _onPlatformViewCreated, - gestureRecognizers: widget.gestureRecognizers, - layoutDirection: Directionality.maybeOf(context) ?? TextDirection.rtl, - creationParams: { - 'initialUrlRequest': widget.initialUrlRequest?.toMap(), - 'initialFile': widget.initialFile, - 'initialData': widget.initialData?.toMap(), - 'initialSettings': initialSettings, - 'contextMenu': widget.contextMenu?.toMap() ?? {}, - 'windowId': widget.windowId, - 'headlessWebViewId': widget.headlessWebView?.isRunning() ?? false - ? widget.headlessWebView?.id - : null, - 'implementation': widget.implementation.toNativeValue(), - 'initialUserScripts': - widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], - 'pullToRefreshSettings': pullToRefreshSettings - }, - creationParamsCodec: const StandardMessageCodec(), - ); - } + return PlatformViewLink( + viewType: 'com.pichillilorenzo/flutter_inappwebview', + surfaceFactory: ( + BuildContext context, + PlatformViewController controller, + ) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + gestureRecognizers: widget.gestureRecognizers ?? + const >{}, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + onCreatePlatformView: (PlatformViewCreationParams params) { + return _createAndroidViewController( + hybridComposition: useHybridComposition, + id: params.id, + viewType: 'com.pichillilorenzo/flutter_inappwebview', + layoutDirection: + Directionality.maybeOf(context) ?? TextDirection.rtl, + creationParams: { + 'initialUrlRequest': widget.initialUrlRequest?.toMap(), + 'initialFile': widget.initialFile, + 'initialData': widget.initialData?.toMap(), + 'initialSettings': initialSettings, + 'contextMenu': widget.contextMenu?.toMap() ?? {}, + 'windowId': widget.windowId, + 'headlessWebViewId': widget.headlessWebView?.isRunning() ?? false + ? widget.headlessWebView?.id + : null, + 'implementation': widget.implementation.toNativeValue(), + 'initialUserScripts': + widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? + [], + 'pullToRefreshSettings': pullToRefreshSettings + }, + ) + ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) + ..addOnPlatformViewCreatedListener( + (id) => _onPlatformViewCreated(id)) + ..create(); + }, + ); } else if (defaultTargetPlatform == TargetPlatform .iOS /* || defaultTargetPlatform == TargetPlatform.macOS*/) { @@ -763,6 +738,31 @@ class _InAppWebViewState extends State { _controller = null; } + 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) { final viewId = (!kIsWeb && (widget.headlessWebView?.isRunning() ?? false)) ? widget.headlessWebView?.id diff --git a/lib/src/in_app_webview/in_app_webview_controller.dart b/lib/src/in_app_webview/in_app_webview_controller.dart index b8e1887e..7cff2139 100644 --- a/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/lib/src/in_app_webview/in_app_webview_controller.dart @@ -134,7 +134,7 @@ class InAppWebViewController { if ((_webview != null && _webview!.onLoadStart != null) || _inAppBrowser != null) { String? url = call.arguments["url"]; - Uri? uri = url != null ? Uri.parse(url) : null; + Uri? uri = url != null ? Uri.tryParse(url) : null; if (_webview != null && _webview!.onLoadStart != null) _webview!.onLoadStart!(this, uri); else @@ -145,7 +145,7 @@ class InAppWebViewController { if ((_webview != null && _webview!.onLoadStop != null) || _inAppBrowser != null) { String? url = call.arguments["url"]; - Uri? uri = url != null ? Uri.parse(url) : null; + Uri? uri = url != null ? Uri.tryParse(url) : null; if (_webview != null && _webview!.onLoadStop != null) _webview!.onLoadStop!(this, uri); else @@ -435,7 +435,7 @@ class InAppWebViewController { _webview!.androidOnRenderProcessUnresponsive != null)) || _inAppBrowser != null) { String? url = call.arguments["url"]; - Uri? uri = url != null ? Uri.parse(url) : null; + Uri? uri = url != null ? Uri.tryParse(url) : null; if (_webview != null) { if (_webview!.onRenderProcessUnresponsive != null) @@ -463,7 +463,7 @@ class InAppWebViewController { _webview!.androidOnRenderProcessResponsive != null)) || _inAppBrowser != null) { String? url = call.arguments["url"]; - Uri? uri = url != null ? Uri.parse(url) : null; + Uri? uri = url != null ? Uri.tryParse(url) : null; if (_webview != null) { if (_webview!.onRenderProcessResponsive != null) @@ -516,7 +516,7 @@ class InAppWebViewController { _webview!.androidOnFormResubmission != null)) || _inAppBrowser != null) { String? url = call.arguments["url"]; - Uri? uri = url != null ? Uri.parse(url) : null; + Uri? uri = url != null ? Uri.tryParse(url) : null; if (_webview != null) { if (_webview!.onFormResubmission != null) @@ -589,7 +589,7 @@ class InAppWebViewController { _inAppBrowser != null) { String url = call.arguments["url"]; bool precomposed = call.arguments["precomposed"]; - Uri uri = Uri.parse(url); + Uri uri = Uri.tryParse(url) ?? Uri(); if (_webview != null) { if (_webview!.onReceivedTouchIconUrl != null) @@ -689,7 +689,7 @@ class InAppWebViewController { String url = call.arguments["url"]; SafeBrowsingThreat? threatType = SafeBrowsingThreat.fromNativeValue(call.arguments["threatType"]); - Uri uri = Uri.parse(url); + Uri uri = Uri.tryParse(url) ?? Uri(); if (_webview != null) { if (_webview!.onSafeBrowsingHit != null) @@ -867,7 +867,7 @@ class InAppWebViewController { _inAppBrowser != null) { String? url = call.arguments["url"]; bool? isReload = call.arguments["isReload"]; - Uri? uri = url != null ? Uri.parse(url) : null; + Uri? uri = url != null ? Uri.tryParse(url) : null; if (_webview != null && _webview!.onUpdateVisitedHistory != null) _webview!.onUpdateVisitedHistory!(this, uri, isReload); else @@ -895,7 +895,7 @@ class InAppWebViewController { if ((_webview != null && _webview!.onPageCommitVisible != null) || _inAppBrowser != null) { String? url = call.arguments["url"]; - Uri? uri = url != null ? Uri.parse(url) : null; + Uri? uri = url != null ? Uri.tryParse(url) : null; if (_webview != null && _webview!.onPageCommitVisible != null) _webview!.onPageCommitVisible!(this, uri); else @@ -1120,7 +1120,7 @@ class InAppWebViewController { _inAppBrowser != null) { String? url = call.arguments["url"]; String? printJobId = call.arguments["printJobId"]; - Uri? uri = url != null ? Uri.parse(url) : null; + Uri? uri = url != null ? Uri.tryParse(url) : null; PrintJobController? printJob = printJobId != null ? PrintJobController(id: printJobId) : null; @@ -1333,7 +1333,7 @@ class InAppWebViewController { Future getUrl() async { Map args = {}; String? url = await _channel.invokeMethod('getUrl', args); - return url != null ? Uri.parse(url) : null; + return url != null ? Uri.tryParse(url) : null; } ///Gets the title for the current page. @@ -1574,11 +1574,17 @@ class InAppWebViewController { int width = int.parse(size.split("x")[0]); int height = int.parse(size.split("x")[1]); favicons.add(Favicon( - url: Uri.parse(urlIcon), rel: rel, width: width, height: height)); + url: Uri.tryParse(urlIcon) ?? Uri(), + rel: rel, + width: width, + height: height)); } } else { favicons.add(Favicon( - url: Uri.parse(urlIcon), rel: rel, width: null, height: null)); + url: Uri.tryParse(urlIcon) ?? Uri(), + rel: rel, + width: null, + height: null)); } return favicons; @@ -2450,7 +2456,7 @@ class InAppWebViewController { Future getOriginalUrl() async { Map args = {}; String? url = await _channel.invokeMethod('getOriginalUrl', args); - return url != null ? Uri.parse(url) : null; + return url != null ? Uri.tryParse(url) : null; } ///Gets the current zoom scale of the WebView. @@ -2547,7 +2553,7 @@ class InAppWebViewController { await _channel.invokeMethod('requestFocusNodeHref', args); return result != null ? RequestFocusNodeHrefResult( - url: result['url'] != null ? Uri.parse(result['url']) : null, + url: result['url'] != null ? Uri.tryParse(result['url']) : null, title: result['title'], src: result['src'], ) @@ -2567,7 +2573,7 @@ class InAppWebViewController { await _channel.invokeMethod('requestImageRef', args); return result != null ? RequestImageRefResult( - url: result['url'] != null ? Uri.parse(result['url']) : null, + url: result['url'] != null ? Uri.tryParse(result['url']) : null, ) : null; } @@ -3014,7 +3020,7 @@ class InAppWebViewController { Future postWebMessage( {required WebMessage message, Uri? targetOrigin}) async { if (targetOrigin == null) { - targetOrigin = Uri.parse(""); + targetOrigin = Uri(); } Map args = {}; args.putIfAbsent('message', () => message.toMap()); @@ -3604,7 +3610,7 @@ class InAppWebViewController { Map args = {}; String? url = await _staticChannel.invokeMethod( 'getSafeBrowsingPrivacyPolicyUrl', args); - return url != null ? Uri.parse(url) : null; + return url != null ? Uri.tryParse(url) : null; } ///Use [setSafeBrowsingAllowlist] instead. diff --git a/lib/src/in_app_webview/in_app_webview_settings.dart b/lib/src/in_app_webview/in_app_webview_settings.dart index 36520c28..8b109bc1 100755 --- a/lib/src/in_app_webview/in_app_webview_settings.dart +++ b/lib/src/in_app_webview/in_app_webview_settings.dart @@ -3,6 +3,7 @@ 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 '../android/webview_asset_loader.dart'; import '../types/action_mode_menu_item.dart'; import '../types/cache_mode.dart'; import '../types/data_detector_types.dart'; @@ -319,6 +320,25 @@ class InAppWebViewSettings_ { ///- MacOS bool? allowUniversalAccessFromFileURLs; + ///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. + ///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**: + ///- Android native WebView + bool? allowBackgroundAudioPlaying; + + ///Use a [WebViewAssetLoader] instance to load local files including application's static assets and resources using http(s):// URLs. + ///Loading local files using web-like URLs instead of `file://` is desirable as it is compatible with the Same-Origin policy. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + WebViewAssetLoader_? webViewAssetLoader; + ///Sets the text zoom of the page in percent. The default value is `100`. /// ///**Supported Platforms/Implementations**: @@ -1251,6 +1271,7 @@ class InAppWebViewSettings_ { ///- Web String? iframeCsp; + @ExchangeableObjectConstructor() InAppWebViewSettings_({ this.useShouldOverrideUrlLoading = false, this.useOnLoadResource = false, @@ -1381,6 +1402,8 @@ class InAppWebViewSettings_ { this.isFindInteractionEnabled = false, this.minimumViewportInset, this.maximumViewportInset, + this.allowBackgroundAudioPlaying = false, + this.webViewAssetLoader, this.iframeAllow, this.iframeAllowFullscreen, this.iframeSandbox, @@ -1409,372 +1432,6 @@ class InAppWebViewSettings_ { maximumViewportInset!.horizontal, "minimumViewportInset cannot be larger than maximumViewportInset"); } - - // Map toMap() { - // List>> contentBlockersMapList = []; - // contentBlockers.forEach((contentBlocker) { - // contentBlockersMapList.add(contentBlocker.toMap()); - // }); - // List dataDetectorTypesList = []; - // dataDetectorTypes.forEach((dataDetectorType) { - // dataDetectorTypesList.add(dataDetectorType.toNativeValue()); - // }); - // - // return { - // "useShouldOverrideUrlLoading": useShouldOverrideUrlLoading, - // "useOnLoadResource": useOnLoadResource, - // "useOnDownloadStart": useOnDownloadStart, - // "clearCache": clearCache, - // "userAgent": userAgent, - // "applicationNameForUserAgent": applicationNameForUserAgent, - // "javaScriptEnabled": javaScriptEnabled, - // "javaScriptCanOpenWindowsAutomatically": - // javaScriptCanOpenWindowsAutomatically, - // "mediaPlaybackRequiresUserGesture": mediaPlaybackRequiresUserGesture, - // "verticalScrollBarEnabled": verticalScrollBarEnabled, - // "horizontalScrollBarEnabled": horizontalScrollBarEnabled, - // "resourceCustomSchemes": resourceCustomSchemes, - // "contentBlockers": contentBlockersMapList, - // "preferredContentMode": preferredContentMode?.toNativeValue(), - // "useShouldInterceptAjaxRequest": useShouldInterceptAjaxRequest, - // "useShouldInterceptFetchRequest": useShouldInterceptFetchRequest, - // "incognito": incognito, - // "cacheEnabled": cacheEnabled, - // "transparentBackground": transparentBackground, - // "disableVerticalScroll": disableVerticalScroll, - // "disableHorizontalScroll": disableHorizontalScroll, - // "disableContextMenu": disableContextMenu, - // "supportZoom": supportZoom, - // "allowFileAccessFromFileURLs": allowFileAccessFromFileURLs, - // "allowUniversalAccessFromFileURLs": allowUniversalAccessFromFileURLs, - // "textZoom": textZoom, - // "clearSessionCache": clearSessionCache, - // "builtInZoomControls": builtInZoomControls, - // "displayZoomControls": displayZoomControls, - // "databaseEnabled": databaseEnabled, - // "domStorageEnabled": domStorageEnabled, - // "useWideViewPort": useWideViewPort, - // "safeBrowsingEnabled": safeBrowsingEnabled, - // "mixedContentMode": mixedContentMode?.toNativeValue(), - // "allowContentAccess": allowContentAccess, - // "allowFileAccess": allowFileAccess, - // "appCachePath": appCachePath, - // "blockNetworkImage": blockNetworkImage, - // "blockNetworkLoads": blockNetworkLoads, - // "cacheMode": cacheMode?.toNativeValue(), - // "cursiveFontFamily": cursiveFontFamily, - // "defaultFixedFontSize": defaultFixedFontSize, - // "defaultFontSize": defaultFontSize, - // "defaultTextEncodingName": defaultTextEncodingName, - // "disabledActionModeMenuItems": - // disabledActionModeMenuItems?.toNativeValue(), - // "fantasyFontFamily": fantasyFontFamily, - // "fixedFontFamily": fixedFontFamily, - // "forceDark": forceDark?.toNativeValue(), - // "forceDarkStrategy": forceDarkStrategy?.toNativeValue(), - // "geolocationEnabled": geolocationEnabled, - // "layoutAlgorithm": layoutAlgorithm?.toNativeValue(), - // "loadWithOverviewMode": loadWithOverviewMode, - // "loadsImagesAutomatically": loadsImagesAutomatically, - // "minimumLogicalFontSize": minimumLogicalFontSize, - // "initialScale": initialScale, - // "needInitialFocus": needInitialFocus, - // "offscreenPreRaster": offscreenPreRaster, - // "sansSerifFontFamily": sansSerifFontFamily, - // "serifFontFamily": serifFontFamily, - // "standardFontFamily": standardFontFamily, - // "saveFormData": saveFormData, - // "thirdPartyCookiesEnabled": thirdPartyCookiesEnabled, - // "hardwareAcceleration": hardwareAcceleration, - // "supportMultipleWindows": supportMultipleWindows, - // "useHybridComposition": useHybridComposition, - // "regexToCancelSubFramesLoading": regexToCancelSubFramesLoading, - // "useShouldInterceptRequest": useShouldInterceptRequest, - // "useOnRenderProcessGone": useOnRenderProcessGone, - // "overScrollMode": overScrollMode?.toNativeValue(), - // "networkAvailable": networkAvailable, - // "scrollBarStyle": scrollBarStyle?.toNativeValue(), - // "verticalScrollbarPosition": verticalScrollbarPosition?.toNativeValue(), - // "scrollBarDefaultDelayBeforeFade": scrollBarDefaultDelayBeforeFade, - // "scrollbarFadingEnabled": scrollbarFadingEnabled, - // "scrollBarFadeDuration": scrollBarFadeDuration, - // "rendererPriorityPolicy": rendererPriorityPolicy?.toMap(), - // "disableDefaultErrorPage": disableDefaultErrorPage, - // "verticalScrollbarThumbColor": verticalScrollbarThumbColor?.toHex(), - // "verticalScrollbarTrackColor": verticalScrollbarTrackColor?.toHex(), - // "horizontalScrollbarThumbColor": horizontalScrollbarThumbColor?.toHex(), - // "horizontalScrollbarTrackColor": horizontalScrollbarTrackColor?.toHex(), - // "willSuppressErrorPage": willSuppressErrorPage, - // "algorithmicDarkeningAllowed": algorithmicDarkeningAllowed, - // "requestedWithHeaderMode": requestedWithHeaderMode?.toNativeValue(), - // "enterpriseAuthenticationAppLinkPolicyEnabled": - // enterpriseAuthenticationAppLinkPolicyEnabled, - // "disallowOverScroll": disallowOverScroll, - // "enableViewportScale": enableViewportScale, - // "suppressesIncrementalRendering": suppressesIncrementalRendering, - // "allowsAirPlayForMediaPlayback": allowsAirPlayForMediaPlayback, - // "allowsBackForwardNavigationGestures": - // allowsBackForwardNavigationGestures, - // "allowsLinkPreview": allowsLinkPreview, - // "ignoresViewportScaleLimits": ignoresViewportScaleLimits, - // "allowsInlineMediaPlayback": allowsInlineMediaPlayback, - // "allowsPictureInPictureMediaPlayback": - // allowsPictureInPictureMediaPlayback, - // "isFraudulentWebsiteWarningEnabled": isFraudulentWebsiteWarningEnabled, - // "selectionGranularity": selectionGranularity.toNativeValue(), - // "dataDetectorTypes": dataDetectorTypesList, - // "sharedCookiesEnabled": sharedCookiesEnabled, - // "automaticallyAdjustsScrollIndicatorInsets": - // automaticallyAdjustsScrollIndicatorInsets, - // "accessibilityIgnoresInvertColors": accessibilityIgnoresInvertColors, - // "decelerationRate": decelerationRate.toNativeValue(), - // "alwaysBounceVertical": alwaysBounceVertical, - // "alwaysBounceHorizontal": alwaysBounceHorizontal, - // "scrollsToTop": scrollsToTop, - // "isPagingEnabled": isPagingEnabled, - // "maximumZoomScale": maximumZoomScale, - // "minimumZoomScale": minimumZoomScale, - // "contentInsetAdjustmentBehavior": - // contentInsetAdjustmentBehavior.toNativeValue(), - // "isDirectionalLockEnabled": isDirectionalLockEnabled, - // "mediaType": mediaType, - // "pageZoom": pageZoom, - // "limitsNavigationsToAppBoundDomains": limitsNavigationsToAppBoundDomains, - // "useOnNavigationResponse": useOnNavigationResponse, - // "applePayAPIEnabled": applePayAPIEnabled, - // "allowingReadAccessTo": allowingReadAccessTo.toString(), - // "disableLongPressContextMenuOnLinks": disableLongPressContextMenuOnLinks, - // "disableInputAccessoryView": disableInputAccessoryView, - // "underPageBackgroundColor": underPageBackgroundColor?.toHex(), - // "isTextInteractionEnabled": isTextInteractionEnabled, - // "isSiteSpecificQuirksModeEnabled": isSiteSpecificQuirksModeEnabled, - // "upgradeKnownHostsToHTTPS": upgradeKnownHostsToHTTPS, - // "isElementFullscreenEnabled": isElementFullscreenEnabled, - // "isFindInteractionEnabled": isFindInteractionEnabled, - // "minimumViewportInset": minimumViewportInset?.toMap(), - // "maximumViewportInset": maximumViewportInset?.toMap(), - // "iframeAllow": iframeAllow, - // "iframeAllowFullscreen": iframeAllowFullscreen, - // "iframeSandbox": iframeSandbox?.map((e) => e.toNativeValue()).toList(), - // "iframeReferrerPolicy": iframeReferrerPolicy, - // "iframeName": iframeName, - // "iframeCsp": iframeCsp, - // }; - // } - // - // ///Gets a [InAppWebViewSettings] instance from a [Map] value. - // factory InAppWebViewSettings.fromMap(Map map) { - // List contentBlockers = []; - // List? contentBlockersMapList = map["contentBlockers"]; - // if (contentBlockersMapList != null) { - // contentBlockersMapList.forEach((contentBlocker) { - // contentBlockers.add(ContentBlocker.fromMap( - // Map>.from( - // Map.from(contentBlocker)))); - // }); - // } - // List dataDetectorTypes = []; - // List dataDetectorTypesList = - // List.from(map["dataDetectorTypes"] ?? []); - // dataDetectorTypesList.forEach((dataDetectorTypeValue) { - // var dataDetectorType = - // DataDetectorTypes.fromNativeValue(dataDetectorTypeValue); - // if (dataDetectorType != null) { - // dataDetectorTypes.add(dataDetectorType); - // } - // }); - // - // var settings = InAppWebViewSettings(); - // settings.useShouldOverrideUrlLoading = map["useShouldOverrideUrlLoading"]; - // settings.useOnLoadResource = map["useOnLoadResource"]; - // settings.useOnDownloadStart = map["useOnDownloadStart"]; - // settings.clearCache = map["clearCache"]; - // settings.userAgent = map["userAgent"]; - // settings.applicationNameForUserAgent = map["applicationNameForUserAgent"]; - // settings.javaScriptEnabled = map["javaScriptEnabled"]; - // settings.javaScriptCanOpenWindowsAutomatically = - // map["javaScriptCanOpenWindowsAutomatically"]; - // settings.mediaPlaybackRequiresUserGesture = - // map["mediaPlaybackRequiresUserGesture"]; - // settings.verticalScrollBarEnabled = map["verticalScrollBarEnabled"]; - // settings.horizontalScrollBarEnabled = map["horizontalScrollBarEnabled"]; - // settings.resourceCustomSchemes = - // List.from(map["resourceCustomSchemes"] ?? []); - // settings.contentBlockers = contentBlockers; - // settings.preferredContentMode = - // UserPreferredContentMode.fromNativeValue(map["preferredContentMode"]); - // settings.useShouldInterceptAjaxRequest = - // map["useShouldInterceptAjaxRequest"]; - // settings.useShouldInterceptFetchRequest = - // map["useShouldInterceptFetchRequest"]; - // settings.incognito = map["incognito"]; - // settings.cacheEnabled = map["cacheEnabled"]; - // settings.transparentBackground = map["transparentBackground"]; - // settings.disableVerticalScroll = map["disableVerticalScroll"]; - // settings.disableHorizontalScroll = map["disableHorizontalScroll"]; - // settings.disableContextMenu = map["disableContextMenu"]; - // settings.supportZoom = map["supportZoom"]; - // settings.allowFileAccessFromFileURLs = map["allowFileAccessFromFileURLs"]; - // settings.allowUniversalAccessFromFileURLs = - // map["allowUniversalAccessFromFileURLs"]; - // if (kIsWeb) { - // settings.iframeAllow = map["iframeAllow"]; - // settings.iframeAllowFullscreen = map["iframeAllowFullscreen"]; - // settings.iframeSandbox = map["iframeSandbox"] != null - // ? Set.from((map["iframeSandbox"].cast() as List) - // .map((e) => Sandbox.fromNativeValue(e))) - // : null; - // settings.iframeReferrerPolicy = - // ReferrerPolicy.fromNativeValue(map["iframeReferrerPolicy"]); - // settings.iframeName = map["iframeName"]; - // settings.iframeCsp = map["iframeCsp"]; - // } else { - // if (defaultTargetPlatform == TargetPlatform.android) { - // settings.textZoom = map["textZoom"]; - // settings.clearSessionCache = map["clearSessionCache"]; - // settings.builtInZoomControls = map["builtInZoomControls"]; - // settings.displayZoomControls = map["displayZoomControls"]; - // settings.databaseEnabled = map["databaseEnabled"]; - // settings.domStorageEnabled = map["domStorageEnabled"]; - // settings.useWideViewPort = map["useWideViewPort"]; - // settings.safeBrowsingEnabled = map["safeBrowsingEnabled"]; - // settings.mixedContentMode = - // MixedContentMode.fromNativeValue(map["mixedContentMode"]); - // settings.allowContentAccess = map["allowContentAccess"]; - // settings.allowFileAccess = map["allowFileAccess"]; - // settings.appCachePath = map["appCachePath"]; - // settings.blockNetworkImage = map["blockNetworkImage"]; - // settings.blockNetworkLoads = map["blockNetworkLoads"]; - // settings.cacheMode = CacheMode.fromNativeValue(map["cacheMode"]); - // settings.cursiveFontFamily = map["cursiveFontFamily"]; - // settings.defaultFixedFontSize = map["defaultFixedFontSize"]; - // settings.defaultFontSize = map["defaultFontSize"]; - // settings.defaultTextEncodingName = map["defaultTextEncodingName"]; - // settings.disabledActionModeMenuItems = - // ActionModeMenuItem.fromNativeValue( - // map["disabledActionModeMenuItems"]); - // settings.fantasyFontFamily = map["fantasyFontFamily"]; - // settings.fixedFontFamily = map["fixedFontFamily"]; - // settings.forceDark = ForceDark.fromNativeValue(map["forceDark"]); - // settings.forceDarkStrategy = - // ForceDarkStrategy.fromNativeValue(map["forceDarkStrategy"]); - // settings.geolocationEnabled = map["geolocationEnabled"]; - // settings.layoutAlgorithm = - // LayoutAlgorithm.fromNativeValue(map["layoutAlgorithm"]); - // settings.loadWithOverviewMode = map["loadWithOverviewMode"]; - // settings.loadsImagesAutomatically = map["loadsImagesAutomatically"]; - // settings.minimumLogicalFontSize = map["minimumLogicalFontSize"]; - // settings.initialScale = map["initialScale"]; - // settings.needInitialFocus = map["needInitialFocus"]; - // settings.offscreenPreRaster = map["offscreenPreRaster"]; - // settings.sansSerifFontFamily = map["sansSerifFontFamily"]; - // settings.serifFontFamily = map["serifFontFamily"]; - // settings.standardFontFamily = map["standardFontFamily"]; - // settings.saveFormData = map["saveFormData"]; - // settings.thirdPartyCookiesEnabled = map["thirdPartyCookiesEnabled"]; - // settings.hardwareAcceleration = map["hardwareAcceleration"]; - // settings.supportMultipleWindows = map["supportMultipleWindows"]; - // settings.regexToCancelSubFramesLoading = - // map["regexToCancelSubFramesLoading"]; - // settings.useHybridComposition = map["useHybridComposition"]; - // settings.useShouldInterceptRequest = map["useShouldInterceptRequest"]; - // settings.useOnRenderProcessGone = map["useOnRenderProcessGone"]; - // settings.overScrollMode = - // OverScrollMode.fromNativeValue(map["overScrollMode"]); - // settings.networkAvailable = map["networkAvailable"]; - // settings.scrollBarStyle = - // ScrollBarStyle.fromNativeValue(map["scrollBarStyle"]); - // settings.verticalScrollbarPosition = - // VerticalScrollbarPosition.fromNativeValue( - // map["verticalScrollbarPosition"]); - // settings.scrollBarDefaultDelayBeforeFade = - // map["scrollBarDefaultDelayBeforeFade"]; - // settings.scrollbarFadingEnabled = map["scrollbarFadingEnabled"]; - // settings.scrollBarFadeDuration = map["scrollBarFadeDuration"]; - // settings.rendererPriorityPolicy = RendererPriorityPolicy.fromMap( - // map["rendererPriorityPolicy"]?.cast()); - // settings.disableDefaultErrorPage = map["disableDefaultErrorPage"]; - // settings.verticalScrollbarThumbColor = - // UtilColor.fromHex(map["verticalScrollbarThumbColor"]); - // settings.verticalScrollbarTrackColor = - // UtilColor.fromHex(map["verticalScrollbarTrackColor"]); - // settings.horizontalScrollbarThumbColor = - // UtilColor.fromHex(map["horizontalScrollbarThumbColor"]); - // settings.horizontalScrollbarTrackColor = - // UtilColor.fromHex(map["horizontalScrollbarTrackColor"]); - // settings.willSuppressErrorPage = map["willSuppressErrorPage"]; - // settings.algorithmicDarkeningAllowed = - // map["algorithmicDarkeningAllowed"]; - // settings.requestedWithHeaderMode = - // RequestedWithHeaderMode.fromNativeValue( - // map["requestedWithHeaderMode"]); - // settings.enterpriseAuthenticationAppLinkPolicyEnabled = - // map["enterpriseAuthenticationAppLinkPolicyEnabled"]; - // } else if (defaultTargetPlatform == TargetPlatform.iOS || - // defaultTargetPlatform == TargetPlatform.macOS) { - // settings.disallowOverScroll = map["disallowOverScroll"]; - // settings.enableViewportScale = map["enableViewportScale"]; - // settings.suppressesIncrementalRendering = - // map["suppressesIncrementalRendering"]; - // settings.allowsAirPlayForMediaPlayback = - // map["allowsAirPlayForMediaPlayback"]; - // settings.allowsBackForwardNavigationGestures = - // map["allowsBackForwardNavigationGestures"]; - // settings.allowsLinkPreview = map["allowsLinkPreview"]; - // settings.ignoresViewportScaleLimits = map["ignoresViewportScaleLimits"]; - // settings.allowsInlineMediaPlayback = map["allowsInlineMediaPlayback"]; - // settings.allowsPictureInPictureMediaPlayback = - // map["allowsPictureInPictureMediaPlayback"]; - // settings.isFraudulentWebsiteWarningEnabled = - // map["isFraudulentWebsiteWarningEnabled"]; - // settings.selectionGranularity = - // SelectionGranularity.fromNativeValue(map["selectionGranularity"])!; - // settings.dataDetectorTypes = dataDetectorTypes; - // settings.sharedCookiesEnabled = map["sharedCookiesEnabled"]; - // settings.automaticallyAdjustsScrollIndicatorInsets = - // map["automaticallyAdjustsScrollIndicatorInsets"]; - // settings.accessibilityIgnoresInvertColors = - // map["accessibilityIgnoresInvertColors"]; - // settings.decelerationRate = ScrollViewDecelerationRate.fromNativeValue( - // map["decelerationRate"])!; - // settings.alwaysBounceVertical = map["alwaysBounceVertical"]; - // settings.alwaysBounceHorizontal = map["alwaysBounceHorizontal"]; - // settings.scrollsToTop = map["scrollsToTop"]; - // settings.isPagingEnabled = map["isPagingEnabled"]; - // settings.maximumZoomScale = map["maximumZoomScale"]; - // settings.minimumZoomScale = map["minimumZoomScale"]; - // settings.contentInsetAdjustmentBehavior = - // ScrollViewContentInsetAdjustmentBehavior.fromNativeValue( - // map["contentInsetAdjustmentBehavior"])!; - // settings.isDirectionalLockEnabled = map["isDirectionalLockEnabled"]; - // settings.mediaType = map["mediaType"]; - // settings.pageZoom = map["pageZoom"]; - // settings.limitsNavigationsToAppBoundDomains = - // map["limitsNavigationsToAppBoundDomains"]; - // settings.useOnNavigationResponse = map["useOnNavigationResponse"]; - // settings.applePayAPIEnabled = map["applePayAPIEnabled"]; - // settings.allowingReadAccessTo = map["allowingReadAccessTo"] != null - // ? Uri.parse(map["allowingReadAccessTo"]) - // : null; - // settings.disableLongPressContextMenuOnLinks = - // map["disableLongPressContextMenuOnLinks"]; - // settings.disableInputAccessoryView = map["disableInputAccessoryView"]; - // settings.underPageBackgroundColor = - // UtilColor.fromHex(map["underPageBackgroundColor"]); - // settings.isTextInteractionEnabled = map["isTextInteractionEnabled"]; - // settings.isSiteSpecificQuirksModeEnabled = - // map["isSiteSpecificQuirksModeEnabled"]; - // settings.upgradeKnownHostsToHTTPS = map["upgradeKnownHostsToHTTPS"]; - // settings.isElementFullscreenEnabled = map["isElementFullscreenEnabled"]; - // settings.isFindInteractionEnabled = map["isFindInteractionEnabled"]; - // settings.minimumViewportInset = MapEdgeInsets.fromMap( - // map["minimumViewportInset"]?.cast()); - // settings.maximumViewportInset = MapEdgeInsets.fromMap( - // map["maximumViewportInset"]?.cast()); - // } - // } - // return settings; - // } } ///Class that represents the options that can be used for a [WebView]. diff --git a/lib/src/in_app_webview/in_app_webview_settings.g.dart b/lib/src/in_app_webview/in_app_webview_settings.g.dart index fa542184..4d046e0b 100644 --- a/lib/src/in_app_webview/in_app_webview_settings.g.dart +++ b/lib/src/in_app_webview/in_app_webview_settings.g.dart @@ -277,6 +277,25 @@ class InAppWebViewSettings { ///- MacOS bool? allowUniversalAccessFromFileURLs; + ///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. + ///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**: + ///- Android native WebView + bool? allowBackgroundAudioPlaying; + + ///Use a [WebViewAssetLoader] instance to load local files including application's static assets and resources using http(s):// URLs. + ///Loading local files using web-like URLs instead of `file://` is desirable as it is compatible with the Same-Origin policy. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + WebViewAssetLoader? webViewAssetLoader; + ///Sets the text zoom of the page in percent. The default value is `100`. /// ///**Supported Platforms/Implementations**: @@ -1265,7 +1284,6 @@ class InAppWebViewSettings { this.loadWithOverviewMode = true, this.loadsImagesAutomatically = true, this.minimumLogicalFontSize = 8, - this.initialScale = 0, this.needInitialFocus = true, this.offscreenPreRaster = false, this.sansSerifFontFamily = "sans-serif", @@ -1274,6 +1292,7 @@ class InAppWebViewSettings { this.saveFormData = true, this.thirdPartyCookiesEnabled = true, this.hardwareAcceleration = true, + this.initialScale = 0, this.supportMultipleWindows = false, this.regexToCancelSubFramesLoading, this.useHybridComposition = true, @@ -1338,12 +1357,35 @@ class InAppWebViewSettings { this.isFindInteractionEnabled = false, this.minimumViewportInset, this.maximumViewportInset, + this.allowBackgroundAudioPlaying = false, + this.webViewAssetLoader, this.iframeAllow, this.iframeAllowFullscreen, this.iframeSandbox, this.iframeReferrerPolicy, this.iframeName, - this.iframeCsp}); + this.iframeCsp}) { + if (this.minimumFontSize == null) + this.minimumFontSize = + defaultTargetPlatform == TargetPlatform.android ? 8 : 0; + assert(this.resourceCustomSchemes == null || + (this.resourceCustomSchemes != null && + !this.resourceCustomSchemes!.contains("http") && + !this.resourceCustomSchemes!.contains("https"))); + assert( + allowingReadAccessTo == null || allowingReadAccessTo!.isScheme("file")); + assert( + (minimumViewportInset == null && maximumViewportInset == null) || + minimumViewportInset != null && + maximumViewportInset != null && + minimumViewportInset!.isNonNegative && + maximumViewportInset!.isNonNegative && + minimumViewportInset!.vertical <= + maximumViewportInset!.vertical && + minimumViewportInset!.horizontal <= + maximumViewportInset!.horizontal, + "minimumViewportInset cannot be larger than maximumViewportInset"); + } ///Gets a possible [InAppWebViewSettings] instance from a [Map] value. static InAppWebViewSettings? fromMap(Map? map) { @@ -1352,6 +1394,8 @@ class InAppWebViewSettings { } final instance = InAppWebViewSettings( minimumFontSize: map['minimumFontSize'], + webViewAssetLoader: WebViewAssetLoader.fromMap( + map['webViewAssetLoader']?.cast()), mixedContentMode: MixedContentMode.fromNativeValue(map['mixedContentMode']), appCachePath: map['appCachePath'], @@ -1386,7 +1430,7 @@ class InAppWebViewSettings { map['requestedWithHeaderMode']), mediaType: map['mediaType'], allowingReadAccessTo: map['allowingReadAccessTo'] != null - ? Uri.parse(map['allowingReadAccessTo']) + ? Uri.tryParse(map['allowingReadAccessTo']) : null, underPageBackgroundColor: map['underPageBackgroundColor'] != null ? UtilColor.fromStringRepresentation(map['underPageBackgroundColor']) @@ -1439,6 +1483,7 @@ class InAppWebViewSettings { instance.allowFileAccessFromFileURLs = map['allowFileAccessFromFileURLs']; instance.allowUniversalAccessFromFileURLs = map['allowUniversalAccessFromFileURLs']; + instance.allowBackgroundAudioPlaying = map['allowBackgroundAudioPlaying']; instance.textZoom = map['textZoom']; instance.clearSessionCache = map['clearSessionCache']; instance.builtInZoomControls = map['builtInZoomControls']; @@ -1576,6 +1621,8 @@ class InAppWebViewSettings { "supportZoom": supportZoom, "allowFileAccessFromFileURLs": allowFileAccessFromFileURLs, "allowUniversalAccessFromFileURLs": allowUniversalAccessFromFileURLs, + "allowBackgroundAudioPlaying": allowBackgroundAudioPlaying, + "webViewAssetLoader": webViewAssetLoader?.toMap(), "textZoom": textZoom, "clearSessionCache": clearSessionCache, "builtInZoomControls": builtInZoomControls, @@ -1704,6 +1751,6 @@ class InAppWebViewSettings { @override String toString() { - return 'InAppWebViewSettings{useShouldOverrideUrlLoading: $useShouldOverrideUrlLoading, useOnLoadResource: $useOnLoadResource, useOnDownloadStart: $useOnDownloadStart, clearCache: $clearCache, userAgent: $userAgent, applicationNameForUserAgent: $applicationNameForUserAgent, javaScriptEnabled: $javaScriptEnabled, javaScriptCanOpenWindowsAutomatically: $javaScriptCanOpenWindowsAutomatically, mediaPlaybackRequiresUserGesture: $mediaPlaybackRequiresUserGesture, minimumFontSize: $minimumFontSize, verticalScrollBarEnabled: $verticalScrollBarEnabled, horizontalScrollBarEnabled: $horizontalScrollBarEnabled, resourceCustomSchemes: $resourceCustomSchemes, contentBlockers: $contentBlockers, preferredContentMode: $preferredContentMode, useShouldInterceptAjaxRequest: $useShouldInterceptAjaxRequest, useShouldInterceptFetchRequest: $useShouldInterceptFetchRequest, incognito: $incognito, cacheEnabled: $cacheEnabled, transparentBackground: $transparentBackground, disableVerticalScroll: $disableVerticalScroll, disableHorizontalScroll: $disableHorizontalScroll, disableContextMenu: $disableContextMenu, supportZoom: $supportZoom, allowFileAccessFromFileURLs: $allowFileAccessFromFileURLs, allowUniversalAccessFromFileURLs: $allowUniversalAccessFromFileURLs, textZoom: $textZoom, clearSessionCache: $clearSessionCache, builtInZoomControls: $builtInZoomControls, displayZoomControls: $displayZoomControls, databaseEnabled: $databaseEnabled, domStorageEnabled: $domStorageEnabled, useWideViewPort: $useWideViewPort, safeBrowsingEnabled: $safeBrowsingEnabled, mixedContentMode: $mixedContentMode, allowContentAccess: $allowContentAccess, allowFileAccess: $allowFileAccess, appCachePath: $appCachePath, blockNetworkImage: $blockNetworkImage, blockNetworkLoads: $blockNetworkLoads, cacheMode: $cacheMode, cursiveFontFamily: $cursiveFontFamily, defaultFixedFontSize: $defaultFixedFontSize, defaultFontSize: $defaultFontSize, defaultTextEncodingName: $defaultTextEncodingName, disabledActionModeMenuItems: $disabledActionModeMenuItems, fantasyFontFamily: $fantasyFontFamily, fixedFontFamily: $fixedFontFamily, forceDark: $forceDark, forceDarkStrategy: $forceDarkStrategy, geolocationEnabled: $geolocationEnabled, layoutAlgorithm: $layoutAlgorithm, loadWithOverviewMode: $loadWithOverviewMode, loadsImagesAutomatically: $loadsImagesAutomatically, minimumLogicalFontSize: $minimumLogicalFontSize, initialScale: $initialScale, needInitialFocus: $needInitialFocus, offscreenPreRaster: $offscreenPreRaster, sansSerifFontFamily: $sansSerifFontFamily, serifFontFamily: $serifFontFamily, standardFontFamily: $standardFontFamily, saveFormData: $saveFormData, thirdPartyCookiesEnabled: $thirdPartyCookiesEnabled, hardwareAcceleration: $hardwareAcceleration, supportMultipleWindows: $supportMultipleWindows, regexToCancelSubFramesLoading: $regexToCancelSubFramesLoading, useHybridComposition: $useHybridComposition, useShouldInterceptRequest: $useShouldInterceptRequest, useOnRenderProcessGone: $useOnRenderProcessGone, overScrollMode: $overScrollMode, networkAvailable: $networkAvailable, scrollBarStyle: $scrollBarStyle, verticalScrollbarPosition: $verticalScrollbarPosition, scrollBarDefaultDelayBeforeFade: $scrollBarDefaultDelayBeforeFade, scrollbarFadingEnabled: $scrollbarFadingEnabled, scrollBarFadeDuration: $scrollBarFadeDuration, rendererPriorityPolicy: $rendererPriorityPolicy, disableDefaultErrorPage: $disableDefaultErrorPage, verticalScrollbarThumbColor: $verticalScrollbarThumbColor, verticalScrollbarTrackColor: $verticalScrollbarTrackColor, horizontalScrollbarThumbColor: $horizontalScrollbarThumbColor, horizontalScrollbarTrackColor: $horizontalScrollbarTrackColor, willSuppressErrorPage: $willSuppressErrorPage, algorithmicDarkeningAllowed: $algorithmicDarkeningAllowed, requestedWithHeaderMode: $requestedWithHeaderMode, enterpriseAuthenticationAppLinkPolicyEnabled: $enterpriseAuthenticationAppLinkPolicyEnabled, disallowOverScroll: $disallowOverScroll, enableViewportScale: $enableViewportScale, suppressesIncrementalRendering: $suppressesIncrementalRendering, allowsAirPlayForMediaPlayback: $allowsAirPlayForMediaPlayback, allowsBackForwardNavigationGestures: $allowsBackForwardNavigationGestures, allowsLinkPreview: $allowsLinkPreview, ignoresViewportScaleLimits: $ignoresViewportScaleLimits, allowsInlineMediaPlayback: $allowsInlineMediaPlayback, allowsPictureInPictureMediaPlayback: $allowsPictureInPictureMediaPlayback, isFraudulentWebsiteWarningEnabled: $isFraudulentWebsiteWarningEnabled, selectionGranularity: $selectionGranularity, dataDetectorTypes: $dataDetectorTypes, sharedCookiesEnabled: $sharedCookiesEnabled, automaticallyAdjustsScrollIndicatorInsets: $automaticallyAdjustsScrollIndicatorInsets, accessibilityIgnoresInvertColors: $accessibilityIgnoresInvertColors, decelerationRate: $decelerationRate, alwaysBounceVertical: $alwaysBounceVertical, alwaysBounceHorizontal: $alwaysBounceHorizontal, scrollsToTop: $scrollsToTop, isPagingEnabled: $isPagingEnabled, maximumZoomScale: $maximumZoomScale, minimumZoomScale: $minimumZoomScale, contentInsetAdjustmentBehavior: $contentInsetAdjustmentBehavior, isDirectionalLockEnabled: $isDirectionalLockEnabled, mediaType: $mediaType, pageZoom: $pageZoom, limitsNavigationsToAppBoundDomains: $limitsNavigationsToAppBoundDomains, useOnNavigationResponse: $useOnNavigationResponse, applePayAPIEnabled: $applePayAPIEnabled, allowingReadAccessTo: $allowingReadAccessTo, disableLongPressContextMenuOnLinks: $disableLongPressContextMenuOnLinks, disableInputAccessoryView: $disableInputAccessoryView, underPageBackgroundColor: $underPageBackgroundColor, isTextInteractionEnabled: $isTextInteractionEnabled, isSiteSpecificQuirksModeEnabled: $isSiteSpecificQuirksModeEnabled, upgradeKnownHostsToHTTPS: $upgradeKnownHostsToHTTPS, isElementFullscreenEnabled: $isElementFullscreenEnabled, isFindInteractionEnabled: $isFindInteractionEnabled, minimumViewportInset: $minimumViewportInset, maximumViewportInset: $maximumViewportInset, iframeAllow: $iframeAllow, iframeAllowFullscreen: $iframeAllowFullscreen, iframeSandbox: $iframeSandbox, iframeReferrerPolicy: $iframeReferrerPolicy, iframeName: $iframeName, iframeCsp: $iframeCsp}'; + return 'InAppWebViewSettings{useShouldOverrideUrlLoading: $useShouldOverrideUrlLoading, useOnLoadResource: $useOnLoadResource, useOnDownloadStart: $useOnDownloadStart, clearCache: $clearCache, userAgent: $userAgent, applicationNameForUserAgent: $applicationNameForUserAgent, javaScriptEnabled: $javaScriptEnabled, javaScriptCanOpenWindowsAutomatically: $javaScriptCanOpenWindowsAutomatically, mediaPlaybackRequiresUserGesture: $mediaPlaybackRequiresUserGesture, minimumFontSize: $minimumFontSize, verticalScrollBarEnabled: $verticalScrollBarEnabled, horizontalScrollBarEnabled: $horizontalScrollBarEnabled, resourceCustomSchemes: $resourceCustomSchemes, contentBlockers: $contentBlockers, preferredContentMode: $preferredContentMode, useShouldInterceptAjaxRequest: $useShouldInterceptAjaxRequest, useShouldInterceptFetchRequest: $useShouldInterceptFetchRequest, incognito: $incognito, cacheEnabled: $cacheEnabled, transparentBackground: $transparentBackground, disableVerticalScroll: $disableVerticalScroll, disableHorizontalScroll: $disableHorizontalScroll, disableContextMenu: $disableContextMenu, supportZoom: $supportZoom, allowFileAccessFromFileURLs: $allowFileAccessFromFileURLs, allowUniversalAccessFromFileURLs: $allowUniversalAccessFromFileURLs, allowBackgroundAudioPlaying: $allowBackgroundAudioPlaying, webViewAssetLoader: $webViewAssetLoader, textZoom: $textZoom, clearSessionCache: $clearSessionCache, builtInZoomControls: $builtInZoomControls, displayZoomControls: $displayZoomControls, databaseEnabled: $databaseEnabled, domStorageEnabled: $domStorageEnabled, useWideViewPort: $useWideViewPort, safeBrowsingEnabled: $safeBrowsingEnabled, mixedContentMode: $mixedContentMode, allowContentAccess: $allowContentAccess, allowFileAccess: $allowFileAccess, appCachePath: $appCachePath, blockNetworkImage: $blockNetworkImage, blockNetworkLoads: $blockNetworkLoads, cacheMode: $cacheMode, cursiveFontFamily: $cursiveFontFamily, defaultFixedFontSize: $defaultFixedFontSize, defaultFontSize: $defaultFontSize, defaultTextEncodingName: $defaultTextEncodingName, disabledActionModeMenuItems: $disabledActionModeMenuItems, fantasyFontFamily: $fantasyFontFamily, fixedFontFamily: $fixedFontFamily, forceDark: $forceDark, forceDarkStrategy: $forceDarkStrategy, geolocationEnabled: $geolocationEnabled, layoutAlgorithm: $layoutAlgorithm, loadWithOverviewMode: $loadWithOverviewMode, loadsImagesAutomatically: $loadsImagesAutomatically, minimumLogicalFontSize: $minimumLogicalFontSize, initialScale: $initialScale, needInitialFocus: $needInitialFocus, offscreenPreRaster: $offscreenPreRaster, sansSerifFontFamily: $sansSerifFontFamily, serifFontFamily: $serifFontFamily, standardFontFamily: $standardFontFamily, saveFormData: $saveFormData, thirdPartyCookiesEnabled: $thirdPartyCookiesEnabled, hardwareAcceleration: $hardwareAcceleration, supportMultipleWindows: $supportMultipleWindows, regexToCancelSubFramesLoading: $regexToCancelSubFramesLoading, useHybridComposition: $useHybridComposition, useShouldInterceptRequest: $useShouldInterceptRequest, useOnRenderProcessGone: $useOnRenderProcessGone, overScrollMode: $overScrollMode, networkAvailable: $networkAvailable, scrollBarStyle: $scrollBarStyle, verticalScrollbarPosition: $verticalScrollbarPosition, scrollBarDefaultDelayBeforeFade: $scrollBarDefaultDelayBeforeFade, scrollbarFadingEnabled: $scrollbarFadingEnabled, scrollBarFadeDuration: $scrollBarFadeDuration, rendererPriorityPolicy: $rendererPriorityPolicy, disableDefaultErrorPage: $disableDefaultErrorPage, verticalScrollbarThumbColor: $verticalScrollbarThumbColor, verticalScrollbarTrackColor: $verticalScrollbarTrackColor, horizontalScrollbarThumbColor: $horizontalScrollbarThumbColor, horizontalScrollbarTrackColor: $horizontalScrollbarTrackColor, willSuppressErrorPage: $willSuppressErrorPage, algorithmicDarkeningAllowed: $algorithmicDarkeningAllowed, requestedWithHeaderMode: $requestedWithHeaderMode, enterpriseAuthenticationAppLinkPolicyEnabled: $enterpriseAuthenticationAppLinkPolicyEnabled, disallowOverScroll: $disallowOverScroll, enableViewportScale: $enableViewportScale, suppressesIncrementalRendering: $suppressesIncrementalRendering, allowsAirPlayForMediaPlayback: $allowsAirPlayForMediaPlayback, allowsBackForwardNavigationGestures: $allowsBackForwardNavigationGestures, allowsLinkPreview: $allowsLinkPreview, ignoresViewportScaleLimits: $ignoresViewportScaleLimits, allowsInlineMediaPlayback: $allowsInlineMediaPlayback, allowsPictureInPictureMediaPlayback: $allowsPictureInPictureMediaPlayback, isFraudulentWebsiteWarningEnabled: $isFraudulentWebsiteWarningEnabled, selectionGranularity: $selectionGranularity, dataDetectorTypes: $dataDetectorTypes, sharedCookiesEnabled: $sharedCookiesEnabled, automaticallyAdjustsScrollIndicatorInsets: $automaticallyAdjustsScrollIndicatorInsets, accessibilityIgnoresInvertColors: $accessibilityIgnoresInvertColors, decelerationRate: $decelerationRate, alwaysBounceVertical: $alwaysBounceVertical, alwaysBounceHorizontal: $alwaysBounceHorizontal, scrollsToTop: $scrollsToTop, isPagingEnabled: $isPagingEnabled, maximumZoomScale: $maximumZoomScale, minimumZoomScale: $minimumZoomScale, contentInsetAdjustmentBehavior: $contentInsetAdjustmentBehavior, isDirectionalLockEnabled: $isDirectionalLockEnabled, mediaType: $mediaType, pageZoom: $pageZoom, limitsNavigationsToAppBoundDomains: $limitsNavigationsToAppBoundDomains, useOnNavigationResponse: $useOnNavigationResponse, applePayAPIEnabled: $applePayAPIEnabled, allowingReadAccessTo: $allowingReadAccessTo, disableLongPressContextMenuOnLinks: $disableLongPressContextMenuOnLinks, disableInputAccessoryView: $disableInputAccessoryView, underPageBackgroundColor: $underPageBackgroundColor, isTextInteractionEnabled: $isTextInteractionEnabled, isSiteSpecificQuirksModeEnabled: $isSiteSpecificQuirksModeEnabled, upgradeKnownHostsToHTTPS: $upgradeKnownHostsToHTTPS, isElementFullscreenEnabled: $isElementFullscreenEnabled, isFindInteractionEnabled: $isFindInteractionEnabled, minimumViewportInset: $minimumViewportInset, maximumViewportInset: $maximumViewportInset, iframeAllow: $iframeAllow, iframeAllowFullscreen: $iframeAllowFullscreen, iframeSandbox: $iframeSandbox, iframeReferrerPolicy: $iframeReferrerPolicy, iframeName: $iframeName, iframeCsp: $iframeCsp}'; } } diff --git a/lib/src/print_job/print_job_settings.dart b/lib/src/print_job/print_job_settings.dart index 1c4d7a80..ba1f6d7e 100644 --- a/lib/src/print_job/print_job_settings.dart +++ b/lib/src/print_job/print_job_settings.dart @@ -440,8 +440,9 @@ class PrintJobSettings { showsProgressPanel: map["showsProgressPanel"], jobDisposition: PrintJobDisposition.fromNativeValue(map["jobDisposition"]), - jobSavingURL: - map["jobSavingURL"] != null ? Uri.parse(map["jobSavingURL"]) : null, + jobSavingURL: map["jobSavingURL"] != null + ? Uri.tryParse(map["jobSavingURL"]) + : null, paperName: map["paperName"], horizontalPagination: PrintJobPaginationMode.fromNativeValue(map["horizontalPagination"]), diff --git a/lib/src/types/ajax_request.g.dart b/lib/src/types/ajax_request.g.dart index c76ed55c..8109d7d8 100644 --- a/lib/src/types/ajax_request.g.dart +++ b/lib/src/types/ajax_request.g.dart @@ -103,7 +103,7 @@ class AjaxRequest { final instance = AjaxRequest( data: map['data'], method: map['method'], - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, isAsync: map['isAsync'], user: map['user'], password: map['password'], @@ -113,7 +113,7 @@ class AjaxRequest { readyState: AjaxRequestReadyState.fromNativeValue(map['readyState']), status: map['status'], responseURL: - map['responseURL'] != null ? Uri.parse(map['responseURL']) : null, + map['responseURL'] != null ? Uri.tryParse(map['responseURL']) : null, responseType: map['responseType'], response: map['response'], responseText: map['responseText'], diff --git a/lib/src/types/download_start_request.g.dart b/lib/src/types/download_start_request.g.dart index b9483292..2bc722ba 100644 --- a/lib/src/types/download_start_request.g.dart +++ b/lib/src/types/download_start_request.g.dart @@ -43,7 +43,7 @@ class DownloadStartRequest { return null; } final instance = DownloadStartRequest( - url: Uri.parse(map['url']), + url: (Uri.tryParse(map['url']) ?? Uri()), userAgent: map['userAgent'], contentDisposition: map['contentDisposition'], mimeType: map['mimeType'], diff --git a/lib/src/types/favicon.g.dart b/lib/src/types/favicon.g.dart index 8c4f644b..9730cdd2 100644 --- a/lib/src/types/favicon.g.dart +++ b/lib/src/types/favicon.g.dart @@ -27,7 +27,7 @@ class Favicon { return null; } final instance = Favicon( - url: Uri.parse(map['url']), + url: (Uri.tryParse(map['url']) ?? Uri()), rel: map['rel'], width: map['width'], height: map['height'], diff --git a/lib/src/types/fetch_request.g.dart b/lib/src/types/fetch_request.g.dart index 3c8f0406..9cf02803 100644 --- a/lib/src/types/fetch_request.g.dart +++ b/lib/src/types/fetch_request.g.dart @@ -67,7 +67,7 @@ class FetchRequest { return null; } final instance = FetchRequest( - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, method: map['method'], headers: map['headers']?.cast(), body: map['body'], diff --git a/lib/src/types/fetch_request_federated_credential.g.dart b/lib/src/types/fetch_request_federated_credential.g.dart index 7638e513..071888c5 100644 --- a/lib/src/types/fetch_request_federated_credential.g.dart +++ b/lib/src/types/fetch_request_federated_credential.g.dart @@ -41,7 +41,7 @@ class FetchRequestFederatedCredential extends FetchRequestCredential { name: map['name'], protocol: map['protocol'], provider: map['provider'], - iconURL: map['iconURL'] != null ? Uri.parse(map['iconURL']) : null, + iconURL: map['iconURL'] != null ? Uri.tryParse(map['iconURL']) : null, ); instance.type = map['type']; return instance; diff --git a/lib/src/types/fetch_request_password_credential.g.dart b/lib/src/types/fetch_request_password_credential.g.dart index 612d81e6..b81fdf3a 100644 --- a/lib/src/types/fetch_request_password_credential.g.dart +++ b/lib/src/types/fetch_request_password_credential.g.dart @@ -32,7 +32,7 @@ class FetchRequestPasswordCredential extends FetchRequestCredential { id: map['id'], name: map['name'], password: map['password'], - iconURL: map['iconURL'] != null ? Uri.parse(map['iconURL']) : null, + iconURL: map['iconURL'] != null ? Uri.tryParse(map['iconURL']) : null, ); instance.type = map['type']; return instance; diff --git a/lib/src/types/in_app_webview_initial_data.g.dart b/lib/src/types/in_app_webview_initial_data.g.dart index d72c8037..b9382c4f 100644 --- a/lib/src/types/in_app_webview_initial_data.g.dart +++ b/lib/src/types/in_app_webview_initial_data.g.dart @@ -46,11 +46,11 @@ class InAppWebViewInitialData { } final instance = InAppWebViewInitialData( data: map['data'], - baseUrl: map['baseUrl'] != null ? Uri.parse(map['baseUrl']) : null, + baseUrl: map['baseUrl'] != null ? Uri.tryParse(map['baseUrl']) : null, androidHistoryUrl: - map['historyUrl'] != null ? Uri.parse(map['historyUrl']) : null, + map['historyUrl'] != null ? Uri.tryParse(map['historyUrl']) : null, historyUrl: - map['historyUrl'] != null ? Uri.parse(map['historyUrl']) : null, + map['historyUrl'] != null ? Uri.tryParse(map['historyUrl']) : null, ); instance.mimeType = map['mimeType']; instance.encoding = map['encoding']; diff --git a/lib/src/types/js_alert_request.g.dart b/lib/src/types/js_alert_request.g.dart index 4fb17353..cb38cf8b 100644 --- a/lib/src/types/js_alert_request.g.dart +++ b/lib/src/types/js_alert_request.g.dart @@ -37,7 +37,7 @@ class JsAlertRequest { return null; } final instance = JsAlertRequest( - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, message: map['message'], iosIsMainFrame: map['isMainFrame'], isMainFrame: map['isMainFrame'], diff --git a/lib/src/types/js_before_unload_request.g.dart b/lib/src/types/js_before_unload_request.g.dart index 5553aece..ea8100ac 100644 --- a/lib/src/types/js_before_unload_request.g.dart +++ b/lib/src/types/js_before_unload_request.g.dart @@ -21,7 +21,7 @@ class JsBeforeUnloadRequest { return null; } final instance = JsBeforeUnloadRequest( - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, message: map['message'], ); return instance; diff --git a/lib/src/types/js_confirm_request.g.dart b/lib/src/types/js_confirm_request.g.dart index 869a81d5..89ed0d50 100644 --- a/lib/src/types/js_confirm_request.g.dart +++ b/lib/src/types/js_confirm_request.g.dart @@ -37,7 +37,7 @@ class JsConfirmRequest { return null; } final instance = JsConfirmRequest( - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, message: map['message'], iosIsMainFrame: map['isMainFrame'], isMainFrame: map['isMainFrame'], diff --git a/lib/src/types/js_prompt_request.g.dart b/lib/src/types/js_prompt_request.g.dart index 29339e92..893a88cb 100644 --- a/lib/src/types/js_prompt_request.g.dart +++ b/lib/src/types/js_prompt_request.g.dart @@ -41,7 +41,7 @@ class JsPromptRequest { return null; } final instance = JsPromptRequest( - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, message: map['message'], defaultValue: map['defaultValue'], iosIsMainFrame: map['isMainFrame'], diff --git a/lib/src/types/loaded_resource.g.dart b/lib/src/types/loaded_resource.g.dart index f955d569..9391d36d 100644 --- a/lib/src/types/loaded_resource.g.dart +++ b/lib/src/types/loaded_resource.g.dart @@ -29,7 +29,7 @@ class LoadedResource { } final instance = LoadedResource( initiatorType: map['initiatorType'], - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, startTime: map['startTime'], duration: map['duration'], ); diff --git a/lib/src/types/permission_request.g.dart b/lib/src/types/permission_request.g.dart index f20d606c..bf5bf761 100644 --- a/lib/src/types/permission_request.g.dart +++ b/lib/src/types/permission_request.g.dart @@ -28,7 +28,7 @@ class PermissionRequest { return null; } final instance = PermissionRequest( - origin: Uri.parse(map['origin']), + origin: (Uri.tryParse(map['origin']) ?? Uri()), frame: FrameInfo.fromMap(map['frame']?.cast()), ); instance.resources = List.from(map['resources'] diff --git a/lib/src/types/print_job_attributes.g.dart b/lib/src/types/print_job_attributes.g.dart index e4218a70..cc76ed8b 100644 --- a/lib/src/types/print_job_attributes.g.dart +++ b/lib/src/types/print_job_attributes.g.dart @@ -273,8 +273,9 @@ class PrintJobAttributes { isVerticallyCentered: map['isVerticallyCentered'], isSelectionOnly: map['isSelectionOnly'], scalingFactor: map['scalingFactor'], - jobSavingURL: - map['jobSavingURL'] != null ? Uri.parse(map['jobSavingURL']) : null, + jobSavingURL: map['jobSavingURL'] != null + ? Uri.tryParse(map['jobSavingURL']) + : null, detailedErrorReporting: map['detailedErrorReporting'], faxNumber: map['faxNumber'], headerAndFooter: map['headerAndFooter'], diff --git a/lib/src/types/request_focus_node_href_result.g.dart b/lib/src/types/request_focus_node_href_result.g.dart index 146a280b..7434e0b8 100644 --- a/lib/src/types/request_focus_node_href_result.g.dart +++ b/lib/src/types/request_focus_node_href_result.g.dart @@ -24,7 +24,7 @@ class RequestFocusNodeHrefResult { return null; } final instance = RequestFocusNodeHrefResult( - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, title: map['title'], src: map['src'], ); diff --git a/lib/src/types/request_image_ref_result.g.dart b/lib/src/types/request_image_ref_result.g.dart index d97a93c2..57c217ef 100644 --- a/lib/src/types/request_image_ref_result.g.dart +++ b/lib/src/types/request_image_ref_result.g.dart @@ -18,7 +18,7 @@ class RequestImageRefResult { return null; } final instance = RequestImageRefResult( - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, ); return instance; } diff --git a/lib/src/types/url_request.g.dart b/lib/src/types/url_request.g.dart index e8548b48..62a72b50 100644 --- a/lib/src/types/url_request.g.dart +++ b/lib/src/types/url_request.g.dart @@ -186,7 +186,7 @@ class URLRequest { return null; } final instance = URLRequest( - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, method: map['method'], body: map['body'], headers: map['headers']?.cast(), @@ -210,10 +210,10 @@ class URLRequest { iosTimeoutInterval: map['timeoutInterval'], timeoutInterval: map['timeoutInterval'], iosMainDocumentURL: map['mainDocumentURL'] != null - ? Uri.parse(map['mainDocumentURL']) + ? Uri.tryParse(map['mainDocumentURL']) : null, mainDocumentURL: map['mainDocumentURL'] != null - ? Uri.parse(map['mainDocumentURL']) + ? Uri.tryParse(map['mainDocumentURL']) : null, assumesHTTP3Capable: map['assumesHTTP3Capable'], attribution: URLRequestAttribution.fromNativeValue(map['attribution']), diff --git a/lib/src/types/url_response.g.dart b/lib/src/types/url_response.g.dart index 57f4bf8b..5a6838c9 100644 --- a/lib/src/types/url_response.g.dart +++ b/lib/src/types/url_response.g.dart @@ -43,7 +43,7 @@ class URLResponse { return null; } final instance = URLResponse( - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, expectedContentLength: map['expectedContentLength'], mimeType: map['mimeType'], suggestedFilename: map['suggestedFilename'], @@ -116,7 +116,7 @@ class IOSURLResponse { return null; } final instance = IOSURLResponse( - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, expectedContentLength: map['expectedContentLength'], mimeType: map['mimeType'], suggestedFilename: map['suggestedFilename'], diff --git a/lib/src/types/web_history_item.g.dart b/lib/src/types/web_history_item.g.dart index 2fe82094..a7f3c97d 100644 --- a/lib/src/types/web_history_item.g.dart +++ b/lib/src/types/web_history_item.g.dart @@ -33,9 +33,9 @@ class WebHistoryItem { } final instance = WebHistoryItem( originalUrl: - map['originalUrl'] != null ? Uri.parse(map['originalUrl']) : null, + map['originalUrl'] != null ? Uri.tryParse(map['originalUrl']) : null, title: map['title'], - url: map['url'] != null ? Uri.parse(map['url']) : null, + url: map['url'] != null ? Uri.tryParse(map['url']) : null, index: map['index'], offset: map['offset'], ); diff --git a/lib/src/types/web_resource_request.g.dart b/lib/src/types/web_resource_request.g.dart index 3255ebc7..53872702 100644 --- a/lib/src/types/web_resource_request.g.dart +++ b/lib/src/types/web_resource_request.g.dart @@ -52,7 +52,7 @@ class WebResourceRequest { return null; } final instance = WebResourceRequest( - url: Uri.parse(map['url']), + url: (Uri.tryParse(map['url']) ?? Uri()), headers: map['headers']?.cast(), method: map['method'], hasGesture: map['hasGesture'], diff --git a/lib/src/types/web_resource_response.dart b/lib/src/types/web_resource_response.dart index 1eb32fee..4c3934a4 100644 --- a/lib/src/types/web_resource_response.dart +++ b/lib/src/types/web_resource_response.dart @@ -10,10 +10,10 @@ part 'web_resource_response.g.dart'; @ExchangeableObject() class WebResourceResponse_ { ///The resource response's MIME type, for example `text/html`. - String contentType; + String? contentType; ///The resource response's encoding. The default value is `utf-8`. - String contentEncoding; + String? contentEncoding; ///The data provided by the resource response. Uint8List? data; diff --git a/lib/src/types/web_resource_response.g.dart b/lib/src/types/web_resource_response.g.dart index 8a705351..d1c78a6b 100644 --- a/lib/src/types/web_resource_response.g.dart +++ b/lib/src/types/web_resource_response.g.dart @@ -9,10 +9,10 @@ part of 'web_resource_response.dart'; ///Class representing a resource response of the [WebView]. class WebResourceResponse { ///The resource response's MIME type, for example `text/html`. - String contentType; + String? contentType; ///The resource response's encoding. The default value is `utf-8`. - String contentEncoding; + String? contentEncoding; ///The data provided by the resource response. Uint8List? data; diff --git a/lib/src/web/in_app_web_view_web_element.dart b/lib/src/web/in_app_web_view_web_element.dart index 5068a705..d3f5b979 100644 --- a/lib/src/web/in_app_web_view_web_element.dart +++ b/lib/src/web/in_app_web_view_web_element.dart @@ -344,7 +344,7 @@ class InAppWebViewWebElement implements Disposable { {required String url, required Uint8List postData}) async { await loadUrl( urlRequest: - URLRequest(url: Uri.parse(url), method: "POST", body: postData)); + URLRequest(url: Uri.tryParse(url), method: "POST", body: postData)); } Future injectJavascriptFileFromUrl( diff --git a/lib/src/web_authentication_session/web_authenticate_session.dart b/lib/src/web_authentication_session/web_authenticate_session.dart index aa387549..6d17b69d 100755 --- a/lib/src/web_authentication_session/web_authenticate_session.dart +++ b/lib/src/web_authentication_session/web_authenticate_session.dart @@ -129,7 +129,7 @@ class WebAuthenticationSession implements Disposable { switch (call.method) { case "onComplete": String? url = call.arguments["url"]; - Uri? uri = url != null ? Uri.parse(url) : null; + Uri? uri = url != null ? Uri.tryParse(url) : null; var error = WebAuthenticationSessionError.fromNativeValue( call.arguments["errorCode"]); if (onComplete != null) { diff --git a/lib/src/web_message/web_message_listener.dart b/lib/src/web_message/web_message_listener.dart index 88690a7e..430370ca 100644 --- a/lib/src/web_message/web_message_listener.dart +++ b/lib/src/web_message/web_message_listener.dart @@ -60,7 +60,7 @@ class WebMessageListener { if (onPostMessage != null) { String? message = call.arguments["message"]; Uri? sourceOrigin = call.arguments["sourceOrigin"] != null - ? Uri.parse(call.arguments["sourceOrigin"]) + ? Uri.tryParse(call.arguments["sourceOrigin"]) : null; bool isMainFrame = call.arguments["isMainFrame"]; onPostMessage!(message, sourceOrigin, isMainFrame, _replyProxy!); diff --git a/pubspec.yaml b/pubspec.yaml index f3f59d36..610e5e40 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_inappwebview description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window. -version: 6.0.0-beta.5 +version: 6.0.0-beta.6 homepage: https://inappwebview.dev/ repository: https://github.com/pichillilorenzo/flutter_inappwebview issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues