added AndroidWebViewFeature, AndroidServiceWorkerController, AndroidServiceWorkerClient classes

This commit is contained in:
Lorenzo Pichilli 2021-02-04 21:54:09 +01:00
parent a023d34fd9
commit 37fa32b31e
19 changed files with 626 additions and 19 deletions

View File

@ -5,12 +5,11 @@
- Added `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options also for iOS (also thanks to [liranhao](https://github.com/liranhao))
- Added limited cookies support on iOS below 11.0 using JavaScript
- Added `IOSCookieManager` class and `CookieManager.instance().ios.getAllCookies` iOS-specific method
- Added `UserScript` and `UserScriptInjectionTime` classes
- Added `UserScript`, `UserScriptInjectionTime`, `ContentWorld`, `AndroidWebViewFeature`, `AndroidServiceWorkerController`, `AndroidServiceWorkerClient` classes
- Added `initialUserScripts` WebView option
- Added `addUserScript`, `addUserScripts`, `removeUserScript`, `removeUserScripts`, `removeAllUserScripts` WebView methods
- Added `isDirectionalLockEnabled`, `mediaType`, `pageZoom`, `limitsNavigationsToAppBoundDomains` iOS-specific webview options
- Added `handlesURLScheme` iOS-specific webview method
- Added `ContentWorld` class
- Updated integration tests
- Merge "Upgraded appcompat to 1.2.0-rc-02" [#465](https://github.com/pichillilorenzo/flutter_inappwebview/pull/465) (thanks to [andreidiaconu](https://github.com/andreidiaconu))
- Merge "Added missing field 'headers' which returned by WebResourceResponse.toMap()" [#490](https://github.com/pichillilorenzo/flutter_inappwebview/pull/490) (thanks to [Doflatango](https://github.com/Doflatango))

View File

@ -35,20 +35,16 @@ import android.webkit.CookieManager;
import android.webkit.DownloadListener;
import android.webkit.ValueCallback;
import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient;
import android.webkit.WebHistoryItem;
import android.webkit.WebSettings;
import android.webkit.WebStorage;
import android.webkit.WebViewClient;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Keep;
import androidx.annotation.RequiresApi;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import androidx.webkit.WebViewRenderProcessClient;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlocker;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerAction;
@ -63,9 +59,6 @@ import com.pichillilorenzo.flutter_inappwebview.Util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
@ -131,10 +124,11 @@ final public class InAppWebView extends InputAwareWebView {
"}";
static final String contentWorldWrapperJS = "(function() {" +
" var iframe = document.getElementById('$CONTENT_WORLD_NAME');" +
" var iframeId = '" + JavaScriptBridgeInterface.name + "_$CONTENT_WORLD_NAME';" +
" var iframe = document.getElementById(iframeId);" +
" if (iframe == null) {" +
" iframe = document.createElement('iframe');" +
" iframe.id = '$CONTENT_WORLD_NAME';" +
" iframe.id = iframeId;" +
" iframe.style = 'display: none; z-index: 0; position: absolute; width: 0px; height: 0px';" +
" document.body.append(iframe);" +
" }" +

View File

@ -29,6 +29,8 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
public static MyCookieManager myCookieManager;
public static CredentialDatabaseHandler credentialDatabaseHandler;
public static MyWebStorage myWebStorage;
public static ServiceWorkerManager serviceWorkerManager;
public static WebViewFeatureManager webViewFeatureManager;
public static ValueCallback<Uri> filePathCallbackLegacy;
public static ValueCallback<Uri[]> filePathCallback;
@ -65,12 +67,17 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
platformViewRegistry.registerViewFactory(
"com.pichillilorenzo/flutter_inappwebview", new FlutterWebViewFactory(messenger, flutterView));
inAppWebViewStatic = new InAppWebViewStatic(messenger);
myCookieManager = new MyCookieManager(messenger);
myWebStorage = new MyWebStorage(messenger);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
serviceWorkerManager = new ServiceWorkerManager(messenger);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
credentialDatabaseHandler = new CredentialDatabaseHandler(messenger);
}
webViewFeatureManager = new WebViewFeatureManager(messenger);
}
@Override
@ -103,6 +110,14 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
inAppWebViewStatic.dispose();
inAppWebViewStatic = null;
}
if (serviceWorkerManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
serviceWorkerManager.dispose();
serviceWorkerManager = null;
}
if (webViewFeatureManager != null) {
webViewFeatureManager.dispose();
webViewFeatureManager = null;
}
filePathCallbackLegacy = null;
filePathCallback = null;
}

View File

@ -34,7 +34,6 @@ public abstract class RequestPermissionHandler implements ActivityCompat.OnReque
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
Log.d("asdasd", "\n\na asd asd \n\n");
if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
List<Runnable> callbacks = actionDictionary.get(requestCode);
for (Runnable runnable : callbacks) {

View File

@ -0,0 +1,156 @@
package com.pichillilorenzo.flutter_inappwebview;
import android.os.Build;
import android.util.Log;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.webkit.ServiceWorkerClientCompat;
import androidx.webkit.ServiceWorkerControllerCompat;
import androidx.webkit.ServiceWorkerWebSettingsCompat;
import androidx.webkit.WebViewFeature;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
@RequiresApi(api = Build.VERSION_CODES.N)
public class ServiceWorkerManager implements MethodChannel.MethodCallHandler {
static final String LOG_TAG = "ServiceWorkerManager";
public static MethodChannel channel;
@Nullable
public static ServiceWorkerControllerCompat serviceWorkerController;
public ServiceWorkerManager(BinaryMessenger messenger) {
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_android_serviceworkercontroller");
channel.setMethodCallHandler(this);
if (WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BASIC_USAGE)) {
serviceWorkerController = ServiceWorkerControllerCompat.getInstance();
serviceWorkerController.setServiceWorkerClient(new ServiceWorkerClientCompat() {
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request) {
final Map<String, Object> obj = new HashMap<>();
obj.put("url", request.getUrl().toString());
obj.put("method", request.getMethod());
obj.put("headers", request.getRequestHeaders());
obj.put("isForMainFrame", request.isForMainFrame());
obj.put("hasGesture", request.hasGesture());
obj.put("isRedirect", request.isRedirect());
Util.WaitFlutterResult flutterResult;
try {
flutterResult = Util.invokeMethodAndWait(channel, "shouldInterceptRequest", obj);
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
if (flutterResult.error != null) {
Log.e(LOG_TAG, flutterResult.error);
}
else if (flutterResult.result != null) {
Map<String, Object> res = (Map<String, Object>) flutterResult.result;
String contentType = (String) res.get("contentType");
String contentEncoding = (String) res.get("contentEncoding");
byte[] data = (byte[]) res.get("data");
Map<String, String> responseHeaders = (Map<String, String>) res.get("headers");
Integer statusCode = (Integer) res.get("statusCode");
String reasonPhrase = (String) res.get("reasonPhrase");
ByteArrayInputStream inputStream = (data != null) ? new ByteArrayInputStream(data) : null;
if ((responseHeaders == null && statusCode == null && reasonPhrase == null) || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return new WebResourceResponse(contentType, contentEncoding, inputStream);
} else {
return new WebResourceResponse(contentType, contentEncoding, statusCode, reasonPhrase, responseHeaders, inputStream);
}
}
return null;
}
});
} else {
serviceWorkerController = null;
}
}
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
ServiceWorkerWebSettingsCompat serviceWorkerWebSettings = (serviceWorkerController != null) ? serviceWorkerController.getServiceWorkerWebSettings() : null;
switch (call.method) {
case "getAllowContentAccess":
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS)) {
result.success(serviceWorkerWebSettings.getAllowContentAccess());
} else {
result.success(false);
}
break;
case "getAllowFileAccess":
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_FILE_ACCESS)) {
result.success(serviceWorkerWebSettings.getAllowFileAccess());
} else {
result.success(false);
}
break;
case "getBlockNetworkLoads":
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS)) {
result.success(serviceWorkerWebSettings.getBlockNetworkLoads());
} else {
result.success(false);
}
break;
case "getCacheMode":
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CACHE_MODE)) {
result.success(serviceWorkerWebSettings.getCacheMode());
} else {
result.success(null);
}
break;
case "setAllowContentAccess":
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS)) {
Boolean allow = (Boolean) call.argument("allow");
serviceWorkerWebSettings.setAllowContentAccess(allow);
}
result.success(true);
break;
case "setAllowFileAccess":
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_FILE_ACCESS)) {
Boolean allow = (Boolean) call.argument("allow");
serviceWorkerWebSettings.setAllowFileAccess(allow);
}
result.success(true);
break;
case "setBlockNetworkLoads":
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS)) {
Boolean flag = (Boolean) call.argument("flag");
serviceWorkerWebSettings.setBlockNetworkLoads(flag);
}
result.success(true);
break;
case "setCacheMode":
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CACHE_MODE)) {
Integer mode = (Integer) call.argument("mode");
serviceWorkerWebSettings.setCacheMode(mode);
}
result.success(true);
break;
default:
result.notImplemented();
}
}
public void dispose() {
channel.setMethodCallHandler(null);
}
}

View File

@ -0,0 +1,34 @@
package com.pichillilorenzo.flutter_inappwebview;
import androidx.webkit.WebViewFeature;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class WebViewFeatureManager implements MethodChannel.MethodCallHandler {
static final String LOG_TAG = "WebViewFeatureManager";
public static MethodChannel channel;
public WebViewFeatureManager(BinaryMessenger messenger) {
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_android_webviewfeature");
channel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
switch (call.method) {
case "isFeatureSupported":
String feature = (String) call.argument("feature");
result.success(WebViewFeature.isFeatureSupported(feature));
default:
result.notImplemented();
}
}
public void dispose() {
channel.setMethodCallHandler(null);
}
}

View File

@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-02-04 01:10:32.198308","version":"1.26.0-18.0.pre.90"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-02-04 21:51:34.603760","version":"1.26.0-18.0.pre.90"}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -9,7 +9,7 @@
/* Begin PBXBuildFile section */
020EF14E4245221B2C22ACE5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B0FC2CF7A6002799890B3102 /* Pods_Runner.framework */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
25A517508F43E58C47090625 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
25A517508F43E58C47090625 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
61FF730023634CA10069C557 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 61FF72FF23634CA10069C557 /* libsqlite3.tbd */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
@ -46,7 +46,7 @@
buildActionMask = 2147483647;
files = (
61FF730023634CA10069C557 /* libsqlite3.tbd in Frameworks */,
25A517508F43E58C47090625 /* BuildFile in Frameworks */,
25A517508F43E58C47090625 /* (null) in Frameworks */,
020EF14E4245221B2C22ACE5 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -81,7 +81,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView(
// contextMenu: contextMenu,
initialUrl: "https://flutter.dev/",
initialUrl: "https://flutter.dev",
// initialFile: "assets/index.html",
initialHeaders: {},
initialUserScripts: UnmodifiableListView<UserScript>([
@ -91,12 +91,14 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: false,
mediaPlaybackRequiresUserGesture: false,
clearCache: true
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
// limitsNavigationsToAppBoundDomains: true // adds Service Worker API on iOS 14.0+
)
),
onWebViewCreated: (controller) {

View File

@ -12,6 +12,8 @@ import 'package:flutter_inappwebview_example/in_app_browser_example.screen.dart'
// InAppLocalhostServer localhostServer = new InAppLocalhostServer();
AndroidServiceWorkerController serviceWorkerController = AndroidServiceWorkerController.instance();
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
// await Permission.camera.request();
@ -19,6 +21,16 @@ Future main() async {
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
if (await AndroidWebViewFeature.isFeatureSupported(AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST)) {
serviceWorkerController.serviceWorkerClient = AndroidServiceWorkerClient(
shouldInterceptRequest: (request) async {
print(request);
return null;
},
);
}
runApp(MyApp());
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -38,3 +38,5 @@ export 'src/web_storage_manager.dart';
export 'src/context_menu.dart';
export 'src/web_storage.dart';
export 'src/X509Certificate/main.dart';
export 'src/android/service_worker_controller.dart';
export 'src/android/webview_feature.dart';

View File

@ -0,0 +1,179 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'webview_feature.dart';
import '../types.dart';
///Class that represents an Android-specific class that manages Service Workers used by [WebView].
///
///**NOTE**: available on Android 24+.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerControllerCompat
class AndroidServiceWorkerController {
static AndroidServiceWorkerController? _instance;
static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_android_serviceworkercontroller');
AndroidServiceWorkerClient? serviceWorkerClient;
///Gets the [AndroidServiceWorkerController] shared instance.
static AndroidServiceWorkerController instance() {
return (_instance != null) ? _instance! : _init();
}
static AndroidServiceWorkerController _init() {
_channel.setMethodCallHandler(_handleMethod);
_instance = AndroidServiceWorkerController();
return _instance!;
}
static Future<dynamic> _handleMethod(MethodCall call) async {
AndroidServiceWorkerController controller = AndroidServiceWorkerController.instance();
AndroidServiceWorkerClient? serviceWorkerClient = controller.serviceWorkerClient;
switch (call.method) {
case "shouldInterceptRequest":
String url = call.arguments["url"];
String method = call.arguments["method"];
Map<String, String>? headers =
call.arguments["headers"]?.cast<String, String>();
bool isForMainFrame = call.arguments["isForMainFrame"];
bool hasGesture = call.arguments["hasGesture"];
bool isRedirect = call.arguments["isRedirect"];
var request = new WebResourceRequest(
url: url,
method: method,
headers: headers,
isForMainFrame: isForMainFrame,
hasGesture: hasGesture,
isRedirect: isRedirect);
if (serviceWorkerClient != null && serviceWorkerClient.shouldInterceptRequest != null) {
return (await serviceWorkerClient.shouldInterceptRequest!(request))
?.toMap();
}
break;
default:
throw UnimplementedError("Unimplemented ${call.method} method");
}
return null;
}
///Gets whether Service Workers support content URL access.
///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_CONTENT_ACCESS].
///
///**NOTE**: available on Android 24+.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getAllowContentAccess()
static Future<bool> getAllowContentAccess() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel.invokeMethod('getAllowContentAccess', args);
}
///Gets whether Service Workers support file access.
///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_FILE_ACCESS].
///
///**NOTE**: available on Android 24+.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getAllowFileAccess()
static Future<bool> getAllowFileAccess() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel.invokeMethod('getAllowFileAccess', args);
}
///Gets whether Service Workers are prohibited from loading any resources from the network.
///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS].
///
///**NOTE**: available on Android 24+.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getBlockNetworkLoads()
static Future<bool> getBlockNetworkLoads() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel.invokeMethod('getBlockNetworkLoads', args);
}
///Gets the current setting for overriding the cache mode.
///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_CACHE_MODE].
///
///**NOTE**: available on Android 24+.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getCacheMode()
static Future<AndroidCacheMode?> getCacheMode() async {
Map<String, dynamic> args = <String, dynamic>{};
return AndroidCacheMode.fromValue(await _channel.invokeMethod('getCacheMode', args));
}
///Enables or disables content URL access from Service Workers.
///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_CONTENT_ACCESS].
///
///**NOTE**: available on Android 24+.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setAllowContentAccess(boolean)
static Future<void> setAllowContentAccess(bool allow) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("allow", () => allow);
await _channel.invokeMethod('setAllowContentAccess', args);
}
///Enables or disables file access within Service Workers.
///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_FILE_ACCESS].
///
///**NOTE**: available on Android 24+.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setAllowFileAccess(boolean)
static Future<void> setAllowFileAccess(bool allow) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("allow", () => allow);
await _channel.invokeMethod('setAllowFileAccess', args);
}
///Sets whether Service Workers should not load resources from the network.
///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS].
///
///**NOTE**: available on Android 24+.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setBlockNetworkLoads(boolean)
static Future<void> setBlockNetworkLoads(bool flag) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("flag", () => flag);
await _channel.invokeMethod('setBlockNetworkLoads', args);
}
///Overrides the way the cache is used.
///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SERVICE_WORKER_CACHE_MODE].
///
///**NOTE**: available on Android 24+.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setCacheMode(int)
static Future<void> setCacheMode(AndroidCacheMode mode) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("mode", () => mode.toValue());
await _channel.invokeMethod('setCacheMode', args);
}
}
///Class that represents an Android-specific class for clients to capture Service Worker related callbacks.
///
///**NOTE**: available on Android 24+.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerClientCompat
class AndroidServiceWorkerClient {
///Notify the host application of a resource request and allow the application to return the data.
///If the return value is `null`, the Service Worker will continue to load the resource as usual.
///Otherwise, the return response and data will be used.
///
///This method is called only if [AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST] is supported.
///You can check whether that flag is supported using [AndroidWebViewFeature.isFeatureSupported].
///
///[request] represents an object containing the details of the request.
///
///**NOTE**: available on Android 24+.
final Future<WebResourceResponse?> Function(WebResourceRequest request)?
shouldInterceptRequest;
AndroidServiceWorkerClient({
this.shouldInterceptRequest
});
}

View File

@ -0,0 +1,214 @@
import 'dart:async';
import 'package:flutter/services.dart';
///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device.
class AndroidWebViewFeature {
static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_android_webviewfeature');
static Future<dynamic> _handleMethod(MethodCall call) async {}
final String _value;
const AndroidWebViewFeature._internal(this._value);
static final Set<AndroidWebViewFeature> values = [
AndroidWebViewFeature.CREATE_WEB_MESSAGE_CHANNEL,
AndroidWebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS,
AndroidWebViewFeature.FORCE_DARK,
AndroidWebViewFeature.FORCE_DARK_STRATEGY,
AndroidWebViewFeature.GET_WEB_CHROME_CLIENT,
AndroidWebViewFeature.GET_WEB_VIEW_CLIENT,
AndroidWebViewFeature.GET_WEB_VIEW_RENDERER,
AndroidWebViewFeature.MULTI_PROCESS,
AndroidWebViewFeature.OFF_SCREEN_PRERASTER,
AndroidWebViewFeature.POST_WEB_MESSAGE,
AndroidWebViewFeature.PROXY_OVERRIDE,
AndroidWebViewFeature.RECEIVE_HTTP_ERROR,
AndroidWebViewFeature.RECEIVE_WEB_RESOURCE_ERROR,
AndroidWebViewFeature.SAFE_BROWSING_ALLOWLIST,
AndroidWebViewFeature.SAFE_BROWSING_ENABLE,
AndroidWebViewFeature.SAFE_BROWSING_HIT,
AndroidWebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL,
AndroidWebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY,
AndroidWebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED,
AndroidWebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL,
AndroidWebViewFeature.SAFE_BROWSING_WHITELIST,
AndroidWebViewFeature.SERVICE_WORKER_BASIC_USAGE,
AndroidWebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS,
AndroidWebViewFeature.SERVICE_WORKER_CACHE_MODE,
AndroidWebViewFeature.SERVICE_WORKER_CONTENT_ACCESS,
AndroidWebViewFeature.SERVICE_WORKER_FILE_ACCESS,
AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST,
AndroidWebViewFeature.SHOULD_OVERRIDE_WITH_REDIRECTS,
AndroidWebViewFeature.START_SAFE_BROWSING,
AndroidWebViewFeature.TRACING_CONTROLLER_BASIC_USAGE,
AndroidWebViewFeature.VISUAL_STATE_CALLBACK,
AndroidWebViewFeature.WEB_MESSAGE_CALLBACK_ON_MESSAGE,
AndroidWebViewFeature.WEB_MESSAGE_LISTENER,
AndroidWebViewFeature.WEB_MESSAGE_PORT_CLOSE,
AndroidWebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE,
AndroidWebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK,
AndroidWebViewFeature.WEB_RESOURCE_ERROR_GET_CODE,
AndroidWebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION,
AndroidWebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT,
AndroidWebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE,
AndroidWebViewFeature.WEB_VIEW_RENDERER_TERMINATE,
].toSet();
static AndroidWebViewFeature? fromValue(String? value) {
if (value != null) {
try {
return AndroidWebViewFeature.values.firstWhere(
(element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
}
String toValue() => _value;
@override
String toString() => _value;
///
static const CREATE_WEB_MESSAGE_CHANNEL = const AndroidWebViewFeature._internal("CREATE_WEB_MESSAGE_CHANNEL");
///
static const DISABLED_ACTION_MODE_MENU_ITEMS = const AndroidWebViewFeature._internal("DISABLED_ACTION_MODE_MENU_ITEMS");
///
static const FORCE_DARK = const AndroidWebViewFeature._internal("FORCE_DARK");
///
static const FORCE_DARK_STRATEGY = const AndroidWebViewFeature._internal("FORCE_DARK_STRATEGY");
///
static const GET_WEB_CHROME_CLIENT = const AndroidWebViewFeature._internal("GET_WEB_CHROME_CLIENT");
///
static const GET_WEB_VIEW_CLIENT = const AndroidWebViewFeature._internal("GET_WEB_VIEW_CLIENT");
///
static const GET_WEB_VIEW_RENDERER = const AndroidWebViewFeature._internal("GET_WEB_VIEW_RENDERER");
///
static const MULTI_PROCESS = const AndroidWebViewFeature._internal("MULTI_PROCESS");
///
static const OFF_SCREEN_PRERASTER = const AndroidWebViewFeature._internal("OFF_SCREEN_PRERASTER");
///
static const POST_WEB_MESSAGE = const AndroidWebViewFeature._internal("POST_WEB_MESSAGE");
///
static const PROXY_OVERRIDE = const AndroidWebViewFeature._internal("PROXY_OVERRIDE");
///
static const RECEIVE_HTTP_ERROR = const AndroidWebViewFeature._internal("RECEIVE_HTTP_ERROR");
///
static const RECEIVE_WEB_RESOURCE_ERROR = const AndroidWebViewFeature._internal("RECEIVE_WEB_RESOURCE_ERROR");
///
static const SAFE_BROWSING_ALLOWLIST = const AndroidWebViewFeature._internal("SAFE_BROWSING_ALLOWLIST");
///
static const SAFE_BROWSING_ENABLE = const AndroidWebViewFeature._internal("SAFE_BROWSING_ENABLE");
///
static const SAFE_BROWSING_HIT = const AndroidWebViewFeature._internal("SAFE_BROWSING_HIT");
///
static const SAFE_BROWSING_PRIVACY_POLICY_URL = const AndroidWebViewFeature._internal("SAFE_BROWSING_PRIVACY_POLICY_URL");
///
static const SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = const AndroidWebViewFeature._internal("SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY");
///
static const SAFE_BROWSING_RESPONSE_PROCEED = const AndroidWebViewFeature._internal("SAFE_BROWSING_RESPONSE_PROCEED");
///
static const SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = const AndroidWebViewFeature._internal("SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL");
///
static const SAFE_BROWSING_WHITELIST = const AndroidWebViewFeature._internal("SAFE_BROWSING_WHITELIST");
///
static const SERVICE_WORKER_BASIC_USAGE = const AndroidWebViewFeature._internal("SERVICE_WORKER_BASIC_USAGE");
///
static const SERVICE_WORKER_BLOCK_NETWORK_LOADS = const AndroidWebViewFeature._internal("SERVICE_WORKER_BLOCK_NETWORK_LOADS");
///
static const SERVICE_WORKER_CACHE_MODE = const AndroidWebViewFeature._internal("SERVICE_WORKER_CACHE_MODE");
///
static const SERVICE_WORKER_CONTENT_ACCESS = const AndroidWebViewFeature._internal("SERVICE_WORKER_CONTENT_ACCESS");
///
static const SERVICE_WORKER_FILE_ACCESS = const AndroidWebViewFeature._internal("SERVICE_WORKER_FILE_ACCESS");
///
static const SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = const AndroidWebViewFeature._internal("SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST");
///
static const SHOULD_OVERRIDE_WITH_REDIRECTS = const AndroidWebViewFeature._internal("SHOULD_OVERRIDE_WITH_REDIRECTS");
///
static const START_SAFE_BROWSING = const AndroidWebViewFeature._internal("START_SAFE_BROWSING");
///
static const TRACING_CONTROLLER_BASIC_USAGE = const AndroidWebViewFeature._internal("TRACING_CONTROLLER_BASIC_USAGE");
///
static const VISUAL_STATE_CALLBACK = const AndroidWebViewFeature._internal("VISUAL_STATE_CALLBACK");
///
static const WEB_MESSAGE_CALLBACK_ON_MESSAGE = const AndroidWebViewFeature._internal("WEB_MESSAGE_CALLBACK_ON_MESSAGE");
///
static const WEB_MESSAGE_LISTENER = const AndroidWebViewFeature._internal("WEB_MESSAGE_LISTENER");
///
static const WEB_MESSAGE_PORT_CLOSE = const AndroidWebViewFeature._internal("WEB_MESSAGE_PORT_CLOSE");
///
static const WEB_MESSAGE_PORT_POST_MESSAGE = const AndroidWebViewFeature._internal("WEB_MESSAGE_PORT_POST_MESSAGE");
///
static const WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = const AndroidWebViewFeature._internal("WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK");
///
static const WEB_RESOURCE_ERROR_GET_CODE = const AndroidWebViewFeature._internal("WEB_RESOURCE_ERROR_GET_CODE");
///
static const WEB_RESOURCE_ERROR_GET_DESCRIPTION = const AndroidWebViewFeature._internal("WEB_RESOURCE_ERROR_GET_DESCRIPTION");
///
static const WEB_RESOURCE_REQUEST_IS_REDIRECT = const AndroidWebViewFeature._internal("WEB_RESOURCE_REQUEST_IS_REDIRECT");
///
static const WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = const AndroidWebViewFeature._internal("WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE");
///
static const WEB_VIEW_RENDERER_TERMINATE = const AndroidWebViewFeature._internal("WEB_VIEW_RENDERER_TERMINATE");
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
///Return whether a feature is supported at run-time. On devices running Android version `Build.VERSION_CODES.LOLLIPOP` and higher,
///this will check whether a feature is supported, depending on the combination of the desired feature, the Android version of device,
///and the WebView APK on the device. If running on a device with a lower API level, this will always return `false`.
///
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewFeature#isFeatureSupported(java.lang.String)
static Future<bool> isFeatureSupported(AndroidWebViewFeature feature) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("feature", () => feature.toValue());
return await _channel.invokeMethod('isFeatureSupported', args);
}
}

View File

@ -929,6 +929,7 @@ class InAppWebViewController {
default:
throw UnimplementedError("Unimplemented ${call.method} method");
}
return null;
}
///Gets the URL for the current page.

View File

@ -4601,7 +4601,7 @@ class UserScript {
///
///**NOTE for iOS 14.0+**: this class represents the native [WKContentWorld](https://developer.apple.com/documentation/webkit/wkcontentworld) class.
///
///**NOTE for Android**: it will create and append an `<iframe>` HTML element with `id` equals to [name] to the webpage's content that contains only the scripts
///**NOTE for Android**: it will create and append an `<iframe>` HTML element with `id` equals to `flutter_inappwebview_[name]` to the webpage's content that contains only the scripts
///in order to define a new scope of execution for JavaScript code. Unfortunately, there isn't any other way to do it.
///For any [ContentWorld], except [ContentWorld.page], if you need to access to the `window` or `document` global Object,
///you need to use `window.top` and `window.top.document` because the code runs inside an `<iframe>`.