Commit 299042f8 authored by Lorenzo Pichilli's avatar Lorenzo Pichilli

added getHtml method, updated android config, remove block on long press...

added getHtml method, updated android config, remove block on long press webview android, fixed other issues
parent 6518697e
...@@ -15,23 +15,41 @@ ...@@ -15,23 +15,41 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="9b41f7a2-a71e-4923-91fb-249d7815b3e7" name="Default" comment=""> <list default="true" id="9b41f7a2-a71e-4923-91fb-249d7815b3e7" name="Default" comment="">
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowser.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/example/assets/favicon.ico" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/CHANGELOG.md" beforeDir="false" afterPath="$PROJECT_DIR$/CHANGELOG.md" afterDir="false" /> <change beforePath="$PROJECT_DIR$/CHANGELOG.md" beforeDir="false" afterPath="$PROJECT_DIR$/CHANGELOG.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/android/build.gradle" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/gradle.properties" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/AndroidManifest.xml" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/AndroidManifest.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ChromeCustomTabs/ChromeCustomTabsActivity.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ChromeCustomTabs/ChromeCustomTabsActivity.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ContentBlocker/ContentBlockerHandler.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ContentBlocker/ContentBlockerHandler.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabaseHandler.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabaseHandler.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserOptions.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserOptions.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebChromeClient.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebChromeClient.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewOptions.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewOptions.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InputAwareWebView.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InputAwareWebView.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/JavaScriptBridgeInterface.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/JavaScriptBridgeInterface.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/MyCookieManager.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/MyCookieManager.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Util.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Util.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/android/gradle.properties" beforeDir="false" afterPath="$PROJECT_DIR$/example/android/gradle.properties" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/assets/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/example/assets/index.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/ios/Flutter/flutter_export_environment.sh" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Flutter/flutter_export_environment.sh" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/lib/inline_example.screen.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/inline_example.screen.dart" afterDir="false" /> <change beforePath="$PROJECT_DIR$/example/lib/inline_example.screen.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/inline_example.screen.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/pubspec.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/example/pubspec.yaml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/FlutterWebViewController.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/FlutterWebViewController.swift" afterDir="false" /> <change beforePath="$PROJECT_DIR$/ios/Classes/FlutterWebViewController.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/FlutterWebViewController.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" afterDir="false" /> <change beforePath="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" afterDir="false" /> <change beforePath="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" beforeDir="false" afterPath="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/channel_manager.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/channel_manager.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/cookie_manager.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/cookie_manager.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/in_app_browser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_browser.dart" afterDir="false" /> <change beforePath="$PROJECT_DIR$/lib/src/in_app_browser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_browser.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/in_app_webview.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_webview.dart" afterDir="false" /> <change beforePath="$PROJECT_DIR$/lib/src/in_app_webview.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_webview.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/types.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/types.dart" afterDir="false" /> <change beforePath="$PROJECT_DIR$/lib/src/webview_options.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/webview_options.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js" beforeDir="false" afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pubspec.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pubspec.yaml" afterDir="false" />
</list> </list>
<ignored path="$PROJECT_DIR$/.dart_tool/" /> <ignored path="$PROJECT_DIR$/.dart_tool/" />
<ignored path="$PROJECT_DIR$/.idea/" /> <ignored path="$PROJECT_DIR$/.idea/" />
...@@ -54,17 +72,26 @@ ...@@ -54,17 +72,26 @@
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/CHANGELOG.md"> <entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="405"> <state relative-caret-position="336">
<caret line="27" column="92" selection-start-line="27" selection-start-column="92" selection-end-line="27" selection-end-column="92" /> <caret line="28" column="24" selection-start-line="28" selection-start-column="24" selection-end-line="28" selection-end-column="24" />
</state> </state>
</provider> </provider>
</entry> </entry>
</file> </file>
<file pinned="false" current-in-tab="true"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="423">
<caret line="49" column="46" selection-start-line="49" selection-start-column="46" selection-end-line="49" selection-end-column="46" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart"> <entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="296"> <state relative-caret-position="183">
<caret line="679" column="4" selection-start-line="679" selection-start-column="4" selection-end-line="679" selection-end-column="4" /> <caret line="114" column="101" selection-start-line="114" selection-start-column="87" selection-end-line="114" selection-end-column="101" />
<folding> <folding>
<element signature="e#0#17#0" expanded="true" /> <element signature="e#0#17#0" expanded="true" />
</folding> </folding>
...@@ -73,22 +100,40 @@ ...@@ -73,22 +100,40 @@
</entry> </entry>
</file> </file>
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/src/types.dart"> <entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="235"> <state relative-caret-position="142">
<caret line="424" column="64" selection-start-line="424" selection-start-column="64" selection-end-line="424" selection-end-column="64" /> <caret line="57" column="24" selection-start-line="57" selection-start-column="7" selection-end-line="57" selection-end-column="24" />
<folding> <folding>
<element signature="e#0#20#0" expanded="true" /> <element signature="e#0#17#0" expanded="true" />
</folding> </folding>
</state> </state>
</provider> </provider>
</entry> </entry>
</file> </file>
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/example/assets/index.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="888">
<caret line="79" column="11" selection-start-line="79" selection-start-column="11" selection-end-line="79" selection-end-column="11" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-107">
<caret line="8" column="36" selection-start-line="8" selection-start-column="36" selection-end-line="8" selection-end-column="36" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart"> <entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-60"> <state relative-caret-position="294">
<caret line="85" column="43" selection-start-line="85" selection-start-column="43" selection-end-line="85" selection-end-column="43" /> <caret line="261" column="187" selection-start-line="259" selection-start-column="14" selection-end-line="261" selection-end-column="187" />
<folding> <folding>
<element signature="e#0#20#0" expanded="true" /> <element signature="e#0#20#0" expanded="true" />
</folding> </folding>
...@@ -108,20 +153,6 @@ ...@@ -108,20 +153,6 @@
</component> </component>
<component name="FindInProjectRecents"> <component name="FindInProjectRecents">
<findStrings> <findStrings>
<find>a</find>
<find>as</find>
<find>IABWebViewClient</find>
<find>clearCache</find>
<find>Auth</find>
<find>onReceivedHttpAuthRequestCallback</find>
<find>Protection</find>
<find>cast</find>
<find>clear</find>
<find>HttpAuthResponse</find>
<find>ClientCertResponse</find>
<find>onReceivedServerTrustAuthRequest</find>
<find>ServerTrustAuthResponse</find>
<find>certific</find>
<find>CER</find> <find>CER</find>
<find>ServerTrustChallenge</find> <find>ServerTrustChallenge</find>
<find>cONTENTMODE</find> <find>cONTENTMODE</find>
...@@ -138,6 +169,20 @@ ...@@ -138,6 +169,20 @@
<find>on</find> <find>on</find>
<find>findAll</find> <find>findAll</find>
<find>getFave</find> <find>getFave</find>
<find>Uri</find>
<find>!= &quot;an</find>
<find>_channel</find>
<find>getHtml</find>
<find>url</find>
<find>getOptions</find>
<find>gestureR</find>
<find>_dispose</find>
<find>dispose</find>
<find>Long</find>
<find>custom</find>
<find>scheme</find>
<find>useOnLoadResource</find>
<find>useShouldOverrideUrlLoading</find>
</findStrings> </findStrings>
<replaceStrings> <replaceStrings>
<replace>activity.getPreferences(0)</replace> <replace>activity.getPreferences(0)</replace>
...@@ -175,7 +220,6 @@ ...@@ -175,7 +220,6 @@
<option value="$PROJECT_DIR$/android/gradle/wrapper/gradle-wrapper.properties" /> <option value="$PROJECT_DIR$/android/gradle/wrapper/gradle-wrapper.properties" />
<option value="$PROJECT_DIR$/example/android/app/build.gradle" /> <option value="$PROJECT_DIR$/example/android/app/build.gradle" />
<option value="$PROJECT_DIR$/example/android/gradle.properties" /> <option value="$PROJECT_DIR$/example/android/gradle.properties" />
<option value="$PROJECT_DIR$/android/build.gradle" />
<option value="$PROJECT_DIR$/example/android/build.gradle" /> <option value="$PROJECT_DIR$/example/android/build.gradle" />
<option value="$PROJECT_DIR$/example/assets/page-3.html" /> <option value="$PROJECT_DIR$/example/assets/page-3.html" />
<option value="$PROJECT_DIR$/example/assets/page-2.html" /> <option value="$PROJECT_DIR$/example/assets/page-2.html" />
...@@ -192,28 +236,29 @@ ...@@ -192,28 +236,29 @@
<option value="$PROJECT_DIR$/example/ios/Runner/Info.plist" /> <option value="$PROJECT_DIR$/example/ios/Runner/Info.plist" />
<option value="$PROJECT_DIR$/example/lib/main.dart" /> <option value="$PROJECT_DIR$/example/lib/main.dart" />
<option value="$PROJECT_DIR$/lib/src/content_blocker.dart" /> <option value="$PROJECT_DIR$/lib/src/content_blocker.dart" />
<option value="$PROJECT_DIR$/example/assets/index.html" />
<option value="$PROJECT_DIR$/example/lib/webview_example.screen.dart" /> <option value="$PROJECT_DIR$/example/lib/webview_example.screen.dart" />
<option value="$PROJECT_DIR$/lib/src/credentials_database.dart" /> <option value="$PROJECT_DIR$/lib/src/credentials_database.dart" />
<option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" /> <option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" />
<option value="$PROJECT_DIR$/example/pubspec.yaml" />
<option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" /> <option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" />
<option value="$PROJECT_DIR$/pubspec.yaml" />
<option value="$PROJECT_DIR$/lib/src/webview_options.dart" />
<option value="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js" />
<option value="$PROJECT_DIR$/lib/src/channel_manager.dart" /> <option value="$PROJECT_DIR$/lib/src/channel_manager.dart" />
<option value="$PROJECT_DIR$/lib/src/cookie_manager.dart" /> <option value="$PROJECT_DIR$/lib/src/cookie_manager.dart" />
<option value="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" /> <option value="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" />
<option value="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" /> <option value="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" />
<option value="$PROJECT_DIR$/lib/src/in_app_browser.dart" />
<option value="$PROJECT_DIR$/CHANGELOG.md" />
<option value="$PROJECT_DIR$/example/lib/inline_example.screen.dart" />
<option value="$PROJECT_DIR$/lib/src/types.dart" /> <option value="$PROJECT_DIR$/lib/src/types.dart" />
<option value="$PROJECT_DIR$/CHANGELOG.md" />
<option value="$PROJECT_DIR$/lib/src/in_app_browser.dart" />
<option value="$PROJECT_DIR$/android/build.gradle" />
<option value="$PROJECT_DIR$/example/pubspec.yaml" />
<option value="$PROJECT_DIR$/pubspec.yaml" />
<option value="$PROJECT_DIR$/lib/src/in_app_webview.dart" /> <option value="$PROJECT_DIR$/lib/src/in_app_webview.dart" />
<option value="$PROJECT_DIR$/example/assets/index.html" />
<option value="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js" />
<option value="$PROJECT_DIR$/lib/src/webview_options.dart" />
<option value="$PROJECT_DIR$/example/lib/inline_example.screen.dart" />
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectFrameBounds" extendedState="6"> <component name="ProjectFrameBounds">
<option name="y" value="23" /> <option name="y" value="23" />
<option name="width" value="1920" /> <option name="width" value="1920" />
<option name="height" value="1057" /> <option name="height" value="1057" />
...@@ -235,62 +280,83 @@ ...@@ -235,62 +280,83 @@
<select /> <select />
</subPane> </subPane>
</pane> </pane>
<pane id="ProjectPane"> <pane id="AndroidView">
<subPane> <subPane>
<expand> <expand>
<path> <path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" /> <item name="flutter_inappbrowser" type="1abcf292:AndroidViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" /> <item name="flutter_inappbrowser_android" type="e9137016:NonAndroidModuleNode" />
</path> </path>
<path> <path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" /> <item name="flutter_inappbrowser" type="1abcf292:AndroidViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" /> <item name="flutter_inappbrowser_android" type="e9137016:NonAndroidModuleNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" /> <item name="java" type="f7eae187:NonAndroidSourceTypeNode" />
</path> </path>
<path> <path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" /> <item name="flutter_inappbrowser" type="1abcf292:AndroidViewProjectNode" />
<item name="flutter_inappbrowser_android" type="e9137016:NonAndroidModuleNode" />
<item name="java" type="f7eae187:NonAndroidSourceTypeNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" /> <item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path> </path>
<path> <path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" /> <item name="flutter_inappbrowser" type="1abcf292:AndroidViewProjectNode" />
<item name="External Libraries" type="cb654da1:ExternalLibrariesNode" /> <item name="flutter_inappbrowser_android" type="e9137016:NonAndroidModuleNode" />
<item name="java" type="f7eae187:NonAndroidSourceTypeNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="InAppWebView" type="462c0819:PsiDirectoryNode" />
</path> </path>
</expand> </expand>
<select /> <select />
</subPane> </subPane>
</pane> </pane>
<pane id="PackagesPane" /> <pane id="ProjectPane">
<pane id="AndroidView">
<subPane> <subPane>
<expand> <expand>
<path> <path>
<item name="flutter_inappbrowser" type="1abcf292:AndroidViewProjectNode" /> <item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser_android" type="e9137016:NonAndroidModuleNode" /> <item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
</path> </path>
<path> <path>
<item name="flutter_inappbrowser" type="1abcf292:AndroidViewProjectNode" /> <item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser_android" type="e9137016:NonAndroidModuleNode" /> <item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="java" type="f7eae187:NonAndroidSourceTypeNode" /> <item name="android" type="462c0819:PsiDirectoryNode" />
</path> </path>
<path> <path>
<item name="flutter_inappbrowser" type="1abcf292:AndroidViewProjectNode" /> <item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser_android" type="e9137016:NonAndroidModuleNode" />
<item name="java" type="f7eae187:NonAndroidSourceTypeNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" /> <item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
</path> </path>
<path> <path>
<item name="flutter_inappbrowser" type="1abcf292:AndroidViewProjectNode" /> <item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser_android" type="e9137016:NonAndroidModuleNode" />
<item name="java" type="f7eae187:NonAndroidSourceTypeNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" /> <item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="InAppWebView" type="462c0819:PsiDirectoryNode" /> <item name="example" type="462c0819:PsiDirectoryNode" />
<item name="assets" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="nodejs_server_test_auth_basic_and_ssl" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="External Libraries" type="cb654da1:ExternalLibrariesNode" />
</path> </path>
</expand> </expand>
<select /> <select />
</subPane> </subPane>
</pane> </pane>
<pane id="PackagesPane" />
</panes> </panes>
</component> </component>
<component name="PropertiesComponent"> <component name="PropertiesComponent">
...@@ -310,13 +376,6 @@ ...@@ -310,13 +376,6 @@
<property name="show.migrate.to.gradle.popup" value="false" /> <property name="show.migrate.to.gradle.popup" value="false" />
</component> </component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/example/assets" />
<recent name="$PROJECT_DIR$" />
<recent name="$PROJECT_DIR$/example" />
<recent name="$PROJECT_DIR$/example/assets/images" />
<recent name="$PROJECT_DIR$/android/libs" />
</key>
<key name="MoveFile.RECENT_KEYS"> <key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/example/assets" /> <recent name="$PROJECT_DIR$/example/assets" />
<recent name="$PROJECT_DIR$/lib/src" /> <recent name="$PROJECT_DIR$/lib/src" />
...@@ -324,6 +383,13 @@ ...@@ -324,6 +383,13 @@
<recent name="$PROJECT_DIR$/lib" /> <recent name="$PROJECT_DIR$/lib" />
<recent name="$PROJECT_DIR$/example/assets/images" /> <recent name="$PROJECT_DIR$/example/assets/images" />
</key> </key>
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/example/assets" />
<recent name="$PROJECT_DIR$" />
<recent name="$PROJECT_DIR$/example" />
<recent name="$PROJECT_DIR$/example/assets/images" />
<recent name="$PROJECT_DIR$/android/libs" />
</key>
</component> </component>
<component name="RunDashboard"> <component name="RunDashboard">
<option name="ruleStates"> <option name="ruleStates">
...@@ -442,10 +508,9 @@ ...@@ -442,10 +508,9 @@
</component> </component>
<component name="ToolWindowManager"> <component name="ToolWindowManager">
<frame x="0" y="23" width="1920" height="1057" extended-state="6" /> <frame x="0" y="23" width="1920" height="1057" extended-state="6" />
<editor active="true" />
<layout> <layout>
<window_info content_ui="combo" id="Project" order="0" sideWeight="0.59067357" visible="true" weight="0.15867944" /> <window_info content_ui="combo" id="Project" order="0" sideWeight="0.58835757" visible="true" weight="0.15867944" />
<window_info id="Structure" order="1" sideWeight="0.40932643" side_tool="true" weight="0.15867944" /> <window_info id="Structure" order="1" sideWeight="0.4116424" side_tool="true" visible="true" weight="0.15867944" />
<window_info id="Designer" order="2" /> <window_info id="Designer" order="2" />
<window_info id="Build Variants" order="3" side_tool="true" /> <window_info id="Build Variants" order="3" side_tool="true" />
<window_info id="Captures" order="4" side_tool="true" weight="0.32936507" /> <window_info id="Captures" order="4" side_tool="true" weight="0.32936507" />
...@@ -455,7 +520,7 @@ ...@@ -455,7 +520,7 @@
<window_info id="Resources Explorer" order="8" /> <window_info id="Resources Explorer" order="8" />
<window_info anchor="bottom" id="Message" order="0" /> <window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" weight="0.32642487" /> <window_info anchor="bottom" id="Find" order="1" weight="0.32642487" />
<window_info active="true" anchor="bottom" id="Run" order="2" sideWeight="0.49307775" visible="true" weight="0.3253886" /> <window_info active="true" anchor="bottom" id="Run" order="2" sideWeight="0.49307775" visible="true" weight="0.5015544" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.34196892" /> <window_info anchor="bottom" id="Debug" order="3" weight="0.34196892" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" /> <window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" /> <window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
...@@ -491,29 +556,6 @@ ...@@ -491,29 +556,6 @@
</ignored-roots> </ignored-roots>
</component> </component>
<component name="editorHistoryManager"> <component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/example/assets/page-3.html" />
<entry file="file://$USER_HOME$/flutter/bin/cache/pkg/sky_engine/lib/convert/json.dart" />
<entry file="file://$PROJECT_DIR$/example/assets/page-2.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="162">
<caret line="15" column="63" selection-start-line="15" selection-start-column="63" selection-end-line="15" selection-end-column="63" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/assets/page-1.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="104">
<caret line="15" column="63" selection-start-line="15" selection-start-column="63" selection-end-line="15" selection-end-column="63" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/android/build.gradle">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="256">
<caret line="25" column="1" lean-forward="true" selection-start-line="25" selection-start-column="1" selection-end-line="25" selection-end-column="1" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/flutter/packages/flutter/lib/src/widgets/platform_view.dart"> <entry file="file://$USER_HOME$/flutter/packages/flutter/lib/src/widgets/platform_view.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-182"> <state relative-caret-position="-182">
...@@ -688,27 +730,6 @@ ...@@ -688,27 +730,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="210">
<caret line="14" column="16" selection-start-line="14" selection-start-column="16" selection-end-line="14" selection-end-column="16" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="315">
<caret line="21" column="28" selection-start-line="21" selection-start-column="28" selection-end-line="22" selection-end-column="23" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="225">
<caret line="15" column="2" lean-forward="true" selection-start-line="15" selection-start-column="2" selection-end-line="15" selection-end-column="2" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.gitignore"> <entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor" /> <provider selected="true" editor-type-id="text-editor" />
</entry> </entry>
...@@ -722,13 +743,6 @@ ...@@ -722,13 +743,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/assets/index.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="330">
<caret line="22" column="17" selection-start-line="22" selection-start-column="17" selection-end-line="22" selection-end-column="17" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/webview_example.screen.dart"> <entry file="file://$PROJECT_DIR$/example/lib/webview_example.screen.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="302"> <state relative-caret-position="302">
...@@ -785,63 +799,114 @@ ...@@ -785,63 +799,114 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart"> <entry file="file://$USER_HOME$/flutter/bin/cache/pkg/sky_engine/lib/core/uri.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="198"> <state relative-caret-position="15">
<caret line="168" column="6" selection-start-line="168" selection-start-column="6" selection-end-line="168" selection-end-column="6" /> <caret line="37" column="15" selection-start-line="37" selection-start-column="15" selection-end-line="37" selection-end-column="15" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/types.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="235">
<caret line="424" column="64" selection-start-line="424" selection-start-column="64" selection-end-line="424" selection-end-column="64" />
<folding> <folding>
<element signature="e#0#17#0" expanded="true" /> <element signature="e#0#20#0" expanded="true" />
</folding> </folding>
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_browser.dart"> <entry file="file://$PROJECT_DIR$/lib/src/in_app_browser.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="401"> <state relative-caret-position="76">
<caret line="426" column="51" selection-start-line="426" selection-start-column="51" selection-end-line="426" selection-end-column="51" /> <caret line="268" column="65" selection-start-line="268" selection-start-column="65" selection-end-line="268" selection-end-column="65" />
<folding> <folding>
<element signature="e#0#20#0" expanded="true" /> <element signature="e#0#20#0" expanded="true" />
</folding> </folding>
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/android/gradle.properties">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/android/local.properties">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/android/gradle.properties">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="141">
<caret line="12" column="32" selection-start-line="12" selection-start-column="32" selection-end-line="12" selection-end-column="32" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-107">
<caret line="8" column="36" selection-start-line="8" selection-start-column="36" selection-end-line="8" selection-end-column="36" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/android/build.gradle">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="256">
<caret line="25" selection-start-line="25" selection-end-line="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md"> <entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="405"> <state relative-caret-position="336">
<caret line="27" column="92" selection-start-line="27" selection-start-column="92" selection-end-line="27" selection-end-column="92" /> <caret line="28" column="24" selection-start-line="28" selection-start-column="24" selection-end-line="28" selection-end-column="24" />
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/lib/src/types.dart"> <entry file="file://$PROJECT_DIR$/example/assets/index.html">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="235"> <state relative-caret-position="888">
<caret line="424" column="64" selection-start-line="424" selection-start-column="64" selection-end-line="424" selection-end-column="64" /> <caret line="79" column="11" selection-start-line="79" selection-start-column="11" selection-end-line="79" selection-end-column="11" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart"> <entry file="file://$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="423">
<caret line="49" column="46" selection-start-line="49" selection-start-column="46" selection-end-line="49" selection-end-column="46" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-60"> <state relative-caret-position="142">
<caret line="85" column="43" selection-start-line="85" selection-start-column="43" selection-end-line="85" selection-end-column="43" /> <caret line="57" column="24" selection-start-line="57" selection-start-column="7" selection-end-line="57" selection-end-column="24" />
<folding> <folding>
<element signature="e#0#20#0" expanded="true" /> <element signature="e#0#17#0" expanded="true" />
</folding> </folding>
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart"> <entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="296"> <state relative-caret-position="183">
<caret line="679" column="4" selection-start-line="679" selection-start-column="4" selection-end-line="679" selection-end-column="4" /> <caret line="114" column="101" selection-start-line="114" selection-start-column="87" selection-end-line="114" selection-end-column="101" />
<folding> <folding>
<element signature="e#0#17#0" expanded="true" /> <element signature="e#0#17#0" expanded="true" />
</folding> </folding>
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="294">
<caret line="261" column="187" selection-start-line="259" selection-start-column="14" selection-end-line="261" selection-end-column="187" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</component> </component>
<component name="masterDetails"> <component name="masterDetails">
<states> <states>
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
- Added `HttpAuthCredentialDatabase` class - Added `HttpAuthCredentialDatabase` class
- Added `onReceivedServerTrustAuthRequest` and `onReceivedClientCertRequest` events to manage SSL requests - Added `onReceivedServerTrustAuthRequest` and `onReceivedClientCertRequest` events to manage SSL requests
- Added `onFindResultReceived` event, `findAllAsync`, `findNext` and `clearMatches` methods - Added `onFindResultReceived` event, `findAllAsync`, `findNext` and `clearMatches` methods
- Added `getHtml` method
### BREAKING CHANGES ### BREAKING CHANGES
- Deleted `WebResourceRequest` class - Deleted `WebResourceRequest` class
...@@ -34,6 +35,7 @@ ...@@ -34,6 +35,7 @@
- Updated `onLoadResource` event - Updated `onLoadResource` event
- Updated `CookieManager` class - Updated `CookieManager` class
- WebView options are now available with the new corresponding classes: `InAppWebViewOptions`, `AndroidInAppWebViewOptions`, `iOSInAppWebViewOptions`, `InAppBrowserOptions`, `AndroidInAppBrowserOptions`, `iOSInAppBrowserOptions`, `AndroidChromeCustomTabsOptions` and `iOSSafariOptions` - 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<Favicon>`) found
## 1.2.1 ## 1.2.1
......
...@@ -33,11 +33,34 @@ android { ...@@ -33,11 +33,34 @@ android {
lintOptions { lintOptions {
disable 'InvalidPackage' 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 { afterEvaluate {
implementation 'androidx.webkit:webkit:1.0.0' def containsEmbeddingDependencies = false
implementation 'androidx.browser:browser:1.0.0' for (def configuration : configurations.all) {
implementation 'androidx.appcompat:appcompat:1.0.0' for (def dependency : configuration.dependencies) {
implementation 'com.squareup.okhttp3:mockwebserver:3.11.0' 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
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536M
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.pichillilorenzo.flutter_inappbrowser"> package="com.pichillilorenzo.flutter_inappbrowser">
<uses-permission android:name="android.permission.INTERNET" />
<application> <application>
<activity android:theme="@style/AppTheme" android:name=".InAppBrowserActivity" android:configChanges="orientation|screenSize"></activity> <activity android:theme="@style/AppTheme" android:name=".InAppBrowserActivity" android:configChanges="orientation|screenSize"></activity>
<activity android:theme="@style/ThemeTransparent" android:name=".ChromeCustomTabs.ChromeCustomTabsActivity" android:configChanges="orientation|screenSize"></activity> <activity android:theme="@style/ThemeTransparent" android:name=".ChromeCustomTabs.ChromeCustomTabsActivity" android:configChanges="orientation|screenSize"></activity>
......
...@@ -36,7 +36,7 @@ public class ChromeCustomTabsActivity extends Activity { ...@@ -36,7 +36,7 @@ public class ChromeCustomTabsActivity extends Activity {
options = new ChromeCustomTabsOptions(); options = new ChromeCustomTabsOptions();
options.parse((HashMap<String, Object>) b.getSerializable("options")); options.parse((HashMap<String, Object>) b.getSerializable("options"));
InAppBrowserFlutterPlugin.instance.chromeCustomTabsActivities.put(uuid, this); InAppBrowserFlutterPlugin.inAppBrowser.chromeCustomTabsActivities.put(uuid, this);
customTabActivityHelper = new CustomTabActivityHelper(); customTabActivityHelper = new CustomTabActivityHelper();
builder = new CustomTabsIntent.Builder(); builder = new CustomTabsIntent.Builder();
...@@ -49,8 +49,8 @@ public class ChromeCustomTabsActivity extends Activity { ...@@ -49,8 +49,8 @@ public class ChromeCustomTabsActivity extends Activity {
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid); obj.put("uuid", uuid);
InAppBrowserFlutterPlugin.instance.channel.invokeMethod("onChromeSafariBrowserOpened", obj); InAppBrowserFlutterPlugin.inAppBrowser.channel.invokeMethod("onChromeSafariBrowserOpened", obj);
InAppBrowserFlutterPlugin.instance.channel.invokeMethod("onChromeSafariBrowserLoaded", obj); InAppBrowserFlutterPlugin.inAppBrowser.channel.invokeMethod("onChromeSafariBrowserLoaded", obj);
} }
private void prepareCustomTabs() { private void prepareCustomTabs() {
...@@ -86,7 +86,7 @@ public class ChromeCustomTabsActivity extends Activity { ...@@ -86,7 +86,7 @@ public class ChromeCustomTabsActivity extends Activity {
finish(); finish();
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid); obj.put("uuid", uuid);
InAppBrowserFlutterPlugin.instance.channel.invokeMethod("onChromeSafariBrowserClosed", obj); InAppBrowserFlutterPlugin.inAppBrowser.channel.invokeMethod("onChromeSafariBrowserClosed", obj);
} }
} }
......
...@@ -7,18 +7,16 @@ import android.util.Log; ...@@ -7,18 +7,16 @@ import android.util.Log;
import android.webkit.WebResourceResponse; import android.webkit.WebResourceResponse;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView; import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappbrowser.Util;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher; import java.util.regex.Matcher;
...@@ -163,8 +161,7 @@ public class ContentBlockerHandler { ...@@ -163,8 +161,7 @@ public class ContentBlockerHandler {
Response response = null; Response response = null;
try { try {
response = Util.getUnsafeOkHttpClient().newCall(mRequest).execute();
response = webView.httpClient.newCall(mRequest).execute();
byte[] dataBytes = response.body().bytes(); byte[] dataBytes = response.body().bytes();
InputStream dataStream = new ByteArrayInputStream(dataBytes); InputStream dataStream = new ByteArrayInputStream(dataBytes);
...@@ -195,7 +192,7 @@ public class ContentBlockerHandler { ...@@ -195,7 +192,7 @@ public class ContentBlockerHandler {
} }
public WebResourceResponse checkUrl(final InAppWebView webView, String url) throws URISyntaxException, InterruptedException, MalformedURLException { 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); return checkUrl(webView, url, responseResourceType);
} }
...@@ -204,7 +201,7 @@ public class ContentBlockerHandler { ...@@ -204,7 +201,7 @@ public class ContentBlockerHandler {
return checkUrl(webView, url, responseResourceType); return checkUrl(webView, url, responseResourceType);
} }
public ContentBlockerTriggerResourceType getResourceTypeFromUrl(InAppWebView webView, String url) { public ContentBlockerTriggerResourceType getResourceTypeFromUrl(String url) {
ContentBlockerTriggerResourceType responseResourceType = ContentBlockerTriggerResourceType.RAW; ContentBlockerTriggerResourceType responseResourceType = ContentBlockerTriggerResourceType.RAW;
if (url.startsWith("http://") || url.startsWith("https://")) { if (url.startsWith("http://") || url.startsWith("https://")) {
...@@ -212,7 +209,7 @@ public class ContentBlockerHandler { ...@@ -212,7 +209,7 @@ public class ContentBlockerHandler {
Request mRequest = new Request.Builder().url(url).head().build(); Request mRequest = new Request.Builder().url(url).head().build();
Response response = null; Response response = null;
try { try {
response = webView.httpClient.newCall(mRequest).execute(); response = Util.getUnsafeOkHttpClient().newCall(mRequest).execute();
if (response.header("content-type") != null) { if (response.header("content-type") != null) {
String[] contentTypeSplitted = response.header("content-type").split(";"); String[] contentTypeSplitted = response.header("content-type").split(";");
......
...@@ -116,4 +116,8 @@ public class CredentialDatabaseHandler implements MethodChannel.MethodCallHandle ...@@ -116,4 +116,8 @@ public class CredentialDatabaseHandler implements MethodChannel.MethodCallHandle
} }
} }
public void dispose() {
channel.setMethodCallHandler(null);
}
} }
...@@ -289,10 +289,6 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { ...@@ -289,10 +289,6 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
result.success(false); result.success(false);
} }
break; break;
case "dispose":
dispose();
result.success(true);
break;
default: default:
result.notImplemented(); result.notImplemented();
} }
...@@ -300,10 +296,12 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { ...@@ -300,10 +296,12 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
@Override @Override
public void dispose() { public void dispose() {
channel.setMethodCallHandler(null);
if (webView != null) { if (webView != null) {
webView.setWebChromeClient(new WebChromeClient()); webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient() { webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) { public void onPageFinished(WebView view, String url) {
webView.dispose();
webView.destroy(); webView.destroy();
webView = null; webView = null;
} }
...@@ -324,4 +322,12 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { ...@@ -324,4 +322,12 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
webView.unlockInputConnection(); webView.unlockInputConnection();
} }
public void onFlutterViewAttached(View flutterView) {
webView.setContainerView(flutterView);
}
public void onFlutterViewDetached() {
webView.setContainerView(null);
}
} }
\ No newline at end of file
/*
*
* 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<String, InAppBrowserActivity> webViewActivities = new HashMap<>();
public Map<String, ChromeCustomTabsActivity> 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<String, String> headers = (Map<String, String>) 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<String, Object> options = (HashMap<String, Object>) call.argument("options");
final HashMap<String, Object> optionsFallback = (HashMap<String, Object>) call.argument("optionsFallback");
open(activity, uuid, uuidFallback, url_final, options, headers, true, optionsFallback, result);
} else {
String url = url_final;
final HashMap<String, Object> options = (HashMap<String, Object>) 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<String, Object> options = (HashMap<String, Object>) 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<String, String>) 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<String, String>) 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<String, Object> inAppBrowserOptionsMap = (HashMap<String, Object>) 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<String>) 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<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
ArrayList<Intent> targetIntents = new ArrayList<Intent>();
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<String, Object> options, Map<String, String> headers, boolean useChromeSafariBrowser, HashMap<String, Object> 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<String, Object> 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<String, String> 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<String, String> 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<String, Object> 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<String, Object> optionsMap) {
InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null)
inAppBrowserActivity.setOptions(options, optionsMap);
}
public HashMap<String, Object> getOptions(String uuid) {
InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null)
return inAppBrowserActivity.getOptions();
return null;
}
public HashMap<String, Object> 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<String> 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();
}
}
}
package com.pichillilorenzo.flutter_inappbrowser; package com.pichillilorenzo.flutter_inappbrowser;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
...@@ -18,6 +17,9 @@ import android.view.Menu; ...@@ -18,6 +17,9 @@ import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.SearchView; import android.widget.SearchView;
...@@ -26,13 +28,10 @@ import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebViewOptions ...@@ -26,13 +28,10 @@ import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebViewOptions
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.flutter.app.FlutterActivity;
import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel;
public class InAppBrowserActivity extends AppCompatActivity { public class InAppBrowserActivity extends AppCompatActivity {
...@@ -71,7 +70,7 @@ public class InAppBrowserActivity extends AppCompatActivity { ...@@ -71,7 +70,7 @@ public class InAppBrowserActivity extends AppCompatActivity {
webViewOptions.parse(optionsMap); webViewOptions.parse(optionsMap);
webView.options = webViewOptions; webView.options = webViewOptions;
InAppBrowserFlutterPlugin.instance.webViewActivities.put(uuid, this); InAppBrowserFlutterPlugin.inAppBrowser.webViewActivities.put(uuid, this);
actionBar = getSupportActionBar(); actionBar = getSupportActionBar();
...@@ -93,7 +92,7 @@ public class InAppBrowserActivity extends AppCompatActivity { ...@@ -93,7 +92,7 @@ public class InAppBrowserActivity extends AppCompatActivity {
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid); 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 { ...@@ -257,7 +256,7 @@ public class InAppBrowserActivity extends AppCompatActivity {
if (canGoBack()) if (canGoBack())
goBack(); goBack();
else if (options.closeOnCannotGoBack) else if (options.closeOnCannotGoBack)
InAppBrowserFlutterPlugin.instance.close(this, uuid, null); InAppBrowserFlutterPlugin.inAppBrowser.close(this, uuid, null);
return true; return true;
} }
return super.onKeyDown(keyCode, event); return super.onKeyDown(keyCode, event);
...@@ -356,7 +355,7 @@ public class InAppBrowserActivity extends AppCompatActivity { ...@@ -356,7 +355,7 @@ public class InAppBrowserActivity extends AppCompatActivity {
} }
public void closeButtonClicked(MenuItem item) { public void closeButtonClicked(MenuItem item) {
InAppBrowserFlutterPlugin.instance.close(this, uuid, null); InAppBrowserFlutterPlugin.inAppBrowser.close(this, uuid, null);
} }
public byte[] takeScreenshot() { public byte[] takeScreenshot() {
...@@ -522,4 +521,18 @@ public class InAppBrowserActivity extends AppCompatActivity { ...@@ -522,4 +521,18 @@ public class InAppBrowserActivity extends AppCompatActivity {
else else
result.success(false); 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");
}
}
} }
/*
*
* 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; 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.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;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.PluginRegistry.Registrar;
/**
* InAppBrowserFlutterPlugin
*/
public class InAppBrowserFlutterPlugin implements MethodCallHandler {
public static InAppBrowserFlutterPlugin instance; public class InAppBrowserFlutterPlugin implements FlutterPlugin {
public Registrar registrar; public PluginRegistry.Registrar registrar;
public MethodChannel channel; public MethodChannel channel;
public Map<String, InAppBrowserActivity> webViewActivities = new HashMap<>();
public Map<String, ChromeCustomTabsActivity> chromeCustomTabsActivities = new HashMap<>();
protected static final String LOG_TAG = "IABFlutterPlugin"; protected static final String LOG_TAG = "InAppBrowserFlutterPlugin";
public InAppBrowserFlutterPlugin(Registrar r) { public static InAppBrowser inAppBrowser;
registrar = r; public static MyCookieManager myCookieManager;
channel = new MethodChannel(registrar.messenger(), "com.pichillilorenzo/flutter_inappbrowser"); public static CredentialDatabaseHandler credentialDatabaseHandler;
channel.setMethodCallHandler(this);
}
/** public InAppBrowserFlutterPlugin() {}
* 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);
new MyCookieManager(registrar); public static void registerWith(PluginRegistry.Registrar registrar) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { inAppBrowser = new InAppBrowser(registrar);
new CredentialDatabaseHandler(registrar);
}
registrar registrar
.platformViewRegistry() .platformViewRegistry()
.registerViewFactory( .registerViewFactory(
"com.pichillilorenzo/flutter_inappwebview", new FlutterWebViewFactory(registrar, registrar.view())); "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 @Override
public void onMethodCall(final MethodCall call, final Result result) { public void onAttachedToEngine(FlutterPluginBinding binding) {
String source; //BinaryMessenger messenger = binding.getFlutterEngine().getDartExecutor();
String urlFile; inAppBrowser = new InAppBrowser(registrar);
final Activity activity = registrar.activity(); binding
final String uuid = (String) call.argument("uuid"); .getFlutterEngine()
.getPlatformViewsController()
switch (call.method) { .getRegistry()
case "open": .registerViewFactory(
boolean isData = (boolean) call.argument("isData"); "com.pichillilorenzo/flutter_inappwebview", new FlutterWebViewFactory(registrar,null));
if (!isData) { myCookieManager = new MyCookieManager(registrar);
final String url_final = (String) call.argument("url"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
credentialDatabaseHandler = new CredentialDatabaseHandler(registrar);
final boolean useChromeSafariBrowser = (boolean) call.argument("useChromeSafariBrowser");
final Map<String, String> headers = (Map<String, String>) 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<String, Object> options = (HashMap<String, Object>) call.argument("options");
final HashMap<String, Object> optionsFallback = (HashMap<String, Object>) call.argument("optionsFallback");
open(activity, uuid, uuidFallback, url_final, options, headers, true, optionsFallback, result);
} else {
String url = url_final;
final HashMap<String, Object> options = (HashMap<String, Object>) 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<String, Object> options = (HashMap<String, Object>) 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<String, String>) 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<String, String>) 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<String, Object> inAppBrowserOptionsMap = (HashMap<String, Object>) 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<String>) 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<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
ArrayList<Intent> targetIntents = new ArrayList<Intent>();
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<String, Object> options, Map<String, String> headers, boolean useChromeSafariBrowser, HashMap<String, Object> 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<String, Object> 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<String, String> 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) { @Override
InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid); public void onDetachedFromEngine(FlutterPluginBinding binding) {
if (inAppBrowserActivity != null) if (inAppBrowser != null) {
inAppBrowserActivity.postUrl(url, postData, result); inAppBrowser.dispose();
} inAppBrowser = null;
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<String, String> headers, Result result) {
InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null) {
if (headers != null)
inAppBrowserActivity.loadFile(url, headers, result);
else
inAppBrowserActivity.loadFile(url, result);
} }
} if (myCookieManager != null) {
myCookieManager.dispose();
public void show(String uuid) { myCookieManager = null;
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<String, Object> 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) { if (credentialDatabaseHandler != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
result.success(true); credentialDatabaseHandler.dispose();
credentialDatabaseHandler = null;
} }
} }
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<String, Object> optionsMap) {
InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null)
inAppBrowserActivity.setOptions(options, optionsMap);
}
public HashMap<String, Object> getOptions(String uuid) {
InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null)
return inAppBrowserActivity.getOptions();
return null;
}
public HashMap<String, Object> 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<String> 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);
}
} }
...@@ -2,7 +2,7 @@ package com.pichillilorenzo.flutter_inappbrowser; ...@@ -2,7 +2,7 @@ package com.pichillilorenzo.flutter_inappbrowser;
public class InAppBrowserOptions extends Options { public class InAppBrowserOptions extends Options {
static final String LOG_TAG = "InAppBrowserOptions"; public static final String LOG_TAG = "InAppBrowserOptions";
public boolean hidden = false; public boolean hidden = false;
public boolean toolbarTop = true; public boolean toolbarTop = true;
......
...@@ -538,6 +538,6 @@ public class InAppWebChromeClient extends WebChromeClient { ...@@ -538,6 +538,6 @@ public class InAppWebChromeClient extends WebChromeClient {
} }
private MethodChannel getChannel() { private MethodChannel getChannel() {
return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.instance.channel : flutterWebView.channel; return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.inAppBrowser.channel : flutterWebView.channel;
} }
} }
...@@ -686,7 +686,7 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -686,7 +686,7 @@ final public class InAppWebView extends InputAwareWebView {
} }
private MethodChannel getChannel() { 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) { public void startSafeBrowsing(final MethodChannel.Result result) {
...@@ -745,6 +745,11 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -745,6 +745,11 @@ final public class InAppWebView extends InputAwareWebView {
webSettings.setBuiltInZoomControls(enabled); webSettings.setBuiltInZoomControls(enabled);
} }
@Override
public void dispose() {
super.dispose();
}
@Override @Override
public void destroy() { public void destroy() {
super.destroy(); super.destroy();
......
...@@ -224,7 +224,7 @@ public class InAppWebViewClient extends WebViewClient { ...@@ -224,7 +224,7 @@ public class InAppWebViewClient extends WebViewClient {
previousAuthRequestFailureCount = 0; previousAuthRequestFailureCount = 0;
credentialsProposed = null; 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) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().flush(); CookieManager.getInstance().flush();
} else { } else {
...@@ -667,7 +667,7 @@ public class InAppWebViewClient extends WebViewClient { ...@@ -667,7 +667,7 @@ public class InAppWebViewClient extends WebViewClient {
} }
private MethodChannel getChannel() { private MethodChannel getChannel() {
return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.instance.channel : flutterWebView.channel; return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.inAppBrowser.channel : flutterWebView.channel;
} }
} }
...@@ -10,7 +10,7 @@ import java.util.Map; ...@@ -10,7 +10,7 @@ import java.util.Map;
public class InAppWebViewOptions extends Options { public class InAppWebViewOptions extends Options {
static final String LOG_TAG = "InAppWebViewOptions"; public static final String LOG_TAG = "InAppWebViewOptions";
public boolean useShouldOverrideUrlLoading = false; public boolean useShouldOverrideUrlLoading = false;
public boolean useOnLoadResource = false; public boolean useOnLoadResource = false;
......
...@@ -4,6 +4,7 @@ import static android.content.Context.INPUT_METHOD_SERVICE; ...@@ -4,6 +4,7 @@ import static android.content.Context.INPUT_METHOD_SERVICE;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.View; import android.view.View;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView; import android.webkit.WebView;
...@@ -15,8 +16,8 @@ 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 * 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 { public class InputAwareWebView extends WebView {
private static final String LOG_TAG = "InputAwareWebView";
public View containerView; public View containerView;
private View threadedInputConnectionProxyView; private View threadedInputConnectionProxyView;
private ThreadedInputConnectionProxyAdapterView proxyAdapterView; private ThreadedInputConnectionProxyAdapterView proxyAdapterView;
...@@ -40,6 +41,19 @@ public class InputAwareWebView extends WebView { ...@@ -40,6 +41,19 @@ public class InputAwareWebView extends WebView {
this.containerView = null; 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. * 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 { ...@@ -82,8 +96,6 @@ public class InputAwareWebView extends WebView {
*/ */
@Override @Override
public boolean checkInputConnectionProxy(final View view) { public boolean checkInputConnectionProxy(final View view) {
if (containerView == null)
return super.checkInputConnectionProxy(view);
// Check to see if the view param is WebView's ThreadedInputConnectionProxyView. // Check to see if the view param is WebView's ThreadedInputConnectionProxyView.
View previousProxy = threadedInputConnectionProxyView; View previousProxy = threadedInputConnectionProxyView;
threadedInputConnectionProxyView = view; threadedInputConnectionProxyView = view;
...@@ -91,6 +103,12 @@ public class InputAwareWebView extends WebView { ...@@ -91,6 +103,12 @@ public class InputAwareWebView extends WebView {
// This isn't a new ThreadedInputConnectionProxyView. Ignore it. // This isn't a new ThreadedInputConnectionProxyView. Ignore it.
return super.checkInputConnectionProxy(view); 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 // 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 // ThreadedInputConnectionProxyView. We are making the assumption that the only view that could
...@@ -115,8 +133,7 @@ public class InputAwareWebView extends WebView { ...@@ -115,8 +133,7 @@ public class InputAwareWebView extends WebView {
@Override @Override
public void clearFocus() { public void clearFocus() {
super.clearFocus(); super.clearFocus();
if (containerView != null) resetInputConnection();
resetInputConnection();
} }
/** /**
...@@ -131,6 +148,10 @@ public class InputAwareWebView extends WebView { ...@@ -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. // No need to reset the InputConnection to the default thread if we've never changed it.
return; 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); setInputConnectionTarget(/*targetView=*/ containerView);
} }
...@@ -143,6 +164,13 @@ public class InputAwareWebView extends WebView { ...@@ -143,6 +164,13 @@ public class InputAwareWebView extends WebView {
* InputConnections should be created on. * InputConnections should be created on.
*/ */
private void setInputConnectionTarget(final View targetView) { 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(); targetView.requestFocus();
containerView.post( containerView.post(
new Runnable() { new Runnable() {
......
...@@ -11,8 +11,6 @@ import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView; ...@@ -11,8 +11,6 @@ import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
...@@ -121,6 +119,6 @@ public class JavaScriptBridgeInterface { ...@@ -121,6 +119,6 @@ public class JavaScriptBridgeInterface {
} }
private MethodChannel getChannel() { private MethodChannel getChannel() {
return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.instance.channel : flutterWebView.channel; return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.inAppBrowser.channel : flutterWebView.channel;
} }
} }
...@@ -226,4 +226,7 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler { ...@@ -226,4 +226,7 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
return sdf.format(new Date(timestamp)); return sdf.format(new Date(timestamp));
} }
public void dispose() {
channel.setMethodCallHandler(null);
}
} }
...@@ -13,14 +13,24 @@ import java.security.Key; ...@@ -13,14 +13,24 @@ import java.security.Key;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CountDownLatch; 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.MethodChannel;
import io.flutter.plugin.common.PluginRegistry; import io.flutter.plugin.common.PluginRegistry;
import okhttp3.OkHttpClient;
public class Util { public class Util {
...@@ -148,4 +158,50 @@ public class Util { ...@@ -148,4 +158,50 @@ public class Util {
this.certificates = certificates; 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);
}
}
} }
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
android.enableR8=true
\ No newline at end of file
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
<link rel="stylesheet" href="http://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="http://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="css/style.css">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<link rel="shortcut icon" href="favicon.ico">
</head> </head>
<body class="text-center"> <body class="text-center">
<div class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column"> <div class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column">
...@@ -26,6 +27,10 @@ ...@@ -26,6 +27,10 @@
<h1 class="cover-heading">Inline WebView</h1> <h1 class="cover-heading">Inline WebView</h1>
<img src="my-special-custom-scheme://images/flutter-logo.svg" alt="flutter logo"> <img src="my-special-custom-scheme://images/flutter-logo.svg" alt="flutter logo">
<p class="lead">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.</p> <p class="lead">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.</p>
<select name="" id="">
<option value="1">option 1</option>
<option value="2">option 2</option>
</select>
<p> <p>
<img src="https://via.placeholder.com/100x50" alt="placeholder 100x50"> <img src="https://via.placeholder.com/100x50" alt="placeholder 100x50">
</p> </p>
......
...@@ -5,6 +5,6 @@ export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappbro ...@@ -5,6 +5,6 @@ export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappbro
export "FLUTTER_TARGET=lib/main.dart" export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios" 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_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1" export "FLUTTER_BUILD_NUMBER=1"
...@@ -83,11 +83,11 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> { ...@@ -83,11 +83,11 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> {
BoxDecoration(border: Border.all(color: Colors.blueAccent)), BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView( child: InAppWebView(
//initialUrl: "https://www.youtube.com/embed/M7lc1UVf-VE?playsinline=1", //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: "chrome://safe-browsing/match?type=malware",
//initialUrl: "http://192.168.1.20:8081/", //initialUrl: "http://192.168.1.20:8081/",
//initialUrl: "https://192.168.1.20:4433/", //initialUrl: "https://192.168.1.20:4433/",
//initialFile: "assets/index.html", initialFile: "assets/index.html",
initialHeaders: {}, initialHeaders: {},
initialOptions: [ initialOptions: [
InAppWebViewOptions( InAppWebViewOptions(
...@@ -143,8 +143,8 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> { ...@@ -143,8 +143,8 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> {
controller.clearSslPreferences(); controller.clearSslPreferences();
controller.clearClientCertPreferences(); controller.clearClientCertPreferences();
} }
//controller.findAllAsync("a"); //controller.findAllAsync("flutter");
controller.getFavicon(); print(await controller.getFavicons());
}, },
onLoadError: (InAppWebViewController controller, String url, int code, String message) async { onLoadError: (InAppWebViewController controller, String url, int code, String message) async {
print("error $url: $code, $message"); print("error $url: $code, $message");
......
...@@ -48,6 +48,7 @@ flutter: ...@@ -48,6 +48,7 @@ flutter:
- assets/page-2.html - assets/page-2.html
- assets/css/ - assets/css/
- assets/images/ - assets/images/
- assets/favicon.ico
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
# assets: # assets:
......
...@@ -301,25 +301,9 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView { ...@@ -301,25 +301,9 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
} }
result(true) result(true)
break break
case "dispose":
dispose()
result(true)
break
default: default:
result(FlutterMethodNotImplemented) result(FlutterMethodNotImplemented)
break 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
}
}
} }
...@@ -238,7 +238,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -238,7 +238,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
var IAWController: FlutterWebViewController? var IAWController: FlutterWebViewController?
var options: InAppWebViewOptions? var options: InAppWebViewOptions?
var currentURL: URL? var currentURL: URL?
var WKNavigationMap: [String: [String: Any]] = [:]
var startPageTime: Int64 = 0 var startPageTime: Int64 = 0
static var credentialsProposed: [URLCredential] = [] static var credentialsProposed: [URLCredential] = []
...@@ -786,13 +785,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -786,13 +785,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let app = UIApplication.shared let app = UIApplication.shared
if let url = navigationAction.request.url { if let url = navigationAction.request.url {
if url.absoluteString != url.absoluteString && (options?.useOnLoadResource)! {
WKNavigationMap[url.absoluteString] = [
"startTime": currentTimeInMilliSeconds(),
"request": navigationAction.request
]
}
// Handle target="_blank" // Handle target="_blank"
if navigationAction.targetFrame == nil && (options?.useOnTargetBlank)! { if navigationAction.targetFrame == nil && (options?.useOnTargetBlank)! {
onTargetBlank(url: url) onTargetBlank(url: url)
...@@ -834,17 +826,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -834,17 +826,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
decidePolicyFor navigationResponse: WKNavigationResponse, decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { 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)! { if (options?.useOnDownloadStart)! {
let mimeType = navigationResponse.response.mimeType let mimeType = navigationResponse.response.mimeType
if let url = navigationResponse.response.url { if let url = navigationResponse.response.url {
...@@ -875,7 +856,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -875,7 +856,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
} }
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.WKNavigationMap = [:]
currentURL = url currentURL = url
InAppWebView.credentialsProposed = [] InAppWebView.credentialsProposed = []
onLoadStop(url: (currentURL?.absoluteString)!) onLoadStop(url: (currentURL?.absoluteString)!)
......
...@@ -17,7 +17,7 @@ A new Flutter plugin. ...@@ -17,7 +17,7 @@ A new Flutter plugin.
s.public_header_files = 'Classes/**/*.h' s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter' 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.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
s.swift_version = '5.0' s.swift_version = '5.0'
end end
...@@ -268,7 +268,8 @@ class InAppBrowser { ...@@ -268,7 +268,8 @@ class InAppBrowser {
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('optionsType', () => "InAppBrowserOptions"); args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
Map<dynamic, dynamic> options = await ChannelManager.channel.invokeMethod('getOptions', args); Map<dynamic, dynamic> options = await ChannelManager.channel.invokeMethod('getOptions', args);
options = options.cast<String, dynamic>(); if (options != null)
options = options.cast<String, dynamic>();
return options; return options;
} }
......
...@@ -10,9 +10,10 @@ import 'package:flutter/services.dart'; ...@@ -10,9 +10,10 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:html/parser.dart' show parse;
import 'types.dart'; import 'types.dart';
import 'in_app_browser.dart'; import 'in_app_browser.dart';
import 'channel_manager.dart';
import 'webview_options.dart'; import 'webview_options.dart';
/* /*
...@@ -255,15 +256,6 @@ class _InAppWebViewState extends State<InAppWebView> { ...@@ -255,15 +256,6 @@ class _InAppWebViewState extends State<InAppWebView> {
InAppWebViewController _controller; InAppWebViewController _controller;
@override
void dispose() {
super.dispose();
if (_controller != null) {
_controller._dispose();
_controller = null;
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Map<String, dynamic> initialOptions = {}; Map<String, dynamic> initialOptions = {};
...@@ -273,7 +265,22 @@ class _InAppWebViewState extends State<InAppWebView> { ...@@ -273,7 +265,22 @@ class _InAppWebViewState extends State<InAppWebView> {
}); });
if (defaultTargetPlatform == TargetPlatform.android) { if (defaultTargetPlatform == TargetPlatform.android) {
return GestureDetector( return AndroidView(
viewType: 'com.pichillilorenzo/flutter_inappwebview',
onPlatformViewCreated: _onPlatformViewCreated,
gestureRecognizers: widget.gestureRecognizers,
layoutDirection: TextDirection.rtl,
creationParams: <String, dynamic>{
'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: () {}, onLongPress: () {},
excludeFromSemantics: true, excludeFromSemantics: true,
child: AndroidView( child: AndroidView(
...@@ -290,7 +297,7 @@ class _InAppWebViewState extends State<InAppWebView> { ...@@ -290,7 +297,7 @@ class _InAppWebViewState extends State<InAppWebView> {
}, },
creationParamsCodec: const StandardMessageCodec(), creationParamsCodec: const StandardMessageCodec(),
), ),
); );*/
} else if (defaultTargetPlatform == TargetPlatform.iOS) { } else if (defaultTargetPlatform == TargetPlatform.iOS) {
return UiKitView( return UiKitView(
viewType: 'com.pichillilorenzo/flutter_inappwebview', viewType: 'com.pichillilorenzo/flutter_inappwebview',
...@@ -593,105 +600,146 @@ class InAppWebViewController { ...@@ -593,105 +600,146 @@ class InAppWebViewController {
return await _channel.invokeMethod('getProgress', args); return await _channel.invokeMethod('getProgress', args);
} }
///Gets the favicon for the current page. ///Gets the content html of the page. It first tries to get the content through javascript.
Future<List<int>> getFavicon() async { ///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<String> getHtml() async {
var html = "";
Map<String, dynamic> options = await getOptions();
if (options != null && options["javaScriptEnabled"] == true) {
html = await injectScriptCode("window.document.getElementsByTagName('html')[0].outerHTML;");
if (html.isNotEmpty)
return html;
}
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);
}
}
return html;
}
///Gets the list of all favicons for the current page.
Future<List<Favicon>> getFavicons() async {
List<Favicon> favicons = []; List<Favicon> favicons = [];
HttpClient client = new HttpClient(); HttpClient client = new HttpClient();
var url = Uri.parse(await getUrl()); var webviewUrl = await getUrl();
var url = (webviewUrl.startsWith("file:///")) ? Uri.file(webviewUrl) : Uri.parse(webviewUrl);
String manifestUrl;
var htmlRequest = await client.getUrl(url); var html = await getHtml();
var html = await (await htmlRequest.close()).transform(Utf8Decoder()).join(); if (html.isEmpty) {
/// TODO: parse HTML instead of using javascript code return favicons;
}
try { var assetPathBase;
List<dynamic> faviconsJs = json.decode(await injectScriptCode("""
function flutter_inappbrowser_ger_favicons() { if (webviewUrl.startsWith("file:///")) {
var favicons = []; var assetPathSplitted = webviewUrl.split("/flutter_assets/");
var links = document.getElementsByTagName('link'); assetPathBase = assetPathSplitted[0] + "/flutter_assets/";
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
});
}
} }
return favicons;
} // get all link html elements
flutter_inappbrowser_ger_favicons(); var document = parse(html);
""")); var links = document.getElementsByTagName('link');
for(Map<String, dynamic> favicon in faviconsJs) { for (var link in links) {
String sizes = favicon["sizes"]; var attributes = link.attributes;
if (sizes != null && sizes != "any") { if (attributes["rel"] == "manifest") {
List<String> sizesSplitted = sizes.split(" "); manifestUrl = attributes["href"];
for (String size in sizesSplitted) { if (!_isUrlAbsolute(manifestUrl)) {
int width = int.parse(size.split("x")[0]); if (manifestUrl.startsWith("/")) {
int height = int.parse(size.split("x")[1]); manifestUrl = manifestUrl.substring(1);
favicons.add(Favicon(url: favicon["href"], rel: favicon["rel"], width: width, height: height));
} }
} else { manifestUrl = ((assetPathBase == null) ? url.scheme + "://" + url.host + "/" : assetPathBase) + manifestUrl;
favicons.add(Favicon(url: favicon["href"], rel: favicon["rel"], width: null, height: null));
} }
continue;
} }
} catch (e) {} if (!attributes["rel"].contains("icon")) {
continue;
}
var completer = new Completer<List<int>>(); favicons.addAll(_createFavicons(url, assetPathBase, attributes["href"], attributes["rel"], attributes["sizes"], false));
var faviconData = new List<int>(); }
// try to get /favicon.ico
try { try {
var faviconUrl = url.scheme + "://" + url.host + "/favicon.ico"; var faviconUrl = url.scheme + "://" + url.host + "/favicon.ico";
await client.headUrl(Uri.parse(faviconUrl)); await client.headUrl(Uri.parse(faviconUrl));
favicons.add(Favicon(url: faviconUrl, rel: "shortcut icon")); favicons.add(Favicon(url: faviconUrl, rel: "shortcut icon"));
} catch(e) {} } catch(e) {
print("/favicon.ico file not found: " + e.toString());
}
var manifestRequest; // try to get the manifest file
HttpClientRequest manifestRequest;
HttpClientResponse manifestResponse;
bool manifestFound = false;
if (manifestUrl == null) {
manifestUrl = url.scheme + "://" + url.host + "/manifest.json";
}
try { try {
var manifestJsonUrl = url.scheme + "://" + url.host + "/manifest.json"; manifestRequest = await client.getUrl(Uri.parse(manifestUrl));
manifestRequest = await client.getUrl(Uri.parse(manifestJsonUrl)); manifestResponse = await manifestRequest.close();
manifestFound = manifestResponse.statusCode == 200 && manifestResponse.headers.contentType?.mimeType == "application/json";
} catch(e) { } catch(e) {
/// TODO: find manifest throught rel="manifest" print("Manifest file not found: " + e.toString());
} }
if (manifestRequest) { if (manifestFound) {
Map<String, dynamic> manifest = json.decode(await (await manifestRequest.close()).transform(Utf8Decoder()).join()); Map<String, dynamic> manifest = json.decode(await manifestResponse.transform(Utf8Decoder()).join());
if (manifest.containsKey("icons")) { if (manifest.containsKey("icons")) {
for(Map<String, dynamic> icon in manifest["icons"]) { for(Map<String, dynamic> icon in manifest["icons"]) {
String url = icon["src"]; favicons.addAll(_createFavicons(url, assetPathBase, icon["src"], icon["rel"], icon["sizes"], true));
List<String> 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<String> 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));
}
} }
} }
} }
//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); bool _isUrlAbsolute(String url) {
client.getUrl(googleFaviconUrl).then((HttpClientRequest request) { return url.startsWith("http://") || url.startsWith("https://");
return request.close(); }
}).then((HttpClientResponse response) {
response.listen((List<int> data) { List<Favicon> _createFavicons(Uri url, String assetPathBase, String urlIcon, String rel, String sizes, bool isManifest) {
faviconData = data; List<Favicon> favicons = [];
}, onDone: () => completer.complete(faviconData));
}).catchError((error) { List<String> urlSplitted = urlIcon.split("/");
completer.completeError(error); if (!_isUrlAbsolute(urlIcon)) {
}); if (urlIcon.startsWith("/")) {
return completer.future; 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<String> 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. ///Loads the given [url] with optional [headers] specified as a map from name to value.
...@@ -1001,7 +1049,6 @@ flutter_inappbrowser_ger_favicons(); ...@@ -1001,7 +1049,6 @@ flutter_inappbrowser_ger_favicons();
args.putIfAbsent('uuid', () => _inAppBrowserUuid); args.putIfAbsent('uuid', () => _inAppBrowserUuid);
} }
args.putIfAbsent('options', () => options); args.putIfAbsent('options', () => options);
args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
await _channel.invokeMethod('setOptions', args); await _channel.invokeMethod('setOptions', args);
} }
...@@ -1012,9 +1059,9 @@ flutter_inappbrowser_ger_favicons(); ...@@ -1012,9 +1059,9 @@ flutter_inappbrowser_ger_favicons();
_inAppBrowser.throwIsNotOpened(); _inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid); args.putIfAbsent('uuid', () => _inAppBrowserUuid);
} }
args.putIfAbsent('optionsType', () => "InAppBrowserOptions"); Map<dynamic, dynamic> options = await _channel.invokeMethod('getOptions', args);
Map<dynamic, dynamic> options = await ChannelManager.channel.invokeMethod('getOptions', args); if (options != null)
options = options.cast<String, dynamic>(); options = options.cast<String, dynamic>();
return options; return options;
} }
...@@ -1183,9 +1230,4 @@ flutter_inappbrowser_ger_favicons(); ...@@ -1183,9 +1230,4 @@ flutter_inappbrowser_ger_favicons();
} }
await _channel.invokeMethod('clearMatches', args); await _channel.invokeMethod('clearMatches', args);
} }
///Dispose/Destroy the WebView.
Future<void> _dispose() async {
await _channel.invokeMethod('dispose');
}
} }
...@@ -43,6 +43,7 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti ...@@ -43,6 +43,7 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti
this.resourceCustomSchemes = const [], this.contentBlockers = const [], this.preferredContentMode = InAppWebViewUserPreferredContentMode.RECOMMENDED}) { this.resourceCustomSchemes = const [], this.contentBlockers = const [], this.preferredContentMode = InAppWebViewUserPreferredContentMode.RECOMMENDED}) {
if (this.minimumFontSize == null) if (this.minimumFontSize == null)
this.minimumFontSize = Platform.isAndroid ? 8 : 0; this.minimumFontSize = Platform.isAndroid ? 8 : 0;
assert(!this.resourceCustomSchemes.contains("http") && !this.resourceCustomSchemes.contains("https"));
} }
@override @override
......
...@@ -23,7 +23,16 @@ appHttps.get('/', (req, res) => { ...@@ -23,7 +23,16 @@ appHttps.get('/', (req, res) => {
// `localhost`. // `localhost`.
if (req.client.authorized) { if (req.client.authorized) {
res.send(`Hello ${cert.subject.CN}, your certificate was issued by ${cert.issuer.CN}!`) res.send(`
<html>
<head>
<script src="/fakeResource" type="text/javascript"></script>
</head>
<body>
<p>Hello ${cert.subject.CN}, your certificate was issued by ${cert.issuer.CN}!</p>
</body>
</html>
`)
// They can still provide a certificate which is not accepted by us. Unfortunately, the `cert` object will be an empty // 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 // object instead of `null` if there is no certificate at all, so we have to check for a known field rather than
// truthiness. // truthiness.
...@@ -34,6 +43,13 @@ appHttps.get('/', (req, res) => { ...@@ -34,6 +43,13 @@ appHttps.get('/', (req, res) => {
} else { } else {
res.status(401).send(`Sorry, but you need to provide a client certificate to continue.`) 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. // Let's create our HTTPS server and we're ready to go.
......
...@@ -5,14 +5,15 @@ author: Lorenzo Pichilli <pichillilorenzo@gmail.com> ...@@ -5,14 +5,15 @@ author: Lorenzo Pichilli <pichillilorenzo@gmail.com>
homepage: https://github.com/pichillilorenzo/flutter_inappbrowser homepage: https://github.com/pichillilorenzo/flutter_inappbrowser
environment: environment:
sdk: ">=2.0.0-dev <3.0.0" sdk: ">=2.0.0-dev.68.0 <3.0.0"
flutter: ">=0.10.1 <2.0.0" flutter: ">=1.9.1+hotfix.5 <2.0.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
uuid: ^2.0.0 uuid: ^2.0.0
mime: ^0.9.6+2 mime: ^0.9.6+2
html: ^0.14.0+3
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec # following page: https://www.dartlang.org/tools/pub/pubspec
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment