fixed onLoadResource, added InAppBrowser.addJavaScriptHandler() and InAppBrowser.removeJavaScriptHandler() methods, v0.3.0
This commit is contained in:
parent
b4e3e73bce
commit
88d88bd11d
154
.idea/workspace.xml
generated
154
.idea/workspace.xml
generated
@ -16,13 +16,19 @@
|
|||||||
<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 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$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" 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/InAppBrowserWebViewClient.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebViewClient.java" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebViewClient.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebViewClient.java" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/WebViewActivity.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/WebViewActivity.java" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/example/lib/main.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/main.dart" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/example/lib/main.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/main.dart" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/ios/Classes/InAppBrowserOptions.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/InAppBrowserOptions.swift" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/ios/Classes/InAppBrowserWebViewController.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/InAppBrowserWebViewController.swift" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/ios/Classes/InAppBrowserWebViewController.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/InAppBrowserWebViewController.swift" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/ios/Classes/MyURLProtocol.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/MyURLProtocol.swift" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/ios/Classes/MyURLProtocol.swift" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/ios/Classes/NSURLProtocol+WKWebVIew.h" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/ios/Classes/NSURLProtocol+WKWebVIew.m" beforeDir="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/Classes/SwiftFlutterPlugin.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" 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/" />
|
||||||
@ -42,8 +48,8 @@
|
|||||||
<file leaf-file-name="flutter_inappbrowser.dart" pinned="false" current-in-tab="false">
|
<file leaf-file-name="flutter_inappbrowser.dart" pinned="false" current-in-tab="false">
|
||||||
<entry file="file://$PROJECT_DIR$/lib/flutter_inappbrowser.dart">
|
<entry file="file://$PROJECT_DIR$/lib/flutter_inappbrowser.dart">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="315">
|
<state relative-caret-position="-386">
|
||||||
<caret line="246" column="71" selection-start-line="246" selection-start-column="71" selection-end-line="246" selection-end-column="71" />
|
<caret line="366" column="187" selection-start-line="366" selection-start-column="187" selection-end-line="366" selection-end-column="187" />
|
||||||
<folding>
|
<folding>
|
||||||
<element signature="e#814#834#0" expanded="true" />
|
<element signature="e#814#834#0" expanded="true" />
|
||||||
</folding>
|
</folding>
|
||||||
@ -54,18 +60,18 @@
|
|||||||
<file leaf-file-name="build.gradle" pinned="false" current-in-tab="false">
|
<file leaf-file-name="build.gradle" pinned="false" current-in-tab="false">
|
||||||
<entry file="file://$PROJECT_DIR$/android/build.gradle">
|
<entry file="file://$PROJECT_DIR$/android/build.gradle">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="262">
|
<state relative-caret-position="462">
|
||||||
<caret line="34" column="5" selection-start-line="34" selection-start-column="5" selection-end-line="34" selection-end-column="5" />
|
<caret line="34" column="5" selection-start-line="34" selection-start-column="5" selection-end-line="34" selection-end-column="5" />
|
||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
</file>
|
</file>
|
||||||
<file leaf-file-name="README.md" pinned="false" current-in-tab="false">
|
<file leaf-file-name="README.md" pinned="false" current-in-tab="true">
|
||||||
<entry file="file://$PROJECT_DIR$/README.md">
|
<entry file="file://$PROJECT_DIR$/README.md">
|
||||||
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
||||||
<state split_layout="SPLIT">
|
<state split_layout="SPLIT">
|
||||||
<first_editor relative-caret-position="2820">
|
<first_editor relative-caret-position="280">
|
||||||
<caret line="188" column="158" selection-start-line="188" selection-start-column="158" selection-end-line="188" selection-end-column="158" />
|
<caret line="592" lean-forward="true" selection-start-line="592" selection-end-line="592" />
|
||||||
</first_editor>
|
</first_editor>
|
||||||
<second_editor>
|
<second_editor>
|
||||||
<markdownNavigatorState />
|
<markdownNavigatorState />
|
||||||
@ -74,11 +80,11 @@
|
|||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
</file>
|
</file>
|
||||||
<file leaf-file-name="pubspec.yaml" pinned="false" current-in-tab="false">
|
<file leaf-file-name="main.dart" pinned="false" current-in-tab="false">
|
||||||
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
|
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="30">
|
<state relative-caret-position="-10">
|
||||||
<caret line="2" column="14" selection-start-line="2" selection-start-column="14" selection-end-line="2" selection-end-column="14" />
|
<caret line="99" lean-forward="true" selection-start-line="99" selection-end-line="99" />
|
||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
@ -87,7 +93,9 @@
|
|||||||
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
|
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
|
||||||
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
||||||
<state split_layout="SPLIT">
|
<state split_layout="SPLIT">
|
||||||
<first_editor />
|
<first_editor relative-caret-position="30">
|
||||||
|
<caret line="2" column="3" selection-start-line="2" selection-start-column="3" selection-end-line="2" selection-end-column="3" />
|
||||||
|
</first_editor>
|
||||||
<second_editor>
|
<second_editor>
|
||||||
<markdownNavigatorState />
|
<markdownNavigatorState />
|
||||||
</second_editor>
|
</second_editor>
|
||||||
@ -95,15 +103,6 @@
|
|||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
</file>
|
</file>
|
||||||
<file leaf-file-name="main.dart" pinned="false" current-in-tab="true">
|
|
||||||
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="537">
|
|
||||||
<caret line="140" column="44" selection-start-line="140" selection-start-column="44" selection-end-line="140" selection-end-column="64" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
</file>
|
|
||||||
</leaf>
|
</leaf>
|
||||||
</component>
|
</component>
|
||||||
<component name="FileTemplateManagerImpl">
|
<component name="FileTemplateManagerImpl">
|
||||||
@ -180,19 +179,18 @@
|
|||||||
<option value="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowser.java" />
|
<option value="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowser.java" />
|
||||||
<option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" />
|
<option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" />
|
||||||
<option value="$PROJECT_DIR$/example/ios/Podfile" />
|
<option value="$PROJECT_DIR$/example/ios/Podfile" />
|
||||||
<option value="$PROJECT_DIR$/CHANGELOG.md" />
|
|
||||||
<option value="$PROJECT_DIR$/README.md" />
|
|
||||||
<option value="$PROJECT_DIR$/android/build.gradle" />
|
<option value="$PROJECT_DIR$/android/build.gradle" />
|
||||||
<option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" />
|
|
||||||
<option value="$PROJECT_DIR$/pubspec.yaml" />
|
<option value="$PROJECT_DIR$/pubspec.yaml" />
|
||||||
<option value="$PROJECT_DIR$/example/lib/main.dart" />
|
<option value="$PROJECT_DIR$/example/lib/main.dart" />
|
||||||
|
<option value="$PROJECT_DIR$/CHANGELOG.md" />
|
||||||
|
<option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" />
|
||||||
|
<option value="$PROJECT_DIR$/README.md" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectFrameBounds">
|
<component name="ProjectFrameBounds" extendedState="6">
|
||||||
<option name="x" value="235" />
|
|
||||||
<option name="y" value="23" />
|
<option name="y" value="23" />
|
||||||
<option name="width" value="1589" />
|
<option name="width" value="1582" />
|
||||||
<option name="height" value="1027" />
|
<option name="height" value="1027" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
||||||
@ -396,7 +394,7 @@
|
|||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="ToolWindowManager">
|
<component name="ToolWindowManager">
|
||||||
<frame x="235" y="23" width="1589" height="1027" extended-state="0" />
|
<frame x="0" y="23" width="1920" height="1057" extended-state="6" />
|
||||||
<editor active="true" />
|
<editor active="true" />
|
||||||
<layout>
|
<layout>
|
||||||
<window_info anchor="bottom" id="Android Profiler" order="7" show_stripe_button="false" />
|
<window_info anchor="bottom" id="Android Profiler" order="7" show_stripe_button="false" />
|
||||||
@ -408,15 +406,15 @@
|
|||||||
<window_info anchor="right" id="Capture Analysis" order="3" />
|
<window_info anchor="right" id="Capture Analysis" order="3" />
|
||||||
<window_info anchor="bottom" id="Event Log" order="8" sideWeight="0.5035553" side_tool="true" weight="0.25689086" />
|
<window_info anchor="bottom" id="Event Log" order="8" sideWeight="0.5035553" side_tool="true" weight="0.25689086" />
|
||||||
<window_info anchor="bottom" id="Dart Analysis" order="14" weight="0.32855567" />
|
<window_info anchor="bottom" id="Dart Analysis" order="14" weight="0.32855567" />
|
||||||
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.49644473" weight="0.39801544" />
|
<window_info active="true" anchor="bottom" id="Run" order="2" sideWeight="0.49644473" visible="true" weight="0.20384204" />
|
||||||
<window_info anchor="bottom" id="Version Control" order="9" />
|
<window_info anchor="bottom" id="Version Control" order="9" />
|
||||||
<window_info active="true" anchor="bottom" id="Terminal" order="10" sideWeight="0.49644473" visible="true" weight="0.25689086" />
|
<window_info anchor="bottom" id="Terminal" order="10" sideWeight="0.49644473" weight="0.25689086" />
|
||||||
<window_info anchor="right" id="Flutter Outline" order="3" weight="0.32922077" />
|
<window_info anchor="right" id="Flutter Outline" order="3" weight="0.32922077" />
|
||||||
<window_info anchor="bottom" id="Logcat" order="11" />
|
<window_info anchor="bottom" id="Logcat" order="11" />
|
||||||
<window_info id="Captures" order="2" weight="0.32936507" />
|
<window_info id="Captures" order="2" weight="0.32936507" />
|
||||||
<window_info id="Capture Tool" order="2" />
|
<window_info id="Capture Tool" order="2" />
|
||||||
<window_info id="Designer" order="2" />
|
<window_info id="Designer" order="2" />
|
||||||
<window_info content_ui="combo" id="Project" order="0" sideWeight="0.49855492" visible="true" weight="0.18293472" />
|
<window_info content_ui="combo" id="Project" order="0" sideWeight="0.49855492" visible="true" weight="0.15069222" />
|
||||||
<window_info id="Structure" order="1" sideWeight="0.5014451" side_tool="true" weight="0.18293472" />
|
<window_info id="Structure" order="1" sideWeight="0.5014451" side_tool="true" weight="0.18293472" />
|
||||||
<window_info anchor="right" id="Device File Explorer" order="3" side_tool="true" />
|
<window_info anchor="right" id="Device File Explorer" order="3" side_tool="true" />
|
||||||
<window_info anchor="right" id="Theme Preview" order="3" />
|
<window_info anchor="right" id="Theme Preview" order="3" />
|
||||||
@ -672,38 +670,6 @@
|
|||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
<entry file="file://$PROJECT_DIR$/README.md">
|
|
||||||
<provider editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="247">
|
|
||||||
<caret line="19" column="15" selection-start-line="19" selection-start-column="3" selection-end-line="19" selection-end-column="15" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
|
||||||
<state split_layout="SPLIT">
|
|
||||||
<first_editor relative-caret-position="2820">
|
|
||||||
<caret line="188" column="158" selection-start-line="188" selection-start-column="158" selection-end-line="188" selection-end-column="158" />
|
|
||||||
</first_editor>
|
|
||||||
<second_editor>
|
|
||||||
<markdownNavigatorState />
|
|
||||||
</second_editor>
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
|
|
||||||
<provider editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="30">
|
|
||||||
<caret line="2" column="69" lean-forward="true" selection-start-line="2" selection-start-column="2" selection-end-line="2" selection-end-column="73" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
|
||||||
<state split_layout="SPLIT">
|
|
||||||
<first_editor />
|
|
||||||
<second_editor>
|
|
||||||
<markdownNavigatorState />
|
|
||||||
</second_editor>
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/android/settings.gradle">
|
<entry file="file://$PROJECT_DIR$/android/settings.gradle">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state>
|
<state>
|
||||||
@ -728,13 +694,6 @@
|
|||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
<entry file="file://$PROJECT_DIR$/android/build.gradle">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="262">
|
|
||||||
<caret line="34" column="5" selection-start-line="34" selection-start-column="5" selection-end-line="34" selection-end-column="5" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
|
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="30">
|
<state relative-caret-position="30">
|
||||||
@ -742,20 +701,61 @@
|
|||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="-10">
|
||||||
|
<caret line="99" lean-forward="true" selection-start-line="99" selection-end-line="99" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
|
||||||
|
<provider editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="30">
|
||||||
|
<caret line="2" column="69" lean-forward="true" selection-start-line="2" selection-start-column="2" selection-end-line="2" selection-end-column="73" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
||||||
|
<state split_layout="SPLIT">
|
||||||
|
<first_editor relative-caret-position="30">
|
||||||
|
<caret line="2" column="3" selection-start-line="2" selection-start-column="3" selection-end-line="2" selection-end-column="3" />
|
||||||
|
</first_editor>
|
||||||
|
<second_editor>
|
||||||
|
<markdownNavigatorState />
|
||||||
|
</second_editor>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/android/build.gradle">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="462">
|
||||||
|
<caret line="34" column="5" selection-start-line="34" selection-start-column="5" selection-end-line="34" selection-end-column="5" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
<entry file="file://$PROJECT_DIR$/lib/flutter_inappbrowser.dart">
|
<entry file="file://$PROJECT_DIR$/lib/flutter_inappbrowser.dart">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="315">
|
<state relative-caret-position="-386">
|
||||||
<caret line="246" column="71" selection-start-line="246" selection-start-column="71" selection-end-line="246" selection-end-column="71" />
|
<caret line="366" column="187" selection-start-line="366" selection-start-column="187" selection-end-line="366" selection-end-column="187" />
|
||||||
<folding>
|
<folding>
|
||||||
<element signature="e#814#834#0" expanded="true" />
|
<element signature="e#814#834#0" expanded="true" />
|
||||||
</folding>
|
</folding>
|
||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
|
<entry file="file://$PROJECT_DIR$/README.md">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider editor-type-id="text-editor">
|
||||||
<state relative-caret-position="537">
|
<state relative-caret-position="247">
|
||||||
<caret line="140" column="44" selection-start-line="140" selection-start-column="44" selection-end-line="140" selection-end-column="64" />
|
<caret line="19" column="15" selection-start-line="19" selection-start-column="3" selection-end-line="19" selection-end-column="15" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
||||||
|
<state split_layout="SPLIT">
|
||||||
|
<first_editor relative-caret-position="280">
|
||||||
|
<caret line="592" lean-forward="true" selection-start-line="592" selection-end-line="592" />
|
||||||
|
</first_editor>
|
||||||
|
<second_editor>
|
||||||
|
<markdownNavigatorState />
|
||||||
|
</second_editor>
|
||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
|
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,7 +1,15 @@
|
|||||||
|
## 0.3.0
|
||||||
|
|
||||||
|
- fixed WebView.storyboard to deployment target 8.0
|
||||||
|
- added `InAppBrowser.onLoadResource()` method. The event fires when the InAppBrowser webview loads a resource
|
||||||
|
- added `InAppBrowser.addJavaScriptHandler()` and `InAppBrowser.removeJavaScriptHandler()` methods to add/remove javascript message handlers
|
||||||
|
- removed `keyboardDisplayRequiresUserAction` from iOS available options
|
||||||
|
- now the `url` parameter of `InAppBrowser.open()` is optional. The default value is `about:blank`
|
||||||
|
|
||||||
## 0.2.1
|
## 0.2.1
|
||||||
|
|
||||||
- added InAppBrowser.onConsoleMessage() method to manage console messages
|
- added `InAppBrowser.onConsoleMessage()` method to manage console messages
|
||||||
- fixed InAppBrowser.injectScriptCode() method when there is not a return value
|
- fixed `InAppBrowser.injectScriptCode()` method when there is not a return value
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
|
|
||||||
|
65
README.md
65
README.md
@ -39,6 +39,10 @@ class MyInAppBrowser extends InAppBrowser {
|
|||||||
@override
|
@override
|
||||||
Future onLoadStop(String url) async {
|
Future onLoadStop(String url) async {
|
||||||
print("\n\nStopped $url\n\n");
|
print("\n\nStopped $url\n\n");
|
||||||
|
|
||||||
|
// call a javascript message handler
|
||||||
|
await this.injectScriptCode("window.flutter_inappbrowser.callHandler('handlerNameTest', 1, 5,'string', {'key': 5}, [4,6,8]);");
|
||||||
|
|
||||||
// print body html
|
// print body html
|
||||||
print(await this.injectScriptCode("document.body.innerHTML"));
|
print(await this.injectScriptCode("document.body.innerHTML"));
|
||||||
|
|
||||||
@ -78,6 +82,11 @@ class MyInAppBrowser extends InAppBrowser {
|
|||||||
this.loadUrl(url);
|
this.loadUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
|
||||||
|
print("Started at: " + response.startTime.toString() + "ms ---> duration: " + response.duration.toString() + "ms " + response.url);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onConsoleMessage(ConsoleMessage consoleMessage) {
|
void onConsoleMessage(ConsoleMessage consoleMessage) {
|
||||||
print("""
|
print("""
|
||||||
@ -105,6 +114,12 @@ class _MyAppState extends State<MyApp> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
|
// listen for post messages coming from the JavaScript side
|
||||||
|
int indexTest = inAppBrowserFallback.addJavaScriptHandler("handlerNameTest", (arguments) async {
|
||||||
|
print("handlerNameTest arguments");
|
||||||
|
print(arguments); // it prints: [1, 5, string, {key: 5}, [4, 6, 8]]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -117,7 +132,8 @@ class _MyAppState extends State<MyApp> {
|
|||||||
body: new Center(
|
body: new Center(
|
||||||
child: new RaisedButton(onPressed: () {
|
child: new RaisedButton(onPressed: () {
|
||||||
inAppBrowser.open("https://flutter.io/", options: {
|
inAppBrowser.open("https://flutter.io/", options: {
|
||||||
"useShouldOverrideUrlLoading": true
|
"useShouldOverrideUrlLoading": true,
|
||||||
|
"useOnLoadResource": true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Text("Open InAppBrowser")
|
child: Text("Open InAppBrowser")
|
||||||
@ -134,12 +150,12 @@ class _MyAppState extends State<MyApp> {
|
|||||||
Opens a URL in a new InAppBrowser instance or the system browser.
|
Opens a URL in a new InAppBrowser instance or the system browser.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
inAppBrowser.open(String url, {Map<String, String> headers = const {}, String target = "_self", Map<String, dynamic> options = const {}});
|
inAppBrowser.open({String url = "about:blank", Map<String, String> headers = const {}, String target = "_self", Map<String, dynamic> options = const {}});
|
||||||
```
|
```
|
||||||
|
|
||||||
Opens an `url` in a new `InAppBrowser` instance or the system browser.
|
Opens an `url` in a new `InAppBrowser` instance or the system browser.
|
||||||
|
|
||||||
- `url`: The `url` to load. Call `encodeUriComponent()` on this if the `url` contains Unicode characters.
|
- `url`: The `url` to load. Call `encodeUriComponent()` on this if the `url` contains Unicode characters. The default value is `about:blank`.
|
||||||
|
|
||||||
- `headers`: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value.
|
- `headers`: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value.
|
||||||
|
|
||||||
@ -153,6 +169,7 @@ Opens an `url` in a new `InAppBrowser` instance or the system browser.
|
|||||||
|
|
||||||
All platforms support:
|
All platforms support:
|
||||||
- __useShouldOverrideUrlLoading__: Set to `true` to be able to listen at the `shouldOverrideUrlLoading` event. The default value is `false`.
|
- __useShouldOverrideUrlLoading__: Set to `true` to be able to listen at the `shouldOverrideUrlLoading` event. The default value is `false`.
|
||||||
|
- __useOnLoadResource__: Set to `true` to be able to listen at the `onLoadResource()` event. The default value is `false`.
|
||||||
- __clearCache__: Set to `true` to have all the browser's cache cleared before the new window is opened. The default value is `false`.
|
- __clearCache__: Set to `true` to have all the browser's cache cleared before the new window is opened. The default value is `false`.
|
||||||
- __userAgent___: Set the custom WebView's user-agent.
|
- __userAgent___: Set the custom WebView's user-agent.
|
||||||
- __javaScriptEnabled__: Set to `true` to enable JavaScript. The default value is `true`.
|
- __javaScriptEnabled__: Set to `true` to enable JavaScript. The default value is `true`.
|
||||||
@ -200,6 +217,7 @@ Example:
|
|||||||
```dart
|
```dart
|
||||||
inAppBrowser.open('https://flutter.io/', options: {
|
inAppBrowser.open('https://flutter.io/', options: {
|
||||||
"useShouldOverrideUrlLoading": true,
|
"useShouldOverrideUrlLoading": true,
|
||||||
|
"useOnLoadResource": true,
|
||||||
"clearCache": true,
|
"clearCache": true,
|
||||||
"disallowOverScroll": true,
|
"disallowOverScroll": true,
|
||||||
"domStorageEnabled": true,
|
"domStorageEnabled": true,
|
||||||
@ -252,7 +270,8 @@ Event fires when the `InAppBrowser` webview receives a `ConsoleMessage`.
|
|||||||
```
|
```
|
||||||
|
|
||||||
Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
|
Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
|
||||||
In order to be able to listen this event, you need to set `useShouldOverrideUrlLoading` option to `true`.
|
|
||||||
|
**NOTE**: In order to be able to listen this event, you need to set `useShouldOverrideUrlLoading` option to `true`.
|
||||||
```dart
|
```dart
|
||||||
@override
|
@override
|
||||||
void shouldOverrideUrlLoading(String url) {
|
void shouldOverrideUrlLoading(String url) {
|
||||||
@ -260,6 +279,18 @@ In order to be able to listen this event, you need to set `useShouldOverrideUrlL
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Event fires when the `InAppBrowser` webview loads a resource.
|
||||||
|
|
||||||
|
**NOTE**: In order to be able to listen this event, you need to set `useOnLoadResource` option to `true`.
|
||||||
|
|
||||||
|
**NOTE only for iOS**: In some cases, the `response.data` of a `response` with `text/html` encoding could be empty.
|
||||||
|
```dart
|
||||||
|
@override
|
||||||
|
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Future\<void\> InAppBrowser.loadUrl
|
#### Future\<void\> InAppBrowser.loadUrl
|
||||||
|
|
||||||
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.
|
||||||
@ -370,7 +401,31 @@ Injects a CSS file into the `InAppBrowser` window. (Only available when the targ
|
|||||||
|
|
||||||
```dart
|
```dart
|
||||||
inAppBrowser.injectStyleFile(String urlFile);
|
inAppBrowser.injectStyleFile(String urlFile);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### int InAppBrowser.addJavaScriptHandler
|
||||||
|
|
||||||
|
Adds/Appends a JavaScript message handler `callback` (`JavaScriptHandlerCallback`) that listen to post messages sent from JavaScript by the handler with name `handlerName`.
|
||||||
|
Returns the position `index` of the handler that can be used to remove it with the `removeJavaScriptHandler()` method.
|
||||||
|
|
||||||
|
The Android implementation uses [addJavascriptInterface](https://developer.android.com/reference/android/webkit/WebView#addJavascriptInterface(java.lang.Object,%20java.lang.String)).
|
||||||
|
The iOS implementation uses [addScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1537172-addscriptmessagehandler?language=objc)
|
||||||
|
|
||||||
|
The JavaScript function that can be used to call the handler is `window.flutter_inappbrowser.callHandler(handlerName <String>, ...args);`, where `args` are [rest parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
|
||||||
|
The `args` will be stringified automatically using `JSON.stringify(args)` method and then they will be decoded on the Dart side.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
inAppBrowser.addJavaScriptHandler(String handlerName, JavaScriptHandlerCallback callback);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### bool InAppBrowser.removeJavaScriptHandler
|
||||||
|
|
||||||
|
Removes a JavaScript message handler previously added with the `addJavaScriptHandler()` method in the `handlerName` list by its position `index`.
|
||||||
|
Returns `true` if the callback is removed, otherwise `false`.
|
||||||
|
```dart
|
||||||
|
inAppBrowser.removeJavaScriptHandler(String handlerName, int index);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### `ChromeSafariBrowser` class
|
### `ChromeSafariBrowser` class
|
||||||
Create a Class that extends the `ChromeSafariBrowser` Class in order to override the callbacks to manage the browser events. Example:
|
Create a Class that extends the `ChromeSafariBrowser` Class in order to override the callbacks to manage the browser events. Example:
|
||||||
|
@ -5,6 +5,7 @@ public class InAppBrowserOptions extends Options {
|
|||||||
final static String LOG_TAG = "InAppBrowserOptions";
|
final static String LOG_TAG = "InAppBrowserOptions";
|
||||||
|
|
||||||
public boolean useShouldOverrideUrlLoading = false;
|
public boolean useShouldOverrideUrlLoading = false;
|
||||||
|
public boolean useOnLoadResource = false;
|
||||||
public boolean clearCache = false;
|
public boolean clearCache = false;
|
||||||
public String userAgent = "";
|
public String userAgent = "";
|
||||||
public boolean javaScriptEnabled = true;
|
public boolean javaScriptEnabled = true;
|
||||||
|
@ -31,6 +31,7 @@ public class InAppBrowserWebViewClient extends WebViewClient {
|
|||||||
protected static final String LOG_TAG = "IABWebViewClient";
|
protected static final String LOG_TAG = "IABWebViewClient";
|
||||||
private WebViewActivity activity;
|
private WebViewActivity activity;
|
||||||
Map<Integer, String> statusCodeMapping = new HashMap<Integer, String>();
|
Map<Integer, String> statusCodeMapping = new HashMap<Integer, String>();
|
||||||
|
long startPageTime = 0;
|
||||||
|
|
||||||
public InAppBrowserWebViewClient(WebViewActivity activity) {
|
public InAppBrowserWebViewClient(WebViewActivity activity) {
|
||||||
super();
|
super();
|
||||||
@ -164,6 +165,8 @@ public class InAppBrowserWebViewClient extends WebViewClient {
|
|||||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||||
super.onPageStarted(view, url, favicon);
|
super.onPageStarted(view, url, favicon);
|
||||||
|
|
||||||
|
startPageTime = System.currentTimeMillis();
|
||||||
|
|
||||||
activity.isLoading = true;
|
activity.isLoading = true;
|
||||||
|
|
||||||
if (activity.searchView != null && !url.equals(activity.searchView.getQuery().toString())) {
|
if (activity.searchView != null && !url.equals(activity.searchView.getQuery().toString())) {
|
||||||
@ -195,7 +198,8 @@ public class InAppBrowserWebViewClient extends WebViewClient {
|
|||||||
view.requestFocus();
|
view.requestFocus();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
view.evaluateJavascript(activity.jsConsoleLogScript, null);
|
view.evaluateJavascript(WebViewActivity.consoleLogJS, null);
|
||||||
|
view.evaluateJavascript(JavaScriptBridgeInterface.flutterInAppBroserJSClass, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
@ -264,14 +268,24 @@ public class InAppBrowserWebViewClient extends WebViewClient {
|
|||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||||
@Override
|
@Override
|
||||||
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request){
|
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request){
|
||||||
|
|
||||||
|
if (!request.getMethod().toLowerCase().equals("get") || !activity.options.useOnLoadResource) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final String url = request.getUrl().toString();
|
final String url = request.getUrl().toString();
|
||||||
|
|
||||||
Request mRequest = new Request.Builder().url(url).build();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long loadingTime = System.currentTimeMillis();
|
Request mRequest = new Request.Builder().url(url).build();
|
||||||
|
|
||||||
|
long startResourceTime = System.currentTimeMillis();
|
||||||
Response response = activity.httpClient.newCall(mRequest).execute();
|
Response response = activity.httpClient.newCall(mRequest).execute();
|
||||||
loadingTime = System.currentTimeMillis() - loadingTime;
|
long startTime = startResourceTime - startPageTime;
|
||||||
|
long duration = System.currentTimeMillis() - startResourceTime;
|
||||||
|
|
||||||
|
if (response.cacheResponse() != null) {
|
||||||
|
duration = 0;
|
||||||
|
}
|
||||||
|
|
||||||
String reasonPhrase = response.message();
|
String reasonPhrase = response.message();
|
||||||
if (reasonPhrase.equals("")) {
|
if (reasonPhrase.equals("")) {
|
||||||
@ -281,20 +295,20 @@ public class InAppBrowserWebViewClient extends WebViewClient {
|
|||||||
|
|
||||||
Map<String, String> headersResponse = new HashMap<String, String>();
|
Map<String, String> headersResponse = new HashMap<String, String>();
|
||||||
for (Map.Entry<String, List<String>> entry : response.headers().toMultimap().entrySet()) {
|
for (Map.Entry<String, List<String>> entry : response.headers().toMultimap().entrySet()) {
|
||||||
String value = "";
|
StringBuilder value = new StringBuilder();
|
||||||
for (String val: entry.getValue()) {
|
for (String val: entry.getValue()) {
|
||||||
value += (value == "") ? val : "; " + val;
|
value.append( (value.toString().isEmpty()) ? val : "; " + val );
|
||||||
}
|
}
|
||||||
headersResponse.put(entry.getKey().toLowerCase(), value);
|
headersResponse.put(entry.getKey().toLowerCase(), value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> headersRequest = new HashMap<String, String>();
|
Map<String, String> headersRequest = new HashMap<String, String>();
|
||||||
for (Map.Entry<String, List<String>> entry : mRequest.headers().toMultimap().entrySet()) {
|
for (Map.Entry<String, List<String>> entry : mRequest.headers().toMultimap().entrySet()) {
|
||||||
String value = "";
|
StringBuilder value = new StringBuilder();
|
||||||
for (String val: entry.getValue()) {
|
for (String val: entry.getValue()) {
|
||||||
value += (value == "") ? val : "; " + val;
|
value.append( (value.toString().isEmpty()) ? val : "; " + val );
|
||||||
}
|
}
|
||||||
headersRequest.put(entry.getKey().toLowerCase(), value);
|
headersRequest.put(entry.getKey().toLowerCase(), value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
@ -309,7 +323,8 @@ public class InAppBrowserWebViewClient extends WebViewClient {
|
|||||||
res.put("url", url);
|
res.put("url", url);
|
||||||
res.put("statusCode", response.code());
|
res.put("statusCode", response.code());
|
||||||
res.put("headers", headersResponse);
|
res.put("headers", headersResponse);
|
||||||
res.put("loadingTime", loadingTime);
|
res.put("startTime", startTime);
|
||||||
|
res.put("duration", duration);
|
||||||
res.put("data", dataBytes);
|
res.put("data", dataBytes);
|
||||||
|
|
||||||
req.put("url", url);
|
req.put("url", url);
|
||||||
@ -332,7 +347,11 @@ public class InAppBrowserWebViewClient extends WebViewClient {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Log.d(LOG_TAG, e.getMessage());
|
Log.d(LOG_TAG, e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.d(LOG_TAG, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.pichillilorenzo.flutter_inappbrowser;
|
||||||
|
|
||||||
|
import android.webkit.JavascriptInterface;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class JavaScriptBridgeInterface {
|
||||||
|
private static final String LOG_TAG = "JSBridgeInterface";
|
||||||
|
static final String name = "flutter_inappbrowser";
|
||||||
|
WebViewActivity activity;
|
||||||
|
|
||||||
|
static final String flutterInAppBroserJSClass = "window." + name + ".callHandler = function(handlerName, ...args) {\n" +
|
||||||
|
"window." + name + "._callHandler(handlerName, JSON.stringify(args));\n" +
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
JavaScriptBridgeInterface(WebViewActivity a) {
|
||||||
|
activity = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public void _callHandler(String handlerName, String args) {
|
||||||
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
obj.put("uuid", activity.uuid);
|
||||||
|
obj.put("handlerName", handlerName);
|
||||||
|
obj.put("args", args);
|
||||||
|
InAppBrowserFlutterPlugin.channel.invokeMethod("onCallJsHandler", obj);
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
import okhttp3.Cache;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
public class WebViewActivity extends AppCompatActivity {
|
public class WebViewActivity extends AppCompatActivity {
|
||||||
@ -40,7 +41,7 @@ public class WebViewActivity extends AppCompatActivity {
|
|||||||
public boolean isHidden = false;
|
public boolean isHidden = false;
|
||||||
OkHttpClient httpClient;
|
OkHttpClient httpClient;
|
||||||
|
|
||||||
static final String jsConsoleLogScript = "(function() {\n"+
|
static final String consoleLogJS = "(function() {\n"+
|
||||||
" var oldLogs = {\n"+
|
" var oldLogs = {\n"+
|
||||||
" 'log': console.log,\n"+
|
" 'log': console.log,\n"+
|
||||||
" 'debug': console.debug,\n"+
|
" 'debug': console.debug,\n"+
|
||||||
@ -89,14 +90,18 @@ public class WebViewActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
prepareWebView();
|
prepareWebView();
|
||||||
|
|
||||||
httpClient = new OkHttpClient();
|
int cacheSize = 10 * 1024 * 1024; // 10MB
|
||||||
|
httpClient = new OkHttpClient().newBuilder().cache(new Cache(getApplicationContext().getCacheDir(), cacheSize)).build();
|
||||||
|
|
||||||
webView.loadUrl(url, headers);
|
webView.loadUrl(url, headers);
|
||||||
|
//webView.loadData("<!DOCTYPE html> <html lang=\"en\"> <head> <meta charset=\"UTF-8\"> <title>Document</title> </head> <body> ciao <img src=\"https://via.placeholder.com/350x150\" /> <img src=\"./images/test\" alt=\"not found\" /></body> </html>", "text/html", "utf8");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareWebView() {
|
private void prepareWebView() {
|
||||||
|
|
||||||
|
webView.addJavascriptInterface(new JavaScriptBridgeInterface(this), JavaScriptBridgeInterface.name);
|
||||||
|
|
||||||
inAppBrowserWebChromeClient = new InAppBrowserWebChromeClient(this);
|
inAppBrowserWebChromeClient = new InAppBrowserWebChromeClient(this);
|
||||||
webView.setWebChromeClient(inAppBrowserWebChromeClient);
|
webView.setWebChromeClient(inAppBrowserWebChromeClient);
|
||||||
|
|
||||||
|
@ -13,8 +13,9 @@ class MyInAppBrowser extends InAppBrowser {
|
|||||||
Future onLoadStop(String url) async {
|
Future onLoadStop(String url) async {
|
||||||
print("\n\nStopped $url\n\n");
|
print("\n\nStopped $url\n\n");
|
||||||
|
|
||||||
// // javascript error
|
await this.injectScriptCode("window.flutter_inappbrowser.callHandler('handlerTest', 1, 5,'string', {'key': 5}, [4,6,8]);");
|
||||||
// await this.injectScriptCode("console.log({'testJavaScriptError': 5}));");
|
await this.injectScriptCode("window.flutter_inappbrowser.callHandler('handlerTest2', false, null, undefined);");
|
||||||
|
await this.injectScriptCode("setTimeout(function(){window.flutter_inappbrowser.callHandler('handlerTest', 'anotherString');}, 1000);");
|
||||||
//
|
//
|
||||||
// await this.injectScriptCode("console.log({'testObject': 5});");
|
// await this.injectScriptCode("console.log({'testObject': 5});");
|
||||||
// await this.injectScriptCode("console.warn('testWarn',null);");
|
// await this.injectScriptCode("console.warn('testWarn',null);");
|
||||||
@ -73,20 +74,21 @@ class MyInAppBrowser extends InAppBrowser {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
|
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
|
||||||
print(response.loadingTime.toString() + "ms " + response.url);
|
|
||||||
if (response.headers["content-length"] != null)
|
print("Started at: " + response.startTime.toString() + "ms ---> duration: " + response.duration.toString() + "ms " + response.url);
|
||||||
print(response.headers["content-length"] + " length");
|
// if (response.headers["content-length"] != null)
|
||||||
|
// print(response.headers["content-length"] + " length");
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onConsoleMessage(ConsoleMessage consoleMessage) {
|
void onConsoleMessage(ConsoleMessage consoleMessage) {
|
||||||
print("""
|
// print("""
|
||||||
console output:
|
// console output:
|
||||||
sourceURL: ${consoleMessage.sourceURL}
|
// sourceURL: ${consoleMessage.sourceURL}
|
||||||
lineNumber: ${consoleMessage.lineNumber}
|
// lineNumber: ${consoleMessage.lineNumber}
|
||||||
message: ${consoleMessage.message}
|
// message: ${consoleMessage.message}
|
||||||
messageLevel: ${consoleMessage.messageLevel}
|
// messageLevel: ${consoleMessage.messageLevel}
|
||||||
""");
|
// """);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,9 +114,12 @@ class MyChromeSafariBrowser extends ChromeSafariBrowser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adding a webview fallback
|
||||||
MyChromeSafariBrowser chromeSafariBrowser = new MyChromeSafariBrowser(inAppBrowserFallback);
|
MyChromeSafariBrowser chromeSafariBrowser = new MyChromeSafariBrowser(inAppBrowserFallback);
|
||||||
|
|
||||||
void main() => runApp(new MyApp());
|
void main() {
|
||||||
|
runApp(new MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
class MyApp extends StatefulWidget {
|
class MyApp extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -126,6 +131,15 @@ class _MyAppState extends State<MyApp> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
int indexTest = inAppBrowserFallback.addJavaScriptHandler("handlerTest", (arguments) async {
|
||||||
|
print("handlerTest arguments");
|
||||||
|
print(arguments);
|
||||||
|
});
|
||||||
|
int indexTest2 = inAppBrowserFallback.addJavaScriptHandler("test2", (arguments) async {
|
||||||
|
print("handlerTest2 arguments");
|
||||||
|
print(arguments);
|
||||||
|
inAppBrowserFallback.removeJavaScriptHandler("test", indexTest);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -139,6 +153,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
child: new RaisedButton(onPressed: () {
|
child: new RaisedButton(onPressed: () {
|
||||||
//chromeSafariBrowser.open("https://flutter.io/");
|
//chromeSafariBrowser.open("https://flutter.io/");
|
||||||
inAppBrowserFallback.open(url: "https://flutter.io/", options: {
|
inAppBrowserFallback.open(url: "https://flutter.io/", options: {
|
||||||
|
//"useOnLoadResource": true,
|
||||||
//"hidden": true,
|
//"hidden": true,
|
||||||
//"toolbarTopFixedTitle": "Fixed title",
|
//"toolbarTopFixedTitle": "Fixed title",
|
||||||
//"useShouldOverrideUrlLoading": true
|
//"useShouldOverrideUrlLoading": true
|
||||||
|
@ -11,6 +11,7 @@ import Foundation
|
|||||||
public class InAppBrowserOptions: Options {
|
public class InAppBrowserOptions: Options {
|
||||||
|
|
||||||
var useShouldOverrideUrlLoading = false
|
var useShouldOverrideUrlLoading = false
|
||||||
|
var useOnLoadResource = false
|
||||||
var clearCache = false
|
var clearCache = false
|
||||||
var userAgent = ""
|
var userAgent = ""
|
||||||
var javaScriptEnabled = true
|
var javaScriptEnabled = true
|
||||||
|
@ -15,7 +15,7 @@ typealias OlderClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, B
|
|||||||
typealias NewerClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void
|
typealias NewerClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void
|
||||||
|
|
||||||
// the message needs to be concatenated with '' in order to have the same behavior like on Android
|
// the message needs to be concatenated with '' in order to have the same behavior like on Android
|
||||||
let jsConsoleLog = """
|
let consoleLogJS = """
|
||||||
(function() {
|
(function() {
|
||||||
var oldLogs = {
|
var oldLogs = {
|
||||||
'consoleLog': console.log,
|
'consoleLog': console.log,
|
||||||
@ -44,53 +44,91 @@ let jsConsoleLog = """
|
|||||||
})();
|
})();
|
||||||
"""
|
"""
|
||||||
|
|
||||||
extension WKWebView{
|
let resourceObserverJS = """
|
||||||
|
(function() {
|
||||||
var keyboardDisplayRequiresUserAction: Bool? {
|
var observer = new PerformanceObserver(function(list) {
|
||||||
get {
|
list.getEntries().forEach(function(entry) {
|
||||||
return self.keyboardDisplayRequiresUserAction
|
window.webkit.messageHandlers['resourceLoaded'].postMessage(JSON.stringify(entry));
|
||||||
}
|
});
|
||||||
set {
|
});
|
||||||
self.setKeyboardRequiresUserInteraction(newValue ?? true)
|
observer.observe({entryTypes: ['resource', 'mark', 'measure']});
|
||||||
}
|
})();
|
||||||
}
|
"""
|
||||||
|
|
||||||
func setKeyboardRequiresUserInteraction( _ value: Bool) {
|
let JAVASCRIPT_BRIDGE_NAME = "flutter_inappbrowser"
|
||||||
|
|
||||||
guard
|
let javaScriptBridgeJS = """
|
||||||
let WKContentViewClass: AnyClass = NSClassFromString("WKContentView") else {
|
window.\(JAVASCRIPT_BRIDGE_NAME) = {};
|
||||||
print("Cannot find the WKContentView class")
|
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function(handlerName, ...args) {
|
||||||
return
|
window.webkit.messageHandlers['callHandler'].postMessage( {'handlerName': handlerName, 'args': JSON.stringify(args)} );
|
||||||
}
|
|
||||||
|
|
||||||
let olderSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:")
|
|
||||||
let newerSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
|
|
||||||
|
|
||||||
if let method = class_getInstanceMethod(WKContentViewClass, olderSelector) {
|
|
||||||
|
|
||||||
let originalImp: IMP = method_getImplementation(method)
|
|
||||||
let original: OlderClosureType = unsafeBitCast(originalImp, to: OlderClosureType.self)
|
|
||||||
let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3) in
|
|
||||||
original(me, olderSelector, arg0, !value, arg2, arg3)
|
|
||||||
}
|
|
||||||
let imp: IMP = imp_implementationWithBlock(block)
|
|
||||||
method_setImplementation(method, imp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let method = class_getInstanceMethod(WKContentViewClass, newerSelector) {
|
|
||||||
|
|
||||||
let originalImp: IMP = method_getImplementation(method)
|
|
||||||
let original: NewerClosureType = unsafeBitCast(originalImp, to: NewerClosureType.self)
|
|
||||||
let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
|
|
||||||
original(me, newerSelector, arg0, !value, arg2, arg3, arg4)
|
|
||||||
}
|
|
||||||
let imp: IMP = imp_implementationWithBlock(block)
|
|
||||||
method_setImplementation(method, imp)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
func currentTimeInMilliSeconds() -> Int {
|
||||||
|
let currentDate = Date()
|
||||||
|
let since1970 = currentDate.timeIntervalSince1970
|
||||||
|
return Int(since1970 * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToDictionary(text: String) -> [String: Any]? {
|
||||||
|
if let data = text.data(using: .utf8) {
|
||||||
|
do {
|
||||||
|
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
|
||||||
|
} catch {
|
||||||
|
print(error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//extension WKWebView{
|
||||||
|
//
|
||||||
|
// var keyboardDisplayRequiresUserAction: Bool? {
|
||||||
|
// get {
|
||||||
|
// return self.keyboardDisplayRequiresUserAction
|
||||||
|
// }
|
||||||
|
// set {
|
||||||
|
// self.setKeyboardRequiresUserInteraction(newValue ?? true)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func setKeyboardRequiresUserInteraction( _ value: Bool) {
|
||||||
|
//
|
||||||
|
// guard
|
||||||
|
// let WKContentViewClass: AnyClass = NSClassFromString("WKContentView") else {
|
||||||
|
// print("Cannot find the WKContentView class")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// let olderSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:")
|
||||||
|
// let newerSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
|
||||||
|
//
|
||||||
|
// if let method = class_getInstanceMethod(WKContentViewClass, olderSelector) {
|
||||||
|
//
|
||||||
|
// let originalImp: IMP = method_getImplementation(method)
|
||||||
|
// let original: OlderClosureType = unsafeBitCast(originalImp, to: OlderClosureType.self)
|
||||||
|
// let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3) in
|
||||||
|
// original(me, olderSelector, arg0, !value, arg2, arg3)
|
||||||
|
// }
|
||||||
|
// let imp: IMP = imp_implementationWithBlock(block)
|
||||||
|
// method_setImplementation(method, imp)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if let method = class_getInstanceMethod(WKContentViewClass, newerSelector) {
|
||||||
|
//
|
||||||
|
// let originalImp: IMP = method_getImplementation(method)
|
||||||
|
// let original: NewerClosureType = unsafeBitCast(originalImp, to: NewerClosureType.self)
|
||||||
|
// let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
|
||||||
|
// original(me, newerSelector, arg0, !value, arg2, arg3, arg4)
|
||||||
|
// }
|
||||||
|
// let imp: IMP = imp_implementationWithBlock(block)
|
||||||
|
// method_setImplementation(method, imp)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
class WKWebView_IBWrapper: WKWebView {
|
class WKWebView_IBWrapper: WKWebView {
|
||||||
required convenience init?(coder: NSCoder) {
|
required convenience init?(coder: NSCoder) {
|
||||||
@ -100,7 +138,7 @@ class WKWebView_IBWrapper: WKWebView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, UITextFieldDelegate, WKScriptMessageHandler, MyURLProtocolDelegate {
|
class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, UITextFieldDelegate, WKScriptMessageHandler {
|
||||||
@IBOutlet var webView: WKWebView_IBWrapper!
|
@IBOutlet var webView: WKWebView_IBWrapper!
|
||||||
@IBOutlet var closeButton: UIButton!
|
@IBOutlet var closeButton: UIButton!
|
||||||
@IBOutlet var reloadButton: UIBarButtonItem!
|
@IBOutlet var reloadButton: UIBarButtonItem!
|
||||||
@ -119,6 +157,8 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
var initHeaders: [String: String]?
|
var initHeaders: [String: String]?
|
||||||
var isHidden = false
|
var isHidden = false
|
||||||
var uuid: String = ""
|
var uuid: String = ""
|
||||||
|
var WKNavigationMap: [String: [String: Any]] = [:]
|
||||||
|
var startPageTime = 0
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
required init(coder aDecoder: NSCoder) {
|
||||||
super.init(coder: aDecoder)!
|
super.init(coder: aDecoder)!
|
||||||
@ -132,7 +172,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
MyURLProtocol.wkWebViewDelegateMap[uuid] = self
|
//MyURLProtocol.wkWebViewDelegateMap[uuid] = self
|
||||||
|
|
||||||
webView.uiDelegate = self
|
webView.uiDelegate = self
|
||||||
webView.navigationDelegate = self
|
webView.navigationDelegate = self
|
||||||
@ -162,6 +202,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
spinner.stopAnimating()
|
spinner.stopAnimating()
|
||||||
|
|
||||||
loadUrl(url: self.currentURL!, headers: self.initHeaders)
|
loadUrl(url: self.currentURL!, headers: self.initHeaders)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent crashes on closing windows
|
// Prevent crashes on closing windows
|
||||||
@ -249,14 +290,25 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
let jscriptWebkitTouchCallout = WKUserScript(source: "document.body.style.webkitTouchCallout='none';", injectionTime: .atDocumentEnd, forMainFrameOnly: true)
|
let jscriptWebkitTouchCallout = WKUserScript(source: "document.body.style.webkitTouchCallout='none';", injectionTime: .atDocumentEnd, forMainFrameOnly: true)
|
||||||
self.webView.configuration.userContentController.addUserScript(jscriptWebkitTouchCallout)
|
self.webView.configuration.userContentController.addUserScript(jscriptWebkitTouchCallout)
|
||||||
|
|
||||||
let jsConsoleLogScript = WKUserScript(source: jsConsoleLog, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
|
||||||
self.webView.configuration.userContentController.addUserScript(jsConsoleLogScript)
|
let consoleLogJSScript = WKUserScript(source: consoleLogJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||||
|
self.webView.configuration.userContentController.addUserScript(consoleLogJSScript)
|
||||||
self.webView.configuration.userContentController.add(self, name: "consoleLog")
|
self.webView.configuration.userContentController.add(self, name: "consoleLog")
|
||||||
self.webView.configuration.userContentController.add(self, name: "consoleDebug")
|
self.webView.configuration.userContentController.add(self, name: "consoleDebug")
|
||||||
self.webView.configuration.userContentController.add(self, name: "consoleError")
|
self.webView.configuration.userContentController.add(self, name: "consoleError")
|
||||||
self.webView.configuration.userContentController.add(self, name: "consoleInfo")
|
self.webView.configuration.userContentController.add(self, name: "consoleInfo")
|
||||||
self.webView.configuration.userContentController.add(self, name: "consoleWarn")
|
self.webView.configuration.userContentController.add(self, name: "consoleWarn")
|
||||||
|
|
||||||
|
let javaScriptBridgeJSScript = WKUserScript(source: javaScriptBridgeJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||||
|
self.webView.configuration.userContentController.addUserScript(javaScriptBridgeJSScript)
|
||||||
|
self.webView.configuration.userContentController.add(self, name: "callHandler")
|
||||||
|
|
||||||
|
if (browserOptions?.useOnLoadResource)! {
|
||||||
|
let resourceObserverJSScript = WKUserScript(source: resourceObserverJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||||
|
self.webView.configuration.userContentController.addUserScript(resourceObserverJSScript)
|
||||||
|
self.webView.configuration.userContentController.add(self, name: "resourceLoaded")
|
||||||
|
}
|
||||||
|
|
||||||
if #available(iOS 10.0, *) {
|
if #available(iOS 10.0, *) {
|
||||||
if (browserOptions?.mediaPlaybackRequiresUserGesture)! {
|
if (browserOptions?.mediaPlaybackRequiresUserGesture)! {
|
||||||
self.webView.configuration.mediaTypesRequiringUserActionForPlayback = .all
|
self.webView.configuration.mediaTypesRequiringUserActionForPlayback = .all
|
||||||
@ -301,21 +353,6 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set uuid in the User-Agent in order to know which webview is making internal requests and
|
|
||||||
// to send the onLoadResource event to the correct webview
|
|
||||||
if #available(iOS 9.0, *) {
|
|
||||||
if (self.webView.customUserAgent != nil) {
|
|
||||||
self.webView.customUserAgent = self.webView.customUserAgent! + " WKWebView/" + self.uuid
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.webView.evaluateJavaScript("navigator.userAgent") { [weak webView] (result, error) in
|
|
||||||
if let webView = self.webView, let userAgent = result as? String {
|
|
||||||
webView.customUserAgent = userAgent + " WKWebView/" + self.uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (browserOptions?.clearCache)! {
|
if (browserOptions?.clearCache)! {
|
||||||
clearCache()
|
clearCache()
|
||||||
}
|
}
|
||||||
@ -480,18 +517,27 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
decidePolicyFor navigationAction: WKNavigationAction,
|
decidePolicyFor navigationAction: WKNavigationAction,
|
||||||
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||||
|
|
||||||
let url = navigationAction.request.url
|
if let url = navigationAction.request.url {
|
||||||
|
|
||||||
if (url != nil && navigationAction.navigationType == .linkActivated && (browserOptions?.useShouldOverrideUrlLoading)!) {
|
if url.absoluteString != self.currentURL?.absoluteString && (browserOptions?.useOnLoadResource)! {
|
||||||
navigationDelegate?.shouldOverrideUrlLoading(uuid: self.uuid, webView: webView, url: url!)
|
WKNavigationMap[url.absoluteString] = [
|
||||||
decisionHandler(.cancel)
|
"startTime": currentTimeInMilliSeconds(),
|
||||||
return
|
"request": navigationAction.request
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
if navigationAction.navigationType == .linkActivated && (browserOptions?.useShouldOverrideUrlLoading)! {
|
||||||
|
navigationDelegate?.shouldOverrideUrlLoading(uuid: self.uuid, webView: webView, url: url)
|
||||||
|
decisionHandler(.cancel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if navigationAction.navigationType == .linkActivated || navigationAction.navigationType == .backForward {
|
||||||
|
currentURL = url
|
||||||
|
updateUrlTextField(url: (url.absoluteString))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if url != nil && (navigationAction.navigationType == .linkActivated || navigationAction.navigationType == .backForward) {
|
|
||||||
currentURL = url
|
|
||||||
updateUrlTextField(url: (url?.absoluteString)!)
|
|
||||||
}
|
|
||||||
|
|
||||||
decisionHandler(.allow)
|
decisionHandler(.allow)
|
||||||
}
|
}
|
||||||
@ -499,9 +545,18 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
func webView(_ webView: WKWebView,
|
func webView(_ webView: WKWebView,
|
||||||
decidePolicyFor navigationResponse: WKNavigationResponse,
|
decidePolicyFor navigationResponse: WKNavigationResponse,
|
||||||
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
|
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
|
||||||
//dump((navigationResponse.response as! HTTPURLResponse))
|
|
||||||
//print(navigationResponse.response.mimeType)
|
if (browserOptions?.useOnLoadResource)! {
|
||||||
//print(navigationResponse.response.url)
|
if let url = navigationResponse.response.url {
|
||||||
|
if WKNavigationMap[url.absoluteString] != nil {
|
||||||
|
let startResourceTime = (WKNavigationMap[url.absoluteString]!["startTime"] as! Int)
|
||||||
|
let startTime = startResourceTime - startPageTime;
|
||||||
|
let duration = currentTimeInMilliSeconds() - startResourceTime;
|
||||||
|
self.didReceiveResourceResponse(navigationResponse.response, fromRequest: WKNavigationMap[url.absoluteString]!["request"] as? URLRequest, withData: Data(), startTime: startTime, duration: duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
decisionHandler(.allow)
|
decisionHandler(.allow)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,6 +595,9 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||||
|
|
||||||
|
self.startPageTime = currentTimeInMilliSeconds()
|
||||||
|
|
||||||
// loading url, start spinner, update back/forward
|
// loading url, start spinner, update back/forward
|
||||||
backButton.isEnabled = webView.canGoBack
|
backButton.isEnabled = webView.canGoBack
|
||||||
forwardButton.isEnabled = webView.canGoForward
|
forwardButton.isEnabled = webView.canGoForward
|
||||||
@ -552,8 +610,8 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||||
|
self.WKNavigationMap = [:]
|
||||||
// update url, stop spinner, update back/forward
|
// update url, stop spinner, update back/forward
|
||||||
|
|
||||||
currentURL = webView.url
|
currentURL = webView.url
|
||||||
updateUrlTextField(url: (currentURL?.absoluteString)!)
|
updateUrlTextField(url: (currentURL?.absoluteString)!)
|
||||||
backButton.isEnabled = webView.canGoBack
|
backButton.isEnabled = webView.canGoBack
|
||||||
@ -576,8 +634,8 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
navigationDelegate?.onLoadError(uuid: self.uuid, webView: webView, error: error)
|
navigationDelegate?.onLoadError(uuid: self.uuid, webView: webView, error: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func didReceiveResponse(_ response: URLResponse, fromRequest request: URLRequest?, withData data: Data, loadingTime time: Int) {
|
func didReceiveResourceResponse(_ response: URLResponse, fromRequest request: URLRequest?, withData data: Data, startTime: Int, duration: Int) {
|
||||||
navigationDelegate?.onLoadResource(uuid: self.uuid, webView: webView, response: response, fromRequest: request, withData: data, loadingTime: time)
|
navigationDelegate?.onLoadResource(uuid: self.uuid, webView: webView, response: response, fromRequest: request, withData: data, startTime: startTime, duration: duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||||
@ -607,5 +665,37 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
|
|||||||
}
|
}
|
||||||
navigationDelegate?.onConsoleMessage(uuid: self.uuid, sourceURL: "", lineNumber: 1, message: message.body as! String, messageLevel: messageLevel)
|
navigationDelegate?.onConsoleMessage(uuid: self.uuid, sourceURL: "", lineNumber: 1, message: message.body as! String, messageLevel: messageLevel)
|
||||||
}
|
}
|
||||||
|
else if message.name == "resourceLoaded" {
|
||||||
|
if let resource = convertToDictionary(text: message.body as! String) {
|
||||||
|
let url = URL(string: resource["name"] as! String)!
|
||||||
|
if !UIApplication.shared.canOpenURL(url) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let startTime = Int(resource["startTime"] as! Double)
|
||||||
|
let duration = Int(resource["duration"] as! Double)
|
||||||
|
var urlRequest = URLRequest(url: url)
|
||||||
|
urlRequest.allHTTPHeaderFields = [:]
|
||||||
|
let config = URLSessionConfiguration.default
|
||||||
|
let session = URLSession(configuration: config)
|
||||||
|
let task = session.dataTask(with: urlRequest) { (data, response, error) in
|
||||||
|
if error != nil {
|
||||||
|
print(error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var withData = data
|
||||||
|
if withData == nil {
|
||||||
|
withData = Data()
|
||||||
|
}
|
||||||
|
self.didReceiveResourceResponse(response!, fromRequest: urlRequest, withData: withData!, startTime: startTime, duration: duration)
|
||||||
|
}
|
||||||
|
task.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if message.name == "callHandler" {
|
||||||
|
let body = message.body as! [String: Any]
|
||||||
|
let handlerName = body["handlerName"] as! String
|
||||||
|
let args = body["args"] as! String
|
||||||
|
self.navigationDelegate?.onCallJsHandler(uuid: self.uuid, webView: webView, handlerName: handlerName, args: args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,149 +0,0 @@
|
|||||||
//
|
|
||||||
// MyURLProtocol.swift
|
|
||||||
// Pods
|
|
||||||
//
|
|
||||||
// Created by Lorenzo on 12/10/18.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import WebKit
|
|
||||||
|
|
||||||
|
|
||||||
func currentTimeInMilliSeconds() -> Int {
|
|
||||||
let currentDate = Date()
|
|
||||||
let since1970 = currentDate.timeIntervalSince1970
|
|
||||||
return Int(since1970 * 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyURLProtocol: URLProtocol {
|
|
||||||
|
|
||||||
// struct Constants {
|
|
||||||
// static let RequestHandledKey = "URLProtocolRequestHandled"
|
|
||||||
// }
|
|
||||||
|
|
||||||
var wkWebViewUuid: String?
|
|
||||||
var session: URLSession?
|
|
||||||
var sessionTask: URLSessionDataTask?
|
|
||||||
var response: URLResponse?
|
|
||||||
var data: Data?
|
|
||||||
static var wkWebViewDelegateMap: [String: MyURLProtocolDelegate] = [:]
|
|
||||||
var loadingTime: Int = 0
|
|
||||||
|
|
||||||
override init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
|
|
||||||
super.init(request: request, cachedResponse: cachedResponse, client: client)
|
|
||||||
|
|
||||||
self.wkWebViewUuid = MyURLProtocol.getUuid(request)
|
|
||||||
|
|
||||||
if session == nil && self.wkWebViewUuid != nil {
|
|
||||||
session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override class func canInit(with request: URLRequest) -> Bool {
|
|
||||||
|
|
||||||
if getUuid(request) == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// if MyURLProtocol.property(forKey: Constants.RequestHandledKey, in: request) != nil {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
|
|
||||||
override func startLoading() {
|
|
||||||
let newRequest = ((request as NSURLRequest).mutableCopy() as? NSMutableURLRequest)!
|
|
||||||
loadingTime = currentTimeInMilliSeconds()
|
|
||||||
//MyURLProtocol.setProperty(true, forKey: Constants.RequestHandledKey, in: newRequest)
|
|
||||||
sessionTask = session?.dataTask(with: newRequest as URLRequest)
|
|
||||||
sessionTask?.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func stopLoading() {
|
|
||||||
if let uuid = self.wkWebViewUuid {
|
|
||||||
if MyURLProtocol.wkWebViewDelegateMap[uuid] != nil && self.response != nil {
|
|
||||||
loadingTime = currentTimeInMilliSeconds() - loadingTime
|
|
||||||
if self.data == nil {
|
|
||||||
self.data = Data()
|
|
||||||
}
|
|
||||||
MyURLProtocol.wkWebViewDelegateMap[uuid]!.didReceiveResponse(self.response!, fromRequest: request, withData: self.data!, loadingTime: loadingTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionTask?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
class func getUuid(_ request: URLRequest?) -> String? {
|
|
||||||
let userAgent: String? = request?.allHTTPHeaderFields?["User-Agent"]
|
|
||||||
var uuid: String? = nil
|
|
||||||
if userAgent != nil {
|
|
||||||
if userAgent!.contains("WKWebView/") {
|
|
||||||
let userAgentSplitted = userAgent!.split(separator: " ")
|
|
||||||
uuid = String(userAgentSplitted[userAgentSplitted.count-1]).replacingOccurrences(of: "WKWebView/", with: "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension MyURLProtocol: URLSessionDataDelegate {
|
|
||||||
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
|
||||||
if self.data == nil {
|
|
||||||
self.data = data
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.data!.append(data)
|
|
||||||
}
|
|
||||||
client?.urlProtocol(self, didLoad: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
|
|
||||||
let policy = URLCache.StoragePolicy(rawValue: request.cachePolicy.rawValue) ?? .notAllowed
|
|
||||||
self.response = response
|
|
||||||
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: policy)
|
|
||||||
completionHandler(.allow)
|
|
||||||
}
|
|
||||||
|
|
||||||
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
|
||||||
if let error = error {
|
|
||||||
client?.urlProtocol(self, didFailWithError: error)
|
|
||||||
} else {
|
|
||||||
client?.urlProtocolDidFinishLoading(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {
|
|
||||||
client?.urlProtocol(self, wasRedirectedTo: request, redirectResponse: response)
|
|
||||||
completionHandler(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
|
|
||||||
guard let error = error else { return }
|
|
||||||
client?.urlProtocol(self, didFailWithError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
|
||||||
let protectionSpace = challenge.protectionSpace
|
|
||||||
let sender = challenge.sender
|
|
||||||
|
|
||||||
if protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
|
|
||||||
if let serverTrust = protectionSpace.serverTrust {
|
|
||||||
let credential = URLCredential(trust: serverTrust)
|
|
||||||
sender?.use(credential, for: challenge)
|
|
||||||
completionHandler(.useCredential, credential)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
|
|
||||||
client?.urlProtocolDidFinishLoading(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protocol MyURLProtocolDelegate {
|
|
||||||
func didReceiveResponse(_ response: URLResponse, fromRequest request: URLRequest?, withData data: Data, loadingTime time: Int)
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
//
|
|
||||||
// NSURLProtocol+WKWebVIew.h
|
|
||||||
// Pods
|
|
||||||
//
|
|
||||||
// Created by Lorenzo on 11/10/18.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NSURLProtocol_WKWebVIew_h
|
|
||||||
#define NSURLProtocol_WKWebVIew_h
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* NSURLProtocol_WKWebVIew_h */
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
@interface NSURLProtocol (WKWebVIew)
|
|
||||||
|
|
||||||
+ (void)wk_registerScheme:(NSString*)scheme;
|
|
||||||
|
|
||||||
+ (void)wk_unregisterScheme:(NSString*)scheme;
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
|
@ -1,54 +0,0 @@
|
|||||||
//
|
|
||||||
// NSURLProtocol+WKWebVIew.m
|
|
||||||
// Pods
|
|
||||||
//
|
|
||||||
// Created by Lorenzo on 11/10/18.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import "NSURLProtocol+WKWebVIew.h"
|
|
||||||
#import <WebKit/WebKit.h>
|
|
||||||
//FOUNDATION_STATIC_INLINE 属于属于runtime范畴,你的.m文件需要频繁调用一个函数,可以用static inline来声明。从SDWebImage从get到的。
|
|
||||||
FOUNDATION_STATIC_INLINE Class ContextControllerClass() {
|
|
||||||
static Class cls;
|
|
||||||
if (!cls) {
|
|
||||||
cls = [[[WKWebView new] valueForKey:@"browsingContextController"] class];
|
|
||||||
}
|
|
||||||
return cls;
|
|
||||||
}
|
|
||||||
|
|
||||||
FOUNDATION_STATIC_INLINE SEL RegisterSchemeSelector() {
|
|
||||||
return NSSelectorFromString(@"registerSchemeForCustomProtocol:");
|
|
||||||
}
|
|
||||||
|
|
||||||
FOUNDATION_STATIC_INLINE SEL UnregisterSchemeSelector() {
|
|
||||||
return NSSelectorFromString(@"unregisterSchemeForCustomProtocol:");
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation NSURLProtocol (WebKitSupport)
|
|
||||||
|
|
||||||
+ (void)wk_registerScheme:(NSString *)scheme {
|
|
||||||
Class cls = ContextControllerClass();
|
|
||||||
SEL sel = RegisterSchemeSelector();
|
|
||||||
if ([(id)cls respondsToSelector:sel]) {
|
|
||||||
// 放弃编辑器警告
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
||||||
[(id)cls performSelector:sel withObject:scheme];
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)wk_unregisterScheme:(NSString *)scheme {
|
|
||||||
Class cls = ContextControllerClass();
|
|
||||||
SEL sel = UnregisterSchemeSelector();
|
|
||||||
if ([(id)cls respondsToSelector:sel]) {
|
|
||||||
// 放弃编辑器警告
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
||||||
[(id)cls performSelector:sel withObject:scheme];
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -22,6 +22,19 @@ import Foundation
|
|||||||
import AVFoundation
|
import AVFoundation
|
||||||
import SafariServices
|
import SafariServices
|
||||||
|
|
||||||
|
//class CustomURLCache: URLCache {
|
||||||
|
// override func cachedResponse(for request: URLRequest) -> CachedURLResponse? {
|
||||||
|
// dump(request.url)
|
||||||
|
// return super.cachedResponse(for: request)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override func getCachedResponse(for dataTask: URLSessionDataTask,
|
||||||
|
// completionHandler: @escaping (CachedURLResponse?) -> Void) {
|
||||||
|
// dump(dataTask.response)
|
||||||
|
// super.getCachedResponse(for: dataTask, completionHandler: completionHandler)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
let WEBVIEW_STORYBOARD = "WebView"
|
let WEBVIEW_STORYBOARD = "WebView"
|
||||||
let WEBVIEW_STORYBOARD_CONTROLLER_ID = "viewController"
|
let WEBVIEW_STORYBOARD_CONTROLLER_ID = "viewController"
|
||||||
|
|
||||||
@ -46,9 +59,12 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||||
URLProtocol.wk_registerScheme("http")
|
// URLProtocol.wk_registerScheme("http")
|
||||||
URLProtocol.wk_registerScheme("https")
|
// URLProtocol.wk_registerScheme("https")
|
||||||
URLProtocol.registerClass(MyURLProtocol.self)
|
// URLProtocol.registerClass(MyURLProtocol.self)
|
||||||
|
|
||||||
|
//URLCache.shared = CustomURLCache()
|
||||||
|
|
||||||
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappbrowser", binaryMessenger: registrar.messenger())
|
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappbrowser", binaryMessenger: registrar.messenger())
|
||||||
let instance = SwiftFlutterPlugin(with: registrar)
|
let instance = SwiftFlutterPlugin(with: registrar)
|
||||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||||
@ -423,7 +439,8 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
|
|||||||
|
|
||||||
if error != nil {
|
if error != nil {
|
||||||
let userInfo = (error! as NSError).userInfo
|
let userInfo = (error! as NSError).userInfo
|
||||||
self.onConsoleMessage(uuid: uuid, sourceURL: (userInfo["WKJavaScriptExceptionSourceURL"] as! URL).absoluteString, lineNumber: userInfo["WKJavaScriptExceptionLineNumber"] as! Int, message: userInfo["WKJavaScriptExceptionMessage"] as! String, messageLevel: "ERROR")
|
dump(userInfo)
|
||||||
|
self.onConsoleMessage(uuid: uuid, sourceURL: (userInfo["WKJavaScriptExceptionSourceURL"] as? URL)?.absoluteString ?? "", lineNumber: userInfo["WKJavaScriptExceptionLineNumber"] as! Int, message: userInfo["WKJavaScriptExceptionMessage"] as! String, messageLevel: "ERROR")
|
||||||
}
|
}
|
||||||
|
|
||||||
if value == nil {
|
if value == nil {
|
||||||
@ -492,7 +509,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func onLoadResource(uuid: String, webView: WKWebView, response: URLResponse, fromRequest request: URLRequest?, withData data: Data, loadingTime time: Int) {
|
func onLoadResource(uuid: String, webView: WKWebView, response: URLResponse, fromRequest request: URLRequest?, withData data: Data, startTime: Int, duration: Int) {
|
||||||
if self.webViewControllers[uuid] != nil {
|
if self.webViewControllers[uuid] != nil {
|
||||||
var headersResponse = (response as! HTTPURLResponse).allHeaderFields as! [String: String]
|
var headersResponse = (response as! HTTPURLResponse).allHeaderFields as! [String: String]
|
||||||
headersResponse.lowercaseKeys()
|
headersResponse.lowercaseKeys()
|
||||||
@ -506,7 +523,8 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
|
|||||||
"url": response.url!.absoluteString,
|
"url": response.url!.absoluteString,
|
||||||
"statusCode": (response as! HTTPURLResponse).statusCode,
|
"statusCode": (response as! HTTPURLResponse).statusCode,
|
||||||
"headers": headersResponse,
|
"headers": headersResponse,
|
||||||
"loadingTime": time,
|
"startTime": startTime,
|
||||||
|
"duration": duration,
|
||||||
"data": data
|
"data": data
|
||||||
],
|
],
|
||||||
"request": [
|
"request": [
|
||||||
@ -551,6 +569,10 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
|
|||||||
channel.invokeMethod("onChromeSafariBrowserClosed", arguments: ["uuid": uuid])
|
channel.invokeMethod("onChromeSafariBrowserClosed", arguments: ["uuid": uuid])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onCallJsHandler(uuid: String, webView: WKWebView, handlerName: String, args: String) {
|
||||||
|
channel.invokeMethod("onCallJsHandler", arguments: ["uuid": uuid, "handlerName": handlerName, "args": args])
|
||||||
|
}
|
||||||
|
|
||||||
func safariExit(uuid: String) {
|
func safariExit(uuid: String) {
|
||||||
if let safariViewController = self.safariViewControllers[uuid] {
|
if let safariViewController = self.safariViewControllers[uuid] {
|
||||||
if #available(iOS 9.0, *) {
|
if #available(iOS 9.0, *) {
|
||||||
|
@ -22,11 +22,13 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
typedef Future<dynamic> ListenerCallback(MethodCall call);
|
typedef Future<dynamic> ListenerCallback(MethodCall call);
|
||||||
|
typedef Future<void> JavaScriptHandlerCallback(List<dynamic> arguments);
|
||||||
|
|
||||||
var _uuidGenerator = new Uuid();
|
var _uuidGenerator = new Uuid();
|
||||||
|
|
||||||
@ -35,6 +37,8 @@ enum ConsoleMessageLevel {
|
|||||||
DEBUG, ERROR, LOG, TIP, WARNING
|
DEBUG, ERROR, LOG, TIP, WARNING
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Public class representing a resource request of the [InAppBrowser] WebView.
|
||||||
|
///It is used by the method [InAppBrowser.onLoadResource()].
|
||||||
class WebResourceRequest {
|
class WebResourceRequest {
|
||||||
|
|
||||||
String url;
|
String url;
|
||||||
@ -45,15 +49,18 @@ class WebResourceRequest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Public class representing a resource response of the [InAppBrowser] WebView.
|
||||||
|
///It is used by the method [InAppBrowser.onLoadResource()].
|
||||||
class WebResourceResponse {
|
class WebResourceResponse {
|
||||||
|
|
||||||
String url;
|
String url;
|
||||||
Map<String, String> headers;
|
Map<String, String> headers;
|
||||||
int statusCode;
|
int statusCode;
|
||||||
int loadingTime;
|
int startTime;
|
||||||
|
int duration;
|
||||||
Uint8List data;
|
Uint8List data;
|
||||||
|
|
||||||
WebResourceResponse(this.url, this.headers, this.statusCode, this.loadingTime, this.data);
|
WebResourceResponse(this.url, this.headers, this.statusCode, this.startTime, this.duration, this.data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +106,7 @@ class _ChannelManager {
|
|||||||
class InAppBrowser {
|
class InAppBrowser {
|
||||||
|
|
||||||
String uuid;
|
String uuid;
|
||||||
|
Map<String, List<JavaScriptHandlerCallback>> javaScriptHandlersMap = HashMap<String, List<JavaScriptHandlerCallback>>();
|
||||||
|
|
||||||
///
|
///
|
||||||
InAppBrowser () {
|
InAppBrowser () {
|
||||||
@ -139,7 +147,8 @@ class InAppBrowser {
|
|||||||
Map<dynamic, dynamic> headersResponse = rawResponse["headers"];
|
Map<dynamic, dynamic> headersResponse = rawResponse["headers"];
|
||||||
headersResponse = headersResponse.cast<String, String>();
|
headersResponse = headersResponse.cast<String, String>();
|
||||||
int statusCode = rawResponse["statusCode"];
|
int statusCode = rawResponse["statusCode"];
|
||||||
int loadingTime = rawResponse["loadingTime"];
|
int startTime = rawResponse["startTime"];
|
||||||
|
int duration = rawResponse["duration"];
|
||||||
Uint8List data = rawResponse["data"];
|
Uint8List data = rawResponse["data"];
|
||||||
|
|
||||||
String urlRequest = rawRequest["url"];
|
String urlRequest = rawRequest["url"];
|
||||||
@ -147,7 +156,7 @@ class InAppBrowser {
|
|||||||
headersRequest = headersResponse.cast<String, String>();
|
headersRequest = headersResponse.cast<String, String>();
|
||||||
String method = rawRequest["method"];
|
String method = rawRequest["method"];
|
||||||
|
|
||||||
var response = new WebResourceResponse(urlResponse, headersResponse, statusCode, loadingTime, data);
|
var response = new WebResourceResponse(urlResponse, headersResponse, statusCode, startTime, duration, data);
|
||||||
var request = new WebResourceRequest(urlRequest, headersRequest, method);
|
var request = new WebResourceRequest(urlRequest, headersRequest, method);
|
||||||
|
|
||||||
onLoadResource(response, request);
|
onLoadResource(response, request);
|
||||||
@ -165,13 +174,22 @@ class InAppBrowser {
|
|||||||
});
|
});
|
||||||
onConsoleMessage(ConsoleMessage(sourceURL, lineNumber, message, messageLevel));
|
onConsoleMessage(ConsoleMessage(sourceURL, lineNumber, message, messageLevel));
|
||||||
break;
|
break;
|
||||||
|
case "onCallJsHandler":
|
||||||
|
String handlerName = call.arguments["handlerName"];
|
||||||
|
List<dynamic> args = jsonDecode(call.arguments["args"]);
|
||||||
|
if (javaScriptHandlersMap.containsKey(handlerName)) {
|
||||||
|
for (var handler in javaScriptHandlersMap[handlerName]) {
|
||||||
|
handler(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return new Future.value("");
|
return new Future.value("");
|
||||||
}
|
}
|
||||||
|
|
||||||
///Opens an [url] in a new [InAppBrowser] instance or the system browser.
|
///Opens an [url] in a new [InAppBrowser] instance or the system browser.
|
||||||
///
|
///
|
||||||
///- [url]: The [url] to load. Call [encodeUriComponent()] on this if the [url] contains Unicode characters.
|
///- [url]: The [url] to load. Call [encodeUriComponent()] on this if the [url] contains Unicode characters. The default value is `about:blank`.
|
||||||
///
|
///
|
||||||
///- [headers]: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value.
|
///- [headers]: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value.
|
||||||
///
|
///
|
||||||
@ -185,6 +203,7 @@ class InAppBrowser {
|
|||||||
///
|
///
|
||||||
/// All platforms support:
|
/// All platforms support:
|
||||||
/// - __useShouldOverrideUrlLoading__: Set to `true` to be able to listen at the [shouldOverrideUrlLoading()] event. The default value is `false`.
|
/// - __useShouldOverrideUrlLoading__: Set to `true` to be able to listen at the [shouldOverrideUrlLoading()] event. The default value is `false`.
|
||||||
|
/// - __useOnLoadResource__: Set to `true` to be able to listen at the [onLoadResource()] event. The default value is `false`.
|
||||||
/// - __clearCache__: Set to `true` to have all the browser's cache cleared before the new window is opened. The default value is `false`.
|
/// - __clearCache__: Set to `true` to have all the browser's cache cleared before the new window is opened. The default value is `false`.
|
||||||
/// - __userAgent___: Set the custom WebView's user-agent.
|
/// - __userAgent___: Set the custom WebView's user-agent.
|
||||||
/// - __javaScriptEnabled__: Set to `true` to enable JavaScript. The default value is `true`.
|
/// - __javaScriptEnabled__: Set to `true` to enable JavaScript. The default value is `true`.
|
||||||
@ -342,6 +361,33 @@ class InAppBrowser {
|
|||||||
return await _ChannelManager.channel.invokeMethod('injectStyleFile', args);
|
return await _ChannelManager.channel.invokeMethod('injectStyleFile', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Adds/Appends a JavaScript message handler [callback] ([JavaScriptHandlerCallback]) that listen to post messages sent from JavaScript by the handler with name [handlerName].
|
||||||
|
///Returns the position `index` of the handler that can be used to remove it with the [removeJavaScriptHandler()] method.
|
||||||
|
///
|
||||||
|
///The Android implementation uses [addJavascriptInterface](https://developer.android.com/reference/android/webkit/WebView#addJavascriptInterface(java.lang.Object,%20java.lang.String)).
|
||||||
|
///The iOS implementation uses [addScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1537172-addscriptmessagehandler?language=objc)
|
||||||
|
///
|
||||||
|
///The JavaScript function that can be used to call the handler is `window.flutter_inappbrowser.callHandler(handlerName <String>, ...args);`, where `args` are [rest parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
|
||||||
|
///The `args` will be stringified automatically using `JSON.stringify(args)` method and then they will be decoded on the Dart side.
|
||||||
|
int addJavaScriptHandler(String handlerName, JavaScriptHandlerCallback callback) {
|
||||||
|
this.javaScriptHandlersMap.putIfAbsent(handlerName, () => List<JavaScriptHandlerCallback>());
|
||||||
|
this.javaScriptHandlersMap[handlerName].add(callback);
|
||||||
|
return this.javaScriptHandlersMap[handlerName].indexOf(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Removes a JavaScript message handler previously added with the [addJavaScriptHandler()] method in the [handlerName] list by its position [index].
|
||||||
|
///Returns `true` if the callback is removed, otherwise `false`.
|
||||||
|
bool removeJavaScriptHandler(String handlerName, int index) {
|
||||||
|
try {
|
||||||
|
this.javaScriptHandlersMap[handlerName].removeAt(index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
on RangeError catch(e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
///Event fires when the [InAppBrowser] starts to load an [url].
|
///Event fires when the [InAppBrowser] starts to load an [url].
|
||||||
void onLoadStart(String url) {
|
void onLoadStart(String url) {
|
||||||
|
|
||||||
@ -363,12 +409,14 @@ class InAppBrowser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
|
///Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
|
||||||
///In order to be able to listen this event, you need to set `useShouldOverrideUrlLoading` option to `true`.
|
///**NOTE**: In order to be able to listen this event, you need to set `useShouldOverrideUrlLoading` option to `true`.
|
||||||
void shouldOverrideUrlLoading(String url) {
|
void shouldOverrideUrlLoading(String url) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///Event fires when the [InAppBrowser] webview will load the resource specified by the given [WebResourceRequest].
|
///Event fires when the [InAppBrowser] webview loads a resource.
|
||||||
|
///**NOTE**: In order to be able to listen this event, you need to set `useOnLoadResource` option to `true`.
|
||||||
|
///**NOTE only for iOS**: In some cases, the [response.data] of a [response] with `text/html` encoding could be empty.
|
||||||
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
|
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user