diff --git a/CHANGELOG.md b/CHANGELOG.md index d0f3348b..50e3e05b 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` diff --git a/android/build.gradle b/android/build.gradle index 65e3160d..91fda8a6 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -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' } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java index 82f21139..e9b148c2 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java @@ -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 headers) { + HttpURLConnection urlConnection = null; + try { + URL url = new URL(urlString); + urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setRequestMethod(method); + if (headers != null) { + for (Map.Entry 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; } /** diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/content_blocker/ContentBlockerHandler.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/content_blocker/ContentBlockerHandler.java index 5df041a1..b159173a 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/content_blocker/ContentBlockerHandler.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/content_blocker/ContentBlockerHandler.java @@ -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 responseHeaders = new HashMap<>(); + for (Map.Entry> 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(); } } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/FlutterWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/FlutterWebView.java index faa4e750..32f0d39e 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/FlutterWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/FlutterWebView.java @@ -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; diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewClient.java index f7db6ed4..0fb32b9f 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewClient.java @@ -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(); } diff --git a/example/ios/Flutter/flutter_export_environment.sh b/example/ios/Flutter/flutter_export_environment.sh index 68661abb..70b11672 100755 --- a/example/ios/Flutter/flutter_export_environment.sh +++ b/example/ios/Flutter/flutter_export_environment.sh @@ -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" diff --git a/lib/src/in_app_webview/in_app_webview.dart b/lib/src/in_app_webview/in_app_webview.dart index 1e6de21b..53afcb5d 100755 --- a/lib/src/in_app_webview/in_app_webview.dart +++ b/lib/src/in_app_webview/in_app_webview.dart @@ -668,11 +668,6 @@ class _InAppWebViewState extends State { 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: ( diff --git a/pubspec.yaml b/pubspec.yaml index 9339ffcf..d3d3979e 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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