diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 57f6868a..697c2180 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -15,23 +15,41 @@
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
-
+
+
+
@@ -54,17 +72,26 @@
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
@@ -73,22 +100,40 @@
-
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
@@ -108,20 +153,6 @@
- a
- as
- IABWebViewClient
- clearCache
- Auth
- onReceivedHttpAuthRequestCallback
- Protection
- cast
- clear
- HttpAuthResponse
- ClientCertResponse
- onReceivedServerTrustAuthRequest
- ServerTrustAuthResponse
- certificCERServerTrustChallengecONTENTMODE
@@ -138,6 +169,20 @@
onfindAllgetFave
+ Uri
+ != "an
+ _channel
+ getHtml
+ url
+ getOptions
+ gestureR
+ _dispose
+ dispose
+ Long
+ custom
+ scheme
+ useOnLoadResource
+ useShouldOverrideUrlLoadingactivity.getPreferences(0)
@@ -175,7 +220,6 @@
-
@@ -192,28 +236,29 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
@@ -235,33 +280,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -291,6 +309,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -310,13 +376,6 @@
-
-
-
-
-
-
-
@@ -324,6 +383,13 @@
+
+
+
+
+
+
+
-
-
-
+
+
@@ -455,7 +520,7 @@
-
+
@@ -491,29 +556,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -688,27 +730,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -722,13 +743,6 @@
-
-
-
-
-
-
-
@@ -785,30 +799,10 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -822,26 +816,97 @@
-
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44e0366f..e18b5148 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,7 @@
- Added `HttpAuthCredentialDatabase` class
- Added `onReceivedServerTrustAuthRequest` and `onReceivedClientCertRequest` events to manage SSL requests
- Added `onFindResultReceived` event, `findAllAsync`, `findNext` and `clearMatches` methods
+- Added `getHtml` method
### BREAKING CHANGES
- Deleted `WebResourceRequest` class
@@ -34,6 +35,7 @@
- Updated `onLoadResource` event
- Updated `CookieManager` class
- WebView options are now available with the new corresponding classes: `InAppWebViewOptions`, `AndroidInAppWebViewOptions`, `iOSInAppWebViewOptions`, `InAppBrowserOptions`, `AndroidInAppBrowserOptions`, `iOSInAppBrowserOptions`, `AndroidChromeCustomTabsOptions` and `iOSSafariOptions`
+- Renamed `getFavicon` to `getFavicons`, now it returns a list of all favicons (`List`) found
## 1.2.1
diff --git a/android/build.gradle b/android/build.gradle
index e53aab08..ee6a0047 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -33,11 +33,34 @@ android {
lintOptions {
disable 'InvalidPackage'
}
+ dependencies {
+ implementation 'androidx.webkit:webkit:1.0.0'
+ implementation 'androidx.browser:browser:1.0.0'
+ implementation 'androidx.appcompat:appcompat:1.0.0'
+ implementation 'com.squareup.okhttp3:mockwebserver:3.11.0'
+ }
}
-dependencies {
- implementation 'androidx.webkit:webkit:1.0.0'
- implementation 'androidx.browser:browser:1.0.0'
- implementation 'androidx.appcompat:appcompat:1.0.0'
- implementation 'com.squareup.okhttp3:mockwebserver:3.11.0'
-}
+afterEvaluate {
+ def containsEmbeddingDependencies = false
+ for (def configuration : configurations.all) {
+ for (def dependency : configuration.dependencies) {
+ if (dependency.group == 'io.flutter' &&
+ dependency.name.startsWith('flutter_embedding') &&
+ dependency.isTransitive())
+ {
+ containsEmbeddingDependencies = true
+ break
+ }
+ }
+ }
+ if (!containsEmbeddingDependencies) {
+ android {
+ dependencies {
+ def lifecycle_version = "1.1.1"
+ compileOnly "android.arch.lifecycle:common-java8:$lifecycle_version"
+ compileOnly "android.arch.lifecycle:runtime:$lifecycle_version"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/gradle.properties b/android/gradle.properties
deleted file mode 100644
index 53ae0ae4..00000000
--- a/android/gradle.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-android.enableJetifier=true
-android.useAndroidX=true
-org.gradle.jvmargs=-Xmx1536M
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index aaa8e933..2a8cbcc8 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -1,9 +1,6 @@
-
-
-
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ChromeCustomTabs/ChromeCustomTabsActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ChromeCustomTabs/ChromeCustomTabsActivity.java
index 426d380f..09ad29be 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ChromeCustomTabs/ChromeCustomTabsActivity.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ChromeCustomTabs/ChromeCustomTabsActivity.java
@@ -36,7 +36,7 @@ public class ChromeCustomTabsActivity extends Activity {
options = new ChromeCustomTabsOptions();
options.parse((HashMap) b.getSerializable("options"));
- InAppBrowserFlutterPlugin.instance.chromeCustomTabsActivities.put(uuid, this);
+ InAppBrowserFlutterPlugin.inAppBrowser.chromeCustomTabsActivities.put(uuid, this);
customTabActivityHelper = new CustomTabActivityHelper();
builder = new CustomTabsIntent.Builder();
@@ -49,8 +49,8 @@ public class ChromeCustomTabsActivity extends Activity {
Map obj = new HashMap<>();
obj.put("uuid", uuid);
- InAppBrowserFlutterPlugin.instance.channel.invokeMethod("onChromeSafariBrowserOpened", obj);
- InAppBrowserFlutterPlugin.instance.channel.invokeMethod("onChromeSafariBrowserLoaded", obj);
+ InAppBrowserFlutterPlugin.inAppBrowser.channel.invokeMethod("onChromeSafariBrowserOpened", obj);
+ InAppBrowserFlutterPlugin.inAppBrowser.channel.invokeMethod("onChromeSafariBrowserLoaded", obj);
}
private void prepareCustomTabs() {
@@ -86,7 +86,7 @@ public class ChromeCustomTabsActivity extends Activity {
finish();
Map obj = new HashMap<>();
obj.put("uuid", uuid);
- InAppBrowserFlutterPlugin.instance.channel.invokeMethod("onChromeSafariBrowserClosed", obj);
+ InAppBrowserFlutterPlugin.inAppBrowser.channel.invokeMethod("onChromeSafariBrowserClosed", obj);
}
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ContentBlocker/ContentBlockerHandler.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ContentBlocker/ContentBlockerHandler.java
index 71848cfd..03440094 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ContentBlocker/ContentBlockerHandler.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ContentBlocker/ContentBlockerHandler.java
@@ -7,18 +7,16 @@ import android.util.Log;
import android.webkit.WebResourceResponse;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView;
+import com.pichillilorenzo.flutter_inappbrowser.Util;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
-import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
@@ -163,8 +161,7 @@ public class ContentBlockerHandler {
Response response = null;
try {
-
- response = webView.httpClient.newCall(mRequest).execute();
+ response = Util.getUnsafeOkHttpClient().newCall(mRequest).execute();
byte[] dataBytes = response.body().bytes();
InputStream dataStream = new ByteArrayInputStream(dataBytes);
@@ -195,7 +192,7 @@ public class ContentBlockerHandler {
}
public WebResourceResponse checkUrl(final InAppWebView webView, String url) throws URISyntaxException, InterruptedException, MalformedURLException {
- ContentBlockerTriggerResourceType responseResourceType = getResourceTypeFromUrl(webView, url);
+ ContentBlockerTriggerResourceType responseResourceType = getResourceTypeFromUrl(url);
return checkUrl(webView, url, responseResourceType);
}
@@ -204,7 +201,7 @@ public class ContentBlockerHandler {
return checkUrl(webView, url, responseResourceType);
}
- public ContentBlockerTriggerResourceType getResourceTypeFromUrl(InAppWebView webView, String url) {
+ public ContentBlockerTriggerResourceType getResourceTypeFromUrl(String url) {
ContentBlockerTriggerResourceType responseResourceType = ContentBlockerTriggerResourceType.RAW;
if (url.startsWith("http://") || url.startsWith("https://")) {
@@ -212,7 +209,7 @@ public class ContentBlockerHandler {
Request mRequest = new Request.Builder().url(url).head().build();
Response response = null;
try {
- response = webView.httpClient.newCall(mRequest).execute();
+ response = Util.getUnsafeOkHttpClient().newCall(mRequest).execute();
if (response.header("content-type") != null) {
String[] contentTypeSplitted = response.header("content-type").split(";");
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabaseHandler.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabaseHandler.java
index 3a7f981c..45fc541d 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabaseHandler.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabaseHandler.java
@@ -116,4 +116,8 @@ public class CredentialDatabaseHandler implements MethodChannel.MethodCallHandle
}
}
+ public void dispose() {
+ channel.setMethodCallHandler(null);
+ }
+
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java
index c291cea5..03ce8346 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java
@@ -289,10 +289,6 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
result.success(false);
}
break;
- case "dispose":
- dispose();
- result.success(true);
- break;
default:
result.notImplemented();
}
@@ -300,10 +296,12 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
@Override
public void dispose() {
+ channel.setMethodCallHandler(null);
if (webView != null) {
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
+ webView.dispose();
webView.destroy();
webView = null;
}
@@ -324,4 +322,12 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
webView.unlockInputConnection();
}
+ public void onFlutterViewAttached(View flutterView) {
+ webView.setContainerView(flutterView);
+ }
+
+ public void onFlutterViewDetached() {
+ webView.setContainerView(null);
+ }
+
}
\ No newline at end of file
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowser.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowser.java
new file mode 100644
index 00000000..27f3fa65
--- /dev/null
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowser.java
@@ -0,0 +1,739 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package com.pichillilorenzo.flutter_inappbrowser;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.os.Parcelable;
+import android.provider.Browser;
+import android.net.Uri;
+import android.os.Bundle;
+import android.webkit.MimeTypeMap;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.util.Log;
+
+import com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs.ChromeCustomTabsActivity;
+import com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs.CustomTabActivityHelper;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.common.MethodChannel.Result;
+import io.flutter.plugin.common.PluginRegistry.Registrar;
+
+/**
+ * InAppBrowser
+ */
+public class InAppBrowser implements MethodChannel.MethodCallHandler {
+
+ public Registrar registrar;
+ public MethodChannel channel;
+ public Map webViewActivities = new HashMap<>();
+ public Map chromeCustomTabsActivities = new HashMap<>();
+
+ protected static final String LOG_TAG = "IABFlutterPlugin";
+
+ public InAppBrowser(Registrar r) {
+ registrar = r;
+ channel = new MethodChannel(registrar.messenger(), "com.pichillilorenzo/flutter_inappbrowser");
+ channel.setMethodCallHandler(this);
+ }
+
+ @Override
+ public void onMethodCall(final MethodCall call, final Result result) {
+ String source;
+ String urlFile;
+ final Activity activity = registrar.activity();
+ final String uuid = (String) call.argument("uuid");
+
+ switch (call.method) {
+ case "open":
+ boolean isData = (boolean) call.argument("isData");
+ if (!isData) {
+ final String url_final = (String) call.argument("url");
+
+ final boolean useChromeSafariBrowser = (boolean) call.argument("useChromeSafariBrowser");
+
+ final Map headers = (Map) call.argument("headers");
+
+ Log.d(LOG_TAG, "use Chrome Custom Tabs = " + useChromeSafariBrowser);
+
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+
+ if (useChromeSafariBrowser) {
+
+ final String uuidFallback = (String) call.argument("uuidFallback");
+
+ final HashMap options = (HashMap) call.argument("options");
+
+ final HashMap optionsFallback = (HashMap) call.argument("optionsFallback");
+
+ open(activity, uuid, uuidFallback, url_final, options, headers, true, optionsFallback, result);
+ } else {
+
+ String url = url_final;
+
+ final HashMap options = (HashMap) call.argument("options");
+
+ final boolean isLocalFile = (boolean) call.argument("isLocalFile");
+ final boolean openWithSystemBrowser = (boolean) call.argument("openWithSystemBrowser");
+
+ if (isLocalFile) {
+ // check if the asset file exists
+ try {
+ url = Util.getUrlAsset(registrar, url);
+ } catch (IOException e) {
+ e.printStackTrace();
+ result.error(LOG_TAG, url + " asset file cannot be found!", e);
+ return;
+ }
+ }
+ // SYSTEM
+ if (openWithSystemBrowser) {
+ Log.d(LOG_TAG, "in system");
+ openExternal(activity, url, result);
+ } else {
+ //Load the dialer
+ if (url.startsWith(WebView.SCHEME_TEL)) {
+ try {
+ Log.d(LOG_TAG, "loading in dialer");
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ intent.setData(Uri.parse(url));
+ activity.startActivity(intent);
+ } catch (android.content.ActivityNotFoundException e) {
+ Log.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
+ }
+ }
+ // load in InAppBrowser
+ else {
+ Log.d(LOG_TAG, "loading in InAppBrowser");
+ open(activity, uuid, null, url, options, headers, false, null, result);
+ }
+ }
+ }
+ }
+ });
+ }
+ else {
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ HashMap options = (HashMap) call.argument("options");
+ String data = (String) call.argument("data");
+ String mimeType = (String) call.argument("mimeType");
+ String encoding = (String) call.argument("encoding");
+ String baseUrl = (String) call.argument("baseUrl");
+ openData(activity, uuid, options, data, mimeType, encoding, baseUrl);
+ result.success(true);
+ }
+ });
+ }
+ break;
+ case "getUrl":
+ result.success(getUrl(uuid));
+ break;
+ case "getTitle":
+ result.success(getTitle(uuid));
+ break;
+ case "getProgress":
+ result.success(getProgress(uuid));
+ break;
+ case "loadUrl":
+ loadUrl(uuid, (String) call.argument("url"), (Map) call.argument("headers"), result);
+ break;
+ case "postUrl":
+ postUrl(uuid, (String) call.argument("url"), (byte[]) call.argument("postData"), result);
+ break;
+ case "loadData":
+ {
+ String data = (String) call.argument("data");
+ String mimeType = (String) call.argument("mimeType");
+ String encoding = (String) call.argument("encoding");
+ String baseUrl = (String) call.argument("baseUrl");
+ loadData(uuid, data, mimeType, encoding, baseUrl, result);
+ }
+ break;
+ case "loadFile":
+ loadFile(uuid, (String) call.argument("url"), (Map) call.argument("headers"), result);
+ break;
+ case "close":
+ close(activity, uuid, result);
+ break;
+ case "injectScriptCode":
+ source = (String) call.argument("source");
+ injectScriptCode(uuid, source, result);
+ break;
+ case "injectScriptFile":
+ urlFile = (String) call.argument("urlFile");
+ injectScriptFile(uuid, urlFile);
+ result.success(true);
+ break;
+ case "injectStyleCode":
+ source = (String) call.argument("source");
+ injectStyleCode(uuid, source);
+ result.success(true);
+ break;
+ case "injectStyleFile":
+ urlFile = (String) call.argument("urlFile");
+ injectStyleFile(uuid, urlFile);
+ result.success(true);
+ break;
+ case "show":
+ show(uuid);
+ result.success(true);
+ break;
+ case "hide":
+ hide(uuid);
+ result.success(true);
+ break;
+ case "reload":
+ reload(uuid);
+ result.success(true);
+ break;
+ case "goBack":
+ goBack(uuid);
+ result.success(true);
+ break;
+ case "canGoBack":
+ result.success(canGoBack(uuid));
+ break;
+ case "goForward":
+ goForward(uuid);
+ result.success(true);
+ break;
+ case "canGoForward":
+ result.success(canGoForward(uuid));
+ break;
+ case "goBackOrForward":
+ goBackOrForward(uuid, (Integer) call.argument("steps"));
+ result.success(true);
+ break;
+ case "canGoBackOrForward":
+ result.success(canGoBackOrForward(uuid, (Integer) call.argument("steps")));
+ break;
+ case "stopLoading":
+ stopLoading(uuid);
+ result.success(true);
+ break;
+ case "isLoading":
+ result.success(isLoading(uuid));
+ break;
+ case "isHidden":
+ result.success(isHidden(uuid));
+ break;
+ case "takeScreenshot":
+ result.success(takeScreenshot(uuid));
+ break;
+ case "setOptions":
+ {
+ String optionsType = (String) call.argument("optionsType");
+ switch (optionsType){
+ case "InAppBrowserOptions":
+ InAppBrowserOptions inAppBrowserOptions = new InAppBrowserOptions();
+ HashMap inAppBrowserOptionsMap = (HashMap) call.argument("options");
+ inAppBrowserOptions.parse(inAppBrowserOptionsMap);
+ setOptions(uuid, inAppBrowserOptions, inAppBrowserOptionsMap);
+ break;
+ default:
+ result.error(LOG_TAG, "Options " + optionsType + " not available.", null);
+ }
+ }
+ result.success(true);
+ break;
+ case "getOptions":
+ result.success(getOptions(uuid));
+ break;
+ case "getCopyBackForwardList":
+ result.success(getCopyBackForwardList(uuid));
+ break;
+ case "startSafeBrowsing":
+ startSafeBrowsing(uuid, result);
+ break;
+ case "setSafeBrowsingWhitelist":
+ setSafeBrowsingWhitelist(uuid, (List) call.argument("hosts"), result);
+ break;
+ case "clearCache":
+ clearCache(uuid);
+ result.success(true);
+ break;
+ case "clearSslPreferences":
+ clearSslPreferences(uuid);
+ result.success(true);
+ break;
+ case "clearClientCertPreferences":
+ clearClientCertPreferences(uuid, result);
+ break;
+ case "findAllAsync":
+ String find = (String) call.argument("find");
+ findAllAsync(uuid, find);
+ result.success(true);
+ break;
+ case "findNext":
+ Boolean forward = (Boolean) call.argument("forward");
+ findNext(uuid, forward, result);
+ break;
+ case "clearMatches":
+ clearMatches(uuid, result);
+ break;
+ default:
+ result.notImplemented();
+ }
+
+ }
+
+ private void injectScriptCode(String uuid, String source, final Result result) {
+ final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null) {
+ inAppBrowserActivity.injectScriptCode(source, result);
+ } else {
+ Log.d(LOG_TAG, "webView is null");
+ }
+ }
+
+ private void injectScriptFile(String uuid, String urlFile) {
+ final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null) {
+ inAppBrowserActivity.injectScriptFile(urlFile);
+ }
+ }
+
+ private void injectStyleCode(String uuid, String source) {
+ final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null) {
+ inAppBrowserActivity.injectStyleCode(source);
+ }
+ }
+
+ private void injectStyleFile(String uuid, String urlFile) {
+ final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null) {
+ inAppBrowserActivity.injectStyleFile(urlFile);
+ }
+ }
+
+ public static String getMimeType(String url) {
+ String type = null;
+ String extension = MimeTypeMap.getFileExtensionFromUrl(url);
+ if (extension != null) {
+ type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ }
+ return type;
+ }
+
+ /**
+ * Display a new browser with the specified URL.
+ *
+ * @param url the url to load.
+ * @param result
+ * @return "" if ok, or error message.
+ */
+ public void openExternal(Activity activity, String url, Result result) {
+ try {
+ Intent intent;
+ intent = new Intent(Intent.ACTION_VIEW);
+ // Omitting the MIME type for file: URLs causes "No Activity found to handle Intent".
+ // Adding the MIME type to http: URLs causes them to not be handled by the downloader.
+ Uri uri = Uri.parse(url);
+ if ("file".equals(uri.getScheme())) {
+ intent.setDataAndType(uri, getMimeType(url));
+ } else {
+ intent.setData(uri);
+ }
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName());
+ // CB-10795: Avoid circular loops by preventing it from opening in the current app
+ this.openExternalExcludeCurrentApp(activity, intent);
+ result.success(true);
+ // not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it
+ } catch (java.lang.RuntimeException e) {
+ Log.d(LOG_TAG, url + " cannot be opened: " + e.toString());
+ result.error(LOG_TAG, url + " cannot be opened!", null);
+ }
+ }
+
+ /**
+ * Opens the intent, providing a chooser that excludes the current app to avoid
+ * circular loops.
+ */
+ private void openExternalExcludeCurrentApp(Activity activity, Intent intent) {
+ String currentPackage = activity.getPackageName();
+ boolean hasCurrentPackage = false;
+ PackageManager pm = activity.getPackageManager();
+ List activities = pm.queryIntentActivities(intent, 0);
+ ArrayList targetIntents = new ArrayList();
+ for (ResolveInfo ri : activities) {
+ if (!currentPackage.equals(ri.activityInfo.packageName)) {
+ Intent targetIntent = (Intent) intent.clone();
+ targetIntent.setPackage(ri.activityInfo.packageName);
+ targetIntents.add(targetIntent);
+ } else {
+ hasCurrentPackage = true;
+ }
+ }
+ // If the current app package isn't a target for this URL, then use
+ // the normal launch behavior
+ if (!hasCurrentPackage || targetIntents.size() == 0) {
+ activity.startActivity(intent);
+ }
+ // If there's only one possible intent, launch it directly
+ else if (targetIntents.size() == 1) {
+ activity.startActivity(targetIntents.get(0));
+ }
+ // Otherwise, show a custom chooser without the current app listed
+ else if (targetIntents.size() > 0) {
+ Intent chooser = Intent.createChooser(targetIntents.remove(targetIntents.size() - 1), null);
+ chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[]{}));
+ activity.startActivity(chooser);
+ }
+ }
+
+ public void open(Activity activity, String uuid, String uuidFallback, String url, HashMap options, Map headers, boolean useChromeSafariBrowser, HashMap optionsFallback, Result result) {
+
+ Intent intent = null;
+ Bundle extras = new Bundle();
+ extras.putString("fromActivity", activity.getClass().getName());
+ extras.putString("url", url);
+ extras.putBoolean("isData", false);
+ extras.putString("uuid", uuid);
+ extras.putSerializable("options", options);
+ extras.putSerializable("headers", (Serializable) headers);
+
+ if (useChromeSafariBrowser && CustomTabActivityHelper.isAvailable(activity)) {
+ intent = new Intent(activity, ChromeCustomTabsActivity.class);
+ }
+ // check for webview fallback
+ else if (useChromeSafariBrowser && !CustomTabActivityHelper.isAvailable(activity) && !uuidFallback.isEmpty()) {
+ Log.d(LOG_TAG, "WebView fallback declared.");
+ // overwrite with extras fallback parameters
+ extras.putString("uuid", uuidFallback);
+ if (optionsFallback != null)
+ extras.putSerializable("options", optionsFallback);
+ else
+ extras.putSerializable("options", (new InAppBrowserOptions()).getHashMap());
+ extras.putSerializable("headers", (Serializable) headers);
+ intent = new Intent(activity, InAppBrowserActivity.class);
+ }
+ // native webview
+ else if (!useChromeSafariBrowser) {
+ intent = new Intent(activity, InAppBrowserActivity.class);
+ }
+ else {
+ Log.d(LOG_TAG, "No WebView fallback declared.");
+ }
+
+ if (intent != null) {
+ intent.putExtras(extras);
+ activity.startActivity(intent);
+ result.success(true);
+ return;
+ }
+
+ result.error(LOG_TAG, "No WebView fallback declared.", null);
+ }
+
+ public void openData(Activity activity, String uuid, HashMap options, String data, String mimeType, String encoding, String baseUrl) {
+ Intent intent = new Intent(activity, InAppBrowserActivity.class);
+ Bundle extras = new Bundle();
+
+ extras.putBoolean("isData", true);
+ extras.putString("uuid", uuid);
+ extras.putSerializable("options", options);
+ extras.putString("data", data);
+ extras.putString("mimeType", mimeType);
+ extras.putString("encoding", encoding);
+ extras.putString("baseUrl", baseUrl);
+
+ intent.putExtras(extras);
+ activity.startActivity(intent);
+ }
+
+ private String getUrl(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.getUrl();
+ return null;
+ }
+
+ private String getTitle(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.getWebViewTitle();
+ return null;
+ }
+
+ private Integer getProgress(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.getProgress();
+ return null;
+ }
+
+ public void loadUrl(String uuid, String url, Map headers, Result result) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null) {
+ if (headers != null)
+ inAppBrowserActivity.loadUrl(url, headers, result);
+ else
+ inAppBrowserActivity.loadUrl(url, result);
+ }
+ }
+
+ public void postUrl(String uuid, String url, byte[] postData, Result result) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.postUrl(url, postData, result);
+ }
+
+ public void loadData(String uuid, String data, String mimeType, String encoding, String baseUrl, Result result) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.loadData(data, mimeType, encoding, baseUrl, result);
+ }
+
+ public void loadFile(String uuid, String url, Map headers, Result result) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null) {
+ if (headers != null)
+ inAppBrowserActivity.loadFile(url, headers, result);
+ else
+ inAppBrowserActivity.loadFile(url, result);
+ }
+ }
+
+ public void show(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.show();
+ }
+
+ public void hide(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.hide();
+ }
+
+ public void reload(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.reload();
+ }
+
+ public boolean isLoading(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.isLoading();
+ return false;
+ }
+
+ public boolean isHidden(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.isHidden;
+ return false;
+ }
+
+ public void stopLoading(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.stopLoading();
+ }
+
+ public void goBack(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.goBack();
+ }
+
+ public boolean canGoBack(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.canGoBack();
+ return false;
+ }
+
+ public void goForward(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.goForward();
+ }
+
+ public boolean canGoForward(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.canGoForward();
+ return false;
+ }
+
+ public void goBackOrForward(String uuid, int steps) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.goBackOrForward(steps);
+ }
+
+ public boolean canGoBackOrForward(String uuid, int steps) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.canGoBackOrForward(steps);
+ return false;
+ }
+
+ public void close(Activity activity, final String uuid, final Result result) {
+ final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null) {
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+
+ Map obj = new HashMap<>();
+ obj.put("uuid", uuid);
+ channel.invokeMethod("onExit", obj);
+
+ // The JS protects against multiple calls, so this should happen only when
+ // close() is called by other native code.
+ if (inAppBrowserActivity == null) {
+ if (result != null) {
+ result.success(true);
+ }
+ return;
+ }
+
+ inAppBrowserActivity.webView.setWebViewClient(new WebViewClient() {
+ // NB: wait for about:blank before dismissing
+ public void onPageFinished(WebView view, String url) {
+ inAppBrowserActivity.close();
+ }
+ });
+ // NB: From SDK 19: "If you call methods on WebView from any thread
+ // other than your app's UI thread, it can cause unexpected results."
+ // http://developer.android.com/guide/webapps/migrating.html#Threads
+ inAppBrowserActivity.webView.loadUrl("about:blank");
+ if (result != null) {
+ result.success(true);
+ }
+ }
+ });
+ }
+ else if (result != null) {
+ result.success(true);
+ }
+ }
+
+ public byte[] takeScreenshot(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.takeScreenshot();
+ return null;
+ }
+
+ public void setOptions(String uuid, InAppBrowserOptions options, HashMap optionsMap) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.setOptions(options, optionsMap);
+ }
+
+ public HashMap getOptions(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.getOptions();
+ return null;
+ }
+
+ public HashMap getCopyBackForwardList(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ return inAppBrowserActivity.getCopyBackForwardList();
+ return null;
+ }
+
+ public void startSafeBrowsing(String uuid, Result result) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.startSafeBrowsing(result);
+ result.success(false);
+ }
+
+ public void setSafeBrowsingWhitelist(String uuid, List hosts, Result result) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.setSafeBrowsingWhitelist(hosts, result);
+ result.success(false);
+ }
+
+ public void clearCache(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.clearCache();
+ }
+
+ public void clearSslPreferences(String uuid) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.clearSslPreferences();
+ }
+
+ public void clearClientCertPreferences(String uuid, Result result) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.clearClientCertPreferences(result);
+ result.success(false);
+ }
+
+ public void findAllAsync(String uuid, String find) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.findAllAsync(find);
+ }
+
+ public void findNext(String uuid, Boolean forward, Result result) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.findNext(forward, result);
+ result.success(false);
+ }
+
+ public void clearMatches(String uuid, Result result) {
+ InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
+ if (inAppBrowserActivity != null)
+ inAppBrowserActivity.clearMatches(result);
+ result.success(false);
+ }
+
+ public void dispose() {
+ channel.setMethodCallHandler(null);
+ for ( InAppBrowserActivity activity : webViewActivities.values()) {
+ activity.dispose();
+ }
+ }
+}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java
index 87b112f1..1494aaf2 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java
@@ -1,6 +1,5 @@
package com.pichillilorenzo.flutter_inappbrowser;
-import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -18,6 +17,9 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.SearchView;
@@ -26,13 +28,10 @@ import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebViewOptions
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import io.flutter.app.FlutterActivity;
-import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.MethodChannel;
public class InAppBrowserActivity extends AppCompatActivity {
@@ -71,7 +70,7 @@ public class InAppBrowserActivity extends AppCompatActivity {
webViewOptions.parse(optionsMap);
webView.options = webViewOptions;
- InAppBrowserFlutterPlugin.instance.webViewActivities.put(uuid, this);
+ InAppBrowserFlutterPlugin.inAppBrowser.webViewActivities.put(uuid, this);
actionBar = getSupportActionBar();
@@ -93,7 +92,7 @@ public class InAppBrowserActivity extends AppCompatActivity {
Map obj = new HashMap<>();
obj.put("uuid", uuid);
- InAppBrowserFlutterPlugin.instance.channel.invokeMethod("onBrowserCreated", obj);
+ InAppBrowserFlutterPlugin.inAppBrowser.channel.invokeMethod("onBrowserCreated", obj);
}
@@ -257,7 +256,7 @@ public class InAppBrowserActivity extends AppCompatActivity {
if (canGoBack())
goBack();
else if (options.closeOnCannotGoBack)
- InAppBrowserFlutterPlugin.instance.close(this, uuid, null);
+ InAppBrowserFlutterPlugin.inAppBrowser.close(this, uuid, null);
return true;
}
return super.onKeyDown(keyCode, event);
@@ -356,7 +355,7 @@ public class InAppBrowserActivity extends AppCompatActivity {
}
public void closeButtonClicked(MenuItem item) {
- InAppBrowserFlutterPlugin.instance.close(this, uuid, null);
+ InAppBrowserFlutterPlugin.inAppBrowser.close(this, uuid, null);
}
public byte[] takeScreenshot() {
@@ -522,4 +521,18 @@ public class InAppBrowserActivity extends AppCompatActivity {
else
result.success(false);
}
+
+ public void dispose() {
+ if (webView != null) {
+ webView.setWebChromeClient(new WebChromeClient());
+ webView.setWebViewClient(new WebViewClient() {
+ public void onPageFinished(WebView view, String url) {
+ webView.dispose();
+ webView.destroy();
+ webView = null;
+ }
+ });
+ webView.loadUrl("about:blank");
+ }
+ }
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java
index 9eb4e24e..a4247e05 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java
@@ -1,754 +1,65 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
package com.pichillilorenzo.flutter_inappbrowser;
-import android.app.Activity;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.os.Build;
-import android.os.Parcelable;
-import android.provider.Browser;
-import android.net.Uri;
-import android.os.Bundle;
-import android.webkit.MimeTypeMap;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.util.Log;
-import com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs.ChromeCustomTabsActivity;
-import com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs.CustomTabActivityHelper;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
-import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
-import io.flutter.plugin.common.MethodChannel.Result;
-import io.flutter.plugin.common.PluginRegistry.Registrar;
+import io.flutter.plugin.common.PluginRegistry;
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
-/**
- * InAppBrowserFlutterPlugin
- */
-public class InAppBrowserFlutterPlugin implements MethodCallHandler {
-
- public static InAppBrowserFlutterPlugin instance;
- public Registrar registrar;
+public class InAppBrowserFlutterPlugin implements FlutterPlugin {
+ public PluginRegistry.Registrar registrar;
public MethodChannel channel;
- public Map webViewActivities = new HashMap<>();
- public Map chromeCustomTabsActivities = new HashMap<>();
- protected static final String LOG_TAG = "IABFlutterPlugin";
+ protected static final String LOG_TAG = "InAppBrowserFlutterPlugin";
- public InAppBrowserFlutterPlugin(Registrar r) {
- registrar = r;
- channel = new MethodChannel(registrar.messenger(), "com.pichillilorenzo/flutter_inappbrowser");
- channel.setMethodCallHandler(this);
- }
+ public static InAppBrowser inAppBrowser;
+ public static MyCookieManager myCookieManager;
+ public static CredentialDatabaseHandler credentialDatabaseHandler;
- /**
- * Plugin registration.
- */
- public static void registerWith(Registrar registrar) {
- Activity activity = registrar.activity();
- // registrar.activity() may return null because of Flutter's background execution feature
- // described here: https://medium.com/flutter-io/executing-dart-in-the-background-with-flutter-plugins-and-geofencing-2b3e40a1a124
- if (activity != null) {
- instance = new InAppBrowserFlutterPlugin(registrar);
+ public InAppBrowserFlutterPlugin() {}
- new MyCookieManager(registrar);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- new CredentialDatabaseHandler(registrar);
- }
+ public static void registerWith(PluginRegistry.Registrar registrar) {
+ inAppBrowser = new InAppBrowser(registrar);
- registrar
- .platformViewRegistry()
- .registerViewFactory(
- "com.pichillilorenzo/flutter_inappwebview", new FlutterWebViewFactory(registrar, registrar.view()));
+ registrar
+ .platformViewRegistry()
+ .registerViewFactory(
+ "com.pichillilorenzo/flutter_inappwebview", new FlutterWebViewFactory(registrar, registrar.view()));
+ new MyCookieManager(registrar);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ new CredentialDatabaseHandler(registrar);
}
}
@Override
- public void onMethodCall(final MethodCall call, final Result result) {
- String source;
- String urlFile;
- final Activity activity = registrar.activity();
- final String uuid = (String) call.argument("uuid");
-
- switch (call.method) {
- case "open":
- boolean isData = (boolean) call.argument("isData");
- if (!isData) {
- final String url_final = (String) call.argument("url");
-
- final boolean useChromeSafariBrowser = (boolean) call.argument("useChromeSafariBrowser");
-
- final Map headers = (Map) call.argument("headers");
-
- Log.d(LOG_TAG, "use Chrome Custom Tabs = " + useChromeSafariBrowser);
-
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
-
- if (useChromeSafariBrowser) {
-
- final String uuidFallback = (String) call.argument("uuidFallback");
-
- final HashMap options = (HashMap) call.argument("options");
-
- final HashMap optionsFallback = (HashMap) call.argument("optionsFallback");
-
- open(activity, uuid, uuidFallback, url_final, options, headers, true, optionsFallback, result);
- } else {
-
- String url = url_final;
-
- final HashMap options = (HashMap) call.argument("options");
-
- final boolean isLocalFile = (boolean) call.argument("isLocalFile");
- final boolean openWithSystemBrowser = (boolean) call.argument("openWithSystemBrowser");
-
- if (isLocalFile) {
- // check if the asset file exists
- try {
- url = Util.getUrlAsset(registrar, url);
- } catch (IOException e) {
- e.printStackTrace();
- result.error(LOG_TAG, url + " asset file cannot be found!", e);
- return;
- }
- }
- // SYSTEM
- if (openWithSystemBrowser) {
- Log.d(LOG_TAG, "in system");
- openExternal(activity, url, result);
- } else {
- //Load the dialer
- if (url.startsWith(WebView.SCHEME_TEL)) {
- try {
- Log.d(LOG_TAG, "loading in dialer");
- Intent intent = new Intent(Intent.ACTION_DIAL);
- intent.setData(Uri.parse(url));
- activity.startActivity(intent);
- } catch (android.content.ActivityNotFoundException e) {
- Log.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
- }
- }
- // load in InAppBrowserFlutterPlugin
- else {
- Log.d(LOG_TAG, "loading in InAppBrowserFlutterPlugin");
- open(activity, uuid, null, url, options, headers, false, null, result);
- }
- }
- }
- }
- });
- }
- else {
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- HashMap options = (HashMap) call.argument("options");
- String data = (String) call.argument("data");
- String mimeType = (String) call.argument("mimeType");
- String encoding = (String) call.argument("encoding");
- String baseUrl = (String) call.argument("baseUrl");
- openData(activity, uuid, options, data, mimeType, encoding, baseUrl);
- result.success(true);
- }
- });
- }
- break;
- case "getUrl":
- result.success(getUrl(uuid));
- break;
- case "getTitle":
- result.success(getTitle(uuid));
- break;
- case "getProgress":
- result.success(getProgress(uuid));
- break;
- case "loadUrl":
- loadUrl(uuid, (String) call.argument("url"), (Map) call.argument("headers"), result);
- break;
- case "postUrl":
- postUrl(uuid, (String) call.argument("url"), (byte[]) call.argument("postData"), result);
- break;
- case "loadData":
- {
- String data = (String) call.argument("data");
- String mimeType = (String) call.argument("mimeType");
- String encoding = (String) call.argument("encoding");
- String baseUrl = (String) call.argument("baseUrl");
- loadData(uuid, data, mimeType, encoding, baseUrl, result);
- }
- break;
- case "loadFile":
- loadFile(uuid, (String) call.argument("url"), (Map) call.argument("headers"), result);
- break;
- case "close":
- close(activity, uuid, result);
- break;
- case "injectScriptCode":
- source = (String) call.argument("source");
- injectScriptCode(uuid, source, result);
- break;
- case "injectScriptFile":
- urlFile = (String) call.argument("urlFile");
- injectScriptFile(uuid, urlFile);
- result.success(true);
- break;
- case "injectStyleCode":
- source = (String) call.argument("source");
- injectStyleCode(uuid, source);
- result.success(true);
- break;
- case "injectStyleFile":
- urlFile = (String) call.argument("urlFile");
- injectStyleFile(uuid, urlFile);
- result.success(true);
- break;
- case "show":
- show(uuid);
- result.success(true);
- break;
- case "hide":
- hide(uuid);
- result.success(true);
- break;
- case "reload":
- reload(uuid);
- result.success(true);
- break;
- case "goBack":
- goBack(uuid);
- result.success(true);
- break;
- case "canGoBack":
- result.success(canGoBack(uuid));
- break;
- case "goForward":
- goForward(uuid);
- result.success(true);
- break;
- case "canGoForward":
- result.success(canGoForward(uuid));
- break;
- case "goBackOrForward":
- goBackOrForward(uuid, (Integer) call.argument("steps"));
- result.success(true);
- break;
- case "canGoBackOrForward":
- result.success(canGoBackOrForward(uuid, (Integer) call.argument("steps")));
- break;
- case "stopLoading":
- stopLoading(uuid);
- result.success(true);
- break;
- case "isLoading":
- result.success(isLoading(uuid));
- break;
- case "isHidden":
- result.success(isHidden(uuid));
- break;
- case "takeScreenshot":
- result.success(takeScreenshot(uuid));
- break;
- case "setOptions":
- {
- String optionsType = (String) call.argument("optionsType");
- switch (optionsType){
- case "InAppBrowserOptions":
- InAppBrowserOptions inAppBrowserOptions = new InAppBrowserOptions();
- HashMap inAppBrowserOptionsMap = (HashMap) call.argument("options");
- inAppBrowserOptions.parse(inAppBrowserOptionsMap);
- setOptions(uuid, inAppBrowserOptions, inAppBrowserOptionsMap);
- break;
- default:
- result.error(LOG_TAG, "Options " + optionsType + " not available.", null);
- }
- }
- result.success(true);
- break;
- case "getOptions":
- result.success(getOptions(uuid));
- break;
- case "getCopyBackForwardList":
- result.success(getCopyBackForwardList(uuid));
- break;
- case "startSafeBrowsing":
- startSafeBrowsing(uuid, result);
- break;
- case "setSafeBrowsingWhitelist":
- setSafeBrowsingWhitelist(uuid, (List) call.argument("hosts"), result);
- break;
- case "clearCache":
- clearCache(uuid);
- result.success(true);
- break;
- case "clearSslPreferences":
- clearSslPreferences(uuid);
- result.success(true);
- break;
- case "clearClientCertPreferences":
- clearClientCertPreferences(uuid, result);
- break;
- case "findAllAsync":
- String find = (String) call.argument("find");
- findAllAsync(uuid, find);
- result.success(true);
- break;
- case "findNext":
- Boolean forward = (Boolean) call.argument("forward");
- findNext(uuid, forward, result);
- break;
- case "clearMatches":
- clearMatches(uuid, result);
- break;
- default:
- result.notImplemented();
- }
-
- }
-
- private void injectScriptCode(String uuid, String source, final Result result) {
- final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null) {
- inAppBrowserActivity.injectScriptCode(source, result);
- } else {
- Log.d(LOG_TAG, "webView is null");
+ public void onAttachedToEngine(FlutterPluginBinding binding) {
+ //BinaryMessenger messenger = binding.getFlutterEngine().getDartExecutor();
+ inAppBrowser = new InAppBrowser(registrar);
+ binding
+ .getFlutterEngine()
+ .getPlatformViewsController()
+ .getRegistry()
+ .registerViewFactory(
+ "com.pichillilorenzo/flutter_inappwebview", new FlutterWebViewFactory(registrar,null));
+ myCookieManager = new MyCookieManager(registrar);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ credentialDatabaseHandler = new CredentialDatabaseHandler(registrar);
}
}
- private void injectScriptFile(String uuid, String urlFile) {
- final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null) {
- inAppBrowserActivity.injectScriptFile(urlFile);
+ @Override
+ public void onDetachedFromEngine(FlutterPluginBinding binding) {
+ if (inAppBrowser != null) {
+ inAppBrowser.dispose();
+ inAppBrowser = null;
}
- }
-
- private void injectStyleCode(String uuid, String source) {
- final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null) {
- inAppBrowserActivity.injectStyleCode(source);
+ if (myCookieManager != null) {
+ myCookieManager.dispose();
+ myCookieManager = null;
}
- }
-
- private void injectStyleFile(String uuid, String urlFile) {
- final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null) {
- inAppBrowserActivity.injectStyleFile(urlFile);
+ if (credentialDatabaseHandler != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ credentialDatabaseHandler.dispose();
+ credentialDatabaseHandler = null;
}
}
-
- public static String getMimeType(String url) {
- String type = null;
- String extension = MimeTypeMap.getFileExtensionFromUrl(url);
- if (extension != null) {
- type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
- }
- return type;
- }
-
- /**
- * Display a new browser with the specified URL.
- *
- * @param url the url to load.
- * @param result
- * @return "" if ok, or error message.
- */
- public void openExternal(Activity activity, String url, Result result) {
- try {
- Intent intent;
- intent = new Intent(Intent.ACTION_VIEW);
- // Omitting the MIME type for file: URLs causes "No Activity found to handle Intent".
- // Adding the MIME type to http: URLs causes them to not be handled by the downloader.
- Uri uri = Uri.parse(url);
- if ("file".equals(uri.getScheme())) {
- intent.setDataAndType(uri, getMimeType(url));
- } else {
- intent.setData(uri);
- }
- intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName());
- // CB-10795: Avoid circular loops by preventing it from opening in the current app
- this.openExternalExcludeCurrentApp(activity, intent);
- result.success(true);
- // not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it
- } catch (java.lang.RuntimeException e) {
- Log.d(LOG_TAG, url + " cannot be opened: " + e.toString());
- result.error(LOG_TAG, url + " cannot be opened!", null);
- }
- }
-
- /**
- * Opens the intent, providing a chooser that excludes the current app to avoid
- * circular loops.
- */
- private void openExternalExcludeCurrentApp(Activity activity, Intent intent) {
- String currentPackage = activity.getPackageName();
- boolean hasCurrentPackage = false;
- PackageManager pm = activity.getPackageManager();
- List activities = pm.queryIntentActivities(intent, 0);
- ArrayList targetIntents = new ArrayList();
- for (ResolveInfo ri : activities) {
- if (!currentPackage.equals(ri.activityInfo.packageName)) {
- Intent targetIntent = (Intent) intent.clone();
- targetIntent.setPackage(ri.activityInfo.packageName);
- targetIntents.add(targetIntent);
- } else {
- hasCurrentPackage = true;
- }
- }
- // If the current app package isn't a target for this URL, then use
- // the normal launch behavior
- if (!hasCurrentPackage || targetIntents.size() == 0) {
- activity.startActivity(intent);
- }
- // If there's only one possible intent, launch it directly
- else if (targetIntents.size() == 1) {
- activity.startActivity(targetIntents.get(0));
- }
- // Otherwise, show a custom chooser without the current app listed
- else if (targetIntents.size() > 0) {
- Intent chooser = Intent.createChooser(targetIntents.remove(targetIntents.size() - 1), null);
- chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[]{}));
- activity.startActivity(chooser);
- }
- }
-
- public void open(Activity activity, String uuid, String uuidFallback, String url, HashMap options, Map headers, boolean useChromeSafariBrowser, HashMap optionsFallback, Result result) {
-
- Intent intent = null;
- Bundle extras = new Bundle();
- extras.putString("fromActivity", activity.getClass().getName());
- extras.putString("url", url);
- extras.putBoolean("isData", false);
- extras.putString("uuid", uuid);
- extras.putSerializable("options", options);
- extras.putSerializable("headers", (Serializable) headers);
-
- if (useChromeSafariBrowser && CustomTabActivityHelper.isAvailable(activity)) {
- intent = new Intent(activity, ChromeCustomTabsActivity.class);
- }
- // check for webview fallback
- else if (useChromeSafariBrowser && !CustomTabActivityHelper.isAvailable(activity) && !uuidFallback.isEmpty()) {
- Log.d(LOG_TAG, "WebView fallback declared.");
- // overwrite with extras fallback parameters
- extras.putString("uuid", uuidFallback);
- if (optionsFallback != null)
- extras.putSerializable("options", optionsFallback);
- else
- extras.putSerializable("options", (new InAppBrowserOptions()).getHashMap());
- extras.putSerializable("headers", (Serializable) headers);
- intent = new Intent(activity, InAppBrowserActivity.class);
- }
- // native webview
- else if (!useChromeSafariBrowser) {
- intent = new Intent(activity, InAppBrowserActivity.class);
- }
- else {
- Log.d(LOG_TAG, "No WebView fallback declared.");
- }
-
- if (intent != null) {
- intent.putExtras(extras);
- activity.startActivity(intent);
- result.success(true);
- return;
- }
-
- result.error(LOG_TAG, "No WebView fallback declared.", null);
- }
-
- public void openData(Activity activity, String uuid, HashMap options, String data, String mimeType, String encoding, String baseUrl) {
- Intent intent = new Intent(activity, InAppBrowserActivity.class);
- Bundle extras = new Bundle();
-
- extras.putBoolean("isData", true);
- extras.putString("uuid", uuid);
- extras.putSerializable("options", options);
- extras.putString("data", data);
- extras.putString("mimeType", mimeType);
- extras.putString("encoding", encoding);
- extras.putString("baseUrl", baseUrl);
-
- intent.putExtras(extras);
- activity.startActivity(intent);
- }
-
- private String getUrl(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.getUrl();
- return null;
- }
-
- private String getTitle(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.getWebViewTitle();
- return null;
- }
-
- private Integer getProgress(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.getProgress();
- return null;
- }
-
- public void loadUrl(String uuid, String url, Map headers, Result result) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null) {
- if (headers != null)
- inAppBrowserActivity.loadUrl(url, headers, result);
- else
- inAppBrowserActivity.loadUrl(url, result);
- }
- }
-
- public void postUrl(String uuid, String url, byte[] postData, Result result) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.postUrl(url, postData, result);
- }
-
- public void loadData(String uuid, String data, String mimeType, String encoding, String baseUrl, Result result) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.loadData(data, mimeType, encoding, baseUrl, result);
- }
-
- public void loadFile(String uuid, String url, Map headers, Result result) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null) {
- if (headers != null)
- inAppBrowserActivity.loadFile(url, headers, result);
- else
- inAppBrowserActivity.loadFile(url, result);
- }
- }
-
- public void show(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.show();
- }
-
- public void hide(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.hide();
- }
-
- public void reload(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.reload();
- }
-
- public boolean isLoading(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.isLoading();
- return false;
- }
-
- public boolean isHidden(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.isHidden;
- return false;
- }
-
- public void stopLoading(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.stopLoading();
- }
-
- public void goBack(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.goBack();
- }
-
- public boolean canGoBack(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.canGoBack();
- return false;
- }
-
- public void goForward(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.goForward();
- }
-
- public boolean canGoForward(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.canGoForward();
- return false;
- }
-
- public void goBackOrForward(String uuid, int steps) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.goBackOrForward(steps);
- }
-
- public boolean canGoBackOrForward(String uuid, int steps) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.canGoBackOrForward(steps);
- return false;
- }
-
- public void close(Activity activity, final String uuid, final Result result) {
- final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null) {
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
-
- Map obj = new HashMap<>();
- obj.put("uuid", uuid);
- channel.invokeMethod("onExit", obj);
-
- // The JS protects against multiple calls, so this should happen only when
- // close() is called by other native code.
- if (inAppBrowserActivity == null) {
- if (result != null) {
- result.success(true);
- }
- return;
- }
-
- inAppBrowserActivity.webView.setWebViewClient(new WebViewClient() {
- // NB: wait for about:blank before dismissing
- public void onPageFinished(WebView view, String url) {
- inAppBrowserActivity.close();
- }
- });
- // NB: From SDK 19: "If you call methods on WebView from any thread
- // other than your app's UI thread, it can cause unexpected results."
- // http://developer.android.com/guide/webapps/migrating.html#Threads
- inAppBrowserActivity.webView.loadUrl("about:blank");
- if (result != null) {
- result.success(true);
- }
- }
- });
- }
- else if (result != null) {
- result.success(true);
- }
- }
-
- public byte[] takeScreenshot(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.takeScreenshot();
- return null;
- }
-
- public void setOptions(String uuid, InAppBrowserOptions options, HashMap optionsMap) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.setOptions(options, optionsMap);
- }
-
- public HashMap getOptions(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.getOptions();
- return null;
- }
-
- public HashMap getCopyBackForwardList(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- return inAppBrowserActivity.getCopyBackForwardList();
- return null;
- }
-
- public void startSafeBrowsing(String uuid, Result result) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.startSafeBrowsing(result);
- result.success(false);
- }
-
- public void setSafeBrowsingWhitelist(String uuid, List hosts, Result result) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.setSafeBrowsingWhitelist(hosts, result);
- result.success(false);
- }
-
- public void clearCache(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.clearCache();
- }
-
- public void clearSslPreferences(String uuid) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.clearSslPreferences();
- }
-
- public void clearClientCertPreferences(String uuid, Result result) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.clearClientCertPreferences(result);
- result.success(false);
- }
-
- public void findAllAsync(String uuid, String find) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.findAllAsync(find);
- }
-
- public void findNext(String uuid, Boolean forward, Result result) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.findNext(forward, result);
- result.success(false);
- }
-
- public void clearMatches(String uuid, Result result) {
- InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
- if (inAppBrowserActivity != null)
- inAppBrowserActivity.clearMatches(result);
- result.success(false);
- }
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserOptions.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserOptions.java
index 58f81d56..fad45c5d 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserOptions.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserOptions.java
@@ -2,7 +2,7 @@ package com.pichillilorenzo.flutter_inappbrowser;
public class InAppBrowserOptions extends Options {
- static final String LOG_TAG = "InAppBrowserOptions";
+ public static final String LOG_TAG = "InAppBrowserOptions";
public boolean hidden = false;
public boolean toolbarTop = true;
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebChromeClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebChromeClient.java
index 79073cac..86a8f75f 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebChromeClient.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebChromeClient.java
@@ -538,6 +538,6 @@ public class InAppWebChromeClient extends WebChromeClient {
}
private MethodChannel getChannel() {
- return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.instance.channel : flutterWebView.channel;
+ return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.inAppBrowser.channel : flutterWebView.channel;
}
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java
index 3fa73093..33d2bcfa 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java
@@ -686,7 +686,7 @@ final public class InAppWebView extends InputAwareWebView {
}
private MethodChannel getChannel() {
- return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.instance.channel : flutterWebView.channel;
+ return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.inAppBrowser.channel : flutterWebView.channel;
}
public void startSafeBrowsing(final MethodChannel.Result result) {
@@ -745,6 +745,11 @@ final public class InAppWebView extends InputAwareWebView {
webSettings.setBuiltInZoomControls(enabled);
}
+ @Override
+ public void dispose() {
+ super.dispose();
+ }
+
@Override
public void destroy() {
super.destroy();
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java
index 093fcb58..7a2053b9 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java
@@ -224,7 +224,7 @@ public class InAppWebViewClient extends WebViewClient {
previousAuthRequestFailureCount = 0;
credentialsProposed = null;
- // CB-10395 InAppBrowserFlutterPlugin's WebView not storing cookies reliable to local device storage
+ // CB-10395 InAppBrowser's WebView not storing cookies reliable to local device storage
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().flush();
} else {
@@ -667,7 +667,7 @@ public class InAppWebViewClient extends WebViewClient {
}
private MethodChannel getChannel() {
- return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.instance.channel : flutterWebView.channel;
+ return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.inAppBrowser.channel : flutterWebView.channel;
}
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewOptions.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewOptions.java
index 116d5e3a..ae2e2042 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewOptions.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewOptions.java
@@ -10,7 +10,7 @@ import java.util.Map;
public class InAppWebViewOptions extends Options {
- static final String LOG_TAG = "InAppWebViewOptions";
+ public static final String LOG_TAG = "InAppWebViewOptions";
public boolean useShouldOverrideUrlLoading = false;
public boolean useOnLoadResource = false;
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InputAwareWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InputAwareWebView.java
index 7c6754d7..7e36b36d 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InputAwareWebView.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InputAwareWebView.java
@@ -4,6 +4,7 @@ import static android.content.Context.INPUT_METHOD_SERVICE;
import android.content.Context;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView;
@@ -15,8 +16,8 @@ import android.webkit.WebView;
* https://github.com/flutter/plugins/blob/master/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
*/
public class InputAwareWebView extends WebView {
+ private static final String LOG_TAG = "InputAwareWebView";
public View containerView;
-
private View threadedInputConnectionProxyView;
private ThreadedInputConnectionProxyAdapterView proxyAdapterView;
@@ -40,6 +41,19 @@ public class InputAwareWebView extends WebView {
this.containerView = null;
}
+ public void setContainerView(View containerView) {
+ this.containerView = containerView;
+
+ if (proxyAdapterView == null) {
+ return;
+ }
+
+ Log.w(LOG_TAG, "The containerView has changed while the proxyAdapterView exists.");
+ if (containerView != null) {
+ setInputConnectionTarget(proxyAdapterView);
+ }
+ }
+
/**
* Set our proxy adapter view to use its cached input connection instead of creating new ones.
*
@@ -82,8 +96,6 @@ public class InputAwareWebView extends WebView {
*/
@Override
public boolean checkInputConnectionProxy(final View view) {
- if (containerView == null)
- return super.checkInputConnectionProxy(view);
// Check to see if the view param is WebView's ThreadedInputConnectionProxyView.
View previousProxy = threadedInputConnectionProxyView;
threadedInputConnectionProxyView = view;
@@ -91,6 +103,12 @@ public class InputAwareWebView extends WebView {
// This isn't a new ThreadedInputConnectionProxyView. Ignore it.
return super.checkInputConnectionProxy(view);
}
+ if (containerView == null) {
+ Log.e(
+ LOG_TAG,
+ "Can't create a proxy view because there's no container view. Text input may not work.");
+ return super.checkInputConnectionProxy(view);
+ }
// We've never seen this before, so we make the assumption that this is WebView's
// ThreadedInputConnectionProxyView. We are making the assumption that the only view that could
@@ -115,8 +133,7 @@ public class InputAwareWebView extends WebView {
@Override
public void clearFocus() {
super.clearFocus();
- if (containerView != null)
- resetInputConnection();
+ resetInputConnection();
}
/**
@@ -131,6 +148,10 @@ public class InputAwareWebView extends WebView {
// No need to reset the InputConnection to the default thread if we've never changed it.
return;
}
+ if (containerView == null) {
+ Log.e(LOG_TAG, "Can't reset the input connection to the container view because there is none.");
+ return;
+ }
setInputConnectionTarget(/*targetView=*/ containerView);
}
@@ -143,6 +164,13 @@ public class InputAwareWebView extends WebView {
* InputConnections should be created on.
*/
private void setInputConnectionTarget(final View targetView) {
+ if (containerView == null) {
+ Log.e(
+ LOG_TAG,
+ "Can't set the input connection target because there is no containerView to use as a handler.");
+ return;
+ }
+
targetView.requestFocus();
containerView.post(
new Runnable() {
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/JavaScriptBridgeInterface.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/JavaScriptBridgeInterface.java
index d62e0bae..5fb96a30 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/JavaScriptBridgeInterface.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/JavaScriptBridgeInterface.java
@@ -11,8 +11,6 @@ import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView;
import org.json.JSONException;
import org.json.JSONObject;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
@@ -121,6 +119,6 @@ public class JavaScriptBridgeInterface {
}
private MethodChannel getChannel() {
- return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.instance.channel : flutterWebView.channel;
+ return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.inAppBrowser.channel : flutterWebView.channel;
}
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/MyCookieManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/MyCookieManager.java
index 1a3e4266..4c011e66 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/MyCookieManager.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/MyCookieManager.java
@@ -226,4 +226,7 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
return sdf.format(new Date(timestamp));
}
+ public void dispose() {
+ channel.setMethodCallHandler(null);
+ }
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Util.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Util.java
index 4d3a4e13..d2c0111b 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Util.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Util.java
@@ -13,14 +13,24 @@ import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+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 io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
+import okhttp3.OkHttpClient;
public class Util {
@@ -148,4 +158,50 @@ public class Util {
this.certificates = certificates;
}
}
+
+ public static OkHttpClient getUnsafeOkHttpClient() {
+ try {
+ // Create a trust manager that does not validate certificate chains
+ final TrustManager[] trustAllCerts = new TrustManager[] {
+ new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return new java.security.cert.X509Certificate[]{};
+ }
+ }
+ };
+
+ // Install the all-trusting trust manager
+ final SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
+ // Create an ssl socket factory with our all-trusting manager
+ final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+
+ OkHttpClient.Builder builder = new OkHttpClient.Builder();
+ builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
+ builder.hostnameVerifier(new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ });
+
+ OkHttpClient okHttpClient = builder
+ .connectTimeout(15, TimeUnit.SECONDS)
+ .writeTimeout(15, TimeUnit.SECONDS)
+ .readTimeout(15, TimeUnit.SECONDS)
+ .build();
+ return okHttpClient;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/example/android/gradle.properties b/example/android/gradle.properties
index 53ae0ae4..41672498 100644
--- a/example/android/gradle.properties
+++ b/example/android/gradle.properties
@@ -1,3 +1,4 @@
-android.enableJetifier=true
-android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
+android.enableR8=true
\ No newline at end of file
diff --git a/example/assets/favicon.ico b/example/assets/favicon.ico
new file mode 100644
index 00000000..e6adc1c6
Binary files /dev/null and b/example/assets/favicon.ico differ
diff --git a/example/assets/index.html b/example/assets/index.html
index 93a71e2e..8b81f09b 100644
--- a/example/assets/index.html
+++ b/example/assets/index.html
@@ -8,6 +8,7 @@
+
@@ -26,6 +27,10 @@
Inline WebView
Cover is a one-page template for building simple and beautiful home pages. Download, edit the text, and add your own fullscreen background photo to make it your own.
+
diff --git a/example/ios/Flutter/flutter_export_environment.sh b/example/ios/Flutter/flutter_export_environment.sh
index 7626a9ca..42466e06 100755
--- a/example/ios/Flutter/flutter_export_environment.sh
+++ b/example/ios/Flutter/flutter_export_environment.sh
@@ -5,6 +5,6 @@ export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappbro
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
-export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios-release"
+export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
diff --git a/example/lib/inline_example.screen.dart b/example/lib/inline_example.screen.dart
index f5567ac4..dcd4cf32 100755
--- a/example/lib/inline_example.screen.dart
+++ b/example/lib/inline_example.screen.dart
@@ -83,11 +83,11 @@ class _InlineExampleScreenState extends State {
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView(
//initialUrl: "https://www.youtube.com/embed/M7lc1UVf-VE?playsinline=1",
- initialUrl: "https://github.com",
+ //initialUrl: "https://github.com",
//initialUrl: "chrome://safe-browsing/match?type=malware",
//initialUrl: "http://192.168.1.20:8081/",
//initialUrl: "https://192.168.1.20:4433/",
- //initialFile: "assets/index.html",
+ initialFile: "assets/index.html",
initialHeaders: {},
initialOptions: [
InAppWebViewOptions(
@@ -143,8 +143,8 @@ class _InlineExampleScreenState extends State {
controller.clearSslPreferences();
controller.clearClientCertPreferences();
}
- //controller.findAllAsync("a");
- controller.getFavicon();
+ //controller.findAllAsync("flutter");
+ print(await controller.getFavicons());
},
onLoadError: (InAppWebViewController controller, String url, int code, String message) async {
print("error $url: $code, $message");
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index c74f3797..ab86227d 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -48,6 +48,7 @@ flutter:
- assets/page-2.html
- assets/css/
- assets/images/
+ - assets/favicon.ico
# To add assets to your application, add an assets section, like this:
# assets:
diff --git a/ios/Classes/FlutterWebViewController.swift b/ios/Classes/FlutterWebViewController.swift
index 1588095e..9202af9d 100755
--- a/ios/Classes/FlutterWebViewController.swift
+++ b/ios/Classes/FlutterWebViewController.swift
@@ -301,25 +301,9 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
}
result(true)
break
- case "dispose":
- dispose()
- result(true)
- break
default:
result(FlutterMethodNotImplemented)
break
}
}
-
- public func dispose() {
- if webView != nil {
- webView!.IABController = nil
- webView!.IAWController = nil
- webView!.uiDelegate = nil
- webView!.navigationDelegate = nil
- webView!.scrollView.delegate = nil
- webView!.stopLoading()
- webView = nil
- }
- }
}
diff --git a/ios/Classes/InAppWebView.swift b/ios/Classes/InAppWebView.swift
index dda0b4d0..601e15e5 100755
--- a/ios/Classes/InAppWebView.swift
+++ b/ios/Classes/InAppWebView.swift
@@ -238,7 +238,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
var IAWController: FlutterWebViewController?
var options: InAppWebViewOptions?
var currentURL: URL?
- var WKNavigationMap: [String: [String: Any]] = [:]
var startPageTime: Int64 = 0
static var credentialsProposed: [URLCredential] = []
@@ -786,13 +785,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let app = UIApplication.shared
if let url = navigationAction.request.url {
- if url.absoluteString != url.absoluteString && (options?.useOnLoadResource)! {
- WKNavigationMap[url.absoluteString] = [
- "startTime": currentTimeInMilliSeconds(),
- "request": navigationAction.request
- ]
- }
-
// Handle target="_blank"
if navigationAction.targetFrame == nil && (options?.useOnTargetBlank)! {
onTargetBlank(url: url)
@@ -834,17 +826,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
-// if (options?.useOnLoadResource)! {
-// if let url = navigationResponse.response.url {
-// if WKNavigationMap[url.absoluteString] != nil {
-// let startResourceTime: Int64 = (WKNavigationMap[url.absoluteString]!["startTime"] as! Int64)
-// let startTime: Int64 = startResourceTime - startPageTime;
-// let duration: Int64 = currentTimeInMilliSeconds() - startResourceTime;
-// onLoadResource(response: navigationResponse.response, fromRequest: WKNavigationMap[url.absoluteString]!["request"] as? URLRequest, withData: Data(), startTime: startTime, duration: duration)
-// }
-// }
-// }
-
if (options?.useOnDownloadStart)! {
let mimeType = navigationResponse.response.mimeType
if let url = navigationResponse.response.url {
@@ -875,7 +856,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
- self.WKNavigationMap = [:]
currentURL = url
InAppWebView.credentialsProposed = []
onLoadStop(url: (currentURL?.absoluteString)!)
diff --git a/ios/flutter_inappbrowser.podspec b/ios/flutter_inappbrowser.podspec
index 08581b88..0c88c439 100755
--- a/ios/flutter_inappbrowser.podspec
+++ b/ios/flutter_inappbrowser.podspec
@@ -17,7 +17,7 @@ A new Flutter plugin.
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
- s.ios.deployment_target = '8.0'
+ s.platform = '8.0'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
s.swift_version = '5.0'
end
diff --git a/lib/src/in_app_browser.dart b/lib/src/in_app_browser.dart
index 35d16e36..61025078 100644
--- a/lib/src/in_app_browser.dart
+++ b/lib/src/in_app_browser.dart
@@ -268,7 +268,8 @@ class InAppBrowser {
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
Map options = await ChannelManager.channel.invokeMethod('getOptions', args);
- options = options.cast();
+ if (options != null)
+ options = options.cast();
return options;
}
diff --git a/lib/src/in_app_webview.dart b/lib/src/in_app_webview.dart
index ab9c7941..6eeaa6b9 100755
--- a/lib/src/in_app_webview.dart
+++ b/lib/src/in_app_webview.dart
@@ -10,9 +10,10 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/gestures.dart';
+import 'package:html/parser.dart' show parse;
+
import 'types.dart';
import 'in_app_browser.dart';
-import 'channel_manager.dart';
import 'webview_options.dart';
/*
@@ -255,15 +256,6 @@ class _InAppWebViewState extends State {
InAppWebViewController _controller;
- @override
- void dispose() {
- super.dispose();
- if (_controller != null) {
- _controller._dispose();
- _controller = null;
- }
- }
-
@override
Widget build(BuildContext context) {
Map initialOptions = {};
@@ -273,7 +265,22 @@ class _InAppWebViewState extends State {
});
if (defaultTargetPlatform == TargetPlatform.android) {
- return GestureDetector(
+ return AndroidView(
+ viewType: 'com.pichillilorenzo/flutter_inappwebview',
+ onPlatformViewCreated: _onPlatformViewCreated,
+ gestureRecognizers: widget.gestureRecognizers,
+ layoutDirection: TextDirection.rtl,
+ creationParams: {
+ 'initialUrl': widget.initialUrl,
+ 'initialFile': widget.initialFile,
+ 'initialData': widget.initialData?.toMap(),
+ 'initialHeaders': widget.initialHeaders,
+ 'initialOptions': initialOptions
+ },
+ creationParamsCodec: const StandardMessageCodec(),
+ );
+ // onLongPress issue: https://github.com/flutter/plugins/blob/f31d16a6ca0c4bd6849cff925a00b6823973696b/packages/webview_flutter/lib/src/webview_android.dart#L31
+ /*return GestureDetector(
onLongPress: () {},
excludeFromSemantics: true,
child: AndroidView(
@@ -290,7 +297,7 @@ class _InAppWebViewState extends State {
},
creationParamsCodec: const StandardMessageCodec(),
),
- );
+ );*/
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
return UiKitView(
viewType: 'com.pichillilorenzo/flutter_inappwebview',
@@ -593,105 +600,146 @@ class InAppWebViewController {
return await _channel.invokeMethod('getProgress', args);
}
- ///Gets the favicon for the current page.
- Future> getFavicon() async {
- List favicons = [];
- HttpClient client = new HttpClient();
- var url = Uri.parse(await getUrl());
-
- var htmlRequest = await client.getUrl(url);
- var html = await (await htmlRequest.close()).transform(Utf8Decoder()).join();
- /// TODO: parse HTML instead of using javascript code
-
- try {
- List faviconsJs = json.decode(await injectScriptCode("""
-function flutter_inappbrowser_ger_favicons() {
- var favicons = [];
- var links = document.getElementsByTagName('link');
- for (var i = 0; i < links.length; i++) {
- var link = links[i];
- if (link.rel.indexOf("icon") >= 0) {
- favicons.push({
- rel: link.rel,
- href: link.href,
- sizes: link.sizes
- });
- }
+ ///Gets the content html of the page. It first tries to get the content through javascript.
+ ///If this doesn't work, it tries to get the content reading the file:
+ ///- checking if it is an asset (`file:///`) or
+ ///- downloading it using an `HttpClient` through the WebView's current url.
+ Future getHtml() async {
+ var html = "";
+ Map options = await getOptions();
+ if (options != null && options["javaScriptEnabled"] == true) {
+ html = await injectScriptCode("window.document.getElementsByTagName('html')[0].outerHTML;");
+ if (html.isNotEmpty)
+ return html;
}
- return favicons;
-}
-flutter_inappbrowser_ger_favicons();
- """));
- for(Map favicon in faviconsJs) {
- String sizes = favicon["sizes"];
- if (sizes != null && sizes != "any") {
- List sizesSplitted = sizes.split(" ");
- for (String size in sizesSplitted) {
- int width = int.parse(size.split("x")[0]);
- int height = int.parse(size.split("x")[1]);
- favicons.add(Favicon(url: favicon["href"], rel: favicon["rel"], width: width, height: height));
- }
- } else {
- favicons.add(Favicon(url: favicon["href"], rel: favicon["rel"], width: null, height: null));
- }
+ var webviewUrl = await getUrl();
+ if (webviewUrl.startsWith("file:///")) {
+ var assetPathSplitted = webviewUrl.split("/flutter_assets/");
+ var assetPath = assetPathSplitted[assetPathSplitted.length - 1];
+ var bytes = await rootBundle.load(assetPath);
+ html = utf8.decode(bytes.buffer.asUint8List());
+ }
+ else {
+ HttpClient client = new HttpClient();
+ var url = Uri.parse(webviewUrl);
+ try {
+ var htmlRequest = await client.getUrl(url);
+ html = await (await htmlRequest.close()).transform(Utf8Decoder()).join();
+ } catch (e) {
+ print(e);
}
- } catch (e) {}
+ }
+ return html;
+ }
+ ///Gets the list of all favicons for the current page.
+ Future> getFavicons() async {
+ List favicons = [];
- var completer = new Completer>();
- var faviconData = new List();
+ HttpClient client = new HttpClient();
+ var webviewUrl = await getUrl();
+ var url = (webviewUrl.startsWith("file:///")) ? Uri.file(webviewUrl) : Uri.parse(webviewUrl);
+ String manifestUrl;
+ var html = await getHtml();
+ if (html.isEmpty) {
+ return favicons;
+ }
+
+ var assetPathBase;
+
+ if (webviewUrl.startsWith("file:///")) {
+ var assetPathSplitted = webviewUrl.split("/flutter_assets/");
+ assetPathBase = assetPathSplitted[0] + "/flutter_assets/";
+ }
+
+ // get all link html elements
+ var document = parse(html);
+ var links = document.getElementsByTagName('link');
+ for (var link in links) {
+ var attributes = link.attributes;
+ if (attributes["rel"] == "manifest") {
+ manifestUrl = attributes["href"];
+ if (!_isUrlAbsolute(manifestUrl)) {
+ if (manifestUrl.startsWith("/")) {
+ manifestUrl = manifestUrl.substring(1);
+ }
+ manifestUrl = ((assetPathBase == null) ? url.scheme + "://" + url.host + "/" : assetPathBase) + manifestUrl;
+ }
+ continue;
+ }
+ if (!attributes["rel"].contains("icon")) {
+ continue;
+ }
+ favicons.addAll(_createFavicons(url, assetPathBase, attributes["href"], attributes["rel"], attributes["sizes"], false));
+ }
+
+ // try to get /favicon.ico
try {
var faviconUrl = url.scheme + "://" + url.host + "/favicon.ico";
await client.headUrl(Uri.parse(faviconUrl));
favicons.add(Favicon(url: faviconUrl, rel: "shortcut icon"));
- } catch(e) {}
-
- var manifestRequest;
- try {
- var manifestJsonUrl = url.scheme + "://" + url.host + "/manifest.json";
- manifestRequest = await client.getUrl(Uri.parse(manifestJsonUrl));
} catch(e) {
- /// TODO: find manifest throught rel="manifest"
+ print("/favicon.ico file not found: " + e.toString());
}
- if (manifestRequest) {
- Map manifest = json.decode(await (await manifestRequest.close()).transform(Utf8Decoder()).join());
+ // try to get the manifest file
+ HttpClientRequest manifestRequest;
+ HttpClientResponse manifestResponse;
+ bool manifestFound = false;
+ if (manifestUrl == null) {
+ manifestUrl = url.scheme + "://" + url.host + "/manifest.json";
+ }
+ try {
+ manifestRequest = await client.getUrl(Uri.parse(manifestUrl));
+ manifestResponse = await manifestRequest.close();
+ manifestFound = manifestResponse.statusCode == 200 && manifestResponse.headers.contentType?.mimeType == "application/json";
+ } catch(e) {
+ print("Manifest file not found: " + e.toString());
+ }
+
+ if (manifestFound) {
+ Map manifest = json.decode(await manifestResponse.transform(Utf8Decoder()).join());
if (manifest.containsKey("icons")) {
for(Map icon in manifest["icons"]) {
- String url = icon["src"];
- List urlSplitted = url.split("/");
- String sizes = icon["sizes"];
- String rel = (sizes != null) ? urlSplitted[urlSplitted.length - 1].replaceFirst("-" + sizes, "").split(" ")[0].split(".")[0] : null;
- if (sizes != null && sizes != "any") {
- List sizesSplitted = sizes.split(" ");
- for (String size in sizesSplitted) {
- int width = int.parse(size.split("x")[0]);
- int height = int.parse(size.split("x")[1]);
- favicons.add(Favicon(url: url, rel: rel, width: width, height: height));
- }
- } else {
- favicons.add(Favicon(url: url, rel: rel, width: null, height: null));
- }
+ favicons.addAll(_createFavicons(url, assetPathBase, icon["src"], icon["rel"], icon["sizes"], true));
}
}
}
- //print(favicons);
+ return favicons;
+ }
- // solution found here: https://stackoverflow.com/a/15750809/4637638
- var googleFaviconUrl = Uri.parse("https://plus.google.com/_/favicon?domain_url=" + url.scheme + "://" + url.host);
- client.getUrl(googleFaviconUrl).then((HttpClientRequest request) {
- return request.close();
- }).then((HttpClientResponse response) {
- response.listen((List data) {
- faviconData = data;
- }, onDone: () => completer.complete(faviconData));
- }).catchError((error) {
- completer.completeError(error);
- });
- return completer.future;
+ bool _isUrlAbsolute(String url) {
+ return url.startsWith("http://") || url.startsWith("https://");
+ }
+
+ List _createFavicons(Uri url, String assetPathBase, String urlIcon, String rel, String sizes, bool isManifest) {
+ List favicons = [];
+
+ List urlSplitted = urlIcon.split("/");
+ if (!_isUrlAbsolute(urlIcon)) {
+ if (urlIcon.startsWith("/")) {
+ urlIcon = urlIcon.substring(1);
+ }
+ urlIcon = ((assetPathBase == null) ? url.scheme + "://" + url.host + "/" : assetPathBase) + urlIcon;
+ }
+ if (isManifest) {
+ rel = (sizes != null) ? urlSplitted[urlSplitted.length - 1].replaceFirst("-" + sizes, "").split(" ")[0].split(".")[0] : null;
+ }
+ if (sizes != null && sizes.isNotEmpty && sizes != "any") {
+ List sizesSplitted = sizes.split(" ");
+ for (String size in sizesSplitted) {
+ int width = int.parse(size.split("x")[0]);
+ int height = int.parse(size.split("x")[1]);
+ favicons.add(Favicon(url: urlIcon, rel: rel, width: width, height: height));
+ }
+ } else {
+ favicons.add(Favicon(url: urlIcon, rel: rel, width: null, height: null));
+ }
+
+ return favicons;
}
///Loads the given [url] with optional [headers] specified as a map from name to value.
@@ -1001,7 +1049,6 @@ flutter_inappbrowser_ger_favicons();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('options', () => options);
- args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
await _channel.invokeMethod('setOptions', args);
}
@@ -1012,9 +1059,9 @@ flutter_inappbrowser_ger_favicons();
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
- args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
- Map options = await ChannelManager.channel.invokeMethod('getOptions', args);
- options = options.cast();
+ Map options = await _channel.invokeMethod('getOptions', args);
+ if (options != null)
+ options = options.cast();
return options;
}
@@ -1183,9 +1230,4 @@ flutter_inappbrowser_ger_favicons();
}
await _channel.invokeMethod('clearMatches', args);
}
-
- ///Dispose/Destroy the WebView.
- Future _dispose() async {
- await _channel.invokeMethod('dispose');
- }
}
diff --git a/lib/src/webview_options.dart b/lib/src/webview_options.dart
index f88a836f..518b7f55 100644
--- a/lib/src/webview_options.dart
+++ b/lib/src/webview_options.dart
@@ -43,6 +43,7 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti
this.resourceCustomSchemes = const [], this.contentBlockers = const [], this.preferredContentMode = InAppWebViewUserPreferredContentMode.RECOMMENDED}) {
if (this.minimumFontSize == null)
this.minimumFontSize = Platform.isAndroid ? 8 : 0;
+ assert(!this.resourceCustomSchemes.contains("http") && !this.resourceCustomSchemes.contains("https"));
}
@override
diff --git a/nodejs_server_test_auth_basic_and_ssl/index.js b/nodejs_server_test_auth_basic_and_ssl/index.js
index d52404db..a6004761 100644
--- a/nodejs_server_test_auth_basic_and_ssl/index.js
+++ b/nodejs_server_test_auth_basic_and_ssl/index.js
@@ -23,7 +23,16 @@ appHttps.get('/', (req, res) => {
// `localhost`.
if (req.client.authorized) {
- res.send(`Hello ${cert.subject.CN}, your certificate was issued by ${cert.issuer.CN}!`)
+ res.send(`
+
+
+
+
+
+
Hello ${cert.subject.CN}, your certificate was issued by ${cert.issuer.CN}!
+
+
+ `)
// They can still provide a certificate which is not accepted by us. Unfortunately, the `cert` object will be an empty
// object instead of `null` if there is no certificate at all, so we have to check for a known field rather than
// truthiness.
@@ -34,6 +43,13 @@ appHttps.get('/', (req, res) => {
} else {
res.status(401).send(`Sorry, but you need to provide a client certificate to continue.`)
}
+ res.end()
+})
+
+appHttps.get('/fakeResource', (req, res) => {
+ res.set("Content-Type", "text/javascript")
+ res.send(`alert("HI");`)
+ res.end()
})
// Let's create our HTTPS server and we're ready to go.
diff --git a/pubspec.yaml b/pubspec.yaml
index 3923f656..efe4d795 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -5,14 +5,15 @@ author: Lorenzo Pichilli
homepage: https://github.com/pichillilorenzo/flutter_inappbrowser
environment:
- sdk: ">=2.0.0-dev <3.0.0"
- flutter: ">=0.10.1 <2.0.0"
+ sdk: ">=2.0.0-dev.68.0 <3.0.0"
+ flutter: ">=1.9.1+hotfix.5 <2.0.0"
dependencies:
flutter:
sdk: flutter
uuid: ^2.0.0
mime: ^0.9.6+2
+ html: ^0.14.0+3
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec