Removed Android Hybrid Composition constraint to use the pull-to-refresh feature, Removed Android com.squareup.okhttp3:okhttp dependency

This commit is contained in:
Lorenzo Pichilli 2022-11-23 17:06:14 +01:00
parent b82baaa061
commit 962fc35ff7
9 changed files with 162 additions and 97 deletions

View File

@ -1,3 +1,8 @@
## 6.0.0-beta.16
- Removed Android Hybrid Composition constraint to use the pull-to-refresh feature
- Removed Android `com.squareup.okhttp3:okhttp` dependency
## 6.0.0-beta.15
- Automatically infer `useShouldOverrideUrlLoading`, `useOnLoadResource`, `useOnDownloadStart`, `useShouldInterceptAjaxRequest`, `useShouldInterceptFetchRequest`, `useShouldInterceptRequest`, `useOnRenderProcessGone`, `useOnNavigationResponse` settings if their value is `null` and the corresponding event is implemented by the WebView (`InAppWebView` and `HeadlessInAppWebView`, not `InAppBrowser`) before it's native initialization
@ -145,6 +150,10 @@
- Removed `URLProtectionSpace.iosIsProxy` property
- `historyUrl` and `baseUrl` of `InAppWebViewInitialData` can be `null`
## 5.7.2
- Removed Android Hybrid Composition constraint to use the pull-to-refresh feature
## 5.7.1+2
- Fixed Android `NullPointerException` on `InAppBrowserActivity.dispose`

View File

@ -48,7 +48,6 @@ android {
implementation 'androidx.webkit:webkit:1.5.0'
implementation 'androidx.browser:browser:1.4.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.squareup.okhttp3:okhttp:3.14.9'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
}
}

View File

@ -33,8 +33,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.Key;
import java.security.KeyStore;
@ -47,18 +49,11 @@ import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.SSLHandshakeException;
import io.flutter.plugin.common.MethodChannel;
import okhttp3.OkHttpClient;
public class Util {
@ -171,12 +166,38 @@ public class Util {
}
}
public static OkHttpClient getBasicOkHttpClient() {
return new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.build();
@Nullable
public static HttpURLConnection makeHttpRequest(String urlString, String method, @Nullable Map<String, String> headers) {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod(method);
if (headers != null) {
for (Map.Entry<String, String> header : headers.entrySet()) {
urlConnection.setRequestProperty(header.getKey(), header.getValue());
}
}
urlConnection.setConnectTimeout(15000); // 15 seconds
urlConnection.setReadTimeout(15000); // 15 seconds
urlConnection.setDoInput(true);
urlConnection.setInstanceFollowRedirects(true);
if ("GET".equalsIgnoreCase(method)) {
urlConnection.setDoOutput(false);
}
urlConnection.connect();
return urlConnection;
}
catch (Exception e) {
if (!(e instanceof SSLHandshakeException)) {
e.printStackTrace();
Log.e(LOG_TAG, e.getMessage());
}
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return null;
}
/**

View File

@ -2,32 +2,34 @@ package com.pichillilorenzo.flutter_inappwebview.content_blocker;
import android.os.Build;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.WebResourceResponse;
import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
import com.pichillilorenzo.flutter_inappwebview.Util;
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
import com.pichillilorenzo.flutter_inappwebview.types.WebResourceRequestExt;
import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebView;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import javax.net.ssl.SSLHandshakeException;
import okhttp3.Request;
import okhttp3.Response;
public class ContentBlockerHandler {
protected static final String LOG_TAG = "ContentBlockerHandler";
@ -48,10 +50,14 @@ public class ContentBlockerHandler {
}
@Nullable
public WebResourceResponse checkUrl(final InAppWebView webView, String url, ContentBlockerTriggerResourceType responseResourceType) throws URISyntaxException, InterruptedException, MalformedURLException {
public WebResourceResponse checkUrl(final InAppWebView webView, WebResourceRequestExt request,
ContentBlockerTriggerResourceType responseResourceType)
throws URISyntaxException, InterruptedException, MalformedURLException {
if (webView.customSettings.contentBlockers == null)
return null;
String url = request.getUrl();
URI u;
try {
u = new URI(url);
@ -182,36 +188,85 @@ public class ContentBlockerHandler {
if (scheme.equals("http") && (port == -1 || port == 80)) {
String urlHttps = url.replace("http://", "https://");
Request mRequest = new Request.Builder().url(urlHttps).build();
Response response = null;
HttpURLConnection urlConnection = Util.makeHttpRequest(urlHttps, request.getMethod(), request.getHeaders());
if (urlConnection != null) {
try {
byte[] dataBytes = Util.readAllBytes(urlConnection.getInputStream());
if (dataBytes == null) {
return null;
}
InputStream dataStream = new ByteArrayInputStream(dataBytes);
try {
response = Util.getBasicOkHttpClient().newCall(mRequest).execute();
byte[] dataBytes = response.body().bytes();
InputStream dataStream = new ByteArrayInputStream(dataBytes);
String encoding = urlConnection.getContentEncoding();
String contentType = urlConnection.getContentType();
if (contentType == null) {
contentType = "text/plain";
} else {
String[] contentTypeSplitted = contentType.split(";");
contentType = contentTypeSplitted[0].trim();
if (encoding == null) {
encoding = (contentTypeSplitted.length > 1 && contentTypeSplitted[1].contains("charset="))
? contentTypeSplitted[1].replace("charset=", "").trim()
: "utf-8";
}
}
String[] contentTypeSplitted = response.header("content-type", "text/plain").split(";");
String contentType = contentTypeSplitted[0].trim();
String encoding = (contentTypeSplitted.length > 1 && contentTypeSplitted[1].contains("charset="))
? contentTypeSplitted[1].replace("charset=", "").trim()
: "utf-8";
response.body().close();
response.close();
return new WebResourceResponse(contentType, encoding, dataStream);
} catch (Exception e) {
if (response != null) {
response.body().close();
response.close();
}
if (!(e instanceof SSLHandshakeException)) {
e.printStackTrace();
Log.e(LOG_TAG, e.getMessage());
String reasonPhrase = urlConnection.getResponseMessage();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && reasonPhrase != null) {
Map<String, String> responseHeaders = new HashMap<>();
for (Map.Entry<String, List<String>> responseHeader : urlConnection.getHeaderFields().entrySet()) {
responseHeaders.put(responseHeader.getKey(), TextUtils.join(",", responseHeader.getValue()));
}
return new WebResourceResponse(contentType,
encoding,
urlConnection.getResponseCode(),
reasonPhrase,
responseHeaders,
dataStream);
} else {
return new WebResourceResponse(contentType,
encoding,
dataStream);
}
} catch (Exception e) {
if (!(e instanceof SSLHandshakeException)) {
e.printStackTrace();
}
} finally {
urlConnection.disconnect();
}
}
// Request mRequest = new Request.Builder().url(urlHttps).build();
// Response response = null;
//
// try {
// response = Util.getBasicOkHttpClient().newCall(mRequest).execute();
// byte[] dataBytes = response.body().bytes();
// InputStream dataStream = new ByteArrayInputStream(dataBytes);
//
// String[] contentTypeSplitted = response.header("content-type", "text/plain").split(";");
//
// String contentType = contentTypeSplitted[0].trim();
// String encoding = (contentTypeSplitted.length > 1 && contentTypeSplitted[1].contains("charset="))
// ? contentTypeSplitted[1].replace("charset=", "").trim()
// : "utf-8";
//
// response.body().close();
// response.close();
//
// return new WebResourceResponse(contentType, encoding, dataStream);
//
// } catch (Exception e) {
// if (response != null) {
// response.body().close();
// response.close();
// }
// if (!(e instanceof SSLHandshakeException)) {
// e.printStackTrace();
// Log.e(LOG_TAG, e.getMessage());
// }
// }
}
break;
}
@ -221,48 +276,36 @@ public class ContentBlockerHandler {
}
@Nullable
public WebResourceResponse checkUrl(final InAppWebView webView, String url) throws URISyntaxException, InterruptedException, MalformedURLException {
ContentBlockerTriggerResourceType responseResourceType = getResourceTypeFromUrl(url);
return checkUrl(webView, url, responseResourceType);
public WebResourceResponse checkUrl(final InAppWebView webView, WebResourceRequestExt request) throws URISyntaxException, InterruptedException, MalformedURLException {
ContentBlockerTriggerResourceType responseResourceType = getResourceTypeFromUrl(request);
return checkUrl(webView, request, responseResourceType);
}
@Nullable
public WebResourceResponse checkUrl(final InAppWebView webView, String url, String contentType) throws URISyntaxException, InterruptedException, MalformedURLException {
public WebResourceResponse checkUrl(final InAppWebView webView, WebResourceRequestExt request, String contentType) throws URISyntaxException, InterruptedException, MalformedURLException {
ContentBlockerTriggerResourceType responseResourceType = getResourceTypeFromContentType(contentType);
return checkUrl(webView, url, responseResourceType);
return checkUrl(webView, request, responseResourceType);
}
public ContentBlockerTriggerResourceType getResourceTypeFromUrl(String url) {
public ContentBlockerTriggerResourceType getResourceTypeFromUrl(WebResourceRequestExt request) {
ContentBlockerTriggerResourceType responseResourceType = ContentBlockerTriggerResourceType.RAW;
String url = request.getUrl();
if (url.startsWith("http://") || url.startsWith("https://")) {
// make an HTTP "HEAD" request to the server for that URL. This will not return the full content of the URL.
Request mRequest = new Request.Builder().url(url).head().build();
Response response = null;
try {
response = Util.getBasicOkHttpClient().newCall(mRequest).execute();
if (response.header("content-type") != null) {
String[] contentTypeSplitted = response.header("content-type").split(";");
String contentType = contentTypeSplitted[0].trim();
String encoding = (contentTypeSplitted.length > 1 && contentTypeSplitted[1].contains("charset="))
? contentTypeSplitted[1].replace("charset=", "").trim()
: "utf-8";
response.body().close();
response.close();
responseResourceType = getResourceTypeFromContentType(contentType);
}
} catch (Exception e) {
if (response != null) {
response.body().close();
response.close();
}
if (!(e instanceof SSLHandshakeException)) {
HttpURLConnection urlConnection = Util.makeHttpRequest(url, "HEAD", request.getHeaders());
if (urlConnection != null) {
try {
String contentType = urlConnection.getContentType();
if (contentType != null) {
String[] contentTypeSplitted = contentType.split(";");
contentType = contentTypeSplitted[0].trim();
responseResourceType = getResourceTypeFromContentType(contentType);
}
} catch (Exception e) {
e.printStackTrace();
Log.e(LOG_TAG, e.getMessage());
} finally {
urlConnection.disconnect();
}
}
}

View File

@ -68,15 +68,13 @@ public class FlutterWebView implements PlatformWebView {
customSettings.useHybridComposition ? null : plugin.flutterView, userScripts);
displayListenerProxy.onPostWebViewInitialization(displayManager);
if (customSettings.useHybridComposition) {
// set MATCH_PARENT layout params to the WebView, otherwise it won't take all the available space!
webView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
PullToRefreshSettings pullToRefreshSettings = new PullToRefreshSettings();
pullToRefreshSettings.parse(pullToRefreshInitialSettings);
pullToRefreshLayout = new PullToRefreshLayout(context, plugin, id, pullToRefreshSettings);
pullToRefreshLayout.addView(webView);
pullToRefreshLayout.prepare();
}
// set MATCH_PARENT layout params to the WebView, otherwise it won't take all the available space!
webView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
PullToRefreshSettings pullToRefreshSettings = new PullToRefreshSettings();
pullToRefreshSettings.parse(pullToRefreshInitialSettings);
pullToRefreshLayout = new PullToRefreshLayout(context, plugin, id, pullToRefreshSettings);
pullToRefreshLayout.addView(webView);
pullToRefreshLayout.prepare();
FindInteractionController findInteractionController = new FindInteractionController(webView, plugin, id, null);
webView.findInteractionController = findInteractionController;

View File

@ -690,7 +690,7 @@ public class InAppWebViewClient extends WebViewClient {
if (customSchemeResponse != null) {
WebResourceResponse response = null;
try {
response = webView.contentBlockerHandler.checkUrl(webView, url, customSchemeResponse.getContentType());
response = webView.contentBlockerHandler.checkUrl(webView, request, customSchemeResponse.getContentType());
} catch (Exception e) {
e.printStackTrace();
}
@ -705,7 +705,7 @@ public class InAppWebViewClient extends WebViewClient {
WebResourceResponse response = null;
if (webView.contentBlockerHandler.getRuleList().size() > 0) {
try {
response = webView.contentBlockerHandler.checkUrl(webView, url);
response = webView.contentBlockerHandler.checkUrl(webView, request);
} catch (Exception e) {
e.printStackTrace();
}

View File

@ -3,11 +3,11 @@
export "FLUTTER_ROOT=/Users/lorenzopichilli/fvm/versions/3.3.6"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=integration_test/webview_flutter_test.dart"
export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"

View File

@ -668,11 +668,6 @@ class _InAppWebViewState extends State<InAppWebView> {
widget.initialOptions?.android.useHybridComposition) ??
true;
if (!useHybridComposition && widget.pullToRefreshController != null) {
throw new Exception(
"To use the pull-to-refresh feature, InAppWebViewSettings.useHybridComposition setting MUST be true!");
}
return PlatformViewLink(
viewType: 'com.pichillilorenzo/flutter_inappwebview',
surfaceFactory: (

View File

@ -1,6 +1,6 @@
name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 6.0.0-beta.15
version: 6.0.0-beta.16
homepage: https://inappwebview.dev/
repository: https://github.com/pichillilorenzo/flutter_inappwebview
issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues