added new Android webview events and options, other bug fixes

This commit is contained in:
Lorenzo Pichilli 2020-05-26 00:26:32 +02:00
parent 3731ef5f52
commit 682c6e2243
18 changed files with 626 additions and 157 deletions

View File

@ -5,10 +5,19 @@
- Added Android keyboard workaround to hide the keyboard when clicking other HTML elements, losing the focus on the previous input
- Added `onEnterFullscreen`, `onExitFullscreen` webview events [#275](https://github.com/pichillilorenzo/flutter_inappwebview/issues/275)
- Added Android support to use camera on HTML inputs that requires it, such as `<input type="file" accept="image/*" capture>` [#353](https://github.com/pichillilorenzo/flutter_inappwebview/issues/353)
- Added `overScrollMode`, `networkAvailable`, `scrollBarStyle`, `verticalScrollbarPosition`, `scrollBarDefaultDelayBeforeFade`, `scrollbarFadingEnabled`, `scrollBarFadeDuration`, `rendererPriorityPolicy` webview options on Android
- Added `pageDown`, `pageUp`, `saveWebArchive`, `zoomIn`, `zoomOut` webview methods on Android
- Added `getCurrentWebViewPackage` static webview method on Android
- Added `shouldInterceptRequest`, `onRenderProcessUnresponsive`, `onRenderProcessResponsive`, `onRenderProcessGone`, `onFormResubmission`, `onPageCommitVisible`, `onScaleChanged` Android events
- Fixed `Print preview is not working? java.lang.IllegalStateException: Can print only from an activity` [#128](https://github.com/pichillilorenzo/flutter_inappwebview/issues/128)
- Fixed `onJsAlert`, `onJsConfirm`, `onJsPrompt` for `InAppBrowser` on Android
- Fixed `onActivityResult` for `InAppBrowser` on Android
- Fixed `InAppBrowser.openWithSystemBrowser crash on iOS` [#358](https://github.com/pichillilorenzo/flutter_inappwebview/issues/358)
### BREAKING CHANGES
- Android `clearClientCertPreferences`, `getSafeBrowsingPrivacyPolicyUrl`, `setSafeBrowsingWhitelist` webview methods are static now
## 3.2.0
- Added `ContextMenu` and `ContextMenuItem` classes [#235](https://github.com/pichillilorenzo/flutter_inappwebview/issues/235)

View File

@ -142,7 +142,7 @@ On **Android**, you need to add some additional configurations.
Add the following codes inside the `<application>` tag of your `android/app/src/main/AndroidManifest.xml`:
```xml
<provider
android:name="androidx.core.content.FileProvider"
android:name="com.pichillilorenzo.flutter_inappwebview.InAppWebViewFileProvider"
android:authorities="${applicationId}.flutter_inappwebview.fileprovider"
android:exported="false"
android:grantUriPermissions="true">

View File

@ -45,8 +45,8 @@ android {
}
}
dependencies {
implementation 'androidx.webkit:webkit:1.0.0'
implementation 'androidx.browser:browser:1.0.0'
implementation 'androidx.webkit:webkit:1.2.0'
implementation 'androidx.browser:browser:1.2.0'
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.squareup.okhttp3:mockwebserver:3.11.0'
}

View File

@ -14,6 +14,7 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@ -23,6 +24,8 @@ import android.widget.SearchView;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions;
@ -31,6 +34,7 @@ import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -52,6 +56,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
public ProgressBar progressBar;
public boolean isHidden = false;
public String fromActivity;
public List<ActivityResultListener> activityResultListeners = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -256,9 +261,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
case "startSafeBrowsing":
startSafeBrowsing(result);
break;
case "setSafeBrowsingWhitelist":
setSafeBrowsingWhitelist((List<String>) call.argument("hosts"), result);
break;
case "clearCache":
clearCache();
result.success(true);
@ -267,9 +269,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
clearSslPreferences();
result.success(true);
break;
case "clearClientCertPreferences":
clearClientCertPreferences(result);
break;
case "findAllAsync":
String find = (String) call.argument("find");
findAllAsync(find);
@ -726,18 +725,19 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
return null;
}
public void startSafeBrowsing(MethodChannel.Result result) {
if (webView != null)
webView.startSafeBrowsing(result);
else
public void startSafeBrowsing(final MethodChannel.Result result) {
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 &&
WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
WebViewCompat.startSafeBrowsing(webView.getContext(), new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean success) {
result.success(success);
}
});
}
else {
result.success(false);
}
public void setSafeBrowsingWhitelist(List<String> hosts, MethodChannel.Result result) {
if (webView != null)
webView.setSafeBrowsingWhitelist(hosts, result);
else
result.success(false);
}
public void clearCache() {
@ -750,19 +750,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
webView.clearSslPreferences();
}
public void clearClientCertPreferences(final MethodChannel.Result result) {
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.clearClientCertPreferences(new Runnable() {
@Override
public void run() {
result.success(true);
}
});
}
else
result.success(false);
}
public void findAllAsync(String find) {
if (webView != null)
webView.findAllAsync(find);
@ -867,7 +854,11 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
public void dispose() {
channel.setMethodCallHandler(null);
activityResultListeners.clear();
if (webView != null) {
if (Shared.activityPluginBinding != null) {
Shared.activityPluginBinding.removeActivityResultListener(webView.inAppWebViewChromeClient);
}
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
@ -880,9 +871,26 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
}
}
@Override
protected void onActivityResult (int requestCode,
int resultCode,
Intent data) {
for (ActivityResultListener listener : activityResultListeners) {
if (listener.onActivityResult(requestCode, resultCode, data)) {
return;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onDestroy() {
dispose();
super.onDestroy();
}
public interface ActivityResultListener {
/** @return true if the result has been handled. */
boolean onActivityResult(int requestCode, int resultCode, Intent data);
}
}

View File

@ -7,11 +7,15 @@ import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util;
@ -31,7 +35,7 @@ import static io.flutter.plugin.common.MethodChannel.Result;
public class FlutterWebView implements PlatformView, MethodCallHandler {
static final String LOG_TAG = "FlutterWebView";
static final String LOG_TAG = "IAWFlutterWebView";
public InAppWebView webView;
public final MethodChannel channel;
@ -253,24 +257,18 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
result.success((webView != null) ? webView.getCopyBackForwardList() : null);
break;
case "startSafeBrowsing":
if (webView != null)
webView.startSafeBrowsing(result);
else
result.success(false);
break;
case "setSafeBrowsingWhitelist":
if (webView != null) {
List<String> hosts = (List<String>) call.argument("hosts");
webView.setSafeBrowsingWhitelist(hosts, result);
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 &&
WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
WebViewCompat.startSafeBrowsing(webView.getContext(), new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean success) {
result.success(success);
}
else
});
}
else {
result.success(false);
break;
case "getSafeBrowsingPrivacyPolicyUrl":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
result.success(webView.getSafeBrowsingPrivacyPolicyUrl().toString());
} else
result.success(null);
}
break;
case "clearCache":
if (webView != null)
@ -282,18 +280,6 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
webView.clearSslPreferences();
result.success(true);
break;
case "clearClientCertPreferences":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.clearClientCertPreferences(new Runnable() {
@Override
public void run() {
result.success(true);
}
});
} else {
result.success(false);
}
break;
case "findAllAsync":
if (webView != null) {
String find = (String) call.argument("find");
@ -379,7 +365,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
break;
case "zoomBy":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Float zoomFactor = (Float) call.argument("zoomFactor");
float zoomFactor = (float) call.argument("zoomFactor");
webView.zoomBy(zoomFactor);
result.success(true);
} else {
@ -410,6 +396,53 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
result.success(null);
}
break;
case "pageDown":
if (webView != null) {
boolean bottom = (boolean) call.argument("bottom");
result.success(webView.pageDown(bottom));
} else {
result.success(false);
}
break;
case "pageUp":
if (webView != null) {
boolean top = (boolean) call.argument("top");
result.success(webView.pageUp(top));
} else {
result.success(false);
}
break;
case "saveWebArchive":
if (webView != null) {
String basename = (String) call.argument("basename");
boolean autoname = (boolean) call.argument("autoname");
webView.saveWebArchive(basename, autoname, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
result.success(value);
}
});
} else {
result.success(null);
}
break;
case "zoomIn":
if (webView != null) {
result.success(webView.zoomIn());
} else {
result.success(false);
}
break;
case "zoomOut":
if (webView != null) {
result.success(webView.zoomOut());
} else {
result.success(false);
}
break;
default:
result.notImplemented();
}
@ -419,6 +452,9 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
public void dispose() {
channel.setMethodCallHandler(null);
if (webView != null) {
if (Shared.activityPluginBinding != null) {
Shared.activityPluginBinding.removeActivityResultListener(webView.inAppWebViewChromeClient);
}
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {

View File

@ -1,6 +1,7 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@ -35,6 +36,8 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlocker;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerAction;
@ -69,6 +72,7 @@ final public class InAppWebView extends InputAwareWebView {
public Object id;
public InAppWebViewClient inAppWebViewClient;
public InAppWebViewChromeClient inAppWebViewChromeClient;
public InAppWebViewRenderProcessClient inAppWebViewRenderProcessClient;
public InAppWebViewOptions options;
public boolean isLoading = false;
public OkHttpClient httpClient;
@ -636,6 +640,11 @@ final public class InAppWebView extends InputAwareWebView {
inAppWebViewClient = new InAppWebViewClient((isFromInAppBrowserActivity) ? inAppBrowserActivity : flutterWebView);
setWebViewClient(inAppWebViewClient);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE)) {
inAppWebViewRenderProcessClient = new InAppWebViewRenderProcessClient((isFromInAppBrowserActivity) ? inAppBrowserActivity : flutterWebView);
WebViewCompat.setWebViewRenderProcessClient(this, inAppWebViewRenderProcessClient);
}
if (options.useOnDownloadStart)
setDownloadListener(new DownloadStartListener());
@ -745,6 +754,33 @@ final public class InAppWebView extends InputAwareWebView {
if (options.regexToCancelSubFramesLoading != null) {
regexToCancelSubFramesLoadingCompiled = Pattern.compile(options.regexToCancelSubFramesLoading);
}
setScrollBarStyle(options.scrollBarStyle);
if (options.scrollBarDefaultDelayBeforeFade != null) {
setScrollBarDefaultDelayBeforeFade(options.scrollBarDefaultDelayBeforeFade);
} else {
options.scrollBarDefaultDelayBeforeFade = getScrollBarDefaultDelayBeforeFade();
}
setScrollbarFadingEnabled(options.scrollbarFadingEnabled);
if (options.scrollBarFadeDuration != null) {
setScrollBarFadeDuration(options.scrollBarFadeDuration);
} else {
options.scrollBarFadeDuration = getScrollBarFadeDuration();
}
setVerticalScrollbarPosition(options.verticalScrollbarPosition);
setVerticalScrollBarEnabled(!options.disableVerticalScroll);
setHorizontalScrollBarEnabled(!options.disableHorizontalScroll);
setOverScrollMode(options.overScrollMode);
if (options.networkAvailable != null) {
setNetworkAvailable(options.networkAvailable);
}
if (options.rendererPriorityPolicy != null && !options.rendererPriorityPolicy.isEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setRendererPriorityPolicy(
(int) options.rendererPriorityPolicy.get("rendererRequestedPriority"),
(boolean) options.rendererPriorityPolicy.get("waivedWhenNotVisible"));
} else if (options.rendererPriorityPolicy == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
options.rendererPriorityPolicy.put("rendererRequestedPriority", getRendererRequestedPriority());
options.rendererPriorityPolicy.put("waivedWhenNotVisible", getRendererPriorityWaivedWhenNotVisible());
}
contentBlockerHandler.getRuleList().clear();
for (Map<String, Map<String, Object>> contentBlocker : options.contentBlockers) {
@ -767,9 +803,6 @@ final public class InAppWebView extends InputAwareWebView {
}
});
setVerticalScrollBarEnabled(!options.disableVerticalScroll);
setHorizontalScrollBarEnabled(!options.disableHorizontalScroll);
gestureDetector = new GestureDetector(this.getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent ev) {
@ -1300,12 +1333,42 @@ final public class InAppWebView extends InputAwareWebView {
}
}
if (newOptionsMap.get("scrollBarStyle") != null && !options.scrollBarStyle.equals(newOptions.scrollBarStyle))
setScrollBarStyle(newOptions.scrollBarStyle);
if (newOptionsMap.get("scrollBarDefaultDelayBeforeFade") != null && !options.scrollBarDefaultDelayBeforeFade.equals(newOptions.scrollBarDefaultDelayBeforeFade))
setScrollBarDefaultDelayBeforeFade(newOptions.scrollBarDefaultDelayBeforeFade);
if (newOptionsMap.get("scrollbarFadingEnabled") != null && !options.scrollbarFadingEnabled.equals(newOptions.scrollbarFadingEnabled))
setScrollbarFadingEnabled(newOptions.scrollbarFadingEnabled);
if (newOptionsMap.get("scrollBarFadeDuration") != null && !options.scrollBarFadeDuration.equals(newOptions.scrollBarFadeDuration))
setScrollBarFadeDuration(newOptions.scrollBarFadeDuration);
if (newOptionsMap.get("verticalScrollbarPosition") != null && !options.verticalScrollbarPosition.equals(newOptions.verticalScrollbarPosition))
setVerticalScrollbarPosition(newOptions.verticalScrollbarPosition);
if (newOptionsMap.get("disableVerticalScroll") != null && options.disableVerticalScroll != newOptions.disableVerticalScroll)
setVerticalScrollBarEnabled(!newOptions.disableVerticalScroll);
if (newOptionsMap.get("disableHorizontalScroll") != null && options.disableHorizontalScroll != newOptions.disableHorizontalScroll)
setHorizontalScrollBarEnabled(!newOptions.disableHorizontalScroll);
if (newOptionsMap.get("overScrollMode") != null && !options.overScrollMode.equals(newOptions.overScrollMode))
setOverScrollMode(newOptions.overScrollMode);
if (newOptionsMap.get("networkAvailable") != null && options.networkAvailable != newOptions.networkAvailable)
setNetworkAvailable(newOptions.networkAvailable);
if (newOptionsMap.get("rendererPriorityPolicy") != null &&
(options.rendererPriorityPolicy.get("rendererRequestedPriority") != newOptions.rendererPriorityPolicy.get("rendererRequestedPriority") ||
options.rendererPriorityPolicy.get("waivedWhenNotVisible") != newOptions.rendererPriorityPolicy.get("waivedWhenNotVisible")) &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setRendererPriorityPolicy(
(int) newOptions.rendererPriorityPolicy.get("rendererRequestedPriority"),
(boolean) newOptions.rendererPriorityPolicy.get("waivedWhenNotVisible"));
}
options = newOptions;
}
@ -1412,32 +1475,6 @@ final public class InAppWebView extends InputAwareWebView {
channel.invokeMethod("onScrollChanged", obj);
}
public void startSafeBrowsing(final MethodChannel.Result result) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
startSafeBrowsing(getContext(), new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean value) {
result.success(value);
}
});
} else {
result.success(false);
}
}
public void setSafeBrowsingWhitelist(List<String> hosts, final MethodChannel.Result result) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setSafeBrowsingWhitelist(hosts, new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean value) {
result.success(value);
}
});
} else {
result.success(false);
}
}
class DownloadStartListener implements DownloadListener {
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {

View File

@ -58,7 +58,7 @@ import io.flutter.plugin.common.PluginRegistry;
import static android.app.Activity.RESULT_OK;
public class InAppWebViewChromeClient extends WebChromeClient implements PluginRegistry.ActivityResultListener {
public class InAppWebViewChromeClient extends WebChromeClient implements PluginRegistry.ActivityResultListener, InAppBrowserActivity.ActivityResultListener {
protected static final String LOG_TAG = "IABWebChromeClient";
private FlutterWebView flutterWebView;
@ -96,10 +96,13 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
private int mOriginalSystemUiVisibility;
public InAppWebViewChromeClient(Object obj) {
if (obj instanceof InAppBrowserActivity)
if (obj instanceof InAppBrowserActivity) {
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
else if (obj instanceof FlutterWebView)
this.inAppBrowserActivity.activityResultListeners.add(this);
}
else if (obj instanceof FlutterWebView) {
this.flutterWebView = (FlutterWebView) obj;
}
this.channel = (this.inAppBrowserActivity != null) ? this.inAppBrowserActivity.channel : this.flutterWebView.channel;
if (Shared.registrar != null)

View File

@ -6,12 +6,14 @@ import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.ClientCertRequest;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.HttpAuthHandler;
import android.webkit.RenderProcessGoneDetail;
import android.webkit.SafeBrowsingResponse;
import android.webkit.SslErrorHandler;
import android.webkit.ValueCallback;
@ -20,15 +22,20 @@ import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.Credential;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.CredentialDatabase;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
@ -47,7 +54,7 @@ import io.flutter.plugin.common.MethodChannel;
public class InAppWebViewClient extends WebViewClient {
protected static final String LOG_TAG = "IABWebViewClient";
protected static final String LOG_TAG = "IAWebViewClient";
private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity;
public MethodChannel channel;
@ -358,7 +365,7 @@ public class InAppWebViewClient extends WebViewClient {
}
}
handler.cancel();
InAppWebViewClient.super.onReceivedHttpAuthRequest(view, handler, host, realm);
}
@Override
@ -368,7 +375,7 @@ public class InAppWebViewClient extends WebViewClient {
@Override
public void notImplemented() {
handler.cancel();
InAppWebViewClient.super.onReceivedHttpAuthRequest(view, handler, host, realm);
}
});
}
@ -483,7 +490,7 @@ public class InAppWebViewClient extends WebViewClient {
}
}
handler.cancel();
InAppWebViewClient.super.onReceivedSslError(view, handler, error);
}
@Override
@ -493,7 +500,7 @@ public class InAppWebViewClient extends WebViewClient {
@Override
public void notImplemented() {
handler.cancel();
InAppWebViewClient.super.onReceivedSslError(view, handler, error);
}
});
}
@ -552,7 +559,7 @@ public class InAppWebViewClient extends WebViewClient {
}
}
request.cancel();
InAppWebViewClient.super.onReceivedClientCertRequest(view, request);
}
@Override
@ -562,20 +569,28 @@ public class InAppWebViewClient extends WebViewClient {
@Override
public void notImplemented() {
request.cancel();
InAppWebViewClient.super.onReceivedClientCertRequest(view, request);
}
});
}
@Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
super.onScaleChanged(view, oldScale, newScale);
final InAppWebView webView = (InAppWebView) view;
webView.scale = newScale;
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("oldScale", oldScale);
obj.put("newScale", newScale);
channel.invokeMethod("onScaleChanged", obj);
}
@RequiresApi(api = Build.VERSION_CODES.O_MR1)
@Override
public void onSafeBrowsingHit(final WebView view, WebResourceRequest request, int threatType, final SafeBrowsingResponse callback) {
public void onSafeBrowsingHit(final WebView view, final WebResourceRequest request, final int threatType, final SafeBrowsingResponse callback) {
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
@ -608,7 +623,7 @@ public class InAppWebViewClient extends WebViewClient {
}
}
callback.showInterstitial(true);
InAppWebViewClient.super.onSafeBrowsingHit(view, request, threatType, callback);
}
@Override
@ -618,7 +633,7 @@ public class InAppWebViewClient extends WebViewClient {
@Override
public void notImplemented() {
callback.showInterstitial(true);
InAppWebViewClient.super.onSafeBrowsingHit(view, request, threatType, callback);
}
});
}
@ -628,6 +643,13 @@ public class InAppWebViewClient extends WebViewClient {
final InAppWebView webView = (InAppWebView) view;
if (webView.options.useShouldInterceptRequest) {
WebResourceResponse onShouldInterceptResponse = onShouldInterceptRequest(url);
if (onShouldInterceptResponse != null) {
return onShouldInterceptResponse;
}
}
URI uri;
try {
uri = new URI(url);
@ -696,10 +718,137 @@ public class InAppWebViewClient extends WebViewClient {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
final InAppWebView webView = (InAppWebView) view;
String url = request.getUrl().toString();
if (webView.options.useShouldInterceptRequest) {
WebResourceResponse onShouldInterceptResponse = onShouldInterceptRequest(request);
if (onShouldInterceptResponse != null) {
return onShouldInterceptResponse;
}
}
return shouldInterceptRequest(view, url);
}
public WebResourceResponse onShouldInterceptRequest(Object request) {
String url = request instanceof String ? (String) request : null;
String method = "GET";
Map<String, String> headers = null;
Boolean hasGesture = false;
Boolean isForMainFrame = true;
Boolean isRedirect = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && request instanceof WebResourceRequest) {
WebResourceRequest webResourceRequest = (WebResourceRequest) request;
url = webResourceRequest.getUrl().toString();
headers = webResourceRequest.getRequestHeaders();
hasGesture = webResourceRequest.hasGesture();
isForMainFrame = webResourceRequest.isForMainFrame();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
isRedirect = webResourceRequest.isRedirect();
}
}
final Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url);
obj.put("method", method);
obj.put("headers", headers);
obj.put("isForMainFrame", isForMainFrame);
obj.put("hasGesture", hasGesture);
obj.put("isRedirect", isRedirect);
Util.WaitFlutterResult flutterResult;
try {
flutterResult = Util.invokeMethodAndWait(channel, "shouldInterceptRequest", obj);
} catch (InterruptedException e) {
e.printStackTrace();
Log.e(LOG_TAG, e.getMessage());
return null;
}
if (flutterResult.error != null) {
Log.e(LOG_TAG, flutterResult.error);
}
else if (flutterResult.result != null) {
Map<String, Object> res = (Map<String, Object>) flutterResult.result;
byte[] data = (byte[]) res.get("data");
return new WebResourceResponse(res.get("content-type").toString(), res.get("content-encoding").toString(), new ByteArrayInputStream(data));
}
return null;
}
@Override
public void onFormResubmission (final WebView view, final Message dontResend, final Message resend) {
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", view.getUrl());
channel.invokeMethod("onFormResubmission", obj, new MethodChannel.Result() {
@Override
public void success(@Nullable Object response) {
Map<String, Object> responseMap = (Map<String, Object>) response;
Integer action = (Integer) responseMap.get("action");
if (action != null) {
switch (action) {
case 1:
resend.sendToTarget();
return;
case 0:
default:
dontResend.sendToTarget();
return;
}
}
InAppWebViewClient.super.onFormResubmission(view, dontResend, resend);
}
@Override
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
Log.d(LOG_TAG, "ERROR: " + errorCode + " " + errorMessage);
}
@Override
public void notImplemented() {
InAppWebViewClient.super.onFormResubmission(view, dontResend, resend);
}
});
}
@Override
public void onPageCommitVisible(WebView view, String url) {
super.onPageCommitVisible(view, url);
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url);
channel.invokeMethod("onPageCommitVisible", obj);
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
Boolean didCrash = detail.didCrash();
Integer rendererPriorityAtExit = detail.rendererPriorityAtExit();
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("didCrash", didCrash);
obj.put("rendererPriorityAtExit", rendererPriorityAtExit);
channel.invokeMethod("onRenderProcessGone", obj);
return super.onRenderProcessGone(view, detail);
}
@Override
public void onUnhandledKeyEvent(WebView view, KeyEvent event) {

View File

@ -2,6 +2,7 @@ package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.webkit.WebSettings;
import com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ChromeCustomTabsOptions;
@ -87,6 +88,16 @@ public class InAppWebViewOptions implements Options {
public Boolean supportMultipleWindows = false;
public String regexToCancelSubFramesLoading;
public Integer overScrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS;
public Boolean networkAvailable = null;
public Integer scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY;
public Integer verticalScrollbarPosition = View.SCROLLBAR_POSITION_DEFAULT;
public Integer scrollBarDefaultDelayBeforeFade = null;
public Boolean scrollbarFadingEnabled = true;
public Integer scrollBarFadeDuration = null;
public Map<String, Object> rendererPriorityPolicy = new HashMap<>();
public Boolean useShouldInterceptRequest = false;
@Override
public InAppWebViewOptions parse(HashMap<String, Object> options) {
for (Map.Entry<String, Object> pair : options.entrySet()) {
@ -295,6 +306,33 @@ public class InAppWebViewOptions implements Options {
case "regexToCancelSubFramesLoading":
regexToCancelSubFramesLoading = (String) value;
break;
case "overScrollMode":
overScrollMode = (Integer) value;
break;
case "networkAvailable":
networkAvailable = (Boolean) value;
break;
case "scrollBarStyle":
scrollBarStyle = (Integer) value;
break;
case "verticalScrollbarPosition":
verticalScrollbarPosition = (Integer) value;
break;
case "scrollBarDefaultDelayBeforeFade":
scrollBarDefaultDelayBeforeFade = (Integer) value;
break;
case "scrollbarFadingEnabled":
scrollbarFadingEnabled = (Boolean) value;
break;
case "scrollBarFadeDuration":
scrollBarFadeDuration = (Integer) value;
break;
case "rendererPriorityPolicy":
rendererPriorityPolicy = (Map<String, Object>) value;
break;
case "useShouldInterceptRequest":
useShouldInterceptRequest = (Boolean) value;
break;
}
}
@ -370,6 +408,15 @@ public class InAppWebViewOptions implements Options {
options.put("hardwareAcceleration", hardwareAcceleration);
options.put("supportMultipleWindows", supportMultipleWindows);
options.put("regexToCancelSubFramesLoading", regexToCancelSubFramesLoading);
options.put("overScrollMode", overScrollMode);
options.put("networkAvailable", networkAvailable);
options.put("scrollBarStyle", scrollBarStyle);
options.put("verticalScrollbarPosition", verticalScrollbarPosition);
options.put("scrollBarDefaultDelayBeforeFade", scrollBarDefaultDelayBeforeFade);
options.put("scrollbarFadingEnabled", scrollbarFadingEnabled);
options.put("scrollBarFadeDuration", scrollBarFadeDuration);
options.put("rendererPriorityPolicy", rendererPriorityPolicy);
options.put("useShouldInterceptRequest", useShouldInterceptRequest);
return options;
}

View File

@ -0,0 +1,100 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.util.Log;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.webkit.WebViewFeature;
import androidx.webkit.WebViewRenderProcess;
import androidx.webkit.WebViewRenderProcessClient;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.MethodChannel;
public class InAppWebViewRenderProcessClient extends WebViewRenderProcessClient {
protected static final String LOG_TAG = "IAWRenderProcessClient";
private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity;
public MethodChannel channel;
public InAppWebViewRenderProcessClient(Object obj) {
super();
if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
else if (obj instanceof FlutterWebView)
this.flutterWebView = (FlutterWebView) obj;
this.channel = (this.inAppBrowserActivity != null) ? this.inAppBrowserActivity.channel : this.flutterWebView.channel;
}
@Override
public void onRenderProcessUnresponsive(@NonNull WebView view, @Nullable final WebViewRenderProcess renderer) {
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
channel.invokeMethod("onRenderProcessUnresponsive", obj, new MethodChannel.Result() {
@Override
public void success(@Nullable Object response) {
Map<String, Object> responseMap = (Map<String, Object>) response;
Integer action = (Integer) responseMap.get("action");
if (action != null && renderer != null) {
switch (action) {
case 1:
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_TERMINATE))
renderer.terminate();
break;
}
}
}
@Override
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
Log.d(LOG_TAG, "ERROR: " + errorCode + " " + errorMessage);
}
@Override
public void notImplemented() {
}
});
}
@Override
public void onRenderProcessResponsive(@NonNull WebView view, @Nullable final WebViewRenderProcess renderer) {
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
channel.invokeMethod("onRenderProcessResponsive", obj, new MethodChannel.Result() {
@Override
public void success(@Nullable Object response) {
Map<String, Object> responseMap = (Map<String, Object>) response;
Integer action = (Integer) responseMap.get("action");
if (action != null && renderer != null) {
switch (action) {
case 1:
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_TERMINATE))
renderer.terminate();
break;
}
}
}
@Override
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
Log.d(LOG_TAG, "ERROR: " + errorCode + " " + errorMessage);
}
@Override
public void notImplemented() {
}
});
}
}

View File

@ -0,0 +1,9 @@
package com.pichillilorenzo.flutter_inappwebview;
import androidx.core.content.FileProvider;
public class InAppWebViewFileProvider extends FileProvider {
// This class intentionally left blank.
}

View File

@ -1,6 +1,17 @@
package com.pichillilorenzo.flutter_inappwebview;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.webkit.ValueCallback;
import android.webkit.WebSettings;
import android.webkit.WebView;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
@ -17,16 +28,72 @@ public class InAppWebViewStatic implements MethodChannel.MethodCallHandler {
}
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
public void onMethodCall(MethodCall call, final MethodChannel.Result result) {
switch (call.method) {
case "getDefaultUserAgent":
result.success(WebSettings.getDefaultUserAgent(Shared.applicationContext));
break;
case "clearClientCertPreferences":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
WebView.clearClientCertPreferences(new Runnable() {
@Override
public void run() {
result.success(true);
}
});
} else {
result.success(false);
}
break;
case "getSafeBrowsingPrivacyPolicyUrl":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 && WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL)) {
result.success(WebViewCompat.getSafeBrowsingPrivacyPolicyUrl().toString());
} else
result.success(null);
break;
case "setSafeBrowsingWhitelist":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 && WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_WHITELIST)) {
List<String> hosts = (List<String>) call.argument("hosts");
WebViewCompat.setSafeBrowsingWhitelist(hosts, new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean value) {
result.success(value);
}
});
}
else
result.success(false);
break;
case "getCurrentWebViewPackage":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
result.success(
convertWebViewPackageToMap(WebViewCompat.getCurrentWebViewPackage(Shared.activity)));
} else {
result.success(false);
}
break;
default:
result.notImplemented();
}
}
public Map<String, Object> convertWebViewPackageToMap(PackageInfo webViewPackageInfo) {
if (webViewPackageInfo == null) {
return null;
}
HashMap<String, Object> webViewPackageInfoMap = new HashMap<>();
webViewPackageInfoMap.put("versionName", webViewPackageInfo.versionName);
webViewPackageInfoMap.put("packageName", webViewPackageInfo.packageName);
return webViewPackageInfoMap;
}
public void dispose() {
channel.setMethodCallHandler(null);
}

View File

@ -1,12 +1,16 @@
package com.pichillilorenzo.flutter_inappwebview;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcelable;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;

View File

@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-05-23 18:32:59.790460","version":"1.17.1"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-05-26 00:10:53.444505","version":"1.17.1"}

View File

@ -101,7 +101,7 @@ class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
useOnLoadResource: true,
))));
},
child: Text("Open Webview Browser")),
child: Text("Open In-App Browser")),
Container(height: 40),
RaisedButton(
onPressed: () async {

View File

@ -76,13 +76,12 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView(
contextMenu: contextMenu,
initialUrl: "https://www.youtube.com/watch?v=oD5RtLhhubg",
initialUrl: "https://github.com/flutter",
// initialFile: "assets/index.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
disableContextMenu: true,
debuggingEnabled: true
),
),
onWebViewCreated: (InAppWebViewController controller) {

View File

@ -13,6 +13,7 @@ import 'package:permission_handler/permission_handler.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
// await Permission.camera.request();
// await Permission.storage.request();
// await localhostServer.start();
runApp(MyApp());
}

View File

@ -1343,56 +1343,12 @@ class AndroidInAppWebViewController {
return await _controller._channel.invokeMethod('startSafeBrowsing', args);
}
///Sets the list of hosts (domain names/IP addresses) that are exempt from SafeBrowsing checks. The list is global for all the WebViews.
///
/// Each rule should take one of these:
///| Rule | Example | Matches Subdomain |
///| -- | -- | -- |
///| HOSTNAME | example.com | Yes |
///| .HOSTNAME | .example.com | No |
///| IPV4_LITERAL | 192.168.1.1 | No |
///| IPV6_LITERAL_WITH_BRACKETS | [10:20:30:40:50:60:70:80] | No |
///
///All other rules, including wildcards, are invalid. The correct syntax for hosts is defined by [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.2.2).
///
///[hosts] represents the list of hosts. This value must never be null.
///
///**NOTE**: available only on Android 27+.
Future<bool> setSafeBrowsingWhitelist({@required List<String> hosts}) async {
assert(hosts != null);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('hosts', () => hosts);
return await _controller._channel
.invokeMethod('setSafeBrowsingWhitelist', args);
}
///Returns a URL pointing to the privacy policy for Safe Browsing reporting. This value will never be `null`.
///
///**NOTE**: available only on Android 27+.
Future<String> getSafeBrowsingPrivacyPolicyUrl() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _controller._channel
.invokeMethod('getSafeBrowsingPrivacyPolicyUrl', args);
}
///Clears the SSL preferences table stored in response to proceeding with SSL certificate errors.
Future<void> clearSslPreferences() async {
Map<String, dynamic> args = <String, dynamic>{};
await _controller._channel.invokeMethod('clearSslPreferences', args);
}
///Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests.
///Note that WebView automatically clears these preferences when the system keychain is updated.
///The preferences are shared by all the WebViews that are created by the embedder application.
///
///**NOTE**: On iOS certificate-based credentials are never stored permanently.
///
///**NOTE**: available on Android 21+.
Future<void> clearClientCertPreferences() async {
Map<String, dynamic> args = <String, dynamic>{};
await _controller._channel.invokeMethod('clearClientCertPreferences', args);
}
///Does a best-effort attempt to pause any processing that can be paused safely, such as animations and geolocation. Note that this call does not pause JavaScript.
///To pause JavaScript globally, use [pauseTimers()]. To resume WebView, call [resume()].
Future<void> pause() async {
@ -1413,6 +1369,50 @@ class AndroidInAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
return await _controller._channel.invokeMethod('getOriginalUrl', args);
}
///Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests.
///Note that WebView automatically clears these preferences when the system keychain is updated.
///The preferences are shared by all the WebViews that are created by the embedder application.
///
///**NOTE**: On iOS certificate-based credentials are never stored permanently.
///
///**NOTE**: available on Android 21+.
static Future<void> clearClientCertPreferences() async {
Map<String, dynamic> args = <String, dynamic>{};
await InAppWebViewController._staticChannel.invokeMethod('clearClientCertPreferences', args);
}
///Returns a URL pointing to the privacy policy for Safe Browsing reporting. This value will never be `null`.
///
///**NOTE**: available only on Android 27+.
static Future<String> getSafeBrowsingPrivacyPolicyUrl() async {
Map<String, dynamic> args = <String, dynamic>{};
return await InAppWebViewController._staticChannel
.invokeMethod('getSafeBrowsingPrivacyPolicyUrl', args);
}
///Sets the list of hosts (domain names/IP addresses) that are exempt from SafeBrowsing checks. The list is global for all the WebViews.
///
/// Each rule should take one of these:
///| Rule | Example | Matches Subdomain |
///| -- | -- | -- |
///| HOSTNAME | example.com | Yes |
///| .HOSTNAME | .example.com | No |
///| IPV4_LITERAL | 192.168.1.1 | No |
///| IPV6_LITERAL_WITH_BRACKETS | [10:20:30:40:50:60:70:80] | No |
///
///All other rules, including wildcards, are invalid. The correct syntax for hosts is defined by [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.2.2).
///
///[hosts] represents the list of hosts. This value must never be null.
///
///**NOTE**: available only on Android 27+.
static Future<bool> setSafeBrowsingWhitelist({@required List<String> hosts}) async {
assert(hosts != null);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('hosts', () => hosts);
return await InAppWebViewController._staticChannel
.invokeMethod('setSafeBrowsingWhitelist', args);
}
}
///InAppWebViewControllerIOS class represents the iOS controller that contains only ios-specific methods for the WebView.