added android proxy controller support
This commit is contained in:
parent
2ab051fca3
commit
d3a834c36a
|
@ -2,6 +2,7 @@
|
|||
|
||||
- Deprecated old classes/properties/methods to make them eventually compatible with other Platforms and WebView engines.
|
||||
- Added Web support
|
||||
- Added `ProxyController` for Android
|
||||
- Added `pauseAllMediaPlayback`, `setAllMediaPlaybackSuspended`, `closeAllMediaPresentations`, `requestMediaPlaybackState`, `isInFullscreen`, `getCameraCaptureState`, `setCameraCaptureState`, `getMicrophoneCaptureState`, `setMicrophoneCaptureState` WebView controller methods
|
||||
- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS` WebView settings
|
||||
- Added support for `onPermissionRequest` event on iOS 15.0+
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ChromeSafariB
|
|||
import com.pichillilorenzo.flutter_inappwebview.credential_database.CredentialDatabaseHandler;
|
||||
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebViewManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.proxy.ProxyManager;
|
||||
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||
|
@ -35,6 +36,7 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
|||
public MyWebStorage myWebStorage;
|
||||
public ServiceWorkerManager serviceWorkerManager;
|
||||
public WebViewFeatureManager webViewFeatureManager;
|
||||
public ProxyManager proxyManager;
|
||||
public FlutterWebViewFactory flutterWebViewFactory;
|
||||
public static ValueCallback<Uri> filePathCallbackLegacy;
|
||||
public static ValueCallback<Uri[]> filePathCallback;
|
||||
|
@ -97,6 +99,7 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
|||
credentialDatabaseHandler = new CredentialDatabaseHandler(this);
|
||||
}
|
||||
webViewFeatureManager = new WebViewFeatureManager(this);
|
||||
proxyManager = new ProxyManager(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,6 +144,10 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
|||
webViewFeatureManager.dispose();
|
||||
webViewFeatureManager = null;
|
||||
}
|
||||
if (proxyManager != null) {
|
||||
proxyManager.dispose();
|
||||
proxyManager = null;
|
||||
}
|
||||
filePathCallbackLegacy = null;
|
||||
filePathCallback = null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.proxy;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.webkit.ProxyConfig;
|
||||
import androidx.webkit.ProxyController;
|
||||
import androidx.webkit.WebViewFeature;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.ProxyRuleExt;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
|
||||
public class ProxyManager implements MethodChannel.MethodCallHandler {
|
||||
|
||||
static final String LOG_TAG = "ProxyManager";
|
||||
|
||||
public MethodChannel channel;
|
||||
@Nullable
|
||||
public static ProxyController proxyController;
|
||||
@Nullable
|
||||
public InAppWebViewFlutterPlugin plugin;
|
||||
|
||||
public ProxyManager(final InAppWebViewFlutterPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
channel = new MethodChannel(plugin.messenger, "com.pichillilorenzo/flutter_inappwebview_proxycontroller");
|
||||
channel.setMethodCallHandler(this);
|
||||
if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) {
|
||||
proxyController = ProxyController.getInstance();
|
||||
} else {
|
||||
proxyController = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
||||
switch (call.method) {
|
||||
case "setProxyOverride":
|
||||
if (proxyController != null) {
|
||||
HashMap<String, Object> settingsMap = (HashMap<String, Object>) call.argument("settings");
|
||||
ProxySettings settings = new ProxySettings();
|
||||
if (settingsMap != null) {
|
||||
settings.parse(settingsMap);
|
||||
}
|
||||
setProxyOverride(settings, result);
|
||||
} else {
|
||||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "clearProxyOverride":
|
||||
if (proxyController != null ) {
|
||||
clearProxyOverride(result);
|
||||
} else {
|
||||
result.success(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
private void setProxyOverride(ProxySettings settings, final MethodChannel.Result result) {
|
||||
if (proxyController != null) {
|
||||
ProxyConfig.Builder proxyConfigBuilder = new ProxyConfig.Builder();
|
||||
for (String bypassRule : settings.bypassRules) {
|
||||
proxyConfigBuilder.addBypassRule(bypassRule);
|
||||
}
|
||||
for (String direct : settings.directs) {
|
||||
proxyConfigBuilder.addDirect(direct);
|
||||
}
|
||||
for (ProxyRuleExt proxyRule : settings.proxyRules) {
|
||||
if (proxyRule.getSchemeFilter() != null) {
|
||||
proxyConfigBuilder.addProxyRule(proxyRule.getUrl(), proxyRule.getSchemeFilter());
|
||||
} else {
|
||||
proxyConfigBuilder.addProxyRule(proxyRule.getUrl());
|
||||
}
|
||||
}
|
||||
if (settings.bypassSimpleHostnames != null && settings.bypassSimpleHostnames) {
|
||||
proxyConfigBuilder.bypassSimpleHostnames();
|
||||
}
|
||||
if (settings.removeImplicitRules != null && settings.removeImplicitRules) {
|
||||
proxyConfigBuilder.removeImplicitRules();
|
||||
}
|
||||
proxyController.setProxyOverride(proxyConfigBuilder.build(), new Executor() {
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
command.run();
|
||||
}
|
||||
}, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
result.success(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void clearProxyOverride(final MethodChannel.Result result) {
|
||||
if (proxyController != null) {
|
||||
proxyController.clearProxyOverride(new Executor() {
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
command.run();
|
||||
}
|
||||
}, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
result.success(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
channel.setMethodCallHandler(null);
|
||||
if (proxyController != null) {
|
||||
// Clears the proxy settings
|
||||
proxyController.clearProxyOverride(new Executor() {
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
|
||||
}
|
||||
}, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
});
|
||||
proxyController = null;
|
||||
}
|
||||
plugin = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.proxy;
|
||||
|
||||
import androidx.webkit.ProxyConfig;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.ProxyRuleExt;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ProxySettings implements ISettings<ProxyConfig> {
|
||||
List<String> bypassRules = new ArrayList<>();
|
||||
List<String> directs = new ArrayList<>();
|
||||
List<ProxyRuleExt> proxyRules = new ArrayList<>();
|
||||
Boolean bypassSimpleHostnames = null;
|
||||
Boolean removeImplicitRules = null;
|
||||
|
||||
@Override
|
||||
public ProxySettings parse(Map<String, Object> settings) {
|
||||
for (Map.Entry<String, Object> pair : settings.entrySet()) {
|
||||
String key = pair.getKey();
|
||||
Object value = pair.getValue();
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case "bypassRules":
|
||||
bypassRules = (List<String>) value;
|
||||
break;
|
||||
case "directs":
|
||||
directs = (List<String>) value;
|
||||
break;
|
||||
case "proxyRules":
|
||||
proxyRules = new ArrayList<>();
|
||||
List<Map<String, String>> proxyRuleMapList = (List<Map<String, String>>) value;
|
||||
for (Map<String, String> proxyRuleMap : proxyRuleMapList) {
|
||||
ProxyRuleExt proxyRuleExt = ProxyRuleExt.fromMap(proxyRuleMap);
|
||||
if (proxyRuleExt != null) {
|
||||
proxyRules.add(proxyRuleExt);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "bypassSimpleHostnames":
|
||||
bypassSimpleHostnames = (Boolean) value;
|
||||
break;
|
||||
case "removeImplicitRules":
|
||||
removeImplicitRules = (Boolean) value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> toMap() {
|
||||
List<Map<String, String>> proxyRuleMapList = new ArrayList<>();
|
||||
for (ProxyRuleExt proxyRuleExt : proxyRules) {
|
||||
proxyRuleMapList.add(proxyRuleExt.toMap());
|
||||
}
|
||||
Map<String, Object> settings = new HashMap<>();
|
||||
settings.put("bypassRules", bypassRules);
|
||||
settings.put("directs", directs);
|
||||
settings.put("proxyRules", proxyRuleMapList);
|
||||
settings.put("bypassSimpleHostnames", bypassSimpleHostnames);
|
||||
settings.put("removeImplicitRules", removeImplicitRules);
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getRealSettings(ProxyConfig proxyConfig) {
|
||||
Map<String, Object> realSettings = toMap();
|
||||
List<Map<String, String>> proxyRuleMapList = new ArrayList<>();
|
||||
List<ProxyConfig.ProxyRule> proxyRules = proxyConfig.getProxyRules();
|
||||
for (ProxyConfig.ProxyRule proxyRule : proxyRules) {
|
||||
Map<String, String> proxyRuleMap = new HashMap<>();
|
||||
proxyRuleMap.put("url", proxyRule.getUrl());
|
||||
proxyRuleMap.put("schemeFilter", proxyRule.getSchemeFilter());
|
||||
proxyRuleMapList.add(proxyRuleMap);
|
||||
}
|
||||
realSettings.put("bypassRules", proxyConfig.getBypassRules());
|
||||
realSettings.put("proxyRules", proxyRuleMapList);
|
||||
return realSettings;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.types;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ProxyRuleExt {
|
||||
@Nullable
|
||||
private String schemeFilter;
|
||||
@NonNull
|
||||
private String url;
|
||||
|
||||
public ProxyRuleExt(@Nullable String schemeFilter, @NonNull String url) {
|
||||
this.schemeFilter = schemeFilter;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ProxyRuleExt fromMap(@Nullable Map<String, String> map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
String url = (String) map.get("url");
|
||||
String schemeFilter = (String) map.get("schemeFilter");
|
||||
return new ProxyRuleExt(schemeFilter, url);
|
||||
}
|
||||
|
||||
public Map<String, String> toMap() {
|
||||
Map<String, String> proxyRuleMap = new HashMap<>();
|
||||
proxyRuleMap.put("url", url);
|
||||
proxyRuleMap.put("schemeFilter", schemeFilter);
|
||||
return proxyRuleMap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getSchemeFilter() {
|
||||
return schemeFilter;
|
||||
}
|
||||
|
||||
public void setSchemeFilter(@Nullable String schemeFilter) {
|
||||
this.schemeFilter = schemeFilter;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(@NonNull String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ProxyRuleExt that = (ProxyRuleExt) o;
|
||||
|
||||
if (schemeFilter != null ? !schemeFilter.equals(that.schemeFilter) : that.schemeFilter != null)
|
||||
return false;
|
||||
return url.equals(that.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = schemeFilter != null ? schemeFilter.hashCode() : 0;
|
||||
result = 31 * result + url.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProxyRuleExt{" +
|
||||
"schemeFilter='" + schemeFilter + '\'' +
|
||||
", url='" + url + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -40,6 +40,16 @@ Future main() async {
|
|||
},
|
||||
));
|
||||
}
|
||||
|
||||
// var proxyAvailable = await WebViewFeature.isFeatureSupported(
|
||||
// WebViewFeature.PROXY_OVERRIDE);
|
||||
// if (proxyAvailable) {
|
||||
// ProxyController proxyController = ProxyController.instance();
|
||||
// await proxyController.clearProxyOverride();
|
||||
// await proxyController.setProxyOverride(settings: ProxySettings(
|
||||
// proxyRules: [ProxyRule(url: "https://192.168.1.102:4433")],
|
||||
// ));
|
||||
// }
|
||||
}
|
||||
|
||||
runApp(MyApp());
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export 'service_worker_controller.dart';
|
||||
export 'webview_feature.dart';
|
||||
export 'proxy_controller.dart';
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'webview_feature.dart';
|
||||
import '../in_app_webview/webview.dart';
|
||||
import '../types/main.dart';
|
||||
|
||||
///Manages setting and clearing a process-specific override for the Android system-wide proxy settings that govern network requests made by [WebView].
|
||||
///
|
||||
///[WebView] may make network requests in order to fetch content that is not otherwise read from the file system or provided directly by application code.
|
||||
///In this case by default the system-wide Android network proxy settings are used to redirect requests to appropriate proxy servers.
|
||||
///
|
||||
///In the rare case that it is necessary for an application to explicitly specify its proxy configuration,
|
||||
///this API may be used to explicitly specify the proxy rules that govern WebView initiated network requests.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - ProxyController](https://developer.android.com/reference/androidx/webkit/ProxyController))
|
||||
class ProxyController {
|
||||
static ProxyController? _instance;
|
||||
static const MethodChannel _channel = const MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_proxycontroller');
|
||||
|
||||
///Gets the [ProxyController] shared instance.
|
||||
///
|
||||
///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.PROXY_OVERRIDE].
|
||||
static ProxyController instance() {
|
||||
return (_instance != null) ? _instance! : _init();
|
||||
}
|
||||
|
||||
static ProxyController _init() {
|
||||
_channel.setMethodCallHandler(_handleMethod);
|
||||
_instance = ProxyController();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
static Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
// ProxyController controller = ProxyController.instance();
|
||||
switch (call.method) {
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
// return null;
|
||||
}
|
||||
|
||||
///Sets [ProxySettings] which will be used by all [WebView]s in the app.
|
||||
///URLs that match patterns in the bypass list will not be directed to any proxy.
|
||||
///Instead, the request will be made directly to the origin specified by the URL.
|
||||
///Network connections are not guaranteed to immediately use the new proxy setting; wait for the method to return before loading a page.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - ProxyController.setProxyOverride](https://developer.android.com/reference/androidx/webkit/ProxyController#setProxyOverride(androidx.webkit.ProxyConfig,%20java.util.concurrent.Executor,%20java.lang.Runnable)))
|
||||
Future<void> setProxyOverride({required ProxySettings settings}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("settings", () => settings.toMap());
|
||||
return await _channel.invokeMethod('setProxyOverride', args);
|
||||
}
|
||||
|
||||
///Clears the proxy settings.
|
||||
///Network connections are not guaranteed to immediately use the new proxy setting; wait for the method to return before loading a page.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - ProxyController.clearProxyOverride](https://developer.android.com/reference/androidx/webkit/ProxyController#clearProxyOverride(java.util.concurrent.Executor,%20java.lang.Runnable)))
|
||||
Future<void> clearProxyOverride() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await _channel.invokeMethod('clearProxyOverride', args);
|
||||
}
|
||||
}
|
||||
|
||||
///Class that represents the settings used to configure the [ProxyController].
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - ProxyConfig](https://developer.android.com/reference/androidx/webkit/ProxyConfig))
|
||||
class ProxySettings {
|
||||
///List of bypass rules.
|
||||
///
|
||||
///A bypass rule describes URLs that should skip proxy override settings and make a direct connection instead. These can be URLs or IP addresses. Wildcards are accepted.
|
||||
///For instance, the rule "*example.com" would mean that requests to "http://example.com" and "www.example.com" would not be directed to any proxy,
|
||||
///instead, would be made directly to the origin specified by the URL.
|
||||
List<String> bypassRules;
|
||||
|
||||
///List of scheme filters.
|
||||
///
|
||||
///URLs that match these scheme filters are connected to directly instead of using a proxy server.
|
||||
List<String> directs;
|
||||
|
||||
///List of proxy rules to be used for all URLs. This method can be called multiple times to add multiple rules. Additional rules have decreasing precedence.
|
||||
///
|
||||
///Proxy is a string in the format `[scheme://]host[:port]`.
|
||||
///Scheme is optional, if present must be `HTTP`, `HTTPS` or [SOCKS](https://tools.ietf.org/html/rfc1928) and defaults to `HTTP`.
|
||||
///Host is one of an IPv6 literal with brackets, an IPv4 literal or one or more labels separated by a period.
|
||||
///Port number is optional and defaults to `80` for `HTTP`, `443` for `HTTPS` and `1080` for `SOCKS`.
|
||||
///
|
||||
///The correct syntax for hosts is defined by [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.2.2).
|
||||
List<ProxyRule> proxyRules;
|
||||
|
||||
///Hostnames without a period in them (and that are not IP literals) will skip proxy settings and be connected to directly instead. Examples: `"abc"`, `"local"`, `"some-domain"`.
|
||||
///
|
||||
///Hostnames with a trailing dot are not considered simple by this definition.
|
||||
bool? bypassSimpleHostnames;
|
||||
|
||||
///By default, certain hostnames implicitly bypass the proxy if they are link-local IPs, or localhost addresses.
|
||||
///For instance hostnames matching any of (non-exhaustive list):
|
||||
///- localhost
|
||||
///- *.localhost
|
||||
///- [::1]
|
||||
///- 127.0.0.1/8
|
||||
///- 169.254/16
|
||||
///- [FE80::]/10
|
||||
///Set this to `true` to override the default behavior and force localhost and link-local URLs to be sent through the proxy.
|
||||
bool? removeImplicitRules;
|
||||
|
||||
ProxySettings(
|
||||
{this.bypassRules = const [],
|
||||
this.directs = const [],
|
||||
this.proxyRules = const [],
|
||||
this.bypassSimpleHostnames,
|
||||
this.removeImplicitRules});
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"bypassRules": bypassRules,
|
||||
"directs": directs,
|
||||
"proxyRules": proxyRules.map((e) => e.toMap()).toList(),
|
||||
"bypassSimpleHostnames": bypassSimpleHostnames,
|
||||
"removeImplicitRules": removeImplicitRules
|
||||
};
|
||||
}
|
||||
|
||||
static ProxySettings fromMap(Map<String, dynamic> map) {
|
||||
var settings = ProxySettings();
|
||||
settings.bypassRules = map["bypassRules"];
|
||||
settings.directs = map["directs"];
|
||||
settings.proxyRules = (map["proxyRules"].cast<Map<String, dynamic>>()
|
||||
as List<Map<String, dynamic>>)
|
||||
.map((e) => ProxyRule.fromMap(e)) as List<ProxyRule>;
|
||||
settings.bypassSimpleHostnames = map["bypassSimpleHostnames"];
|
||||
settings.removeImplicitRules = map["removeImplicitRules"];
|
||||
return settings;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return this.toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
|
||||
ProxySettings copy() {
|
||||
return ProxySettings.fromMap(this.toMap());
|
||||
}
|
||||
}
|
|
@ -15,6 +15,8 @@ class ServiceWorkerController {
|
|||
'com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller');
|
||||
|
||||
///Gets the [ServiceWorkerController] shared instance.
|
||||
///
|
||||
///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_BASIC_USAGE].
|
||||
static ServiceWorkerController instance() {
|
||||
return (_instance != null) ? _instance! : _init();
|
||||
}
|
||||
|
@ -23,7 +25,8 @@ class ServiceWorkerController {
|
|||
|
||||
ServiceWorkerClient? get serviceWorkerClient => _serviceWorkerClient;
|
||||
|
||||
///Sets the service worker client.
|
||||
///Sets the client to capture service worker related callbacks.
|
||||
///A [ServiceWorkerClient] should be set before any service workers are active, e.g. a safe place is before any WebView instances are created or pages loaded.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - ServiceWorkerControllerCompat.setServiceWorkerClient](https://developer.android.com/reference/androidx/webkit/ServiceWorkerControllerCompat#setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat)))
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
import '../in_app_webview/in_app_webview_controller.dart';
|
||||
import '../in_app_webview/in_app_webview_settings.dart';
|
||||
|
||||
///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device.
|
||||
class WebViewFeature {
|
||||
|
@ -70,15 +72,15 @@ class WebViewFeature {
|
|||
@override
|
||||
String toString() => _value;
|
||||
|
||||
///
|
||||
///This feature covers [InAppWebViewController.createWebMessageChannel].
|
||||
static const CREATE_WEB_MESSAGE_CHANNEL =
|
||||
const WebViewFeature._internal("CREATE_WEB_MESSAGE_CHANNEL");
|
||||
|
||||
///
|
||||
///This feature covers [InAppWebViewSettings.disabledActionModeMenuItems].
|
||||
static const DISABLED_ACTION_MODE_MENU_ITEMS =
|
||||
const WebViewFeature._internal("DISABLED_ACTION_MODE_MENU_ITEMS");
|
||||
|
||||
///
|
||||
///This feature covers [InAppWebViewSettings.forceDark].
|
||||
static const FORCE_DARK = const WebViewFeature._internal("FORCE_DARK");
|
||||
|
||||
///
|
||||
|
|
|
@ -60,11 +60,6 @@ class InAppBrowserClassSettings {
|
|||
return options;
|
||||
}
|
||||
|
||||
static Map<String, dynamic> instanceToMap(
|
||||
InAppBrowserClassSettings settings) {
|
||||
return settings.toMap();
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return this.toMap();
|
||||
}
|
||||
|
|
|
@ -144,4 +144,6 @@ export 'webview_render_process_action.dart';
|
|||
export 'window_features.dart';
|
||||
export 'web_resource_error.dart';
|
||||
export 'web_resource_error_type.dart';
|
||||
export 'media_capture_state.dart';
|
||||
export 'media_capture_state.dart';
|
||||
export 'proxy_rule.dart';
|
||||
export 'proxy_scheme_filter.dart';
|
|
@ -0,0 +1,36 @@
|
|||
import 'proxy_scheme_filter.dart';
|
||||
|
||||
///Class that holds a scheme filter and a proxy URL.
|
||||
class ProxyRule {
|
||||
///Represents the scheme filter.
|
||||
ProxySchemeFilter? schemeFilter;
|
||||
|
||||
///Represents the proxy URL.
|
||||
String url;
|
||||
|
||||
ProxyRule({required this.url, this.schemeFilter});
|
||||
|
||||
///Gets a possible [ProxyRule] instance from a [Map] value.
|
||||
static ProxyRule? fromMap(Map<String, dynamic>? map) {
|
||||
return map != null
|
||||
? ProxyRule(
|
||||
url: map["url"],
|
||||
schemeFilter: ProxySchemeFilter.fromValue(map["schemeFilter"]))
|
||||
: null;
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toMap() {
|
||||
return {"url": url, "schemeFilter": schemeFilter?.toValue()};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return this.toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import '../android/proxy_controller.dart';
|
||||
|
||||
///Class that represent scheme filters used by [ProxyController].
|
||||
class ProxySchemeFilter {
|
||||
final String _value;
|
||||
|
||||
const ProxySchemeFilter._internal(this._value);
|
||||
|
||||
///Set of all values of [ProxySchemeFilter].
|
||||
static final Set<ProxySchemeFilter> values = [
|
||||
ProxySchemeFilter.MATCH_ALL_SCHEMES,
|
||||
ProxySchemeFilter.MATCH_HTTP,
|
||||
ProxySchemeFilter.MATCH_HTTPS,
|
||||
].toSet();
|
||||
|
||||
///Gets a possible [ProxySchemeFilter] instance from a [String] value.
|
||||
static ProxySchemeFilter? fromValue(String? value) {
|
||||
if (value != null) {
|
||||
try {
|
||||
return ProxySchemeFilter.values
|
||||
.firstWhere((element) => element.toValue() == value);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Gets [String] value.
|
||||
String toValue() => _value;
|
||||
|
||||
@override
|
||||
String toString() => _value;
|
||||
|
||||
///Matches all schemes.
|
||||
static const MATCH_ALL_SCHEMES = const ProxySchemeFilter._internal("*");
|
||||
|
||||
///HTTP scheme.
|
||||
static const MATCH_HTTP = const ProxySchemeFilter._internal("http");
|
||||
|
||||
///HTTPS scheme.
|
||||
static const MATCH_HTTPS =
|
||||
const ProxySchemeFilter._internal("https");
|
||||
|
||||
bool operator ==(value) => value == _value;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
// Example of the server https is taken from here: https://engineering.circle.com/https-authorized-certs-with-node-js-315e548354a2
|
||||
// Conversion of client1-crt.pem to certificate.pfx: https://stackoverflow.com/a/38408666/4637638
|
||||
const express = require('express')
|
||||
const https = require('https')
|
||||
const cors = require('cors')
|
||||
const auth = require('basic-auth')
|
||||
const app = express()
|
||||
const appHttps = express()
|
||||
const appAuthBasic = express()
|
||||
const express = require('express');
|
||||
const proxy = require('express-http-proxy');
|
||||
const https = require('https');
|
||||
const cors = require('cors');
|
||||
const auth = require('basic-auth');
|
||||
const app = express();
|
||||
const appHttps = express();
|
||||
const appAuthBasic = express();
|
||||
const appProxy = express();
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const bodyParser = require('body-parser');
|
||||
|
@ -20,6 +22,7 @@ var options = {
|
|||
rejectUnauthorized: false
|
||||
};
|
||||
|
||||
appHttps.use('/', proxy('www.google.com'));
|
||||
appHttps.get('/', (req, res) => {
|
||||
console.log(JSON.stringify(req.headers))
|
||||
const cert = req.connection.getPeerCertificate()
|
||||
|
@ -197,3 +200,7 @@ app.get("/test-download-file", (req, res) => {
|
|||
})
|
||||
|
||||
app.listen(8082)
|
||||
|
||||
appProxy.use(cors());
|
||||
appProxy.use('/', proxy('www.google.com'));
|
||||
appProxy.listen(8083);
|
|
@ -108,6 +108,11 @@
|
|||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "4.2.8",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
|
@ -155,6 +160,31 @@
|
|||
"vary": "~1.1.2"
|
||||
}
|
||||
},
|
||||
"express-http-proxy": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/express-http-proxy/-/express-http-proxy-1.6.3.tgz",
|
||||
"integrity": "sha512-/l77JHcOUrDUX8V67E287VEUQT0lbm71gdGVoodnlWBziarYKgMcpqT7xvh/HM8Jv52phw8Bd8tY+a7QjOr7Yg==",
|
||||
"requires": {
|
||||
"debug": "^3.0.1",
|
||||
"es6-promise": "^4.1.1",
|
||||
"raw-body": "^2.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"finalhandler": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"body-parser": "^1.19.0",
|
||||
"cors": "^2.8.5",
|
||||
"express": "latest",
|
||||
"express-http-proxy": "^1.6.3",
|
||||
"https": "latest",
|
||||
"multiparty": "^4.2.2"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue