Commit 7756aa86 authored by pichillilorenzo's avatar pichillilorenzo

v0.5.0

parent ee7910b0
...@@ -15,7 +15,36 @@ ...@@ -15,7 +15,36 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="9b41f7a2-a71e-4923-91fb-249d7815b3e7" name="Default" comment=""> <list default="true" id="9b41f7a2-a71e-4923-91fb-249d7815b3e7" name="Default" comment="">
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ios/Classes/InAppWebViewOptions.swift" 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$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/AndroidManifest.xml" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/AndroidManifest.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserOptions.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserOptions.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebChromeClient.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebViewClient.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/JavaScriptBridgeInterface.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/JavaScriptBridgeInterface.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Options.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Options.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/InAppBrowserActivity.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/ChromeCustomTabsActivity.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/ChromeCustomTabsOptions.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/CustomTabActivityHelper.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/CustomTabsHelper.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/KeepAliveService.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/ServiceConnection.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/ServiceConnectionCallback.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/res/layout/activity_web_view.xml" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/res/layout/activity_web_view.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/res/menu/menu_main.xml" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/res/menu/menu_main.xml" 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$/flutter_inappbrowser.iml" beforeDir="false" afterPath="$PROJECT_DIR$/flutter_inappbrowser.iml" 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/SwiftFlutterPlugin.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Storyboards/WebView.storyboard" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Storyboards/WebView.storyboard" 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/" />
...@@ -35,8 +64,8 @@ ...@@ -35,8 +64,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="129"> <state relative-caret-position="133">
<caret line="581" column="54" selection-start-line="581" selection-start-column="47" selection-end-line="581" selection-end-column="54" /> <caret line="500" column="57" selection-start-line="500" selection-start-column="57" selection-end-line="500" selection-end-column="57" />
<folding> <folding>
<element signature="e#814#831#0" expanded="true" /> <element signature="e#814#831#0" expanded="true" />
</folding> </folding>
...@@ -44,11 +73,14 @@ ...@@ -44,11 +73,14 @@
</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$/example/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="405"> <state relative-caret-position="721">
<caret line="27" column="15" selection-start-line="27" selection-start-column="15" selection-end-line="27" selection-end-column="15" /> <caret line="254" column="25" selection-start-line="254" selection-start-column="25" selection-end-line="254" selection-end-column="25" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state> </state>
</provider> </provider>
</entry> </entry>
...@@ -57,8 +89,22 @@ ...@@ -57,8 +89,22 @@
<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="382"> <first_editor relative-caret-position="120">
<caret line="509" column="29" selection-start-line="509" selection-start-column="29" selection-end-line="509" selection-end-column="29" /> <caret line="8" column="91" selection-start-line="8" selection-start-column="91" selection-end-line="8" selection-end-column="91" />
</first_editor>
<second_editor>
<markdownNavigatorState />
</second_editor>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="CHANGELOG.md" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="390">
<caret line="26" column="34" selection-start-line="26" selection-start-column="34" selection-end-line="26" selection-end-column="34" />
</first_editor> </first_editor>
<second_editor> <second_editor>
<markdownNavigatorState /> <markdownNavigatorState />
...@@ -67,6 +113,15 @@ ...@@ -67,6 +113,15 @@
</provider> </provider>
</entry> </entry>
</file> </file>
<file leaf-file-name="pubspec.yaml" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="45">
<caret line="3" column="52" lean-forward="true" selection-start-line="3" selection-start-column="52" selection-end-line="3" selection-end-column="52" />
</state>
</provider>
</entry>
</file>
</leaf> </leaf>
</component> </component>
<component name="FileTemplateManagerImpl"> <component name="FileTemplateManagerImpl">
...@@ -78,36 +133,36 @@ ...@@ -78,36 +133,36 @@
</component> </component>
<component name="FindInProjectRecents"> <component name="FindInProjectRecents">
<findStrings> <findStrings>
<find>Client</find> <find>isHi</find>
<find>LOAD_START_EVENT</find> <find>isHidde</find>
<find>loadstop</find> <find>onWebViewCreated</find>
<find>JSONO</find> <find>WebViewCreatedCallback</find>
<find>closeDialog</find> <find>MethodChannel</find>
<find>LOAD_STOP_EVENT</find> <find>WebViewController</find>
<find>com.pichillilorenzo.flutterwebview</find> <find>HashMap</find>
<find>flutter_webview</find> <find>String code</find>
<find>show</find> <find>flutter_inappwebview</find>
<find>SELF</find> <find>_closeServer</find>
<find>customscheme</find> <find>_startS</find>
<find>EXIT</find> <find>_throwIsAlreadyOpened</find>
<find>injectScriptCode</find> <find>isHidden</find>
<find>injectDeferredObject</find> <find>shouldOverrideUrlLoading</find>
<find>sourceFile</find> <find>toolbarTopFixedTitle</find>
<find>hideUrlBar</find> <find>useChromeSafariBrowser</find>
<find>InAppBrowser</find> <find>toolbat</find>
<find>presentationStyle</find> <find>onProgress</find>
<find>###</find> <find>goBack</find>
<find>inAppBrowserFallback</find> <find>_controller</find>
<find>target</find> <find>canGoBack</find>
<find>.assets</find> <find>getOp</find>
<find>_blank</find> <find>onLoadError</find>
<find>.html</find> <find>StatefulWidget</find>
<find>close(</find> <find>onLoadResource</find>
<find>isOpened</find> <find>useShouldOverrideUrlLoading</find>
<find>javascript</find> <find>):</find>
<find>header</find> <find>InAppLocalhostServer</find>
<find>localhost</find> <find>local</find>
<find>/main.dart</find> <find>openWithSystemBrowser</find>
</findStrings> </findStrings>
<replaceStrings> <replaceStrings>
<replace>activity.getPreferences(0)</replace> <replace>activity.getPreferences(0)</replace>
...@@ -147,20 +202,20 @@ ...@@ -147,20 +202,20 @@
<option value="$PROJECT_DIR$/example/html/index.html" /> <option value="$PROJECT_DIR$/example/html/index.html" />
<option value="$PROJECT_DIR$/example/assets/css/style.css" /> <option value="$PROJECT_DIR$/example/assets/css/style.css" />
<option value="$PROJECT_DIR$/example/assets/index.html" /> <option value="$PROJECT_DIR$/example/assets/index.html" />
<option value="$PROJECT_DIR$/pubspec.yaml" /> <option value="$PROJECT_DIR$/example/ios/Flutter/Generated.xcconfig" />
<option value="$PROJECT_DIR$/example/pubspec.yaml" />
<option value="$PROJECT_DIR$/CHANGELOG.md" /> <option value="$PROJECT_DIR$/CHANGELOG.md" />
<option value="$PROJECT_DIR$/example/lib/main.dart" /> <option value="$PROJECT_DIR$/example/lib/main.dart" />
<option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" /> <option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" />
<option value="$PROJECT_DIR$/example/ios/Flutter/Generated.xcconfig" /> <option value="$PROJECT_DIR$/pubspec.yaml" />
<option value="$PROJECT_DIR$/example/pubspec.yaml" />
<option value="$PROJECT_DIR$/README.md" /> <option value="$PROJECT_DIR$/README.md" />
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectFrameBounds"> <component name="ProjectFrameBounds">
<option name="x" value="116" /> <option name="x" value="2053" />
<option name="y" value="23" /> <option name="y" value="23" />
<option name="width" value="1695" /> <option name="width" value="1787" />
<option name="height" value="1057" /> <option name="height" value="1057" />
</component> </component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true" /> <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
...@@ -176,11 +231,6 @@ ...@@ -176,11 +231,6 @@
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" /> <item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" /> <item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
</path> </path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
</path>
<path> <path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" /> <item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" /> <item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
...@@ -202,8 +252,8 @@ ...@@ -202,8 +252,8 @@
<select /> <select />
</subPane> </subPane>
</pane> </pane>
<pane id="AndroidView" />
<pane id="PackagesPane" /> <pane id="PackagesPane" />
<pane id="AndroidView" />
</panes> </panes>
</component> </component>
<component name="PropertiesComponent"> <component name="PropertiesComponent">
...@@ -360,77 +410,43 @@ ...@@ -360,77 +410,43 @@
<servers /> <servers />
</component> </component>
<component name="ToolWindowManager"> <component name="ToolWindowManager">
<frame x="116" y="23" width="1695" height="1057" extended-state="0" /> <frame x="2053" y="23" width="1787" height="1057" extended-state="0" />
<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" />
<window_info id="Build Variants" order="2" side_tool="true" /> <window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Messages" order="12" weight="0.23692636" />
<window_info anchor="right" id="Palette&#9;" order="9" />
<window_info id="Image Layers" order="7" />
<window_info id="Build Variants" order="3" side_tool="true" />
<window_info anchor="right" id="Capture Analysis" order="4" />
<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.3276414" />
<window_info anchor="right" id="Flutter Outline" order="3" weight="0.32922077" /> <window_info anchor="bottom" id="Run" order="2" sideWeight="0.49644473" weight="0.3564568" />
<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.35005337" />
<window_info anchor="right" id="Flutter Outline" order="6" weight="0.32922077" />
<window_info anchor="bottom" id="Logcat" order="11" /> <window_info anchor="bottom" id="Logcat" order="11" />
<window_info id="Capture Tool" order="2" /> <window_info id="Captures" order="4" weight="0.32936507" />
<window_info id="Capture Tool" order="6" />
<window_info id="Designer" order="2" /> <window_info id="Designer" order="2" />
<window_info content_ui="combo" id="Project" order="0" sideWeight="0.49724367" visible="true" weight="0.14670487" />
<window_info id="Structure" order="1" sideWeight="0.50275636" side_tool="true" weight="0.1910871" /> <window_info id="Structure" order="1" sideWeight="0.50275636" side_tool="true" weight="0.1910871" />
<window_info anchor="right" id="Device File Explorer" order="3" side_tool="true" /> <window_info anchor="right" id="Device File Explorer" order="5" side_tool="true" />
<window_info anchor="right" id="Theme Preview" order="7" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.34364995" /> <window_info anchor="bottom" id="Debug" order="3" weight="0.34364995" />
<window_info anchor="bottom" id="TODO" order="6" /> <window_info id="Favorites" order="5" side_tool="true" />
<window_info anchor="bottom" id="Messages" order="12" weight="0.23692636" />
<window_info anchor="right" id="Palette&#9;" order="3" />
<window_info id="Image Layers" order="2" />
<window_info anchor="right" id="Capture Analysis" order="3" />
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.49644473" weight="0.36179295" />
<window_info anchor="bottom" id="Version Control" order="9" />
<window_info anchor="bottom" id="Terminal" order="10" sideWeight="0.49644473" visible="true" weight="0.25080043" />
<window_info id="Captures" order="2" weight="0.32936507" />
<window_info active="true" content_ui="combo" id="Project" order="0" sideWeight="0.49724367" visible="true" weight="0.25771326" />
<window_info anchor="bottom" id="Find" order="1" weight="0.3276414" />
<window_info anchor="right" id="Theme Preview" order="3" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="right" id="Flutter Inspector" order="3" weight="0.32938388" /> <window_info anchor="right" id="Flutter Inspector" order="3" weight="0.32938388" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" /> <window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="right" id="Commander" order="0" weight="0.4" /> <window_info anchor="right" id="Commander" order="0" weight="0.4" />
<window_info anchor="right" id="Assistant" order="4" visible="true" weight="0.32987013" /> <window_info anchor="right" id="Assistant" order="8" visible="true" weight="0.32987013" />
<window_info anchor="bottom" id="Message" order="0" /> <window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" /> <window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" /> <window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="bottom" id="Dependency Viewer" order="13" weight="0.32800853" /> <window_info anchor="bottom" id="Dependency Viewer" order="13" weight="0.32800853" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" /> <window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="bottom" id="Find" order="1" weight="0.32745314" />
</layout> </layout>
<layout-to-restore>
<window_info id="Designer" order="4" />
<window_info anchor="bottom" id="Dart Analysis" order="14" weight="0.3290735" />
<window_info id="Build Variants" order="2" side_tool="true" />
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.4973545" weight="0.32161874" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" id="Flutter Inspector" order="8" weight="0.32987013" />
<window_info id="Favorites" order="7" side_tool="true" />
<window_info id="Captures" order="6" weight="0.32936507" />
<window_info id="Capture Tool" order="3" />
<window_info anchor="right" id="Capture Analysis" order="6" />
<window_info anchor="bottom" id="Android Profiler" order="7" show_stripe_button="false" />
<window_info anchor="right" id="Commander" order="0" weight="0.4" />
<window_info anchor="bottom" id="Event Log" order="8" sideWeight="0.5026455" side_tool="true" weight="0.31735888" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="right" id="Device File Explorer" order="4" side_tool="true" />
<window_info anchor="right" id="Flutter Outline" order="3" weight="0.32936507" />
<window_info anchor="bottom" id="Logcat" order="11" />
<window_info anchor="bottom" id="Dependency Viewer" order="13" weight="0.32800853" />
<window_info anchor="bottom" id="Version Control" order="9" />
<window_info anchor="bottom" id="Terminal" order="10" sideWeight="0.4973545" visible="true" weight="0.27888888" />
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.18376623" />
<window_info anchor="bottom" id="Messages" order="12" weight="0.23777778" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info id="Image Layers" order="5" />
<window_info anchor="right" id="Palette&#9;" order="5" />
<window_info anchor="right" id="Theme Preview" order="7" />
<window_info id="Structure" order="1" weight="0.24969475" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="Find" order="1" weight="0.3290735" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
</layout-to-restore>
</component> </component>
<component name="UnknownFeatures"> <component name="UnknownFeatures">
<option featureType="com.intellij.fileTypeFactory" implementationName="*.css" /> <option featureType="com.intellij.fileTypeFactory" implementationName="*.css" />
...@@ -440,15 +456,10 @@ ...@@ -440,15 +456,10 @@
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">
<breakpoint-manager> <breakpoint-manager>
<option name="time" value="14" /> <option name="time" value="15" />
</breakpoint-manager> </breakpoint-manager>
</component> </component>
<component name="editorHistoryManager"> <component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/example/pubspec.lock">
<provider selected="true" editor-type-id="text-editor">
<state />
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/android/gradle.properties"> <entry file="file://$PROJECT_DIR$/example/android/gradle.properties">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state /> <state />
...@@ -608,40 +619,6 @@ ...@@ -608,40 +619,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="60">
<caret line="4" column="21" selection-start-line="4" selection-start-column="21" selection-end-line="4" selection-end-column="21" />
</first_editor>
<second_editor>
<markdownNavigatorState />
</second_editor>
</state>
</provider>
<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>
</entry>
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="30">
<caret line="2" column="14" selection-start-line="2" selection-start-column="14" selection-end-line="2" selection-end-column="14" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="495">
<caret line="33" column="11" selection-start-line="23" selection-start-column="30" selection-end-line="33" selection-end-column="11" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/flutter_inappbrowser.iml"> <entry file="file://$PROJECT_DIR$/flutter_inappbrowser.iml">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="330"> <state relative-caret-position="330">
...@@ -712,21 +689,65 @@ ...@@ -712,21 +689,65 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/README.md">
<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$/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="129"> <state relative-caret-position="133">
<caret line="581" column="54" selection-start-line="581" selection-start-column="47" selection-end-line="581" selection-end-column="54" /> <caret line="500" column="57" selection-start-line="500" selection-start-column="57" selection-end-line="500" selection-end-column="57" />
<folding> <folding>
<element signature="e#814#831#0" expanded="true" /> <element signature="e#814#831#0" expanded="true" />
</folding> </folding>
</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="721">
<caret line="254" column="25" selection-start-line="254" selection-start-column="25" selection-end-line="254" selection-end-column="25" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="45">
<caret line="3" column="52" lean-forward="true" selection-start-line="3" selection-start-column="52" selection-end-line="3" selection-end-column="52" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="390">
<caret line="26" column="34" selection-start-line="26" selection-start-column="34" selection-end-line="26" selection-end-column="34" />
</first_editor>
<second_editor>
<markdownNavigatorState />
</second_editor>
</state>
</provider>
<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>
</entry>
<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="382"> <first_editor relative-caret-position="120">
<caret line="509" column="29" selection-start-line="509" selection-start-column="29" selection-end-line="509" selection-end-column="29" /> <caret line="8" column="91" selection-start-line="8" selection-start-column="91" selection-end-line="8" selection-end-column="91" />
</first_editor> </first_editor>
<second_editor> <second_editor>
<markdownNavigatorState /> <markdownNavigatorState />
......
## 0.5.0
- added initial support for Inline WebViews using the `InAppWebView` widget
- added `InAppBrowser.openFile()` method
- added `InAppBrowser.onProgressChanged()` event
- moved `InAppBrowser` WebView related functions on the `InAppWebViewController` class
- added `InAppLocalhostServer` class
- added `InAppWebView.canGoBack()` and `InAppWebView.canGoForward()` methods
- removed `openWithSystemBrowser` and `isLocalFile` option. Now use the corresponding method
- code refactoring
## 0.4.1 ## 0.4.1
- added `InAppBrowser.takeScreenshot()` - added `InAppBrowser.takeScreenshot()`
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
[![Donate to this project using Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.me/LorenzoPichilli) [![Donate to this project using Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.me/LorenzoPichilli)
[![Donate to this project using Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://www.patreon.com/bePatron?u=9269604) [![Donate to this project using Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://www.patreon.com/bePatron?u=9269604)
A Flutter plugin that allows you to open an in-app browser window. A Flutter plugin that allows you to add an inline webview or open an in-app browser window.
This plugin is inspired by the popular [cordova-plugin-inappbrowser](https://github.com/apache/cordova-plugin-inappbrowser)! This plugin is inspired by the popular [cordova-plugin-inappbrowser](https://github.com/apache/cordova-plugin-inappbrowser)!
### IMPORTANT Note for iOS ### IMPORTANT Note for iOS
...@@ -29,13 +29,454 @@ First, add `flutter_inappbrowser` as a [dependency in your pubspec.yaml file](ht ...@@ -29,13 +29,454 @@ First, add `flutter_inappbrowser` as a [dependency in your pubspec.yaml file](ht
## Usage ## Usage
Classes: Classes:
- [InAppBrowser](#inappbrowser): Native WebView. - [InAppWebView](#inappwebview): Flutter Widget for adding an **inline native WebView** integrated in the flutter widget tree. [**Available only for Android** ([AndroidView](https://docs.flutter.io/flutter/widgets/AndroidView-class.html)) at this moment].
- [ChromeSafariBrowser](#chromesafaribrowser): [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS. - [InAppBrowser](#inappbrowser): In-App Browser using native WebView.
- [ChromeSafariBrowser](#chromesafaribrowser): In-App Browser using [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS.
- [InAppLocalhostServer](#inapplocalhostserver): This class allows you to create a simple server on `http://localhost:[port]/`. The default `port` value is `8080`.
Screenshots [here](#screenshots). ### `InAppWebView` class
Flutter Widget for adding an **inline native WebView** integrated in the flutter widget tree.
[AndroidView](https://docs.flutter.io/flutter/widgets/AndroidView-class.html) is not officially stable yet!
So, if you want use it, you can but you will have some limitation such as the inability to use the keyboard!
**Available only for Android** ([AndroidView](https://docs.flutter.io/flutter/widgets/AndroidView-class.html)) at this moment.
Example
```dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappbrowser/flutter_inappbrowser.dart';
Future main() async {
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
InAppWebViewController webView;
String url = "";
double progress = 0;
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Inline WebView example app'),
),
body: Container(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(20.0),
child: Text("CURRENT URL\n${ (url.length > 50) ? url.substring(0, 50) + "..." : url }"),
),
(progress != 1.0) ? LinearProgressIndicator(value: progress) : null,
Expanded(
child: Container(
margin: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.blueAccent)
),
child: InAppWebView(
initialUrl: "https://flutter.io/",
initialHeaders: {
},
initialOptions: {
},
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
print("started $url");
setState(() {
this.url = url;
});
},
onProgressChanged: (InAppWebViewController controller, int progress) {
setState(() {
this.progress = progress/100;
});
},
),
),
),
ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Icon(Icons.arrow_back),
onPressed: () {
if (webView != null) {
webView.goBack();
}
},
),
RaisedButton(
child: Icon(Icons.arrow_forward),
onPressed: () {
if (webView != null) {
webView.goForward();
}
},
),
RaisedButton(
child: Icon(Icons.refresh),
onPressed: () {
if (webView != null) {
webView.reload();
}
},
),
],
),
].where((Object o) => o != null).toList(),
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: 0,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.mail),
title: Text('Item 2'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Item 3')
)
],
),
),
);
}
}
```
Screenshots:
- Android:
![android](https://user-images.githubusercontent.com/5956938/47271038-7aebda80-d574-11e8-98fd-41e6bbc9fe2d.gif)
#### InAppWebView.initialUrl
Initial url that will be loaded.
#### InAppWebView.initialFile
Initial asset file that will be loaded. See `InAppWebView.loadFile()` for explanation.
#### InAppWebView.initialHeaders
Initial headers that will be used.
#### InAppWebView.initialOptions
Initial options that will be used.
All platforms support:
- __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`.
- __userAgent___: Set the custom WebView's user-agent.
- __javaScriptEnabled__: Set to `true` to enable JavaScript. The default value is `true`.
- __javaScriptCanOpenWindowsAutomatically__: Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`.
- __mediaPlaybackRequiresUserGesture__: Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`.
**Android** supports these additional options:
- __clearSessionCache__: Set to `true` to have the session cookie cache cleared before the new window is opened.
- __builtInZoomControls__: Set to `true` if the WebView should use its built-in zoom mechanisms. The default value is `false`.
- __supportZoom__: Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`.
- __databaseEnabled__: Set to `true` if you want the database storage API is enabled. The default value is `false`.
- __domStorageEnabled__: Set to `true` if you want the DOM storage API is enabled. The default value is `false`.
- __useWideViewPort__: Set to `true` if the WebView should enable support for the "viewport" HTML meta tag or should use a wide viewport. When the value of the setting is false, the layout width is always set to the width of the WebView control in device-independent (CSS) pixels. When the value is true and the page contains the viewport meta tag, the value of the width specified in the tag is used. If the page does not contain the tag or does not provide a width, then a wide viewport will be used. The default value is `true`.
- __safeBrowsingEnabled__: Set to `true` if you want the Safe Browsing is enabled. Safe Browsing allows WebView to protect against malware and phishing attacks by verifying the links. The default value is `true`.
**iOS** supports these additional options:
- __disallowOverScroll__: Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`.
- __enableViewportScale__: Set to `true` to allow a viewport meta tag to either disable or restrict the range of user scaling. The default value is `false`.
- __suppressesIncrementalRendering__: Set to `true` if you want the WebView suppresses content rendering until it is fully loaded into memory.. The default value is `false`.
- __allowsAirPlayForMediaPlayback__: Set to `true` to allow AirPlay. The default value is `true`.
- __allowsBackForwardNavigationGestures__: Set to `true` to allow the horizontal swipe gestures trigger back-forward list navigations. The default value is `true`.
- __allowsLinkPreview__: Set to `true` to allow that pressing on a link displays a preview of the destination for the link. The default value is `true`.
- __ignoresViewportScaleLimits__: Set to `true` if you want that the WebView should always allow scaling of the webpage, regardless of the author's intent. The ignoresViewportScaleLimits property overrides the `user-scalable` HTML property in a webpage. The default value is `false`.
- __allowsInlineMediaPlayback__: Set to `true` to allow HTML5 media playback to appear inline within the screen layout, using browser-supplied controls rather than native controls. For this to work, add the `webkit-playsinline` attribute to any `<video>` elements. The default value is `false`.
- __allowsPictureInPictureMediaPlayback__: Set to `true` to allow HTML5 videos play picture-in-picture. The default value is `true`.
#### Events
Event `onWebViewCreated` fires when the `InAppWebView` is created.
```dart
InAppWebView(
initialUrl: "https://flutter.io/",
onWebViewCreated: (InAppWebViewController controller) {}
}
```
Event `onLoadStart` fires when the `InAppWebView` starts to load an `url`.
```dart
InAppWebView(
initialUrl: "https://flutter.io/",
onLoadStart: (InAppWebViewController controller, String url) {}
}
```
Event `onLoadStop` fires when the `InAppWebView` finishes loading an `url`.
```dart
InAppWebView(
initialUrl: "https://flutter.io/",
onLoadStop: (InAppWebViewController controller, String url) {}
}
```
Event `onLoadError` fires when the `InAppWebView` encounters an error loading an `url`.
```dart
InAppWebView(
initialUrl: "https://flutter.io/",
onLoadError: (InAppWebViewController controller, String url, int code, String message) {}
}
```
Event `onProgressChanged` fires when the current `progress` (range 0-100) of loading a page is changed.
```dart
InAppWebView(
initialUrl: "https://flutter.io/",
onProgressChanged: (InAppWebViewController controller, int progress) {}
}
```
Event `onConsoleMessage` fires when the `InAppWebView` receives a `ConsoleMessage`.
```dart
InAppWebView(
initialUrl: "https://flutter.io/",
onConsoleMessage: (InAppWebViewController controller, ConsoleMessage consoleMessage) {}
}
```
`shouldOverrideUrlLoading`: Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
**NOTE**: In order to be able to listen this event, you need to set `useShouldOverrideUrlLoading` option to `true`.
```dart
InAppWebView(
initialUrl: "https://flutter.io/",
shouldOverrideUrlLoading: (InAppWebViewController controller, String url) {}
}
```
Event `onLoadResource` fires when the `InAppWebView` 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
InAppWebView(
initialUrl: "https://flutter.io/",
onLoadResource: (InAppWebViewController controller, WebResourceResponse response, WebResourceRequest request) {}
}
```
#### Future\<void\> InAppWebView.loadUrl
Loads the given `url` with optional `headers` specified as a map from name to value.
```dart
inAppWebViewController.loadUrl(String url, {Map<String, String> headers = const {}});
```
#### Future\<void\> InAppWebView.loadFile
Loads the given `assetFilePath` with optional `headers` specified as a map from name to value.
To be able to load your local files (html, js, css, etc.), you need to add them in the `assets` section of the `pubspec.yaml` file, otherwise they cannot be found!
Example of a `pubspec.yaml` file:
```yaml
...
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- assets/index.html
- assets/css/
- assets/images/
...
```
Example of a `main.dart` file:
```dart
...
inAppWebViewController.loadFile("assets/index.html");
...
```
```dart
inAppWebViewController.loadFile(String assetFilePath, {Map<String, String> headers = const {}});
```
#### Future\<void\> InAppWebView.reload
Reloads the `InAppWebView` window.
```dart
inAppWebViewController.reload();
```
#### Future\<void\> InAppWebView.goBack
Goes back in the history of the `InAppWebView` window.
```dart
inAppWebViewController.goBack();
```
#### Future\<bool\> InAppWebView.canGoBack
Returns a Boolean value indicating whether the `InAppWebView` can move backward.
```dart
inAppWebViewController.canGoBack();
```
#### Future\<void\> InAppWebView.goForward
Goes forward in the history of the `InAppWebView` window.
```dart
inAppWebViewController.goForward();
```
#### Future\<bool\> InAppWebView.canGoForward
Returns a Boolean value indicating whether the `InAppWebView` can move forward.
```dart
inAppWebViewController.canGoForward();
```
#### Future\<bool\> InAppWebView.isLoading
Check if the Web View of the `InAppWebView` instance is in a loading state.
```dart
inAppWebViewController.isLoading();
```
#### Future\<void\> InAppWebView.stopLoading
Stops the Web View of the `InAppWebView` instance from loading.
```dart
inAppWebViewController.stopLoading();
```
#### Future\<String\> InAppWebView.injectScriptCode
Injects JavaScript code into the `InAppWebView` window and returns the result of the evaluation.
```dart
inAppWebViewController.injectScriptCode(String source);
```
#### Future\<void\> InAppWebView.injectScriptFile
Injects a JavaScript file into the `InAppWebView` window.
```dart
inAppWebViewController.injectScriptFile(String urlFile);
```
#### Future\<void\> InAppWebView.injectStyleCode
Injects CSS into the `InAppWebView` window.
```dart
inAppWebViewController.injectStyleCode(String source);
```
#### Future\<void\> InAppWebView.injectStyleFile
Injects a CSS file into the `InAppWebView` window.
```dart
inAppWebViewController.injectStyleFile(String urlFile);
```
#### int InAppWebView.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
inAppWebViewController.addJavaScriptHandler(String handlerName, JavaScriptHandlerCallback callback);
```
#### bool InAppWebView.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
inAppWebViewController.removeJavaScriptHandler(String handlerName, int index);
```
#### Future\<Uint8List\> InAppWebView.takeScreenshot
Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it.
**NOTE for iOS**: available from iOS 11.0+.
```dart
inAppWebViewController.takeScreenshot();
```
#### Future\<void\> InAppWebView.setOptions
Sets the `InAppWebView` options with the new `options` and evaluates them.
```dart
inAppWebViewController.setOptions(Map<String, dynamic> options);
```
#### Future\<Map\<String, dynamic\>\> InAppWebView.getOptions
Gets the current `InAppWebView` options. Returns `null` if the options are not setted yet.
```dart
inAppWebViewController.getOptions();
```
### `InAppBrowser` class ### `InAppBrowser` class
Native WebView. In-App Browser using native WebView.
`inAppBrowser.webViewController` can be used to access the `InAppWebView` API.
Create a Class that extends the `InAppBrowser` Class in order to override the callbacks to manage the browser events. Create a Class that extends the `InAppBrowser` Class in order to override the callbacks to manage the browser events.
Example: Example:
...@@ -55,29 +496,29 @@ class MyInAppBrowser extends InAppBrowser { ...@@ -55,29 +496,29 @@ class MyInAppBrowser extends InAppBrowser {
print("\n\nStopped $url\n\n"); print("\n\nStopped $url\n\n");
// call a javascript message handler // call a javascript message handler
await this.injectScriptCode("window.flutter_inappbrowser.callHandler('handlerNameTest', 1, 5,'string', {'key': 5}, [4,6,8]);"); await this.webViewController.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.webViewController.injectScriptCode("document.body.innerHTML"));
// console messages // console messages
await this.injectScriptCode("console.log({'testObject': 5});"); // the message will be: [object Object] await this.webViewController.injectScriptCode("console.log({'testObject': 5});"); // the message will be: [object Object]
await this.injectScriptCode("console.log('testObjectStringify', JSON.stringify({'testObject': 5}));"); // the message will be: testObjectStringify {"testObject": 5} await this.webViewController.injectScriptCode("console.log('testObjectStringify', JSON.stringify({'testObject': 5}));"); // the message will be: testObjectStringify {"testObject": 5}
await this.injectScriptCode("console.error('testError', false);"); // the message will be: testError false await this.webViewController.injectScriptCode("console.error('testError', false);"); // the message will be: testError false
// add jquery library and custom javascript // add jquery library and custom javascript
await this.injectScriptFile("https://code.jquery.com/jquery-3.3.1.min.js"); await this.webViewController.injectScriptFile("https://code.jquery.com/jquery-3.3.1.min.js");
this.injectScriptCode(""" this.webViewController.injectScriptCode("""
\$( "body" ).html( "Next Step..." ) \$( "body" ).html( "Next Step..." )
"""); """);
// add custom css // add custom css
this.injectStyleCode(""" this.webViewController.injectStyleCode("""
body { body {
background-color: #3c3c3c !important; background-color: #3c3c3c !important;
} }
"""); """);
this.injectStyleFile("https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"); this.webViewController.injectStyleFile("https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css");
} }
@override @override
...@@ -93,7 +534,7 @@ class MyInAppBrowser extends InAppBrowser { ...@@ -93,7 +534,7 @@ class MyInAppBrowser extends InAppBrowser {
@override @override
void shouldOverrideUrlLoading(String url) { void shouldOverrideUrlLoading(String url) {
print("\n\n override $url\n\n"); print("\n\n override $url\n\n");
this.loadUrl(url); this.webViewController.loadUrl(url);
} }
@override @override
...@@ -130,7 +571,7 @@ class _MyAppState extends State<MyApp> { ...@@ -130,7 +571,7 @@ class _MyAppState extends State<MyApp> {
super.initState(); super.initState();
// listen for post messages coming from the JavaScript side // listen for post messages coming from the JavaScript side
int indexTest = inAppBrowser.addJavaScriptHandler("handlerNameTest", (arguments) async { int indexTest = inAppBrowser.webViewController.addJavaScriptHandler("handlerNameTest", (arguments) async {
print("handlerNameTest arguments"); print("handlerNameTest arguments");
print(arguments); // it prints: [1, 5, string, {key: 5}, [4, 6, 8]] print(arguments); // it prints: [1, 5, string, {key: 5}, [4, 6, 8]]
}); });
...@@ -159,17 +600,18 @@ class _MyAppState extends State<MyApp> { ...@@ -159,17 +600,18 @@ class _MyAppState extends State<MyApp> {
} }
``` ```
#### Future\<void\> InAppBrowser.open Screenshots:
- iOS:
![ios](https://user-images.githubusercontent.com/5956938/45934084-2a935400-bf99-11e8-9d71-9e1758b5b8c6.gif)
Opens a URL in a new InAppBrowser instance or the system browser. - Android:
**NOTE**: If you open the given `url` with the system browser (`openWithSystemBrowser: true`), you wont be able to use the `InAppBrowser` methods! ![android](https://user-images.githubusercontent.com/5956938/45934080-26ffcd00-bf99-11e8-8136-d39a81bd83e7.gif)
```dart #### Future\<void\> InAppBrowser.open
inAppBrowser.open({String url = "about:blank", Map<String, String> headers = const {}, 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.
- `url`: The `url` to load. Call `encodeUriComponent()` on this if the `url` contains Unicode characters. The default value is `about:blank`. - `url`: The `url` to load. Call `encodeUriComponent()` on this if the `url` contains Unicode characters. The default value is `about:blank`.
...@@ -178,17 +620,15 @@ Opens an `url` in a new `InAppBrowser` instance or the system browser. ...@@ -178,17 +620,15 @@ Opens an `url` in a new `InAppBrowser` instance or the system browser.
- `options`: Options for the `InAppBrowser`. - `options`: Options for the `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`. - __useOnLoadResource__: Set to `true` to be able to listen at the `onLoadResource()` event. The default value is `false`.
- __openWithSystemBrowser__: Set to `true` to open the given `url` with the system browser. The default value is `false`.
- __isLocalFile__: Set to `true` if the `url` is pointing to a local file (the file must be addded in the `assets` section of your `pubspec.yaml`. See `loadFile()` explanation). 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`.
- __javaScriptCanOpenWindowsAutomatically__: Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`. - __javaScriptCanOpenWindowsAutomatically__: Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`.
- __hidden__: Set to `true` to create the browser and load the page, but not show it. The `onLoadStop` event fires when loading is complete. Omit or set to `false` (default) to have the browser open and load normally. - __hidden__: Set to `true` to create the browser and load the page, but not show it. The `onLoadStop` event fires when loading is complete. Omit or set to `false` (default) to have the browser open and load normally.
- __toolbarTop__: Set to `false` to hide the toolbar at the top of the WebView. The default value is `true`. - __toolbarTop__: Set to `false` to hide the toolbar at the top of the WebView. The default value is `true`.
- __toolbarTopBackgroundColor__: Set the custom background color of the toolbat at the top. - __toolbarTopBackgroundColor__: Set the custom background color of the toolbar at the top.
- __hideUrlBar__: Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`. - __hideUrlBar__: Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`.
- __mediaPlaybackRequiresUserGesture__: Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`. - __mediaPlaybackRequiresUserGesture__: Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`.
...@@ -209,7 +649,7 @@ Opens an `url` in a new `InAppBrowser` instance or the system browser. ...@@ -209,7 +649,7 @@ Opens an `url` in a new `InAppBrowser` instance or the system browser.
- __disallowOverScroll__: Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`. - __disallowOverScroll__: Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`.
- __toolbarBottom__: Set to `false` to hide the toolbar at the bottom of the WebView. The default value is `true`. - __toolbarBottom__: Set to `false` to hide the toolbar at the bottom of the WebView. The default value is `true`.
- __toolbarBottomBackgroundColor__: Set the custom background color of the toolbat at the bottom. - __toolbarBottomBackgroundColor__: Set the custom background color of the toolbar at the bottom.
- __toolbarBottomTranslucent__: Set to `true` to set the toolbar at the bottom translucent. The default value is `true`. - __toolbarBottomTranslucent__: Set to `true` to set the toolbar at the bottom translucent. The default value is `true`.
- __closeButtonCaption__: Set the custom text for the close button. - __closeButtonCaption__: Set the custom text for the close button.
- __closeButtonColor__: Set the custom color for the close button. - __closeButtonColor__: Set the custom color for the close button.
...@@ -239,110 +679,15 @@ inAppBrowser.open('https://flutter.io/', options: { ...@@ -239,110 +679,15 @@ inAppBrowser.open('https://flutter.io/', options: {
}); });
``` ```
#### static Future\<void\> InAppBrowser.openWithSystemBrowser
This is a static method that opens an `url` in the system browser.
This has the same behaviour of an `InAppBrowser` instance calling the `open()` method with option `openWithSystemBrowser: true`.
```dart
InAppBrowser.openWithSystemBrowser(String url);
```
#### Future\<void\> InAppBrowser.openOnLocalhost
Serve the `assetFilePath` from Flutter assets on http://localhost:`port`/. It is similar to `InAppBrowser.open()` with option `isLocalFile: true`, but it starts a server.
**NOTE for iOS**: For the iOS Platform, you need to add the `NSAllowsLocalNetworking` key with `true` in the `Info.plist` file (See [ATS Configuration Basics](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35)):
```xml
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
```
The `NSAllowsLocalNetworking` key is available since **iOS 10**.
```dart
inAppBrowser.openOnLocalhost(String assetFilePath, {int port = 8080, Map<String, String> headers = const {}, Map<String, dynamic> options = const {}});
```
#### Events
Event fires when the `InAppBrowser` starts to load an `url`.
```dart
@override
void onLoadStart(String url) {
}
```
Event fires when the `InAppBrowser` finishes loading an `url`.
```dart
@override
void onLoadStop(String url) {
}
```
Event fires when the `InAppBrowser` encounters an error loading an `url`.
```dart
@override
void onLoadError(String url, String code, String message) {
}
```
Event fires when the `InAppBrowser` window is closed.
```dart
@override
void onExit() {
}
```
Event fires when the `InAppBrowser` webview receives a `ConsoleMessage`.
```dart
@override
void onConsoleMessage(ConsoleMessage consoleMessage) {
}
```
Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
**NOTE**: In order to be able to listen this event, you need to set `useShouldOverrideUrlLoading` option to `true`.
```dart
@override
void shouldOverrideUrlLoading(String url) {
}
```
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 ```dart
@override inAppBrowser.open({String url = "about:blank", Map<String, String> headers = const {}, Map<String, dynamic> options = const {}});
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
}
```
#### Future\<void\> InAppBrowser.loadUrl
Loads the given `url` with optional `headers` specified as a map from name to value.
```dart
inAppBrowser.loadUrl(String url, {Map<String, String> headers = const {}});
``` ```
#### Future\<void\> InAppBrowser.loadFile #### Future\<void\> InAppBrowser.openFile
Loads the given `assetFilePath` with optional `headers` specified as a map from name to value. Opens the giver `assetFilePath` file in a new `InAppBrowser` instance. The other arguments are the same of `InAppBrowser.open()`.
To be able to load your local files (html, js, css, etc.), you need to add them in the `assets` section of the `pubspec.yaml` file, otherwise they cannot be found! To be able to load your local files (assets, js, css, etc.), you need to add them in the `assets` section of the `pubspec.yaml` file, otherwise they cannot be found!
Example of a `pubspec.yaml` file: Example of a `pubspec.yaml` file:
```yaml ```yaml
...@@ -366,12 +711,20 @@ flutter: ...@@ -366,12 +711,20 @@ flutter:
Example of a `main.dart` file: Example of a `main.dart` file:
```dart ```dart
... ...
inAppBrowser.loadFile("assets/index.html"); inAppBrowser.openFile("assets/index.html");
... ...
``` ```
```dart ```dart
inAppBrowser.loadFile(String assetFilePath, {Map<String, String> headers = const {}}); inAppBrowser.openFile(String assetFilePath, {Map<String, String> headers = const {}, Map<String, dynamic> options = const {}});
```
#### static Future\<void\> InAppBrowser.openWithSystemBrowser
This is a static method that opens an `url` in the system browser. You wont be able to use the `InAppBrowser` methods here!
```dart
InAppBrowser.openWithSystemBrowser(String url);
``` ```
#### Future\<void\> InAppBrowser.show #### Future\<void\> InAppBrowser.show
...@@ -398,142 +751,112 @@ Closes the `InAppBrowser` window. ...@@ -398,142 +751,112 @@ Closes the `InAppBrowser` window.
inAppBrowser.close(); inAppBrowser.close();
``` ```
#### Future\<void\> InAppBrowser.reload #### Future\<bool\> InAppBrowser.isHidden
Reloads the `InAppBrowser` window. Check if the Web View of the `InAppBrowser` instance is hidden.
```dart ```dart
inAppBrowser.reload(); inAppBrowser.isHidden();
``` ```
#### Future\<void\> InAppBrowser.goBack #### Future\<void\> InAppBrowser.setOptions
Goes back in the history of the `InAppBrowser` window.
Sets the `InAppBrowser` options and the `InAppWebView` options with the new `options` and evaluates them.
```dart ```dart
inAppBrowser.goBack(); inAppBrowser.setOptions(Map<String, dynamic> options);
``` ```
#### Future\<void\> InAppBrowser.goForward #### Future\<Map\<String, dynamic\>\> InAppBrowser.getOptions
Goes forward in the history of the `InAppBrowser` window.
Gets the current `InAppBrowser` options and the current `InAppWebView` options merged together. Returns `null` if the options are not setted yet.
```dart ```dart
inAppBrowser.goForward(); inAppBrowser.getOptions();
``` ```
#### Future\<bool\> InAppBrowser.isLoading #### bool InAppBrowser.isOpened
Check if the Web View of the `InAppBrowser` instance is in a loading state.
Returns `true` if the `InAppBrowser` instance is opened, otherwise `false`.
```dart ```dart
inAppBrowser.isLoading(); inAppBrowser.isOpened();
``` ```
#### Future\<void\> InAppBrowser.stopLoading #### Events
Stops the Web View of the `InAppBrowser` instance from loading.
Event `onLoadStart` fires when the `InAppBrowser` starts to load an `url`.
```dart ```dart
inAppBrowser.stopLoading(); @override
``` void onLoadStart(String url) {
#### Future\<bool\> InAppBrowser.isHidden
Check if the Web View of the `InAppBrowser` instance is hidden.
```dart }
inAppBrowser.isHidden();
``` ```
#### Future\<String\> InAppBrowser.injectScriptCode Event `onLoadStop` fires when the `InAppBrowser` finishes loading an `url`.
Injects JavaScript code into the `InAppBrowser` window and returns the result of the evaluation.
```dart ```dart
inAppBrowser.injectScriptCode(String source); @override
``` void onLoadStop(String url) {
#### Future\<void\> InAppBrowser.injectScriptFile
Injects a JavaScript file into the `InAppBrowser` window.
```dart }
inAppBrowser.injectScriptFile(String urlFile);
``` ```
#### Future\<void\> InAppBrowser.injectStyleCode Event `onLoadError` fires when the `InAppBrowser` encounters an error loading an `url`.
Injects CSS into the `InAppBrowser` window.
```dart ```dart
inAppBrowser.injectStyleCode(String source); @override
``` void onLoadError(String url, int code, String message) {
#### Future\<void\> InAppBrowser.injectStyleFile
Injects a CSS file into the `InAppBrowser` window.
```dart }
inAppBrowser.injectStyleFile(String urlFile);
``` ```
#### int InAppBrowser.addJavaScriptHandler Event `onProgressChanged` fires when the current `progress` (range 0-100) of loading a page is changed.
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 ```dart
inAppBrowser.addJavaScriptHandler(String handlerName, JavaScriptHandlerCallback callback); @override
``` void onProgressChanged(int progress) {
#### 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);
``` ```
#### Future\<Uint8List\> InAppBrowser.takeScreenshot Event `onExit` fires when the `InAppBrowser` window is closed.
Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it.
**NOTE for iOS**: available from iOS 11.0+.
```dart ```dart
inAppBrowser.takeScreenshot(); @override
``` void onExit() {
#### Future\<void\> InAppBrowser.setOptions }
```
Sets the `InAppBrowser` options with the new `options` and evaluates them. Event `onConsoleMessage` fires when the `InAppBrowser` webview receives a `ConsoleMessage`.
```dart ```dart
inAppBrowser.setOptions(Map<String, dynamic> options); @override
void onConsoleMessage(ConsoleMessage consoleMessage) {
}
``` ```
#### Future\<Map\<String, dynamic\>\> InAppBrowser.getOptions `shouldOverrideUrlLoading`: Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
Gets the current `InAppBrowser` options. Returns `null` if the options are not setted yet. **NOTE**: In order to be able to listen this event, you need to set `useShouldOverrideUrlLoading` option to `true`.
```dart ```dart
inAppBrowser.getOptions(); @override
void shouldOverrideUrlLoading(String url) {
}
``` ```
#### bool InAppBrowser.isOpened Event `onLoadResource` fires when the `InAppBrowser` webview loads a resource.
Returns `true` if the `InAppBrowser` instance is opened, otherwise `false`. **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 ```dart
inAppBrowser.isOpened(); @override
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
}
``` ```
### `ChromeSafariBrowser` class ### `ChromeSafariBrowser` class
[Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS. [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS.
You can initialize the `ChromeSafariBrowser` instance with an `InAppBrowser` fallback instance.
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:
```dart ```dart
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
...@@ -632,8 +955,17 @@ class _MyAppState extends State<MyApp> { ...@@ -632,8 +955,17 @@ class _MyAppState extends State<MyApp> {
``` ```
Screenshots:
- iOS:
![ios](https://user-images.githubusercontent.com/5956938/46532148-0c362e00-c8a0-11e8-9a0e-343e049dcf35.gif)
- Android:
![android](https://user-images.githubusercontent.com/5956938/46532149-0c362e00-c8a0-11e8-8134-9af18f38a746.gif)
#### Future\<void\> ChromeSafariBrowser.open #### Future\<void\> ChromeSafariBrowser.open
Opens an `url` in a new `ChromeSafariBrowser` instance or the system browser. Opens an `url` in a new `ChromeSafariBrowser` instance.
- `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.
...@@ -673,7 +1005,7 @@ chromeSafariBrowser.open("https://flutter.io/", options: { ...@@ -673,7 +1005,7 @@ chromeSafariBrowser.open("https://flutter.io/", options: {
#### Events #### Events
Event fires when the `ChromeSafariBrowser` is opened. Event `onOpened` fires when the `ChromeSafariBrowser` is opened.
```dart ```dart
@override @override
void onOpened() { void onOpened() {
...@@ -681,7 +1013,7 @@ Event fires when the `ChromeSafariBrowser` is opened. ...@@ -681,7 +1013,7 @@ Event fires when the `ChromeSafariBrowser` is opened.
} }
``` ```
Event fires when the `ChromeSafariBrowser` is loaded. Event `onLoaded` fires when the `ChromeSafariBrowser` is loaded.
```dart ```dart
@override @override
void onLoaded() { void onLoaded() {
...@@ -689,7 +1021,7 @@ Event fires when the `ChromeSafariBrowser` is loaded. ...@@ -689,7 +1021,7 @@ Event fires when the `ChromeSafariBrowser` is loaded.
} }
``` ```
Event fires when the `ChromeSafariBrowser` is closed. Event `onClosed` fires when the `ChromeSafariBrowser` is closed.
```dart ```dart
@override @override
void onClosed() { void onClosed() {
...@@ -697,22 +1029,67 @@ Event fires when the `ChromeSafariBrowser` is closed. ...@@ -697,22 +1029,67 @@ Event fires when the `ChromeSafariBrowser` is closed.
} }
``` ```
## Screenshots: ### `InAppLocalhostServer` class
This class allows you to create a simple server on `http://localhost:[port]/` in order to be able to load your assets file on a server. The default `port` value is `8080`.
#### InAppBrowser Example:
iOS: ```dart
// ...
![ios](https://user-images.githubusercontent.com/5956938/45934084-2a935400-bf99-11e8-9d71-9e1758b5b8c6.gif) InAppLocalhostServer localhostServer = new InAppLocalhostServer();
Android: Future main() async {
await localhostServer.start();
runApp(new MyApp());
}
![android](https://user-images.githubusercontent.com/5956938/45934080-26ffcd00-bf99-11e8-8136-d39a81bd83e7.gif) // ...
#### ChromeSafariBrowser @override
iOS: Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Flutter InAppBrowser Plugin example app'),
),
body: new Center(
child: new RaisedButton(
onPressed: () async {
await inAppBrowserFallback.open(
url: "http://localhost:8080/assets/index.html",
options: {});
},
child: Text("Open InAppBrowser")
),
),
),
);
}
![ios](https://user-images.githubusercontent.com/5956938/46532148-0c362e00-c8a0-11e8-9a0e-343e049dcf35.gif) // ...
Android: ```
![android](https://user-images.githubusercontent.com/5956938/46532149-0c362e00-c8a0-11e8-8134-9af18f38a746.gif) #### Future\<void\> InAppLocalhostServer.start
\ No newline at end of file Starts a server on `http://localhost:[port]/`.
**NOTE for iOS**: For the iOS Platform, you need to add the `NSAllowsLocalNetworking` key with `true` in the `Info.plist` file (See [ATS Configuration Basics](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35)):
```xml
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
```
The `NSAllowsLocalNetworking` key is available since **iOS 10**.
```dart
localhostServer.start();
```
#### Future\<void\> InAppLocalhostServer.close
Closes the server.
```dart
localhostServer.close();
```
\ No newline at end of file
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application> <application>
<activity android:theme="@style/AppTheme" android:name=".WebViewActivity" android:configChanges="orientation|screenSize"></activity> <activity android:theme="@style/AppTheme" android:name=".InAppBrowserActivity" android:configChanges="orientation|screenSize"></activity>
<activity android:theme="@style/ThemeTransparent" android:name=".chrome_custom_tabs.ChromeCustomTabsActivity" android:configChanges="orientation|screenSize"></activity> <activity android:theme="@style/ThemeTransparent" android:name=".ChromeCustomTabs.ChromeCustomTabsActivity" android:configChanges="orientation|screenSize"></activity>
</application> </application>
</manifest> </manifest>
\ No newline at end of file
package com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs; package com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
...@@ -6,10 +6,8 @@ import android.graphics.Color; ...@@ -6,10 +6,8 @@ import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsIntent;
import android.util.Log;
import com.pichillilorenzo.flutter_inappbrowser.InAppBrowserFlutterPlugin; import com.pichillilorenzo.flutter_inappbrowser.InAppBrowserFlutterPlugin;
import com.pichillilorenzo.flutter_inappbrowser.InAppBrowserOptions;
import com.pichillilorenzo.flutter_inappbrowser.R; import com.pichillilorenzo.flutter_inappbrowser.R;
import java.util.HashMap; import java.util.HashMap;
......
package com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs; package com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs;
import com.pichillilorenzo.flutter_inappbrowser.Options; import com.pichillilorenzo.flutter_inappbrowser.Options;
......
package com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs; package com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs;
import android.app.Activity; import android.app.Activity;
import android.net.Uri; import android.net.Uri;
...@@ -10,8 +10,6 @@ import android.support.customtabs.CustomTabsSession; ...@@ -10,8 +10,6 @@ import android.support.customtabs.CustomTabsSession;
import java.util.List; import java.util.List;
import static android.support.v4.app.ActivityCompat.startActivityForResult;
/** /**
* This is a helper class to manage the connection to the Custom Tabs Service. * This is a helper class to manage the connection to the Custom Tabs Service.
*/ */
......
package com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs; package com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
...@@ -6,7 +6,6 @@ import android.content.IntentFilter; ...@@ -6,7 +6,6 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.net.Uri; import android.net.Uri;
import android.support.customtabs.CustomTabsClient;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
......
package com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs; package com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs;
import android.app.Service; import android.app.Service;
import android.content.Intent; import android.content.Intent;
......
package com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs; package com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs;
import android.content.ComponentName; import android.content.ComponentName;
import android.support.customtabs.CustomTabsClient; import android.support.customtabs.CustomTabsClient;
......
package com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs; package com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs;
import android.support.customtabs.CustomTabsClient; import android.support.customtabs.CustomTabsClient;
......
package com.pichillilorenzo.flutter_inappbrowser;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebViewOptions;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import static io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import static io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.plugin.platform.PlatformView;
public class FlutterWebView implements PlatformView, MethodCallHandler {
static final String LOG_TAG = "FlutterWebView";
public final Activity activity;
public InAppWebView webView;
public MethodChannel channel;
public final Registrar registrar;
public final Context context;
public FlutterWebView(Registrar registrar, int id, HashMap<String, Object> params) {
this.registrar = registrar;
this.activity = registrar.activity();
this.context = registrar.context();
String initialUrl = (String) params.get("initialUrl");
String initialFile = (String) params.get("initialFile");
Map<String, String> initialHeaders = (Map<String, String>) params.get("initialHeaders");
HashMap<String, Object> initialOptions = (HashMap<String, Object>) params.get("initialOptions");
InAppWebViewOptions options = new InAppWebViewOptions();
options.parse(initialOptions);
if (initialFile != null) {
try {
initialUrl = Util.getUrlAsset(registrar, initialFile);
} catch (IOException e) {
e.printStackTrace();
Log.e(LOG_TAG, initialFile + " asset file cannot be found!", e);
return;
}
}
webView = new InAppWebView(context, this, id, options);
webView.prepare();
channel = new MethodChannel(registrar.messenger(), "com.pichillilorenzo/flutter_inappwebview_" + id);
channel.setMethodCallHandler(this);
webView.loadUrl(initialUrl, initialHeaders);
}
@Override
public View getView() {
return webView;
}
@Override
public void onMethodCall(MethodCall call, Result result) {
String source;
String jsWrapper;
String urlFile;
switch (call.method) {
case "loadUrl":
if (webView != null)
webView.loadUrl(call.argument("url").toString(), (Map<String, String>) call.argument("headers"), result);
else
result.success(false);
break;
case "loadFile":
if (webView != null)
webView.loadFile(call.argument("url").toString(), (Map<String, String>) call.argument("headers"), result);
else
result.success(false);
break;
case "injectScriptCode":
if (webView != null) {
source = call.argument("source").toString();
jsWrapper = "(function(){return JSON.stringify(eval(%s));})();";
webView.injectDeferredObject(source, jsWrapper, result);
}
else {
result.success("");
}
break;
case "injectScriptFile":
if (webView != null) {
urlFile = call.argument("urlFile").toString();
jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document);";
webView.injectDeferredObject(urlFile, jsWrapper, null);
}
result.success(true);
break;
case "injectStyleCode":
if (webView != null) {
source = call.argument("source").toString();
jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document);";
webView.injectDeferredObject(source, jsWrapper, null);
}
result.success(true);
break;
case "injectStyleFile":
if (webView != null) {
urlFile = call.argument("urlFile").toString();
jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document);";
webView.injectDeferredObject(urlFile, jsWrapper, null);
}
result.success(true);
break;
case "reload":
if (webView != null)
webView.reload();
result.success(true);
break;
case "goBack":
if (webView != null)
webView.goBack();
result.success(true);
break;
case "canGoBack":
result.success((webView != null) && webView.canGoBack());
break;
case "goForward":
if (webView != null)
webView.goForward();
result.success(true);
break;
case "canGoForward":
result.success((webView != null) && webView.canGoForward());
break;
case "stopLoading":
if (webView != null)
webView.stopLoading();
result.success(true);
break;
case "isLoading":
result.success((webView != null) && webView.isLoading());
break;
case "takeScreenshot":
result.success((webView != null) ? webView.takeScreenshot() : null);
break;
case "setOptions":
if (webView != null) {
InAppWebViewOptions inAppWebViewOptions = new InAppWebViewOptions();
HashMap<String, Object> inAppWebViewOptionsMap = (HashMap<String, Object>) call.argument("options");
inAppWebViewOptions.parse(inAppWebViewOptionsMap);
webView.setOptions(inAppWebViewOptions, inAppWebViewOptionsMap);
}
result.success(true);
break;
case "getOptions":
result.success((webView != null) ? webView.getOptions() : null);
break;
case "dispose":
dispose();
result.success(true);
break;
default:
result.notImplemented();
}
}
@Override
public void dispose() {
if (webView != null) {
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
webView = null;
}
});
webView.loadUrl("about:blank");
}
}
}
\ No newline at end of file
package com.pichillilorenzo.flutter_inappbrowser;
import android.app.Activity;
import android.content.Context;
import java.util.HashMap;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;
public class FlutterWebViewFactory extends PlatformViewFactory {
private final BinaryMessenger messenger;
private final Registrar registrar;
private final Activity activity;
public FlutterWebViewFactory(Registrar registrar, Activity activity) {
super(StandardMessageCodec.INSTANCE);
this.registrar = registrar;
this.messenger = registrar.messenger();
this.activity = activity;
}
@Override
public PlatformView create(Context context, int id, Object args) {
HashMap<String, Object> params = (HashMap<String, Object>) args;
return new FlutterWebView(registrar, id, params);
}
}
...@@ -6,75 +6,40 @@ import android.graphics.Canvas; ...@@ -6,75 +6,40 @@ import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Picture; import android.graphics.Picture;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.webkit.ValueCallback;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.SearchView; import android.widget.SearchView;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebViewOptions;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap; 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; public class InAppBrowserActivity extends AppCompatActivity {
public class WebViewActivity extends AppCompatActivity { static final String LOG_TAG = "InAppBrowserActivity";
public String uuid;
String uuid; public InAppWebView webView;
WebView webView; public ActionBar actionBar;
ActionBar actionBar; public Menu menu;
Menu menu; public SearchView searchView;
InAppBrowserWebViewClient inAppBrowserWebViewClient; public InAppBrowserOptions options;
InAppBrowserWebChromeClient inAppBrowserWebChromeClient; public Map<String, String> headers;
SearchView searchView; public ProgressBar progressBar;
InAppBrowserOptions options;
Map<String, String> headers;
ProgressBar progressBar;
public boolean isLoading = false;
public boolean isHidden = false; public boolean isHidden = false;
OkHttpClient httpClient;
static final String consoleLogJS = "(function() {" +
" var oldLogs = {" +
" 'log': console.log," +
" 'debug': console.debug," +
" 'error': console.error," +
" 'info': console.info," +
" 'warn': console.warn" +
" };" +
" for (var k in oldLogs) {" +
" (function(oldLog) {" +
" console[oldLog] = function() {" +
" var message = '';" +
" for (var i in arguments) {" +
" if (message == '') {" +
" message += arguments[i];" +
" }" +
" else {" +
" message += ' ' + arguments[i];" +
" }" +
" }" +
" oldLogs[oldLog].call(console, message);" +
" }" +
" })(k);" +
" }" +
"})();";
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
...@@ -83,13 +48,19 @@ public class WebViewActivity extends AppCompatActivity { ...@@ -83,13 +48,19 @@ public class WebViewActivity extends AppCompatActivity {
setContentView(R.layout.activity_web_view); setContentView(R.layout.activity_web_view);
webView = findViewById(R.id.webView); webView = findViewById(R.id.webView);
webView.inAppBrowserActivity = this;
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
uuid = b.getString("uuid"); uuid = b.getString("uuid");
String url = b.getString("url"); String url = b.getString("url");
HashMap<String, Object> optionsMap = (HashMap<String, Object>) b.getSerializable("options");
options = new InAppBrowserOptions(); options = new InAppBrowserOptions();
options.parse((HashMap<String, Object>) b.getSerializable("options")); options.parse(optionsMap);
InAppWebViewOptions webViewOptions = new InAppWebViewOptions();
webViewOptions.parse(optionsMap);
webView.options = webViewOptions;
headers = (HashMap<String, String>) b.getSerializable("headers"); headers = (HashMap<String, String>) b.getSerializable("headers");
...@@ -97,100 +68,22 @@ public class WebViewActivity extends AppCompatActivity { ...@@ -97,100 +68,22 @@ public class WebViewActivity extends AppCompatActivity {
actionBar = getSupportActionBar(); actionBar = getSupportActionBar();
prepareWebView(); prepareView();
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 assets> <assets 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> </assets>", "text/assets", "utf8"); //webView.loadData("<!DOCTYPE assets> <assets 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> </assets>", "text/assets", "utf8");
} }
private void prepareWebView() { private void prepareView() {
webView.addJavascriptInterface(new JavaScriptBridgeInterface(this), JavaScriptBridgeInterface.name); webView.prepare();
inAppBrowserWebChromeClient = new InAppBrowserWebChromeClient(this);
webView.setWebChromeClient(inAppBrowserWebChromeClient);
inAppBrowserWebViewClient = new InAppBrowserWebViewClient(this);
webView.setWebViewClient(inAppBrowserWebViewClient);
// final Activity activity = this;
//
// webView.setDownloadListener(new DownloadListener() {
// @Override
// public void onDownloadStart(final String url, final String userAgent,
// final String contentDisposition, final String mimetype,
// final long contentLength) {
//
// RequestPermissionHandler.checkAndRun(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, RequestPermissionHandler.REQUEST_CODE_WRITE_EXTERNAL_STORAGE, new Runnable(){
// @Override
// public void run(){
// DownloadManager.Request request = new DownloadManager.Request(
// Uri.parse(url));
//
// final String filename = URLUtil.guessFileName(url, contentDisposition, mimetype);
// request.allowScanningByMediaScanner();
// request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //Notify client once download is completed!
// request.setVisibleInDownloadsUi(true);
// request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
// DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
// if (dm != null) {
// dm.enqueue(request);
// Toast.makeText(getApplicationContext(), "Downloading File: " + filename, //To notify the Client that the file is being downloaded
// Toast.LENGTH_LONG).show();
// }
// else {
// Toast.makeText(getApplicationContext(), "Cannot Download File: " + filename, //To notify the Client that the file cannot be downloaded
// Toast.LENGTH_LONG).show();
// }
// }
// });
// }
// });
WebSettings settings = webView.getSettings();
if (options.hidden) if (options.hidden)
hide(); hide();
else else
show(); show();
settings.setJavaScriptEnabled(options.javaScriptEnabled);
settings.setJavaScriptCanOpenWindowsAutomatically(options.javaScriptCanOpenWindowsAutomatically);
settings.setBuiltInZoomControls(options.builtInZoomControls);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
settings.setSafeBrowsingEnabled(options.safeBrowsingEnabled);
settings.setMediaPlaybackRequiresUserGesture(options.mediaPlaybackRequiresUserGesture);
settings.setDatabaseEnabled(options.databaseEnabled);
settings.setDomStorageEnabled(options.domStorageEnabled);
if (!options.userAgent.isEmpty())
settings.setUserAgentString(options.userAgent);
if (options.clearCache)
clearCache();
else if (options.clearSessionCache)
CookieManager.getInstance().removeSessionCookie();
// Enable Thirdparty Cookies on >=Android 5.0 device
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
settings.setLoadWithOverviewMode(true);
settings.setUseWideViewPort(options.useWideViewPort);
settings.setSupportZoom(options.supportZoom);
// fix webview scaling
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
else
settings.setTextZoom(100);
progressBar = findViewById(R.id.progressBar); progressBar = findViewById(R.id.progressBar);
if (!options.progressBar) if (!options.progressBar)
...@@ -272,34 +165,34 @@ public class WebViewActivity extends AppCompatActivity { ...@@ -272,34 +165,34 @@ public class WebViewActivity extends AppCompatActivity {
} }
public void loadUrl(String url, MethodChannel.Result result) { public void loadUrl(String url, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) { if (webView != null) {
webView.loadUrl(url); webView.loadUrl(url, result);
} else { } else {
result.error("Cannot load url", "", null); result.error(LOG_TAG, "Cannot load url " + url, null);
} }
} }
public void loadUrl(String url, Map<String, String> headers, MethodChannel.Result result) { public void loadUrl(String url, Map<String, String> headers, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) { if (webView != null) {
webView.loadUrl(url, headers); webView.loadUrl(url, headers, result);
} else { } else {
result.error("Cannot load url", "", null); result.error(LOG_TAG, "Cannot load url " + url, null);
} }
} }
public void loadFile(String url, MethodChannel.Result result) { public void loadFile(String url, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) { if (webView != null) {
webView.loadUrl(url); webView.loadFile(url, result);
} else { } else {
result.error("Cannot load url", "", null); result.error(LOG_TAG, "Cannot load file " + url, null);
} }
} }
public void loadFile(String url, Map<String, String> headers, MethodChannel.Result result) { public void loadFile(String url, Map<String, String> headers, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) { if (webView != null) {
webView.loadUrl(url, headers); webView.loadFile(url, headers, result);
} else { } else {
result.error("Cannot load url", "", null); result.error(LOG_TAG, "Cannot load file " + url, null);
} }
} }
...@@ -351,7 +244,7 @@ public class WebViewActivity extends AppCompatActivity { ...@@ -351,7 +244,7 @@ public class WebViewActivity extends AppCompatActivity {
public void show() { public void show() {
isHidden = false; isHidden = false;
Intent openActivity = new Intent(InAppBrowserFlutterPlugin.registrar.activity(), WebViewActivity.class); Intent openActivity = new Intent(InAppBrowserFlutterPlugin.registrar.activity(), InAppBrowserActivity.class);
openActivity.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); openActivity.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivityIfNeeded(openActivity, 0); startActivityIfNeeded(openActivity, 0);
} }
...@@ -363,31 +256,10 @@ public class WebViewActivity extends AppCompatActivity { ...@@ -363,31 +256,10 @@ public class WebViewActivity extends AppCompatActivity {
public boolean isLoading() { public boolean isLoading() {
if (webView != null) if (webView != null)
return isLoading; return webView.isLoading;
return false; return false;
} }
private void clearCookies() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean aBoolean) {
}
});
} else {
CookieManager.getInstance().removeAllCookie();
}
}
private void clearCache() {
if (webView != null) {
webView.clearCache(true);
clearCookies();
webView.clearFormData();
}
}
public void goBackButtonClicked(MenuItem item) { public void goBackButtonClicked(MenuItem item) {
goBack(); goBack();
} }
...@@ -433,7 +305,9 @@ public class WebViewActivity extends AppCompatActivity { ...@@ -433,7 +305,9 @@ public class WebViewActivity extends AppCompatActivity {
public void setOptions(InAppBrowserOptions newOptions, HashMap<String, Object> newOptionsMap) { public void setOptions(InAppBrowserOptions newOptions, HashMap<String, Object> newOptionsMap) {
WebSettings settings = webView.getSettings(); InAppWebViewOptions newInAppWebViewOptions = new InAppWebViewOptions();
newInAppWebViewOptions.parse(newOptionsMap);
webView.setOptions(newInAppWebViewOptions, newOptionsMap);
if (newOptionsMap.get("hidden") != null && options.hidden != newOptions.hidden) { if (newOptionsMap.get("hidden") != null && options.hidden != newOptions.hidden) {
if (newOptions.hidden) if (newOptions.hidden)
...@@ -442,41 +316,6 @@ public class WebViewActivity extends AppCompatActivity { ...@@ -442,41 +316,6 @@ public class WebViewActivity extends AppCompatActivity {
show(); show();
} }
if (newOptionsMap.get("javaScriptEnabled") != null && options.javaScriptEnabled != newOptions.javaScriptEnabled)
settings.setJavaScriptEnabled(newOptions.javaScriptEnabled);
if (newOptionsMap.get("javaScriptCanOpenWindowsAutomatically") != null && options.javaScriptCanOpenWindowsAutomatically != newOptions.javaScriptCanOpenWindowsAutomatically)
settings.setJavaScriptCanOpenWindowsAutomatically(newOptions.javaScriptCanOpenWindowsAutomatically);
if (newOptionsMap.get("builtInZoomControls") != null && options.builtInZoomControls != newOptions.builtInZoomControls)
settings.setBuiltInZoomControls(newOptions.builtInZoomControls);
if (newOptionsMap.get("safeBrowsingEnabled") != null && options.safeBrowsingEnabled != newOptions.safeBrowsingEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
settings.setSafeBrowsingEnabled(newOptions.safeBrowsingEnabled);
if (newOptionsMap.get("mediaPlaybackRequiresUserGesture") != null && options.mediaPlaybackRequiresUserGesture != newOptions.mediaPlaybackRequiresUserGesture)
settings.setMediaPlaybackRequiresUserGesture(newOptions.mediaPlaybackRequiresUserGesture);
if (newOptionsMap.get("databaseEnabled") != null && options.databaseEnabled != newOptions.databaseEnabled)
settings.setDatabaseEnabled(newOptions.databaseEnabled);
if (newOptionsMap.get("domStorageEnabled") != null && options.domStorageEnabled != newOptions.domStorageEnabled)
settings.setDomStorageEnabled(newOptions.domStorageEnabled);
if (newOptionsMap.get("userAgent") != null && options.userAgent != newOptions.userAgent && !newOptions.userAgent.isEmpty())
settings.setUserAgentString(newOptions.userAgent);
if (newOptionsMap.get("clearCache") != null && newOptions.clearCache)
clearCache();
else if (newOptionsMap.get("clearSessionCache") != null && newOptions.clearSessionCache)
CookieManager.getInstance().removeSessionCookie();
if (newOptionsMap.get("useWideViewPort") != null && options.useWideViewPort != newOptions.useWideViewPort)
settings.setUseWideViewPort(newOptions.useWideViewPort);
if (newOptionsMap.get("supportZoom") != null && options.supportZoom != newOptions.supportZoom)
settings.setSupportZoom(newOptions.supportZoom);
if (newOptionsMap.get("progressBar") != null && options.progressBar != newOptions.progressBar && progressBar != null) { if (newOptionsMap.get("progressBar") != null && options.progressBar != newOptions.progressBar && progressBar != null) {
if (newOptions.progressBar) if (newOptions.progressBar)
progressBar.setMax(0); progressBar.setMax(0);
...@@ -511,7 +350,19 @@ public class WebViewActivity extends AppCompatActivity { ...@@ -511,7 +350,19 @@ public class WebViewActivity extends AppCompatActivity {
} }
public HashMap<String, Object> getOptions() { public HashMap<String, Object> getOptions() {
return (options != null) ? options.getHashMap() : null; HashMap<String, Object> webViewOptionsMap = webView.getOptions();
if (options == null || webViewOptionsMap == null)
return null;
HashMap<String, Object> optionsMap = options.getHashMap();
optionsMap.putAll(webViewOptionsMap);
return optionsMap;
} }
public void injectDeferredObject(String source, String jsWrapper, MethodChannel.Result result) {
if (webView != null)
webView.injectDeferredObject(source, jsWrapper, result);
else
result.success("");
}
} }
...@@ -25,29 +25,20 @@ import android.app.Activity; ...@@ -25,29 +25,20 @@ import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.res.AssetManager;
import android.os.Parcelable; import android.os.Parcelable;
import android.provider.Browser; import android.provider.Browser;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.util.Base64;
import android.util.JsonReader;
import android.util.JsonToken;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import android.webkit.ValueCallback;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import android.util.Log; import android.util.Log;
import com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs.ChromeCustomTabsActivity; import com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs.ChromeCustomTabsActivity;
import com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs.ChromeCustomTabsOptions; import com.pichillilorenzo.flutter_inappbrowser.ChromeCustomTabs.CustomTabActivityHelper;
import com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs.CustomTabActivityHelper;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable; import java.io.Serializable;
import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
...@@ -67,13 +58,11 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { ...@@ -67,13 +58,11 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
public static Registrar registrar; public static Registrar registrar;
public Activity activity; public Activity activity;
public static MethodChannel channel; public static MethodChannel channel;
public static Map<String, WebViewActivity> webViewActivities = new HashMap<>(); public static Map<String, InAppBrowserActivity> webViewActivities = new HashMap<>();
public static Map<String, ChromeCustomTabsActivity> chromeCustomTabsActivities = new HashMap<>(); public static Map<String, ChromeCustomTabsActivity> chromeCustomTabsActivities = new HashMap<>();
protected static final String LOG_TAG = "IABFlutterPlugin"; protected static final String LOG_TAG = "IABFlutterPlugin";
static final String ANDROID_ASSET_URL = "file:///android_asset/";
public InAppBrowserFlutterPlugin(Registrar r, Activity activity) { public InAppBrowserFlutterPlugin(Registrar r, Activity activity) {
registrar = r; registrar = r;
this.activity = activity; this.activity = activity;
...@@ -84,8 +73,15 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { ...@@ -84,8 +73,15 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
* Plugin registration. * Plugin registration.
*/ */
public static void registerWith(Registrar registrar) { public static void registerWith(Registrar registrar) {
Activity activity = registrar.activity();
final MethodChannel channel = new MethodChannel(registrar.messenger(), "com.pichillilorenzo/flutter_inappbrowser"); final MethodChannel channel = new MethodChannel(registrar.messenger(), "com.pichillilorenzo/flutter_inappbrowser");
channel.setMethodCallHandler(new InAppBrowserFlutterPlugin(registrar, registrar.activity())); channel.setMethodCallHandler(new InAppBrowserFlutterPlugin(registrar, activity));
registrar
.platformViewRegistry()
.registerViewFactory(
"com.pichillilorenzo/flutter_inappwebview", new FlutterWebViewFactory(registrar, activity));
} }
@Override @Override
...@@ -113,48 +109,32 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { ...@@ -113,48 +109,32 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
final String uuidFallback = (String) call.argument("uuidFallback"); final String uuidFallback = (String) call.argument("uuidFallback");
final ChromeCustomTabsOptions options = new ChromeCustomTabsOptions(); final HashMap<String, Object> options = (HashMap<String, Object>) call.argument("options");
options.parse((HashMap<String, Object>) call.argument("options"));
final InAppBrowserOptions optionsFallback = new InAppBrowserOptions(); final HashMap<String, Object> optionsFallback = (HashMap<String, Object>) call.argument("optionsFallback");
optionsFallback.parse((HashMap<String, Object>) call.argument("optionsFallback"));
open(uuid, uuidFallback, url_final, options, headers, true, optionsFallback, result); open(uuid, uuidFallback, url_final, options, headers, true, optionsFallback, result);
} else { } else {
String url = url_final; String url = url_final;
final InAppBrowserOptions options = new InAppBrowserOptions(); final HashMap<String, Object> options = (HashMap<String, Object>) call.argument("options");
options.parse((HashMap<String, Object>) call.argument("options"));
if (options.isLocalFile) { final boolean isLocalFile = (boolean) call.argument("isLocalFile");
// check if the asset file exists final boolean openWithSystemBrowser = (boolean) call.argument("openWithSystemBrowser");
String key = registrar.lookupKeyForAsset(url);
AssetManager mg = registrar.activeContext().getResources().getAssets();
InputStream is = null;
boolean assetExists = false;
try {
is = mg.open(key);
assetExists = true;
} catch (IOException ex) {
} finally { if (isLocalFile) {
if (is != null) { // check if the asset file exists
try { try {
is.close(); url = Util.getUrlAsset(registrar, url);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} result.error(LOG_TAG, url + " asset file cannot be found!", e);
}
}
if (!assetExists) {
result.error(LOG_TAG, key + " asset file cannot be found!", null);
return; return;
} }
url = ANDROID_ASSET_URL + key;
} }
// SYSTEM // SYSTEM
if (options.openWithSystemBrowser) { if (openWithSystemBrowser) {
Log.d(LOG_TAG, "in system"); Log.d(LOG_TAG, "in system");
openExternal(url, result); openExternal(url, result);
} }
...@@ -257,9 +237,9 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { ...@@ -257,9 +237,9 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
switch (optionsType){ switch (optionsType){
case "InAppBrowserOptions": case "InAppBrowserOptions":
InAppBrowserOptions inAppBrowserOptions = new InAppBrowserOptions(); InAppBrowserOptions inAppBrowserOptions = new InAppBrowserOptions();
HashMap<String, Object> inAppBroeserOptionsMap = (HashMap<String, Object>) call.argument("options"); HashMap<String, Object> inAppBrowserOptionsMap = (HashMap<String, Object>) call.argument("options");
inAppBrowserOptions.parse(inAppBroeserOptionsMap); inAppBrowserOptions.parse(inAppBrowserOptionsMap);
setOptions(uuid, inAppBrowserOptions, inAppBroeserOptionsMap); setOptions(uuid, inAppBrowserOptions, inAppBrowserOptionsMap);
break; break;
default: default:
result.error(LOG_TAG, "Options " + optionsType + " not available.", null); result.error(LOG_TAG, "Options " + optionsType + " not available.", null);
...@@ -276,85 +256,10 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { ...@@ -276,85 +256,10 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
} }
/**
* Inject an object (script or style) into the InAppBrowserFlutterPlugin WebView.
* <p>
* This is a helper method for the inject{Script|Style}{Code|File} API calls, which
* provides a consistent method for injecting JavaScript code into the document.
* <p>
* If a wrapper string is supplied, then the source string will be JSON-encoded (adding
* quotes) and wrapped using string formatting. (The wrapper string should have a single
* '%s' marker)
*
* @param uuid
* @param source The source object (filename or script/style text) to inject into
* the document.
* @param jsWrapper A JavaScript string to wrap the source string in, so that the object
* is properly injected, or null if the source string is JavaScript text
* @param result
*/
private void injectDeferredObject(String uuid, String source, String jsWrapper, final Result result) { private void injectDeferredObject(String uuid, String source, String jsWrapper, final Result result) {
final WebViewActivity webViewActivity = webViewActivities.get(uuid); final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) { if (inAppBrowserActivity != null) {
String scriptToInject; inAppBrowserActivity.injectDeferredObject(source, jsWrapper, result);
if (jsWrapper != null) {
org.json.JSONArray jsonEsc = new org.json.JSONArray();
jsonEsc.put(source);
String jsonRepr = jsonEsc.toString();
String jsonSourceString = jsonRepr.substring(1, jsonRepr.length() - 1);
scriptToInject = String.format(jsWrapper, jsonSourceString);
} else {
scriptToInject = source;
}
final String finalScriptToInject = scriptToInject;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
// This action will have the side-effect of blurring the currently focused element
webViewActivity.webView.loadUrl("javascript:" + finalScriptToInject);
} else {
webViewActivity.webView.evaluateJavascript(finalScriptToInject, new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
if (result == null)
return;
JsonReader reader = new JsonReader(new StringReader(s));
// Must set lenient to parse single values
reader.setLenient(true);
try {
String msg;
if (reader.peek() == JsonToken.STRING) {
msg = reader.nextString();
JsonReader reader2 = new JsonReader(new StringReader(msg));
reader2.setLenient(true);
if (reader2.peek() == JsonToken.STRING)
msg = reader2.nextString();
result.success(msg);
} else {
result.success("");
}
} catch (IOException e) {
Log.e(LOG_TAG, "IOException", e);
} finally {
try {
reader.close();
} catch (IOException e) {
// NOOP
}
}
}
});
}
}
});
} else { } else {
Log.d(LOG_TAG, "Can't inject code into the system browser"); Log.d(LOG_TAG, "Can't inject code into the system browser");
} }
...@@ -435,14 +340,14 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { ...@@ -435,14 +340,14 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
} }
} }
public void open(String uuid, String uuidFallback, String url, Options options, Map<String, String> headers, boolean useChromeSafariBrowser, InAppBrowserOptions optionsFallback, Result result) { public void open(String uuid, String uuidFallback, String url, HashMap<String, Object> options, Map<String, String> headers, boolean useChromeSafariBrowser, HashMap<String, Object> optionsFallback, Result result) {
Intent intent = null; Intent intent = null;
Bundle extras = new Bundle(); Bundle extras = new Bundle();
extras.putString("url", url); extras.putString("url", url);
extras.putString("uuid", uuid); extras.putString("uuid", uuid);
extras.putSerializable("options", options.getHashMap()); extras.putSerializable("options", options);
extras.putSerializable("headers", (Serializable) headers); extras.putSerializable("headers", (Serializable) headers);
if (useChromeSafariBrowser && CustomTabActivityHelper.isAvailable(activity)) { if (useChromeSafariBrowser && CustomTabActivityHelper.isAvailable(activity)) {
...@@ -454,15 +359,15 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { ...@@ -454,15 +359,15 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
// overwrite with extras fallback parameters // overwrite with extras fallback parameters
extras.putString("uuid", uuidFallback); extras.putString("uuid", uuidFallback);
if (optionsFallback != null) if (optionsFallback != null)
extras.putSerializable("options", optionsFallback.getHashMap()); extras.putSerializable("options", optionsFallback);
else else
extras.putSerializable("options", (new InAppBrowserOptions()).getHashMap()); extras.putSerializable("options", (new InAppBrowserOptions()).getHashMap());
extras.putSerializable("headers", (Serializable) headers); extras.putSerializable("headers", (Serializable) headers);
intent = new Intent(activity, WebViewActivity.class); intent = new Intent(activity, InAppBrowserActivity.class);
} }
// native webview // native webview
else if (!useChromeSafariBrowser) { else if (!useChromeSafariBrowser) {
intent = new Intent(activity, WebViewActivity.class); intent = new Intent(activity, InAppBrowserActivity.class);
} }
else { else {
Log.d(LOG_TAG, "No WebView fallback declared."); Log.d(LOG_TAG, "No WebView fallback declared.");
...@@ -479,93 +384,93 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { ...@@ -479,93 +384,93 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
} }
public void loadUrl(String uuid, String url, Map<String, String> headers, Result result) { public void loadUrl(String uuid, String url, Map<String, String> headers, Result result) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) { if (inAppBrowserActivity != null) {
if (headers != null) if (headers != null)
webViewActivity.loadUrl(url, headers, result); inAppBrowserActivity.loadUrl(url, headers, result);
else else
webViewActivity.loadUrl(url, result); inAppBrowserActivity.loadUrl(url, result);
} }
} }
public void loadFile(String uuid, String url, Map<String, String> headers, Result result) { public void loadFile(String uuid, String url, Map<String, String> headers, Result result) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) { if (inAppBrowserActivity != null) {
if (headers != null) if (headers != null)
webViewActivity.loadFile(url, headers, result); inAppBrowserActivity.loadFile(url, headers, result);
else else
webViewActivity.loadFile(url, result); inAppBrowserActivity.loadFile(url, result);
} }
} }
public void show(String uuid) { public void show(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
webViewActivity.show(); inAppBrowserActivity.show();
} }
public void hide(String uuid) { public void hide(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
webViewActivity.hide(); inAppBrowserActivity.hide();
} }
public void reload(String uuid) { public void reload(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
webViewActivity.reload(); inAppBrowserActivity.reload();
} }
public boolean isLoading(String uuid) { public boolean isLoading(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
return webViewActivity.isLoading(); return inAppBrowserActivity.isLoading();
return false; return false;
} }
public boolean isHidden(String uuid) { public boolean isHidden(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
return webViewActivity.isHidden; return inAppBrowserActivity.isHidden;
return false; return false;
} }
public void stopLoading(String uuid) { public void stopLoading(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
webViewActivity.stopLoading(); inAppBrowserActivity.stopLoading();
} }
public void goBack(String uuid) { public void goBack(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
webViewActivity.goBack(); inAppBrowserActivity.goBack();
} }
public boolean canGoBack(String uuid) { public boolean canGoBack(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
return webViewActivity.canGoBack(); return inAppBrowserActivity.canGoBack();
return false; return false;
} }
public void goForward(String uuid) { public void goForward(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
webViewActivity.goForward(); inAppBrowserActivity.goForward();
} }
public boolean canGoForward(String uuid) { public boolean canGoForward(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
return webViewActivity.canGoForward(); return inAppBrowserActivity.canGoForward();
return false; return false;
} }
public static void close(final String uuid, final Result result) { public static void close(final String uuid, final Result result) {
final WebViewActivity webViewActivity = webViewActivities.get(uuid); final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) { if (inAppBrowserActivity != null) {
registrar.activity().runOnUiThread(new Runnable() { registrar.activity().runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -576,23 +481,23 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { ...@@ -576,23 +481,23 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
// The JS protects against multiple calls, so this should happen only when // The JS protects against multiple calls, so this should happen only when
// close() is called by other native code. // close() is called by other native code.
if (webViewActivity == null) { if (inAppBrowserActivity == null) {
if (result != null) { if (result != null) {
result.success(true); result.success(true);
} }
return; return;
} }
webViewActivity.webView.setWebViewClient(new WebViewClient() { inAppBrowserActivity.webView.setWebViewClient(new WebViewClient() {
// NB: wait for about:blank before dismissing // NB: wait for about:blank before dismissing
public void onPageFinished(WebView view, String url) { public void onPageFinished(WebView view, String url) {
webViewActivity.close(); inAppBrowserActivity.close();
} }
}); });
// NB: From SDK 19: "If you call methods on WebView from any thread // NB: From SDK 19: "If you call methods on WebView from any thread
// other than your app's UI thread, it can cause unexpected results." // other than your app's UI thread, it can cause unexpected results."
// http://developer.android.com/guide/webapps/migrating.html#Threads // http://developer.android.com/guide/webapps/migrating.html#Threads
webViewActivity.webView.loadUrl("about:blank"); inAppBrowserActivity.webView.loadUrl("about:blank");
if (result != null) { if (result != null) {
result.success(true); result.success(true);
} }
...@@ -605,22 +510,22 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { ...@@ -605,22 +510,22 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
} }
public byte[] takeScreenshot(String uuid) { public byte[] takeScreenshot(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
return webViewActivity.takeScreenshot(); return inAppBrowserActivity.takeScreenshot();
return null; return null;
} }
public void setOptions(String uuid, InAppBrowserOptions options, HashMap<String, Object> optionsMap) { public void setOptions(String uuid, InAppBrowserOptions options, HashMap<String, Object> optionsMap) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
webViewActivity.setOptions(options, optionsMap); inAppBrowserActivity.setOptions(options, optionsMap);
} }
public HashMap<String, Object> getOptions(String uuid) { public HashMap<String, Object> getOptions(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid); InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (webViewActivity != null) if (inAppBrowserActivity != null)
return webViewActivity.getOptions(); return inAppBrowserActivity.getOptions();
return null; return null;
} }
......
...@@ -4,29 +4,13 @@ public class InAppBrowserOptions extends Options { ...@@ -4,29 +4,13 @@ public class InAppBrowserOptions extends Options {
static final String LOG_TAG = "InAppBrowserOptions"; static final String LOG_TAG = "InAppBrowserOptions";
public boolean useShouldOverrideUrlLoading = false;
public boolean useOnLoadResource = false;
public boolean openWithSystemBrowser = false;
public boolean clearCache = false;
public String userAgent = "";
public boolean javaScriptEnabled = true;
public boolean javaScriptCanOpenWindowsAutomatically = false;
public boolean hidden = false; public boolean hidden = false;
public boolean toolbarTop = true; public boolean toolbarTop = true;
public String toolbarTopBackgroundColor = ""; public String toolbarTopBackgroundColor = "";
public String toolbarTopFixedTitle = ""; public String toolbarTopFixedTitle = "";
public boolean hideUrlBar = false; public boolean hideUrlBar = false;
public boolean mediaPlaybackRequiresUserGesture = true;
public boolean isLocalFile = false;
public boolean hideTitleBar = false; public boolean hideTitleBar = false;
public boolean closeOnCannotGoBack = true; public boolean closeOnCannotGoBack = true;
public boolean clearSessionCache = false;
public boolean builtInZoomControls = false;
public boolean supportZoom = true;
public boolean databaseEnabled = false;
public boolean domStorageEnabled = false;
public boolean useWideViewPort = true;
public boolean safeBrowsingEnabled = true;
public boolean progressBar = true; public boolean progressBar = true;
} }
package com.pichillilorenzo.flutter_inappbrowser; package com.pichillilorenzo.flutter_inappbrowser.InAppWebView;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.util.Log;
import android.view.View; import android.view.View;
import android.webkit.ConsoleMessage; import android.webkit.ConsoleMessage;
import android.webkit.ValueCallback; import android.webkit.ValueCallback;
import android.webkit.WebChromeClient; import android.webkit.WebChromeClient;
import android.webkit.WebView; import android.webkit.WebView;
import com.pichillilorenzo.flutter_inappbrowser.FlutterWebView;
import com.pichillilorenzo.flutter_inappbrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappbrowser.InAppBrowserFlutterPlugin;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class InAppBrowserWebChromeClient extends WebChromeClient { import io.flutter.plugin.common.MethodChannel;
public class InAppWebChromeClient extends WebChromeClient {
protected static final String LOG_TAG = "IABWebChromeClient"; protected static final String LOG_TAG = "IABWebChromeClient";
private WebViewActivity activity; private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity;
private ValueCallback<Uri[]> mUploadMessageArray; private ValueCallback<Uri[]> mUploadMessageArray;
private ValueCallback<Uri> mUploadMessage; private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE = 1; private final static int FILECHOOSER_RESULTCODE = 1;
public InAppBrowserWebChromeClient(WebViewActivity activity) { public InAppWebChromeClient(Object obj) {
super(); super();
this.activity = activity; if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
else if (obj instanceof FlutterWebView)
this.flutterWebView = (FlutterWebView) obj;
} }
@Override @Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) { public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid); if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("sourceURL", consoleMessage.sourceId()); obj.put("sourceURL", consoleMessage.sourceId());
obj.put("lineNumber", consoleMessage.lineNumber()); obj.put("lineNumber", consoleMessage.lineNumber());
obj.put("message", consoleMessage.message()); obj.put("message", consoleMessage.message());
obj.put("messageLevel", consoleMessage.messageLevel().toString()); obj.put("messageLevel", consoleMessage.messageLevel().toString());
InAppBrowserFlutterPlugin.channel.invokeMethod("onConsoleMessage", obj); getChannel().invokeMethod("onConsoleMessage", obj);
return true; return true;
} }
@Override @Override
public void onProgressChanged(WebView view, int progress) { public void onProgressChanged(WebView view, int progress) {
if (activity.progressBar != null) { if (inAppBrowserActivity != null && inAppBrowserActivity.progressBar != null) {
activity.progressBar.setVisibility(View.VISIBLE); inAppBrowserActivity.progressBar.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
activity.progressBar.setProgress(progress, true); inAppBrowserActivity.progressBar.setProgress(progress, true);
} else { } else {
activity.progressBar.setProgress(progress); inAppBrowserActivity.progressBar.setProgress(progress);
} }
if (progress == 100) { if (progress == 100) {
activity.progressBar.setVisibility(View.GONE); inAppBrowserActivity.progressBar.setVisibility(View.GONE);
} }
} }
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("progress", progress);
getChannel().invokeMethod("onProgressChanged", obj);
super.onProgressChanged(view, progress); super.onProgressChanged(view, progress);
} }
@Override @Override
public void onReceivedTitle(WebView view, String title) { public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title); super.onReceivedTitle(view, title);
if (activity.actionBar != null && activity.options.toolbarTopFixedTitle.isEmpty()) if (inAppBrowserActivity != null && inAppBrowserActivity.actionBar != null && inAppBrowserActivity.options.toolbarTopFixedTitle.isEmpty())
activity.actionBar.setTitle(title); inAppBrowserActivity.actionBar.setTitle(title);
} }
@Override @Override
...@@ -76,7 +93,7 @@ public class InAppBrowserWebChromeClient extends WebChromeClient { ...@@ -76,7 +93,7 @@ public class InAppBrowserWebChromeClient extends WebChromeClient {
Intent i = new Intent(Intent.ACTION_GET_CONTENT); Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE); i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*"); i.setType("image/*");
activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE); ((inAppBrowserActivity != null) ? inAppBrowserActivity : flutterWebView.activity).startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
} }
...@@ -86,7 +103,7 @@ public class InAppBrowserWebChromeClient extends WebChromeClient { ...@@ -86,7 +103,7 @@ public class InAppBrowserWebChromeClient extends WebChromeClient {
Intent i = new Intent(Intent.ACTION_GET_CONTENT); Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE); i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*"); i.setType("*/*");
activity.startActivityForResult( ((inAppBrowserActivity != null) ? inAppBrowserActivity : flutterWebView.activity).startActivityForResult(
Intent.createChooser(i, "File Browser"), Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE); FILECHOOSER_RESULTCODE);
} }
...@@ -97,7 +114,7 @@ public class InAppBrowserWebChromeClient extends WebChromeClient { ...@@ -97,7 +114,7 @@ public class InAppBrowserWebChromeClient extends WebChromeClient {
Intent i = new Intent(Intent.ACTION_GET_CONTENT); Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE); i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*"); i.setType("image/*");
activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE); ((inAppBrowserActivity != null) ? inAppBrowserActivity : flutterWebView.activity).startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
} }
...@@ -120,7 +137,11 @@ public class InAppBrowserWebChromeClient extends WebChromeClient { ...@@ -120,7 +137,11 @@ public class InAppBrowserWebChromeClient extends WebChromeClient {
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent); chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser"); chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
activity.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE); ((inAppBrowserActivity != null) ? inAppBrowserActivity : flutterWebView.activity).startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
return true; return true;
} }
private MethodChannel getChannel() {
return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.channel : flutterWebView.channel;
}
} }
package com.pichillilorenzo.flutter_inappbrowser.InAppWebView;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Picture;
import android.os.Build;
import android.util.AttributeSet;
import android.util.JsonReader;
import android.util.JsonToken;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.ValueCallback;
import android.webkit.WebSettings;
import android.webkit.WebView;
import com.pichillilorenzo.flutter_inappbrowser.FlutterWebView;
import com.pichillilorenzo.flutter_inappbrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappbrowser.JavaScriptBridgeInterface;
import com.pichillilorenzo.flutter_inappbrowser.Util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.MethodChannel;
import okhttp3.Cache;
import okhttp3.OkHttpClient;
import static com.pichillilorenzo.flutter_inappbrowser.InAppBrowserFlutterPlugin.registrar;
public class InAppWebView extends WebView {
static final String LOG_TAG = "InAppWebView";
public InAppBrowserActivity inAppBrowserActivity;
public FlutterWebView flutterWebView;
int id;
InAppWebViewClient inAppWebViewClient;
InAppWebChromeClient inAppWebChromeClient;
public InAppWebViewOptions options;
public boolean isLoading = false;
OkHttpClient httpClient;
int okHttpClientCacheSize = 10 * 1024 * 1024; // 10MB
static final String consoleLogJS = "(function() {" +
" var oldLogs = {" +
" 'log': console.log," +
" 'debug': console.debug," +
" 'error': console.error," +
" 'info': console.info," +
" 'warn': console.warn" +
" };" +
" for (var k in oldLogs) {" +
" (function(oldLog) {" +
" console[oldLog] = function() {" +
" var message = '';" +
" for (var i in arguments) {" +
" if (message == '') {" +
" message += arguments[i];" +
" }" +
" else {" +
" message += ' ' + arguments[i];" +
" }" +
" }" +
" oldLogs[oldLog].call(console, message);" +
" }" +
" })(k);" +
" }" +
"})();";
public InAppWebView(Context context) {
super(context);
}
public InAppWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public InAppWebView(Context context, AttributeSet attrs, int defaultStyle) {
super(context, attrs, defaultStyle);
}
public InAppWebView(Context context, Object obj, int id, InAppWebViewOptions options) {
super(context);
if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
else if (obj instanceof FlutterWebView)
this.flutterWebView = (FlutterWebView) obj;
this.id = id;
this.options = options;
}
public void prepare() {
boolean isFromInAppBrowserActivity = inAppBrowserActivity != null;
httpClient = new OkHttpClient().newBuilder().cache(new Cache(getContext().getCacheDir(), okHttpClientCacheSize)).build();
addJavascriptInterface(new JavaScriptBridgeInterface((isFromInAppBrowserActivity) ? inAppBrowserActivity : flutterWebView), JavaScriptBridgeInterface.name);
inAppWebChromeClient = new InAppWebChromeClient((isFromInAppBrowserActivity) ? inAppBrowserActivity : flutterWebView);
setWebChromeClient(inAppWebChromeClient);
inAppWebViewClient = new InAppWebViewClient((isFromInAppBrowserActivity) ? inAppBrowserActivity : flutterWebView);
setWebViewClient(inAppWebViewClient);
// final Activity activity = this;
//
// webView.setDownloadListener(new DownloadListener() {
// @Override
// public void onDownloadStart(final String url, final String userAgent,
// final String contentDisposition, final String mimetype,
// final long contentLength) {
//
// RequestPermissionHandler.checkAndRun(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, RequestPermissionHandler.REQUEST_CODE_WRITE_EXTERNAL_STORAGE, new Runnable(){
// @Override
// public void run(){
// DownloadManager.Request request = new DownloadManager.Request(
// Uri.parse(url));
//
// final String filename = URLUtil.guessFileName(url, contentDisposition, mimetype);
// request.allowScanningByMediaScanner();
// request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //Notify client once download is completed!
// request.setVisibleInDownloadsUi(true);
// request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
// DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
// if (dm != null) {
// dm.enqueue(request);
// Toast.makeText(getApplicationContext(), "Downloading File: " + filename, //To notify the Client that the file is being downloaded
// Toast.LENGTH_LONG).show();
// }
// else {
// Toast.makeText(getApplicationContext(), "Cannot Download File: " + filename, //To notify the Client that the file cannot be downloaded
// Toast.LENGTH_LONG).show();
// }
// }
// });
// }
// });
WebSettings settings = getSettings();
settings.setJavaScriptEnabled(options.javaScriptEnabled);
settings.setJavaScriptCanOpenWindowsAutomatically(options.javaScriptCanOpenWindowsAutomatically);
settings.setBuiltInZoomControls(options.builtInZoomControls);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
settings.setSafeBrowsingEnabled(options.safeBrowsingEnabled);
settings.setMediaPlaybackRequiresUserGesture(options.mediaPlaybackRequiresUserGesture);
settings.setDatabaseEnabled(options.databaseEnabled);
settings.setDomStorageEnabled(options.domStorageEnabled);
if (!options.userAgent.isEmpty())
settings.setUserAgentString(options.userAgent);
if (options.clearCache)
clearAllCache();
else if (options.clearSessionCache)
CookieManager.getInstance().removeSessionCookie();
// Enable Thirdparty Cookies on >=Android 5.0 device
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
CookieManager.getInstance().setAcceptThirdPartyCookies(this, true);
settings.setLoadWithOverviewMode(true);
settings.setUseWideViewPort(options.useWideViewPort);
settings.setSupportZoom(options.supportZoom);
// fix webview scaling
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
else
settings.setTextZoom(100);
}
public void loadUrl(String url, MethodChannel.Result result) {
if (!url.isEmpty()) {
loadUrl(url);
} else {
result.error(LOG_TAG, "url is empty", null);
}
}
public void loadUrl(String url, Map<String, String> headers, MethodChannel.Result result) {
if (!url.isEmpty()) {
loadUrl(url, headers);
} else {
result.error(LOG_TAG, "url is empty", null);
}
}
public void loadFile(String url, MethodChannel.Result result) {
try {
url = Util.getUrlAsset(registrar, url);
} catch (IOException e) {
result.error(LOG_TAG, url + " asset file cannot be found!", e);
return;
}
if (!url.isEmpty()) {
loadUrl(url);
} else {
result.error(LOG_TAG, "url is empty", null);
}
}
public void loadFile(String url, Map<String, String> headers, MethodChannel.Result result) {
try {
url = Util.getUrlAsset(registrar, url);
} catch (IOException e) {
result.error(LOG_TAG, url + " asset file cannot be found!", e);
return;
}
if (!url.isEmpty()) {
loadUrl(url, headers);
} else {
result.error(LOG_TAG, "url is empty", null);
}
}
public boolean isLoading() {
return isLoading;
}
private void clearCookies() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean aBoolean) {
}
});
} else {
CookieManager.getInstance().removeAllCookie();
}
}
public void clearAllCache() {
clearCache(true);
clearCookies();
clearFormData();
}
public byte[] takeScreenshot() {
Picture picture = capturePicture();
Bitmap b = Bitmap.createBitmap( getWidth(),
getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
picture.draw(c);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
b.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return byteArrayOutputStream.toByteArray();
}
public void setOptions(InAppWebViewOptions newOptions, HashMap<String, Object> newOptionsMap) {
WebSettings settings = getSettings();
if (newOptionsMap.get("javaScriptEnabled") != null && options.javaScriptEnabled != newOptions.javaScriptEnabled)
settings.setJavaScriptEnabled(newOptions.javaScriptEnabled);
if (newOptionsMap.get("javaScriptCanOpenWindowsAutomatically") != null && options.javaScriptCanOpenWindowsAutomatically != newOptions.javaScriptCanOpenWindowsAutomatically)
settings.setJavaScriptCanOpenWindowsAutomatically(newOptions.javaScriptCanOpenWindowsAutomatically);
if (newOptionsMap.get("builtInZoomControls") != null && options.builtInZoomControls != newOptions.builtInZoomControls)
settings.setBuiltInZoomControls(newOptions.builtInZoomControls);
if (newOptionsMap.get("safeBrowsingEnabled") != null && options.safeBrowsingEnabled != newOptions.safeBrowsingEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
settings.setSafeBrowsingEnabled(newOptions.safeBrowsingEnabled);
if (newOptionsMap.get("mediaPlaybackRequiresUserGesture") != null && options.mediaPlaybackRequiresUserGesture != newOptions.mediaPlaybackRequiresUserGesture)
settings.setMediaPlaybackRequiresUserGesture(newOptions.mediaPlaybackRequiresUserGesture);
if (newOptionsMap.get("databaseEnabled") != null && options.databaseEnabled != newOptions.databaseEnabled)
settings.setDatabaseEnabled(newOptions.databaseEnabled);
if (newOptionsMap.get("domStorageEnabled") != null && options.domStorageEnabled != newOptions.domStorageEnabled)
settings.setDomStorageEnabled(newOptions.domStorageEnabled);
if (newOptionsMap.get("userAgent") != null && options.userAgent != newOptions.userAgent && !newOptions.userAgent.isEmpty())
settings.setUserAgentString(newOptions.userAgent);
if (newOptionsMap.get("clearCache") != null && newOptions.clearCache)
clearAllCache();
else if (newOptionsMap.get("clearSessionCache") != null && newOptions.clearSessionCache)
CookieManager.getInstance().removeSessionCookie();
if (newOptionsMap.get("useWideViewPort") != null && options.useWideViewPort != newOptions.useWideViewPort)
settings.setUseWideViewPort(newOptions.useWideViewPort);
if (newOptionsMap.get("supportZoom") != null && options.supportZoom != newOptions.supportZoom)
settings.setSupportZoom(newOptions.supportZoom);
options = newOptions;
}
public HashMap<String, Object> getOptions() {
return (options != null) ? options.getHashMap() : null;
}
public void injectDeferredObject(String source, String jsWrapper, final MethodChannel.Result result) {
String scriptToInject;
if (jsWrapper != null) {
org.json.JSONArray jsonEsc = new org.json.JSONArray();
jsonEsc.put(source);
String jsonRepr = jsonEsc.toString();
String jsonSourceString = jsonRepr.substring(1, jsonRepr.length() - 1);
scriptToInject = String.format(jsWrapper, jsonSourceString);
} else {
scriptToInject = source;
}
final String finalScriptToInject = scriptToInject;
( (inAppBrowserActivity != null) ? inAppBrowserActivity : flutterWebView.activity ).runOnUiThread(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
// This action will have the side-effect of blurring the currently focused element
loadUrl("javascript:" + finalScriptToInject);
} else {
evaluateJavascript(finalScriptToInject, new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
if (result == null)
return;
JsonReader reader = new JsonReader(new StringReader(s));
// Must set lenient to parse single values
reader.setLenient(true);
try {
String msg;
if (reader.peek() == JsonToken.STRING) {
msg = reader.nextString();
JsonReader reader2 = new JsonReader(new StringReader(msg));
reader2.setLenient(true);
if (reader2.peek() == JsonToken.STRING)
msg = reader2.nextString();
result.success(msg);
} else {
result.success("");
}
} catch (IOException e) {
Log.e(LOG_TAG, "IOException", e);
} finally {
try {
reader.close();
} catch (IOException e) {
// NOOP
}
}
}
});
}
}
});
}
}
package com.pichillilorenzo.flutter_inappbrowser; package com.pichillilorenzo.flutter_inappbrowser.InAppWebView;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
...@@ -16,6 +16,11 @@ import android.webkit.WebResourceResponse; ...@@ -16,6 +16,11 @@ import android.webkit.WebResourceResponse;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import com.pichillilorenzo.flutter_inappbrowser.FlutterWebView;
import com.pichillilorenzo.flutter_inappbrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappbrowser.InAppBrowserFlutterPlugin;
import com.pichillilorenzo.flutter_inappbrowser.JavaScriptBridgeInterface;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
...@@ -27,16 +32,24 @@ import io.flutter.plugin.common.MethodChannel; ...@@ -27,16 +32,24 @@ import io.flutter.plugin.common.MethodChannel;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
public class InAppBrowserWebViewClient extends WebViewClient { public class InAppWebViewClient extends WebViewClient {
protected static final String LOG_TAG = "IABWebViewClient"; protected static final String LOG_TAG = "IABWebViewClient";
private WebViewActivity activity; private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity;
Map<Integer, String> statusCodeMapping = new HashMap<Integer, String>(); Map<Integer, String> statusCodeMapping = new HashMap<Integer, String>();
long startPageTime = 0; long startPageTime = 0;
public InAppBrowserWebViewClient(WebViewActivity activity) { public InAppWebViewClient(Object obj) {
super(); super();
this.activity = activity; if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
else if (obj instanceof FlutterWebView)
this.flutterWebView = (FlutterWebView) obj;
prepareStatusCodeMapping();
}
private void prepareStatusCodeMapping () {
statusCodeMapping.put(100, "Continue"); statusCodeMapping.put(100, "Continue");
statusCodeMapping.put(101, "Switching Protocols"); statusCodeMapping.put(101, "Switching Protocols");
statusCodeMapping.put(200, "OK"); statusCodeMapping.put(200, "OK");
...@@ -90,11 +103,12 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -90,11 +103,12 @@ public class InAppBrowserWebViewClient extends WebViewClient {
@Override @Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) { public boolean shouldOverrideUrlLoading(WebView webView, String url) {
if (activity.options.useShouldOverrideUrlLoading) { if (((inAppBrowserActivity != null) ? inAppBrowserActivity.webView : flutterWebView.webView).options.useShouldOverrideUrlLoading) {
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid); if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url); obj.put("url", url);
InAppBrowserFlutterPlugin.channel.invokeMethod("shouldOverrideUrlLoading", obj); getChannel().invokeMethod("shouldOverrideUrlLoading", obj);
return true; return true;
} }
...@@ -102,7 +116,7 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -102,7 +116,7 @@ public class InAppBrowserWebViewClient extends WebViewClient {
try { try {
Intent intent = new Intent(Intent.ACTION_DIAL); Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(url)); intent.setData(Uri.parse(url));
activity.startActivity(intent); ((inAppBrowserActivity != null) ? inAppBrowserActivity : flutterWebView.activity).startActivity(intent);
return true; return true;
} catch (android.content.ActivityNotFoundException e) { } catch (android.content.ActivityNotFoundException e) {
Log.e(LOG_TAG, "Error dialing " + url + ": " + e.toString()); Log.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
...@@ -111,7 +125,7 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -111,7 +125,7 @@ public class InAppBrowserWebViewClient extends WebViewClient {
try { try {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url)); intent.setData(Uri.parse(url));
activity.startActivity(intent); ((inAppBrowserActivity != null) ? inAppBrowserActivity : flutterWebView.activity).startActivity(intent);
return true; return true;
} catch (android.content.ActivityNotFoundException e) { } catch (android.content.ActivityNotFoundException e) {
Log.e(LOG_TAG, "Error with " + url + ": " + e.toString()); Log.e(LOG_TAG, "Error with " + url + ": " + e.toString());
...@@ -142,7 +156,7 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -142,7 +156,7 @@ public class InAppBrowserWebViewClient extends WebViewClient {
intent.setData(Uri.parse("sms:" + address)); intent.setData(Uri.parse("sms:" + address));
intent.putExtra("address", address); intent.putExtra("address", address);
intent.setType("vnd.android-dir/mms-sms"); intent.setType("vnd.android-dir/mms-sms");
activity.startActivity(intent); ((inAppBrowserActivity != null) ? inAppBrowserActivity : flutterWebView.activity).startActivity(intent);
return true; return true;
} catch (android.content.ActivityNotFoundException e) { } catch (android.content.ActivityNotFoundException e) {
Log.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString()); Log.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
...@@ -167,23 +181,24 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -167,23 +181,24 @@ public class InAppBrowserWebViewClient extends WebViewClient {
startPageTime = System.currentTimeMillis(); startPageTime = System.currentTimeMillis();
activity.isLoading = true; ((inAppBrowserActivity != null) ? inAppBrowserActivity.webView : flutterWebView.webView).isLoading = true;
if (activity.searchView != null && !url.equals(activity.searchView.getQuery().toString())) { if (inAppBrowserActivity != null && inAppBrowserActivity.searchView != null && !url.equals(inAppBrowserActivity.searchView.getQuery().toString())) {
activity.searchView.setQuery(url, false); inAppBrowserActivity.searchView.setQuery(url, false);
} }
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid); if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url); obj.put("url", url);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadStart", obj); getChannel().invokeMethod("onLoadStart", obj);
} }
public void onPageFinished(WebView view, String url) { public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url); super.onPageFinished(view, url);
activity.isLoading = false; ((inAppBrowserActivity != null) ? inAppBrowserActivity.webView : flutterWebView.webView).isLoading = false;
// CB-10395 InAppBrowserFlutterPlugin's WebView not storing cookies reliable to local device storage // CB-10395 InAppBrowserFlutterPlugin's WebView not storing cookies reliable to local device storage
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
...@@ -197,38 +212,41 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -197,38 +212,41 @@ 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(WebViewActivity.consoleLogJS, null); view.evaluateJavascript(InAppWebView.consoleLogJS, null);
view.evaluateJavascript(JavaScriptBridgeInterface.flutterInAppBroserJSClass, null); view.evaluateJavascript(JavaScriptBridgeInterface.flutterInAppBroserJSClass, null);
} }
else { else {
view.loadUrl("javascript:"+WebViewActivity.consoleLogJS); view.loadUrl("javascript:"+InAppWebView.consoleLogJS);
view.loadUrl("javascript:"+JavaScriptBridgeInterface.flutterInAppBroserJSClass); view.loadUrl("javascript:"+JavaScriptBridgeInterface.flutterInAppBroserJSClass);
} }
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid); if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url); obj.put("url", url);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadStop", obj); getChannel().invokeMethod("onLoadStop", obj);
} }
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl); super.onReceivedError(view, errorCode, description, failingUrl);
activity.isLoading = false; ((inAppBrowserActivity != null) ? inAppBrowserActivity.webView : flutterWebView.webView).isLoading = false;
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid); if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", failingUrl); obj.put("url", failingUrl);
obj.put("code", errorCode); obj.put("code", errorCode);
obj.put("message", description); obj.put("message", description);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadError", obj); getChannel().invokeMethod("onLoadError", obj);
} }
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error); super.onReceivedSslError(view, handler, error);
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid); if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", error.getUrl()); obj.put("url", error.getUrl());
obj.put("code", error.getPrimaryError()); obj.put("code", error.getPrimaryError());
String message; String message;
...@@ -254,7 +272,7 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -254,7 +272,7 @@ public class InAppBrowserWebViewClient extends WebViewClient {
break; break;
} }
obj.put("message", "SslError: " + message); obj.put("message", "SslError: " + message);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadError", obj); getChannel().invokeMethod("onLoadError", obj);
handler.cancel(); handler.cancel();
} }
...@@ -271,8 +289,8 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -271,8 +289,8 @@ 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") ||
if (!request.getMethod().toLowerCase().equals("get") || !activity.options.useOnLoadResource) { !(((inAppBrowserActivity != null) ? inAppBrowserActivity.webView : flutterWebView.webView).options.useOnLoadResource)) {
return null; return null;
} }
...@@ -282,7 +300,7 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -282,7 +300,7 @@ public class InAppBrowserWebViewClient extends WebViewClient {
Request mRequest = new Request.Builder().url(url).build(); Request mRequest = new Request.Builder().url(url).build();
long startResourceTime = System.currentTimeMillis(); long startResourceTime = System.currentTimeMillis();
Response response = activity.httpClient.newCall(mRequest).execute(); Response response = ((inAppBrowserActivity != null) ? inAppBrowserActivity.webView : flutterWebView.webView).httpClient.newCall(mRequest).execute();
long startTime = startResourceTime - startPageTime; long startTime = startResourceTime - startPageTime;
startTime = (startTime < 0) ? 0 : startTime; startTime = (startTime < 0) ? 0 : startTime;
long duration = System.currentTimeMillis() - startResourceTime; long duration = System.currentTimeMillis() - startResourceTime;
...@@ -319,7 +337,8 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -319,7 +337,8 @@ public class InAppBrowserWebViewClient extends WebViewClient {
Map<String, Object> res = new HashMap<>(); Map<String, Object> res = new HashMap<>();
Map<String, Object> req = new HashMap<>(); Map<String, Object> req = new HashMap<>();
obj.put("uuid", activity.uuid); if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
byte[] dataBytes = response.body().bytes(); byte[] dataBytes = response.body().bytes();
InputStream dataStream = new ByteArrayInputStream(dataBytes); InputStream dataStream = new ByteArrayInputStream(dataBytes);
...@@ -338,7 +357,7 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -338,7 +357,7 @@ public class InAppBrowserWebViewClient extends WebViewClient {
obj.put("response", res); obj.put("response", res);
obj.put("request", req); obj.put("request", req);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadResource", obj); getChannel().invokeMethod("onLoadResource", obj);
return new WebResourceResponse( return new WebResourceResponse(
response.header("content-type", "text/plain").split(";")[0].trim(), response.header("content-type", "text/plain").split(";")[0].trim(),
...@@ -359,5 +378,8 @@ public class InAppBrowserWebViewClient extends WebViewClient { ...@@ -359,5 +378,8 @@ public class InAppBrowserWebViewClient extends WebViewClient {
return null; return null;
} }
private MethodChannel getChannel() {
return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.channel : flutterWebView.channel;
}
} }
package com.pichillilorenzo.flutter_inappbrowser.InAppWebView;
import com.pichillilorenzo.flutter_inappbrowser.Options;
public class InAppWebViewOptions extends Options {
static final String LOG_TAG = "InAppWebViewOptions";
public boolean useShouldOverrideUrlLoading = false;
public boolean useOnLoadResource = false;
public boolean clearCache = false;
public String userAgent = "";
public boolean javaScriptEnabled = true;
public boolean javaScriptCanOpenWindowsAutomatically = false;
public boolean mediaPlaybackRequiresUserGesture = true;
public boolean clearSessionCache = false;
public boolean builtInZoomControls = false;
public boolean supportZoom = true;
public boolean databaseEnabled = false;
public boolean domStorageEnabled = false;
public boolean useWideViewPort = true;
public boolean safeBrowsingEnabled = true;
}
...@@ -5,25 +5,36 @@ import android.webkit.JavascriptInterface; ...@@ -5,25 +5,36 @@ import android.webkit.JavascriptInterface;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import io.flutter.plugin.common.MethodChannel;
public class JavaScriptBridgeInterface { public class JavaScriptBridgeInterface {
private static final String LOG_TAG = "JSBridgeInterface"; private static final String LOG_TAG = "JSBridgeInterface";
static final String name = "flutter_inappbrowser"; public static final String name = "flutter_inappbrowser";
WebViewActivity activity; private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity;
static final String flutterInAppBroserJSClass = "window." + name + ".callHandler = function(handlerName, ...args) {" + public static final String flutterInAppBroserJSClass = "window." + name + ".callHandler = function(handlerName, ...args) {" +
"window." + name + "._callHandler(handlerName, JSON.stringify(args));" + "window." + name + "._callHandler(handlerName, JSON.stringify(args));" +
"}"; "}";
JavaScriptBridgeInterface(WebViewActivity a) { public JavaScriptBridgeInterface(Object obj) {
activity = a; if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
else if (obj instanceof FlutterWebView)
this.flutterWebView = (FlutterWebView) obj;
} }
@JavascriptInterface @JavascriptInterface
public void _callHandler(String handlerName, String args) { public void _callHandler(String handlerName, String args) {
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid); if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("handlerName", handlerName); obj.put("handlerName", handlerName);
obj.put("args", args); obj.put("args", args);
InAppBrowserFlutterPlugin.channel.invokeMethod("onCallJsHandler", obj); getChannel().invokeMethod("onCallJsHandler", obj);
}
private MethodChannel getChannel() {
return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.channel : flutterWebView.channel;
} }
} }
...@@ -9,7 +9,7 @@ import java.util.HashMap; ...@@ -9,7 +9,7 @@ import java.util.HashMap;
public class Options { public class Options {
static String LOG_TAG = ""; static String LOG_TAG = "Options";
public Options parse(HashMap<String, Object> options) { public Options parse(HashMap<String, Object> options) {
Iterator it = options.entrySet().iterator(); Iterator it = options.entrySet().iterator();
......
package com.pichillilorenzo.flutter_inappbrowser;
import android.content.res.AssetManager;
import java.io.IOException;
import java.io.InputStream;
import io.flutter.plugin.common.PluginRegistry;
public class Util {
public static final String ANDROID_ASSET_URL = "file:///android_asset/";
public static String getUrlAsset (PluginRegistry.Registrar registrar, String assetFilePath) throws IOException {
String key = registrar.lookupKeyForAsset(assetFilePath);
AssetManager mg = registrar.activeContext().getResources().getAssets();
InputStream is = null;
IOException e = null;
try {
is = mg.open(key);
} catch (IOException ex) {
e = ex;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
e = ex;
}
}
}
if (e != null) {
throw e;
}
return ANDROID_ASSET_URL + key;
}
}
...@@ -7,15 +7,13 @@ ...@@ -7,15 +7,13 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:clickable="true" android:clickable="true"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
tools:context=".WebViewActivity" tools:context=".InAppBrowserActivity"
android:focusable="true"> android:focusable="true">
<WebView <com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView
android:id="@+id/webView" android:id="@+id/webView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent" />
</WebView>
<ProgressBar <ProgressBar
android:id="@+id/progressBar" android:id="@+id/progressBar"
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
xmlns:appcompat="http://schemas.android.com/apk/res-auto" xmlns:appcompat="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/tools"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context=".WebViewActivity"> tools:context=".InAppBrowserActivity">
<item <item
android:id="@+id/action_go_back" android:id="@+id/action_go_back"
......
...@@ -7,15 +7,20 @@ class MyInAppBrowser extends InAppBrowser { ...@@ -7,15 +7,20 @@ class MyInAppBrowser extends InAppBrowser {
@override @override
Future onLoadStart(String url) async { Future onLoadStart(String url) async {
print("\n\nStarted $url\n\n"); print("\n\nStarted $url\n\n");
//print("\n\n ${await this.isHidden()} \n\n"); print("\n\n ${await this.isHidden()} \n\n");
print(await this.webViewController.canGoBack());
print(await this.webViewController.canGoForward());
} }
@override @override
Future onLoadStop(String url) async { Future onLoadStop(String url) async {
print("\n\nStopped $url\n\n"); print("\n\nStopped $url\n\n");
// var screenshot = await this.takeScreenshot(); // print(await this.webViewController.canGoBack());
// await this.injectScriptCode(""" // print(await this.webViewController.canGoForward());
// var screenshot = await this.webViewController.takeScreenshot();
// await this.webViewController.injectScriptCode("""
// document.body.innerHTML = '<img style="max-width: 100%; width: 100%" src="data:image/png;base64,${base64.encode(screenshot)}" />'; // document.body.innerHTML = '<img style="max-width: 100%; width: 100%" src="data:image/png;base64,${base64.encode(screenshot)}" />';
// """); // """);
...@@ -48,49 +53,49 @@ class MyInAppBrowser extends InAppBrowser { ...@@ -48,49 +53,49 @@ class MyInAppBrowser extends InAppBrowser {
// } // }
// print("\n\n ${await this.isHidden()} \n\n"); // print("\n\n ${await this.isHidden()} \n\n");
// await this.injectScriptCode("window.flutter_inappbrowser.callHandler('handlerTest', 1, 5,'string', {'key': 5}, [4,6,8]);");
// 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.webViewController.injectScriptCode("window.flutter_inappbrowser.callHandler('handlerTest', 1, 5,'string', {'key': 5}, [4,6,8]);");
// await this.injectScriptCode("console.warn('testWarn',null);"); // await this.webViewController.injectScriptCode("window.flutter_inappbrowser.callHandler('handlerTest2', false, null, undefined);");
// await this.injectScriptCode("console.log('testObjectStringify', JSON.stringify({'asd': 5}));"); // await this.webViewController.injectScriptCode("setTimeout(function(){window.flutter_inappbrowser.callHandler('handlerTest', 'anotherString');}, 1000);");
// await this.injectScriptCode("console.info('testInfo', 6);");
// await this.injectScriptCode("console.error('testError', false);");
// await this.injectScriptCode("console.debug('testDebug', true);");
// print(await this.injectScriptCode("document.body.innerHTML"));
// print(await this.injectScriptCode("null")); // await this.webViewController.injectScriptCode("console.log({'testObject': 5});");
// print(await this.injectScriptCode("undefined")); // await this.webViewController.injectScriptCode("console.warn('testWarn',null);");
// print(await this.injectScriptCode("3")); // await this.webViewController.injectScriptCode("console.log('testObjectStringify', JSON.stringify({'asd': 5}));");
// print(await this.injectScriptCode(""" // await this.webViewController.injectScriptCode("console.info('testInfo', 6);");
// await this.webViewController.injectScriptCode("console.error('testError', false);");
// await this.webViewController.injectScriptCode("console.debug('testDebug', true);");
//
// print(await this.webViewController.injectScriptCode("document.body.innerHTML"));
//
// print(await this.webViewController.injectScriptCode("null"));
// print(await this.webViewController.injectScriptCode("undefined"));
// print(await this.webViewController.injectScriptCode("3"));
// print(await this.webViewController.injectScriptCode("""
// function asd (a,b) { // function asd (a,b) {
// return a+b; // return a+b;
// }; // };
// asd(3,5); // asd(3,5);
// """)); // """));
// print(await this.injectScriptCode(""" // print(await this.webViewController.injectScriptCode("""
// ["3",56,"sdf"]; // ["3",56,"sdf"];
// """)); // """));
// print(await this.injectScriptCode(""" // print(await this.webViewController.injectScriptCode("""
// var x = {"as":4, "dfdfg": 6}; // var x = {"as":4, "dfdfg": 6};
// x; // x;
// """)); // """));
// //
// await this.injectScriptFile("https://code.jquery.com/jquery-3.3.1.min.js"); // await this.webViewController.injectScriptFile("https://code.jquery.com/jquery-3.3.1.min.js");
// this.injectScriptCode(""" // this.webViewController.injectScriptCode("""
// \$( "body" ).html( "Next Step..." ) // \$( "body" ).html( "Next Step..." )
// """); // """);
// //
// // add custom css // // add custom css
// this.injectStyleCode(""" // this.webViewController.injectStyleCode("""
// body { // body {
// background-color: #3c3c3c !important; // background-color: #3c3c3c !important;
// } // }
// """); // """);
// this.injectStyleFile("https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"); // this.webViewController.injectStyleFile("https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css");
} }
@override @override
...@@ -98,6 +103,11 @@ class MyInAppBrowser extends InAppBrowser { ...@@ -98,6 +103,11 @@ class MyInAppBrowser extends InAppBrowser {
print("\n\nCan't load $url.. Error: $message\n\n"); print("\n\nCan't load $url.. Error: $message\n\n");
} }
@override
void onProgressChanged(int progress) {
print("Progress: $progress");
}
@override @override
void onExit() { void onExit() {
print("\n\nBrowser closed!\n\n"); print("\n\nBrowser closed!\n\n");
...@@ -106,7 +116,7 @@ class MyInAppBrowser extends InAppBrowser { ...@@ -106,7 +116,7 @@ class MyInAppBrowser extends InAppBrowser {
@override @override
void shouldOverrideUrlLoading(String url) { void shouldOverrideUrlLoading(String url) {
print("\n\n override $url\n\n"); print("\n\n override $url\n\n");
this.loadUrl(url); this.webViewController.loadUrl(url);
} }
@override @override
...@@ -159,8 +169,10 @@ class MyChromeSafariBrowser extends ChromeSafariBrowser { ...@@ -159,8 +169,10 @@ class MyChromeSafariBrowser extends ChromeSafariBrowser {
// adding a webview fallback // adding a webview fallback
MyChromeSafariBrowser chromeSafariBrowser = MyChromeSafariBrowser chromeSafariBrowser =
new MyChromeSafariBrowser(inAppBrowserFallback); new MyChromeSafariBrowser(inAppBrowserFallback);
InAppLocalhostServer localhostServer = new InAppLocalhostServer();
Future main() async { Future main() async {
// await localhostServer.start();
runApp(new MyApp()); runApp(new MyApp());
} }
...@@ -173,15 +185,15 @@ class _MyAppState extends State<MyApp> { ...@@ -173,15 +185,15 @@ class _MyAppState extends State<MyApp> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// int indexTest = inAppBrowserFallback.addJavaScriptHandler("handlerTest", // int indexTest = inAppBrowserFallback.webViewController.addJavaScriptHandler("handlerTest",
// (arguments) async { // (arguments) async {
// print("handlerTest arguments"); // print("handlerTest arguments");
// print(arguments); // print(arguments);
// }); // });
// int indexTest2 = inAppBrowserFallback.addJavaScriptHandler("test2", (arguments) async { // int indexTest2 = inAppBrowserFallback.webViewController.addJavaScriptHandler("test2", (arguments) async {
// print("handlerTest2 arguments"); // print("handlerTest2 arguments");
// print(arguments); // print(arguments);
// inAppBrowserFallback.removeJavaScriptHandler("test", indexTest); // inAppBrowserFallback.webViewController.removeJavaScriptHandler("test", indexTest);
// }); // });
} }
...@@ -196,32 +208,30 @@ class _MyAppState extends State<MyApp> { ...@@ -196,32 +208,30 @@ class _MyAppState extends State<MyApp> {
child: new RaisedButton( child: new RaisedButton(
onPressed: () async { onPressed: () async {
// await chromeSafariBrowser.open("https://flutter.io/"); // await chromeSafariBrowser.open("https://flutter.io/");
//
// await InAppBrowser.openWithSystemBrowser("https://flutter.io/"); // await InAppBrowser.openWithSystemBrowser("https://flutter.io/");
//
// await inAppBrowserFallback.openOnLocalhost("assets/index.html", options: { // await inAppBrowserFallback.open(url: "http://localhost:8080/assets/index.html", options: {
// "useOnLoadResource": true, // "useOnLoadResource": true,
// //"hidden": true, // //"hidden": true,
// //"toolbarTopFixedTitle": "Fixed title", // //"toolbarTopFixedTitle": "Fixed title",
// //"useShouldOverrideUrlLoading": true // //"useShouldOverrideUrlLoading": true
// //"hideUrlBar": true, // //"hideUrlBar": true,
// //"toolbarTop": false, // //"toolbarTop": false,
// //"toolbarBottom": false // "toolbarBottom": false
// }); // });
//
// await inAppBrowserFallback.open(url: "assets/index.html", options: { // await inAppBrowserFallback.openFile("assets/index.html", options: {
// "isLocalFile": true,
// "useOnLoadResource": true, // "useOnLoadResource": true,
// //"hidden": true, // //"hidden": true,
// //"toolbarTopFixedTitle": "Fixed title",
// //"useShouldOverrideUrlLoading": true // //"useShouldOverrideUrlLoading": true
// //"hideUrlBar": true, // //"hideUrlBar": true,
// //"toolbarTop": false, // //"toolbarTop": false,
// //"toolbarBottom": false // //"toolbarBottom": false
// }); // });
//
await inAppBrowserFallback.open(url: "https://flutter.io/", options: { await inAppBrowserFallback.open(url: "https://flutter.io/", options: {
//"useOnLoadResource": true, "useOnLoadResource": true,
//"hidden": true, //"hidden": true,
//"toolbarTopFixedTitle": "Fixed title", //"toolbarTopFixedTitle": "Fixed title",
//"useShouldOverrideUrlLoading": true //"useShouldOverrideUrlLoading": true
...@@ -229,11 +239,143 @@ class _MyAppState extends State<MyApp> { ...@@ -229,11 +239,143 @@ class _MyAppState extends State<MyApp> {
//"toolbarTop": false, //"toolbarTop": false,
//"toolbarBottom": false //"toolbarBottom": false
}); });
//await inAppBrowserFallback.openOnLocalhost("assets/index.html");
}, },
child: Text("Open InAppBrowser")), child: Text("Open InAppBrowser")
),
), ),
), ),
); );
} }
} }
// Inline WebView Example
//
//import 'dart:async';
//import 'package:flutter/material.dart';
//import 'package:flutter_inappbrowser/flutter_inappbrowser.dart';
//
//Future main() async {
// runApp(new MyApp());
//}
//
//class MyApp extends StatefulWidget {
// @override
// _MyAppState createState() => new _MyAppState();
//}
//
//class _MyAppState extends State<MyApp> {
//
// InAppWebViewController webView;
// String url = "";
// double progress = 0;
//
// @override
// void initState() {
// super.initState();
// }
//
// @override
// void dispose() {
// super.dispose();
// }
//
// @override
// Widget build(BuildContext context) {
// return MaterialApp(
// home: Scaffold(
// appBar: AppBar(
// title: const Text('Inline WebView example app'),
// ),
// body: Container(
// child: Column(
// children: <Widget>[
// Container(
// padding: EdgeInsets.all(20.0),
// child: Text("CURRENT URL\n${ (url.length > 50) ? url.substring(0, 50) + "..." : url }"),
// ),
// (progress != 1.0) ? LinearProgressIndicator(value: progress) : null,
// Expanded(
// child: Container(
// margin: const EdgeInsets.all(10.0),
// decoration: BoxDecoration(
// border: Border.all(color: Colors.blueAccent)
// ),
// child: InAppWebView(
// initialUrl: "https://flutter.io/",
// initialHeaders: {
//
// },
// initialOptions: {
//
// },
// onWebViewCreated: (InAppWebViewController controller) {
// webView = controller;
// },
// onLoadStart: (InAppWebViewController controller, String url) {
// print("started $url");
// setState(() {
// this.url = url;
// });
// },
// onProgressChanged: (InAppWebViewController controller, int progress) {
// setState(() {
// this.progress = progress/100;
// });
// },
// ),
// ),
// ),
// ButtonBar(
// alignment: MainAxisAlignment.center,
// children: <Widget>[
// RaisedButton(
// child: Icon(Icons.arrow_back),
// onPressed: () {
// if (webView != null) {
// webView.goBack();
// }
// },
// ),
// RaisedButton(
// child: Icon(Icons.arrow_forward),
// onPressed: () {
// if (webView != null) {
// webView.goForward();
// }
// },
// ),
// RaisedButton(
// child: Icon(Icons.refresh),
// onPressed: () {
// if (webView != null) {
// webView.reload();
// }
// },
// ),
// ],
// ),
// ].where((Object o) => o != null).toList(),
// ),
// ),
// bottomNavigationBar: BottomNavigationBar(
// currentIndex: 0,
// items: [
// BottomNavigationBarItem(
// icon: Icon(Icons.home),
// title: Text('Home'),
// ),
// BottomNavigationBarItem(
// icon: Icon(Icons.mail),
// title: Text('Item 2'),
// ),
// BottomNavigationBarItem(
// icon: Icon(Icons.person),
// title: Text('Item 3')
// )
// ],
// ),
// ),
// );
// }
//}
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/example/.dart_tool" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/example/.pub" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/example/build" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/example/ios/Flutter/flutter_assets/packages" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/flutter_assets/packages" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/flutter_assets/packages" />
</content> </content>
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
......
...@@ -10,21 +10,12 @@ import Foundation ...@@ -10,21 +10,12 @@ import Foundation
@objcMembers @objcMembers
public class InAppBrowserOptions: Options { public class InAppBrowserOptions: Options {
var useShouldOverrideUrlLoading = false
var useOnLoadResource = false
var openWithSystemBrowser = false;
var clearCache = false
var userAgent = ""
var javaScriptEnabled = true
var javaScriptCanOpenWindowsAutomatically = false
var hidden = false var hidden = false
var toolbarTop = true var toolbarTop = true
var toolbarTopBackgroundColor = "" var toolbarTopBackgroundColor = ""
var toolbarTopFixedTitle = ""
var hideUrlBar = false var hideUrlBar = false
var mediaPlaybackRequiresUserGesture = true
var isLocalFile = false
var disallowOverScroll = false
var toolbarBottom = true var toolbarBottom = true
var toolbarBottomBackgroundColor = "" var toolbarBottomBackgroundColor = ""
var toolbarBottomTranslucent = true var toolbarBottomTranslucent = true
...@@ -32,15 +23,6 @@ public class InAppBrowserOptions: Options { ...@@ -32,15 +23,6 @@ public class InAppBrowserOptions: Options {
var closeButtonColor = "" var closeButtonColor = ""
var presentationStyle = 0 //fullscreen var presentationStyle = 0 //fullscreen
var transitionStyle = 0 //crossDissolve var transitionStyle = 0 //crossDissolve
var enableViewportScale = false
//var keyboardDisplayRequiresUserAction = true
var suppressesIncrementalRendering = false
var allowsAirPlayForMediaPlayback = true
var allowsBackForwardNavigationGestures = true
var allowsLinkPreview = true
var ignoresViewportScaleLimits = false
var allowsInlineMediaPlayback = false
var allowsPictureInPictureMediaPlayback = true
var spinner = true var spinner = true
override init(){ override init(){
......
...@@ -129,7 +129,7 @@ func convertToDictionary(text: String) -> [String: Any]? { ...@@ -129,7 +129,7 @@ func convertToDictionary(text: String) -> [String: Any]? {
// //
//} //}
class WKWebView_IBWrapper: WKWebView { class InAppWebView_IBWrapper: InAppWebView {
required convenience init?(coder: NSCoder) { required convenience init?(coder: NSCoder) {
let config = WKWebViewConfiguration() let config = WKWebViewConfiguration()
self.init(frame: .zero, configuration: config) self.init(frame: .zero, configuration: config)
...@@ -139,7 +139,7 @@ class WKWebView_IBWrapper: WKWebView { ...@@ -139,7 +139,7 @@ class WKWebView_IBWrapper: WKWebView {
class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, UITextFieldDelegate, WKScriptMessageHandler { class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, UITextFieldDelegate, WKScriptMessageHandler {
@IBOutlet var webView: WKWebView_IBWrapper! @IBOutlet var webView: InAppWebView_IBWrapper!
@IBOutlet var closeButton: UIButton! @IBOutlet var closeButton: UIButton!
@IBOutlet var reloadButton: UIBarButtonItem! @IBOutlet var reloadButton: UIBarButtonItem!
@IBOutlet var backButton: UIBarButtonItem! @IBOutlet var backButton: UIBarButtonItem!
...@@ -159,6 +159,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -159,6 +159,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
var currentURL: URL? var currentURL: URL?
var tmpWindow: UIWindow? var tmpWindow: UIWindow?
var browserOptions: InAppBrowserOptions? var browserOptions: InAppBrowserOptions?
var webViewOptions: InAppWebViewOptions?
var initHeaders: [String: String]? var initHeaders: [String: String]?
var isHidden = false var isHidden = false
var uuid: String = "" var uuid: String = ""
...@@ -216,7 +217,8 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -216,7 +217,8 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
// Prevent crashes on closing windows // Prevent crashes on closing windows
deinit { deinit {
webView?.uiDelegate = nil webView.removeObserver(self, forKeyPath: "estimatedProgress")
webView.uiDelegate = nil
} }
override func viewWillDisappear (_ animated: Bool) { override func viewWillDisappear (_ animated: Bool) {
...@@ -231,6 +233,11 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -231,6 +233,11 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
func prepareWebView() { func prepareWebView() {
//UIApplication.shared.statusBarStyle = preferredStatusBarStyle //UIApplication.shared.statusBarStyle = preferredStatusBarStyle
self.webView.addObserver(self,
forKeyPath: #keyPath(WKWebView.estimatedProgress),
options: .new,
context: nil)
self.webView.configuration.userContentController = WKUserContentController() self.webView.configuration.userContentController = WKUserContentController()
self.webView.configuration.preferences = WKPreferences() self.webView.configuration.preferences = WKPreferences()
...@@ -273,7 +280,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -273,7 +280,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
self.modalTransitionStyle = UIModalTransitionStyle(rawValue: (browserOptions?.transitionStyle)!)! self.modalTransitionStyle = UIModalTransitionStyle(rawValue: (browserOptions?.transitionStyle)!)!
// prevent webView from bouncing // prevent webView from bouncing
if (browserOptions?.disallowOverScroll)! { if (webViewOptions?.disallowOverScroll)! {
if self.webView.responds(to: #selector(getter: self.webView.scrollView)) { if self.webView.responds(to: #selector(getter: self.webView.scrollView)) {
self.webView.scrollView.bounces = false self.webView.scrollView.bounces = false
} }
...@@ -286,7 +293,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -286,7 +293,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
} }
} }
if (browserOptions?.enableViewportScale)! { if (webViewOptions?.enableViewportScale)! {
let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);" let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"
let userScript = WKUserScript(source: jscript, injectionTime: .atDocumentEnd, forMainFrameOnly: true) let userScript = WKUserScript(source: jscript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
self.webView.configuration.userContentController.addUserScript(userScript) self.webView.configuration.userContentController.addUserScript(userScript)
...@@ -314,44 +321,44 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -314,44 +321,44 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
self.webView.configuration.userContentController.add(self, name: "resourceLoaded") self.webView.configuration.userContentController.add(self, name: "resourceLoaded")
if #available(iOS 10.0, *) { if #available(iOS 10.0, *) {
self.webView.configuration.mediaTypesRequiringUserActionForPlayback = ((browserOptions?.mediaPlaybackRequiresUserGesture)!) ? .all : [] self.webView.configuration.mediaTypesRequiringUserActionForPlayback = ((webViewOptions?.mediaPlaybackRequiresUserGesture)!) ? .all : []
} else { } else {
// Fallback on earlier versions // Fallback on earlier versions
self.webView.configuration.mediaPlaybackRequiresUserAction = (browserOptions?.mediaPlaybackRequiresUserGesture)! self.webView.configuration.mediaPlaybackRequiresUserAction = (webViewOptions?.mediaPlaybackRequiresUserGesture)!
} }
self.webView.configuration.allowsInlineMediaPlayback = (browserOptions?.allowsInlineMediaPlayback)! self.webView.configuration.allowsInlineMediaPlayback = (webViewOptions?.allowsInlineMediaPlayback)!
//self.webView.keyboardDisplayRequiresUserAction = browserOptions?.keyboardDisplayRequiresUserAction //self.webView.keyboardDisplayRequiresUserAction = browserOptions?.keyboardDisplayRequiresUserAction
self.webView.configuration.suppressesIncrementalRendering = (browserOptions?.suppressesIncrementalRendering)! self.webView.configuration.suppressesIncrementalRendering = (webViewOptions?.suppressesIncrementalRendering)!
self.webView.allowsBackForwardNavigationGestures = (browserOptions?.allowsBackForwardNavigationGestures)! self.webView.allowsBackForwardNavigationGestures = (webViewOptions?.allowsBackForwardNavigationGestures)!
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
self.webView.allowsLinkPreview = (browserOptions?.allowsLinkPreview)! self.webView.allowsLinkPreview = (webViewOptions?.allowsLinkPreview)!
} }
if #available(iOS 10.0, *) { if #available(iOS 10.0, *) {
self.webView.configuration.ignoresViewportScaleLimits = (browserOptions?.ignoresViewportScaleLimits)! self.webView.configuration.ignoresViewportScaleLimits = (webViewOptions?.ignoresViewportScaleLimits)!
} }
self.webView.configuration.allowsInlineMediaPlayback = (browserOptions?.allowsInlineMediaPlayback)! self.webView.configuration.allowsInlineMediaPlayback = (webViewOptions?.allowsInlineMediaPlayback)!
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
self.webView.configuration.allowsPictureInPictureMediaPlayback = (browserOptions?.allowsPictureInPictureMediaPlayback)! self.webView.configuration.allowsPictureInPictureMediaPlayback = (webViewOptions?.allowsPictureInPictureMediaPlayback)!
} }
self.webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = (browserOptions?.javaScriptCanOpenWindowsAutomatically)! self.webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = (webViewOptions?.javaScriptCanOpenWindowsAutomatically)!
self.webView.configuration.preferences.javaScriptEnabled = (browserOptions?.javaScriptEnabled)! self.webView.configuration.preferences.javaScriptEnabled = (webViewOptions?.javaScriptEnabled)!
if ((browserOptions?.userAgent)! != "") { if ((webViewOptions?.userAgent)! != "") {
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
self.webView.customUserAgent = (browserOptions?.userAgent)! self.webView.customUserAgent = (webViewOptions?.userAgent)!
} }
} }
if (browserOptions?.clearCache)! { if (webViewOptions?.clearCache)! {
clearCache() clearCache()
} }
...@@ -519,14 +526,14 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -519,14 +526,14 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
if let url = navigationAction.request.url { if let url = navigationAction.request.url {
if url.absoluteString != self.currentURL?.absoluteString && (browserOptions?.useOnLoadResource)! { if url.absoluteString != self.currentURL?.absoluteString && (webViewOptions?.useOnLoadResource)! {
WKNavigationMap[url.absoluteString] = [ WKNavigationMap[url.absoluteString] = [
"startTime": currentTimeInMilliSeconds(), "startTime": currentTimeInMilliSeconds(),
"request": navigationAction.request "request": navigationAction.request
] ]
} }
if navigationAction.navigationType == .linkActivated && (browserOptions?.useShouldOverrideUrlLoading)! { if navigationAction.navigationType == .linkActivated && (webViewOptions?.useShouldOverrideUrlLoading)! {
if navigationDelegate != nil { if navigationDelegate != nil {
navigationDelegate?.shouldOverrideUrlLoading(uuid: self.uuid, webView: webView, url: url) navigationDelegate?.shouldOverrideUrlLoading(uuid: self.uuid, webView: webView, url: url)
} }
...@@ -548,7 +555,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -548,7 +555,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
decidePolicyFor navigationResponse: WKNavigationResponse, decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if (browserOptions?.useOnLoadResource)! { if (webViewOptions?.useOnLoadResource)! {
if let url = navigationResponse.response.url { if let url = navigationResponse.response.url {
if WKNavigationMap[url.absoluteString] != nil { if WKNavigationMap[url.absoluteString] != nil {
let startResourceTime = (WKNavigationMap[url.absoluteString]!["startTime"] as! Int) let startResourceTime = (WKNavigationMap[url.absoluteString]!["startTime"] as! Int)
...@@ -678,7 +685,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -678,7 +685,7 @@ 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" && (browserOptions?.useOnLoadResource)! { else if message.name == "resourceLoaded" && (webViewOptions?.useOnLoadResource)! {
if let resource = convertToDictionary(text: message.body as! String) { if let resource = convertToDictionary(text: message.body as! String) {
let url = URL(string: resource["name"] as! String)! let url = URL(string: resource["name"] as! String)!
if !UIApplication.shared.canOpenURL(url) { if !UIApplication.shared.canOpenURL(url) {
...@@ -730,6 +737,9 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -730,6 +737,9 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
func setOptions(newOptions: InAppBrowserOptions, newOptionsMap: [String: Any]) { func setOptions(newOptions: InAppBrowserOptions, newOptionsMap: [String: Any]) {
let newInAppWebViewOptions = InAppWebViewOptions()
newInAppWebViewOptions.parse(options: newOptionsMap)
if newOptionsMap["hidden"] != nil && browserOptions?.hidden != newOptions.hidden { if newOptionsMap["hidden"] != nil && browserOptions?.hidden != newOptions.hidden {
if newOptions.hidden { if newOptions.hidden {
self.navigationDelegate?.hide(uuid: self.uuid) self.navigationDelegate?.hide(uuid: self.uuid)
...@@ -784,93 +794,107 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio ...@@ -784,93 +794,107 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
self.modalTransitionStyle = UIModalTransitionStyle(rawValue: newOptions.transitionStyle)! self.modalTransitionStyle = UIModalTransitionStyle(rawValue: newOptions.transitionStyle)!
} }
if newOptionsMap["disallowOverScroll"] != nil && browserOptions?.disallowOverScroll != newOptions.disallowOverScroll { if newOptionsMap["disallowOverScroll"] != nil && webViewOptions?.disallowOverScroll != newInAppWebViewOptions.disallowOverScroll {
if self.webView.responds(to: #selector(getter: self.webView.scrollView)) { if self.webView.responds(to: #selector(getter: self.webView.scrollView)) {
self.webView.scrollView.bounces = !newOptions.disallowOverScroll self.webView.scrollView.bounces = !newInAppWebViewOptions.disallowOverScroll
} }
else { else {
for subview: UIView in self.webView.subviews { for subview: UIView in self.webView.subviews {
if subview is UIScrollView { if subview is UIScrollView {
(subview as! UIScrollView).bounces = !newOptions.disallowOverScroll (subview as! UIScrollView).bounces = !newInAppWebViewOptions.disallowOverScroll
} }
} }
} }
} }
if newOptionsMap["enableViewportScale"] != nil && browserOptions?.enableViewportScale != newOptions.enableViewportScale && newOptions.enableViewportScale { if newOptionsMap["enableViewportScale"] != nil && webViewOptions?.enableViewportScale != newInAppWebViewOptions.enableViewportScale && newInAppWebViewOptions.enableViewportScale {
let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);" let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"
self.webView.evaluateJavaScript(jscript, completionHandler: nil) self.webView.evaluateJavaScript(jscript, completionHandler: nil)
} }
if newOptionsMap["mediaPlaybackRequiresUserGesture"] != nil && browserOptions?.mediaPlaybackRequiresUserGesture != newOptions.mediaPlaybackRequiresUserGesture { if newOptionsMap["mediaPlaybackRequiresUserGesture"] != nil && webViewOptions?.mediaPlaybackRequiresUserGesture != newInAppWebViewOptions.mediaPlaybackRequiresUserGesture {
if #available(iOS 10.0, *) { if #available(iOS 10.0, *) {
self.webView.configuration.mediaTypesRequiringUserActionForPlayback = (newOptions.mediaPlaybackRequiresUserGesture) ? .all : [] self.webView.configuration.mediaTypesRequiringUserActionForPlayback = (newInAppWebViewOptions.mediaPlaybackRequiresUserGesture) ? .all : []
} else { } else {
// Fallback on earlier versions // Fallback on earlier versions
self.webView.configuration.mediaPlaybackRequiresUserAction = newOptions.mediaPlaybackRequiresUserGesture self.webView.configuration.mediaPlaybackRequiresUserAction = newInAppWebViewOptions.mediaPlaybackRequiresUserGesture
} }
} }
if newOptionsMap["allowsInlineMediaPlayback"] != nil && browserOptions?.allowsInlineMediaPlayback != newOptions.allowsInlineMediaPlayback { if newOptionsMap["allowsInlineMediaPlayback"] != nil && webViewOptions?.allowsInlineMediaPlayback != newInAppWebViewOptions.allowsInlineMediaPlayback {
self.webView.configuration.allowsInlineMediaPlayback = newOptions.allowsInlineMediaPlayback self.webView.configuration.allowsInlineMediaPlayback = newInAppWebViewOptions.allowsInlineMediaPlayback
} }
// if newOptionsMap["keyboardDisplayRequiresUserAction"] != nil && browserOptions?.keyboardDisplayRequiresUserAction != newOptions.keyboardDisplayRequiresUserAction { // if newOptionsMap["keyboardDisplayRequiresUserAction"] != nil && browserOptions?.keyboardDisplayRequiresUserAction != newOptions.keyboardDisplayRequiresUserAction {
// self.webView.keyboardDisplayRequiresUserAction = newOptions.keyboardDisplayRequiresUserAction // self.webView.keyboardDisplayRequiresUserAction = newOptions.keyboardDisplayRequiresUserAction
// } // }
if newOptionsMap["suppressesIncrementalRendering"] != nil && browserOptions?.suppressesIncrementalRendering != newOptions.suppressesIncrementalRendering { if newOptionsMap["suppressesIncrementalRendering"] != nil && webViewOptions?.suppressesIncrementalRendering != newInAppWebViewOptions.suppressesIncrementalRendering {
self.webView.configuration.suppressesIncrementalRendering = newOptions.suppressesIncrementalRendering self.webView.configuration.suppressesIncrementalRendering = newInAppWebViewOptions.suppressesIncrementalRendering
} }
if newOptionsMap["allowsBackForwardNavigationGestures"] != nil && browserOptions?.allowsBackForwardNavigationGestures != newOptions.allowsBackForwardNavigationGestures { if newOptionsMap["allowsBackForwardNavigationGestures"] != nil && webViewOptions?.allowsBackForwardNavigationGestures != newInAppWebViewOptions.allowsBackForwardNavigationGestures {
self.webView.allowsBackForwardNavigationGestures = newOptions.allowsBackForwardNavigationGestures self.webView.allowsBackForwardNavigationGestures = newInAppWebViewOptions.allowsBackForwardNavigationGestures
} }
if newOptionsMap["allowsLinkPreview"] != nil && browserOptions?.allowsLinkPreview != newOptions.allowsLinkPreview { if newOptionsMap["allowsLinkPreview"] != nil && webViewOptions?.allowsLinkPreview != newInAppWebViewOptions.allowsLinkPreview {
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
self.webView.allowsLinkPreview = newOptions.allowsLinkPreview self.webView.allowsLinkPreview = newInAppWebViewOptions.allowsLinkPreview
} }
} }
if newOptionsMap["ignoresViewportScaleLimits"] != nil && browserOptions?.ignoresViewportScaleLimits != newOptions.ignoresViewportScaleLimits { if newOptionsMap["ignoresViewportScaleLimits"] != nil && webViewOptions?.ignoresViewportScaleLimits != newInAppWebViewOptions.ignoresViewportScaleLimits {
if #available(iOS 10.0, *) { if #available(iOS 10.0, *) {
self.webView.configuration.ignoresViewportScaleLimits = newOptions.ignoresViewportScaleLimits self.webView.configuration.ignoresViewportScaleLimits = newInAppWebViewOptions.ignoresViewportScaleLimits
} }
} }
if newOptionsMap["allowsInlineMediaPlayback"] != nil && browserOptions?.allowsInlineMediaPlayback != newOptions.allowsInlineMediaPlayback { if newOptionsMap["allowsInlineMediaPlayback"] != nil && webViewOptions?.allowsInlineMediaPlayback != newInAppWebViewOptions.allowsInlineMediaPlayback {
self.webView.configuration.allowsInlineMediaPlayback = newOptions.allowsInlineMediaPlayback self.webView.configuration.allowsInlineMediaPlayback = newInAppWebViewOptions.allowsInlineMediaPlayback
} }
if newOptionsMap["allowsPictureInPictureMediaPlayback"] != nil && browserOptions?.allowsPictureInPictureMediaPlayback != newOptions.allowsPictureInPictureMediaPlayback { if newOptionsMap["allowsPictureInPictureMediaPlayback"] != nil && webViewOptions?.allowsPictureInPictureMediaPlayback != newInAppWebViewOptions.allowsPictureInPictureMediaPlayback {
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
self.webView.configuration.allowsPictureInPictureMediaPlayback = newOptions.allowsPictureInPictureMediaPlayback self.webView.configuration.allowsPictureInPictureMediaPlayback = newInAppWebViewOptions.allowsPictureInPictureMediaPlayback
} }
} }
if newOptionsMap["javaScriptCanOpenWindowsAutomatically"] != nil && browserOptions?.javaScriptCanOpenWindowsAutomatically != newOptions.javaScriptCanOpenWindowsAutomatically { if newOptionsMap["javaScriptCanOpenWindowsAutomatically"] != nil && webViewOptions?.javaScriptCanOpenWindowsAutomatically != newInAppWebViewOptions.javaScriptCanOpenWindowsAutomatically {
self.webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = newOptions.javaScriptCanOpenWindowsAutomatically self.webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = newInAppWebViewOptions.javaScriptCanOpenWindowsAutomatically
} }
if newOptionsMap["javaScriptEnabled"] != nil && browserOptions?.javaScriptEnabled != newOptions.javaScriptEnabled { if newOptionsMap["javaScriptEnabled"] != nil && webViewOptions?.javaScriptEnabled != newInAppWebViewOptions.javaScriptEnabled {
self.webView.configuration.preferences.javaScriptEnabled = newOptions.javaScriptEnabled self.webView.configuration.preferences.javaScriptEnabled = newInAppWebViewOptions.javaScriptEnabled
} }
if newOptionsMap["userAgent"] != nil && browserOptions?.userAgent != newOptions.userAgent && (newOptions.userAgent != "") { if newOptionsMap["userAgent"] != nil && webViewOptions?.userAgent != newInAppWebViewOptions.userAgent && (newInAppWebViewOptions.userAgent != "") {
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
self.webView.customUserAgent = newOptions.userAgent self.webView.customUserAgent = newInAppWebViewOptions.userAgent
} }
} }
if newOptionsMap["clearCache"] != nil && newOptions.clearCache { if newOptionsMap["clearCache"] != nil && newInAppWebViewOptions.clearCache {
clearCache() clearCache()
} }
self.browserOptions = newOptions self.browserOptions = newOptions
self.webViewOptions = newInAppWebViewOptions
} }
func getOptions() -> [String: Any]? { func getOptions() -> [String: Any]? {
return (self.browserOptions != nil) ? self.browserOptions?.getHashMap() : nil if (self.browserOptions == nil || self.webViewOptions == nil) {
return nil
}
var optionsMap = self.browserOptions!.getHashMap()
optionsMap.merge(self.webViewOptions!.getHashMap(), uniquingKeysWith: { (current, _) in current })
return optionsMap
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?,
change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "estimatedProgress" {
let progress = Int(webView.estimatedProgress * 100)
self.navigationDelegate?.onProgressChanged(uuid: self.uuid, webView: self.webView, progress: progress)
}
} }
} }
//
// InAppWebView.swift
// flutter_inappbrowser
//
// Created by Lorenzo on 21/10/18.
//
import Foundation
import WebKit
public class InAppWebView: WKWebView {
}
//
// InAppWebViewOptions.swift
// flutter_inappbrowser
//
// Created by Lorenzo on 21/10/18.
//
import Foundation
@objcMembers
public class InAppWebViewOptions: Options {
var useShouldOverrideUrlLoading = false
var useOnLoadResource = false
var clearCache = false
var userAgent = ""
var javaScriptEnabled = true
var javaScriptCanOpenWindowsAutomatically = false
var mediaPlaybackRequiresUserGesture = true
var disallowOverScroll = false
var enableViewportScale = false
//var keyboardDisplayRequiresUserAction = true
var suppressesIncrementalRendering = false
var allowsAirPlayForMediaPlayback = true
var allowsBackForwardNavigationGestures = true
var allowsLinkPreview = true
var ignoresViewportScaleLimits = false
var allowsInlineMediaPlayback = false
var allowsPictureInPictureMediaPlayback = true
override init(){
super.init()
}
}
...@@ -68,6 +68,9 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -68,6 +68,9 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
case "loadUrl": case "loadUrl":
self.loadUrl(uuid: uuid, arguments: arguments!, result: result) self.loadUrl(uuid: uuid, arguments: arguments!, result: result)
break break
case "loadFile":
self.loadFile(uuid: uuid, arguments: arguments!, result: result)
break
case "close": case "close":
self.close(uuid: uuid) self.close(uuid: uuid)
result(true) result(true)
...@@ -205,29 +208,39 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -205,29 +208,39 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
let url: String = (arguments["url"] as? String)! let url: String = (arguments["url"] as? String)!
let headers = (arguments["headers"] as? [String: String])! let headers = (arguments["headers"] as? [String: String])!
let absoluteUrl = URL(string: url)?.absoluteURL var absoluteUrl = URL(string: url)?.absoluteURL
let useChromeSafariBrowser = (arguments["useChromeSafariBrowser"] as? Bool) let useChromeSafariBrowser = (arguments["useChromeSafariBrowser"] as? Bool)!
if useChromeSafariBrowser! { if useChromeSafariBrowser {
let uuidFallback = (arguments["uuidFallback"] as? String)! let uuidFallback = (arguments["uuidFallback"] as? String)!
let safariOptions = SafariBrowserOptions() let safariOptions = (arguments["options"] as? [String: Any])!
safariOptions.parse(options: (arguments["options"] as? [String: Any])!)
let optionsFallback = InAppBrowserOptions() let optionsFallback = (arguments["optionsFallback"] as? [String: Any])!
optionsFallback.parse(options: (arguments["optionsFallback"] as? [String: Any])!)
open(uuid: uuid, uuidFallback: uuidFallback, inAppBrowser: absoluteUrl!, headers: headers, withOptions: safariOptions, useChromeSafariBrowser: true, withOptionsFallback: optionsFallback, result: result); open(uuid: uuid, uuidFallback: uuidFallback, inAppBrowser: absoluteUrl!, headers: headers, withOptions: safariOptions, useChromeSafariBrowser: true, withOptionsFallback: optionsFallback, result: result);
} }
else { else {
let options = InAppBrowserOptions() let options = (arguments["options"] as? [String: Any])!
options.parse(options: (arguments["options"] as? [String: Any])!)
let isLocalFile = (arguments["isLocalFile"] as? Bool)!
var openWithSystemBrowser = (arguments["openWithSystemBrowser"] as? Bool)!
if isLocalFile {
let key = SwiftFlutterPlugin.registrar!.lookupKey(forAsset: url)
let assetURL = Bundle.main.url(forResource: key, withExtension: nil)
if assetURL == nil {
result(FlutterError(code: "InAppBrowserFlutterPlugin", message: url + " asset file cannot be found!", details: nil))
return
}
absoluteUrl = assetURL!
}
if isSystemUrl(absoluteUrl!) { if isSystemUrl(absoluteUrl!) {
options.openWithSystemBrowser = true openWithSystemBrowser = true
} }
if (options.openWithSystemBrowser) { if (openWithSystemBrowser) {
open(inSystem: absoluteUrl!, result: result) open(inSystem: absoluteUrl!, result: result)
} }
else { else {
...@@ -236,7 +249,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -236,7 +249,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
} }
} }
func open(uuid: String, uuidFallback: String?, inAppBrowser url: URL, headers: [String: String], withOptions options: Options, useChromeSafariBrowser: Bool, withOptionsFallback optionsFallback: Options?, result: @escaping FlutterResult) { func open(uuid: String, uuidFallback: String?, inAppBrowser url: URL, headers: [String: String], withOptions options: [String: Any], useChromeSafariBrowser: Bool, withOptionsFallback optionsFallback: [String: Any]?, result: @escaping FlutterResult) {
var uuid = uuid var uuid = uuid
...@@ -270,10 +283,12 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -270,10 +283,12 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
self.tmpWindow?.makeKeyAndVisible() self.tmpWindow?.makeKeyAndVisible()
let browserOptions: InAppBrowserOptions let browserOptions: InAppBrowserOptions
let webViewOptions: InAppWebViewOptions
if useChromeSafariBrowser == true { if useChromeSafariBrowser == true {
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
let safariOptions = options as! SafariBrowserOptions let safariOptions = SafariBrowserOptions()
safariOptions.parse(options: options)
let safari: SafariViewController let safari: SafariViewController
...@@ -310,23 +325,19 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -310,23 +325,19 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
return return
} }
uuid = uuidFallback! uuid = uuidFallback!
browserOptions = optionsFallback as! InAppBrowserOptions browserOptions = InAppBrowserOptions()
browserOptions.parse(options: optionsFallback!)
webViewOptions = InAppWebViewOptions()
webViewOptions.parse(options: optionsFallback!)
} }
} }
else { else {
browserOptions = options as! InAppBrowserOptions browserOptions = InAppBrowserOptions()
} browserOptions.parse(options: options)
var currentURL = url webViewOptions = InAppWebViewOptions()
webViewOptions.parse(options: options)
if browserOptions.isLocalFile {
let key = SwiftFlutterPlugin.registrar!.lookupKey(forAsset: url.absoluteString)
let assetURL = Bundle.main.url(forResource: key, withExtension: nil)
if assetURL == nil {
result(FlutterError(code: "InAppBrowserFlutterPlugin", message: url.absoluteString + " asset file cannot be found!", details: nil))
return
}
currentURL = assetURL!
} }
let storyboard = UIStoryboard(name: WEBVIEW_STORYBOARD, bundle: Bundle(for: InAppBrowserFlutterPlugin.self)) let storyboard = UIStoryboard(name: WEBVIEW_STORYBOARD, bundle: Bundle(for: InAppBrowserFlutterPlugin.self))
...@@ -335,9 +346,10 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -335,9 +346,10 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
let webViewController: InAppBrowserWebViewController = self.webViewControllers[uuid] as! InAppBrowserWebViewController let webViewController: InAppBrowserWebViewController = self.webViewControllers[uuid] as! InAppBrowserWebViewController
webViewController.uuid = uuid webViewController.uuid = uuid
webViewController.browserOptions = browserOptions webViewController.browserOptions = browserOptions
webViewController.webViewOptions = webViewOptions
webViewController.isHidden = browserOptions.hidden webViewController.isHidden = browserOptions.hidden
webViewController.tmpWindow = tmpWindow webViewController.tmpWindow = tmpWindow
webViewController.currentURL = currentURL webViewController.currentURL = url
webViewController.initHeaders = headers webViewController.initHeaders = headers
webViewController.navigationDelegate = self webViewController.navigationDelegate = self
...@@ -365,11 +377,29 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -365,11 +377,29 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
public func loadUrl(uuid: String, arguments: NSDictionary, result: @escaping FlutterResult) { public func loadUrl(uuid: String, arguments: NSDictionary, result: @escaping FlutterResult) {
let webViewController: InAppBrowserWebViewController = self.webViewControllers[uuid] as! InAppBrowserWebViewController let webViewController: InAppBrowserWebViewController = self.webViewControllers[uuid] as! InAppBrowserWebViewController
let url: String? = (arguments["url"] as? String)! if let url = arguments["url"] as? String {
let headers = (arguments["headers"] as? [String: String])!
let absoluteUrl = URL(string: url)!.absoluteURL
webViewController.loadUrl(url: absoluteUrl, headers: headers)
}
else {
result(FlutterError(code: "InAppBrowserFlutterPlugin", message: "url is empty", details: nil))
}
result(true)
}
public func loadFile(uuid: String, arguments: NSDictionary, result: @escaping FlutterResult) {
let webViewController: InAppBrowserWebViewController = self.webViewControllers[uuid] as! InAppBrowserWebViewController
if let url = arguments["url"] as? String {
let headers = (arguments["headers"] as? [String: String])! let headers = (arguments["headers"] as? [String: String])!
if url != nil { let key = SwiftFlutterPlugin.registrar!.lookupKey(forAsset: url)
let absoluteUrl = URL(string: url!)!.absoluteURL let assetURL = Bundle.main.url(forResource: key, withExtension: nil)
if assetURL == nil {
result(FlutterError(code: "InAppBrowserFlutterPlugin", message: url + " asset file cannot be found!", details: nil))
return
}
let absoluteUrl = URL(string: url)!.absoluteURL
webViewController.loadUrl(url: absoluteUrl, headers: headers) webViewController.loadUrl(url: absoluteUrl, headers: headers)
} }
else { else {
...@@ -530,6 +560,12 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { ...@@ -530,6 +560,12 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
} }
} }
func onProgressChanged(uuid: String, webView: WKWebView, progress: Int) {
if let webViewController = self.webViewControllers[uuid] {
channel.invokeMethod("onProgressChanged", arguments: ["uuid": uuid, "progress": progress])
}
}
func onLoadResource(uuid: String, webView: WKWebView, response: URLResponse, fromRequest request: URLRequest?, withData data: Data, startTime: Int, duration: 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]
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/> <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gA9-n8-qaQ" customClass="WKWebView_IBWrapper" customModule="flutter_inappbrowser"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gA9-n8-qaQ" customClass="InAppWebView_IBWrapper" customModule="flutter_inappbrowser">
<rect key="frame" x="0.0" y="66" width="414" height="626"/> <rect key="frame" x="0.0" y="66" width="414" height="626"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view> </view>
...@@ -96,7 +96,7 @@ ...@@ -96,7 +96,7 @@
<outlet property="toolbarTop" destination="vlz-kT-71x" id="WgC-80-Z28"/> <outlet property="toolbarTop" destination="vlz-kT-71x" id="WgC-80-Z28"/>
<outlet property="toolbarTop_BottomToWebViewTopConstraint" destination="Sjd-dV-din" id="vkO-Yu-xaE"/> <outlet property="toolbarTop_BottomToWebViewTopConstraint" destination="Sjd-dV-din" id="vkO-Yu-xaE"/>
<outlet property="urlField" destination="sy2-Vx-Cxd" id="MCW-lJ-Ehl"/> <outlet property="urlField" destination="sy2-Vx-Cxd" id="MCW-lJ-Ehl"/>
<outlet property="webView" destination="gA9-n8-qaQ" id="7JE-lp-bwe"/> <outlet property="webView" destination="gA9-n8-qaQ" id="439-rj-hHB"/>
</connections> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="cYA-mw-BIR" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="cYA-mw-BIR" userLabel="First Responder" sceneMemberID="firstResponder"/>
......
...@@ -25,7 +25,11 @@ import 'dart:collection'; ...@@ -25,7 +25,11 @@ import 'dart:collection';
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/gestures.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import 'package:mime/mime.dart'; import 'package:mime/mime.dart';
...@@ -90,7 +94,7 @@ class _ChannelManager { ...@@ -90,7 +94,7 @@ class _ChannelManager {
return await listeners[uuid](call); return await listeners[uuid](call);
} }
static void addListener (String key, ListenerCallback callback) { static void addListener(String key, ListenerCallback callback) {
if (!initialized) if (!initialized)
init(); init();
listeners.putIfAbsent(key, () => callback); listeners.putIfAbsent(key, () => callback);
...@@ -101,102 +105,37 @@ class _ChannelManager { ...@@ -101,102 +105,37 @@ class _ChannelManager {
} }
} }
///InAppBrowser class. ///InAppBrowser class. [webViewController] can be used to access the [InAppWebView] API.
/// ///
///This class uses the native WebView of the platform. ///This class uses the native WebView of the platform.
class InAppBrowser { class InAppBrowser {
String uuid; String uuid;
Map<String, List<JavaScriptHandlerCallback>> javaScriptHandlersMap = HashMap<String, List<JavaScriptHandlerCallback>>(); Map<String, List<JavaScriptHandlerCallback>> javaScriptHandlersMap = HashMap<String, List<JavaScriptHandlerCallback>>();
HttpServer _server;
bool _isOpened = false; bool _isOpened = false;
/// WebView Controller that can be used to access the [InAppWebView] API.
InAppWebViewController webViewController;
/// ///
InAppBrowser () { InAppBrowser () {
uuid = _uuidGenerator.v4(); uuid = _uuidGenerator.v4();
_ChannelManager.addListener(uuid, _handleMethod); _ChannelManager.addListener(uuid, _handleMethod);
_isOpened = false; _isOpened = false;
webViewController = new InAppWebViewController.fromInAppBrowser(uuid, _ChannelManager.channel, this);
} }
Future<dynamic> _handleMethod(MethodCall call) async { Future<dynamic> _handleMethod(MethodCall call) async {
switch(call.method) { switch(call.method) {
case "onLoadStart":
String url = call.arguments["url"];
onLoadStart(url);
break;
case "onLoadStop":
String url = call.arguments["url"];
onLoadStop(url);
break;
case "onLoadError":
String url = call.arguments["url"];
int code = call.arguments["code"];
String message = call.arguments["message"];
onLoadError(url, code, message);
break;
case "onExit": case "onExit":
this._closeServer();
this._isOpened = false; this._isOpened = false;
onExit(); onExit();
break; break;
case "shouldOverrideUrlLoading":
String url = call.arguments["url"];
shouldOverrideUrlLoading(url);
break;
case "onLoadResource":
Map<dynamic, dynamic> rawResponse = call.arguments["response"];
rawResponse = rawResponse.cast<String, dynamic>();
Map<dynamic, dynamic> rawRequest = call.arguments["request"];
rawRequest = rawRequest.cast<String, dynamic>();
String urlResponse = rawResponse["url"];
Map<dynamic, dynamic> headersResponse = rawResponse["headers"];
headersResponse = headersResponse.cast<String, String>();
int statusCode = rawResponse["statusCode"];
int startTime = rawResponse["startTime"];
int duration = rawResponse["duration"];
Uint8List data = rawResponse["data"];
String urlRequest = rawRequest["url"];
Map<dynamic, dynamic> headersRequest = rawRequest["headers"];
headersRequest = headersResponse.cast<String, String>();
String method = rawRequest["method"];
var response = new WebResourceResponse(urlResponse, headersResponse, statusCode, startTime, duration, data);
var request = new WebResourceRequest(urlRequest, headersRequest, method);
onLoadResource(response, request);
break;
case "onConsoleMessage":
String sourceURL = call.arguments["sourceURL"];
int lineNumber = call.arguments["lineNumber"];
String message = call.arguments["message"];
ConsoleMessageLevel messageLevel;
ConsoleMessageLevel.values.forEach((element) {
if ("ConsoleMessageLevel." + call.arguments["messageLevel"] == element.toString()) {
messageLevel = element;
return;
}
});
onConsoleMessage(ConsoleMessage(sourceURL, lineNumber, message, messageLevel));
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;
default: default:
throw UnimplementedError("Unimplemented ${call.method} method"); return webViewController._handleMethod(call);
} }
} }
///Opens an [url] in a new [InAppBrowser] instance or in the system browser (`openWithSystemBrowser: true`). ///Opens an [url] in a new [InAppBrowser] instance.
///
///**NOTE**: If you open the given [url] with the system browser (`openWithSystemBrowser: true`), you wont be able to use the [InAppBrowser] methods!
/// ///
///- [url]: The [url] to load. Call [encodeUriComponent()] on this if the [url] contains Unicode characters. The default value is `about:blank`. ///- [url]: The [url] to load. Call [encodeUriComponent()] on this if the [url] contains Unicode characters. The default value is `about:blank`.
/// ///
...@@ -207,15 +146,13 @@ class InAppBrowser { ...@@ -207,15 +146,13 @@ 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`. /// - __useOnLoadResource__: Set to `true` to be able to listen at the [onLoadResource()] event. The default value is `false`.
/// - __openWithSystemBrowser__: Set to `true` to open the given `url` with the system browser. The default value is `false`.
/// - __isLocalFile__: Set to `true` if the [url] is pointing to a local file (the file must be addded in the `assets` section of your `pubspec.yaml`. See [loadFile()] explanation). 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`.
/// - __javaScriptCanOpenWindowsAutomatically__: Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`. /// - __javaScriptCanOpenWindowsAutomatically__: Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`.
/// - __hidden__: Set to `true` to create the browser and load the page, but not show it. The `onLoadStop` event fires when loading is complete. Omit or set to `false` (default) to have the browser open and load normally. /// - __hidden__: Set to `true` to create the browser and load the page, but not show it. The `onLoadStop` event fires when loading is complete. Omit or set to `false` (default) to have the browser open and load normally.
/// - __toolbarTop__: Set to `false` to hide the toolbar at the top of the WebView. The default value is `true`. /// - __toolbarTop__: Set to `false` to hide the toolbar at the top of the WebView. The default value is `true`.
/// - __toolbarTopBackgroundColor__: Set the custom background color of the toolbat at the top. /// - __toolbarTopBackgroundColor__: Set the custom background color of the toolbar at the top.
/// - __hideUrlBar__: Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`. /// - __hideUrlBar__: Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`.
/// - __mediaPlaybackRequiresUserGesture__: Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`. /// - __mediaPlaybackRequiresUserGesture__: Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`.
/// ///
...@@ -236,7 +173,7 @@ class InAppBrowser { ...@@ -236,7 +173,7 @@ class InAppBrowser {
/// ///
/// - __disallowOverScroll__: Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`. /// - __disallowOverScroll__: Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`.
/// - __toolbarBottom__: Set to `false` to hide the toolbar at the bottom of the WebView. The default value is `true`. /// - __toolbarBottom__: Set to `false` to hide the toolbar at the bottom of the WebView. The default value is `true`.
/// - __toolbarBottomBackgroundColor__: Set the custom background color of the toolbat at the bottom. /// - __toolbarBottomBackgroundColor__: Set the custom background color of the toolbar at the bottom.
/// - __toolbarBottomTranslucent__: Set to `true` to set the toolbar at the bottom translucent. The default value is `true`. /// - __toolbarBottomTranslucent__: Set to `true` to set the toolbar at the bottom translucent. The default value is `true`.
/// - __closeButtonCaption__: Set the custom text for the close button. /// - __closeButtonCaption__: Set the custom text for the close button.
/// - __closeButtonColor__: Set the custom color for the close button. /// - __closeButtonColor__: Set the custom color for the close button.
...@@ -252,120 +189,21 @@ class InAppBrowser { ...@@ -252,120 +189,21 @@ class InAppBrowser {
/// - __allowsPictureInPictureMediaPlayback__: Set to `true` to allow HTML5 videos play picture-in-picture. The default value is `true`. /// - __allowsPictureInPictureMediaPlayback__: Set to `true` to allow HTML5 videos play picture-in-picture. The default value is `true`.
/// - __spinner__: Set to `false` to hide the spinner when the WebView is loading a page. The default value is `true`. /// - __spinner__: Set to `false` to hide the spinner when the WebView is loading a page. The default value is `true`.
Future<void> open({String url = "about:blank", Map<String, String> headers = const {}, Map<String, dynamic> options = const {}}) async { Future<void> open({String url = "about:blank", Map<String, String> headers = const {}, Map<String, dynamic> options = const {}}) async {
assert(url != null);
this._throwIsAlreadyOpened(message: 'Cannot open $url!'); this._throwIsAlreadyOpened(message: 'Cannot open $url!');
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headers); args.putIfAbsent('headers', () => headers);
args.putIfAbsent('options', () => options); args.putIfAbsent('options', () => options);
args.putIfAbsent('openWithSystemBrowser', () => false);
args.putIfAbsent('isLocalFile', () => false);
args.putIfAbsent('useChromeSafariBrowser', () => false); args.putIfAbsent('useChromeSafariBrowser', () => false);
await _ChannelManager.channel.invokeMethod('open', args); await _ChannelManager.channel.invokeMethod('open', args);
this._isOpened = true; this._isOpened = true;
} }
///This is a static method that opens an [url] in the system browser. ///Opens the giver [assetFilePath] file in a new [InAppBrowser] instance. The other arguments are the same of [InAppBrowser.open()].
///This has the same behaviour of an [InAppBrowser] instance calling the [open()] method with option `openWithSystemBrowser: true`.
static Future<void> openWithSystemBrowser(String url) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => "");
args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => {});
args.putIfAbsent('options', () => {"openWithSystemBrowser": true});
args.putIfAbsent('useChromeSafariBrowser', () => false);
return await _ChannelManager.channel.invokeMethod('open', args);
}
Future<void> _startServer({int port = 8080}) async {
this._closeServer();
var completer = new Completer();
runZoned(() {
HttpServer.bind('127.0.0.1', port).then((server) {
print('Server running at http://127.0.0.1:' + port.toString());
this._server = server;
server.listen((HttpRequest request) async {
var body = List<int>();
var path = request.requestedUri.path;
path = (path.startsWith('/')) ? path.substring(1) : path;
path += (path.endsWith('/')) ? 'index.html' : '';
try {
body = (await rootBundle.load(path))
.buffer.asUint8List();
} catch (e) {
print(e.toString());
request.response.close();
return;
}
var contentType = ['text', 'html'];
if (!request.requestedUri.path.endsWith('/') && request.requestedUri.pathSegments.isNotEmpty) {
var mimeType = lookupMimeType(request.requestedUri.path, headerBytes: body);
if (mimeType != null) {
contentType = mimeType.split('/');
}
}
request.response.headers.contentType = new ContentType(contentType[0], contentType[1], charset: 'utf-8');
request.response.add(body);
request.response.close();
});
completer.complete();
});
}, onError: (e, stackTrace) => print('Error: $e $stackTrace'));
return completer.future;
}
Future<void> _closeServer() async {
if (this._server != null) {
final port = this._server.port;
await this._server.close(force: true);
print('Server running at http://127.0.0.1:$port closed');
this._server = null;
}
}
///Serve the [assetFilePath] from Flutter assets on http://localhost:[port]/. It is similar to [InAppBrowser.open()] with option `isLocalFile: true`, but it starts a server.
///
///**NOTE for iOS**: For the iOS Platform, you need to add the `NSAllowsLocalNetworking` key with `true` in the `Info.plist` file (See [ATS Configuration Basics](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35):
///```xml
///<key>NSAppTransportSecurity</key>
///<dict>
/// <key>NSAllowsLocalNetworking</key>
/// <true/>
///</dict>
///```
///The `NSAllowsLocalNetworking` key is available since **iOS 10**.
Future<void> openOnLocalhost(String assetFilePath, {int port = 8080, Map<String, String> headers = const {}, Map<String, dynamic> options = const {}}) async {
this._throwIsAlreadyOpened(message: 'Cannot open $assetFilePath!');
await this._startServer(port: port);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => 'http://localhost:${this._server.port}/' + assetFilePath);
args.putIfAbsent('headers', () => headers);
options["isLocalFile"] = false;
args.putIfAbsent('options', () => options);
args.putIfAbsent('useChromeSafariBrowser', () => false);
await _ChannelManager.channel.invokeMethod('open', args);
this._isOpened = true;
}
///Loads the given [url] with optional [headers] specified as a map from name to value.
Future<void> loadUrl(String url, {Map<String, String> headers = const {}}) async {
this._throwIsNotOpened(message: 'Cannot laod $url!');
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headers);
await _ChannelManager.channel.invokeMethod('loadUrl', args);
}
///Loads the given [assetFilePath] with optional [headers] specified as a map from name to value.
/// ///
///To be able to load your local files (assets, js, css, etc.), you need to add them in the `assets` section of the `pubspec.yaml` file, otherwise they cannot be found! ///To be able to load your local files (assets, js, css, etc.), you need to add them in the `assets` section of the `pubspec.yaml` file, otherwise they cannot be found!
/// ///
...@@ -391,16 +229,35 @@ class InAppBrowser { ...@@ -391,16 +229,35 @@ class InAppBrowser {
///Example of a `main.dart` file: ///Example of a `main.dart` file:
///```dart ///```dart
///... ///...
///inAppBrowser.loadFile("assets/index.html"); ///inAppBrowser.openFile("assets/index.html");
///... ///...
///``` ///```
Future<void> loadFile(String assetFilePath, {Map<String, String> headers = const {}}) async { Future<void> openFile(String assetFilePath, {Map<String, String> headers = const {}, Map<String, dynamic> options = const {}}) async {
this._throwIsNotOpened(message: 'Cannot laod $assetFilePath!'); assert(assetFilePath != null);
this._throwIsAlreadyOpened(message: 'Cannot open $assetFilePath!');
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => assetFilePath); args.putIfAbsent('url', () => assetFilePath);
args.putIfAbsent('headers', () => headers); args.putIfAbsent('headers', () => headers);
await _ChannelManager.channel.invokeMethod('loadFile', args); args.putIfAbsent('options', () => options);
args.putIfAbsent('openWithSystemBrowser', () => false);
args.putIfAbsent('isLocalFile', () => true);
args.putIfAbsent('useChromeSafariBrowser', () => false);
await _ChannelManager.channel.invokeMethod('open', args);
this._isOpened = true;
}
///This is a static method that opens an [url] in the system browser. You wont be able to use the [InAppBrowser] methods here!
static Future<void> openWithSystemBrowser(String url) async {
assert(url != null);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => "");
args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => {});
args.putIfAbsent('isLocalFile', () => false);
args.putIfAbsent('openWithSystemBrowser', () => true);
args.putIfAbsent('useChromeSafariBrowser', () => false);
return await _ChannelManager.channel.invokeMethod('open', args);
} }
///Displays an [InAppBrowser] window that was opened hidden. Calling this has no effect if the [InAppBrowser] was already visible. ///Displays an [InAppBrowser] window that was opened hidden. Calling this has no effect if the [InAppBrowser] was already visible.
...@@ -427,46 +284,6 @@ class InAppBrowser { ...@@ -427,46 +284,6 @@ class InAppBrowser {
await _ChannelManager.channel.invokeMethod('close', args); await _ChannelManager.channel.invokeMethod('close', args);
} }
///Reloads the [InAppBrowser] window.
Future<void> reload() async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
await _ChannelManager.channel.invokeMethod('reload', args);
}
///Goes back in the history of the [InAppBrowser] window.
Future<void> goBack() async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
await _ChannelManager.channel.invokeMethod('goBack', args);
}
///Goes forward in the history of the [InAppBrowser] window.
Future<void> goForward() async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
await _ChannelManager.channel.invokeMethod('goForward', args);
}
///Check if the Web View of the [InAppBrowser] instance is in a loading state.
Future<bool> isLoading() async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
return await _ChannelManager.channel.invokeMethod('isLoading', args);
}
///Stops the Web View of the [InAppBrowser] instance from loading.
Future<void> stopLoading() async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
await _ChannelManager.channel.invokeMethod('stopLoading', args);
}
///Check if the Web View of the [InAppBrowser] instance is hidden. ///Check if the Web View of the [InAppBrowser] instance is hidden.
Future<bool> isHidden() async { Future<bool> isHidden() async {
this._throwIsNotOpened(); this._throwIsNotOpened();
...@@ -475,67 +292,30 @@ class InAppBrowser { ...@@ -475,67 +292,30 @@ class InAppBrowser {
return await _ChannelManager.channel.invokeMethod('isHidden', args); return await _ChannelManager.channel.invokeMethod('isHidden', args);
} }
///Injects JavaScript code into the [InAppBrowser] window and returns the result of the evaluation. ///Sets the [InAppBrowser] options with the new [options] and evaluates them.
Future<String> injectScriptCode(String source) async { Future<void> setOptions(Map<String, dynamic> options) async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('source', () => source);
return await _ChannelManager.channel.invokeMethod('injectScriptCode', args);
}
///Injects a JavaScript file into the [InAppBrowser] window.
Future<void> injectScriptFile(String urlFile) async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('urlFile', () => urlFile);
await _ChannelManager.channel.invokeMethod('injectScriptFile', args);
}
///Injects CSS into the [InAppBrowser] window.
Future<void> injectStyleCode(String source) async {
this._throwIsNotOpened(); this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('source', () => source); args.putIfAbsent('options', () => options);
await _ChannelManager.channel.invokeMethod('injectStyleCode', args); args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
await _ChannelManager.channel.invokeMethod('setOptions', args);
} }
///Injects a CSS file into the [InAppBrowser] window. ///Gets the current [InAppBrowser] options. Returns `null` if the options are not setted yet.
Future<void> injectStyleFile(String urlFile) async { Future<Map<String, dynamic>> getOptions() async {
this._throwIsNotOpened(); this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('urlFile', () => urlFile); args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
await _ChannelManager.channel.invokeMethod('injectStyleFile', args); Map<dynamic, dynamic> options = await _ChannelManager.channel.invokeMethod('getOptions', args);
} options = options.cast<String, dynamic>();
return options;
///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 [InAppBrowser] instance is opened, otherwise `false`.
///Returns `true` if the callback is removed, otherwise `false`. bool isOpened() {
bool removeJavaScriptHandler(String handlerName, int index) { return this._isOpened;
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].
...@@ -553,66 +333,35 @@ class InAppBrowser { ...@@ -553,66 +333,35 @@ class InAppBrowser {
} }
///Event fires when the current [progress] (range 0-100) of loading a page is changed.
void onProgressChanged(int progress) {
}
///Event fires when the [InAppBrowser] window is closed. ///Event fires when the [InAppBrowser] window is closed.
void onExit() { void onExit() {
} }
///Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
///
///**NOTE**: In order to be able to listen this event, you need to set `useShouldOverrideUrlLoading` option to `true`.
void shouldOverrideUrlLoading(String url) {
}
///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/assets` encoding could be empty.
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
}
///Event fires when the [InAppBrowser] webview receives a [ConsoleMessage]. ///Event fires when the [InAppBrowser] webview receives a [ConsoleMessage].
void onConsoleMessage(ConsoleMessage consoleMessage) { void onConsoleMessage(ConsoleMessage consoleMessage) {
} }
///Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it. ///Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
/// ///
///**NOTE for iOS**: available from iOS 11.0+. ///**NOTE**: In order to be able to listen this event, you need to set `useShouldOverrideUrlLoading` option to `true`.
Future<Uint8List> takeScreenshot() async { void shouldOverrideUrlLoading(String url) {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
return await _ChannelManager.channel.invokeMethod('takeScreenshot', args);
}
///Sets the [InAppBrowser] options with the new [options] and evaluates them.
Future<void> setOptions(Map<String, dynamic> options) async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('options', () => options);
args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
await _ChannelManager.channel.invokeMethod('setOptions', args);
} }
///Gets the current [InAppBrowser] options. Returns `null` if the options are not setted yet. ///Event fires when the [InAppBrowser] webview loads a resource.
Future<Map<String, dynamic>> getOptions() async { ///
this._throwIsNotOpened(); ///**NOTE**: In order to be able to listen this event, you need to set `useOnLoadResource` option to `true`.
Map<String, dynamic> args = <String, dynamic>{}; ///
args.putIfAbsent('uuid', () => uuid); ///**NOTE only for iOS**: In some cases, the [response.data] of a [response] with `text/assets` encoding could be empty.
args.putIfAbsent('optionsType', () => "InAppBrowserOptions"); void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
Map<dynamic, dynamic> options = await _ChannelManager.channel.invokeMethod('getOptions', args);
options = options.cast<String, dynamic>();
return options;
}
///Returns `true` if the [InAppBrowser] instance is opened, otherwise `false`.
bool isOpened() {
return this._isOpened;
} }
void _throwIsAlreadyOpened({String message = ''}) { void _throwIsAlreadyOpened({String message = ''}) {
...@@ -639,7 +388,7 @@ class ChromeSafariBrowser { ...@@ -639,7 +388,7 @@ class ChromeSafariBrowser {
InAppBrowser browserFallback; InAppBrowser browserFallback;
bool _isOpened = false; bool _isOpened = false;
///Initialize the [ChromeSafariBrowser] instance with a [InAppBrowser] fallback instance or `null`. ///Initialize the [ChromeSafariBrowser] instance with an [InAppBrowser] fallback instance or `null`.
ChromeSafariBrowser (bf) { ChromeSafariBrowser (bf) {
uuid = _uuidGenerator.v4(); uuid = _uuidGenerator.v4();
browserFallback = bf; browserFallback = bf;
...@@ -664,7 +413,7 @@ class ChromeSafariBrowser { ...@@ -664,7 +413,7 @@ class ChromeSafariBrowser {
} }
} }
///Opens an [url] in a new [ChromeSafariBrowser] instance or the system browser. ///Opens an [url] in a new [ChromeSafariBrowser] instance.
/// ///
///- [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.
/// ///
...@@ -692,6 +441,7 @@ class ChromeSafariBrowser { ...@@ -692,6 +441,7 @@ class ChromeSafariBrowser {
///- __presentationStyle__: Set the custom modal presentation style when presenting the WebView. The default value is `0 //fullscreen`. See [UIModalPresentationStyle](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle) for all the available styles. ///- __presentationStyle__: Set the custom modal presentation style when presenting the WebView. The default value is `0 //fullscreen`. See [UIModalPresentationStyle](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle) for all the available styles.
///- __transitionStyle__: Set to the custom transition style when presenting the WebView. The default value is `0 //crossDissolve`. See [UIModalTransitionStyle](https://developer.apple.com/documentation/uikit/uimodaltransitionStyle) for all the available styles. ///- __transitionStyle__: Set to the custom transition style when presenting the WebView. The default value is `0 //crossDissolve`. See [UIModalTransitionStyle](https://developer.apple.com/documentation/uikit/uimodaltransitionStyle) for all the available styles.
Future<void> open(String url, {Map<String, dynamic> options = const {}, Map<String, String> headersFallback = const {}, Map<String, dynamic> optionsFallback = const {}}) async { Future<void> open(String url, {Map<String, dynamic> options = const {}, Map<String, String> headersFallback = const {}, Map<String, dynamic> optionsFallback = const {}}) async {
assert(url != null);
this._throwIsAlreadyOpened(message: 'Cannot open $url!'); this._throwIsAlreadyOpened(message: 'Cannot open $url!');
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
...@@ -736,3 +486,605 @@ class ChromeSafariBrowser { ...@@ -736,3 +486,605 @@ class ChromeSafariBrowser {
} }
} }
} }
typedef void onWebViewCreatedCallback(InAppWebViewController controller);
typedef void onWebViewLoadStartCallback(InAppWebViewController controller, String url);
typedef void onWebViewLoadStopCallback(InAppWebViewController controller, String url);
typedef void onWebViewLoadErrorCallback(InAppWebViewController controller, String url, int code, String message);
typedef void onWebViewProgressChangedCallback(InAppWebViewController controller, int progress);
typedef void onWebViewConsoleMessageCallback(InAppWebViewController controller, ConsoleMessage consoleMessage);
typedef void shouldOverrideUrlLoadingCallback(InAppWebViewController controller, String url);
typedef void onWebViewLoadResourceCallback(InAppWebViewController controller, WebResourceResponse response, WebResourceRequest request);
///InAppWebView Widget class.
///
///Flutter Widget for adding an **inline native WebView** integrated in the flutter widget tree.
///
///All platforms support these options:
/// - __useShouldOverrideUrlLoading__: Set to `true` to be able to listen at the [InAppWebView.shouldOverrideUrlLoading()] event. The default value is `false`.
/// - __useOnLoadResource__: Set to `true` to be able to listen at the [InAppWebView.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`.
/// - __userAgent___: Set the custom WebView's user-agent.
/// - __javaScriptEnabled__: Set to `true` to enable JavaScript. The default value is `true`.
/// - __javaScriptCanOpenWindowsAutomatically__: Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`.
/// - __mediaPlaybackRequiresUserGesture__: Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`.
///
/// **Android** supports these additional options:
///
/// - __clearSessionCache__: Set to `true` to have the session cookie cache cleared before the new window is opened.
/// - __builtInZoomControls__: Set to `true` if the WebView should use its built-in zoom mechanisms. The default value is `false`.
/// - __supportZoom__: Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`.
/// - __databaseEnabled__: Set to `true` if you want the database storage API is enabled. The default value is `false`.
/// - __domStorageEnabled__: Set to `true` if you want the DOM storage API is enabled. The default value is `false`.
/// - __useWideViewPort__: Set to `true` if the WebView should enable support for the "viewport" HTML meta tag or should use a wide viewport. When the value of the setting is false, the layout width is always set to the width of the WebView control in device-independent (CSS) pixels. When the value is true and the page contains the viewport meta tag, the value of the width specified in the tag is used. If the page does not contain the tag or does not provide a width, then a wide viewport will be used. The default value is `true`.
/// - __safeBrowsingEnabled__: Set to `true` if you want the Safe Browsing is enabled. Safe Browsing allows WebView to protect against malware and phishing attacks by verifying the links. The default value is `true`.
///
/// **iOS** supports these additional options:
///
/// - __disallowOverScroll__: Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`.
/// - __enableViewportScale__: Set to `true` to allow a viewport meta tag to either disable or restrict the range of user scaling. The default value is `false`.
/// - __suppressesIncrementalRendering__: Set to `true` if you want the WebView suppresses content rendering until it is fully loaded into memory.. The default value is `false`.
/// - __allowsAirPlayForMediaPlayback__: Set to `true` to allow AirPlay. The default value is `true`.
/// - __allowsBackForwardNavigationGestures__: Set to `true` to allow the horizontal swipe gestures trigger back-forward list navigations. The default value is `true`.
/// - __allowsLinkPreview__: Set to `true` to allow that pressing on a link displays a preview of the destination for the link. The default value is `true`.
/// - __ignoresViewportScaleLimits__: Set to `true` if you want that the WebView should always allow scaling of the webpage, regardless of the author's intent. The ignoresViewportScaleLimits property overrides the `user-scalable` HTML property in a webpage. The default value is `false`.
/// - __allowsInlineMediaPlayback__: Set to `true` to allow HTML5 media playback to appear inline within the screen layout, using browser-supplied controls rather than native controls. For this to work, add the `webkit-playsinline` attribute to any `<video>` elements. The default value is `false`.
/// - __allowsPictureInPictureMediaPlayback__: Set to `true` to allow HTML5 videos play picture-in-picture. The default value is `true`.
class InAppWebView extends StatefulWidget {
///Event fires when the [InAppWebView] is created.
final onWebViewCreatedCallback onWebViewCreated;
///Event fires when the [InAppWebView] starts to load an [url].
final onWebViewLoadStartCallback onLoadStart;
///Event fires when the [InAppWebView] finishes loading an [url].
final onWebViewLoadStopCallback onLoadStop;
///Event fires when the [InAppWebView] encounters an error loading an [url].
final onWebViewLoadErrorCallback onLoadError;
///Event fires when the current [progress] of loading a page is changed.
final onWebViewProgressChangedCallback onProgressChanged;
///Event fires when the [InAppWebView] receives a [ConsoleMessage].
final onWebViewConsoleMessageCallback onConsoleMessage;
///Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
///
///**NOTE**: In order to be able to listen this event, you need to set `useShouldOverrideUrlLoading` option to `true`.
final shouldOverrideUrlLoadingCallback shouldOverrideUrlLoading;
///Event fires when the [InAppWebView] 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/assets` encoding could be empty.
final onWebViewLoadResourceCallback onLoadResource;
///Initial url that will be loaded.
final String initialUrl;
///Initial asset file that will be loaded. See [InAppWebView.loadFile()] for explanation.
final String initialFile;
///Initial headers that will be used.
final Map<String, String> initialHeaders;
///Initial options that will be used.
final Map<String, dynamic> initialOptions;
final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;
const InAppWebView({
Key key,
this.initialUrl = "about:blank",
this.initialFile,
this.initialHeaders = const {},
this.initialOptions = const {},
this.onWebViewCreated,
this.onLoadStart,
this.onLoadStop,
this.onLoadError,
this.onConsoleMessage,
this.onProgressChanged,
this.shouldOverrideUrlLoading,
this.onLoadResource,
this.gestureRecognizers,
}) : super(key: key);
@override
_InAppWebViewState createState() => _InAppWebViewState();
}
class _InAppWebViewState extends State<InAppWebView> {
InAppWebViewController _controller;
@override
void dispose() {
super.dispose();
if (_controller != null) {
_controller._dispose();
_controller = null;
}
}
@override
Widget build(BuildContext context) {
if (defaultTargetPlatform == TargetPlatform.android) {
return GestureDetector(
onLongPress: () {},
child: AndroidView(
viewType: 'com.pichillilorenzo/flutter_inappwebview',
onPlatformViewCreated: _onPlatformViewCreated,
gestureRecognizers: widget.gestureRecognizers,
layoutDirection: TextDirection.rtl,
creationParams: <String, dynamic>{
'initialUrl': widget.initialUrl,
'initialFile': widget.initialFile,
'initialHeaders': widget.initialHeaders,
'initialOptions': widget.initialOptions
},
creationParamsCodec: const StandardMessageCodec(),
),
);
}
return Text(
'$defaultTargetPlatform is not yet supported by the flutter_inappbrowser plugin');
}
@override
void didUpdateWidget(InAppWebView oldWidget) {
super.didUpdateWidget(oldWidget);
}
void _onPlatformViewCreated(int id) {
_controller = InAppWebViewController(id, widget);
if (widget.onWebViewCreated != null) {
widget.onWebViewCreated(_controller);
}
}
}
/// Controls an [InAppWebView].
///
/// A [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated]
/// callback for an [InAppWebView] widget.
class InAppWebViewController {
InAppWebView _widget;
MethodChannel _channel;
Map<String, List<JavaScriptHandlerCallback>> javaScriptHandlersMap = HashMap<String, List<JavaScriptHandlerCallback>>();
bool _isOpened = false;
int _id;
String _inAppBrowserUuid;
InAppBrowser _inAppBrowser;
InAppWebViewController(int id, InAppWebView widget) {
_id = id;
_channel = MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id');
_channel.setMethodCallHandler(_handleMethod);
_widget = widget;
}
InAppWebViewController.fromInAppBrowser(String uuid, MethodChannel channel, InAppBrowser inAppBrowser) {
_inAppBrowserUuid = uuid;
_channel = channel;
_inAppBrowser = inAppBrowser;
}
Future<dynamic> _handleMethod(MethodCall call) async {
switch(call.method) {
case "onLoadStart":
String url = call.arguments["url"];
if (_widget != null)
_widget.onLoadStart(this, url);
else
_inAppBrowser.onLoadStart(url);
break;
case "onLoadStop":
String url = call.arguments["url"];
if (_widget != null)
_widget.onLoadStop(this, url);
else
_inAppBrowser.onLoadStop(url);
break;
case "onLoadError":
String url = call.arguments["url"];
int code = call.arguments["code"];
String message = call.arguments["message"];
if (_widget != null)
_widget.onLoadError(this, url, code, message);
else
_inAppBrowser.onLoadError(url, code, message);
break;
case "onProgressChanged":
int progress = call.arguments["progress"];
if (_widget != null)
_widget.onProgressChanged(this, progress);
else
_inAppBrowser.onProgressChanged(progress);
break;
case "shouldOverrideUrlLoading":
String url = call.arguments["url"];
if (_widget != null)
_widget.shouldOverrideUrlLoading(this, url);
else
_inAppBrowser.shouldOverrideUrlLoading(url);
break;
case "onLoadResource":
Map<dynamic, dynamic> rawResponse = call.arguments["response"];
rawResponse = rawResponse.cast<String, dynamic>();
Map<dynamic, dynamic> rawRequest = call.arguments["request"];
rawRequest = rawRequest.cast<String, dynamic>();
String urlResponse = rawResponse["url"];
Map<dynamic, dynamic> headersResponse = rawResponse["headers"];
headersResponse = headersResponse.cast<String, String>();
int statusCode = rawResponse["statusCode"];
int startTime = rawResponse["startTime"];
int duration = rawResponse["duration"];
Uint8List data = rawResponse["data"];
String urlRequest = rawRequest["url"];
Map<dynamic, dynamic> headersRequest = rawRequest["headers"];
headersRequest = headersResponse.cast<String, String>();
String method = rawRequest["method"];
var response = new WebResourceResponse(urlResponse, headersResponse, statusCode, startTime, duration, data);
var request = new WebResourceRequest(urlRequest, headersRequest, method);
if (_widget != null)
_widget.onLoadResource(this, response, request);
else
_inAppBrowser.onLoadResource(response, request);
break;
case "onConsoleMessage":
String sourceURL = call.arguments["sourceURL"];
int lineNumber = call.arguments["lineNumber"];
String message = call.arguments["message"];
ConsoleMessageLevel messageLevel;
ConsoleMessageLevel.values.forEach((element) {
if ("ConsoleMessageLevel." + call.arguments["messageLevel"] == element.toString()) {
messageLevel = element;
return;
}
});
if (_widget != null)
_widget.onConsoleMessage(this, ConsoleMessage(sourceURL, lineNumber, message, messageLevel));
else
_inAppBrowser.onConsoleMessage(ConsoleMessage(sourceURL, lineNumber, message, messageLevel));
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;
default:
throw UnimplementedError("Unimplemented ${call.method} method");
}
}
///Loads the given [url] with optional [headers] specified as a map from name to value.
Future<void> loadUrl(String url, {Map<String, String> headers = const {}}) async {
assert(url != null);
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened(message: 'Cannot laod $url!');
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headers);
await _channel.invokeMethod('loadUrl', args);
}
///Loads the given [assetFilePath] with optional [headers] specified as a map from name to value.
///
///To be able to load your local files (assets, js, css, etc.), you need to add them in the `assets` section of the `pubspec.yaml` file, otherwise they cannot be found!
///
///Example of a `pubspec.yaml` file:
///```yaml
///...
///
///# The following section is specific to Flutter.
///flutter:
///
/// # The following line ensures that the Material Icons font is
/// # included with your application, so that you can use the icons in
/// # the material Icons class.
/// uses-material-design: true
///
/// assets:
/// - assets/index.html
/// - assets/css/
/// - assets/images/
///
///...
///```
///Example of a `main.dart` file:
///```dart
///...
///inAppBrowser.loadFile("assets/index.html");
///...
///```
Future<void> loadFile(String assetFilePath, {Map<String, String> headers = const {}}) async {
assert(assetFilePath != null);
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened(message: 'Cannot laod $assetFilePath!');
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('url', () => assetFilePath);
args.putIfAbsent('headers', () => headers);
await _channel.invokeMethod('loadFile', args);
}
///Reloads the [InAppWebView] window.
Future<void> reload() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('reload', args);
}
///Goes back in the history of the [InAppWebView] window.
Future<void> goBack() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('goBack', args);
}
///Returns a Boolean value indicating whether the [InAppWebView] can move backward.
Future<bool> canGoBack() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('canGoBack', args);
}
///Goes forward in the history of the [InAppWebView] window.
Future<void> goForward() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('goForward', args);
}
///Returns a Boolean value indicating whether the [InAppWebView] can move forward.
Future<bool> canGoForward() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('canGoForward', args);
}
///Check if the Web View of the [InAppWebView] instance is in a loading state.
Future<bool> isLoading() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('isLoading', args);
}
///Stops the Web View of the [InAppWebView] instance from loading.
Future<void> stopLoading() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('stopLoading', args);
}
///Injects JavaScript code into the [InAppWebView] window and returns the result of the evaluation.
Future<String> injectScriptCode(String source) async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('source', () => source);
return await _channel.invokeMethod('injectScriptCode', args);
}
///Injects a JavaScript file into the [InAppWebView] window.
Future<void> injectScriptFile(String urlFile) async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('urlFile', () => urlFile);
await _channel.invokeMethod('injectScriptFile', args);
}
///Injects CSS into the [InAppWebView] window.
Future<void> injectStyleCode(String source) async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('source', () => source);
await _channel.invokeMethod('injectStyleCode', args);
}
///Injects a CSS file into the [InAppWebView] window.
Future<void> injectStyleFile(String urlFile) async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('urlFile', () => urlFile);
await _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;
}
///Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it.
///
///**NOTE for iOS**: available from iOS 11.0+.
Future<Uint8List> takeScreenshot() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('takeScreenshot', args);
}
///Sets the [InAppWebView] options with the new [options] and evaluates them.
Future<void> setOptions(Map<String, dynamic> options) async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('options', () => options);
args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
await _channel.invokeMethod('setOptions', args);
}
///Gets the current [InAppWebView] options. Returns `null` if the options are not setted yet.
Future<Map<String, dynamic>> getOptions() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null) {
_inAppBrowser._throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
Map<dynamic, dynamic> options = await _ChannelManager.channel.invokeMethod('getOptions', args);
options = options.cast<String, dynamic>();
return options;
}
Future<void> _dispose() async {
await _channel.invokeMethod('dispose');
}
}
///InAppLocalhostServer class.
///
///This class allows you to create a simple server on `http://localhost:[port]/` in order to be able to load your assets file on a server. The default [port] value is `8080`.
class InAppLocalhostServer {
HttpServer _server;
int _port = 8080;
InAppLocalhostServer({int port = 8080}) {
this._port = port;
}
///Starts a server on http://localhost:[port]/.
///
///**NOTE for iOS**: For the iOS Platform, you need to add the `NSAllowsLocalNetworking` key with `true` in the `Info.plist` file (See [ATS Configuration Basics](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35)):
///```xml
///<key>NSAppTransportSecurity</key>
///<dict>
/// <key>NSAllowsLocalNetworking</key>
/// <true/>
///</dict>
///```
///The `NSAllowsLocalNetworking` key is available since **iOS 10**.
Future<void> start() async {
if (this._server != null) {
throw Exception('Server already started on http://localhost:$_port');
}
var completer = new Completer();
runZoned(() {
HttpServer.bind('127.0.0.1', _port).then((server) {
print('Server running on http://localhost:' + _port.toString());
this._server = server;
server.listen((HttpRequest request) async {
var body = List<int>();
var path = request.requestedUri.path;
path = (path.startsWith('/')) ? path.substring(1) : path;
path += (path.endsWith('/')) ? 'index.html' : '';
try {
body = (await rootBundle.load(path))
.buffer.asUint8List();
} catch (e) {
print(e.toString());
request.response.close();
return;
}
var contentType = ['text', 'html'];
if (!request.requestedUri.path.endsWith('/') && request.requestedUri.pathSegments.isNotEmpty) {
var mimeType = lookupMimeType(request.requestedUri.path, headerBytes: body);
if (mimeType != null) {
contentType = mimeType.split('/');
}
}
print(contentType);
request.response.headers.contentType = new ContentType(contentType[0], contentType[1], charset: 'utf-8');
request.response.add(body);
request.response.close();
});
completer.complete();
});
}, onError: (e, stackTrace) => print('Error: $e $stackTrace'));
return completer.future;
}
///Closes the server.
Future<void> close() async {
if (this._server != null) {
await this._server.close(force: true);
print('Server running on http://localhost:$_port closed');
this._server = null;
}
}
}
\ No newline at end of file
name: flutter_inappbrowser name: flutter_inappbrowser
description: A Flutter plugin that allows you to open an in-app browser window. (inspired by the popular cordova-plugin-inappbrowser). description: A Flutter plugin that allows you to add an inline webview or open an in-app browser window. (inspired by the popular cordova-plugin-inappbrowser).
version: 0.4.1 version: 0.5.0
author: Lorenzo Pichilli <pichillilorenzo@gmail.com> author: Lorenzo Pichilli <pichillilorenzo@gmail.com>
homepage: https://github.com/pichillilorenzo/flutter_inappbrowser homepage: https://github.com/pichillilorenzo/flutter_inappbrowser
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment