├── .gitignore ├── LICENSE ├── NOTICE ├── PlatformScriptsWorkflow.md ├── README.md ├── hooks ├── after_build │ └── 000-build_64_bit.js ├── after_plugin_install │ └── 000-shared_mode_special.js ├── before_build │ └── 000-build_64_bit.js ├── before_plugin_uninstall │ └── 000-shared_mode_special.js └── update_config.js ├── package.json ├── platforms └── android │ ├── src │ └── org │ │ └── crosswalk │ │ └── engine │ │ ├── XWalkCordovaClientCertRequest.java │ │ ├── XWalkCordovaCookieManager.java │ │ ├── XWalkCordovaHttpAuthHandler.java │ │ ├── XWalkCordovaResourceClient.java │ │ ├── XWalkCordovaUiClient.java │ │ ├── XWalkCordovaView.java │ │ ├── XWalkExposedJsApi.java │ │ ├── XWalkFileChooser.java │ │ └── XWalkWebViewEngine.java │ └── xwalk.gradle └── plugin.xml /.gitignore: -------------------------------------------------------------------------------- 1 | platforms/android/libs/xwalk_core_library/build/ 2 | platforms/android/libs/xwalk_core_library/build.gradle 3 | *.iml 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | /************************************************/ 205 | This product bundles CordovaXWalkCoreExtensionBridge.java as well 206 | as the XWalk.pak and the Crosswalk JSApi which is available under a 207 | "3-clause BSD" license. For details, see below: 208 | 209 | Copyright (c) 2013 Intel Corporation. All rights reserved. 210 | 211 | Redistribution and use in source and binary forms, with or without 212 | modification, are permitted provided that the following conditions are 213 | met: 214 | 215 | * Redistributions of source code must retain the above copyright 216 | notice, this list of conditions and the following disclaimer. 217 | * Redistributions in binary form must reproduce the above 218 | copyright notice, this list of conditions and the following disclaimer 219 | in the documentation and/or other materials provided with the 220 | distribution. 221 | * Neither the name of Intel Corporation nor the names of its 222 | contributors may be used to endorse or promote products derived from 223 | this software without specific prior written permission. 224 | 225 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 226 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 227 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 228 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 229 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 230 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 231 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 232 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 233 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 234 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 235 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 236 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Apache Cordova 2 | Copyright 2014 The Apache Software Foundation 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org) 6 | 7 | This software includes software developed at Intel Corporation. 8 | Copyright 2014 Intel Corporation 9 | -------------------------------------------------------------------------------- /PlatformScriptsWorkflow.md: -------------------------------------------------------------------------------- 1 | ### Directions for Non-CLI Android-Only cordova project 2 | 3 | * Pull down the Cordova Android 4 | ``` 5 | $ git clone https://github.com/apache/cordova-android.git 6 | ``` 7 | * Generate a project, e.g creating HelloWorld 8 | ``` 9 | $ /path/to/cordova-android/bin/create hello com.example.hello HelloWorld 10 | ``` 11 | * Navigate to the project folder 12 | ``` 13 | $ cd hello 14 | ``` 15 | * Install Crosswalk engine plugin by plugman (version >= 0.22.17) 16 | ``` 17 | $ plugman install --platform android --plugin https://github.com/MobileChromeApps/cordova-crosswalk-engine.git --project . 18 | ``` 19 | * Build 20 | ``` 21 | $ ./cordova/build 22 | ``` 23 | The build script will automatically fetch the Crosswalk WebView libraries from Crosswalk project download site (https://download.01.org/crosswalk/releases/crosswalk/android/) and build for both X86 and ARM architectures. 24 | 25 | For example, building HelloWorld generates: 26 | 27 | ``` 28 | /path/to/hello/build/outputs/apk/hello-x86-debug.apk 29 | /path/to/hello/build/outputs/apk/hello-armv7-debug.apk 30 | ``` 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cordova-plugin-crosswalk-webview 2 | 3 | Makes your Cordova application use the [Crosswalk WebView](https://crosswalk-project.org/) 4 | instead of the System WebView. Requires cordova-android 4.0 or greater. 5 | 6 | ### Benefits 7 | 8 | * WebView doesn't change depending on Android version 9 | * Capabilities: such as WebRTC, WebAudio, Web Components 10 | * Performance improvements (compared to older system webviews) 11 | 12 | 13 | ### Drawbacks 14 | 15 | * Increased memory footprint 16 | * An overhead of ~30MB (as reported by the RSS column of ps) 17 | * Increased APK size (about 17MB) 18 | * Increased size on disk when installed (about 50MB) 19 | * Crosswalk WebView stores data (IndexedDB, LocalStorage, etc) separately from System WebView 20 | * You'll need to manually migrate local data when switching between the two (note: this is fixed in Crosswalk 15) 21 | 22 | ### Install 23 | 24 | The following directions are for cordova-cli (most people). Alternatively you can use the [Android platform scripts workflow](PlatformScriptsWorkflow.md). 25 | 26 | * Open an existing cordova project, with cordova-android 4.0.0+, and using the latest CLI. Crosswalk variables can be configured as an option when installing the plugin 27 | * Add this plugin 28 | 29 | ``` 30 | $ cordova plugin add cordova-plugin-crosswalk-webview 31 | ``` 32 | 33 | * Build 34 | ``` 35 | $ cordova build android 36 | ``` 37 | The build script will automatically fetch the Crosswalk WebView libraries from Crosswalk project download site (https://download.01.org/crosswalk/releases/crosswalk/android/maven2/) and build for both X86 and ARM architectures. 38 | 39 | For example, building android with Crosswalk generates: 40 | 41 | ``` 42 | /path/to/hello/platforms/android/build/outputs/apk/hello-x86-debug.apk 43 | /path/to/hello/platforms/android/build/outputs/apk/hello-armv7-debug.apk 44 | ``` 45 | 46 | Note that you might have to run `cordova clean` before building, if you previously built the app without cordova-plugin-crosswalk-webview. Also, manually uninstall the app from the device/emulator before attempting to install the crosswalk-enabled version. 47 | 48 | Also note that it is also possible to publish a multi-APK application on the Play Store that uses Crosswalk for Pre-L devices, and the (updatable) system webview for L+: 49 | 50 | To build Crosswalk-enabled apks, add this plugin and run: 51 | 52 | $ cordova build --release 53 | 54 | To build System-webview apk, remove this plugin and run: 55 | 56 | $ cordova build --release -- --minSdkVersion=21 57 | 58 | ### Configure 59 | 60 | You can try out a different Crosswalk version by specifying certain variables while installing the plugin, or by changing the value of `xwalkVersion` in your `config.xml` after installing the plugin. Some examples: 61 | 62 | 63 | cordova plugin add cordova-plugin-crosswalk-webview --variable XWALK_VERSION="org.xwalk:xwalk_core_library:14+" 64 | cordova plugin add cordova-plugin-crosswalk-webview --variable XWALK_VERSION="xwalk_core_library:14+" 65 | cordova plugin add cordova-plugin-crosswalk-webview --variable XWALK_VERSION="14+" 66 | cordova plugin add cordova-plugin-crosswalk-webview --variable XWALK_VERSION="14" 67 | 68 | 69 | 70 | 71 | 72 | You can also use a Crosswalk beta version. Some examples: 73 | 74 | 75 | cordova plugin add cordova-plugin-crosswalk-webview --variable XWALK_VERSION="org.xwalk:xwalk_core_library_beta:14+" 76 | 77 | 78 | You can set [command-line flags](http://peter.sh/experiments/chromium-command-line-switches/) as well: 79 | 80 | 81 | cordova plugin add cordova-plugin-crosswalk-webview --variable XWALK_COMMANDLINE="--disable-pull-to-refresh-effect" 82 | 83 | 84 | You can use the Crosswalk [shared mode](https://crosswalk-project.org/documentation/shared_mode.html) which allows multiple Crosswalk applications to share one Crosswalk runtime downloaded from the Play Store. 85 | 86 | 87 | cordova plugin add cordova-plugin-crosswalk-webview --variable XWALK_MODE="shared" 88 | 89 | 90 | You can also use a Crosswalk beta version on shared mode, e.g.: 91 | 92 | 93 | cordova plugin add cordova-plugin-crosswalk-webview --variable XWALK_VERSION="org.xwalk:xwalk_shared_library_beta:14+" 94 | 95 | You can use the Crosswalk [lite mode](https://crosswalk-project.org/documentation/crosswalk_lite.html) which is the Crosswalk runtime designed to be as small as possible by removing less common libraries and features and compressing the APK. 96 | 97 | 98 | cordova plugin add cordova-plugin-crosswalk-webview --variable XWALK_MODE="lite" 99 | 100 | 101 | You can set background color with the preference of BackgroundColor. 102 | 103 | 104 | 105 | 106 | You can also set user agent with the preference of xwalkUserAgent. 107 | 108 | 109 | 110 | ### Release Notes 111 | 112 | #### 2.3.0 (January 21, 2017) 113 | * Uses the latest Crosswalk 23 stable version by default 114 | 115 | #### 2.2.0 (November 4, 2016) 116 | * Uses the latest Crosswalk 22 stable version by default 117 | * Keep compatible for Cordova-android 6.0 with evaluating Javascript bridge 118 | * This version requires cordova-android 6.0.0 or newer 119 | 120 | #### 2.1.0 (September 9, 2016) 121 | * Uses the latest Crosswalk 21 stable version by default 122 | 123 | #### 2.0.0 (August 17, 2016) 124 | * Uses the latest Crosswalk 20 stable version by default 125 | * Discontinue support for Android 4.0 (ICS) in Crosswalk starting with version 20 126 | 127 | #### 1.8.0 (June 30, 2016) 128 | * Uses the latest Crosswalk 19 stable version by default 129 | 130 | #### 1.7.0 (May 4, 2016) 131 | * Uses the latest Crosswalk 18 stable version by default 132 | * Support to use [Crosswalk Lite](https://crosswalk-project.org/documentation/crosswalk_lite.html), It's possible to specify lite value with the variable of XWALK_MODE at install plugin time. 133 | * [Cordova screenshot plugin](https://github.com/gitawego/cordova-screenshot.git) can capture the visible content of web page with Crosswalk library. 134 | * Doesn't work with Crosswalk 17 and earlier 135 | 136 | #### 1.6.0 (March 11, 2016) 137 | * Uses the latest Crosswalk 17 stable version by default 138 | * Support to [package apps for 64-bit devices](https://crosswalk-project.org/documentation/android/android_64bit.html), it's possible to specify 64-bit targets using the `--xwalk64bit` option in the build command: 139 | 140 | cordova build android --xwalk64bit 141 | 142 | #### 1.5.0 (January 18, 2016) 143 | * Uses the latest Crosswalk 16 stable version by default 144 | * The message of xwalk's ready can be listened 145 | 146 | #### 1.4.0 (November 5, 2015) 147 | * Uses the latest Crosswalk 15 stable version by default 148 | * Support User Agent and Background Color configuration preferences 149 | * Compatible with the newest Cordova version 5.3.4 150 | 151 | #### 1.3.0 (August 28, 2015) 152 | * Crosswalk variables can be configured as an option via CLI 153 | * Support for [Crosswalk's shared mode](https://crosswalk-project.org/documentation/shared_mode.html) via the XWALK_MODE install variable or xwalkMode preference 154 | * Uses the latest Crosswalk 14 stable version by default 155 | * The ANIMATABLE_XWALK_VIEW preference is false by default 156 | * Doesn't work with Crosswalk 14.43.343.17 and earlier 157 | 158 | #### 1.2.0 (April 22, 2015) 159 | * Made Crosswalk command-line configurable via `` 160 | * Disabled pull-down-to-refresh by default 161 | 162 | #### 1.1.0 (April 21, 2015) 163 | * Based on Crosswalk v13 164 | * Made Crosswalk version configurable via `` 165 | 166 | #### 1.0.0 (Mar 25, 2015) 167 | * Initial release 168 | * Based on Crosswalk v11 169 | -------------------------------------------------------------------------------- /hooks/after_build/000-build_64_bit.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | module.exports = function(context) { 4 | 5 | /** @external */ 6 | var deferral = context.requireCordovaModule('q').defer(), 7 | UpdateConfig = require('./../update_config.js'), 8 | updateConfig = new UpdateConfig(context); 9 | 10 | /** Main method */ 11 | var main = function() { 12 | // Remove the xwalk variables 13 | updateConfig.afterBuild64bit(); 14 | 15 | deferral.resolve(); 16 | }; 17 | 18 | main(); 19 | 20 | return deferral.promise; 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /hooks/after_plugin_install/000-shared_mode_special.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | module.exports = function(context) { 4 | 5 | /** @external */ 6 | var deferral = context.requireCordovaModule('q').defer(), 7 | UpdateConfig = require('./../update_config.js'), 8 | updateConfig = new UpdateConfig(context); 9 | 10 | /** Main method */ 11 | var main = function() { 12 | // Add xwalk preference to config.xml 13 | updateConfig.addPreferences(); 14 | 15 | deferral.resolve(); 16 | }; 17 | 18 | main(); 19 | 20 | return deferral.promise; 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /hooks/before_build/000-build_64_bit.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | module.exports = function(context) { 4 | 5 | /** @external */ 6 | var deferral = context.requireCordovaModule('q').defer(), 7 | UpdateConfig = require('./../update_config.js'), 8 | updateConfig = new UpdateConfig(context); 9 | 10 | /** Main method */ 11 | var main = function() { 12 | // Remove the xwalk variables 13 | updateConfig.beforeBuild64bit(); 14 | 15 | deferral.resolve(); 16 | }; 17 | 18 | main(); 19 | 20 | return deferral.promise; 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /hooks/before_plugin_uninstall/000-shared_mode_special.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | module.exports = function(context) { 4 | 5 | /** @external */ 6 | var deferral = context.requireCordovaModule('q').defer(), 7 | UpdateConfig = require('./../update_config.js'), 8 | updateConfig = new UpdateConfig(context); 9 | 10 | /** Main method */ 11 | var main = function() { 12 | // Remove the xwalk variables 13 | updateConfig.removePreferences(); 14 | 15 | deferral.resolve(); 16 | }; 17 | 18 | main(); 19 | 20 | return deferral.promise; 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /hooks/update_config.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | module.exports = function(context) { 4 | 5 | var ConfigParser, XmlHelpers; 6 | try { 7 | // cordova-lib >= 5.3.4 doesn't contain ConfigParser and xml-helpers anymore 8 | ConfigParser = context.requireCordovaModule("cordova-common").ConfigParser; 9 | XmlHelpers = context.requireCordovaModule("cordova-common").xmlHelpers; 10 | } catch (e) { 11 | ConfigParser = context.requireCordovaModule("cordova-lib/src/configparser/ConfigParser"); 12 | XmlHelpers = context.requireCordovaModule("cordova-lib/src/util/xml-helpers"); 13 | } 14 | 15 | /** @external */ 16 | var fs = context.requireCordovaModule('fs'), 17 | path = context.requireCordovaModule('path'), 18 | et = context.requireCordovaModule('elementtree'); 19 | 20 | /** @defaults */ 21 | var xwalkVariables = {}, 22 | argumentsString = context.cmdLine, 23 | pluginConfigurationFile = path.join(context.opts.plugin.dir, 'plugin.xml'), 24 | androidPlatformDir = path.join(context.opts.projectRoot, 25 | 'platforms', 'android'), 26 | projectConfigurationFile = path.join(context.opts.projectRoot, 27 | 'config.xml'), 28 | platformConfigurationFile = path.join(androidPlatformDir, 29 | 'res', 'xml', 'config.xml'), 30 | projectManifestFile = path.join(androidPlatformDir, 31 | 'AndroidManifest.xml'), 32 | xwalk64bit = "xwalk64bit", 33 | xwalkLiteVersion = "", 34 | specificVersion = false; 35 | 36 | /** Init */ 37 | var CordovaConfig = new ConfigParser(platformConfigurationFile); 38 | 39 | var addPermission = function() { 40 | var projectManifestXmlRoot = XmlHelpers.parseElementtreeSync(projectManifestFile); 41 | var child = et.XML(''); 42 | XmlHelpers.graftXML(projectManifestXmlRoot, [child], '/manifest'); 43 | fs.writeFileSync(projectManifestFile, projectManifestXmlRoot.write({indent: 4}), 'utf-8'); 44 | } 45 | 46 | var removePermission = function() { 47 | var projectManifestXmlRoot = XmlHelpers.parseElementtreeSync(projectManifestFile); 48 | var child = et.XML(''); 49 | XmlHelpers.pruneXML(projectManifestXmlRoot, [child], '/manifest'); 50 | fs.writeFileSync(projectManifestFile, projectManifestXmlRoot.write({indent: 4}), 'utf-8'); 51 | } 52 | 53 | var defaultPreferences = function() { 54 | var pluginPreferences = {}; 55 | 56 | var pluginXmlRoot = XmlHelpers.parseElementtreeSync(pluginConfigurationFile), 57 | tagName = "preference", 58 | containerName = "config-file", 59 | targetPlatform = 'android', 60 | targetPlatformTag = pluginXmlRoot.find('./platform[@name="' + targetPlatform + '"]'); 61 | 62 | var tagsInRoot = pluginXmlRoot.findall(tagName) || [], 63 | tagsInPlatform = targetPlatformTag ? targetPlatformTag.findall(tagName) : [], 64 | tagsInContainer = targetPlatformTag ? targetPlatformTag.findall(containerName) : [], 65 | tagsList = tagsInRoot.concat(tagsInContainer); 66 | 67 | // Parses tags within -blocks 68 | tagsList.map(function(prefTag) { 69 | prefTag.getchildren().forEach(function(element) { 70 | if ((element.tag == 'preference') && (element.attrib['name']) && element.attrib['default']) { 71 | // Don't add xwalkLiteVersion in the app/config.xml 72 | if (element.attrib['name'] == "xwalkLiteVersion") { 73 | xwalkLiteVersion = element.attrib['default']; 74 | } else { 75 | pluginPreferences[element.attrib['name']] = element.attrib['default']; 76 | } 77 | } 78 | }); 79 | }); 80 | 81 | return pluginPreferences; 82 | } 83 | 84 | /** The style of name align with config.xml */ 85 | var setConfigPreference = function(name, value) { 86 | var trimName = name.replace('_', ''); 87 | for (var localName in xwalkVariables) { 88 | if (localName.toUpperCase() == trimName.toUpperCase()) { 89 | xwalkVariables[localName] = value; 90 | if (localName == 'xwalkVersion') { 91 | specificVersion = true; 92 | } 93 | } 94 | } 95 | } 96 | 97 | /** Pase the cli command to get the specific preference*/ 98 | var parseCliPreference = function() { 99 | var commandlineVariablesList = argumentsString.split('--variable'); 100 | if (commandlineVariablesList) { 101 | commandlineVariablesList.forEach(function(element) { 102 | element = element.trim(); 103 | if(element && element.indexOf('XWALK') == 0) { 104 | var preference = element.split('='); 105 | if (preference && preference.length == 2) { 106 | setConfigPreference(preference[0], preference[1]); 107 | } 108 | } 109 | }); 110 | } 111 | } 112 | 113 | /** Add preference */ 114 | this.addPreferences = function() { 115 | // Pick the xwalk variables with the cli preferences 116 | // parseCliPreference(); 117 | 118 | // Add the permission of writing external storage when using shared mode 119 | if (CordovaConfig.getGlobalPreference('xwalkMode') == 'shared') { 120 | addPermission(); 121 | } 122 | 123 | // Configure the final value in the config.xml 124 | // var configXmlRoot = XmlHelpers.parseElementtreeSync(projectConfigurationFile); 125 | // var preferenceUpdated = false; 126 | // for (var name in xwalkVariables) { 127 | // var child = configXmlRoot.find('./preference[@name="' + name + '"]'); 128 | // if(!child) { 129 | // preferenceUpdated = true; 130 | // child = et.XML(''); 131 | // XmlHelpers.graftXML(configXmlRoot, [child], '/*'); 132 | // } 133 | // } 134 | // if(preferenceUpdated) { 135 | // fs.writeFileSync(projectConfigurationFile, configXmlRoot.write({indent: 4}), 'utf-8'); 136 | // } 137 | } 138 | 139 | /** Remove preference*/ 140 | this.removePreferences = function() { 141 | if (CordovaConfig.getGlobalPreference('xwalkMode') == 'shared') { 142 | // Add the permission of write_external_storage in shared mode 143 | removePermission(); 144 | } 145 | 146 | // var configXmlRoot = XmlHelpers.parseElementtreeSync(projectConfigurationFile); 147 | // for (var name in xwalkVariables) { 148 | // var child = configXmlRoot.find('./preference[@name="' + name + '"]'); 149 | // if (child) { 150 | // XmlHelpers.pruneXML(configXmlRoot, [child], '/*'); 151 | // } 152 | // } 153 | // fs.writeFileSync(projectConfigurationFile, configXmlRoot.write({indent: 4}), 'utf-8'); 154 | } 155 | 156 | var build64bit = function() { 157 | var build64bit = false; 158 | var commandlineVariablesList = argumentsString.split('--'); 159 | 160 | if (commandlineVariablesList) { 161 | commandlineVariablesList.forEach(function(element) { 162 | element = element.trim(); 163 | if(element && element.indexOf(xwalk64bit) == 0) { 164 | build64bit = true; 165 | } 166 | }); 167 | } 168 | return build64bit; 169 | } 170 | 171 | this.beforeBuild64bit = function() { 172 | if(build64bit()) { 173 | var configXmlRoot = XmlHelpers.parseElementtreeSync(projectConfigurationFile); 174 | var child = configXmlRoot.find('./preference[@name="' + xwalk64bit + '"]'); 175 | if(!child) { 176 | child = et.XML(''); 177 | XmlHelpers.graftXML(configXmlRoot, [child], '/*'); 178 | fs.writeFileSync(projectConfigurationFile, configXmlRoot.write({indent: 4}), 'utf-8'); 179 | } 180 | } 181 | } 182 | 183 | this.afterBuild64bit = function() { 184 | if(build64bit()) { 185 | var configXmlRoot = XmlHelpers.parseElementtreeSync(projectConfigurationFile); 186 | var child = configXmlRoot.find('./preference[@name="' + xwalk64bit + '"]'); 187 | if (child) { 188 | XmlHelpers.pruneXML(configXmlRoot, [child], '/*'); 189 | fs.writeFileSync(projectConfigurationFile, configXmlRoot.write({indent: 4}), 'utf-8'); 190 | } 191 | } 192 | 193 | console.log("Crosswalk info:"); 194 | console.log(" After much discussion and analysis of the market,"); 195 | console.log(" we have decided to discontinue support for Android 4.0 (ICS) in Crosswalk starting with version 20,"); 196 | console.log(" so the minSdkVersion of Cordova project is configured to 16 by default. \n"); 197 | } 198 | 199 | xwalkVariables = defaultPreferences(); 200 | 201 | }; 202 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-crosswalk-webview", 3 | "version": "2.3.0", 4 | "description": "Changes the default WebView to CrossWalk", 5 | "cordova": { 6 | "id": "cordova-plugin-crosswalk-webview", 7 | "platforms": [ 8 | "android" 9 | ] 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/crosswalk-project/cordova-plugin-crosswalk-webview.git" 14 | }, 15 | "keywords": [ 16 | "cordova", 17 | "chromium", 18 | "crosswalk", 19 | "webview", 20 | "engine", 21 | "ecosystem:cordova", 22 | "cordova-android" 23 | ], 24 | "engines": { 25 | "cordovaDependencies": { 26 | "2.0.0": { 27 | "cordova": ">=5.2.0", 28 | "cordova-android": "4 - 5" 29 | }, 30 | "2.1.0": { 31 | "cordova": ">=5.2.0", 32 | "cordova-android": "4 - 5" 33 | }, 34 | "2.2.0": { 35 | "cordova": ">=5.2.0", 36 | "cordova-android": ">=6" 37 | }, 38 | "3.0.0": { 39 | "cordova": ">100" 40 | } 41 | } 42 | }, 43 | "author": "", 44 | "license": "Apache 2.0", 45 | "bugs": { 46 | "url": "https://crosswalk-project.org/jira" 47 | }, 48 | "homepage": "https://github.com/crosswalk-project/cordova-plugin-crosswalk-webview" 49 | } 50 | -------------------------------------------------------------------------------- /platforms/android/src/org/crosswalk/engine/XWalkCordovaClientCertRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | package org.crosswalk.engine; 20 | 21 | import org.apache.cordova.ICordovaClientCertRequest; 22 | import org.xwalk.core.ClientCertRequest; 23 | 24 | import java.security.cert.X509Certificate; 25 | import java.security.Principal; 26 | import java.security.PrivateKey; 27 | import java.util.Arrays; 28 | 29 | public class XWalkCordovaClientCertRequest implements ICordovaClientCertRequest { 30 | 31 | private final ClientCertRequest request; 32 | 33 | public XWalkCordovaClientCertRequest(ClientCertRequest request) { 34 | this.request = request; 35 | } 36 | 37 | /** 38 | * Cancel this request 39 | */ 40 | public void cancel() { 41 | request.cancel(); 42 | } 43 | 44 | /* 45 | * Returns the host name of the server requesting the certificate. 46 | */ 47 | public String getHost() { 48 | return request.getHost(); 49 | } 50 | 51 | /* 52 | * Returns the acceptable types of asymmetric keys (can be null). 53 | */ 54 | public String[] getKeyTypes() { 55 | return request.getKeyTypes(); 56 | } 57 | 58 | /* 59 | * Returns the port number of the server requesting the certificate. 60 | */ 61 | public int getPort() { 62 | return request.getPort(); 63 | } 64 | 65 | /* 66 | * Returns the acceptable certificate issuers for the certificate matching the private 67 | * key (can be null). 68 | */ 69 | public Principal[] getPrincipals() { 70 | return request.getPrincipals(); 71 | } 72 | 73 | /* 74 | * Ignore the request for now. Do not remember user's choice. 75 | */ 76 | public void ignore() { 77 | request.ignore(); 78 | } 79 | 80 | /* 81 | * Proceed with the specified private key and client certificate chain. Remember the user's 82 | * positive choice and use it for future requests. 83 | * 84 | * @param privateKey The privateKey 85 | * @param chain The certificate chain 86 | */ 87 | public void proceed(PrivateKey privateKey, X509Certificate[] chain) { 88 | request.proceed(privateKey, Arrays.asList(chain)); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /platforms/android/src/org/crosswalk/engine/XWalkCordovaCookieManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | package org.crosswalk.engine; 20 | 21 | import org.apache.cordova.ICordovaCookieManager; 22 | import org.xwalk.core.XWalkCookieManager; 23 | 24 | class XWalkCordovaCookieManager implements ICordovaCookieManager { 25 | 26 | protected XWalkCookieManager cookieManager = null; 27 | 28 | public XWalkCordovaCookieManager() { 29 | cookieManager = new XWalkCookieManager(); 30 | } 31 | 32 | public void setCookiesEnabled(boolean accept) { 33 | cookieManager.setAcceptCookie(accept); 34 | } 35 | 36 | public void setCookie(final String url, final String value) { 37 | cookieManager.setCookie(url, value); 38 | } 39 | 40 | public String getCookie(final String url) { 41 | return cookieManager.getCookie(url); 42 | } 43 | 44 | public void clearCookies() { 45 | cookieManager.removeAllCookie(); 46 | } 47 | 48 | public void flush() { 49 | cookieManager.flushCookieStore(); 50 | } 51 | }; 52 | 53 | 54 | -------------------------------------------------------------------------------- /platforms/android/src/org/crosswalk/engine/XWalkCordovaHttpAuthHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | package org.crosswalk.engine; 20 | 21 | import org.apache.cordova.ICordovaHttpAuthHandler; 22 | import org.xwalk.core.XWalkHttpAuthHandler; 23 | 24 | /** 25 | * Specifies interface for HTTP auth handler object which is used to handle auth requests and 26 | * specifying user credentials. 27 | */ 28 | public class XWalkCordovaHttpAuthHandler implements ICordovaHttpAuthHandler { 29 | 30 | private final XWalkHttpAuthHandler handler; 31 | 32 | public XWalkCordovaHttpAuthHandler(XWalkHttpAuthHandler handler) { 33 | this.handler = handler; 34 | } 35 | 36 | /** 37 | * Instructs the XWalkView to cancel the authentication request. 38 | */ 39 | public void cancel() { 40 | handler.cancel(); 41 | } 42 | 43 | /** 44 | * Instructs the XWalkView to proceed with the authentication with the given credentials. 45 | * 46 | * @param username 47 | * @param password 48 | */ 49 | public void proceed(String username, String password) { 50 | handler.proceed(username, password); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /platforms/android/src/org/crosswalk/engine/XWalkCordovaResourceClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | package org.crosswalk.engine; 20 | 21 | import android.content.pm.ApplicationInfo; 22 | import android.content.pm.PackageManager; 23 | import android.net.Uri; 24 | import android.net.http.SslError; 25 | import android.webkit.ValueCallback; 26 | import android.webkit.WebResourceResponse; 27 | 28 | import org.apache.cordova.CordovaResourceApi; 29 | import org.apache.cordova.CordovaResourceApi.OpenForReadResult; 30 | import org.apache.cordova.LOG; 31 | import org.apache.cordova.PluginManager; 32 | import org.xwalk.core.ClientCertRequest; 33 | import org.xwalk.core.XWalkHttpAuthHandler; 34 | import org.xwalk.core.XWalkResourceClient; 35 | import org.xwalk.core.XWalkView; 36 | 37 | import java.io.FileNotFoundException; 38 | import java.io.IOException; 39 | 40 | public class XWalkCordovaResourceClient extends XWalkResourceClient { 41 | 42 | private static final String TAG = "XWalkCordovaResourceClient"; 43 | protected XWalkWebViewEngine parentEngine; 44 | 45 | public XWalkCordovaResourceClient(XWalkWebViewEngine parentEngine) { 46 | super(parentEngine.webView); 47 | this.parentEngine = parentEngine; 48 | } 49 | 50 | /** 51 | * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). 52 | * The errorCode parameter corresponds to one of the ERROR_* constants. 53 | * 54 | * @param view The WebView that is initiating the callback. 55 | * @param errorCode The error code corresponding to an ERROR_* value. 56 | * @param description A String describing the error. 57 | * @param failingUrl The url that failed to load. 58 | */ 59 | @Override 60 | public void onReceivedLoadError(XWalkView view, int errorCode, String description, 61 | String failingUrl) { 62 | LOG.d(TAG, "CordovaWebViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl); 63 | 64 | parentEngine.client.onReceivedError(errorCode, description, failingUrl); 65 | } 66 | 67 | @Override 68 | public WebResourceResponse shouldInterceptLoadRequest(XWalkView view, String url) { 69 | try { 70 | // Check the against the white-list. 71 | if (!parentEngine.pluginManager.shouldAllowRequest(url)) { 72 | LOG.w(TAG, "URL blocked by whitelist: " + url); 73 | // Results in a 404. 74 | return new WebResourceResponse("text/plain", "UTF-8", null); 75 | } 76 | 77 | CordovaResourceApi resourceApi = parentEngine.resourceApi; 78 | Uri origUri = Uri.parse(url); 79 | // Allow plugins to intercept WebView requests. 80 | Uri remappedUri = resourceApi.remapUri(origUri); 81 | 82 | if (!origUri.equals(remappedUri)) { 83 | OpenForReadResult result = resourceApi.openForRead(remappedUri, true); 84 | return new WebResourceResponse(result.mimeType, "UTF-8", result.inputStream); 85 | } 86 | // If we don't need to special-case the request, let the browser load it. 87 | return null; 88 | } catch (IOException e) { 89 | if (!(e instanceof FileNotFoundException)) { 90 | LOG.e(TAG, "Error occurred while loading a file (returning a 404).", e); 91 | } 92 | // Results in a 404. 93 | return new WebResourceResponse("text/plain", "UTF-8", null); 94 | } 95 | } 96 | 97 | @Override 98 | public boolean shouldOverrideUrlLoading(XWalkView view, String url) { 99 | return parentEngine.client.onNavigationAttempt(url); 100 | } 101 | 102 | 103 | /** 104 | * Notify the host application that an SSL error occurred while loading a 105 | * resource. The host application must call either callback.onReceiveValue(true) 106 | * or callback.onReceiveValue(false). Note that the decision may be 107 | * retained for use in response to future SSL errors. The default behavior 108 | * is to pop up a dialog. 109 | */ 110 | @Override 111 | public void onReceivedSslError(XWalkView view, ValueCallback callback, SslError error) { 112 | final String packageName = parentEngine.cordova.getActivity().getPackageName(); 113 | final PackageManager pm = parentEngine.cordova.getActivity().getPackageManager(); 114 | 115 | ApplicationInfo appInfo; 116 | try { 117 | appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); 118 | if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { 119 | // debug = true 120 | callback.onReceiveValue(true); 121 | } else { 122 | // debug = false 123 | callback.onReceiveValue(false); 124 | } 125 | } catch (PackageManager.NameNotFoundException e) { 126 | // When it doubt, lock it out! 127 | callback.onReceiveValue(false); 128 | } 129 | } 130 | 131 | @Override 132 | public void onReceivedHttpAuthRequest(XWalkView view, XWalkHttpAuthHandler handler, 133 | String host, String realm) { 134 | // Check if there is some plugin which can resolve this auth challenge 135 | PluginManager pluginManager = parentEngine.pluginManager; 136 | if (pluginManager != null && pluginManager.onReceivedHttpAuthRequest( 137 | parentEngine.parentWebView, 138 | new XWalkCordovaHttpAuthHandler(handler), host, realm)) { 139 | parentEngine.client.clearLoadTimeoutTimer(); 140 | return; 141 | } 142 | 143 | // By default handle 401 like we'd normally do! 144 | super.onReceivedHttpAuthRequest(view, handler, host, realm); 145 | } 146 | 147 | @Override 148 | public void onReceivedClientCertRequest(XWalkView view, ClientCertRequest request) { 149 | // Check if there is some plugin which can resolve this certificate request 150 | PluginManager pluginManager = parentEngine.pluginManager; 151 | if (pluginManager != null && pluginManager.onReceivedClientCertRequest( 152 | parentEngine.parentWebView, new XWalkCordovaClientCertRequest(request))) { 153 | parentEngine.client.clearLoadTimeoutTimer(); 154 | return; 155 | } 156 | 157 | super.onReceivedClientCertRequest(view, request); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /platforms/android/src/org/crosswalk/engine/XWalkCordovaUiClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | package org.crosswalk.engine; 20 | 21 | import android.app.Activity; 22 | import android.content.ActivityNotFoundException; 23 | import android.content.Intent; 24 | import android.net.Uri; 25 | import android.util.Log; 26 | import android.webkit.ValueCallback; 27 | 28 | import org.apache.cordova.CordovaDialogsHelper; 29 | import org.apache.cordova.CordovaPlugin; 30 | import org.apache.cordova.LOG; 31 | import org.xwalk.core.XWalkJavascriptResult; 32 | import org.xwalk.core.XWalkUIClient; 33 | import org.xwalk.core.XWalkView; 34 | 35 | import org.crosswalk.engine.XWalkWebViewEngine.PermissionRequestListener; 36 | 37 | public class XWalkCordovaUiClient extends XWalkUIClient { 38 | private static final String TAG = "XWalkCordovaUiClient"; 39 | protected final CordovaDialogsHelper dialogsHelper; 40 | protected final XWalkWebViewEngine parentEngine; 41 | 42 | private XWalkFileChooser mFileChooser; 43 | private CordovaPlugin mFileChooserResultPlugin; 44 | 45 | private static final int FILECHOOSER_RESULTCODE = 5173; 46 | 47 | public XWalkCordovaUiClient(XWalkWebViewEngine parentEngine) { 48 | super(parentEngine.webView); 49 | this.parentEngine = parentEngine; 50 | dialogsHelper = new CordovaDialogsHelper(parentEngine.webView.getContext()); 51 | } 52 | 53 | @Override 54 | public boolean onJavascriptModalDialog(XWalkView view, JavascriptMessageType type, String url, 55 | String message, String defaultValue, XWalkJavascriptResult result) { 56 | switch (type) { 57 | case JAVASCRIPT_ALERT: 58 | return onJsAlert(view, url, message, result); 59 | case JAVASCRIPT_CONFIRM: 60 | return onJsConfirm(view, url, message, result); 61 | case JAVASCRIPT_PROMPT: 62 | return onJsPrompt(view, url, message, defaultValue, result); 63 | case JAVASCRIPT_BEFOREUNLOAD: 64 | // Reuse onJsConfirm to show the dialog. 65 | return onJsConfirm(view, url, message, result); 66 | default: 67 | break; 68 | } 69 | assert (false); 70 | return false; 71 | } 72 | 73 | /** 74 | * Tell the client to display a javascript alert dialog. 75 | */ 76 | public boolean onJsAlert(XWalkView view, String url, String message, 77 | final XWalkJavascriptResult result) { 78 | dialogsHelper.showAlert(message, new CordovaDialogsHelper.Result() { 79 | @Override 80 | public void gotResult(boolean success, String value) { 81 | if (success) { 82 | result.confirm(); 83 | } else { 84 | result.cancel(); 85 | } 86 | } 87 | }); 88 | return true; 89 | } 90 | 91 | /** 92 | * Tell the client to display a confirm dialog to the user. 93 | */ 94 | public boolean onJsConfirm(XWalkView view, String url, String message, 95 | final XWalkJavascriptResult result) { 96 | dialogsHelper.showConfirm(message, new CordovaDialogsHelper.Result() { 97 | @Override 98 | public void gotResult(boolean success, String value) { 99 | if (success) { 100 | result.confirm(); 101 | } else { 102 | result.cancel(); 103 | } 104 | } 105 | }); 106 | return true; 107 | } 108 | 109 | /** 110 | * Tell the client to display a prompt dialog to the user. 111 | * If the client returns true, WebView will assume that the client will 112 | * handle the prompt dialog and call the appropriate JsPromptResult method. 113 | *

114 | * Since we are hacking prompts for our own purposes, we should not be using them for 115 | * this purpose, perhaps we should hack console.log to do this instead! 116 | */ 117 | public boolean onJsPrompt(XWalkView view, String origin, String message, String defaultValue, 118 | final XWalkJavascriptResult result) { 119 | // Unlike the @JavascriptInterface bridge, this method is always called on the UI thread. 120 | String handledRet = parentEngine.bridge.promptOnJsPrompt(origin, message, defaultValue); 121 | if (handledRet != null) { 122 | result.confirmWithResult(handledRet); 123 | } else { 124 | dialogsHelper.showPrompt(message, defaultValue, new CordovaDialogsHelper.Result() { 125 | @Override 126 | public void gotResult(boolean success, String value) { 127 | if (success) { 128 | result.confirmWithResult(value); 129 | } else { 130 | result.cancel(); 131 | } 132 | } 133 | }); 134 | 135 | } 136 | return true; 137 | } 138 | 139 | /** 140 | * Notify the host application that a page has started loading. 141 | * This method is called once for each main frame load so a page with iframes or framesets will call onPageLoadStarted 142 | * one time for the main frame. This also means that onPageLoadStarted will not be called when the contents of an 143 | * embedded frame changes, i.e. clicking a link whose target is an iframe. 144 | * 145 | * @param view The webView initiating the callback. 146 | * @param url The url of the page. 147 | */ 148 | @Override 149 | public void onPageLoadStarted(XWalkView view, String url) { 150 | LOG.d(TAG, "onPageLoadStarted(" + url + ")"); 151 | if (view.getUrl() != null) { 152 | // Flush stale messages. 153 | parentEngine.client.onPageStarted(url); 154 | parentEngine.bridge.reset(); 155 | } 156 | } 157 | 158 | /** 159 | * Notify the host application that a page has stopped loading. 160 | * This method is called only for main frame. When onPageLoadStopped() is called, the rendering picture may not be updated yet. 161 | * 162 | * @param view The webView initiating the callback. 163 | * @param url The url of the page. 164 | * @param status The load status of the webView, can be FINISHED, CANCELLED or FAILED. 165 | */ 166 | @Override 167 | public void onPageLoadStopped(XWalkView view, String url, LoadStatus status) { 168 | LOG.d(TAG, "onPageLoadStopped(" + url + ")"); 169 | if (status == LoadStatus.FINISHED) { 170 | parentEngine.client.onPageFinishedLoading(url); 171 | } else if (status == LoadStatus.FAILED) { 172 | // TODO: Should this call parentEngine.client.onReceivedError()? 173 | // Right now we call this from ResourceClient, but maybe that is just for sub-resources? 174 | } 175 | } 176 | 177 | // File Chooser 178 | @Override 179 | public void openFileChooser(XWalkView view, final ValueCallback uploadFile, 180 | final String acceptType, final String capture) { 181 | if (mFileChooser == null) { 182 | mFileChooser = new XWalkFileChooser(parentEngine.cordova.getActivity()); 183 | mFileChooserResultPlugin = new CordovaPlugin() { 184 | @Override 185 | public void onActivityResult(int requestCode, int resultCode, Intent intent) { 186 | mFileChooser.onActivityResult(requestCode, resultCode, intent); 187 | } 188 | }; 189 | } 190 | 191 | PermissionRequestListener listener = new PermissionRequestListener() { 192 | @Override 193 | public void onRequestPermissionResult(int requestCode, String[] permissions, 194 | int[] grantResults) { 195 | for (int i = 0; i < permissions.length; ++i) { 196 | Log.d(TAG, "permission:" + permissions[i] + " result:" + grantResults[i]); 197 | } 198 | parentEngine.cordova.setActivityResultCallback(mFileChooserResultPlugin); 199 | mFileChooser.showFileChooser(uploadFile, acceptType, capture); 200 | } 201 | }; 202 | 203 | if (!parentEngine.requestPermissionsForFileChooser(listener)) { 204 | parentEngine.cordova.setActivityResultCallback(mFileChooserResultPlugin); 205 | mFileChooser.showFileChooser(uploadFile, acceptType, capture); 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /platforms/android/src/org/crosswalk/engine/XWalkCordovaView.java: -------------------------------------------------------------------------------- 1 | package org.crosswalk.engine; 2 | 3 | import org.apache.cordova.CordovaPreferences; 4 | import org.xwalk.core.XWalkPreferences; 5 | import org.xwalk.core.XWalkResourceClient; 6 | import org.xwalk.core.XWalkUIClient; 7 | import org.xwalk.core.XWalkView; 8 | 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.pm.ApplicationInfo; 12 | import android.content.pm.PackageManager; 13 | import android.os.Bundle; 14 | import android.util.AttributeSet; 15 | import android.util.Log; 16 | import android.view.KeyEvent; 17 | 18 | import org.apache.cordova.CordovaPlugin; 19 | import org.apache.cordova.CordovaWebView; 20 | import org.apache.cordova.CordovaWebViewEngine; 21 | 22 | public class XWalkCordovaView extends XWalkView implements CordovaWebViewEngine.EngineView { 23 | 24 | public static final String TAG = "XWalkCordovaView"; 25 | 26 | protected XWalkCordovaResourceClient resourceClient; 27 | protected XWalkCordovaUiClient uiClient; 28 | protected XWalkWebViewEngine parentEngine; 29 | 30 | private static boolean hasSetStaticPref; 31 | // This needs to run before the super's constructor. 32 | private static Context setGlobalPrefs(Context context, CordovaPreferences preferences) { 33 | if (!hasSetStaticPref) { 34 | hasSetStaticPref = true; 35 | ApplicationInfo ai = null; 36 | try { 37 | ai = context.getPackageManager().getApplicationInfo(context.getApplicationContext().getPackageName(), PackageManager.GET_META_DATA); 38 | } catch (PackageManager.NameNotFoundException e) { 39 | throw new RuntimeException(e); 40 | } 41 | boolean prefAnimatable = preferences == null ? false : preferences.getBoolean("CrosswalkAnimatable", false); 42 | boolean manifestAnimatable = ai.metaData == null ? false : ai.metaData.getBoolean("CrosswalkAnimatable"); 43 | // Selects between a TextureView (obeys framework transforms applied to view) or a SurfaceView (better performance). 44 | XWalkPreferences.setValue(XWalkPreferences.ANIMATABLE_XWALK_VIEW, prefAnimatable || manifestAnimatable); 45 | if ((ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { 46 | XWalkPreferences.setValue(XWalkPreferences.REMOTE_DEBUGGING, true); 47 | } 48 | XWalkPreferences.setValue(XWalkPreferences.JAVASCRIPT_CAN_OPEN_WINDOW, true); 49 | XWalkPreferences.setValue(XWalkPreferences.ALLOW_UNIVERSAL_ACCESS_FROM_FILE, true); 50 | } 51 | return context; 52 | } 53 | 54 | public XWalkCordovaView(Context context, CordovaPreferences preferences) { 55 | super(setGlobalPrefs(context, preferences), (AttributeSet)null); 56 | } 57 | 58 | public XWalkCordovaView(Context context, AttributeSet attrs) { 59 | super(setGlobalPrefs(context, null), attrs); 60 | } 61 | 62 | void init(XWalkWebViewEngine parentEngine) { 63 | this.parentEngine = parentEngine; 64 | if (resourceClient == null) { 65 | setResourceClient(new XWalkCordovaResourceClient(parentEngine)); 66 | } 67 | if (uiClient == null) { 68 | setUIClient(new XWalkCordovaUiClient(parentEngine)); 69 | } 70 | } 71 | 72 | @Override 73 | public void setResourceClient(XWalkResourceClient client) { 74 | // XWalk calls this method from its constructor. 75 | if (client instanceof XWalkCordovaResourceClient) { 76 | this.resourceClient = (XWalkCordovaResourceClient)client; 77 | } 78 | super.setResourceClient(client); 79 | } 80 | 81 | @Override 82 | public void setUIClient(XWalkUIClient client) { 83 | // XWalk calls this method from its constructor. 84 | if (client instanceof XWalkCordovaUiClient) { 85 | this.uiClient = (XWalkCordovaUiClient)client; 86 | } 87 | super.setUIClient(client); 88 | } 89 | 90 | // Call CordovaInterface to start activity for result to make sure 91 | // onActivityResult() callback will be triggered from CordovaActivity correctly. 92 | // Todo(leonhsl) How to handle |options|? 93 | @Override 94 | public void startActivityForResult(Intent intent, int requestCode, Bundle options) { 95 | parentEngine.cordova.startActivityForResult(new CordovaPlugin() { 96 | @Override 97 | public void onActivityResult(int requestCode, int resultCode, Intent intent) { 98 | // Route to XWalkView. 99 | Log.i(TAG, "Route onActivityResult() to XWalkView"); 100 | XWalkCordovaView.this.onActivityResult(requestCode, resultCode, intent); 101 | } 102 | }, intent, requestCode); 103 | } 104 | 105 | @Override 106 | public boolean dispatchKeyEvent(KeyEvent event) { 107 | Boolean ret = parentEngine.client.onDispatchKeyEvent(event); 108 | if (ret != null) { 109 | return ret.booleanValue(); 110 | } 111 | return super.dispatchKeyEvent(event); 112 | } 113 | 114 | @Override 115 | public void pauseTimers() { 116 | // This is called by XWalkViewInternal.onActivityStateChange(). 117 | // We don't want them paused by default though. 118 | } 119 | 120 | public void pauseTimersForReal() { 121 | super.pauseTimers(); 122 | } 123 | 124 | @Override 125 | public CordovaWebView getCordovaWebView() { 126 | return parentEngine == null ? null : parentEngine.getCordovaWebView(); 127 | } 128 | 129 | @Override 130 | public void setBackgroundColor(int color) { 131 | if (parentEngine != null && parentEngine.isXWalkReady()) { 132 | super.setBackgroundColor(color); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /platforms/android/src/org/crosswalk/engine/XWalkExposedJsApi.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | package org.crosswalk.engine; 20 | 21 | import android.os.Looper; 22 | 23 | import org.apache.cordova.CordovaBridge; 24 | import org.apache.cordova.ExposedJsApi; 25 | import org.json.JSONException; 26 | import org.xwalk.core.JavascriptInterface; 27 | 28 | class XWalkExposedJsApi implements ExposedJsApi { 29 | private final CordovaBridge bridge; 30 | 31 | XWalkExposedJsApi(CordovaBridge bridge) { 32 | this.bridge = bridge; 33 | } 34 | 35 | @JavascriptInterface 36 | public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException { 37 | if (Looper.myLooper() == null) { 38 | Looper.prepare(); 39 | } 40 | return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments); 41 | } 42 | 43 | @JavascriptInterface 44 | public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException { 45 | bridge.jsSetNativeToJsBridgeMode(bridgeSecret, value); 46 | } 47 | 48 | @JavascriptInterface 49 | public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException { 50 | return bridge.jsRetrieveJsMessages(bridgeSecret, fromOnlineEvent); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /platforms/android/src/org/crosswalk/engine/XWalkFileChooser.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | 21 | package org.crosswalk.engine; 22 | 23 | import android.app.Activity; 24 | import android.content.Intent; 25 | import android.content.pm.PackageInfo; 26 | import android.content.pm.PackageManager; 27 | import android.content.pm.PackageManager.NameNotFoundException; 28 | import android.Manifest; 29 | import android.net.Uri; 30 | import android.os.Environment; 31 | import android.provider.MediaStore; 32 | import android.util.Log; 33 | import android.webkit.ValueCallback; 34 | 35 | import java.io.File; 36 | import java.io.IOException; 37 | import java.text.SimpleDateFormat; 38 | import java.util.ArrayList; 39 | import java.util.Arrays; 40 | import java.util.Date; 41 | 42 | public class XWalkFileChooser { 43 | private static final String IMAGE_TYPE = "image/"; 44 | private static final String VIDEO_TYPE = "video/"; 45 | private static final String AUDIO_TYPE = "audio/"; 46 | private static final String ALL_IMAGE_TYPES = IMAGE_TYPE + "*"; 47 | private static final String ALL_VIDEO_TYPES = VIDEO_TYPE + "*"; 48 | private static final String ALL_AUDIO_TYPES = AUDIO_TYPE + "*"; 49 | private static final String ANY_TYPES = "*/*"; 50 | private static final String SPLIT_EXPRESSION = ","; 51 | private static final String PATH_PREFIX = "file:"; 52 | private static final String WRITE_EXTERNAL_STORAGE= "android.permission.WRITE_EXTERNAL_STORAGE"; 53 | 54 | public static final int INPUT_FILE_REQUEST_CODE = 1; 55 | 56 | private static final String TAG = "XWalkFileChooser"; 57 | 58 | private Activity mActivity; 59 | private ValueCallback mFilePathCallback; 60 | private String mCameraPhotoPath; 61 | 62 | public XWalkFileChooser(Activity activity) { 63 | mActivity = activity; 64 | } 65 | 66 | public boolean showFileChooser(ValueCallback uploadFile, String acceptType, 67 | String capture) { 68 | mFilePathCallback = uploadFile; 69 | 70 | Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 71 | if (takePictureIntent.resolveActivity(mActivity.getPackageManager()) != null) { 72 | // Create the File where the photo should go 73 | File photoFile = createImageFile(); 74 | // Continue only if the File was successfully created 75 | if (photoFile != null) { 76 | mCameraPhotoPath = PATH_PREFIX + photoFile.getAbsolutePath(); 77 | takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath); 78 | takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile)); 79 | } else { 80 | takePictureIntent = null; 81 | } 82 | } 83 | 84 | Intent camcorder = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); 85 | Intent soundRecorder = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); 86 | Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT); 87 | contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE); 88 | ArrayList extraIntents = new ArrayList(); 89 | 90 | // A single mime type. 91 | if (!(acceptType.contains(SPLIT_EXPRESSION) || acceptType.contains(ANY_TYPES))) { 92 | if (capture.equals("true")) { 93 | if (acceptType.startsWith(IMAGE_TYPE)) { 94 | if (takePictureIntent != null) { 95 | mActivity.startActivityForResult(takePictureIntent, INPUT_FILE_REQUEST_CODE); 96 | Log.d(TAG, "Started taking picture"); 97 | return true; 98 | } 99 | } else if (acceptType.startsWith(VIDEO_TYPE)) { 100 | mActivity.startActivityForResult(camcorder, INPUT_FILE_REQUEST_CODE); 101 | Log.d(TAG, "Started camcorder"); 102 | return true; 103 | } else if (acceptType.startsWith(AUDIO_TYPE)) { 104 | mActivity.startActivityForResult(soundRecorder, INPUT_FILE_REQUEST_CODE); 105 | Log.d(TAG, "Started sound recorder"); 106 | return true; 107 | } 108 | } else { 109 | if (acceptType.startsWith(IMAGE_TYPE)) { 110 | if (takePictureIntent != null) { 111 | extraIntents.add(takePictureIntent); 112 | } 113 | contentSelectionIntent.setType(ALL_IMAGE_TYPES); 114 | } else if (acceptType.startsWith(VIDEO_TYPE)) { 115 | extraIntents.add(camcorder); 116 | contentSelectionIntent.setType(ALL_VIDEO_TYPES); 117 | } else if (acceptType.startsWith(AUDIO_TYPE)) { 118 | extraIntents.add(soundRecorder); 119 | contentSelectionIntent.setType(ALL_AUDIO_TYPES); 120 | } 121 | } 122 | } 123 | 124 | // Couldn't resolve an accept type. 125 | if (extraIntents.isEmpty() && canWriteExternalStorage()) { 126 | if (takePictureIntent != null) { 127 | extraIntents.add(takePictureIntent); 128 | } 129 | extraIntents.add(camcorder); 130 | extraIntents.add(soundRecorder); 131 | contentSelectionIntent.setType(ANY_TYPES); 132 | } 133 | 134 | Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); 135 | chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent); 136 | if (!extraIntents.isEmpty()) { 137 | chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, 138 | extraIntents.toArray(new Intent[] { })); 139 | } 140 | mActivity.startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE); 141 | Log.d(TAG, "Started chooser"); 142 | return true; 143 | } 144 | 145 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 146 | if(requestCode == INPUT_FILE_REQUEST_CODE && mFilePathCallback != null) { 147 | Log.d(TAG, "Activity result: " + resultCode); 148 | Uri results = null; 149 | 150 | // Check that the response is a good one 151 | if(Activity.RESULT_OK == resultCode) { 152 | // In Android M, camera results return an empty Intent rather than null. 153 | if(data == null || (data.getAction() == null && data.getData() == null)) { 154 | // If there is not data, then we may have taken a photo 155 | if(mCameraPhotoPath != null) { 156 | results = Uri.parse(mCameraPhotoPath); 157 | } 158 | } else { 159 | String dataString = data.getDataString(); 160 | if (dataString != null) { 161 | results = Uri.parse(dataString); 162 | } 163 | deleteImageFile(); 164 | } 165 | } else if (Activity.RESULT_CANCELED == resultCode) { 166 | deleteImageFile(); 167 | } 168 | 169 | if (results != null) { 170 | Log.d(TAG, "Received file: " + results.toString()); 171 | } 172 | mFilePathCallback.onReceiveValue(results); 173 | mFilePathCallback = null; 174 | } 175 | } 176 | 177 | private boolean canWriteExternalStorage() { 178 | try { 179 | PackageManager packageManager = mActivity.getPackageManager(); 180 | PackageInfo packageInfo = packageManager.getPackageInfo( 181 | mActivity.getPackageName(), PackageManager.GET_PERMISSIONS); 182 | return Arrays.asList(packageInfo.requestedPermissions).contains(WRITE_EXTERNAL_STORAGE); 183 | } catch (NameNotFoundException e) { 184 | return false; 185 | } catch (NullPointerException e) { 186 | return false; 187 | } 188 | } 189 | 190 | private File createImageFile() { 191 | // FIXME: If the external storage state is not "MEDIA_MOUNTED", we need to get 192 | // other volume paths by "getVolumePaths()" when it was exposed. 193 | String state = Environment.getExternalStorageState(); 194 | if (!state.equals(Environment.MEDIA_MOUNTED)) { 195 | Log.e(TAG, "External storage is not mounted."); 196 | return null; 197 | } 198 | 199 | // Create an image file name 200 | String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 201 | String imageFileName = "JPEG_" + timeStamp + "_"; 202 | File storageDir = Environment.getExternalStoragePublicDirectory( 203 | Environment.DIRECTORY_PICTURES); 204 | if (!storageDir.exists()) { 205 | storageDir.mkdirs(); 206 | } 207 | 208 | try { 209 | File file = File.createTempFile(imageFileName, ".jpg", storageDir); 210 | Log.d(TAG, "Created image file: " + file.getAbsolutePath()); 211 | return file; 212 | } catch (IOException e) { 213 | // Error occurred while creating the File 214 | Log.e(TAG, "Unable to create Image File, " + 215 | "please make sure permission 'WRITE_EXTERNAL_STORAGE' was added."); 216 | return null; 217 | } 218 | } 219 | 220 | private boolean deleteImageFile() { 221 | if (mCameraPhotoPath == null || !mCameraPhotoPath.contains(PATH_PREFIX)) { 222 | return false; 223 | } 224 | String filePath = mCameraPhotoPath.split(PATH_PREFIX)[1]; 225 | boolean result = new File(filePath).delete(); 226 | Log.d(TAG, "Delete image file: " + filePath + " result: " + result); 227 | return result; 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /platforms/android/src/org/crosswalk/engine/XWalkWebViewEngine.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | package org.crosswalk.engine; 21 | 22 | import android.app.Activity; 23 | import android.content.Context; 24 | import android.content.Intent; 25 | import android.content.pm.PackageInfo; 26 | import android.content.pm.PackageManager; 27 | import android.content.pm.PackageManager.NameNotFoundException; 28 | import android.content.res.AssetManager; 29 | import android.graphics.Bitmap; 30 | import android.graphics.Color; 31 | import android.Manifest; 32 | import android.util.Log; 33 | import android.view.View; 34 | import android.webkit.ValueCallback; 35 | 36 | import java.io.File; 37 | import java.io.IOException; 38 | import java.util.ArrayList; 39 | 40 | import org.apache.cordova.CordovaBridge; 41 | import org.apache.cordova.CordovaInterface; 42 | import org.apache.cordova.CordovaPlugin; 43 | import org.apache.cordova.CordovaPreferences; 44 | import org.apache.cordova.CordovaResourceApi; 45 | import org.apache.cordova.CordovaWebView; 46 | import org.apache.cordova.CordovaWebViewEngine; 47 | import org.apache.cordova.ICordovaCookieManager; 48 | import org.apache.cordova.NativeToJsMessageQueue; 49 | import org.apache.cordova.PluginEntry; 50 | import org.apache.cordova.PluginManager; 51 | import org.xwalk.core.XWalkActivityDelegate; 52 | import org.xwalk.core.XWalkNavigationHistory; 53 | import org.xwalk.core.XWalkView; 54 | import org.xwalk.core.XWalkGetBitmapCallback; 55 | 56 | /** 57 | * Glue class between CordovaWebView (main Cordova logic) and XWalkCordovaView (the actual View). 58 | */ 59 | public class XWalkWebViewEngine implements CordovaWebViewEngine { 60 | 61 | public static final String TAG = "XWalkWebViewEngine"; 62 | public static final String XWALK_USER_AGENT = "xwalkUserAgent"; 63 | public static final String XWALK_Z_ORDER_ON_TOP = "xwalkZOrderOnTop"; 64 | 65 | private static final String XWALK_EXTENSIONS_FOLDER = "xwalk-extensions"; 66 | 67 | private static final int PERMISSION_REQUEST_CODE = 100; 68 | 69 | protected final XWalkCordovaView webView; 70 | protected XWalkCordovaCookieManager cookieManager; 71 | protected CordovaBridge bridge; 72 | protected CordovaWebViewEngine.Client client; 73 | protected CordovaWebView parentWebView; 74 | protected CordovaInterface cordova; 75 | protected PluginManager pluginManager; 76 | protected CordovaResourceApi resourceApi; 77 | protected NativeToJsMessageQueue nativeToJsMessageQueue; 78 | protected XWalkActivityDelegate activityDelegate; 79 | protected String startUrl; 80 | protected CordovaPreferences preferences; 81 | 82 | /** Used when created via reflection. */ 83 | public XWalkWebViewEngine(Context context, CordovaPreferences preferences) { 84 | this.preferences = preferences; 85 | Runnable cancelCommand = new Runnable() { 86 | @Override 87 | public void run() { 88 | cordova.getActivity().finish(); 89 | } 90 | }; 91 | Runnable completeCommand = new Runnable() { 92 | @Override 93 | public void run() { 94 | cookieManager = new XWalkCordovaCookieManager(); 95 | 96 | initWebViewSettings(); 97 | exposeJsInterface(webView, bridge); 98 | loadExtensions(); 99 | 100 | CordovaPlugin notifPlugin = new CordovaPlugin() { 101 | @Override 102 | public void onNewIntent(Intent intent) { 103 | Log.i(TAG, "notifPlugin route onNewIntent() to XWalkView: " + intent.toString()); 104 | XWalkWebViewEngine.this.webView.onNewIntent(intent); 105 | } 106 | 107 | @Override 108 | public Object onMessage(String id, Object data) { 109 | if (id.equals("captureXWalkBitmap")) { 110 | // Capture bitmap on UI thread. 111 | XWalkWebViewEngine.this.cordova.getActivity().runOnUiThread(new Runnable() { 112 | public void run() { 113 | XWalkWebViewEngine.this.webView.captureBitmapAsync( 114 | new XWalkGetBitmapCallback() { 115 | @Override 116 | public void onFinishGetBitmap(Bitmap bitmap, 117 | int response) { 118 | pluginManager.postMessage( 119 | "onGotXWalkBitmap", bitmap); 120 | } 121 | }); 122 | } 123 | }); 124 | } 125 | return null; 126 | } 127 | }; 128 | pluginManager.addService(new PluginEntry("XWalkNotif", notifPlugin)); 129 | 130 | // Send the massage of xwalk's ready to plugin. 131 | if (pluginManager != null) { 132 | pluginManager.postMessage("onXWalkReady", this); 133 | } 134 | 135 | if (startUrl != null) { 136 | webView.load(startUrl, null); 137 | } 138 | } 139 | }; 140 | activityDelegate = new XWalkActivityDelegate((Activity) context, cancelCommand, completeCommand); 141 | 142 | webView = new XWalkCordovaView(context, preferences); 143 | } 144 | 145 | // Use two-phase init so that the control will work with XML layouts. 146 | 147 | @Override 148 | public void init(CordovaWebView parentWebView, CordovaInterface cordova, CordovaWebViewEngine.Client client, 149 | CordovaResourceApi resourceApi, PluginManager pluginManager, 150 | NativeToJsMessageQueue nativeToJsMessageQueue) { 151 | if (this.cordova != null) { 152 | throw new IllegalStateException(); 153 | } 154 | this.parentWebView = parentWebView; 155 | this.cordova = cordova; 156 | this.client = client; 157 | this.resourceApi = resourceApi; 158 | this.pluginManager = pluginManager; 159 | this.nativeToJsMessageQueue = nativeToJsMessageQueue; 160 | 161 | CordovaPlugin activityDelegatePlugin = new CordovaPlugin() { 162 | @Override 163 | public void onResume(boolean multitasking) { 164 | activityDelegate.onResume(); 165 | } 166 | }; 167 | pluginManager.addService(new PluginEntry("XWalkActivityDelegate", activityDelegatePlugin)); 168 | 169 | webView.init(this); 170 | 171 | nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode( 172 | new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() { 173 | @Override 174 | public void setNetworkAvailable(boolean value) { 175 | webView.setNetworkAvailable(value); 176 | } 177 | @Override 178 | public void runOnUiThread(Runnable r) { 179 | XWalkWebViewEngine.this.cordova.getActivity().runOnUiThread(r); 180 | } 181 | })); 182 | nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.EvalBridgeMode(this, cordova)); 183 | bridge = new CordovaBridge(pluginManager, nativeToJsMessageQueue); 184 | } 185 | 186 | @Override 187 | public CordovaWebView getCordovaWebView() { 188 | return parentWebView; 189 | } 190 | 191 | @Override 192 | public View getView() { 193 | return webView; 194 | } 195 | 196 | private void initWebViewSettings() { 197 | webView.setVerticalScrollBarEnabled(false); 198 | 199 | boolean zOrderOnTop = preferences == null ? false : preferences.getBoolean(XWALK_Z_ORDER_ON_TOP, false); 200 | webView.setZOrderOnTop(zOrderOnTop); 201 | 202 | // Set xwalk webview settings by Cordova preferences. 203 | String xwalkUserAgent = preferences == null ? "" : preferences.getString(XWALK_USER_AGENT, ""); 204 | if (!xwalkUserAgent.isEmpty()) { 205 | webView.setUserAgentString(xwalkUserAgent); 206 | } 207 | 208 | String appendUserAgent = preferences.getString("AppendUserAgent", ""); 209 | if (!appendUserAgent.isEmpty()) { 210 | webView.setUserAgentString(webView.getUserAgentString() + " " + appendUserAgent); 211 | } 212 | 213 | if (preferences.contains("BackgroundColor")) { 214 | int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK); 215 | webView.setBackgroundColor(backgroundColor); 216 | } 217 | } 218 | 219 | private static void exposeJsInterface(XWalkView webView, CordovaBridge bridge) { 220 | XWalkExposedJsApi exposedJsApi = new XWalkExposedJsApi(bridge); 221 | webView.addJavascriptInterface(exposedJsApi, "_cordovaNative"); 222 | } 223 | 224 | private void loadExtensions() { 225 | AssetManager assetManager = cordova.getActivity().getAssets(); 226 | String[] extList; 227 | try { 228 | Log.i(TAG, "Iterate assets/xwalk-extensions folder"); 229 | extList = assetManager.list(XWALK_EXTENSIONS_FOLDER); 230 | } catch (IOException e) { 231 | Log.w(TAG, "Failed to iterate assets/xwalk-extensions folder"); 232 | return; 233 | } 234 | 235 | for (String path : extList) { 236 | // Load the extension. 237 | Log.i(TAG, "Start to load extension: " + path); 238 | webView.getExtensionManager().loadExtension(XWALK_EXTENSIONS_FOLDER + File.separator + path); 239 | } 240 | } 241 | 242 | @Override 243 | public boolean canGoBack() { 244 | if (!activityDelegate.isXWalkReady()) return false; 245 | return this.webView.getNavigationHistory().canGoBack(); 246 | } 247 | 248 | @Override 249 | public boolean goBack() { 250 | if (this.webView.getNavigationHistory().canGoBack()) { 251 | this.webView.getNavigationHistory().navigate(XWalkNavigationHistory.Direction.BACKWARD, 1); 252 | return true; 253 | } 254 | return false; 255 | } 256 | 257 | @Override 258 | public void setPaused(boolean value) { 259 | if (!activityDelegate.isXWalkReady()) return; 260 | if (value) { 261 | // TODO: I think this has been fixed upstream and we don't need to override pauseTimers() anymore. 262 | webView.pauseTimersForReal(); 263 | } else { 264 | webView.resumeTimers(); 265 | } 266 | } 267 | 268 | @Override 269 | public void destroy() { 270 | if (!activityDelegate.isXWalkReady()) return; 271 | webView.onDestroy(); 272 | } 273 | 274 | @Override 275 | public void clearHistory() { 276 | if (!activityDelegate.isXWalkReady()) return; 277 | this.webView.getNavigationHistory().clear(); 278 | } 279 | 280 | @Override 281 | public void stopLoading() { 282 | if (!activityDelegate.isXWalkReady()) return; 283 | this.webView.stopLoading(); 284 | } 285 | 286 | @Override 287 | public void clearCache() { 288 | if (!activityDelegate.isXWalkReady()) return; 289 | webView.clearCache(true); 290 | } 291 | 292 | @Override 293 | public String getUrl() { 294 | if (!activityDelegate.isXWalkReady()) return null; 295 | return this.webView.getUrl(); 296 | } 297 | 298 | @Override 299 | public ICordovaCookieManager getCookieManager() { 300 | return cookieManager; 301 | } 302 | 303 | @Override 304 | public void loadUrl(String url, boolean clearNavigationStack) { 305 | if (!activityDelegate.isXWalkReady()) { 306 | startUrl = url; 307 | return; 308 | } 309 | webView.load(url, null); 310 | } 311 | 312 | /** 313 | * This API is used in Cordova-Android 6.0.0 override from 314 | * 315 | * CordovaWebViewEngine.java 316 | * @since Cordova 6.0 317 | */ 318 | public void evaluateJavascript(String js, ValueCallback callback) { 319 | webView.evaluateJavascript(js, callback); 320 | } 321 | 322 | public boolean isXWalkReady() { 323 | return activityDelegate.isXWalkReady(); 324 | } 325 | 326 | public interface PermissionRequestListener { 327 | public void onRequestPermissionResult(int requestCode, String[] permissions, 328 | int[] grantResults); 329 | } 330 | 331 | public boolean requestPermissionsForFileChooser(final PermissionRequestListener listener) { 332 | ArrayList dangerous_permissions = new ArrayList(); 333 | try { 334 | PackageManager packageManager = cordova.getActivity().getPackageManager(); 335 | PackageInfo packageInfo = packageManager.getPackageInfo( 336 | cordova.getActivity().getPackageName(), PackageManager.GET_PERMISSIONS); 337 | for (String permission : packageInfo.requestedPermissions) { 338 | if (permission.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) 339 | || permission.equals(Manifest.permission.CAMERA)) { 340 | dangerous_permissions.add(permission); 341 | } 342 | } 343 | } catch (NameNotFoundException e) { 344 | } 345 | 346 | if (dangerous_permissions.isEmpty()) { 347 | return false; 348 | } 349 | 350 | CordovaPlugin permissionRequestPlugin = new CordovaPlugin() { 351 | @Override 352 | public void onRequestPermissionResult(int requestCode, String[] permissions, 353 | int[] grantResults) { 354 | if (requestCode != PERMISSION_REQUEST_CODE) return; 355 | listener.onRequestPermissionResult(requestCode, permissions, grantResults); 356 | } 357 | }; 358 | try { 359 | cordova.requestPermissions(permissionRequestPlugin, PERMISSION_REQUEST_CODE, 360 | dangerous_permissions.toArray(new String[dangerous_permissions.size()])); 361 | } catch (NoSuchMethodError e) { 362 | return false; 363 | } 364 | return true; 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /platforms/android/xwalk.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | def EMBEDDED_MODE = "embedded" 21 | def SHARED_MODE = "shared" 22 | def LITE_MODE = "lite" 23 | def DEFAULT_GROUP_ID = "org.xwalk:" 24 | def SHARED_ARTIFACT_ID = "xwalk_shared_library:" 25 | def EMBEDD_ARTIFACT_ID = "xwalk_core_library:" 26 | def CANARY_ARTIFACT_ID = "xwalk_core_library_canary:" 27 | def BIT_64 = ":64bit@aar" 28 | def DEFAULT_MIN_SDK_VERSION = 14 29 | 30 | def getConfigPreference(name) { 31 | name = name.toLowerCase() 32 | def xml = file("res/xml/config.xml").getText() 33 | // Disable namespace awareness since Cordova doesn't use them properly 34 | def root = new XmlParser(false, false).parseText(xml) 35 | 36 | def ret, defaultValue 37 | root.preference.each { it -> 38 | def attrName = it.attribute("name") 39 | if (attrName && attrName.toLowerCase() == name) { 40 | if (it.attribute('default') != null) { 41 | defaultValue = it.attribute('default'); 42 | } else { 43 | ret = it.attribute("value") 44 | } 45 | } 46 | } 47 | return ret ? ret : defaultValue 48 | } 49 | 50 | if (!project.hasProperty('xwalk64bit')) { 51 | ext.xwalk64bit = getConfigPreference("xwalk64bit"); 52 | println xwalk64bit 53 | } 54 | if (cdvBuildMultipleApks == null) { 55 | ext.xwalkMultipleApk = getConfigPreference("xwalkMultipleApk").toBoolean(); 56 | } else { 57 | ext.xwalkMultipleApk = cdvBuildMultipleApks.toBoolean(); 58 | } 59 | 60 | def minSdk = getConfigPreference("android-minSdkVersion"); 61 | if (cdvMinSdkVersion == null) { 62 | ext.cdvMinSdkVersion = minSdk && Integer.parseInt(minSdk) > DEFAULT_MIN_SDK_VERSION ? minSdk : DEFAULT_MIN_SDK_VERSION; 63 | } else if (Integer.parseInt('' + cdvMinSdkVersion) < Integer.parseInt(minSdk)) { 64 | ext.cdvMinSdkVersion = minSdk; 65 | } 66 | 67 | if (!project.hasProperty('xwalkMode')) { 68 | ext.xwalkMode = getConfigPreference("xwalkMode"); 69 | } 70 | 71 | 72 | if (ext.xwalkMode == SHARED_MODE) { 73 | // Build one apk at shared mode because the value of 74 | // ext.cdvBuildMultipleApks is false by default. 75 | xwalk64bit = null; 76 | } else if (xwalk64bit == null) { 77 | // Build embedded 32 bit crosswalk will generate two apks by default. 78 | ext.cdvBuildMultipleApks = xwalkMultipleApk; 79 | } 80 | 81 | // Set defaults before project's build-extras.gradle 82 | if (!project.hasProperty('xwalkVersion')) { 83 | ext.xwalkVersion = getConfigPreference("xwalkVersion") 84 | } 85 | 86 | // Set defaults before project's build-extras.gradle 87 | if (!project.hasProperty('xwalkLiteVersion')) { 88 | ext.xwalkLiteVersion = getConfigPreference("xwalkLiteVersion") 89 | } 90 | 91 | if (!project.hasProperty('xwalkCommandLine')) { 92 | ext.xwalkCommandLine = getConfigPreference("xwalkCommandLine") 93 | } 94 | // Apply values after project's build-extras.gradle 95 | cdvPluginPostBuildExtras.add({ 96 | def xwalkMavenRepo = 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2'; 97 | if (xwalkMode == LITE_MODE) { 98 | xwalkMavenRepo = 'https://download.01.org/crosswalk/releases/crosswalk-lite/android/maven2'; 99 | } 100 | repositories { 101 | maven { 102 | url xwalkMavenRepo 103 | } 104 | } 105 | 106 | android { 107 | if (xwalk64bit != null) { 108 | productFlavors { 109 | x86_64 { 110 | versionCode defaultConfig.versionCode + 6 111 | ndk { 112 | abiFilters "x86_64", "" 113 | } 114 | } 115 | arm64 { 116 | versionCode defaultConfig.versionCode + 9 117 | ndk { 118 | abiFilters "arm64-v8a", "" 119 | } 120 | } 121 | } 122 | } 123 | } 124 | 125 | def xwalkSpec = xwalkVersion 126 | if (ext.xwalkMode == LITE_MODE) { 127 | xwalkSpec = xwalkLiteVersion; 128 | } 129 | 130 | if ((xwalkSpec =~ /:/).count == 1) { 131 | xwalkSpec = DEFAULT_GROUP_ID + xwalkSpec 132 | } else if ((xwalkSpec =~ /:/).count == 0) { 133 | if (xwalkSpec ==~ /\d+/) { 134 | xwalkSpec = "${xwalkSpec}+" 135 | } 136 | 137 | def artifactid = EMBEDD_ARTIFACT_ID; 138 | if (ext.xwalkMode == SHARED_MODE) { 139 | artifactid = SHARED_ARTIFACT_ID; 140 | } else if (ext.xwalkMode == LITE_MODE) { 141 | artifactid = CANARY_ARTIFACT_ID; 142 | } 143 | xwalkSpec = DEFAULT_GROUP_ID + artifactid + xwalkSpec 144 | } 145 | if (xwalk64bit != null) { 146 | xwalkSpec = xwalkSpec + BIT_64 147 | } 148 | println xwalkSpec 149 | 150 | dependencies { 151 | compile xwalkSpec 152 | } 153 | 154 | if (file('assets/xwalk-command-line').exists()) { 155 | println('Not writing assets/xwalk-command-line since file already exists.') 156 | return 157 | } 158 | android.applicationVariants.all { variant -> 159 | def variantName = variant.name.capitalize() 160 | def mergeTask = tasks["merge${variantName}Assets"] 161 | def processTask = tasks["process${variantName}Resources"] 162 | def outFile = new File (mergeTask.outputDir, "xwalk-command-line") 163 | def newTask = project.task("createXwalkCommandLineFile${variantName}") << { 164 | mergeTask.outputDir.mkdirs() 165 | outFile.write("xwalk ${xwalkCommandLine}\n") 166 | } 167 | newTask.dependsOn(mergeTask) 168 | processTask.dependsOn(newTask) 169 | } 170 | }) 171 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | Crosswalk WebView Engine 9 | Changes the default WebView to CrossWalk 10 | Apache 2.0 11 | cordova,chromium,crosswalk,webview 12 | https://github.com/crosswalk-project/cordova-plugin-crosswalk-webview 13 | https://crosswalk-project.org/jira 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | After much discussion and analysis of the market, we have decided to discontinue support for Android 4.0 (ICS) in Crosswalk starting with version 20. 62 | 63 | So the minSdkVersion of Cordova project is configured to 16 by default. 64 | 65 | 66 | --------------------------------------------------------------------------------