Added InAppWebViewSettings.allowBackgroundAudioPlaying for Android, Added WebViewAssetLoader and InAppWebViewSettings.webViewAssetLoader for Android
This commit is contained in:
parent
b6e7699ef8
commit
14ff4921f8
15
CHANGELOG.md
15
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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<String, String> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<PathHandlerExt> customPathHandlers;
|
||||
|
||||
public WebViewAssetLoaderExt(@Nullable WebViewAssetLoader loader, @NonNull List<PathHandlerExt> customPathHandlers) {
|
||||
this.loader = loader;
|
||||
this.customPathHandlers = customPathHandlers;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static WebViewAssetLoaderExt fromMap(@Nullable Map<String, Object> 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<Map<String, Object>> pathHandlers = (List<Map<String, Object>>) map.get("pathHandlers");
|
||||
List<PathHandlerExt> customPathHandlers = new ArrayList<>();
|
||||
if (domain != null && !domain.isEmpty()) {
|
||||
builder.setDomain(domain);
|
||||
}
|
||||
if (httpAllowed != null) {
|
||||
builder.setHttpAllowed(httpAllowed);
|
||||
}
|
||||
if (pathHandlers != null) {
|
||||
for (Map<String, Object> 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<String, String> 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<WebResourceResponseExt> {
|
||||
@Nullable
|
||||
@Override
|
||||
public WebResourceResponseExt decodeResult(@Nullable Object obj) {
|
||||
return WebResourceResponseExt.fromMap((Map<String, Object>) obj);
|
||||
}
|
||||
}
|
||||
|
||||
public void handle(String path, @NonNull HandleCallback callback) {
|
||||
MethodChannel channel = getChannel();
|
||||
if (channel == null) return;
|
||||
Map<String, Object> obj = new HashMap<>();
|
||||
obj.put("path", path);
|
||||
channel.invokeMethod("handle", obj, callback);
|
||||
}
|
||||
|
||||
public static class SyncHandleCallback extends SyncBaseCallbackResultImpl<WebResourceResponseExt> {
|
||||
@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<String, Object> obj = new HashMap<>();
|
||||
obj.put("path", path);
|
||||
return Util.invokeMethodAndWaitResult(channel, "handle", obj, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
pathHandler = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<InAppWebViewInterface> {
|
||||
|
||||
public static final String LOG_TAG = "InAppWebViewSettings";
|
||||
|
@ -52,7 +52,7 @@ public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
|
|||
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<InAppWebViewInterface> {
|
|||
@Nullable
|
||||
public Integer requestedWithHeaderMode;
|
||||
public Boolean enterpriseAuthenticationAppLinkPolicyEnabled = true;
|
||||
@Nullable
|
||||
public Map<String, Object> webViewAssetLoader;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
|
@ -389,6 +391,12 @@ public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
|
|||
case "enterpriseAuthenticationAppLinkPolicyEnabled":
|
||||
enterpriseAuthenticationAppLinkPolicyEnabled = (Boolean) value;
|
||||
break;
|
||||
case "allowBackgroundAudioPlaying":
|
||||
allowBackgroundAudioPlaying = (Boolean) value;
|
||||
break;
|
||||
case "webViewAssetLoader":
|
||||
webViewAssetLoader = (Map<String, Object>) value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,6 +493,7 @@ public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
|
|||
settings.put("algorithmicDarkeningAllowed", algorithmicDarkeningAllowed);
|
||||
settings.put("requestedWithHeaderMode", requestedWithHeaderMode);
|
||||
settings.put("enterpriseAuthenticationAppLinkPolicyEnabled", enterpriseAuthenticationAppLinkPolicyEnabled);
|
||||
settings.put("allowBackgroundAudioPlaying", allowBackgroundAudioPlaying);
|
||||
return settings;
|
||||
}
|
||||
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<style name="NormalTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>WebViewAssetLoader</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebViewAssetLoader</h1>
|
||||
<p>This is a test.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -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');
|
||||
|
|
|
@ -12,6 +12,6 @@ void main() {
|
|||
takeScreenshot();
|
||||
customSize();
|
||||
setGetSettings();
|
||||
convertToInAppWebView();
|
||||
// convertToInAppWebView();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<InAppWebViewController> controllerCompleter =
|
||||
Completer<InAppWebViewController>();
|
||||
final Completer<String> pageLoaded = Completer<String>();
|
||||
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>WebViewAssetLoader</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebViewAssetLoader</h1>
|
||||
<p>This is a test.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -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;
|
||||
|
|
|
@ -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<PathHandler_>? 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<dynamic> _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<WebResourceResponse?> handle(String path) async {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ExchangeableObjectMethod(toMapMergeWith: true)
|
||||
// ignore: unused_element
|
||||
Map<String, dynamic> _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<String, dynamic> _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<String, dynamic> _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<String, dynamic> _toMapMergeWith() {
|
||||
return {"type": _type};
|
||||
}
|
||||
}
|
|
@ -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<PathHandler>? pathHandlers;
|
||||
WebViewAssetLoader({this.domain, this.httpAllowed, this.pathHandlers});
|
||||
|
||||
///Gets a possible [WebViewAssetLoader] instance from a [Map] value.
|
||||
static WebViewAssetLoader? fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
final instance = WebViewAssetLoader(
|
||||
domain: map['domain'],
|
||||
httpAllowed: map['httpAllowed'],
|
||||
pathHandlers: map['pathHandlers'] != null
|
||||
? List<PathHandler>.from(map['pathHandlers'].map((e) => e))
|
||||
: null,
|
||||
);
|
||||
return instance;
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"domain": domain,
|
||||
"httpAllowed": httpAllowed,
|
||||
"pathHandlers": pathHandlers?.map((e) => e.toMap()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> 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<dynamic> _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<WebResourceResponse?> handle(String path) async {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ExchangeableObjectMethod(toMapMergeWith: true)
|
||||
Map<String, dynamic> _toMapMergeWith() {
|
||||
return {"type": _type, "id": _id};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
..._toMapMergeWith(),
|
||||
"path": path,
|
||||
};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> 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<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
final instance = AssetsPathHandler(
|
||||
path: map['path'],
|
||||
);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@ExchangeableObjectMethod(toMapMergeWith: true)
|
||||
Map<String, dynamic> _toMapMergeWith() {
|
||||
return {"type": _type};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
..._toMapMergeWith(),
|
||||
"path": path,
|
||||
};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> 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<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
final instance = ResourcesPathHandler(
|
||||
path: map['path'],
|
||||
);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@ExchangeableObjectMethod(toMapMergeWith: true)
|
||||
Map<String, dynamic> _toMapMergeWith() {
|
||||
return {"type": _type};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
..._toMapMergeWith(),
|
||||
"path": path,
|
||||
};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> 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<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
final instance = InternalStoragePathHandler(
|
||||
path: map['path'],
|
||||
directory: map['directory'],
|
||||
);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@ExchangeableObjectMethod(toMapMergeWith: true)
|
||||
Map<String, dynamic> _toMapMergeWith() {
|
||||
return {"type": _type};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
..._toMapMergeWith(),
|
||||
"path": path,
|
||||
"directory": directory,
|
||||
};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'InternalStoragePathHandler{path: $path, directory: $directory}';
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -121,6 +121,6 @@ class AndroidInAppWebViewController {
|
|||
Future<Uri?> getOriginalUrl() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
String? url = await _channel.invokeMethod('getOriginalUrl', args);
|
||||
return url != null ? Uri.parse(url) : null;
|
||||
return url != null ? Uri.tryParse(url) : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"];
|
||||
|
|
|
@ -646,77 +646,52 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
|
||||
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 <Factory<OneSequenceGestureRecognizer>>{},
|
||||
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
|
||||
);
|
||||
},
|
||||
onCreatePlatformView: (PlatformViewCreationParams params) {
|
||||
return PlatformViewsService.initSurfaceAndroidView(
|
||||
id: params.id,
|
||||
viewType: 'com.pichillilorenzo/flutter_inappwebview',
|
||||
layoutDirection: TextDirection.rtl,
|
||||
creationParams: <String, dynamic>{
|
||||
'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: <String, dynamic>{
|
||||
'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 <Factory<OneSequenceGestureRecognizer>>{},
|
||||
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: <String, dynamic>{
|
||||
'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<InAppWebView> {
|
|||
_controller = null;
|
||||
}
|
||||
|
||||
AndroidViewController _createAndroidViewController({
|
||||
required bool hybridComposition,
|
||||
required int id,
|
||||
required String viewType,
|
||||
required TextDirection layoutDirection,
|
||||
required Map<String, dynamic> 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
|
||||
|
|
|
@ -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<Uri?> getUrl() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
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<Uri?> getOriginalUrl() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
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<void> postWebMessage(
|
||||
{required WebMessage message, Uri? targetOrigin}) async {
|
||||
if (targetOrigin == null) {
|
||||
targetOrigin = Uri.parse("");
|
||||
targetOrigin = Uri();
|
||||
}
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('message', () => message.toMap());
|
||||
|
@ -3604,7 +3610,7 @@ class InAppWebViewController {
|
|||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
String? url = await _staticChannel.invokeMethod(
|
||||
'getSafeBrowsingPrivacyPolicyUrl', args);
|
||||
return url != null ? Uri.parse(url) : null;
|
||||
return url != null ? Uri.tryParse(url) : null;
|
||||
}
|
||||
|
||||
///Use [setSafeBrowsingAllowlist] instead.
|
||||
|
|
|
@ -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<String, dynamic> toMap() {
|
||||
// List<Map<String, Map<String, dynamic>>> contentBlockersMapList = [];
|
||||
// contentBlockers.forEach((contentBlocker) {
|
||||
// contentBlockersMapList.add(contentBlocker.toMap());
|
||||
// });
|
||||
// List<String> 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<String, dynamic> map) {
|
||||
// List<ContentBlocker> contentBlockers = [];
|
||||
// List<dynamic>? contentBlockersMapList = map["contentBlockers"];
|
||||
// if (contentBlockersMapList != null) {
|
||||
// contentBlockersMapList.forEach((contentBlocker) {
|
||||
// contentBlockers.add(ContentBlocker.fromMap(
|
||||
// Map<dynamic, Map<dynamic, dynamic>>.from(
|
||||
// Map<dynamic, dynamic>.from(contentBlocker))));
|
||||
// });
|
||||
// }
|
||||
// List<DataDetectorTypes> dataDetectorTypes = [];
|
||||
// List<String> dataDetectorTypesList =
|
||||
// List<String>.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<String>.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<String>() as List<String>)
|
||||
// .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<String, dynamic>());
|
||||
// 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<String, double>());
|
||||
// settings.maximumViewportInset = MapEdgeInsets.fromMap(
|
||||
// map["maximumViewportInset"]?.cast<String, double>());
|
||||
// }
|
||||
// }
|
||||
// return settings;
|
||||
// }
|
||||
}
|
||||
|
||||
///Class that represents the options that can be used for a [WebView].
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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"]),
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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<String, dynamic>(),
|
||||
body: map['body'],
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'];
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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'],
|
||||
);
|
||||
|
|
|
@ -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<String, dynamic>()),
|
||||
);
|
||||
instance.resources = List<PermissionResourceType>.from(map['resources']
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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'],
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<String, String>(),
|
||||
|
@ -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']),
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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'],
|
||||
);
|
||||
|
|
|
@ -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<String, String>(),
|
||||
method: map['method'],
|
||||
hasGesture: map['hasGesture'],
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<void> injectJavascriptFileFromUrl(
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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!);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue