This commit is contained in:
pichillilorenzo 2018-10-16 01:27:58 +02:00
parent c7356f33c8
commit 860a42e2ba
26 changed files with 1751 additions and 1237 deletions

281
.idea/workspace.xml generated
View File

@ -15,13 +15,30 @@
</component>
<component name="ChangeListManager">
<list default="true" id="9b41f7a2-a71e-4923-91fb-249d7815b3e7" name="Default" comment="">
<change afterPath="$PROJECT_DIR$/example/assets/css/style.css" afterDir="false" />
<change afterPath="$PROJECT_DIR$/example/assets/images/dart.svg" afterDir="false" />
<change afterPath="$PROJECT_DIR$/example/assets/index.html" 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$/example/ios/Runner.xcodeproj/project.pbxproj" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Runner.xcodeproj/project.pbxproj" 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" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebChromeClient.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebViewClient.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebViewClient.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/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/WebViewActivity.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/ChromeCustomTabsActivity.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/ChromeCustomTabsActivity.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/CustomTabActivityHelper.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/CustomTabActivityHelper.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/ios/Runner/Info.plist" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Runner/Info.plist" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/lib/main.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/main.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/pubspec.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/example/pubspec.yaml" 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/Options.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/Options.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/SafariViewController.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/SafariViewController.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/en.lproj/WebView.storyboard" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" beforeDir="false" afterPath="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/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>
@ -43,20 +60,25 @@
<file leaf-file-name="flutter_inappbrowser.dart" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/flutter_inappbrowser.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="5791">
<caret line="429" column="3" selection-start-line="429" selection-start-column="3" selection-end-line="429" selection-end-column="3" />
<state relative-caret-position="128">
<caret line="82" column="21" selection-start-line="82" selection-start-column="6" selection-end-line="82" selection-end-column="21" />
<folding>
<element signature="e#814#834#0" expanded="true" />
<element signature="e#814#831#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</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="30">
<caret line="2" column="14" selection-start-line="2" selection-start-column="14" selection-end-line="2" selection-end-column="14" />
<file leaf-file-name="README.md" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="4980">
<caret line="332" column="20" lean-forward="true" selection-start-line="332" selection-start-column="20" selection-end-line="332" selection-end-column="41" />
</first_editor>
<second_editor>
<markdownNavigatorState />
</second_editor>
</state>
</provider>
</entry>
@ -65,8 +87,8 @@
<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="30">
<caret line="2" column="39" selection-start-line="2" selection-start-column="39" selection-end-line="2" selection-end-column="39" />
<first_editor relative-caret-position="135">
<caret line="9" lean-forward="true" selection-start-line="9" selection-end-line="9" />
</first_editor>
<second_editor>
<markdownNavigatorState />
@ -75,6 +97,18 @@
</provider>
</entry>
</file>
<file leaf-file-name="main.dart" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="165">
<caret line="165" column="42" selection-start-line="165" selection-start-column="42" selection-end-line="165" selection-end-column="42" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
@ -86,11 +120,6 @@
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>loadUrl</find>
<find>showWebPage</find>
<find>InAppBrowserClient</find>
<find>LOG.</find>
<find>preferences</find>
<find>getPre</find>
<find>client</find>
<find>webView</find>
@ -116,6 +145,11 @@
<find>presentationStyle</find>
<find>###</find>
<find>inAppBrowserFallback</find>
<find>target</find>
<find>.assets</find>
<find>_blank</find>
<find>.html</find>
<find>close(</find>
</findStrings>
<replaceStrings>
<replace>activity.getPreferences(0)</replace>
@ -142,7 +176,6 @@
<option value="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutterwebview/InAppBrowserClient.java" />
<option value="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutterwebview/InAppBrowserDialog.java" />
<option value="$PROJECT_DIR$/android/settings.gradle" />
<option value="$PROJECT_DIR$/example/pubspec.yaml" />
<option value="$PROJECT_DIR$/ios/Classes/InAppBrowser.m" />
<option value="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" />
<option value="$PROJECT_DIR$/LICENSE" />
@ -152,17 +185,23 @@
<option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" />
<option value="$PROJECT_DIR$/example/ios/Podfile" />
<option value="$PROJECT_DIR$/android/build.gradle" />
<option value="$PROJECT_DIR$/example/lib/main.dart" />
<option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" />
<option value="$PROJECT_DIR$/README.md" />
<option value="$PROJECT_DIR$/example/html/css/style.css" />
<option value="$PROJECT_DIR$/example/html/index.html" />
<option value="$PROJECT_DIR$/pubspec.yaml" />
<option value="$PROJECT_DIR$/example/assets/css/style.css" />
<option value="$PROJECT_DIR$/example/assets/index.html" />
<option value="$PROJECT_DIR$/example/pubspec.yaml" />
<option value="$PROJECT_DIR$/example/lib/main.dart" />
<option value="$PROJECT_DIR$/README.md" />
<option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" />
<option value="$PROJECT_DIR$/CHANGELOG.md" />
</list>
</option>
</component>
<component name="ProjectFrameBounds">
<option name="y" value="23" />
<option name="width" value="1920" />
<option name="x" value="-1920" />
<option name="y" value="-21" />
<option name="width" value="1711" />
<option name="height" value="1057" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
@ -171,6 +210,18 @@
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scope">
<subPane subId="Project Files">
<expand>
<path>
<item name="Root" type="cbb8eebc:String" user="Root" />
<item name="flutter_inappbrowser" type="cbb8eebc:String" user="flutter_inappbrowser" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="PackagesPane" />
<pane id="ProjectPane">
<subPane>
<expand>
@ -183,6 +234,23 @@
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="android" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
<item name="assets" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
@ -193,19 +261,7 @@
</subPane>
<option name="show-excluded-files" value="false" />
</pane>
<pane id="PackagesPane" />
<pane id="AndroidView" />
<pane id="Scope">
<subPane subId="Project Files">
<expand>
<path>
<item name="Root" type="cbb8eebc:String" user="Root" />
<item name="flutter_inappbrowser" type="cbb8eebc:String" user="flutter_inappbrowser" />
</path>
</expand>
<select />
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
@ -224,12 +280,13 @@
<property name="show.migrate.to.gradle.popup" value="false" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/android/libs" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/android/src/main/java" />
</key>
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/example/assets/images" />
<recent name="$PROJECT_DIR$/android/libs" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
@ -361,7 +418,7 @@
<servers />
</component>
<component name="ToolWindowManager">
<frame x="0" y="23" width="1920" height="1057" extended-state="0" />
<frame x="-1920" y="-21" width="1711" height="1057" extended-state="0" />
<editor active="true" />
<layout>
<window_info anchor="bottom" id="Android Profiler" order="7" show_stripe_button="false" />
@ -372,22 +429,22 @@
<window_info anchor="right" id="Capture Analysis" order="3" />
<window_info anchor="bottom" id="Event Log" order="8" sideWeight="0.5035553" side_tool="true" weight="0.25689086" />
<window_info anchor="bottom" id="Dart Analysis" order="14" weight="0.32855567" />
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.49644473" weight="0.20384204" />
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.49644473" weight="0.3628602" />
<window_info anchor="bottom" id="Version Control" order="9" />
<window_info anchor="bottom" id="Terminal" order="10" sideWeight="0.49644473" visible="true" weight="0.25506938" />
<window_info anchor="bottom" id="Terminal" order="10" sideWeight="0.49644473" visible="true" weight="0.2529349" />
<window_info anchor="right" id="Flutter Outline" order="3" weight="0.32922077" />
<window_info anchor="bottom" id="Logcat" order="11" />
<window_info id="Captures" order="2" weight="0.32936507" />
<window_info id="Capture Tool" order="2" />
<window_info id="Designer" order="2" />
<window_info active="true" content_ui="combo" id="Project" order="0" sideWeight="0.49855492" visible="true" weight="0.15069222" />
<window_info id="Structure" order="1" sideWeight="0.5014451" side_tool="true" weight="0.18293472" />
<window_info active="true" content_ui="combo" id="Project" order="0" sideWeight="0.49724367" visible="true" weight="0.16956261" />
<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="Theme Preview" order="3" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="right" id="Flutter Inspector" order="3" weight="0.32987013" />
<window_info anchor="bottom" id="Messages" order="12" weight="0.2370452" />
<window_info anchor="bottom" id="Messages" order="12" weight="0.23692636" />
<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="Assistant" order="4" visible="true" weight="0.32987013" />
@ -396,13 +453,12 @@
<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="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="bottom" id="Find" order="1" weight="0.328125" />
<window_info anchor="bottom" id="Find" order="1" weight="0.3276414" />
</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 id="Image Layers" order="5" />
<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" />
@ -414,25 +470,29 @@
<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 content_ui="combo" id="Project" order="0" visible="true" weight="0.18376623" />
<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 anchor="bottom" id="Inspection" order="5" weight="0.4" />
<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 anchor="bottom" id="TODO" order="6" />
<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 name="UnknownFeatures">
<option featureType="com.intellij.fileTypeFactory" implementationName="*.css" />
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
@ -442,21 +502,6 @@
</breakpoint-manager>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="270">
<caret line="18" column="34" lean-forward="true" selection-start-line="18" selection-start-column="34" selection-end-line="18" selection-end-column="34" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="135">
<caret line="9" column="34" lean-forward="true" selection-start-line="9" selection-start-column="34" selection-end-line="9" selection-end-column="34" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutterwebview/WebViewActivity.java" />
<entry file="file://$PROJECT_DIR$/example/android/app/src/main/java/com/pichillilorenzo/flutterwebviewexample/MainActivity.java">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="135">
@ -584,13 +629,6 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret column="6" selection-start-column="6" selection-end-column="26" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1560">
@ -647,30 +685,6 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret line="1" column="64" selection-start-line="1" selection-start-column="64" selection-end-line="1" selection-end-column="64" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="256">
<caret line="52" column="66" selection-start-line="52" selection-start-column="66" selection-end-line="52" selection-end-column="66" />
</first_editor>
<second_editor>
<markdownNavigatorState />
</second_editor>
</state>
</provider>
<provider editor-type-id="text-editor">
<state relative-caret-position="247">
<caret line="19" column="15" selection-start-line="19" selection-start-column="3" selection-end-line="19" selection-end-column="15" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/flutter_inappbrowser.iml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="330">
@ -692,39 +706,92 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/flutter_inappbrowser.dart">
<entry file="file://$PROJECT_DIR$/example/assets/images/dart.svg">
<provider selected="true" editor-type-id="images">
<state />
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/assets/css/style.css">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="5791">
<caret line="429" column="3" selection-start-line="429" selection-start-column="3" selection-end-line="429" selection-end-column="3" />
<folding>
<element signature="e#814#834#0" expanded="true" />
</folding>
<state relative-caret-position="75">
<caret line="5" column="20" selection-start-line="5" selection-start-column="20" selection-end-line="5" selection-end-column="20" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/assets/index.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="435">
<caret line="29" column="29" selection-start-line="29" selection-start-column="29" selection-end-line="29" selection-end-column="29" />
</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 relative-caret-position="180">
<caret line="12" column="14" selection-start-line="12" selection-start-column="14" selection-end-line="12" selection-end-column="14" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="405">
<caret line="27" column="13" selection-start-line="27" selection-start-column="13" selection-end-line="27" selection-end-column="13" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="165">
<caret line="165" column="42" selection-start-line="165" selection-start-column="42" selection-end-line="165" selection-end-column="42" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/flutter_inappbrowser.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="128">
<caret line="82" column="21" selection-start-line="82" selection-start-column="6" selection-end-line="82" selection-end-column="21" />
<folding>
<element signature="e#814#831#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider editor-type-id="text-editor">
<state relative-caret-position="247">
<caret line="19" column="15" selection-start-line="19" selection-start-column="3" selection-end-line="19" selection-end-column="15" />
</state>
</provider>
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="30">
<caret line="2" column="39" selection-start-line="2" selection-start-column="39" selection-end-line="2" selection-end-column="39" />
<first_editor relative-caret-position="4980">
<caret line="332" column="20" lean-forward="true" selection-start-line="332" selection-start-column="20" selection-end-line="332" selection-end-column="41" />
</first_editor>
<second_editor>
<markdownNavigatorState />
</second_editor>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider editor-type-id="text-editor">
<state relative-caret-position="30">
<caret line="2" column="69" lean-forward="true" selection-start-line="2" selection-start-column="2" selection-end-line="2" selection-end-column="73" />
</state>
</provider>
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="135">
<caret line="9" lean-forward="true" selection-start-line="9" selection-end-line="9" />
</first_editor>
<second_editor>
<markdownNavigatorState />
</second_editor>
</state>
</provider>
</entry>
</component>
<component name="masterDetails">

View File

@ -1,3 +1,13 @@
## 0.4.0
- removed `target` parameter to `InAppBrowser.open()` method. To open the url on the system browser, use the `openWithSystemBrowser: true` option
- fixes for the `_ChannelManager` private class
- fixed `EXC_BAD_INSTRUCTION` onLoadStart in Swift
- added `openWithSystemBrowser` and `isLocalFile` options
- added `InAppBrowser.openWithSystemBrowser` method
- added `InAppBrowser.openOnLocalhost` method
- added `InAppBrowser.loadFile` method
## 0.3.2
- fixed WebView.storyboard path for iOS

147
README.md
View File

@ -56,13 +56,13 @@ class MyInAppBrowser extends InAppBrowser {
await this.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.injectScriptCode("console.error('testError', false);"); // the message will be: testError false
// add jquery library and custom javascript
await this.injectScriptFile("https://code.jquery.com/jquery-3.3.1.min.js");
this.injectScriptCode("""
\$( "body" ).html( "Next Step..." )
""");
// add custom css
this.injectStyleCode("""
body {
@ -81,7 +81,7 @@ class MyInAppBrowser extends InAppBrowser {
void onExit() {
print("\n\nBrowser closed!\n\n");
}
@override
void shouldOverrideUrlLoading(String url) {
print("\n\n override $url\n\n");
@ -136,8 +136,8 @@ class _MyAppState extends State<MyApp> {
title: const Text('Flutter InAppBrowser Plugin example app'),
),
body: new Center(
child: new RaisedButton(onPressed: () {
inAppBrowser.open(url: "https://flutter.io/", options: {
child: new RaisedButton(onPressed: () async {
await inAppBrowser.open(url: "https://flutter.io/", options: {
"useShouldOverrideUrlLoading": true,
"useOnLoadResource": true
});
@ -155,8 +155,10 @@ class _MyAppState extends State<MyApp> {
Opens a URL in a new InAppBrowser instance or the system browser.
**NOTE**: If you open the given `url` with the system browser (`openWithSystemBrowser: true`), you wont be able to use the `InAppBrowser` methods!
```dart
inAppBrowser.open({String url = "about:blank", Map<String, String> headers = const {}, String target = "_self", Map<String, dynamic> options = const {}});
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.
@ -165,17 +167,13 @@ Opens an `url` in a new `InAppBrowser` instance or the system browser.
- `headers`: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value.
- `target`: The target in which to load the `url`, an optional parameter that defaults to `_self`.
- `_self`: Opens in the `InAppBrowser`.
- `_blank`: Opens in the `InAppBrowser`.
- `_system`: Opens in the system's web browser.
- `options`: Options for the `InAppBrowser`.
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`.
- __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`.
- __userAgent___: Set the custom WebView's user-agent.
- __javaScriptEnabled__: Set to `true` to enable JavaScript. The default value is `true`.
@ -185,9 +183,9 @@ Opens an `url` in a new `InAppBrowser` instance or the system browser.
- __toolbarTopBackgroundColor__: Set the custom background color of the toolbat at the top.
- __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`.
**Android** supports these additional options:
- __hideTitleBar__: Set to `true` if you want the title should be displayed. The default value is `false`.
- __closeOnCannotGoBack__: Set to `false` to not close the InAppBrowser when the user click on the back button and the WebView cannot go back to the history. The default value is `true`.
- __clearSessionCache__: Set to `true` to have the session cookie cache cleared before the new window is opened.
@ -200,14 +198,14 @@ Opens an `url` in a new `InAppBrowser` instance or the system browser.
- __progressBar__: Set to `false` to hide the progress bar at the bottom of the toolbar at the top. 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`.
- __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.
- __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.
- __closeButtonColor__: Set the custom color for the close button.
- __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.
- __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`.
@ -218,7 +216,7 @@ Opens an `url` in a new `InAppBrowser` instance or the system browser.
- __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`.
- __spinner__: Set to `false` to hide the spinner when the WebView is loading a page. The default value is `true`.
Example:
```dart
inAppBrowser.open('https://flutter.io/', options: {
@ -231,7 +229,34 @@ inAppBrowser.open('https://flutter.io/', options: {
"toolbarBottomTranslucent": false,
"allowsLinkPreview": false
});
```
```
#### 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
@ -239,7 +264,7 @@ Event fires when the `InAppBrowser` starts to load an `url`.
```dart
@override
void onLoadStart(String url) {
}
```
@ -247,7 +272,7 @@ Event fires when the `InAppBrowser` finishes loading an `url`.
```dart
@override
void onLoadStop(String url) {
}
```
@ -255,7 +280,7 @@ Event fires when the `InAppBrowser` encounters an error loading an `url`.
```dart
@override
void onLoadError(String url, String code, String message) {
}
```
@ -263,7 +288,7 @@ Event fires when the `InAppBrowser` window is closed.
```dart
@override
void onExit() {
}
```
@ -305,13 +330,49 @@ Loads the given `url` with optional `headers` specified as a map from name to va
inAppBrowser.loadUrl(String url, {Map<String, String> headers = const {}});
```
#### Future\<void\> InAppBrowser.loadFile
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");
...
```
```dart
inAppBrowser.loadFile(String assetFilePath, {Map<String, String> headers = const {}});
```
#### Future\<void\> InAppBrowser.show
Displays an `InAppBrowser` window that was opened hidden. Calling this has no effect if the `InAppBrowser` was already visible.
```dart
inAppBrowser.show();
```
```
#### Future\<void\> InAppBrowser.hide
@ -319,7 +380,7 @@ Hides the `InAppBrowser` window. Calling this has no effect if the `InAppBrowser
```dart
inAppBrowser.hide();
```
```
#### Future\<void\> InAppBrowser.close
@ -327,7 +388,7 @@ Closes the `InAppBrowser` window.
```dart
inAppBrowser.close();
```
```
#### Future\<void\> InAppBrowser.reload
@ -335,7 +396,7 @@ Reloads the `InAppBrowser` window.
```dart
inAppBrowser.reload();
```
```
#### Future\<void\> InAppBrowser.goBack
@ -343,7 +404,7 @@ Goes back in the history of the `InAppBrowser` window.
```dart
inAppBrowser.goBack();
```
```
#### Future\<void\> InAppBrowser.goForward
@ -351,7 +412,7 @@ Goes forward in the history of the `InAppBrowser` window.
```dart
inAppBrowser.goForward();
```
```
#### Future\<bool\> InAppBrowser.isLoading
@ -359,7 +420,7 @@ Check if the Web View of the `InAppBrowser` instance is in a loading state.
```dart
inAppBrowser.isLoading();
```
```
#### Future\<void\> InAppBrowser.stopLoading
@ -367,7 +428,7 @@ Stops the Web View of the `InAppBrowser` instance from loading.
```dart
inAppBrowser.stopLoading();
```
```
#### Future\<bool\> InAppBrowser.isHidden
@ -375,35 +436,35 @@ Check if the Web View of the `InAppBrowser` instance is hidden.
```dart
inAppBrowser.isHidden();
```
```
#### Future\<String\> InAppBrowser.injectScriptCode
Injects JavaScript code into the `InAppBrowser` window and returns the result of the evaluation. (Only available when the target is set to `_blank` or to `_self`)
Injects JavaScript code into the `InAppBrowser` window and returns the result of the evaluation.
```dart
inAppBrowser.injectScriptCode(String source);
```
```
#### Future\<void\> InAppBrowser.injectScriptFile
Injects a JavaScript file into the `InAppBrowser` window. (Only available when the target is set to `_blank` or to `_self`)
Injects a JavaScript file into the `InAppBrowser` window.
```dart
inAppBrowser.injectScriptFile(String urlFile);
```
```
#### Future\<void\> InAppBrowser.injectStyleCode
Injects CSS into the `InAppBrowser` window. (Only available when the target is set to `_blank` or to `_self`)
Injects CSS into the `InAppBrowser` window.
```dart
inAppBrowser.injectStyleCode(String source);
```
```
#### Future\<void\> InAppBrowser.injectStyleFile
Injects a CSS file into the `InAppBrowser` window. (Only available when the target is set to `_blank` or to `_self`)
Injects a CSS file into the `InAppBrowser` window.
```dart
inAppBrowser.injectStyleFile(String urlFile);
@ -462,13 +523,13 @@ class MyInAppBrowser extends InAppBrowser {
void onExit() {
print("\n\nBrowser closed!\n\n");
}
}
MyInAppBrowser inAppBrowserFallback = new MyInAppBrowser();
class MyChromeSafariBrowser extends ChromeSafariBrowser {
MyChromeSafariBrowser(browserFallback) : super(browserFallback);
@override
@ -579,7 +640,7 @@ Event fires when the `ChromeSafariBrowser` is opened.
```dart
@override
void onOpened() {
}
```
@ -587,7 +648,7 @@ Event fires when the `ChromeSafariBrowser` is loaded.
```dart
@override
void onLoaded() {
}
```
@ -595,7 +656,7 @@ Event fires when the `ChromeSafariBrowser` is closed.
```dart
@override
void onClosed() {
}
```

View File

@ -25,6 +25,7 @@ import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.AssetManager;
import android.os.Parcelable;
import android.provider.Browser;
import android.net.Uri;
@ -40,8 +41,10 @@ import android.util.Log;
import com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs.ChromeCustomTabsActivity;
import com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs.ChromeCustomTabsOptions;
import com.pichillilorenzo.flutter_inappbrowser.chrome_custom_tabs.CustomTabActivityHelper;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.util.ArrayList;
@ -55,7 +58,9 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
/** InAppBrowserFlutterPlugin */
/**
* InAppBrowserFlutterPlugin
*/
public class InAppBrowserFlutterPlugin implements MethodCallHandler {
public static Registrar registrar;
@ -64,9 +69,9 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
public static Map<String, WebViewActivity> webViewActivities = new HashMap<>();
public static Map<String, ChromeCustomTabsActivity> chromeCustomTabsActivities = new HashMap<>();
private static final String NULL = "null";
protected static final String LOG_TAG = "InAppBrowserFlutterP";
protected static final String LOG_TAG = "IABFlutterPlugin";
static final String ANDROID_ASSET_URL = "file:///android_asset/";
public InAppBrowserFlutterPlugin(Registrar r, Activity activity) {
registrar = r;
@ -74,7 +79,9 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
channel = new MethodChannel(registrar.messenger(), "com.pichillilorenzo/flutter_inappbrowser");
}
/** Plugin registration. */
/**
* Plugin registration.
*/
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "com.pichillilorenzo/flutter_inappbrowser");
channel.setMethodCallHandler(new InAppBrowserFlutterPlugin(registrar, registrar.activity()));
@ -89,15 +96,7 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
switch (call.method) {
case "open":
final String url = call.argument("url").toString();
String t = call.argument("target").toString();
if (t == null || t.equals("") || t.equals(NULL)) {
t = "_self";
}
final String target = t;
Log.d(LOG_TAG, "target = " + target);
final String url_final = call.argument("url").toString();
final boolean useChromeSafariBrowser = (boolean) call.argument("useChromeSafariBrowser");
@ -119,19 +118,48 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
final InAppBrowserOptions optionsFallback = new InAppBrowserOptions();
optionsFallback.parse((HashMap<String, Object>) call.argument("optionsFallback"));
open(uuid, uuidFallback, url, options, headers,true, optionsFallback);
}
else {
open(uuid, uuidFallback, url_final, options, headers, true, optionsFallback, result);
} else {
String url = url_final;
final InAppBrowserOptions options = new InAppBrowserOptions();
options.parse((HashMap<String, Object>) call.argument("options"));
if ("_self".equals(target)) {
Log.d(LOG_TAG, "in self");
if (options.isLocalFile) {
// check if the asset file exists
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 (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (!assetExists) {
result.error(LOG_TAG, key + " asset file cannot be found!", null);
return;
}
url = ANDROID_ASSET_URL + key;
}
// SYSTEM
if (options.openWithSystemBrowser) {
Log.d(LOG_TAG, "in system");
openExternal(url, result);
}
else {
//Load the dialer
if (url.startsWith(WebView.SCHEME_TEL))
{
if (url.startsWith(WebView.SCHEME_TEL)) {
try {
Log.d(LOG_TAG, "loading in dialer");
Intent intent = new Intent(Intent.ACTION_DIAL);
@ -144,31 +172,21 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
// load in InAppBrowserFlutterPlugin
else {
Log.d(LOG_TAG, "loading in InAppBrowserFlutterPlugin");
open(uuid, null, url, options, headers, false, null);
open(uuid, null, url, options, headers, false, null, result);
}
}
// SYSTEM
else if ("_system".equals(target)) {
Log.d(LOG_TAG, "in system");
openExternal(url);
}
// BLANK - or anything else
else {
Log.d(LOG_TAG, "in blank");
open(uuid, null, url, options, headers, false, null);
}
}
result.success(true);
}
});
break;
case "loadUrl":
loadUrl(uuid, call.argument("url").toString(), (Map<String, String>) call.argument("headers"), result);
break;
case "loadFile":
loadFile(uuid, call.argument("url").toString(), (Map<String, String>) call.argument("headers"), result);
break;
case "close":
close(uuid);
result.success(true);
close(uuid, result);
break;
case "injectScriptCode":
source = call.argument("source").toString();
@ -237,18 +255,19 @@ 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 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) {
@ -259,7 +278,7 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
org.json.JSONArray jsonEsc = new org.json.JSONArray();
jsonEsc.put(source);
String jsonRepr = jsonEsc.toString();
String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
String jsonSourceString = jsonRepr.substring(1, jsonRepr.length() - 1);
scriptToInject = String.format(jsWrapper, jsonSourceString);
} else {
scriptToInject = source;
@ -295,8 +314,7 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
msg = reader2.nextString();
result.success(msg);
}
else {
} else {
result.success("");
}
@ -332,9 +350,10 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
* Display a new browser with the specified URL.
*
* @param url the url to load.
* @param result
* @return "" if ok, or error message.
*/
public void openExternal(String url) {
public void openExternal(String url, Result result) {
try {
Intent intent;
intent = new Intent(Intent.ACTION_VIEW);
@ -349,9 +368,11 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName());
// CB-10795: Avoid circular loops by preventing it from opening in the current app
this.openExternalExcludeCurrentApp(intent);
result.success(true);
// not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it
} catch (java.lang.RuntimeException e) {
Log.d(LOG_TAG, "InAppBrowserFlutterPlugin: Error loading url "+url+":"+ e.toString());
Log.d(LOG_TAG, url + " cannot be opened: " + e.toString());
result.error(LOG_TAG, url + " cannot be opened!", null);
}
}
@ -367,11 +388,10 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
ArrayList<Intent> targetIntents = new ArrayList<Intent>();
for (ResolveInfo ri : activities) {
if (!currentPackage.equals(ri.activityInfo.packageName)) {
Intent targetIntent = (Intent)intent.clone();
Intent targetIntent = (Intent) intent.clone();
targetIntent.setPackage(ri.activityInfo.packageName);
targetIntents.add(targetIntent);
}
else {
} else {
hasCurrentPackage = true;
}
}
@ -386,35 +406,57 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
}
// Otherwise, show a custom chooser without the current app listed
else if (targetIntents.size() > 0) {
Intent chooser = Intent.createChooser(targetIntents.remove(targetIntents.size()-1), null);
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[] {}));
Intent chooser = Intent.createChooser(targetIntents.remove(targetIntents.size() - 1), null);
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[]{}));
activity.startActivity(chooser);
}
}
public static void open(String uuid, String uuidFallback, final String url, Options options, Map<String, String> headers, boolean useChromeSafariBrowser, InAppBrowserOptions optionsFallback) {
Intent intent = new Intent(registrar.activity(), (useChromeSafariBrowser) ? ChromeCustomTabsActivity.class : WebViewActivity.class);
public void open(String uuid, String uuidFallback, String url, Options options, Map<String, String> headers, boolean useChromeSafariBrowser, InAppBrowserOptions optionsFallback, Result result) {
Intent intent = null;
Bundle extras = new Bundle();
extras.putString("uuid", uuid);
extras.putString("url", url);
extras.putString("uuid", uuid);
extras.putSerializable("options", options.getHashMap());
extras.putSerializable("headers", (Serializable) headers);
if (useChromeSafariBrowser) {
extras.putString("uuidFallback", uuidFallback);
if (useChromeSafariBrowser && CustomTabActivityHelper.isAvailable(activity)) {
intent = new Intent(activity, ChromeCustomTabsActivity.class);
}
// check for webview fallback
else if (useChromeSafariBrowser && !CustomTabActivityHelper.isAvailable(activity) && !uuidFallback.isEmpty()) {
Log.d(LOG_TAG, "WebView fallback declared.");
// overwrite with extras fallback parameters
extras.putString("uuid", uuidFallback);
if (optionsFallback != null)
extras.putSerializable("optionsFallback", optionsFallback.getHashMap());
extras.putSerializable("options", optionsFallback.getHashMap());
else
extras.putSerializable("optionsFallback", (new InAppBrowserOptions()).getHashMap());
extras.putSerializable("options", (new InAppBrowserOptions()).getHashMap());
extras.putSerializable("headers", (Serializable) headers);
intent = new Intent(activity, WebViewActivity.class);
}
// native webview
else if (!useChromeSafariBrowser) {
intent = new Intent(activity, WebViewActivity.class);
}
else {
Log.d(LOG_TAG, "No WebView fallback declared.");
}
intent.putExtras(extras);
if (intent != null) {
intent.putExtras(extras);
activity.startActivity(intent);
result.success(true);
return;
}
registrar.activity().startActivity(intent);
result.error(LOG_TAG, "No WebView fallback declared.", null);
}
public void loadUrl(String uuid, String url, Map<String, String> headers, Result result) {
WebViewActivity webViewActivity = webViewActivities.get(uuid);
WebViewActivity webViewActivity = webViewActivities.get(uuid);
if (webViewActivity != null) {
if (headers != null)
webViewActivity.loadUrl(url, headers, result);
@ -423,6 +465,16 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
}
}
public void loadFile(String uuid, String url, Map<String, String> headers, Result result) {
WebViewActivity webViewActivity = webViewActivities.get(uuid);
if (webViewActivity != null) {
if (headers != null)
webViewActivity.loadFile(url, headers, result);
else
webViewActivity.loadFile(url, result);
}
}
public void show(String uuid) {
WebViewActivity webViewActivity = webViewActivities.get(uuid);
if (webViewActivity != null)
@ -488,7 +540,7 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
}
public static void close(final String uuid) {
public static void close(final String uuid, final Result result) {
final WebViewActivity webViewActivity = webViewActivities.get(uuid);
if (webViewActivity != null) {
registrar.activity().runOnUiThread(new Runnable() {
@ -501,8 +553,12 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
// The JS protects against multiple calls, so this should happen only when
// close() is called by other native code.
if (webViewActivity == null)
if (webViewActivity == null) {
if (result != null) {
result.success(true);
}
return;
}
webViewActivity.webView.setWebViewClient(new WebViewClient() {
// NB: wait for about:blank before dismissing
@ -514,8 +570,14 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
// other than your app's UI thread, it can cause unexpected results."
// http://developer.android.com/guide/webapps/migrating.html#Threads
webViewActivity.webView.loadUrl("about:blank");
if (result != null) {
result.success(true);
}
}
});
}
else if (result != null) {
result.success(true);
}
}
}

View File

@ -2,29 +2,31 @@ package com.pichillilorenzo.flutter_inappbrowser;
public class InAppBrowserOptions extends Options {
final static String LOG_TAG = "InAppBrowserOptions";
static final String LOG_TAG = "InAppBrowserOptions";
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 hidden = false;
public boolean toolbarTop = true;
public String toolbarTopBackgroundColor = "";
public String toolbarTopFixedTitle = "";
public boolean hideUrlBar = false;
public boolean mediaPlaybackRequiresUserGesture = true;
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 toolbarTop = true;
public String toolbarTopBackgroundColor = "";
public String toolbarTopFixedTitle = "";
public boolean hideUrlBar = false;
public boolean mediaPlaybackRequiresUserGesture = true;
public boolean isLocalFile = false;
public boolean hideTitleBar = false;
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 hideTitleBar = false;
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;
}

View File

@ -16,112 +16,111 @@ import java.util.Map;
public class InAppBrowserWebChromeClient extends WebChromeClient {
protected static final String LOG_TAG = "IABWebChromeClient";
private WebViewActivity activity;
private ValueCallback<Uri[]> mUploadMessageArray;
private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE=1;
protected static final String LOG_TAG = "IABWebChromeClient";
private WebViewActivity activity;
private ValueCallback<Uri[]> mUploadMessageArray;
private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE = 1;
public InAppBrowserWebChromeClient(WebViewActivity activity) {
super();
this.activity = activity;
public InAppBrowserWebChromeClient(WebViewActivity activity) {
super();
this.activity = activity;
}
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("sourceURL", consoleMessage.sourceId());
obj.put("lineNumber", consoleMessage.lineNumber());
obj.put("message", consoleMessage.message());
obj.put("messageLevel", consoleMessage.messageLevel().toString());
InAppBrowserFlutterPlugin.channel.invokeMethod("onConsoleMessage", obj);
return true;
}
@Override
public void onProgressChanged(WebView view, int progress) {
if (activity.progressBar != null) {
activity.progressBar.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
activity.progressBar.setProgress(progress, true);
} else {
activity.progressBar.setProgress(progress);
}
if (progress == 100) {
activity.progressBar.setVisibility(View.GONE);
}
}
super.onProgressChanged(view, progress);
}
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("sourceURL", consoleMessage.sourceId());
obj.put("lineNumber", consoleMessage.lineNumber());
obj.put("message", consoleMessage.message());
obj.put("messageLevel", consoleMessage.messageLevel().toString());
InAppBrowserFlutterPlugin.channel.invokeMethod("onConsoleMessage", obj);
return true;
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (activity.actionBar != null && activity.options.toolbarTopFixedTitle.isEmpty())
activity.actionBar.setTitle(title);
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
}
//The undocumented magic method override
//Eclipse will swear at you if you try to put @Override here
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
// For Android 3.0+
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
activity.startActivityForResult(
Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE);
}
//For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
//For Android 5.0+
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
if (mUploadMessageArray != null) {
mUploadMessageArray.onReceiveValue(null);
}
mUploadMessageArray = filePathCallback;
@Override
public void onProgressChanged(WebView view, int progress) {
if (activity.progressBar != null) {
activity.progressBar.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
activity.progressBar.setProgress(progress, true);
}
else {
activity.progressBar.setProgress(progress);
}
if (progress == 100) {
activity.progressBar.setVisibility(View.GONE);
}
}
super.onProgressChanged(view, progress);
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
Intent[] intentArray;
intentArray = new Intent[0];
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (activity.actionBar != null && activity.options.toolbarTopFixedTitle.isEmpty())
activity.actionBar.setTitle(title);
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
}
//The undocumented magic method override
//Eclipse will swear at you if you try to put @Override here
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
activity.startActivityForResult(Intent.createChooser(i,"File Chooser"), FILECHOOSER_RESULTCODE);
}
// For Android 3.0+
public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
activity.startActivityForResult(
Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE);
}
//For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
activity.startActivityForResult( Intent.createChooser( i, "File Chooser" ), FILECHOOSER_RESULTCODE );
}
//For Android 5.0+
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams){
if(mUploadMessageArray != null){
mUploadMessageArray.onReceiveValue(null);
}
mUploadMessageArray = filePathCallback;
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
Intent[] intentArray;
intentArray = new Intent[0];
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
activity.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
return true;
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
activity.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
return true;
}
}

View File

@ -23,337 +23,341 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.MethodChannel;
import okhttp3.Request;
import okhttp3.Response;
public class InAppBrowserWebViewClient extends WebViewClient {
protected static final String LOG_TAG = "IABWebViewClient";
private WebViewActivity activity;
Map<Integer, String> statusCodeMapping = new HashMap<Integer, String>();
long startPageTime = 0;
protected static final String LOG_TAG = "IABWebViewClient";
private WebViewActivity activity;
Map<Integer, String> statusCodeMapping = new HashMap<Integer, String>();
long startPageTime = 0;
public InAppBrowserWebViewClient(WebViewActivity activity) {
super();
this.activity = activity;
statusCodeMapping.put(100, "Continue");
statusCodeMapping.put(101, "Switching Protocols");
statusCodeMapping.put(200, "OK");
statusCodeMapping.put(201, "Created");
statusCodeMapping.put(202, "Accepted");
statusCodeMapping.put(203, "Non-Authoritative Information");
statusCodeMapping.put(204, "No Content");
statusCodeMapping.put(205, "Reset Content");
statusCodeMapping.put(206, "Partial Content");
statusCodeMapping.put(300, "Multiple Choices");
statusCodeMapping.put(301, "Moved Permanently");
statusCodeMapping.put(302, "Found");
statusCodeMapping.put(303, "See Other");
statusCodeMapping.put(304, "Not Modified");
statusCodeMapping.put(307, "Temporary Redirect");
statusCodeMapping.put(308, "Permanent Redirect");
statusCodeMapping.put(400, "Bad Request");
statusCodeMapping.put(401, "Unauthorized");
statusCodeMapping.put(403, "Forbidden");
statusCodeMapping.put(404, "Not Found");
statusCodeMapping.put(405, "Method Not Allowed");
statusCodeMapping.put(406, "Not Acceptable");
statusCodeMapping.put(407, "Proxy Authentication Required");
statusCodeMapping.put(408, "Request Timeout");
statusCodeMapping.put(409, "Conflict");
statusCodeMapping.put(410, "Gone");
statusCodeMapping.put(411, "Length Required");
statusCodeMapping.put(412, "Precondition Failed");
statusCodeMapping.put(413, "Payload Too Large");
statusCodeMapping.put(414, "URI Too Long");
statusCodeMapping.put(415, "Unsupported Media Type");
statusCodeMapping.put(416, "Range Not Satisfiable");
statusCodeMapping.put(417, "Expectation Failed");
statusCodeMapping.put(418, "I'm a teapot");
statusCodeMapping.put(422, "Unprocessable Entity");
statusCodeMapping.put(425, "Too Early");
statusCodeMapping.put(426, "Upgrade Required");
statusCodeMapping.put(428, "Precondition Required");
statusCodeMapping.put(429, "Too Many Requests");
statusCodeMapping.put(431, "Request Header Fields Too Large");
statusCodeMapping.put(451, "Unavailable For Legal Reasons");
statusCodeMapping.put(500, "Internal Server Error");
statusCodeMapping.put(501, "Not Implemented");
statusCodeMapping.put(502, "Bad Gateway");
statusCodeMapping.put(503, "Service Unavailable");
statusCodeMapping.put(504, "Gateway Timeout");
statusCodeMapping.put(505, "HTTP Version Not Supported");
statusCodeMapping.put(511, "Network Authentication Required");
public InAppBrowserWebViewClient(WebViewActivity activity) {
super();
this.activity = activity;
statusCodeMapping.put(100, "Continue");
statusCodeMapping.put(101, "Switching Protocols");
statusCodeMapping.put(200, "OK");
statusCodeMapping.put(201, "Created");
statusCodeMapping.put(202, "Accepted");
statusCodeMapping.put(203, "Non-Authoritative Information");
statusCodeMapping.put(204, "No Content");
statusCodeMapping.put(205, "Reset Content");
statusCodeMapping.put(206, "Partial Content");
statusCodeMapping.put(300, "Multiple Choices");
statusCodeMapping.put(301, "Moved Permanently");
statusCodeMapping.put(302, "Found");
statusCodeMapping.put(303, "See Other");
statusCodeMapping.put(304, "Not Modified");
statusCodeMapping.put(307, "Temporary Redirect");
statusCodeMapping.put(308, "Permanent Redirect");
statusCodeMapping.put(400, "Bad Request");
statusCodeMapping.put(401, "Unauthorized");
statusCodeMapping.put(403, "Forbidden");
statusCodeMapping.put(404, "Not Found");
statusCodeMapping.put(405, "Method Not Allowed");
statusCodeMapping.put(406, "Not Acceptable");
statusCodeMapping.put(407, "Proxy Authentication Required");
statusCodeMapping.put(408, "Request Timeout");
statusCodeMapping.put(409, "Conflict");
statusCodeMapping.put(410, "Gone");
statusCodeMapping.put(411, "Length Required");
statusCodeMapping.put(412, "Precondition Failed");
statusCodeMapping.put(413, "Payload Too Large");
statusCodeMapping.put(414, "URI Too Long");
statusCodeMapping.put(415, "Unsupported Media Type");
statusCodeMapping.put(416, "Range Not Satisfiable");
statusCodeMapping.put(417, "Expectation Failed");
statusCodeMapping.put(418, "I'm a teapot");
statusCodeMapping.put(422, "Unprocessable Entity");
statusCodeMapping.put(425, "Too Early");
statusCodeMapping.put(426, "Upgrade Required");
statusCodeMapping.put(428, "Precondition Required");
statusCodeMapping.put(429, "Too Many Requests");
statusCodeMapping.put(431, "Request Header Fields Too Large");
statusCodeMapping.put(451, "Unavailable For Legal Reasons");
statusCodeMapping.put(500, "Internal Server Error");
statusCodeMapping.put(501, "Not Implemented");
statusCodeMapping.put(502, "Bad Gateway");
statusCodeMapping.put(503, "Service Unavailable");
statusCodeMapping.put(504, "Gateway Timeout");
statusCodeMapping.put(505, "HTTP Version Not Supported");
statusCodeMapping.put(511, "Network Authentication Required");
}
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
if (activity.options.useShouldOverrideUrlLoading) {
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", url);
InAppBrowserFlutterPlugin.channel.invokeMethod("shouldOverrideUrlLoading", obj);
return true;
}
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
if (activity.options.useShouldOverrideUrlLoading) {
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", url);
InAppBrowserFlutterPlugin.channel.invokeMethod("shouldOverrideUrlLoading", obj);
return true;
}
if (url.startsWith(WebView.SCHEME_TEL)) {
try {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
return true;
} catch (android.content.ActivityNotFoundException e) {
Log.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
}
}
else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:") || url.startsWith("intent:")) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
return true;
} catch (android.content.ActivityNotFoundException e) {
Log.e(LOG_TAG, "Error with " + url + ": " + e.toString());
}
}
// If sms:5551212?body=This is the message
else if (url.startsWith("sms:")) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
// Get address
String address;
int parmIndex = url.indexOf('?');
if (parmIndex == -1) {
address = url.substring(4);
} else {
address = url.substring(4, parmIndex);
// If body, then set sms body
Uri uri = Uri.parse(url);
String query = uri.getQuery();
if (query != null) {
if (query.startsWith("body=")) {
intent.putExtra("sms_body", query.substring(5));
}
}
}
intent.setData(Uri.parse("sms:" + address));
intent.putExtra("address", address);
intent.setType("vnd.android-dir/mms-sms");
activity.startActivity(intent);
return true;
} catch (android.content.ActivityNotFoundException e) {
Log.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
}
}
return super.shouldOverrideUrlLoading(webView, url);
if (url.startsWith(WebView.SCHEME_TEL)) {
try {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
return true;
} catch (android.content.ActivityNotFoundException e) {
Log.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
}
} else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:") || url.startsWith("intent:")) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
return true;
} catch (android.content.ActivityNotFoundException e) {
Log.e(LOG_TAG, "Error with " + url + ": " + e.toString());
}
}
// If sms:5551212?body=This is the message
else if (url.startsWith("sms:")) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
/*
* onPageStarted fires the LOAD_START_EVENT
*
* @param view
* @param url
* @param favicon
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
startPageTime = System.currentTimeMillis();
activity.isLoading = true;
if (activity.searchView != null && !url.equals(activity.searchView.getQuery().toString())) {
activity.searchView.setQuery(url, false);
}
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", url);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadStart", obj);
}
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
activity.isLoading = false;
// CB-10395 InAppBrowserFlutterPlugin's WebView not storing cookies reliable to local device storage
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().flush();
// Get address
String address;
int parmIndex = url.indexOf('?');
if (parmIndex == -1) {
address = url.substring(4);
} else {
CookieSyncManager.getInstance().sync();
}
address = url.substring(4, parmIndex);
// https://issues.apache.org/jira/browse/CB-11248
view.clearFocus();
view.requestFocus();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
view.evaluateJavascript(WebViewActivity.consoleLogJS, null);
view.evaluateJavascript(JavaScriptBridgeInterface.flutterInAppBroserJSClass, null);
}
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", url);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadStop", obj);
}
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
activity.isLoading = false;
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", failingUrl);
obj.put("code", errorCode);
obj.put("message", description);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadError", obj);
}
public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", error.getUrl());
obj.put("code", error.getPrimaryError());
String message;
switch (error.getPrimaryError()) {
case SslError.SSL_DATE_INVALID:
message = "The date of the certificate is invalid";
break;
case SslError.SSL_EXPIRED:
message = "The certificate has expired";
break;
case SslError.SSL_IDMISMATCH:
message = "Hostname mismatch";
break;
default:
case SslError.SSL_INVALID:
message = "A generic error occurred";
break;
case SslError.SSL_NOTYETVALID:
message = "The certificate is not yet valid";
break;
case SslError.SSL_UNTRUSTED:
message = "The certificate authority is not trusted";
break;
}
obj.put("message", "SslError: " + message);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadError", obj);
handler.cancel();
}
/**
* On received http auth request.
*/
@Override
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
// By default handle 401 like we'd normally do!
super.onReceivedHttpAuthRequest(view, handler, host, realm);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request){
if (!request.getMethod().toLowerCase().equals("get") || !activity.options.useOnLoadResource) {
return null;
}
final String url = request.getUrl().toString();
try {
Request mRequest = new Request.Builder().url(url).build();
long startResourceTime = System.currentTimeMillis();
Response response = activity.httpClient.newCall(mRequest).execute();
long startTime = startResourceTime - startPageTime;
long duration = System.currentTimeMillis() - startResourceTime;
if (response.cacheResponse() != null) {
duration = 0;
// If body, then set sms body
Uri uri = Uri.parse(url);
String query = uri.getQuery();
if (query != null) {
if (query.startsWith("body=")) {
intent.putExtra("sms_body", query.substring(5));
}
String reasonPhrase = response.message();
if (reasonPhrase.equals("")) {
reasonPhrase = statusCodeMapping.get(response.code());
}
reasonPhrase = (reasonPhrase.equals("") || reasonPhrase == null) ? "OK" : reasonPhrase;
Map<String, String> headersResponse = new HashMap<String, String>();
for (Map.Entry<String, List<String>> entry : response.headers().toMultimap().entrySet()) {
StringBuilder value = new StringBuilder();
for (String val: entry.getValue()) {
value.append( (value.toString().isEmpty()) ? val : "; " + val );
}
headersResponse.put(entry.getKey().toLowerCase(), value.toString());
}
Map<String, String> headersRequest = new HashMap<String, String>();
for (Map.Entry<String, List<String>> entry : mRequest.headers().toMultimap().entrySet()) {
StringBuilder value = new StringBuilder();
for (String val: entry.getValue()) {
value.append( (value.toString().isEmpty()) ? val : "; " + val );
}
headersRequest.put(entry.getKey().toLowerCase(), value.toString());
}
Map<String, Object> obj = new HashMap<>();
Map<String, Object> res = new HashMap<>();
Map<String, Object> req = new HashMap<>();
obj.put("uuid", activity.uuid);
byte[] dataBytes = response.body().bytes();
InputStream dataStream = new ByteArrayInputStream(dataBytes);
res.put("url", url);
res.put("statusCode", response.code());
res.put("headers", headersResponse);
res.put("startTime", startTime);
res.put("duration", duration);
res.put("data", dataBytes);
req.put("url", url);
req.put("headers", headersRequest);
req.put("method", mRequest.method());
obj.put("response", res);
obj.put("request", req);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadResource", obj);
return new WebResourceResponse(
response.header("content-type", "text/plain").split(";")[0].trim(),
response.header("content-encoding"),
response.code(),
reasonPhrase,
headersResponse,
dataStream
);
} catch (IOException e) {
e.printStackTrace();
Log.d(LOG_TAG, e.getMessage());
} catch (Exception e) {
e.printStackTrace();
Log.d(LOG_TAG, e.getMessage());
}
}
return null;
intent.setData(Uri.parse("sms:" + address));
intent.putExtra("address", address);
intent.setType("vnd.android-dir/mms-sms");
activity.startActivity(intent);
return true;
} catch (android.content.ActivityNotFoundException e) {
Log.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
}
}
return super.shouldOverrideUrlLoading(webView, url);
}
/*
* onPageStarted fires the LOAD_START_EVENT
*
* @param view
* @param url
* @param favicon
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
startPageTime = System.currentTimeMillis();
activity.isLoading = true;
if (activity.searchView != null && !url.equals(activity.searchView.getQuery().toString())) {
activity.searchView.setQuery(url, false);
}
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", url);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadStart", obj);
}
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
activity.isLoading = false;
// CB-10395 InAppBrowserFlutterPlugin's WebView not storing cookies reliable to local device storage
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().flush();
} else {
CookieSyncManager.getInstance().sync();
}
// https://issues.apache.org/jira/browse/CB-11248
view.clearFocus();
view.requestFocus();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
view.evaluateJavascript(WebViewActivity.consoleLogJS, null);
view.evaluateJavascript(JavaScriptBridgeInterface.flutterInAppBroserJSClass, null);
}
else {
view.loadUrl("javascript:"+WebViewActivity.consoleLogJS);
view.loadUrl("javascript:"+JavaScriptBridgeInterface.flutterInAppBroserJSClass);
}
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", url);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadStop", obj);
}
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
activity.isLoading = false;
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", failingUrl);
obj.put("code", errorCode);
obj.put("message", description);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadError", obj);
}
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", error.getUrl());
obj.put("code", error.getPrimaryError());
String message;
switch (error.getPrimaryError()) {
case SslError.SSL_DATE_INVALID:
message = "The date of the certificate is invalid";
break;
case SslError.SSL_EXPIRED:
message = "The certificate has expired";
break;
case SslError.SSL_IDMISMATCH:
message = "Hostname mismatch";
break;
default:
case SslError.SSL_INVALID:
message = "A generic error occurred";
break;
case SslError.SSL_NOTYETVALID:
message = "The certificate is not yet valid";
break;
case SslError.SSL_UNTRUSTED:
message = "The certificate authority is not trusted";
break;
}
obj.put("message", "SslError: " + message);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadError", obj);
handler.cancel();
}
/**
* On received http auth request.
*/
@Override
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
// By default handle 401 like we'd normally do!
super.onReceivedHttpAuthRequest(view, handler, host, realm);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
if (!request.getMethod().toLowerCase().equals("get") || !activity.options.useOnLoadResource) {
return null;
}
final String url = request.getUrl().toString();
try {
Request mRequest = new Request.Builder().url(url).build();
long startResourceTime = System.currentTimeMillis();
Response response = activity.httpClient.newCall(mRequest).execute();
long startTime = startResourceTime - startPageTime;
startTime = (startTime < 0) ? 0 : startTime;
long duration = System.currentTimeMillis() - startResourceTime;
if (response.cacheResponse() != null) {
duration = 0;
}
String reasonPhrase = response.message();
if (reasonPhrase.equals("")) {
reasonPhrase = statusCodeMapping.get(response.code());
}
reasonPhrase = (reasonPhrase.equals("") || reasonPhrase == null) ? "OK" : reasonPhrase;
Map<String, String> headersResponse = new HashMap<String, String>();
for (Map.Entry<String, List<String>> entry : response.headers().toMultimap().entrySet()) {
StringBuilder value = new StringBuilder();
for (String val : entry.getValue()) {
value.append((value.toString().isEmpty()) ? val : "; " + val);
}
headersResponse.put(entry.getKey().toLowerCase(), value.toString());
}
Map<String, String> headersRequest = new HashMap<String, String>();
for (Map.Entry<String, List<String>> entry : mRequest.headers().toMultimap().entrySet()) {
StringBuilder value = new StringBuilder();
for (String val : entry.getValue()) {
value.append((value.toString().isEmpty()) ? val : "; " + val);
}
headersRequest.put(entry.getKey().toLowerCase(), value.toString());
}
Map<String, Object> obj = new HashMap<>();
Map<String, Object> res = new HashMap<>();
Map<String, Object> req = new HashMap<>();
obj.put("uuid", activity.uuid);
byte[] dataBytes = response.body().bytes();
InputStream dataStream = new ByteArrayInputStream(dataBytes);
res.put("url", url);
res.put("statusCode", response.code());
res.put("headers", headersResponse);
res.put("startTime", startTime);
res.put("duration", duration);
res.put("data", dataBytes);
req.put("url", url);
req.put("headers", headersRequest);
req.put("method", mRequest.method());
obj.put("response", res);
obj.put("request", req);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadResource", obj);
return new WebResourceResponse(
response.header("content-type", "text/plain").split(";")[0].trim(),
response.header("content-encoding"),
response.code(),
reasonPhrase,
headersResponse,
dataStream
);
} catch (IOException e) {
e.printStackTrace();
Log.d(LOG_TAG, e.getMessage());
} catch (Exception e) {
e.printStackTrace();
Log.d(LOG_TAG, e.getMessage());
}
return null;
}
}

View File

@ -10,9 +10,9 @@ public class JavaScriptBridgeInterface {
static final String name = "flutter_inappbrowser";
WebViewActivity activity;
static final String flutterInAppBroserJSClass = "window." + name + ".callHandler = function(handlerName, ...args) {\n" +
"window." + name + "._callHandler(handlerName, JSON.stringify(args));\n" +
"}\n";
static final String flutterInAppBroserJSClass = "window." + name + ".callHandler = function(handlerName, ...args) {" +
"window." + name + "._callHandler(handlerName, JSON.stringify(args));" +
"}";
JavaScriptBridgeInterface(WebViewActivity a) {
activity = a;

View File

@ -9,32 +9,33 @@ import java.util.HashMap;
public class Options {
final static String LOG_TAG = "";
static String LOG_TAG = "";
public void parse(HashMap<String, Object> options) {
Iterator it = options.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> pair = (Map.Entry<String, Object>)it.next();
try {
this.getClass().getDeclaredField(pair.getKey()).set(this, pair.getValue());
} catch (NoSuchFieldException e) {
Log.d(LOG_TAG, e.getMessage());
} catch (IllegalAccessException e) {
Log.d(LOG_TAG, e.getMessage());
}
}
public Options parse(HashMap<String, Object> options) {
Iterator it = options.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> pair = (Map.Entry<String, Object>) it.next();
try {
this.getClass().getDeclaredField(pair.getKey()).set(this, pair.getValue());
} catch (NoSuchFieldException e) {
Log.d(LOG_TAG, e.getMessage());
} catch (IllegalAccessException e) {
Log.d(LOG_TAG, e.getMessage());
}
}
return this;
}
public HashMap<String, Object> getHashMap() {
HashMap<String, Object> options = new HashMap<>();
for (Field f: this.getClass().getDeclaredFields()) {
try {
options.put(f.getName(), f.get(this));
} catch (IllegalAccessException e) {
Log.d(LOG_TAG, e.getMessage());
}
}
return options;
public HashMap<String, Object> getHashMap() {
HashMap<String, Object> options = new HashMap<>();
for (Field f : this.getClass().getDeclaredFields()) {
try {
options.put(f.getName(), f.get(this));
} catch (IllegalAccessException e) {
Log.d(LOG_TAG, e.getMessage());
}
}
return options;
}
}

View File

@ -28,85 +28,85 @@ import okhttp3.OkHttpClient;
public class WebViewActivity extends AppCompatActivity {
String uuid;
WebView webView;
ActionBar actionBar;
InAppBrowserWebViewClient inAppBrowserWebViewClient;
InAppBrowserWebChromeClient inAppBrowserWebChromeClient;
SearchView searchView;
InAppBrowserOptions options;
Map<String, String> headers;
ProgressBar progressBar;
public boolean isLoading = false;
public boolean isHidden = false;
OkHttpClient httpClient;
String uuid;
WebView webView;
ActionBar actionBar;
InAppBrowserWebViewClient inAppBrowserWebViewClient;
InAppBrowserWebChromeClient inAppBrowserWebChromeClient;
SearchView searchView;
InAppBrowserOptions options;
Map<String, String> headers;
ProgressBar progressBar;
public boolean isLoading = false;
public boolean isHidden = false;
OkHttpClient httpClient;
static final String consoleLogJS = "(function() {\n"+
" var oldLogs = {\n"+
" 'log': console.log,\n"+
" 'debug': console.debug,\n"+
" 'error': console.error,\n"+
" 'info': console.info,\n"+
" 'warn': console.warn\n"+
" };\n"+
" for (var k in oldLogs) {\n"+
" (function(oldLog) {\n"+
" console[oldLog] = function() {\n"+
" var message = ''\n"+
" for (var i in arguments) {\n"+
" if (message == '') {\n"+
" message += arguments[i];\n"+
" }\n"+
" else {\n"+
" message += ' ' + arguments[i];\n"+
" }\n"+
" }\n"+
" oldLogs[oldLog].call(console, message);\n"+
" }\n"+
" })(k);\n"+
" }\n"+
"})();";
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
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
setContentView(R.layout.activity_web_view);
webView = findViewById(R.id.webView);
webView = findViewById(R.id.webView);
Bundle b = getIntent().getExtras();
uuid = b.getString("uuid");
String url = b.getString("url");
Bundle b = getIntent().getExtras();
uuid = b.getString("uuid");
String url = b.getString("url");
options = new InAppBrowserOptions();
options.parse((HashMap<String, Object>) b.getSerializable("options"));
options = new InAppBrowserOptions();
options.parse((HashMap<String, Object>) b.getSerializable("options"));
headers = (HashMap<String, String>) b.getSerializable("headers");
headers = (HashMap<String, String>) b.getSerializable("headers");
InAppBrowserFlutterPlugin.webViewActivities.put(uuid, this);
InAppBrowserFlutterPlugin.webViewActivities.put(uuid, this);
actionBar = getSupportActionBar();
actionBar = getSupportActionBar();
prepareWebView();
prepareWebView();
int cacheSize = 10 * 1024 * 1024; // 10MB
httpClient = new OkHttpClient().newBuilder().cache(new Cache(getApplicationContext().getCacheDir(), cacheSize)).build();
int cacheSize = 10 * 1024 * 1024; // 10MB
httpClient = new OkHttpClient().newBuilder().cache(new Cache(getApplicationContext().getCacheDir(), cacheSize)).build();
webView.loadUrl(url, headers);
//webView.loadData("<!DOCTYPE html> <html lang=\"en\"> <head> <meta charset=\"UTF-8\"> <title>Document</title> </head> <body> ciao <img src=\"https://via.placeholder.com/350x150\" /> <img src=\"./images/test\" alt=\"not found\" /></body> </html>", "text/html", "utf8");
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");
}
}
private void prepareWebView() {
private void prepareWebView() {
webView.addJavascriptInterface(new JavaScriptBridgeInterface(this), JavaScriptBridgeInterface.name);
webView.addJavascriptInterface(new JavaScriptBridgeInterface(this), JavaScriptBridgeInterface.name);
inAppBrowserWebChromeClient = new InAppBrowserWebChromeClient(this);
webView.setWebChromeClient(inAppBrowserWebChromeClient);
inAppBrowserWebChromeClient = new InAppBrowserWebChromeClient(this);
webView.setWebChromeClient(inAppBrowserWebChromeClient);
inAppBrowserWebViewClient = new InAppBrowserWebViewClient(this);
webView.setWebViewClient(inAppBrowserWebViewClient);
inAppBrowserWebViewClient = new InAppBrowserWebViewClient(this);
webView.setWebViewClient(inAppBrowserWebViewClient);
// final Activity activity = this;
//
@ -142,241 +142,256 @@ public class WebViewActivity extends AppCompatActivity {
// }
// });
WebSettings settings = webView.getSettings();
WebSettings settings = webView.getSettings();
if (options.hidden)
hide();
else
show();
if (options.hidden)
hide();
else
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.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.setMediaPlaybackRequiresUserGesture(options.mediaPlaybackRequiresUserGesture);
settings.setDatabaseEnabled(options.databaseEnabled);
settings.setDomStorageEnabled(options.domStorageEnabled);
settings.setDatabaseEnabled(options.databaseEnabled);
settings.setDomStorageEnabled(options.domStorageEnabled);
if (!options.userAgent.isEmpty())
settings.setUserAgentString(options.userAgent);
if (!options.userAgent.isEmpty())
settings.setUserAgentString(options.userAgent);
if (options.clearCache)
clearCache();
else if (options.clearSessionCache)
CookieManager.getInstance().removeSessionCookie();
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);
// 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);
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);
// fix webview scaling
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
else
settings.setTextZoom(100);
if (options.progressBar)
progressBar = findViewById(R.id.progressBar);
if (options.progressBar)
progressBar = findViewById(R.id.progressBar);
actionBar.setDisplayShowTitleEnabled(!options.hideTitleBar);
actionBar.setDisplayShowTitleEnabled(!options.hideTitleBar);
if (!options.toolbarTop)
actionBar.hide();
if (!options.toolbarTop)
actionBar.hide();
if (!options.toolbarTopBackgroundColor.isEmpty())
actionBar.setBackgroundDrawable(new ColorDrawable(Color.parseColor(options.toolbarTopBackgroundColor)));
if (!options.toolbarTopBackgroundColor.isEmpty())
actionBar.setBackgroundDrawable(new ColorDrawable(Color.parseColor(options.toolbarTopBackgroundColor)));
if (!options.toolbarTopFixedTitle.isEmpty())
actionBar.setTitle(options.toolbarTopFixedTitle);
if (!options.toolbarTopFixedTitle.isEmpty())
actionBar.setTitle(options.toolbarTopFixedTitle);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
// Inflate menu to add items to action bar if it is present.
inflater.inflate(R.menu.menu_main, menu);
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
// Inflate menu to add items to action bar if it is present.
inflater.inflate(R.menu.menu_main, menu);
searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
searchView.setFocusable(true);
searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
searchView.setFocusable(true);
if (options.hideUrlBar)
menu.findItem(R.id.menu_search).setVisible(false);
if (options.hideUrlBar)
menu.findItem(R.id.menu_search).setVisible(false);
searchView.setQuery(webView.getUrl(), false);
searchView.setQuery(webView.getUrl(), false);
if (options.toolbarTopFixedTitle.isEmpty())
actionBar.setTitle(webView.getTitle());
if (options.toolbarTopFixedTitle.isEmpty())
actionBar.setTitle(webView.getTitle());
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
if (!query.isEmpty()) {
webView.loadUrl(query);
searchView.setQuery("", false);
searchView.setIconified(true);
return true;
}
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
searchView.setOnCloseListener(new SearchView.OnCloseListener() {
@Override
public boolean onClose() {
if (searchView.getQuery().toString().isEmpty())
searchView.setQuery(webView.getUrl(), false);
return false;
}
});
searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean b) {
if (!b) {
searchView.setQuery("", false);
searchView.setIconified(true);
}
}
});
return true;
}
public void loadUrl (String url, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) {
webView.loadUrl(url);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
if (!query.isEmpty()) {
webView.loadUrl(query);
searchView.setQuery("", false);
searchView.setIconified(true);
return true;
}
else {
result.error("Cannot load url", "", null);
}
}
public void loadUrl (String url, Map<String, String> headers, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) {
webView.loadUrl(url, headers);
}
else {
result.error("Cannot load url", "", null);
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
if (canGoBack())
goBack();
else if (options.closeOnCannotGoBack)
InAppBrowserFlutterPlugin.close(uuid);
return true;
}
return super.onKeyDown(keyCode, event);
}
public void close() {
hide();
finish();
}
public void reload() {
if (webView != null)
webView.reload();
}
public void goBack() {
if (webView != null && canGoBack())
webView.goBack();
}
public void goForward() {
if (webView != null && canGoForward())
webView.goForward();
}
public boolean canGoBack() {
return webView.canGoBack();
}
public boolean canGoForward() {
return webView.canGoForward();
}
public void hide() {
isHidden = true;
Intent openActivity = new Intent(this, InAppBrowserFlutterPlugin.registrar.activity().getClass());
openActivity.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivityIfNeeded(openActivity, 0);
}
public void show() {
isHidden = false;
Intent openActivity = new Intent(InAppBrowserFlutterPlugin.registrar.activity(), WebViewActivity.class);
openActivity.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivityIfNeeded(openActivity, 0);
}
public void stopLoading(){
if (webView != null)
webView.stopLoading();
}
public boolean isLoading() {
if (webView != null)
return isLoading;
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) {
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
}
});
} else {
CookieManager.getInstance().removeAllCookie();
});
searchView.setOnCloseListener(new SearchView.OnCloseListener() {
@Override
public boolean onClose() {
if (searchView.getQuery().toString().isEmpty())
searchView.setQuery(webView.getUrl(), false);
return false;
}
});
searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean b) {
if (!b) {
searchView.setQuery("", false);
searchView.setIconified(true);
}
}
}
});
private void clearCache() {
webView.clearCache(true);
clearCookies();
webView.clearFormData();
}
return true;
}
public void goBackButtonClicked(MenuItem item) {
public void loadUrl(String url, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) {
webView.loadUrl(url);
} else {
result.error("Cannot load url", "", null);
}
}
public void loadUrl(String url, Map<String, String> headers, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) {
webView.loadUrl(url, headers);
} else {
result.error("Cannot load url", "", null);
}
}
public void loadFile(String url, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) {
webView.loadUrl(url);
} else {
result.error("Cannot load url", "", null);
}
}
public void loadFile(String url, Map<String, String> headers, MethodChannel.Result result) {
if (webView != null && !url.isEmpty()) {
webView.loadUrl(url, headers);
} else {
result.error("Cannot load url", "", null);
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
if (canGoBack())
goBack();
else if (options.closeOnCannotGoBack)
InAppBrowserFlutterPlugin.close(uuid, null);
return true;
}
return super.onKeyDown(keyCode, event);
}
public void goForwardButtonClicked(MenuItem item) {
goForward();
}
public void close() {
hide();
finish();
}
public void shareButtonClicked(MenuItem item) {
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("text/plain");
share.putExtra(Intent.EXTRA_TEXT, webView.getUrl());
startActivity(Intent.createChooser(share, "Share"));
}
public void reload() {
if (webView != null)
webView.reload();
}
public void reloadButtonClicked(MenuItem item) {
reload();
}
public void goBack() {
if (webView != null && canGoBack())
webView.goBack();
}
public void closeButtonClicked(MenuItem item) {
InAppBrowserFlutterPlugin.close(uuid);
public void goForward() {
if (webView != null && canGoForward())
webView.goForward();
}
public boolean canGoBack() {
return webView.canGoBack();
}
public boolean canGoForward() {
return webView.canGoForward();
}
public void hide() {
isHidden = true;
Intent openActivity = new Intent(this, InAppBrowserFlutterPlugin.registrar.activity().getClass());
openActivity.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivityIfNeeded(openActivity, 0);
}
public void show() {
isHidden = false;
Intent openActivity = new Intent(InAppBrowserFlutterPlugin.registrar.activity(), WebViewActivity.class);
openActivity.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivityIfNeeded(openActivity, 0);
}
public void stopLoading() {
if (webView != null)
webView.stopLoading();
}
public boolean isLoading() {
if (webView != null)
return isLoading;
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() {
webView.clearCache(true);
clearCookies();
webView.clearFormData();
}
public void goBackButtonClicked(MenuItem item) {
goBack();
}
public void goForwardButtonClicked(MenuItem item) {
goForward();
}
public void shareButtonClicked(MenuItem item) {
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("text/plain");
share.putExtra(Intent.EXTRA_TEXT, webView.getUrl());
startActivity(Intent.createChooser(share, "Share"));
}
public void reloadButtonClicked(MenuItem item) {
reload();
}
public void closeButtonClicked(MenuItem item) {
InAppBrowserFlutterPlugin.close(uuid, null);
}
}

View File

@ -17,99 +17,78 @@ import java.util.Map;
public class ChromeCustomTabsActivity extends Activity {
protected static final String LOG_TAG = "CustomTabsActivity";
String uuid;
String uuidFallback;
CustomTabsIntent.Builder builder;
ChromeCustomTabsOptions options;
Map<String, String> headersFallback;
InAppBrowserOptions optionsFallback;
private CustomTabActivityHelper customTabActivityHelper;
private final int CHROME_CUSTOM_TAB_REQUEST_CODE = 100;
protected static final String LOG_TAG = "CustomTabsActivity";
String uuid;
CustomTabsIntent.Builder builder;
ChromeCustomTabsOptions options;
private CustomTabActivityHelper customTabActivityHelper;
private final int CHROME_CUSTOM_TAB_REQUEST_CODE = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chrome_custom_tabs_layout);
setContentView(R.layout.chrome_custom_tabs_layout);
Bundle b = getIntent().getExtras();
uuid = b.getString("uuid");
uuidFallback = b.getString("uuidFallback");
String url = b.getString("url");
Bundle b = getIntent().getExtras();
assert b != null;
uuid = b.getString("uuid");
String url = b.getString("url");
options = new ChromeCustomTabsOptions();
options.parse((HashMap<String, Object>) b.getSerializable("options"));
options = new ChromeCustomTabsOptions();
options.parse((HashMap<String, Object>) b.getSerializable("options"));
headersFallback = (HashMap<String, String>) b.getSerializable("headers");
InAppBrowserFlutterPlugin.chromeCustomTabsActivities.put(uuid, this);
optionsFallback = new InAppBrowserOptions();
optionsFallback.parse((HashMap<String, Object>) b.getSerializable("optionsFallback"));
customTabActivityHelper = new CustomTabActivityHelper();
builder = new CustomTabsIntent.Builder();
InAppBrowserFlutterPlugin.chromeCustomTabsActivities.put(uuid, this);
prepareCustomTabs();
customTabActivityHelper = new CustomTabActivityHelper();
builder = new CustomTabsIntent.Builder();
CustomTabsIntent customTabsIntent = builder.build();
prepareCustomTabs();
CustomTabActivityHelper.openCustomTab(this, customTabsIntent, Uri.parse(url), CHROME_CUSTOM_TAB_REQUEST_CODE);
CustomTabsIntent customTabsIntent = builder.build();
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid);
InAppBrowserFlutterPlugin.channel.invokeMethod("onChromeSafariBrowserOpened", obj);
InAppBrowserFlutterPlugin.channel.invokeMethod("onChromeSafariBrowserLoaded", obj);
}
boolean chromeCustomTabsOpened = customTabActivityHelper.openCustomTab(this, customTabsIntent, Uri.parse(url), CHROME_CUSTOM_TAB_REQUEST_CODE,
new CustomTabActivityHelper.CustomTabFallback() {
@Override
public void openUri(Activity activity, Uri uri) {
if (!uuidFallback.isEmpty())
InAppBrowserFlutterPlugin.open(uuidFallback, null, uri.toString(), optionsFallback, headersFallback, false, null);
else {
Log.d(LOG_TAG, "No WebView fallback declared.");
activity.finish();
}
}
});
private void prepareCustomTabs() {
if (options.addShareButton)
builder.addDefaultShareMenuItem();
if (chromeCustomTabsOpened) {
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid);
InAppBrowserFlutterPlugin.channel.invokeMethod("onChromeSafariBrowserOpened", obj);
InAppBrowserFlutterPlugin.channel.invokeMethod("onChromeSafariBrowserLoaded", obj);
}
}
private void prepareCustomTabs() {
if (options.addShareButton)
builder.addDefaultShareMenuItem();
if (!options.toolbarBackgroundColor.isEmpty())
builder.setToolbarColor(Color.parseColor(options.toolbarBackgroundColor));
builder.setShowTitle(options.showTitle);
if (options.enableUrlBarHiding)
builder.enableUrlBarHiding();
builder.setInstantAppsEnabled(options.instantAppsEnabled);
}
@Override
protected void onStart() {
super.onStart();
customTabActivityHelper.bindCustomTabsService(this);
}
@Override
protected void onStop() {
super.onStop();
customTabActivityHelper.unbindCustomTabsService(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CHROME_CUSTOM_TAB_REQUEST_CODE) {
finish();
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid);
InAppBrowserFlutterPlugin.channel.invokeMethod("onChromeSafariBrowserClosed", obj);
}
if (!options.toolbarBackgroundColor.isEmpty())
builder.setToolbarColor(Color.parseColor(options.toolbarBackgroundColor));
builder.setShowTitle(options.showTitle);
if (options.enableUrlBarHiding)
builder.enableUrlBarHiding();
builder.setInstantAppsEnabled(options.instantAppsEnabled);
}
@Override
protected void onStart() {
super.onStart();
customTabActivityHelper.bindCustomTabsService(this);
}
@Override
protected void onStop() {
super.onStop();
customTabActivityHelper.unbindCustomTabsService(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CHROME_CUSTOM_TAB_REQUEST_CODE) {
finish();
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid);
InAppBrowserFlutterPlugin.channel.invokeMethod("onChromeSafariBrowserClosed", obj);
}
}
}

View File

@ -27,26 +27,13 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
* @param activity The host activity.
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available.
* @param uri the Uri to be opened.
* @param fallback a CustomTabFallback to be used if Custom Tabs is not available.
*/
public static boolean openCustomTab(Activity activity,
public static void openCustomTab(Activity activity,
CustomTabsIntent customTabsIntent,
Uri uri,
int requestCode,
CustomTabFallback fallback) {
//If we cant find a package name, it means theres no browser that supports
//Chrome Custom Tabs installed. So, we fallback to the webview
if (!isAvailable(activity)) {
if (fallback != null) {
fallback.openUri(activity, uri);
}
} else {
//customTabsIntent.intent.setPackage(packageName);
customTabsIntent.intent.setData(uri);
activity.startActivityForResult(customTabsIntent.intent, requestCode);
return true;
}
return false;
int requestCode) {
customTabsIntent.intent.setData(uri);
activity.startActivityForResult(customTabsIntent.intent, requestCode);
}
public static boolean isAvailable(Activity activity) {
@ -144,16 +131,4 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
void onCustomTabsDisconnected();
}
/**
* To be used as a fallback to open the Uri when Custom Tabs is not available.
*/
public interface CustomTabFallback {
/**
*
* @param activity The Activity that wants to open the Uri.
* @param uri The uri to be opened by the fallback.
*/
void openUri(Activity activity, Uri uri);
}
}

View File

@ -0,0 +1,7 @@
body {
background-color: #333;
color: #fff;
}
img {
max-width: 100%;
}

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 439 137.29" enable-background="new 0 0 439 137.29" xml:space="preserve">
<g>
<g opacity="0.54">
<path d="M207.08,20.2h27.55c9.35,0,17.51,1.93,24.49,5.8c6.97,3.87,12.33,9.25,16.07,16.13c3.74,6.89,5.61,14.79,5.61,23.72
s-1.87,16.83-5.61,23.72s-9.1,12.26-16.07,16.13c-6.97,3.87-15.13,5.8-24.49,5.8h-27.55V20.2z M234.63,101.19
c10.8,0,19.36-3.1,25.7-9.31c6.33-6.21,9.5-14.88,9.5-26.02s-3.17-19.81-9.5-26.02c-6.33-6.21-14.9-9.31-25.7-9.31H217.8v70.65
h16.83V101.19z"/>
<path d="M297.49,110.75c-3.74-1.87-6.63-4.44-8.67-7.72c-2.04-3.27-3.06-6.99-3.06-11.16c0-6.89,2.59-12.26,7.78-16.13
c5.18-3.87,11.73-5.8,19.64-5.8c3.91,0,7.54,0.43,10.9,1.28s5.93,1.83,7.72,2.93V70.2c0-4.85-1.7-8.74-5.1-11.67
c-3.4-2.93-7.7-4.4-12.88-4.4c-3.66,0-7.01,0.79-10.08,2.36c-3.06,1.57-5.48,3.76-7.27,6.57l-8.16-6.12
c2.55-3.91,6.06-6.97,10.52-9.18c4.46-2.21,9.42-3.32,14.86-3.32c8.84,0,15.79,2.32,20.85,6.95c5.06,4.64,7.59,10.95,7.59,18.94
v41.19H331.8v-9.31h-0.51c-1.87,3.15-4.68,5.82-8.42,8.03c-3.74,2.21-7.95,3.32-12.63,3.32
C305.49,113.56,301.24,112.62,297.49,110.75z M321.47,101.19c3.14-1.87,5.65-4.38,7.52-7.52s2.81-6.59,2.81-10.33
c-2.04-1.36-4.55-2.47-7.52-3.32c-2.98-0.85-6.12-1.28-9.44-1.28c-5.95,0-10.44,1.23-13.45,3.7c-3.02,2.47-4.53,5.66-4.53,9.56
c0,3.57,1.36,6.46,4.08,8.67c2.72,2.21,6.16,3.32,10.33,3.32C314.92,103.99,318.33,103.06,321.47,101.19z"/>
<path d="M353.57,47.5h10.33v10.33h0.51c1.53-3.83,4.12-6.8,7.78-8.93c3.65-2.12,7.65-3.19,11.99-3.19c1.87,0,3.44,0.13,4.72,0.38
v11.1c-1.45-0.34-3.4-0.51-5.87-0.51c-5.53,0-10.01,1.83-13.45,5.48c-3.44,3.66-5.17,8.42-5.17,14.28v36.09h-10.84V47.5
L353.57,47.5z M420.89,112.26c-2.25-0.86-4.14-2.03-5.68-3.51c-1.7-1.64-2.98-3.55-3.83-5.71c-0.85-2.16-1.28-4.8-1.28-7.92V56.3
h-11.35v-9.82h11.35V28.12h10.84v18.36h15.81v9.82h-15.81v36.24c0,3.65,0.68,6.34,2.04,8.08c1.61,1.91,3.95,2.87,7.01,2.87
c2.46,0,4.85-0.72,7.14-2.17v10.59c-1.28,0.59-2.57,1.02-3.89,1.28s-3,0.38-5.04,0.38C425.59,113.56,423.15,113.12,420.89,112.26z
"/>
</g>
<g>
<path fill="#01579B" d="M29.64,108.94L6.36,85.66c-2.76-2.84-4.48-6.84-4.48-10.75c0-1.81,1.02-4.64,1.79-6.27l21.49-44.77
L29.64,108.94z"/>
<path fill="#40C4FF" d="M109.34,28.35L86.06,5.07c-2.03-2.04-6.27-4.48-9.85-4.48c-3.08,0-6.1,0.62-8.06,1.79L25.17,23.87
L109.34,28.35z"/>
<polygon fill="#40C4FF" points="57.4,136.7 113.82,136.7 113.82,112.52 71.73,99.09 33.23,112.52 "/>
<path fill="#29B6F6" d="M25.17,96.41c0,7.18,0.9,8.95,4.48,12.54l3.58,3.58h80.59l-39.4-44.77L25.17,23.88V96.41z"/>
<path fill="#01579B" d="M96.8,23.87H25.16l88.65,88.65h24.18V57l-28.65-28.65C105.32,24.31,101.74,23.87,96.8,23.87z"/>
<path opacity="0.2" fill="#FFFFFF" enable-background="new " d="M30.54,109.84c-3.58-3.6-4.48-7.14-4.48-13.43V24.77l-0.9-0.9
V96.4C25.17,102.7,25.17,104.44,30.54,109.84l2.69,2.69l0,0L30.54,109.84z"/>
<polygon opacity="0.2" fill="#263238" enable-background="new " points="137.1,56.11 137.1,111.63 112.92,111.63
113.82,112.52 138,112.52 138,57.01 "/>
<path opacity="0.2" fill="#FFFFFF" enable-background="new " d="M109.34,28.35c-4.44-4.44-8.08-4.48-13.43-4.48H25.17l0.9,0.9
h69.85C98.58,24.77,105.33,24.32,109.34,28.35L109.34,28.35z"/>
<radialGradient id="SVGID_1_" cx="69.955" cy="60.8864" r="68.065" gradientTransform="matrix(1 0 0 -1 0 129.5328)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.1"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</radialGradient>
<path opacity="0.2" fill="url(#SVGID_1_)" enable-background="new " d="M137.1,56.11l-27.76-27.76L86.06,5.07
c-2.03-2.04-6.27-4.48-9.85-4.48c-3.08,0-6.1,0.62-8.06,1.79L25.17,23.87L3.68,68.64c-0.77,1.63-1.79,4.46-1.79,6.27
c0,3.91,1.72,7.91,4.48,10.75l21.46,21.3c0.51,0.63,1.11,1.27,1.83,1.98l0.9,0.9l2.69,2.69l23.28,23.28l0.9,0.9h55.52h0.9v-24.18
h24.18v-0.06V57.01L137.1,56.11z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

34
example/assets/index.html Normal file
View File

@ -0,0 +1,34 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="css/style.css">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>
<div class="container">
<div class="container">
<img src="images/dart.svg" alt="dart logo">
<div class="row">
<div class="col-sm">
One of three columns
</div>
<div class="col-sm">
One of three columns
</div>
<div class="col-sm">
One of three columns
</div>
</div>
</div>
<script>
console.log("hello");
</script>
</div>
</body>
</html>

View File

@ -2,6 +2,11 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>

View File

@ -1,8 +1,8 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappbrowser/flutter_inappbrowser.dart';
class MyInAppBrowser extends InAppBrowser {
@override
Future onLoadStart(String url) async {
print("\n\nStarted $url\n\n");
@ -13,6 +13,8 @@ class MyInAppBrowser extends InAppBrowser {
Future onLoadStop(String url) async {
print("\n\nStopped $url\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);");
@ -40,8 +42,7 @@ class MyInAppBrowser extends InAppBrowser {
// var x = {"as":4, "dfdfg": 6};
// x;
// """));
//print("\n\n ${await this.isHidden()} \n\n");
//
// await this.injectScriptFile("https://code.jquery.com/jquery-3.3.1.min.js");
// this.injectScriptCode("""
// \$( "body" ).html( "Next Step..." )
@ -73,29 +74,33 @@ class MyInAppBrowser extends InAppBrowser {
}
@override
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
print("Started at: " + response.startTime.toString() + "ms ---> duration: " + response.duration.toString() + "ms " + response.url);
void onLoadResource(
WebResourceResponse response, WebResourceRequest request) {
print("Started at: " +
response.startTime.toString() +
"ms ---> duration: " +
response.duration.toString() +
"ms " +
response.url);
// if (response.headers["content-length"] != null)
// print(response.headers["content-length"] + " length");
}
@override
void onConsoleMessage(ConsoleMessage consoleMessage) {
// print("""
// console output:
// sourceURL: ${consoleMessage.sourceURL}
// lineNumber: ${consoleMessage.lineNumber}
// message: ${consoleMessage.message}
// messageLevel: ${consoleMessage.messageLevel}
// """);
print("""
console output:
sourceURL: ${consoleMessage.sourceURL}
lineNumber: ${consoleMessage.lineNumber}
message: ${consoleMessage.message}
messageLevel: ${consoleMessage.messageLevel}
""");
}
}
MyInAppBrowser inAppBrowserFallback = new MyInAppBrowser();
class MyChromeSafariBrowser extends ChromeSafariBrowser {
MyChromeSafariBrowser(browserFallback) : super(browserFallback);
@override
@ -115,9 +120,10 @@ class MyChromeSafariBrowser extends ChromeSafariBrowser {
}
// adding a webview fallback
MyChromeSafariBrowser chromeSafariBrowser = new MyChromeSafariBrowser(inAppBrowserFallback);
MyChromeSafariBrowser chromeSafariBrowser =
new MyChromeSafariBrowser(inAppBrowserFallback);
void main() {
Future main() async {
runApp(new MyApp());
}
@ -127,11 +133,11 @@ class MyApp extends StatefulWidget {
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
// int indexTest = inAppBrowserFallback.addJavaScriptHandler("handlerTest", (arguments) async {
// int indexTest = inAppBrowserFallback.addJavaScriptHandler("handlerTest",
// (arguments) async {
// print("handlerTest arguments");
// print(arguments);
// });
@ -150,21 +156,45 @@ class _MyAppState extends State<MyApp> {
title: const Text('Flutter InAppBrowser Plugin example app'),
),
body: new Center(
child: new RaisedButton(onPressed: () {
//chromeSafariBrowser.open("https://flutter.io/");
inAppBrowserFallback.open(url: "https://flutter.io/", options: {
//"useOnLoadResource": true,
//"hidden": true,
//"toolbarTopFixedTitle": "Fixed title",
//"useShouldOverrideUrlLoading": true
//"hideUrlBar": true,
//"toolbarTop": false,
//"toolbarBottom": false
});
child: new RaisedButton(
onPressed: () async {
// await chromeSafariBrowser.open("https://flutter.io/");
},
child: Text("Open InAppBrowser")
),
// await InAppBrowser.openWithSystemBrowser("https://flutter.io/");
await inAppBrowserFallback.openOnLocalhost("assets/index.html", options: {
"useOnLoadResource": true,
//"hidden": true,
//"toolbarTopFixedTitle": "Fixed title",
//"useShouldOverrideUrlLoading": true
//"hideUrlBar": true,
//"toolbarTop": false,
//"toolbarBottom": false
});
// await inAppBrowserFallback.open(url: "assets/index.html", options: {
// "isLocalFile": true,
// "useOnLoadResource": true,
// //"hidden": true,
// //"toolbarTopFixedTitle": "Fixed title",
// //"useShouldOverrideUrlLoading": true
// //"hideUrlBar": true,
// //"toolbarTop": false,
// //"toolbarBottom": false
// });
// await inAppBrowserFallback.open(url: "https://flutter.io/", options: {
// //"useOnLoadResource": true,
// //"hidden": true,
// //"toolbarTopFixedTitle": "Fixed title",
// //"useShouldOverrideUrlLoading": true
// //"hideUrlBar": true,
// //"toolbarTop": false,
// //"toolbarBottom": false
// });
//await inAppBrowserFallback.openOnLocalhost("assets/index.html");
},
child: Text("Open InAppBrowser")),
),
),
);

View File

@ -38,6 +38,11 @@ flutter:
# the material Icons class.
uses-material-design: true
assets:
- assets/index.html
- assets/css/
- assets/images/
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg

View File

@ -14,6 +14,12 @@
<excludeFolder url="file://$MODULE_DIR$/example/flutter_plugin/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/flutter_plugin/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/flutter_plugin/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/build" />
<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/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/flutter_assets/packages" />
</content>
<orderEntry type="sourceFolder" forTests="false" />

View File

@ -12,6 +12,7 @@ public class InAppBrowserOptions: Options {
var useShouldOverrideUrlLoading = false
var useOnLoadResource = false
var openWithSystemBrowser = false;
var clearCache = false
var userAgent = ""
var javaScriptEnabled = true
@ -21,6 +22,7 @@ public class InAppBrowserOptions: Options {
var toolbarTopBackgroundColor = ""
var hideUrlBar = false
var mediaPlaybackRequiresUserGesture = true
var isLocalFile = false
var disallowOverScroll = false
var toolbarBottom = true

View File

@ -81,7 +81,6 @@ func convertToDictionary(text: String) -> [String: Any]? {
return nil
}
//extension WKWebView{
//
// var keyboardDisplayRequiresUserAction: Bool? {
@ -139,6 +138,7 @@ class WKWebView_IBWrapper: WKWebView {
}
class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, UITextFieldDelegate, WKScriptMessageHandler {
@IBOutlet var webView: WKWebView_IBWrapper!
@IBOutlet var closeButton: UIButton!
@IBOutlet var reloadButton: UIBarButtonItem!
@ -200,7 +200,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
spinner.hidesWhenStopped = true
spinner.isHidden = false
spinner.stopAnimating()
loadUrl(url: self.currentURL!, headers: self.initHeaders)
}
@ -421,11 +421,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
}
@objc func close() {
currentURL = nil
if (navigationDelegate != nil) {
navigationDelegate?.browserExit(uuid: self.uuid)
}
//currentURL = nil
weak var weakSelf = self
@ -435,12 +431,18 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
weakSelf?.presentingViewController?.dismiss(animated: true, completion: {() -> Void in
self.tmpWindow?.windowLevel = 0.0
UIApplication.shared.delegate?.window??.makeKeyAndVisible()
if (self.navigationDelegate != nil) {
self.navigationDelegate?.browserExit(uuid: self.uuid)
}
})
}
else {
weakSelf?.parent?.dismiss(animated: true, completion: {() -> Void in
self.tmpWindow?.windowLevel = 0.0
UIApplication.shared.delegate?.window??.makeKeyAndVisible()
if (self.navigationDelegate != nil) {
self.navigationDelegate?.browserExit(uuid: self.uuid)
}
})
}
})
@ -527,7 +529,9 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
}
if navigationAction.navigationType == .linkActivated && (browserOptions?.useShouldOverrideUrlLoading)! {
navigationDelegate?.shouldOverrideUrlLoading(uuid: self.uuid, webView: webView, url: url)
if navigationDelegate != nil {
navigationDelegate?.shouldOverrideUrlLoading(uuid: self.uuid, webView: webView, url: url)
}
decisionHandler(.cancel)
return
}
@ -606,7 +610,9 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
spinner.startAnimating()
}
return (navigationDelegate?.onLoadStart(uuid: self.uuid, webView: webView))!
if navigationDelegate != nil {
navigationDelegate?.onLoadStart(uuid: self.uuid, webView: webView)
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
@ -617,7 +623,10 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
backButton.isEnabled = webView.canGoBack
forwardButton.isEnabled = webView.canGoForward
spinner.stopAnimating()
navigationDelegate?.onLoadStop(uuid: self.uuid, webView: webView)
if navigationDelegate != nil {
navigationDelegate?.onLoadStop(uuid: self.uuid, webView: webView)
}
}
func webView(_ view: WKWebView,
@ -627,15 +636,19 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print("webView:didFailNavigationWithError - \(Int(error._code)): \(error.localizedDescription)")
backButton.isEnabled = webView.canGoBack
forwardButton.isEnabled = webView.canGoForward
spinner.stopAnimating()
navigationDelegate?.onLoadError(uuid: self.uuid, webView: webView, error: error)
if navigationDelegate != nil {
navigationDelegate?.onLoadError(uuid: self.uuid, webView: webView, error: error)
}
}
func didReceiveResourceResponse(_ response: URLResponse, fromRequest request: URLRequest?, withData data: Data, startTime: Int, duration: Int) {
navigationDelegate?.onLoadResource(uuid: self.uuid, webView: webView, response: response, fromRequest: request, withData: data, startTime: startTime, duration: duration)
if navigationDelegate != nil {
navigationDelegate?.onLoadResource(uuid: self.uuid, webView: webView, response: response, fromRequest: request, withData: data, startTime: startTime, duration: duration)
}
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
@ -663,7 +676,9 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
messageLevel = "LOG"
break;
}
navigationDelegate?.onConsoleMessage(uuid: self.uuid, sourceURL: "", lineNumber: 1, message: message.body as! String, messageLevel: messageLevel)
if navigationDelegate != nil {
navigationDelegate?.onConsoleMessage(uuid: self.uuid, sourceURL: "", lineNumber: 1, message: message.body as! String, messageLevel: messageLevel)
}
}
else if message.name == "resourceLoaded" {
if let resource = convertToDictionary(text: message.body as! String) {
@ -695,7 +710,9 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
let body = message.body as! [String: Any]
let handlerName = body["handlerName"] as! String
let args = body["args"] as! String
self.navigationDelegate?.onCallJsHandler(uuid: self.uuid, webView: webView, handlerName: handlerName, args: args)
if navigationDelegate != nil {
self.navigationDelegate?.onCallJsHandler(uuid: self.uuid, webView: webView, handlerName: handlerName, args: args)
}
}
}
}

View File

@ -14,12 +14,13 @@ public class Options: NSObject {
super.init()
}
public func parse(options: [String: Any]) {
public func parse(options: [String: Any]) -> Options {
for (key, value) in options {
if self.responds(to: Selector(key)) {
self.setValue(value, forKey: key)
}
}
return self
}
}

View File

@ -40,15 +40,15 @@ class SafariViewController: SFSafariViewController, SFSafariViewControllerDelega
}
func close() {
if (statusDelegate != nil) {
statusDelegate?.safariExit(uuid: self.uuid)
}
dismiss(animated: true)
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(400), execute: {() -> Void in
self.tmpWindow?.windowLevel = 0.0
UIApplication.shared.delegate?.window??.makeKeyAndVisible()
if (self.statusDelegate != nil) {
self.statusDelegate?.safariExit(uuid: self.uuid)
}
})
}

View File

@ -22,19 +22,6 @@ import Foundation
import AVFoundation
import SafariServices
//class CustomURLCache: URLCache {
// override func cachedResponse(for request: URLRequest) -> CachedURLResponse? {
// dump(request.url)
// return super.cachedResponse(for: request)
// }
//
// override func getCachedResponse(for dataTask: URLSessionDataTask,
// completionHandler: @escaping (CachedURLResponse?) -> Void) {
// dump(dataTask.response)
// super.getCachedResponse(for: dataTask, completionHandler: completionHandler)
// }
//}
let WEBVIEW_STORYBOARD = "WebView"
let WEBVIEW_STORYBOARD_CONTROLLER_ID = "viewController"
@ -47,6 +34,9 @@ extension Dictionary where Key: ExpressibleByStringLiteral {
}
public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
static var registrar: FlutterPluginRegistrar?
var webViewControllers: [String: InAppBrowserWebViewController?] = [:]
var safariViewControllers: [String: Any?] = [:]
@ -59,11 +49,8 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
}
public static func register(with registrar: FlutterPluginRegistrar) {
// URLProtocol.wk_registerScheme("http")
// URLProtocol.wk_registerScheme("https")
// URLProtocol.registerClass(MyURLProtocol.self)
//URLCache.shared = CustomURLCache()
SwiftFlutterPlugin.registrar = registrar
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappbrowser", binaryMessenger: registrar.messenger())
let instance = SwiftFlutterPlugin(with: registrar)
@ -73,7 +60,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
let uuid: String = (arguments!["uuid"] as? String)!
switch call.method {
case "open":
self.open(uuid: uuid, arguments: arguments!, result: result)
@ -177,7 +164,6 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
}
else {
print("IAB.close() called but it was already closed.")
return
}
}
@ -192,41 +178,38 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
let url: String = (arguments["url"] as? String)!
let headers = (arguments["headers"] as? [String: String])!
var target: String? = (arguments["target"] as? String)!
target = target != nil ? target : "_self"
let absoluteUrl = URL(string: url)?.absoluteURL
let useChromeSafariBrowser = (arguments["useChromeSafariBrowser"] as? Bool)
if useChromeSafariBrowser! {
let uuidFallback = (arguments["uuidFallback"] as? String)!
let options = (arguments["options"] as? [String: Any])!
let optionsFallback = (arguments["optionsFallback"] as? [String: Any])!
let safariOptions = SafariBrowserOptions()
safariOptions.parse(options: (arguments["options"] as? [String: Any])!)
open(uuid: uuid, uuidFallback: uuidFallback, inAppBrowser: absoluteUrl!, headers: headers, withOptions: options, useChromeSafariBrowser: true, withOptionsFallback: optionsFallback);
let optionsFallback = InAppBrowserOptions()
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);
}
else {
let options = (arguments["options"] as? [String: Any])!
let options = InAppBrowserOptions()
options.parse(options: (arguments["options"] as? [String: Any])!)
if isSystemUrl(absoluteUrl!) {
target = "_system"
options.openWithSystemBrowser = true
}
if (target == "_self" || target == "_target") {
open(uuid: uuid, uuidFallback: nil, inAppBrowser: absoluteUrl!, headers: headers, withOptions: options, useChromeSafariBrowser: false, withOptionsFallback: nil)
}
else if (target == "_system") {
open(inSystem: absoluteUrl!)
if (options.openWithSystemBrowser) {
open(inSystem: absoluteUrl!, result: result)
}
else {
// anything else
open(uuid: uuid, uuidFallback: nil, inAppBrowser: absoluteUrl!, headers: headers,withOptions: options, useChromeSafariBrowser: false, withOptionsFallback: nil)
open(uuid: uuid, uuidFallback: nil, inAppBrowser: absoluteUrl!, headers: headers, withOptions: options, useChromeSafariBrowser: false, withOptionsFallback: nil, result: result)
}
}
result(true)
}
func open(uuid: String, uuidFallback: String?, inAppBrowser url: URL, headers: [String: String], withOptions options: [String: Any], useChromeSafariBrowser: Bool, withOptionsFallback optionsFallback: [String: Any]?) {
func open(uuid: String, uuidFallback: String?, inAppBrowser url: URL, headers: [String: String], withOptions options: Options, useChromeSafariBrowser: Bool, withOptionsFallback optionsFallback: Options?, result: @escaping FlutterResult) {
var uuid = uuid
@ -235,7 +218,6 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
}
let safariViewController = self.safariViewControllers[uuid]
if safariViewController != nil {
if #available(iOS 9.0, *) {
(safariViewController! as! SafariViewController).close()
@ -264,8 +246,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
if useChromeSafariBrowser == true {
if #available(iOS 9.0, *) {
let safariOptions = SafariBrowserOptions()
safariOptions.parse(options: options)
let safariOptions = options as! SafariBrowserOptions
let safari: SafariViewController
@ -290,24 +271,37 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
tmpController.present(self.safariViewControllers[uuid]! as! SFSafariViewController, animated: true)
onChromeSafariBrowserOpened(uuid: uuid)
result(true)
return
}
else {
if uuidFallback == nil {
print("No WebView fallback declared.")
result(true)
return
}
uuid = uuidFallback!
browserOptions = InAppBrowserOptions()
browserOptions.parse(options: optionsFallback!)
browserOptions = optionsFallback as! InAppBrowserOptions
}
}
else {
browserOptions = InAppBrowserOptions()
browserOptions.parse(options: options)
browserOptions = options as! InAppBrowserOptions
}
var currentURL = url
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 vc = storyboard.instantiateViewController(withIdentifier: WEBVIEW_STORYBOARD_CONTROLLER_ID)
self.webViewControllers[uuid] = vc as? InAppBrowserWebViewController
@ -316,7 +310,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
webViewController.browserOptions = browserOptions
webViewController.isHidden = browserOptions.hidden
webViewController.tmpWindow = tmpWindow
webViewController.currentURL = url
webViewController.currentURL = currentURL
webViewController.initHeaders = headers
webViewController.navigationDelegate = self
@ -338,6 +332,8 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
else {
tmpController.present(webViewController, animated: true, completion: nil)
}
result(true)
}
public func loadUrl(uuid: String, arguments: NSDictionary, result: @escaping FlutterResult) {
@ -350,7 +346,6 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
webViewController.loadUrl(url: absoluteUrl, headers: headers)
}
else {
print("url is empty")
result(FlutterError(code: "InAppBrowserFlutterPlugin", message: "url is empty", details: nil))
}
result(true)
@ -403,17 +398,18 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
}
}
func open(inSystem url: URL) {
func open(inSystem url: URL, result: @escaping FlutterResult) {
if !UIApplication.shared.canOpenURL(url) {
NotificationCenter.default.post(Notification(name: Notification.Name(rawValue: "PluginHandleOpenURLNotification"), object: url))
return
result(FlutterError(code: "InAppBrowserFlutterPlugin", message: url.absoluteString + " cannot be opened!", details: nil))
}
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url)
else {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url)
}
}
result(true)
}
// This is a helper method for the inject{Script|Style}{Code|File} API calls, which
@ -429,7 +425,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
let jsonData: Data? = try? JSONSerialization.data(withJSONObject: [source], options: [])
let sourceArrayString = String(data: jsonData!, encoding: String.Encoding.utf8)
if sourceArrayString != nil {
let sourceString: String? = (sourceArrayString! as NSString).substring(with: NSRange(location: 1, length: (sourceArrayString?.characters.count ?? 0) - 2))
let sourceString: String? = (sourceArrayString! as NSString).substring(with: NSRange(location: 1, length: (sourceArrayString?.count ?? 0) - 2))
let jsToInject = String(format: jsWrapper, sourceString!)
webViewController?.webView?.evaluateJavaScript(jsToInject, completionHandler: {(value, error) in
@ -439,7 +435,6 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
if error != nil {
let userInfo = (error! as NSError).userInfo
dump(userInfo)
self.onConsoleMessage(uuid: uuid, sourceURL: (userInfo["WKJavaScriptExceptionSourceURL"] as? URL)?.absoluteString ?? "", lineNumber: userInfo["WKJavaScriptExceptionLineNumber"] as! Int, message: userInfo["WKJavaScriptExceptionMessage"] as! String, messageLevel: "ERROR")
}
@ -458,7 +453,6 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
result!(value)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
result!(FlutterError(code: "InAppBrowserFlutterPlugin", message: "Failed to load: \(error.localizedDescription)", details: error))
}

View File

@ -19,6 +19,7 @@
*
*/
import 'dart:io';
import 'dart:async';
import 'dart:collection';
import 'dart:typed_data';
@ -26,6 +27,7 @@ import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:uuid/uuid.dart';
import 'package:mime/mime.dart';
typedef Future<dynamic> ListenerCallback(MethodCall call);
typedef Future<void> JavaScriptHandlerCallback(List<dynamic> arguments);
@ -85,8 +87,7 @@ class _ChannelManager {
static Future<dynamic> _handleMethod(MethodCall call) async {
String uuid = call.arguments["uuid"];
listeners[uuid](call);
return new Future.value("");
return await listeners[uuid](call);
}
static void addListener (String key, ListenerCallback callback) {
@ -107,11 +108,14 @@ class InAppBrowser {
String uuid;
Map<String, List<JavaScriptHandlerCallback>> javaScriptHandlersMap = HashMap<String, List<JavaScriptHandlerCallback>>();
HttpServer _server;
bool _isOpened = false;
///
InAppBrowser () {
uuid = _uuidGenerator.v4();
_ChannelManager.addListener(uuid, _handleMethod);
_isOpened = false;
}
Future<dynamic> _handleMethod(MethodCall call) async {
@ -131,6 +135,8 @@ class InAppBrowser {
onLoadError(url, code, message);
break;
case "onExit":
this._closeServer();
this._isOpened = false;
onExit();
break;
case "shouldOverrideUrlLoading":
@ -183,133 +189,271 @@ class InAppBrowser {
}
}
break;
default:
throw UnimplementedError("Unimplemented ${call.method} method");
}
return new Future.value("");
}
///Opens an [url] in a new [InAppBrowser] instance or the system browser.
///Opens an [url] in a new [InAppBrowser] instance or in the system browser (`openWithSystemBrowser: true`).
///
///**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`.
///
///- [headers]: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value.
///
///- [target]: The target in which to load the [url], an optional parameter that defaults to `_self`.
///
/// - `_self`: Opens in the [InAppBrowser].
/// - `_blank`: Opens in the [InAppBrowser].
/// - `_system`: Opens in the system's web browser.
///
///- [options]: Options for the [InAppBrowser].
///
/// 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`.
/// - __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`.
/// - __toolbarTopBackgroundColor__: Set the custom background color of the toolbat at the top.
/// - __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`.
/// - 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`.
/// - __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`.
/// - __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`.
/// - __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`.
/// - __toolbarTopBackgroundColor__: Set the custom background color of the toolbat at the top.
/// - __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`.
///
/// **Android** supports these additional options:
/// - **Android** supports these additional options:
///
/// - __hideTitleBar__: Set to `true` if you want the title should be displayed. The default value is `false`.
/// - __closeOnCannotGoBack__: Set to `false` to not close the InAppBrowser when the user click on the back button and the WebView cannot go back to the history. The default value is `true`.
/// - __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`.
/// - __progressBar__: Set to `false` to hide the progress bar at the bottom of the toolbar at the top. The default value is `true`.
/// - __hideTitleBar__: Set to `true` if you want the title should be displayed. The default value is `false`.
/// - __closeOnCannotGoBack__: Set to `false` to not close the InAppBrowser when the user click on the back button and the WebView cannot go back to the history. The default value is `true`.
/// - __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`.
/// - __progressBar__: Set to `false` to hide the progress bar at the bottom of the toolbar at the top. The default value is `true`.
///
/// **iOS** supports these additional options:
/// - **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`.
/// - __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.
/// - __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.
/// - __closeButtonColor__: Set the custom color for the close button.
/// - __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.
/// - __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`.
/// - __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 {}, String target = "_self", Map<String, dynamic> options = const {}}) async {
/// - __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`.
/// - __toolbarBottomBackgroundColor__: Set the custom background color of the toolbat at the bottom.
/// - __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.
/// - __closeButtonColor__: Set the custom color for the close button.
/// - __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.
/// - __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`.
/// - __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 {
this._throwIsAlreadyOpened(message: 'Cannot open $url!');
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headers);
args.putIfAbsent('target', () => target);
args.putIfAbsent('options', () => options);
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.
///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);
return await _ChannelManager.channel.invokeMethod('loadUrl', args);
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!
///
///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 {
this._throwIsNotOpened(message: 'Cannot laod $assetFilePath!');
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => assetFilePath);
args.putIfAbsent('headers', () => headers);
await _ChannelManager.channel.invokeMethod('loadFile', args);
}
///Displays an [InAppBrowser] window that was opened hidden. Calling this has no effect if the [InAppBrowser] was already visible.
Future<void> show() async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
return await _ChannelManager.channel.invokeMethod('show', args);
await _ChannelManager.channel.invokeMethod('show', args);
}
///Hides the [InAppBrowser] window. Calling this has no effect if the [InAppBrowser] was already hidden.
Future<void> hide() async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
return await _ChannelManager.channel.invokeMethod('hide', args);
await _ChannelManager.channel.invokeMethod('hide', args);
}
///Closes the [InAppBrowser] window.
Future<void> close() async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
return 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);
return await _ChannelManager.channel.invokeMethod('reload', args);
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);
return await _ChannelManager.channel.invokeMethod('goBack', args);
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);
return await _ChannelManager.channel.invokeMethod('goForward', args);
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);
@ -317,48 +461,54 @@ class InAppBrowser {
///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);
return await _ChannelManager.channel.invokeMethod('stopLoading', args);
await _ChannelManager.channel.invokeMethod('stopLoading', args);
}
///Check if the Web View of the [InAppBrowser] instance is hidden.
Future<bool> isHidden() async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
return await _ChannelManager.channel.invokeMethod('isHidden', args);
}
///Injects JavaScript code into the [InAppBrowser] window and returns the result of the evaluation. (Only available when the target is set to `_blank` or to `_self`)
///Injects JavaScript code into the [InAppBrowser] window and returns the result of the evaluation.
Future<String> injectScriptCode(String source) 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. (Only available when the target is set to `_blank` or to `_self`)
///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);
return await _ChannelManager.channel.invokeMethod('injectScriptFile', args);
await _ChannelManager.channel.invokeMethod('injectScriptFile', args);
}
///Injects CSS into the [InAppBrowser] window. (Only available when the target is set to `_blank` or to `_self`)
///Injects CSS into the [InAppBrowser] window.
Future<void> injectStyleCode(String source) async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('source', () => source);
return await _ChannelManager.channel.invokeMethod('injectStyleCode', args);
await _ChannelManager.channel.invokeMethod('injectStyleCode', args);
}
///Injects a CSS file into the [InAppBrowser] window. (Only available when the target is set to `_blank` or to `_self`)
///Injects a CSS file into the [InAppBrowser] window.
Future<void> injectStyleFile(String urlFile) async {
this._throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('urlFile', () => urlFile);
return await _ChannelManager.channel.invokeMethod('injectStyleFile', args);
await _ChannelManager.channel.invokeMethod('injectStyleFile', args);
}
///Adds/Appends a JavaScript message handler [callback] ([JavaScriptHandlerCallback]) that listen to post messages sent from JavaScript by the handler with name [handlerName].
@ -419,7 +569,7 @@ class InAppBrowser {
///
///**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.
///**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) {
}
@ -429,6 +579,22 @@ class InAppBrowser {
}
///Returns `true` if the browser is opened, otherwise `false`.
bool isOpened() {
return this._isOpened;
}
void _throwIsAlreadyOpened({String message = ''}) {
if (this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is already opened.']);
}
}
void _throwIsNotOpened({String message = ''}) {
if (!this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is not opened.']);
}
}
}
///ChromeSafariBrowser class.
@ -440,12 +606,14 @@ class InAppBrowser {
class ChromeSafariBrowser {
String uuid;
InAppBrowser browserFallback;
bool _isOpened = false;
///Initialize the [ChromeSafariBrowser] instance with a [InAppBrowser] fallback instance or `null`.
ChromeSafariBrowser (bf) {
uuid = _uuidGenerator.v4();
browserFallback = bf;
_ChannelManager.addListener(uuid, _handleMethod);
_isOpened = false;
}
Future<dynamic> _handleMethod(MethodCall call) async {
@ -458,9 +626,11 @@ class ChromeSafariBrowser {
break;
case "onChromeSafariBrowserClosed":
onClosed();
this._isOpened = false;
break;
default:
throw UnimplementedError("Unimplemented ${call.method} method");
}
return new Future.value("");
}
///Opens an [url] in a new [ChromeSafariBrowser] instance or the system browser.
@ -491,16 +661,17 @@ 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.
///- __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 {
this._throwIsAlreadyOpened(message: 'Cannot open $url!');
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('uuidFallback', () => (browserFallback != null) ? browserFallback.uuid : '');
args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headersFallback);
args.putIfAbsent('target', () => "");
args.putIfAbsent('options', () => options);
args.putIfAbsent('optionsFallback', () => optionsFallback);
args.putIfAbsent('useChromeSafariBrowser', () => true);
return await _ChannelManager.channel.invokeMethod('open', args);
await _ChannelManager.channel.invokeMethod('open', args);
this._isOpened = true;
}
///Event fires when the [ChromeSafariBrowser] is opened.
@ -517,4 +688,20 @@ class ChromeSafariBrowser {
void onClosed() {
}
bool isOpened() {
return this._isOpened;
}
void _throwIsAlreadyOpened({String message = ''}) {
if (this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is already opened.']);
}
}
void _throwIsNotOpened({String message = ''}) {
if (!this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is not opened.']);
}
}
}

View File

@ -1,6 +1,6 @@
name: flutter_inappbrowser
description: A Flutter plugin that allows you to open an in-app browser window. (inspired by the popular cordova-plugin-inappbrowser).
version: 0.3.2
version: 0.4.0
author: Lorenzo Pichilli <pichillilorenzo@gmail.com>
homepage: https://github.com/pichillilorenzo/flutter_inappbrowser
@ -11,6 +11,7 @@ dependencies:
flutter:
sdk: flutter
uuid: ^1.0.3
mime: ^0.9.6+2
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec