mirror of
https://github.com/apache/cordova-android.git
synced 2026-05-30 00:00:04 +08:00
Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fb13ab6de2 | |||
| a272fd1531 | |||
| 90766ae0fa | |||
| fcb6cc44f1 | |||
| ef4383561e | |||
| 025ca36d3a | |||
| c2bba3f5e3 | |||
| 1c3f7e6f93 | |||
| e65bfa41a5 | |||
| e2e38ad2b4 | |||
| caa82b2f26 | |||
| 5652fdc030 | |||
| 7dbc5b0965 | |||
| 60578f3f0d | |||
| afdd16a214 | |||
| 014327c59a | |||
| 0cde8819cf | |||
| 07632b0eeb | |||
| 4a7f825cfe | |||
| 4bc2051f44 | |||
| 34dde53506 | |||
| 7a09182446 | |||
| eb8cf56e8e | |||
| 12a27643db | |||
| c6ccde0558 | |||
| 16e3ebd87b | |||
| 94c096dd5b | |||
| 2e3e4ec3b2 | |||
| 6e222c3938 | |||
| 3b3bd9b6c9 | |||
| 4e3331ba66 | |||
| b6c5a5fc9a | |||
| 94943a9a84 | |||
| 71e72f215d | |||
| 58cdfd86d0 | |||
| dfa66b9dd4 | |||
| d56ea25816 | |||
| c91b272648 | |||
| ca8bb75b40 | |||
| 36eab713a1 | |||
| 7133576fe9 | |||
| effffcba1d | |||
| 404ce8bc3e | |||
| a91bd095b0 | |||
| fd6a1e5ed0 | |||
| 7d6ac87033 | |||
| 8aa813b862 | |||
| 95aa5c9f1c | |||
| 4319447cb5 | |||
| 50ea162251 | |||
| 9c239804d3 | |||
| 320e31bb10 | |||
| 8b55a16986 | |||
| 41125ea1e2 | |||
| 73219bf2d2 | |||
| d6eb723b7f | |||
| 993d73762c | |||
| 48b51c451a | |||
| 3d191d5884 | |||
| 955133f173 | |||
| c2cafb4b45 | |||
| 67f474ef42 | |||
| cd6c0e1de9 | |||
| 92be0033a8 | |||
| b934c1be6a | |||
| 145b50a320 | |||
| a33cdc9c7b | |||
| 62101e85ff | |||
| 0a3714e5e0 | |||
| 86a2830d75 | |||
| 9300e97d2b | |||
| 3792f75281 | |||
| a14c794255 | |||
| aef96e95e8 | |||
| cc860804f6 | |||
| d8a19b5565 | |||
| 1c5b5e2ce6 | |||
| 2f24e42dc1 | |||
| 0c12aa163e | |||
| ec47274fbd | |||
| 04ccb06e3f | |||
| d31ee20ba5 | |||
| 9b25d45b93 | |||
| d51abdd73e | |||
| 9ea8b2237a | |||
| e86c2e5970 | |||
| caeb86843d | |||
| 0f15608175 | |||
| 705991e5b0 | |||
| b636874bd9 | |||
| 965e4e9b19 | |||
| af77977fda | |||
| e74baf188f | |||
| 663a71255f | |||
| 79aa3e159d | |||
| 95118398dd | |||
| 4d18a8e55f | |||
| 3bab41f138 | |||
| f577af0886 | |||
| aab47bd453 | |||
| 445ddd89fb | |||
| 6f21a96238 | |||
| c47bcb2f54 | |||
| b0b628ffc2 | |||
| 4b4a2e9f9e | |||
| 58afd0b604 | |||
| 4352456129 | |||
| bb141a70e8 | |||
| ff260c03ca | |||
| 297f862ccc | |||
| 141bbfb051 | |||
| 663a919ed1 | |||
| 483babe3bc | |||
| b407641049 | |||
| 32e07c22d0 | |||
| d427c52aac | |||
| eb623a84d5 | |||
| 07290277ba | |||
| 743541218f | |||
| 94de0a7ce2 | |||
| 36e9fb292b | |||
| 2661e010d9 | |||
| 7687becfee | |||
| 1641f09dc9 | |||
| 1505673393 | |||
| 629e05b7b1 | |||
| e98f6ae570 | |||
| 0b5bf0c098 | |||
| 1deefa48ee | |||
| 50c4aef873 | |||
| cf42d31214 | |||
| 00caa1c0a0 |
@@ -1,3 +1,24 @@
|
|||||||
|
<!--
|
||||||
|
#
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
-->
|
||||||
|
|
||||||
# Contributing to Apache Cordova
|
# Contributing to Apache Cordova
|
||||||
|
|
||||||
Anyone can contribute to Cordova. And we need your contributions.
|
Anyone can contribute to Cordova. And we need your contributions.
|
||||||
|
|||||||
@@ -199,4 +199,89 @@
|
|||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
|
ADDITIONAL LICENSES:
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
bin/node_modules/q
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to
|
||||||
|
deal in the Software without restriction, including without limitation the
|
||||||
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
bin/node_modules/shelljs
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
Copyright (c) 2012, Artur Adib <aadib@mozilla.com>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
You may use this project under the terms of the New BSD license as follows:
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of Artur Adib nor the
|
||||||
|
names of the contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
bin/node_modules/shelljs
|
||||||
|
================================================================================
|
||||||
|
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|||||||
+175
@@ -20,6 +20,181 @@
|
|||||||
-->
|
-->
|
||||||
## Release Notes for Cordova (Android) ##
|
## Release Notes for Cordova (Android) ##
|
||||||
|
|
||||||
|
### 3.6.4 (Sept 30, 2014) ###
|
||||||
|
|
||||||
|
* Set VERSION to 3.6.4 (via coho)
|
||||||
|
* Update JS snapshot to version 3.6.4 (via coho)
|
||||||
|
* CB-7634 Detect JAVA_HOME properly on Ubuntu
|
||||||
|
* CB-7579 Fix run script's ability to use non-arch-specific APKs
|
||||||
|
* CB-6511 Fixes build for android when app name contains unicode characters.
|
||||||
|
* CB-7463: Adding licences. I don't know what the gradle syntax is for comments, that still needs to be done.
|
||||||
|
* CB-7463: Looked at the Apache BigTop git, gradle uses C-style comments
|
||||||
|
* CB-7460: Fixing bug with KitKat where the background colour would override the CSS colours on the application
|
||||||
|
* CB-7674 Preference activation no longer occurs in CordovaActivity.onCreate()
|
||||||
|
|
||||||
|
### 3.6.0 (Sept 2014) ###
|
||||||
|
|
||||||
|
* Set VERSION to 3.6.0 (via coho)
|
||||||
|
* CB-7410 fix the menu test
|
||||||
|
* CB-7410 Fix the errorUrl test
|
||||||
|
* CB-7410 Fix Basic Authentication test
|
||||||
|
* CB-3445: Allow build and run scripts to select APK by architecture
|
||||||
|
* CB-3445: Add environment variable 'BUILD_MULTIPLE_APKS' for splitting APKs based on architecture
|
||||||
|
* CB-3445: Ensure that JAR files in libs directory are included
|
||||||
|
* CB-7267 update RELEASENOTES for 3.5.1
|
||||||
|
* CB-7410 clarify the title
|
||||||
|
* CB-7385 update cordova.js for testing prior to branch/tag
|
||||||
|
* CB-7410 add whitelist entries to get iframe/GoogleMaps working
|
||||||
|
* CB-7291 propogate change in method signature to the native tests
|
||||||
|
* CB-7291: Restrict meaning of "\*" in internal whitelist to just http and https
|
||||||
|
* CB-7291: Only add file, content and data URLs to internal whitelist
|
||||||
|
* CB-7291: Add defaults to external whitelist
|
||||||
|
* CB-7291: Add external-launch-whitelist and use it for filtering intent launches
|
||||||
|
* CB-3445: Read project.properties to configure gradle libraries
|
||||||
|
* CB-7325 Fix error message in android_sdk_version.js when missing SDK on windows
|
||||||
|
* CB-7335 Add a .gitignore to android project template
|
||||||
|
* CB-7330 Fix dangling function call in last commit (broke gradle builds)
|
||||||
|
* CB-7330 Don't run "android update" during creation
|
||||||
|
* CB-3445 Add gradle support clean command (plus some code cleanup)
|
||||||
|
* CB-7044 Fix typo in prev commit causing check_reqs to always fail.
|
||||||
|
* CB-3445 Copy gradle wrapper in build instead of create
|
||||||
|
* CB-3445 Add .gradle template files for "update" as well as "create"
|
||||||
|
* CB-7044 Add JAVA_HOME when not set. Be stricter about ANDROID_HOME
|
||||||
|
* CB-3445 Speed up gradle building (incremental builds go from 10s -> 1.5s for me)
|
||||||
|
* CB-3445: android: Copy Gradle wrapper from Android SDK rather than bundling a JAR
|
||||||
|
* CB-3445: Add which to checked-in node_modules
|
||||||
|
* CB-3445: Add option to build and install with gradle
|
||||||
|
* CB-3445: Add an initial set of Gradle build scripts
|
||||||
|
* CB-7321 Don't require ant for create script
|
||||||
|
* CB-7044, CB-7299 Fix up PATH problems when possible.
|
||||||
|
* Change in test's AndroidManifest.xml needed for the test to run properly. Forgot the manifest.
|
||||||
|
* Change in test's AndroidManifest.xml needed for the test to run properly
|
||||||
|
* Adding tests related to 3.5.1
|
||||||
|
* CB-7261 Fix setNativeToJsBridgeMode sometimes crashing when switching to ONLINE_EVENT
|
||||||
|
* CB-7265 Fix crash when navigating to custom protocol (introduced in 3.5.1)
|
||||||
|
* Filter out non-launchable intents
|
||||||
|
* Handle unsupported protocol errors in webview better
|
||||||
|
* CB-7238: I should have collapsed this, but Config.init() must go before the creation of CordovaWebView
|
||||||
|
* CB-7238: Minor band-aid to get tests running again, this has to go away before 3.6.0 is released, since this is an API change.
|
||||||
|
* Extend whitelist to handle URLs without // chars
|
||||||
|
* CB-7172 Force window to have focus after resume
|
||||||
|
* CB-7159 Set background color of webView as well as its parent
|
||||||
|
* CB-7018 Fix setButtonPlumbedToJs never un-listening
|
||||||
|
* Undeprecate some just-deprecated symbols in PluginManager.
|
||||||
|
* @Deprecate methods of PluginManager that were never meant to be public
|
||||||
|
* Move plugin instantiation and instance storing logic PluginEntry->PluginManager
|
||||||
|
* Fix broken unit test due to missing Config.init() call
|
||||||
|
* Update to check for Google Glass APIs
|
||||||
|
* Fix for `android` not being in PATH check on Windows
|
||||||
|
* Displaying error when regex does not match.
|
||||||
|
* Fix broken compile due to previous commit :(
|
||||||
|
* Tweak CordovaPlugin.initialize method to be less deprecated.
|
||||||
|
* Un-deprecate CordovaActivity.init() - it's needed to tweak prefs in onCreate
|
||||||
|
* Tweak log messages in CordovaBridge with bridgeSecret is wrong
|
||||||
|
* Backport CordovaBridge from 4.0.x -> master
|
||||||
|
* Update unit tests to not use most deprecated things (e.g. DroidGap)
|
||||||
|
* Add non-String overloades for CordovaPreferences.set()
|
||||||
|
* Make CordovaWebview resilient to init() not being called (for backwards-compatibility)
|
||||||
|
* Add node_module licenses to LICENSE
|
||||||
|
* Update cordova.js snapshot to work with bridge changes
|
||||||
|
* Provide CordovaPlugin with CordovaPreferences. Add new Plugin.initialize()
|
||||||
|
* Convert usages of Config.\* to use the non-static versions
|
||||||
|
* Change getProperty -> prefs.get\* within CordovaActivity
|
||||||
|
* Make CordovaUriHelper class package-private
|
||||||
|
* Fix PluginManager.setPluginEntries not removing old entries
|
||||||
|
* Move registration of App plugin from config.xml -> code
|
||||||
|
* Make setWebViewClient an override instead of an overload. Delete Location-change JS->Native bridge mode (missed some of it).
|
||||||
|
* CB-4404 Revert setting android:windowSoftInputMode to "adjustPan"
|
||||||
|
* Refactor: Use ConfigXmlParser in activity. Adds CordovaWebView.init()
|
||||||
|
* Deprecate some convenience methods on CordovaActivity
|
||||||
|
* Fix CordovaPreferences not correctly parsing hex values (valueOf->decode)
|
||||||
|
* Refactor: Move url-filter information into PluginEntry.
|
||||||
|
* Don't re-parse config.xml in onResume.
|
||||||
|
* Move handling of Fullscreen preference to CordovaActivity
|
||||||
|
* Delete dead code from CordovaActivity
|
||||||
|
* Update .classpath to make Eclipse happy (just re-orders one line)
|
||||||
|
* Delete "CB-3064: The errorUrl is..." Log message left over from debugging presumably
|
||||||
|
* Refactor Config into ConfigXmlParser, CordovaPreferences
|
||||||
|
* Delete Location-change JS->Native bridge mode
|
||||||
|
* CB-5988 Allow exec() only from file: or start-up URL's domain
|
||||||
|
* CB-6761 Fix native->JS bridge ceasing to fire when page changes and online is set to false and the JS loads quickly
|
||||||
|
* Update the errorurl to no longer use intents
|
||||||
|
* This breaks running the JUnit tests, we'll bring it back soon
|
||||||
|
* Refactoring the URI handling on Cordova, removing dead code
|
||||||
|
* CB-7018 Clean up and deprecation of some button-related functions
|
||||||
|
* CB-7017 Fix onload=true being set on all subsequent plugins
|
||||||
|
* CB-5971: Fix package / project validation
|
||||||
|
* CB-5971: Add unit tests to cordova-android
|
||||||
|
* CB-5971: Factor out package/project name validation logic
|
||||||
|
* Delete explicit activity.finish() in back button handling. No change in behaviour.
|
||||||
|
* CB-5971: This would have been a good first bug, too bad
|
||||||
|
* CB-4404: Changing where android:windowSoftInputMode is in the manifest so it works
|
||||||
|
* Add documentation referencing other implementation.
|
||||||
|
* CB-6851 Deprecate WebView.sendJavascript()
|
||||||
|
* CB-6876 Show the correct executable name
|
||||||
|
* CB-6876 Fix the "print usage"
|
||||||
|
* Trivial spelling fix in comments when reading CordovaResourceApi
|
||||||
|
* CB-6818: I want to remove this code, because Square didn't do their headers properly
|
||||||
|
* CB-6860 Add activity_name and launcher_name to AndroidManifest.xml & strings.xml
|
||||||
|
* Add a comment to custom_rules.xml saying why we move AndroidManifest.xml
|
||||||
|
* Remove +x from README.md
|
||||||
|
* CB-6784 Add missing licenses
|
||||||
|
* CB-6784 Add license to CONTRIBUTING.md
|
||||||
|
* Revert "defaults.xml: Add AndroidLaunchMode preference"
|
||||||
|
* updated RELEASENOTES
|
||||||
|
* CB-6315: Wrapping this so it runs on the UI thread
|
||||||
|
* CB-6723 Update package name for Robotium
|
||||||
|
* CB-6707 Update minSdkVersion to 10 consistently
|
||||||
|
* CB-5652 make visible cordova version
|
||||||
|
* Update JS snapshot to version 3.6.0-dev (via coho)
|
||||||
|
* Update JS snapshot to version 3.6.0-dev (via coho)
|
||||||
|
* Set VERSION to 3.6.0-dev (via coho)
|
||||||
|
|
||||||
|
### 3.5.1 (August 2014) ###
|
||||||
|
|
||||||
|
This was a security update to address CVE-2014-3500, CVE-2014-3501,
|
||||||
|
and CVE-2014-3502. For more information, see
|
||||||
|
http://cordova.apache.org/announcements/2014/08/04/android-351.html
|
||||||
|
|
||||||
|
* Filter out non-launchable intents
|
||||||
|
* Handle unsupported protocol errors in webview better
|
||||||
|
* Update the errorurl to no longer use intents
|
||||||
|
* Refactoring the URI handling on Cordova, removing dead code
|
||||||
|
|
||||||
|
### 3.5.0 (May 2014) ###
|
||||||
|
|
||||||
|
* OkHttp has broken headers. Updating for ASF compliance.
|
||||||
|
* Revert accidentally removed lines from NOTICE
|
||||||
|
* CB-6552: added top level package.json
|
||||||
|
* CB-6491 add CONTRIBUTING.md
|
||||||
|
* CB-6543 Fix cordova/run failure when no custom_rules.xml available
|
||||||
|
* defaults.xml: Add AndroidLaunchMode preference
|
||||||
|
* Add JavaDoc for CordovaResourceApi
|
||||||
|
* CB-6388: Handle binary data correctly in LOAD_URL bridge
|
||||||
|
* Fix CB-6048: Set launchMode=singleTop so tapping app icon does not always restart app
|
||||||
|
* Remove incorrect usage of AlertDialog.Builder.create
|
||||||
|
* Catch uncaught exceptions in from plugins and turn them into error responses.
|
||||||
|
* Add NOTICE file
|
||||||
|
* CB-6047 Fix online sometimes getting in a bad state on page transitions.
|
||||||
|
* Add another convenience overload for CordovaResourceApi.copyResource
|
||||||
|
* Update framework's .classpath to what Eclipse wants it to be.
|
||||||
|
* README.md: `android update` to `android-19`.
|
||||||
|
* Fix NPE when POLLING bridge mode is used.
|
||||||
|
* Updating NOTICE to include Square for OkHttp
|
||||||
|
* CB-5398 Apply KitKat content URI fix to all content URIs
|
||||||
|
* CB-5398 Work-around for KitKat content: URLs not rendering in <img> tags
|
||||||
|
* CB-5908: add splascreen images to template
|
||||||
|
* CB-5395: Make scheme and host (but not path) case-insensitive in whitelist
|
||||||
|
* Ignore multiple onPageFinished() callbacks & onReceivedError due to stopLoading()
|
||||||
|
* Removing addJavascriptInterface support from all Android versions lower than 4.2 due to security vu
|
||||||
|
* CB-4984 Don't create on CordovaActivity name
|
||||||
|
* CB-5917 Add a loadUrlIntoView overload that doesn't recreate plugins.
|
||||||
|
* Use thread pool for load timeout.
|
||||||
|
* CB-5715 For CLI, hide assets/www and res/xml/config.xml by default
|
||||||
|
* CB-5793 ant builds: Rename AndroidManifest during -post-build to avoid Eclipse detecting ant-build/
|
||||||
|
* CB-5889 Make update script find project name instead of using "null" for CordovaLib
|
||||||
|
* CB-5889 Add a message in the update script about needing to import CordovaLib when using an IDE.
|
||||||
|
|
||||||
### 3.4.0 (Feb 2014) ###
|
### 3.4.0 (Feb 2014) ###
|
||||||
|
|
||||||
43 commits from 10 authors. Highlights include:
|
43 commits from 10 authors. Highlights include:
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ get_sdks = function() {
|
|||||||
|
|
||||||
return Q();
|
return Q();
|
||||||
}, function(stderr) {
|
}, function(stderr) {
|
||||||
if (stderr.match(/command\snot\sfound/)) {
|
if (stderr.match(/command\snot\sfound/) || stderr.match(/'android' is not recognized/)) {
|
||||||
return Q.reject(new Error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'));
|
return Q.reject(new Error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'));
|
||||||
} else {
|
} else {
|
||||||
return Q.reject(new Error('An error occurred while listing Android targets'));
|
return Q.reject(new Error('An error occurred while listing Android targets'));
|
||||||
|
|||||||
+126
-38
@@ -19,78 +19,166 @@
|
|||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var shell = require('shelljs'),
|
var shelljs = require('shelljs'),
|
||||||
child_process = require('child_process'),
|
child_process = require('child_process'),
|
||||||
Q = require('q'),
|
Q = require('q'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
|
which = require('which'),
|
||||||
ROOT = path.join(__dirname, '..', '..');
|
ROOT = path.join(__dirname, '..', '..');
|
||||||
|
|
||||||
|
var isWindows = process.platform == 'win32';
|
||||||
|
|
||||||
|
function forgivingWhichSync(cmd) {
|
||||||
|
try {
|
||||||
|
return which.sync(cmd);
|
||||||
|
} catch (e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryCommand(cmd, errMsg) {
|
||||||
|
var d = Q.defer();
|
||||||
|
child_process.exec(cmd, function(err, stdout, stderr) {
|
||||||
|
if (err) d.reject(new Error(errMsg));
|
||||||
|
else d.resolve(stdout);
|
||||||
|
});
|
||||||
|
return d.promise;
|
||||||
|
}
|
||||||
|
|
||||||
// Get valid target from framework/project.properties
|
// Get valid target from framework/project.properties
|
||||||
module.exports.get_target = function() {
|
module.exports.get_target = function() {
|
||||||
if(fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) {
|
if(fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) {
|
||||||
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
|
var target = shelljs.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
|
||||||
return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
|
return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
|
||||||
} else if (fs.existsSync(path.join(ROOT, 'project.properties'))) {
|
} else if (fs.existsSync(path.join(ROOT, 'project.properties'))) {
|
||||||
// if no target found, we're probably in a project and project.properties is in ROOT.
|
// if no target found, we're probably in a project and project.properties is in ROOT.
|
||||||
// this is called on the project itself, and can support Google APIs AND Vanilla Android
|
// this is called on the project itself, and can support Google APIs AND Vanilla Android
|
||||||
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')) ||
|
var target = shelljs.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')) ||
|
||||||
shell.grep(/target=Google Inc.:Google APIs:[\d+]/, path.join(ROOT, 'project.properties'));
|
shelljs.grep(/target=Google Inc.:Google APIs:[\d+]/, path.join(ROOT, 'project.properties'));
|
||||||
|
if(target == "" || !target) {
|
||||||
|
// Try Google Glass APIs
|
||||||
|
target = shelljs.grep(/target=Google Inc.:Glass Development Kit Preview:[\d+]/, path.join(ROOT, 'project.properties'));
|
||||||
|
}
|
||||||
return target.split('=')[1].replace('\n', '').replace('\r', '');
|
return target.split('=')[1].replace('\n', '').replace('\r', '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise.
|
// Returns a promise. Called only by build and clean commands.
|
||||||
module.exports.check_ant = function() {
|
module.exports.check_ant = function() {
|
||||||
var d = Q.defer();
|
return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.');
|
||||||
child_process.exec('ant -version', function(err, stdout, stderr) {
|
};
|
||||||
if (err) d.reject(new Error('ERROR : executing command \'ant\', make sure you have ant installed and added to your path.'));
|
|
||||||
else d.resolve();
|
// Returns a promise. Called only by build and clean commands.
|
||||||
});
|
module.exports.check_gradle = function() {
|
||||||
return d.promise;
|
var sdkDir = process.env['ANDROID_HOME'];
|
||||||
}
|
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
|
||||||
|
if (!fs.existsSync(wrapperDir)) {
|
||||||
|
return Q.reject(new Error('Could not find gradle wrapper within android sdk. Might need to update your Android SDK.\n' +
|
||||||
|
'Looked here: ' + wrapperDir));
|
||||||
|
}
|
||||||
|
return Q.when();
|
||||||
|
};
|
||||||
|
|
||||||
// Returns a promise.
|
// Returns a promise.
|
||||||
module.exports.check_java = function() {
|
module.exports.check_java = function() {
|
||||||
var d = Q.defer();
|
var javacPath = forgivingWhichSync('javac');
|
||||||
child_process.exec('java -version', function(err, stdout, stderr) {
|
var hasJavaHome = !!process.env['JAVA_HOME'];
|
||||||
if(err) {
|
return Q().then(function() {
|
||||||
var msg =
|
if (hasJavaHome) {
|
||||||
'Failed to run \'java -version\', make sure your java environment is set up\n' +
|
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
|
||||||
'including JDK and JRE.\n' +
|
if (!javacPath) {
|
||||||
'Your JAVA_HOME variable is ' + process.env.JAVA_HOME + '\n';
|
process.env['PATH'] += path.delimiter + path.join(process.env['JAVA_HOME'], 'bin');
|
||||||
d.reject(new Error(msg + err));
|
}
|
||||||
|
} else {
|
||||||
|
if (javacPath) {
|
||||||
|
// OS X has a command for finding JAVA_HOME.
|
||||||
|
if (fs.existsSync('/usr/libexec/java_home')) {
|
||||||
|
return tryCommand('/usr/libexec/java_home', 'Failed to run: /usr/libexec/java_home')
|
||||||
|
.then(function(stdout) {
|
||||||
|
process.env['JAVA_HOME'] = stdout.trim();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// See if we can derive it from javac's location.
|
||||||
|
// fs.realpathSync is require on Ubuntu, which symplinks from /usr/bin -> JDK
|
||||||
|
var maybeJavaHome = path.dirname(path.dirname(fs.realpathSync(javacPath)));
|
||||||
|
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
|
||||||
|
process.env['JAVA_HOME'] = maybeJavaHome;
|
||||||
|
} else {
|
||||||
|
throw new Error('Could not find JAVA_HOME. Try setting the environment variable manually');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (isWindows) {
|
||||||
|
// Try to auto-detect java in the default install paths.
|
||||||
|
var firstJdkDir =
|
||||||
|
shelljs.ls(process.env['ProgramFiles'] + '\\java\\jdk*')[0] ||
|
||||||
|
shelljs.ls('C:\\Program Files\\java\\jdk*')[0] ||
|
||||||
|
shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0];
|
||||||
|
if (firstJdkDir) {
|
||||||
|
// shelljs always uses / in paths.
|
||||||
|
firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
|
||||||
|
if (!javacPath) {
|
||||||
|
process.env['PATH'] += path.delimiter + path.join(firstJdkDir, 'bin');
|
||||||
|
}
|
||||||
|
process.env['JAVA_HOME'] = firstJdkDir;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else d.resolve();
|
}).then(function() {
|
||||||
|
var msg =
|
||||||
|
'Failed to run "java -version", make sure your java environment is set up\n' +
|
||||||
|
'including JDK and JRE.\n' +
|
||||||
|
'Your JAVA_HOME variable is: ' + process.env['JAVA_HOME'];
|
||||||
|
return tryCommand('java -version', msg)
|
||||||
|
}).then(function() {
|
||||||
|
msg = 'Failed to run "javac -version", make sure you have a Java JDK (not just a JRE) installed.';
|
||||||
|
return tryCommand('javac -version', msg)
|
||||||
});
|
});
|
||||||
return d.promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise.
|
// Returns a promise.
|
||||||
module.exports.check_android = function() {
|
module.exports.check_android = function() {
|
||||||
var valid_target = this.get_target();
|
return Q().then(function() {
|
||||||
var d = Q.defer();
|
var androidCmdPath = forgivingWhichSync('android');
|
||||||
child_process.exec('android list targets', function(err, stdout, stderr) {
|
var adbInPath = !!forgivingWhichSync('adb');
|
||||||
if (err) d.reject(stderr);
|
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
|
||||||
else d.resolve(stdout);
|
if (hasAndroidHome && !androidCmdPath) {
|
||||||
|
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
|
||||||
|
}
|
||||||
|
if (androidCmdPath && !hasAndroidHome) {
|
||||||
|
var parentDir = path.dirname(androidCmdPath);
|
||||||
|
if (path.basename(parentDir) == 'tools') {
|
||||||
|
process.env['ANDROID_HOME'] = path.dirname(parentDir);
|
||||||
|
hasAndroidHome = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasAndroidHome && !adbInPath) {
|
||||||
|
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
|
||||||
|
}
|
||||||
|
if (!process.env['ANDROID_HOME']) {
|
||||||
|
throw new Error('ANDROID_HOME is not set and "android" command not in your PATH. You must fulfill at least one of these conditions.');
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(process.env['ANDROID_HOME'])) {
|
||||||
|
throw new Error('ANDROID_HOME is set to a non-existant path: ' + process.env['ANDROID_HOME']);
|
||||||
|
}
|
||||||
|
// Check that the target sdk level is installed.
|
||||||
|
return module.exports.check_android_target(module.exports.get_target());
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return d.promise.then(function(output) {
|
module.exports.check_android_target = function(valid_target) {
|
||||||
|
var msg = 'Failed to run "android". Make sure you have the latest Android SDK installed, and that the "android" command (inside the tools/ folder) is added to your PATH.';
|
||||||
|
return tryCommand('android list targets', msg)
|
||||||
|
.then(function(output) {
|
||||||
if (!output.match(valid_target)) {
|
if (!output.match(valid_target)) {
|
||||||
return Q.reject(new Error('Please install Android target ' + valid_target.split('-')[1] + ' (the Android newest SDK). Make sure you have the latest Android tools installed as well. Run \"android\" from your command-line to install/update any missing SDKs or tools.'));
|
throw new Error('Please install Android target "' + valid_target + '".\n' +
|
||||||
}
|
'Hint: Run "android" from your command-line to open the SDK manager.');
|
||||||
return Q();
|
|
||||||
}, function(stderr) {
|
|
||||||
if (stderr.match(/command\snot\sfound/)) {
|
|
||||||
return Q.reject(new Error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'));
|
|
||||||
} else {
|
|
||||||
return Q.reject(new Error('An error occurred while listing Android targets'));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
// Returns a promise.
|
// Returns a promise.
|
||||||
module.exports.run = function() {
|
module.exports.run = function() {
|
||||||
return Q.all([this.check_ant(), this.check_java(), this.check_android()]);
|
return Q.all([this.check_java(), this.check_android()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+106
-26
@@ -71,6 +71,7 @@ function copyJsAndLibrary(projectPath, shared, projectName) {
|
|||||||
shell.mkdir('-p', nestedCordovaLibPath);
|
shell.mkdir('-p', nestedCordovaLibPath);
|
||||||
shell.cp('-f', path.join(ROOT, 'framework', 'AndroidManifest.xml'), nestedCordovaLibPath);
|
shell.cp('-f', path.join(ROOT, 'framework', 'AndroidManifest.xml'), nestedCordovaLibPath);
|
||||||
shell.cp('-f', path.join(ROOT, 'framework', 'project.properties'), nestedCordovaLibPath);
|
shell.cp('-f', path.join(ROOT, 'framework', 'project.properties'), nestedCordovaLibPath);
|
||||||
|
shell.cp('-f', path.join(ROOT, 'framework', 'build.gradle'), nestedCordovaLibPath);
|
||||||
shell.cp('-r', path.join(ROOT, 'framework', 'src'), nestedCordovaLibPath);
|
shell.cp('-r', path.join(ROOT, 'framework', 'src'), nestedCordovaLibPath);
|
||||||
// Create an eclipse project file and set the name of it to something unique.
|
// Create an eclipse project file and set the name of it to something unique.
|
||||||
// Without this, you can't import multiple CordovaLib projects into the same workspace.
|
// Without this, you can't import multiple CordovaLib projects into the same workspace.
|
||||||
@@ -82,14 +83,46 @@ function copyJsAndLibrary(projectPath, shared, projectName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function runAndroidUpdate(projectPath, target_api, shared) {
|
function extractSubProjectPaths(data) {
|
||||||
var targetFrameworkDir = getFrameworkDir(projectPath, shared);
|
var ret = {};
|
||||||
return exec('android update project --subprojects --path "' + projectPath + '" --target ' + target_api + ' --library "' + path.relative(projectPath, targetFrameworkDir) + '"');
|
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
|
||||||
|
var m;
|
||||||
|
while (m = r.exec(data)) {
|
||||||
|
ret[m[1]] = 1;
|
||||||
|
}
|
||||||
|
return Object.keys(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyAntRules(projectPath) {
|
function writeProjectProperties(projectPath, target_api, shared) {
|
||||||
|
var dstPath = path.join(projectPath, 'project.properties');
|
||||||
|
var templatePath = path.join(ROOT, 'bin', 'templates', 'project', 'project.properties');
|
||||||
|
var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
|
||||||
|
var data = fs.readFileSync(srcPath, 'utf8');
|
||||||
|
data = data.replace(/^target=.*/m, 'target=' + target_api);
|
||||||
|
var subProjects = extractSubProjectPaths(data);
|
||||||
|
subProjects = subProjects.filter(function(p) {
|
||||||
|
return !(/^CordovaLib$/m.exec(p) ||
|
||||||
|
/[\\\/]cordova-android[\\\/]framework$/m.exec(p) ||
|
||||||
|
/^(\.\.[\\\/])+framework$/m.exec(p)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
subProjects.unshift(shared ? path.relative(projectPath, path.join(ROOT, 'framework')) : 'CordovaLib');
|
||||||
|
data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, '');
|
||||||
|
if (!/\n$/.exec(data)) {
|
||||||
|
data += '\n';
|
||||||
|
}
|
||||||
|
for (var i = 0; i < subProjects.length; ++i) {
|
||||||
|
data += 'android.library.reference.' + (i+1) + '=' + subProjects[i] + '\n';
|
||||||
|
}
|
||||||
|
fs.writeFileSync(dstPath, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyBuildRules(projectPath) {
|
||||||
var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
|
var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
|
||||||
shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath);
|
shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath);
|
||||||
|
|
||||||
|
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
|
||||||
|
shell.cp('-f', path.join(srcDir, 'settings.gradle'), projectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyScripts(projectPath) {
|
function copyScripts(projectPath) {
|
||||||
@@ -106,6 +139,50 @@ function copyScripts(projectPath) {
|
|||||||
shell.cp(path.join(ROOT, 'bin', 'lib', 'android_sdk_version.js'), path.join(projectPath, 'cordova', 'lib', 'android_sdk_version.js'));
|
shell.cp(path.join(ROOT, 'bin', 'lib', 'android_sdk_version.js'), path.join(projectPath, 'cordova', 'lib', 'android_sdk_version.js'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether a package name is acceptable for use as an android project.
|
||||||
|
* Returns a promise, fulfilled if the package name is acceptable; rejected
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
function validatePackageName(package_name) {
|
||||||
|
//Make the package conform to Java package types
|
||||||
|
//Enforce underscore limitation
|
||||||
|
if (!/^[a-zA-Z]+(\.[a-zA-Z0-9][a-zA-Z0-9_]*)+$/.test(package_name)) {
|
||||||
|
return Q.reject('Package name must look like: com.company.Name');
|
||||||
|
}
|
||||||
|
|
||||||
|
//Class is a reserved word
|
||||||
|
if(/\b[Cc]lass\b/.test(package_name)) {
|
||||||
|
return Q.reject('class is a reserved word');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Q.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether a project name is acceptable for use as an android class.
|
||||||
|
* Returns a promise, fulfilled if the project name is acceptable; rejected
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
function validateProjectName(project_name) {
|
||||||
|
//Make sure there's something there
|
||||||
|
if (project_name === '') {
|
||||||
|
return Q.reject('Project name cannot be empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
//Enforce stupid name error
|
||||||
|
if (project_name === 'CordovaActivity') {
|
||||||
|
return Q.reject('Project name cannot be CordovaActivity');
|
||||||
|
}
|
||||||
|
|
||||||
|
//Classes in Java don't begin with numbers
|
||||||
|
if (/^[0-9]/.test(project_name)) {
|
||||||
|
return Q.reject('Project name must not begin with a number');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Q.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* $ create [options]
|
* $ create [options]
|
||||||
*
|
*
|
||||||
@@ -133,9 +210,11 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
|||||||
project_template_dir :
|
project_template_dir :
|
||||||
path.join(ROOT, 'bin', 'templates', 'project');
|
path.join(ROOT, 'bin', 'templates', 'project');
|
||||||
|
|
||||||
var safe_activity_name = project_name.replace(/\W/g, '');
|
|
||||||
var package_as_path = package_name.replace(/\./g, path.sep);
|
var package_as_path = package_name.replace(/\./g, path.sep);
|
||||||
var activity_dir = path.join(project_path, 'src', package_as_path);
|
var activity_dir = path.join(project_path, 'src', package_as_path);
|
||||||
|
// safe_activity_name is being hardcoded to avoid issues with unicode app name (https://issues.apache.org/jira/browse/CB-6511)
|
||||||
|
// TODO: provide option to specify activity name via CLI (proposal: https://issues.apache.org/jira/browse/CB-7231)
|
||||||
|
var safe_activity_name = "CordovaApp";
|
||||||
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
|
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
|
||||||
var target_api = check_reqs.get_target();
|
var target_api = check_reqs.get_target();
|
||||||
var manifest_path = path.join(project_path, 'AndroidManifest.xml');
|
var manifest_path = path.join(project_path, 'AndroidManifest.xml');
|
||||||
@@ -145,17 +224,15 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
|||||||
return Q.reject('Project already exists! Delete and recreate');
|
return Q.reject('Project already exists! Delete and recreate');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!/[a-zA-Z0-9_]+\.[a-zA-Z0-9_](.[a-zA-Z0-9_])*/.test(package_name)) {
|
//Make the package conform to Java package types
|
||||||
return Q.reject('Package name must look like: com.company.Name');
|
return validatePackageName(package_name)
|
||||||
}
|
|
||||||
|
|
||||||
if (project_name === 'CordovaActivity') {
|
|
||||||
return Q.reject('Project name cannot be CordovaActivity');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that requirements are met and proper targets are installed
|
|
||||||
return check_reqs.run()
|
|
||||||
.then(function() {
|
.then(function() {
|
||||||
|
validateProjectName(project_name);
|
||||||
|
})
|
||||||
|
// Check that requirements are met and proper targets are installed
|
||||||
|
.then(function() {
|
||||||
|
return check_reqs.run();
|
||||||
|
}).then(function() {
|
||||||
// Log the given values for the project
|
// Log the given values for the project
|
||||||
console.log('Creating Cordova project for the Android platform:');
|
console.log('Creating Cordova project for the Android platform:');
|
||||||
console.log('\tPath: ' + project_path);
|
console.log('\tPath: ' + project_path);
|
||||||
@@ -170,6 +247,7 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
|||||||
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
|
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
|
||||||
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
|
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
|
||||||
shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
|
shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
|
||||||
|
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
|
||||||
|
|
||||||
// Manually create directories that would be empty within the template (since git doesn't track directories).
|
// Manually create directories that would be empty within the template (since git doesn't track directories).
|
||||||
shell.mkdir(path.join(project_path, 'libs'));
|
shell.mkdir(path.join(project_path, 'libs'));
|
||||||
@@ -201,10 +279,10 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
|||||||
shell.sed('-i', /__PACKAGE__/, package_name, manifest_path);
|
shell.sed('-i', /__PACKAGE__/, package_name, manifest_path);
|
||||||
shell.sed('-i', /__APILEVEL__/, target_api.split('-')[1], manifest_path);
|
shell.sed('-i', /__APILEVEL__/, target_api.split('-')[1], manifest_path);
|
||||||
copyScripts(project_path);
|
copyScripts(project_path);
|
||||||
copyAntRules(project_path);
|
copyBuildRules(project_path);
|
||||||
});
|
});
|
||||||
// Link it to local android install.
|
// Link it to local android install.
|
||||||
return runAndroidUpdate(project_path, target_api, use_shared_project);
|
writeProjectProperties(project_path, target_api);
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
console.log('Project successfully created.');
|
console.log('Project successfully created.');
|
||||||
});
|
});
|
||||||
@@ -227,22 +305,24 @@ function extractProjectNameFromManifest(projectPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise.
|
// Returns a promise.
|
||||||
exports.updateProject = function(projectPath) {
|
exports.updateProject = function(projectPath, shared) {
|
||||||
var version = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
|
var newVersion = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
|
||||||
// Check that requirements are met and proper targets are installed
|
// Check that requirements are met and proper targets are installed
|
||||||
return check_reqs.run()
|
return check_reqs.run()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
var projectName = extractProjectNameFromManifest(projectPath);
|
var projectName = extractProjectNameFromManifest(projectPath);
|
||||||
var target_api = check_reqs.get_target();
|
var target_api = check_reqs.get_target();
|
||||||
copyJsAndLibrary(projectPath, false, projectName);
|
copyJsAndLibrary(projectPath, shared, projectName);
|
||||||
copyScripts(projectPath);
|
copyScripts(projectPath);
|
||||||
copyAntRules(projectPath);
|
copyBuildRules(projectPath);
|
||||||
removeDebuggableFromManifest(projectPath);
|
removeDebuggableFromManifest(projectPath);
|
||||||
return runAndroidUpdate(projectPath, target_api, false)
|
writeProjectProperties(projectPath, target_api, shared);
|
||||||
.then(function() {
|
console.log('Android project is now at version ' + newVersion);
|
||||||
console.log('Android project is now at version ' + version);
|
console.log('If you updated from a pre-3.2.0 version and use an IDE, we now require that you import the "CordovaLib" library project.');
|
||||||
console.log('If you updated from a pre-3.2.0 version and use an IDE, we now require that you import the "CordovaLib" library project.');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// For testing
|
||||||
|
exports.validatePackageName = validatePackageName;
|
||||||
|
exports.validateProjectName = validateProjectName;
|
||||||
|
|||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
The "which" util from npm's guts.
|
||||||
|
|
||||||
|
Finds the first instance of a specified executable in the PATH
|
||||||
|
environment variable. Does not cache the results, so `hash -r` is not
|
||||||
|
needed when the PATH changes.
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
var which = require("../")
|
||||||
|
if (process.argv.length < 3) {
|
||||||
|
console.error("Usage: which <thing>")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
which(process.argv[2], function (er, thing) {
|
||||||
|
if (er) {
|
||||||
|
console.error(er.message)
|
||||||
|
process.exit(er.errno || 127)
|
||||||
|
}
|
||||||
|
console.log(thing)
|
||||||
|
})
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"author": {
|
||||||
|
"name": "Isaac Z. Schlueter",
|
||||||
|
"email": "i@izs.me",
|
||||||
|
"url": "http://blog.izs.me"
|
||||||
|
},
|
||||||
|
"name": "which",
|
||||||
|
"description": "Like which(1) unix command. Find the first instance of an executable in the PATH.",
|
||||||
|
"version": "1.0.5",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/isaacs/node-which.git"
|
||||||
|
},
|
||||||
|
"main": "which.js",
|
||||||
|
"bin": {
|
||||||
|
"which": "./bin/which"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {},
|
||||||
|
"readme": "The \"which\" util from npm's guts.\n\nFinds the first instance of a specified executable in the PATH\nenvironment variable. Does not cache the results, so `hash -r` is not\nneeded when the PATH changes.\n",
|
||||||
|
"readmeFilename": "README.md",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/isaacs/node-which/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/isaacs/node-which",
|
||||||
|
"_id": "which@1.0.5",
|
||||||
|
"_from": "which@"
|
||||||
|
}
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
module.exports = which
|
||||||
|
which.sync = whichSync
|
||||||
|
|
||||||
|
var path = require("path")
|
||||||
|
, fs
|
||||||
|
, COLON = process.platform === "win32" ? ";" : ":"
|
||||||
|
, isExe
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs = require("graceful-fs")
|
||||||
|
} catch (ex) {
|
||||||
|
fs = require("fs")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform == "win32") {
|
||||||
|
// On windows, there is no good way to check that a file is executable
|
||||||
|
isExe = function isExe () { return true }
|
||||||
|
} else {
|
||||||
|
isExe = function isExe (mod, uid, gid) {
|
||||||
|
//console.error(mod, uid, gid);
|
||||||
|
//console.error("isExe?", (mod & 0111).toString(8))
|
||||||
|
var ret = (mod & 0001)
|
||||||
|
|| (mod & 0010) && process.getgid && gid === process.getgid()
|
||||||
|
|| (mod & 0100) && process.getuid && uid === process.getuid()
|
||||||
|
//console.error("isExe?", ret)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function which (cmd, cb) {
|
||||||
|
if (isAbsolute(cmd)) return cb(null, cmd)
|
||||||
|
var pathEnv = (process.env.PATH || "").split(COLON)
|
||||||
|
, pathExt = [""]
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
pathEnv.push(process.cwd())
|
||||||
|
pathExt = (process.env.PATHEXT || ".EXE").split(COLON)
|
||||||
|
if (cmd.indexOf(".") !== -1) pathExt.unshift("")
|
||||||
|
}
|
||||||
|
//console.error("pathEnv", pathEnv)
|
||||||
|
;(function F (i, l) {
|
||||||
|
if (i === l) return cb(new Error("not found: "+cmd))
|
||||||
|
var p = path.resolve(pathEnv[i], cmd)
|
||||||
|
;(function E (ii, ll) {
|
||||||
|
if (ii === ll) return F(i + 1, l)
|
||||||
|
var ext = pathExt[ii]
|
||||||
|
//console.error(p + ext)
|
||||||
|
fs.stat(p + ext, function (er, stat) {
|
||||||
|
if (!er &&
|
||||||
|
stat &&
|
||||||
|
stat.isFile() &&
|
||||||
|
isExe(stat.mode, stat.uid, stat.gid)) {
|
||||||
|
//console.error("yes, exe!", p + ext)
|
||||||
|
return cb(null, p + ext)
|
||||||
|
}
|
||||||
|
return E(ii + 1, ll)
|
||||||
|
})
|
||||||
|
})(0, pathExt.length)
|
||||||
|
})(0, pathEnv.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
function whichSync (cmd) {
|
||||||
|
if (isAbsolute(cmd)) return cmd
|
||||||
|
var pathEnv = (process.env.PATH || "").split(COLON)
|
||||||
|
, pathExt = [""]
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
pathEnv.push(process.cwd())
|
||||||
|
pathExt = (process.env.PATHEXT || ".EXE").split(COLON)
|
||||||
|
if (cmd.indexOf(".") !== -1) pathExt.unshift("")
|
||||||
|
}
|
||||||
|
for (var i = 0, l = pathEnv.length; i < l; i ++) {
|
||||||
|
var p = path.join(pathEnv[i], cmd)
|
||||||
|
for (var j = 0, ll = pathExt.length; j < ll; j ++) {
|
||||||
|
var cur = p + pathExt[j]
|
||||||
|
var stat
|
||||||
|
try { stat = fs.statSync(cur) } catch (ex) {}
|
||||||
|
if (stat &&
|
||||||
|
stat.isFile() &&
|
||||||
|
isExe(stat.mode, stat.uid, stat.gid)) return cur
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("not found: "+cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var isAbsolute = process.platform === "win32" ? absWin : absUnix
|
||||||
|
|
||||||
|
function absWin (p) {
|
||||||
|
if (absUnix(p)) return true
|
||||||
|
// pull off the device/UNC bit from a windows path.
|
||||||
|
// from node's lib/path.js
|
||||||
|
var splitDeviceRe =
|
||||||
|
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?/
|
||||||
|
, result = splitDeviceRe.exec(p)
|
||||||
|
, device = result[1] || ''
|
||||||
|
, isUnc = device && device.charAt(1) !== ':'
|
||||||
|
, isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
|
||||||
|
|
||||||
|
return isAbsolute
|
||||||
|
}
|
||||||
|
|
||||||
|
function absUnix (p) {
|
||||||
|
return p.charAt(0) === "/" || p === ""
|
||||||
|
}
|
||||||
@@ -24,13 +24,17 @@ var build = require('./lib/build'),
|
|||||||
args = process.argv;
|
args = process.argv;
|
||||||
|
|
||||||
// Support basic help commands
|
// Support basic help commands
|
||||||
if(args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
|
if(args[2] == '--help' ||
|
||||||
args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
|
args[2] == '/?' ||
|
||||||
|
args[2] == '-h' ||
|
||||||
|
args[2] == 'help' ||
|
||||||
|
args[2] == '-help' ||
|
||||||
|
args[2] == '/help') {
|
||||||
build.help();
|
build.help();
|
||||||
} else {
|
} else {
|
||||||
reqs.run().then(function() {
|
reqs.run().done(function() {
|
||||||
return build.run(args[2]);
|
return build.run(args.slice(2));
|
||||||
}).done(null, function(err) {
|
}, function(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,18 +19,26 @@
|
|||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var clean = require('./lib/clean'),
|
var build = require('./lib/build'),
|
||||||
reqs = require('./lib/check_reqs'),
|
reqs = require('./lib/check_reqs'),
|
||||||
args = process.argv;
|
args = process.argv;
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
// Usage support for when args are given
|
// Support basic help commands
|
||||||
if(args.length > 2) {
|
if(args[2] == '--help' ||
|
||||||
clean.help();
|
args[2] == '/?' ||
|
||||||
|
args[2] == '-h' ||
|
||||||
|
args[2] == 'help' ||
|
||||||
|
args[2] == '-help' ||
|
||||||
|
args[2] == '/help') {
|
||||||
|
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
|
||||||
|
console.log('Cleans the project directory.');
|
||||||
|
process.exit(0);
|
||||||
} else {
|
} else {
|
||||||
reqs.run().done(function() {
|
reqs.run().done(function() {
|
||||||
return clean.run();
|
return build.runClean(args.slice(2));
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
console.error('ERROR: ' + err);
|
console.error(err);
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,4 @@
|
|||||||
|
|
||||||
<!-- Preferences for Android -->
|
<!-- Preferences for Android -->
|
||||||
<preference name="loglevel" value="DEBUG" />
|
<preference name="loglevel" value="DEBUG" />
|
||||||
<preference name="AndroidLaunchMode" value="singleTop" />
|
|
||||||
|
|
||||||
<!-- This is required for native Android hooks -->
|
|
||||||
<feature name="App">
|
|
||||||
<param name="android-package" value="org.apache.cordova.App" />
|
|
||||||
</feature>
|
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
Vendored
+335
-63
@@ -20,94 +20,366 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
var shell = require('shelljs'),
|
var shell = require('shelljs'),
|
||||||
|
exec = require('./exec'),
|
||||||
spawn = require('./spawn'),
|
spawn = require('./spawn'),
|
||||||
Q = require('q'),
|
Q = require('q'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
ROOT = path.join(__dirname, '..', '..');
|
ROOT = path.join(__dirname, '..', '..');
|
||||||
|
var check_reqs = require('./check_reqs');
|
||||||
|
|
||||||
|
var LOCAL_PROPERTIES_TEMPLATE =
|
||||||
|
'# This file is automatically generated.\n' +
|
||||||
|
'# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n';
|
||||||
|
|
||||||
|
function findApks(directory) {
|
||||||
|
var ret = [];
|
||||||
|
if (fs.existsSync(directory)) {
|
||||||
|
fs.readdirSync(directory).forEach(function(p) {
|
||||||
|
if (path.extname(p) == '.apk') {
|
||||||
|
ret.push(path.join(directory, p));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortFilesByDate(files) {
|
||||||
|
return files.map(function(p) {
|
||||||
|
return { p: p, t: fs.statSync(p).mtime };
|
||||||
|
}).sort(function(a, b) {
|
||||||
|
var timeDiff = b.t - a.t;
|
||||||
|
return timeDiff === 0 ? a.p.length - b.p.length : timeDiff;
|
||||||
|
}).map(function(p) { return p.p; });
|
||||||
|
}
|
||||||
|
|
||||||
|
function findOutputApksHelper(dir, build_type) {
|
||||||
|
var ret = findApks(dir).filter(function(candidate) {
|
||||||
|
// Need to choose between release and debug .apk.
|
||||||
|
if (build_type === 'debug') {
|
||||||
|
return /-debug/.exec(candidate) && !/-unaligned|-unsigned/.exec(candidate);
|
||||||
|
}
|
||||||
|
if (build_type === 'release') {
|
||||||
|
return /-release/.exec(candidate) && !/-unaligned/.exec(candidate);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
ret = sortFilesByDate(ret);
|
||||||
|
if (ret.length === 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
var archSpecific = !!/-x86|-arm/.exec(ret[0]);
|
||||||
|
return ret.filter(function(p) {
|
||||||
|
return !!/-x86|-arm/.exec(p) == archSpecific;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function hasCustomRules() {
|
function hasCustomRules() {
|
||||||
return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
|
return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
|
||||||
}
|
}
|
||||||
module.exports.getAntArgs = function(cmd) {
|
|
||||||
var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
|
function extractProjectNameFromManifest(projectPath) {
|
||||||
// custom_rules.xml is required for incremental builds.
|
var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
|
||||||
if (hasCustomRules()) {
|
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||||
args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
|
var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||||
|
if (!m) {
|
||||||
|
throw new Error('Could not find activity name in ' + manifestPath);
|
||||||
}
|
}
|
||||||
return args;
|
return m[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractSubProjectPaths() {
|
||||||
|
var data = fs.readFileSync(path.join(ROOT, 'project.properties'), 'utf8');
|
||||||
|
var ret = {};
|
||||||
|
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
|
||||||
|
var m;
|
||||||
|
while (m = r.exec(data)) {
|
||||||
|
ret[m[1]] = 1;
|
||||||
|
}
|
||||||
|
return Object.keys(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
var builders = {
|
||||||
|
ant: {
|
||||||
|
getArgs: function(cmd) {
|
||||||
|
var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
|
||||||
|
// custom_rules.xml is required for incremental builds.
|
||||||
|
if (hasCustomRules()) {
|
||||||
|
args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
},
|
||||||
|
|
||||||
|
prepEnv: function() {
|
||||||
|
return check_reqs.check_ant()
|
||||||
|
.then(function() {
|
||||||
|
// Copy in build.xml on each build so that:
|
||||||
|
// A) we don't require the Android SDK at project creation time, and
|
||||||
|
// B) we always use the SDK's latest version of it.
|
||||||
|
var sdkDir = process.env['ANDROID_HOME'];
|
||||||
|
var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
|
||||||
|
function writeBuildXml(projectPath) {
|
||||||
|
var newData = buildTemplate.replace('PROJECT_NAME', extractProjectNameFromManifest(ROOT));
|
||||||
|
fs.writeFileSync(path.join(projectPath, 'build.xml'), newData);
|
||||||
|
if (!fs.existsSync(path.join(projectPath, 'local.properties'))) {
|
||||||
|
fs.writeFileSync(path.join(projectPath, 'local.properties'), LOCAL_PROPERTIES_TEMPLATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var subProjects = extractSubProjectPaths();
|
||||||
|
writeBuildXml(ROOT);
|
||||||
|
for (var i = 0; i < subProjects.length; ++i) {
|
||||||
|
writeBuildXml(path.join(ROOT, subProjects[i]));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Builds the project with ant.
|
||||||
|
* Returns a promise.
|
||||||
|
*/
|
||||||
|
build: function(build_type) {
|
||||||
|
// Without our custom_rules.xml, we need to clean before building.
|
||||||
|
var ret = Q();
|
||||||
|
if (!hasCustomRules()) {
|
||||||
|
// clean will call check_ant() for us.
|
||||||
|
ret = this.clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = this;
|
||||||
|
var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release');
|
||||||
|
return check_reqs.check_ant()
|
||||||
|
.then(function() {
|
||||||
|
return spawn('ant', args);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clean: function() {
|
||||||
|
var args = this.getArgs('clean');
|
||||||
|
return check_reqs.check_ant()
|
||||||
|
.then(function() {
|
||||||
|
return spawn('ant', args);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
findOutputApks: function(build_type) {
|
||||||
|
var binDir = path.join(ROOT, hasCustomRules() ? 'ant-build' : 'bin');
|
||||||
|
return findOutputApksHelper(binDir, build_type);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gradle: {
|
||||||
|
getArgs: function(cmd) {
|
||||||
|
var lintSteps;
|
||||||
|
if (process.env['BUILD_MULTIPLE_APKS']) {
|
||||||
|
lintSteps = [
|
||||||
|
'lint',
|
||||||
|
'lintVitalX86Release',
|
||||||
|
'lintVitalArmv7Release',
|
||||||
|
'compileLint',
|
||||||
|
'copyReleaseLint',
|
||||||
|
'copyDebugLint'
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
lintSteps = [
|
||||||
|
'lint',
|
||||||
|
'lintVitalRelease',
|
||||||
|
'compileLint',
|
||||||
|
'copyReleaseLint',
|
||||||
|
'copyDebugLint'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
var args = [cmd, '-b', path.join(ROOT, 'build.gradle')];
|
||||||
|
// 10 seconds -> 6 seconds
|
||||||
|
args.push('-Dorg.gradle.daemon=true');
|
||||||
|
// Excluding lint: 6s-> 1.6s
|
||||||
|
for (var i = 0; i < lintSteps.length; ++i) {
|
||||||
|
args.push('-x', lintSteps[i]);
|
||||||
|
}
|
||||||
|
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
|
||||||
|
// args.push('-Dorg.gradle.parallel=true');
|
||||||
|
return args;
|
||||||
|
},
|
||||||
|
|
||||||
|
prepEnv: function() {
|
||||||
|
return check_reqs.check_gradle()
|
||||||
|
.then(function() {
|
||||||
|
// Copy the gradle wrapper on each build so that:
|
||||||
|
// A) we don't require the Android SDK at project creation time, and
|
||||||
|
// B) we always use the SDK's latest version of it.
|
||||||
|
var projectPath = ROOT;
|
||||||
|
// check_reqs ensures that this is set.
|
||||||
|
var sdkDir = process.env['ANDROID_HOME'];
|
||||||
|
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
|
||||||
|
if (process.platform == 'win32') {
|
||||||
|
shell.cp('-f', path.join(wrapperDir, 'gradlew.bat'), projectPath);
|
||||||
|
} else {
|
||||||
|
shell.cp('-f', path.join(wrapperDir, 'gradlew'), projectPath);
|
||||||
|
}
|
||||||
|
shell.rm('-rf', path.join(projectPath, 'gradle', 'wrapper'));
|
||||||
|
shell.mkdir('-p', path.join(projectPath, 'gradle'));
|
||||||
|
shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Builds the project with gradle.
|
||||||
|
* Returns a promise.
|
||||||
|
*/
|
||||||
|
build: function(build_type) {
|
||||||
|
var builder = this;
|
||||||
|
var wrapper = path.join(ROOT, 'gradlew');
|
||||||
|
var args = builder.getArgs('build');
|
||||||
|
return Q().then(function() {
|
||||||
|
return spawn(wrapper, args);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clean: function() {
|
||||||
|
var builder = this;
|
||||||
|
var wrapper = path.join(ROOT, 'gradlew');
|
||||||
|
var args = builder.getArgs('clean');
|
||||||
|
return Q().then(function() {
|
||||||
|
return spawn(wrapper, args);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
findOutputApks: function(build_type) {
|
||||||
|
var binDir = path.join(ROOT, 'build', 'apk');
|
||||||
|
return findOutputApksHelper(binDir, build_type);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
none: {
|
||||||
|
prepEnv: function() {
|
||||||
|
return Q();
|
||||||
|
},
|
||||||
|
build: function() {
|
||||||
|
console.log('Skipping build...');
|
||||||
|
return Q(null);
|
||||||
|
},
|
||||||
|
clean: function() {
|
||||||
|
return Q();
|
||||||
|
},
|
||||||
|
findOutputApks: function(build_type) {
|
||||||
|
return sortFilesByDate(builders.ant.findOutputApks(build_type).concat(builders.gradle.findOutputApks(build_type)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseOpts(options) {
|
||||||
|
// Backwards-compatibility: Allow a single string argument
|
||||||
|
if (typeof options == "string") options = [options];
|
||||||
|
|
||||||
|
var ret = {
|
||||||
|
buildType: 'debug',
|
||||||
|
buildMethod: process.env['ANDROID_BUILD'] || 'ant'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate through command line options
|
||||||
|
for (var i=0; options && (i < options.length); ++i) {
|
||||||
|
if (/^--/.exec(options[i])) {
|
||||||
|
var option = options[i].substring(2);
|
||||||
|
switch(option) {
|
||||||
|
case 'debug':
|
||||||
|
case 'release':
|
||||||
|
ret.buildType = option;
|
||||||
|
break;
|
||||||
|
case 'ant':
|
||||||
|
case 'gradle':
|
||||||
|
ret.buildMethod = option;
|
||||||
|
break;
|
||||||
|
case 'nobuild' :
|
||||||
|
ret.buildMethod = 'none';
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
return Q.reject('Build option \'' + options[i] + '\' not recognized.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Q.reject('Build option \'' + options[i] + '\' not recognized.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Builds the project with the specifed options
|
||||||
|
* Returns a promise.
|
||||||
|
*/
|
||||||
|
module.exports.runClean = function(options) {
|
||||||
|
var opts = parseOpts(options);
|
||||||
|
var builder = builders[opts.buildMethod];
|
||||||
|
return builder.prepEnv()
|
||||||
|
.then(function() {
|
||||||
|
return builder.clean();
|
||||||
|
}).then(function() {
|
||||||
|
shell.rm('-rf', path.join(ROOT, 'out'));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Builds the project with ant.
|
* Builds the project with the specifed options
|
||||||
* Returns a promise.
|
* Returns a promise.
|
||||||
*/
|
*/
|
||||||
module.exports.run = function(build_type) {
|
module.exports.run = function(options) {
|
||||||
//default build type
|
var opts = parseOpts(options);
|
||||||
build_type = typeof build_type !== 'undefined' ? build_type : "--debug";
|
|
||||||
var args = module.exports.getAntArgs('debug');
|
var builder = builders[opts.buildMethod];
|
||||||
switch(build_type) {
|
return builder.prepEnv()
|
||||||
case '--debug' :
|
.then(function() {
|
||||||
break;
|
return builder.build(opts.buildType);
|
||||||
case '--release' :
|
}).then(function() {
|
||||||
args[0] = 'release';
|
var apkPaths = builder.findOutputApks(opts.buildType);
|
||||||
break;
|
console.log('Built the following apk(s):');
|
||||||
case '--nobuild' :
|
console.log(' ' + apkPaths.join('\n '));
|
||||||
console.log('Skipping build...');
|
return {
|
||||||
return Q();
|
apkPaths: apkPaths,
|
||||||
default :
|
buildType: opts.buildType,
|
||||||
return Q.reject('Build option \'' + build_type + '\' not recognized.');
|
buildMethod: opts.buildMethod
|
||||||
}
|
};
|
||||||
// Without our custom_rules.xml, we need to clean before building.
|
|
||||||
var ret = Q();
|
|
||||||
if (!hasCustomRules()) {
|
|
||||||
ret = require('./clean').run();
|
|
||||||
}
|
|
||||||
return ret.then(function() {
|
|
||||||
return spawn('ant', args);
|
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gets the path to the apk file, if not such file exists then
|
* Detects the architecture of a device/emulator
|
||||||
* the script will error out. (should we error or just return undefined?)
|
* Returns "arm" or "x86".
|
||||||
*/
|
*/
|
||||||
module.exports.get_apk = function() {
|
module.exports.detectArchitecture = function(target) {
|
||||||
var binDir = '';
|
return exec('adb -s ' + target + ' shell cat /proc/cpuinfo')
|
||||||
if(!hasCustomRules()) {
|
.then(function(output) {
|
||||||
binDir = path.join(ROOT, 'bin');
|
if (/intel/i.exec(output)) {
|
||||||
} else {
|
return 'x86';
|
||||||
binDir = path.join(ROOT, 'ant-build');
|
}
|
||||||
}
|
return 'arm';
|
||||||
if (fs.existsSync(binDir)) {
|
});
|
||||||
var candidates = fs.readdirSync(binDir).filter(function(p) {
|
};
|
||||||
// Need to choose between release and debug .apk.
|
|
||||||
return path.extname(p) == '.apk';
|
module.exports.findBestApkForArchitecture = function(buildResults, arch) {
|
||||||
}).map(function(p) {
|
var paths = buildResults.apkPaths.filter(function(p) {
|
||||||
p = path.join(binDir, p);
|
if (buildResults.buildType == 'debug') {
|
||||||
return { p: p, t: fs.statSync(p).mtime };
|
return /-debug/.exec(p);
|
||||||
}).sort(function(a,b) {
|
}
|
||||||
return a.t > b.t ? -1 :
|
return !/-debug/.exec(p);
|
||||||
a.t < b.t ? 1 : 0;
|
});
|
||||||
});
|
var archPattern = new RegExp('-' + arch);
|
||||||
if (candidates.length === 0) {
|
var hasArchPattern = /-x86|-arm/;
|
||||||
console.error('ERROR : No .apk found in ' + binDir + ' directory');
|
for (var i = 0; i < paths.length; ++i) {
|
||||||
process.exit(2);
|
if (hasArchPattern.exec(paths[i])) {
|
||||||
|
if (archPattern.exec(paths[i])) {
|
||||||
|
return paths[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return paths[i];
|
||||||
}
|
}
|
||||||
console.log('Using apk: ' + candidates[0].p);
|
|
||||||
return candidates[0].p;
|
|
||||||
} else {
|
|
||||||
console.error('ERROR : unable to find project ' + binDir + ' directory, could not locate .apk');
|
|
||||||
process.exit(2);
|
|
||||||
}
|
}
|
||||||
}
|
throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType);
|
||||||
|
};
|
||||||
|
|
||||||
module.exports.help = function() {
|
module.exports.help = function() {
|
||||||
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [build_type]');
|
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [build_type]');
|
||||||
console.log('Build Types : ');
|
console.log('Build Types : ');
|
||||||
console.log(' \'--debug\': Default build, will build project in using ant debug');
|
console.log(' \'--debug\': Default build, will build project in debug mode');
|
||||||
console.log(' \'--release\': will build project using ant release');
|
console.log(' \'--release\': will build project for release');
|
||||||
|
console.log(' \'--ant\': Default build, will build project with ant');
|
||||||
|
console.log(' \'--gradle\': will build project with gradle');
|
||||||
console.log(' \'--nobuild\': will skip build process (can be used with run command)');
|
console.log(' \'--nobuild\': will skip build process (can be used with run command)');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
};
|
||||||
|
|||||||
Vendored
+11
-7
@@ -48,7 +48,7 @@ module.exports.list = function() {
|
|||||||
* and launches it.
|
* and launches it.
|
||||||
* Returns a promise.
|
* Returns a promise.
|
||||||
*/
|
*/
|
||||||
module.exports.install = function(target) {
|
module.exports.install = function(target, buildResults) {
|
||||||
var launchName;
|
var launchName;
|
||||||
return this.list()
|
return this.list()
|
||||||
.then(function(device_list) {
|
.then(function(device_list) {
|
||||||
@@ -56,16 +56,20 @@ module.exports.install = function(target) {
|
|||||||
return Q.reject('ERROR: Failed to deploy to device, no devices found.');
|
return Q.reject('ERROR: Failed to deploy to device, no devices found.');
|
||||||
|
|
||||||
// default device
|
// default device
|
||||||
target = typeof target !== 'undefined' ? target : device_list[0];
|
target = target || device_list[0];
|
||||||
|
|
||||||
if (device_list.indexOf(target) < 0)
|
if (device_list.indexOf(target) < 0)
|
||||||
return Q.reject('ERROR: Unable to find target \'' + target + '\'.');
|
return Q.reject('ERROR: Unable to find target \'' + target + '\'.');
|
||||||
|
|
||||||
var apk_path = build.get_apk();
|
return build.detectArchitecture(target)
|
||||||
launchName = appinfo.getActivityName();
|
.then(function(arch) {
|
||||||
console.log('Installing app on device...');
|
var apk_path = build.findBestApkForArchitecture(buildResults, arch);
|
||||||
var cmd = 'adb -s ' + target + ' install -r "' + apk_path + '"';
|
launchName = appinfo.getActivityName();
|
||||||
return exec(cmd);
|
console.log('Using apk: ' + apk_path);
|
||||||
|
console.log('Installing app on device...');
|
||||||
|
var cmd = 'adb -s ' + target + ' install -r "' + apk_path + '"';
|
||||||
|
return exec(cmd);
|
||||||
|
});
|
||||||
}).then(function(output) {
|
}).then(function(output) {
|
||||||
if (output.match(/Failure/)) return Q.reject('ERROR: Failed to install apk to device: ' + output);
|
if (output.match(/Failure/)) return Q.reject('ERROR: Failed to install apk to device: ' + output);
|
||||||
|
|
||||||
|
|||||||
+9
-5
@@ -283,7 +283,7 @@ module.exports.create_image = function(name, target) {
|
|||||||
* If no started emulators are found, error out.
|
* If no started emulators are found, error out.
|
||||||
* Returns a promise.
|
* Returns a promise.
|
||||||
*/
|
*/
|
||||||
module.exports.install = function(target) {
|
module.exports.install = function(target, buildResults) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.list_started()
|
return this.list_started()
|
||||||
.then(function(emulator_list) {
|
.then(function(emulator_list) {
|
||||||
@@ -292,14 +292,18 @@ module.exports.install = function(target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// default emulator
|
// default emulator
|
||||||
target = typeof target !== 'undefined' ? target : emulator_list[0];
|
target = target || emulator_list[0];
|
||||||
if (emulator_list.indexOf(target) < 0) {
|
if (emulator_list.indexOf(target) < 0) {
|
||||||
return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
|
return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Installing app on emulator...');
|
return build.detectArchitecture(target)
|
||||||
var apk_path = build.get_apk();
|
.then(function(arch) {
|
||||||
return exec('adb -s ' + target + ' install -r "' + apk_path + '"');
|
var apk_path = build.findBestApkForArchitecture(buildResults, arch);
|
||||||
|
console.log('Installing app on emulator...');
|
||||||
|
console.log('Using apk: ' + apk_path);
|
||||||
|
return exec('adb -s ' + target + ' install -r "' + apk_path + '"');
|
||||||
|
});
|
||||||
}).then(function(output) {
|
}).then(function(output) {
|
||||||
if (output.match(/Failure/)) {
|
if (output.match(/Failure/)) {
|
||||||
return Q.reject('Failed to install apk to emulator: ' + output);
|
return Q.reject('Failed to install apk to emulator: ' + output);
|
||||||
|
|||||||
Vendored
+15
-15
@@ -33,16 +33,16 @@ var path = require('path'),
|
|||||||
* Returns a promise.
|
* Returns a promise.
|
||||||
*/
|
*/
|
||||||
module.exports.run = function(args) {
|
module.exports.run = function(args) {
|
||||||
var build_type;
|
var buildFlags = [];
|
||||||
var install_target;
|
var install_target;
|
||||||
|
|
||||||
for (var i=2; i<args.length; i++) {
|
for (var i=2; i<args.length; i++) {
|
||||||
if (args[i] == '--debug') {
|
if (args[i] == '--debug') {
|
||||||
build_type = '--debug';
|
buildFlags.push('--debug');
|
||||||
} else if (args[i] == '--release') {
|
} else if (args[i] == '--release') {
|
||||||
build_type = '--release';
|
buildFlags.push('--release');
|
||||||
} else if (args[i] == '--nobuild') {
|
} else if (args[i] == '--nobuild') {
|
||||||
build_type = '--nobuild';
|
buildFlags.push('--nobuild');
|
||||||
} else if (args[i] == '--device') {
|
} else if (args[i] == '--device') {
|
||||||
install_target = '--device';
|
install_target = '--device';
|
||||||
} else if (args[i] == '--emulator') {
|
} else if (args[i] == '--emulator') {
|
||||||
@@ -55,13 +55,13 @@ var path = require('path'),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.run(build_type).then(function() {
|
return build.run(buildFlags).then(function(buildResults) {
|
||||||
if (install_target == '--device') {
|
if (install_target == '--device') {
|
||||||
return device.install();
|
return device.install(null, buildResults);
|
||||||
} else if (install_target == '--emulator') {
|
} else if (install_target == '--emulator') {
|
||||||
return emulator.list_started().then(function(started) {
|
return emulator.list_started().then(function(started) {
|
||||||
var p = started && started.length > 0 ? Q() : emulator.start();
|
var p = started && started.length > 0 ? Q() : emulator.start();
|
||||||
return p.then(function() { emulator.install(); });
|
return p.then(function() { return emulator.install(null, buildResults); });
|
||||||
});
|
});
|
||||||
} else if (install_target) {
|
} else if (install_target) {
|
||||||
var devices, started_emulators, avds;
|
var devices, started_emulators, avds;
|
||||||
@@ -75,16 +75,16 @@ var path = require('path'),
|
|||||||
}).then(function(res) {
|
}).then(function(res) {
|
||||||
avds = res;
|
avds = res;
|
||||||
if (devices.indexOf(install_target) > -1) {
|
if (devices.indexOf(install_target) > -1) {
|
||||||
return device.install(install_target);
|
return device.install(install_target, buildResults);
|
||||||
} else if (started_emulators.indexOf(install_target) > -1) {
|
} else if (started_emulators.indexOf(install_target) > -1) {
|
||||||
return emulator.install(install_target);
|
return emulator.install(install_target, buildResults);
|
||||||
} else {
|
} else {
|
||||||
// if target emulator isn't started, then start it.
|
// if target emulator isn't started, then start it.
|
||||||
var emulator_ID;
|
var emulator_ID;
|
||||||
for(avd in avds) {
|
for(avd in avds) {
|
||||||
if(avds[avd].name == install_target) {
|
if(avds[avd].name == install_target) {
|
||||||
return emulator.start(install_target)
|
return emulator.start(install_target)
|
||||||
.then(function() { emulator.install(emulator_ID); });
|
.then(function() { emulator.install(emulator_ID, buildResults); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Q.reject('Target \'' + install_target + '\' not found, unable to run project');
|
return Q.reject('Target \'' + install_target + '\' not found, unable to run project');
|
||||||
@@ -96,13 +96,13 @@ var path = require('path'),
|
|||||||
.then(function(device_list) {
|
.then(function(device_list) {
|
||||||
if (device_list.length > 0) {
|
if (device_list.length > 0) {
|
||||||
console.log('WARNING : No target specified, deploying to device \'' + device_list[0] + '\'.');
|
console.log('WARNING : No target specified, deploying to device \'' + device_list[0] + '\'.');
|
||||||
return device.install(device_list[0]);
|
return device.install(device_list[0], buildResults);
|
||||||
} else {
|
} else {
|
||||||
return emulator.list_started()
|
return emulator.list_started()
|
||||||
.then(function(emulator_list) {
|
.then(function(emulator_list) {
|
||||||
if (emulator_list.length > 0) {
|
if (emulator_list.length > 0) {
|
||||||
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_list[0] + '\'.');
|
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_list[0] + '\'.');
|
||||||
return emulator.install(emulator_list[0]);
|
return emulator.install(emulator_list[0], buildResults);
|
||||||
} else {
|
} else {
|
||||||
console.log('WARNING : No started emulators found, starting an emulator.');
|
console.log('WARNING : No started emulators found, starting an emulator.');
|
||||||
return emulator.best_image()
|
return emulator.best_image()
|
||||||
@@ -111,7 +111,7 @@ var path = require('path'),
|
|||||||
return emulator.start(best_avd.name)
|
return emulator.start(best_avd.name)
|
||||||
.then(function(emulator_ID) {
|
.then(function(emulator_ID) {
|
||||||
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_ID + '\'.');
|
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_ID + '\'.');
|
||||||
return emulator.install(emulator_ID);
|
return emulator.install(emulator_ID, buildResults);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return emulator.start();
|
return emulator.start();
|
||||||
@@ -125,8 +125,8 @@ var path = require('path'),
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.help = function() {
|
module.exports.help = function(args) {
|
||||||
console.log('Usage: ' + path.relative(process.cwd(), args[0]) + ' [options]');
|
console.log('Usage: ' + path.relative(process.cwd(), args[1]) + ' [options]');
|
||||||
console.log('Build options :');
|
console.log('Build options :');
|
||||||
console.log(' --debug : Builds project in debug mode');
|
console.log(' --debug : Builds project in debug mode');
|
||||||
console.log(' --release : Builds project in release mode');
|
console.log(' --release : Builds project in release mode');
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ var run = require('./lib/run'),
|
|||||||
// Support basic help commands
|
// Support basic help commands
|
||||||
if (args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
|
if (args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
|
||||||
args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
|
args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
|
||||||
run.help();
|
run.help(args);
|
||||||
} else {
|
} else {
|
||||||
reqs.run().done(function() {
|
reqs.run().done(function() {
|
||||||
return run.run(args);
|
return run.run(args);
|
||||||
|
|||||||
@@ -20,6 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Coho updates this line:
|
// Coho updates this line:
|
||||||
var VERSION = "3.5.0-dev";
|
var VERSION = "3.6.4";
|
||||||
|
|
||||||
console.log(VERSION);
|
console.log(VERSION);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ package __ID__;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class __ACTIVITY__ extends CordovaActivity
|
public class __ACTIVITY__ extends CordovaActivity
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState)
|
public void onCreate(Bundle savedInstanceState)
|
||||||
@@ -30,8 +30,6 @@ public class __ACTIVITY__ extends CordovaActivity
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
super.init();
|
super.init();
|
||||||
// Set by <content src="index.html" /> in config.xml
|
// Set by <content src="index.html" /> in config.xml
|
||||||
super.loadUrl(Config.getStartUrl());
|
loadUrl(launchUrl);
|
||||||
//super.loadUrl("file:///android_asset/www/index.html");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
specific language governing permissions and limitations
|
specific language governing permissions and limitations
|
||||||
under the License.
|
under the License.
|
||||||
-->
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="__PACKAGE__" android:versionName="1.0" android:versionCode="1" android:hardwareAccelerated="true">
|
package="__PACKAGE__" android:versionName="1.0" android:versionCode="1" android:hardwareAccelerated="true">
|
||||||
<supports-screens
|
<supports-screens
|
||||||
android:largeScreens="true"
|
android:largeScreens="true"
|
||||||
android:normalScreens="true"
|
android:normalScreens="true"
|
||||||
@@ -32,10 +32,13 @@
|
|||||||
|
|
||||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||||
android:hardwareAccelerated="true">
|
android:hardwareAccelerated="true">
|
||||||
<activity android:name="__ACTIVITY__" android:label="@string/app_name" android:launchMode="singleTop"
|
<activity android:name="__ACTIVITY__"
|
||||||
|
android:label="@string/activity_name"
|
||||||
|
android:launchMode="singleTop"
|
||||||
android:theme="@android:style/Theme.Black.NoTitleBar"
|
android:theme="@android:style/Theme.Black.NoTitleBar"
|
||||||
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
|
||||||
<intent-filter>
|
<intent-filter android:label="@string/launcher_name">
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -43,4 +46,4 @@
|
|||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="__APILEVEL__"/>
|
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="__APILEVEL__"/>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
apply plugin: 'android'
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:0.10.+'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.multiarch=false
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile fileTree(dir: 'libs', include: '*.jar')
|
||||||
|
for (subproject in getProjectList()) {
|
||||||
|
compile project(subproject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
|
java.srcDirs = ['src']
|
||||||
|
resources.srcDirs = ['src']
|
||||||
|
aidl.srcDirs = ['src']
|
||||||
|
renderscript.srcDirs = ['src']
|
||||||
|
res.srcDirs = ['res']
|
||||||
|
assets.srcDirs = ['assets']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
versionCode Integer.parseInt("" + getVersionCodeFromManifest() + "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
compileSdkVersion 19
|
||||||
|
buildToolsVersion "19.0.0"
|
||||||
|
|
||||||
|
if (multiarch || System.env.BUILD_MULTIPLE_APKS) {
|
||||||
|
productFlavors {
|
||||||
|
armv7 {
|
||||||
|
versionCode defaultConfig.versionCode + 2
|
||||||
|
ndk {
|
||||||
|
abiFilters "armeabi-v7a", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x86 {
|
||||||
|
versionCode defaultConfig.versionCode + 4
|
||||||
|
ndk {
|
||||||
|
abiFilters "x86", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all {
|
||||||
|
ndk {
|
||||||
|
abiFilters "all", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_7
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_7
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
task wrapper(type: Wrapper) {
|
||||||
|
gradleVersion = '1.12'
|
||||||
|
}
|
||||||
|
|
||||||
|
def getVersionCodeFromManifest() {
|
||||||
|
def manifestFile = file(android.sourceSets.main.manifest.srcFile)
|
||||||
|
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
|
||||||
|
def matcher = pattern.matcher(manifestFile.getText())
|
||||||
|
matcher.find()
|
||||||
|
return Integer.parseInt(matcher.group(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
def getProjectList() {
|
||||||
|
def manifestFile = file("project.properties")
|
||||||
|
def pattern = Pattern.compile("android.library.reference.(\\d+)\\s*=\\s*(.*)")
|
||||||
|
def matcher = pattern.matcher(manifestFile.getText())
|
||||||
|
def projects = []
|
||||||
|
while (matcher.find()) {
|
||||||
|
projects.add(":" + matcher.group(2).replace("/",":"))
|
||||||
|
}
|
||||||
|
return projects
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
</path>
|
</path>
|
||||||
<echo message="Set jars path to: ${toString:project.all.jars.path}"/>
|
<echo message="Set jars path to: ${toString:project.all.jars.path}"/>
|
||||||
</target>
|
</target>
|
||||||
|
<!-- Rename AndroidManifest.xml so that Eclipse's import wizard doesn't detect ant-build as a project -->
|
||||||
<target name="-post-build">
|
<target name="-post-build">
|
||||||
<move file="ant-build/AndroidManifest.xml" tofile="ant-build/AndroidManifest.cordova.xml" failonerror="false" overwrite="true" />
|
<move file="ant-build/AndroidManifest.xml" tofile="ant-build/AndroidManifest.cordova.xml" failonerror="false" overwrite="true" />
|
||||||
<move file="CordovaLib/ant-build/AndroidManifest.xml" tofile="CordovaLib/ant-build/AndroidManifest.cordova.xml" failonerror="false" overwrite="true" />
|
<move file="CordovaLib/ant-build/AndroidManifest.xml" tofile="CordovaLib/ant-build/AndroidManifest.cordova.xml" failonerror="false" overwrite="true" />
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Non-project-specific build files:
|
||||||
|
build.xml
|
||||||
|
local.properties
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/gradle
|
||||||
|
# Ant builds
|
||||||
|
ant-built
|
||||||
|
ant-gen
|
||||||
|
# Eclipse builds
|
||||||
|
gen
|
||||||
|
out
|
||||||
|
# Gradle builds
|
||||||
|
/build
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
#
|
||||||
|
# To customize properties used by the Ant build system edit
|
||||||
|
# "ant.properties", and override values to adapt the script to your
|
||||||
|
# project structure.
|
||||||
|
#
|
||||||
|
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||||
|
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||||
|
|
||||||
|
android.library.reference.1=CordovaLib
|
||||||
|
# Project target.
|
||||||
|
target=This_gets_replaced
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<!-- App label shown within list of installed apps, battery & network usage screens. -->
|
||||||
<string name="app_name">__NAME__</string>
|
<string name="app_name">__NAME__</string>
|
||||||
|
<!-- App label shown on the launcher. -->
|
||||||
|
<string name="launcher_name">@string/app_name</string>
|
||||||
|
<!-- App label shown on the task switcher. -->
|
||||||
|
<string name="activity_name">@string/launcher_name</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
def getProjectList() {
|
||||||
|
def manifestFile = file("project.properties")
|
||||||
|
def pattern = Pattern.compile("android.library.reference.(\\d+)\\s*=\\s*(.*)")
|
||||||
|
def matcher = pattern.matcher(manifestFile.getText())
|
||||||
|
def projects = []
|
||||||
|
while (matcher.find()) {
|
||||||
|
projects.add(":" + matcher.group(2).replace("/",":"))
|
||||||
|
}
|
||||||
|
return projects
|
||||||
|
}
|
||||||
|
|
||||||
|
for (subproject in getProjectList()) {
|
||||||
|
include subproject
|
||||||
|
}
|
||||||
|
|
||||||
|
include ':'
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||||
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="gen"/>
|
<classpathentry kind="src" path="gen"/>
|
||||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
|
||||||
<classpathentry kind="output" path="bin/classes"/>
|
<classpathentry kind="output" path="bin/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
@@ -19,5 +19,5 @@
|
|||||||
-->
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.apache.cordova" android:versionName="1.0" android:versionCode="1">
|
package="org.apache.cordova" android:versionName="1.0" android:versionCode="1">
|
||||||
<uses-sdk android:minSdkVersion="8" />
|
<uses-sdk android:minSdkVersion="10" />
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
Vendored
+203
-38
@@ -1,5 +1,5 @@
|
|||||||
// Platform: android
|
// Platform: android
|
||||||
// 3.5.0-dev-81f9a00
|
// 8ca0f3b2b87e0759c5236b91c80f18438544409c
|
||||||
/*
|
/*
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
or more contributor license agreements. See the NOTICE file
|
or more contributor license agreements. See the NOTICE file
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
;(function() {
|
;(function() {
|
||||||
var CORDOVA_JS_BUILD_LABEL = '3.5.0-dev-81f9a00';
|
var PLATFORM_VERSION_BUILD_LABEL = '3.6.4';
|
||||||
// file: src/scripts/require.js
|
// file: src/scripts/require.js
|
||||||
|
|
||||||
/*jshint -W079 */
|
/*jshint -W079 */
|
||||||
@@ -175,7 +175,8 @@ function createEvent(type, data) {
|
|||||||
var cordova = {
|
var cordova = {
|
||||||
define:define,
|
define:define,
|
||||||
require:require,
|
require:require,
|
||||||
version:CORDOVA_JS_BUILD_LABEL,
|
version:PLATFORM_VERSION_BUILD_LABEL,
|
||||||
|
platformVersion:PLATFORM_VERSION_BUILD_LABEL,
|
||||||
platformId:platform.id,
|
platformId:platform.id,
|
||||||
/**
|
/**
|
||||||
* Methods to add/remove your own addEventListener hijacking on document + window.
|
* Methods to add/remove your own addEventListener hijacking on document + window.
|
||||||
@@ -265,7 +266,7 @@ var cordova = {
|
|||||||
try {
|
try {
|
||||||
cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
|
cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Error in error callback: " + callbackId + " = "+e);
|
console.log("Error in success callback: " + callbackId + " = "+e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -344,18 +345,18 @@ define("cordova/android/promptbasednativeapi", function(require, exports, module
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the API of ExposedJsApi.java, but uses prompt() to communicate.
|
* Implements the API of ExposedJsApi.java, but uses prompt() to communicate.
|
||||||
* This is used only on the 2.3 simulator, where addJavascriptInterface() is broken.
|
* This is used pre-JellyBean, where addJavascriptInterface() is disabled.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
exec: function(service, action, callbackId, argsJson) {
|
exec: function(bridgeSecret, service, action, callbackId, argsJson) {
|
||||||
return prompt(argsJson, 'gap:'+JSON.stringify([service, action, callbackId]));
|
return prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId]));
|
||||||
},
|
},
|
||||||
setNativeToJsBridgeMode: function(value) {
|
setNativeToJsBridgeMode: function(bridgeSecret, value) {
|
||||||
prompt(value, 'gap_bridge_mode:');
|
prompt(value, 'gap_bridge_mode:' + bridgeSecret);
|
||||||
},
|
},
|
||||||
retrieveJsMessages: function(fromOnlineEvent) {
|
retrieveJsMessages: function(bridgeSecret, fromOnlineEvent) {
|
||||||
return prompt(+fromOnlineEvent, 'gap_poll:');
|
return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -825,6 +826,7 @@ channel.createSticky('onNativeReady');
|
|||||||
channel.createSticky('onCordovaReady');
|
channel.createSticky('onCordovaReady');
|
||||||
|
|
||||||
// Event to indicate that all automatically loaded JS plugins are loaded and ready.
|
// Event to indicate that all automatically loaded JS plugins are loaded and ready.
|
||||||
|
// FIXME remove this
|
||||||
channel.createSticky('onPluginsReady');
|
channel.createSticky('onPluginsReady');
|
||||||
|
|
||||||
// Event to indicate that Cordova is ready
|
// Event to indicate that Cordova is ready
|
||||||
@@ -868,13 +870,10 @@ var cordova = require('cordova'),
|
|||||||
nativeApiProvider = require('cordova/android/nativeapiprovider'),
|
nativeApiProvider = require('cordova/android/nativeapiprovider'),
|
||||||
utils = require('cordova/utils'),
|
utils = require('cordova/utils'),
|
||||||
base64 = require('cordova/base64'),
|
base64 = require('cordova/base64'),
|
||||||
|
channel = require('cordova/channel'),
|
||||||
jsToNativeModes = {
|
jsToNativeModes = {
|
||||||
PROMPT: 0,
|
PROMPT: 0,
|
||||||
JS_OBJECT: 1,
|
JS_OBJECT: 1
|
||||||
// This mode is currently for benchmarking purposes only. It must be enabled
|
|
||||||
// on the native side through the ENABLE_LOCATION_CHANGE_EXEC_MODE
|
|
||||||
// constant within CordovaWebViewClient.java before it will work.
|
|
||||||
LOCATION_CHANGE: 2
|
|
||||||
},
|
},
|
||||||
nativeToJsModes = {
|
nativeToJsModes = {
|
||||||
// Polls for messages using the JS->Native bridge.
|
// Polls for messages using the JS->Native bridge.
|
||||||
@@ -894,9 +893,17 @@ var cordova = require('cordova'),
|
|||||||
jsToNativeBridgeMode, // Set lazily.
|
jsToNativeBridgeMode, // Set lazily.
|
||||||
nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT,
|
nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT,
|
||||||
pollEnabled = false,
|
pollEnabled = false,
|
||||||
messagesFromNative = [];
|
messagesFromNative = [],
|
||||||
|
bridgeSecret = -1;
|
||||||
|
|
||||||
function androidExec(success, fail, service, action, args) {
|
function androidExec(success, fail, service, action, args) {
|
||||||
|
if (bridgeSecret < 0) {
|
||||||
|
// If we ever catch this firing, we'll need to queue up exec()s
|
||||||
|
// and fire them once we get a secret. For now, I don't think
|
||||||
|
// it's possible for exec() to be called since plugins are parsed but
|
||||||
|
// not run until until after onNativeReady.
|
||||||
|
throw new Error('exec() called without bridgeSecret');
|
||||||
|
}
|
||||||
// Set default bridge modes if they have not already been set.
|
// Set default bridge modes if they have not already been set.
|
||||||
// By default, we use the failsafe, since addJavascriptInterface breaks too often
|
// By default, we use the failsafe, since addJavascriptInterface breaks too often
|
||||||
if (jsToNativeBridgeMode === undefined) {
|
if (jsToNativeBridgeMode === undefined) {
|
||||||
@@ -917,29 +924,35 @@ function androidExec(success, fail, service, action, args) {
|
|||||||
cordova.callbacks[callbackId] = {success:success, fail:fail};
|
cordova.callbacks[callbackId] = {success:success, fail:fail};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) {
|
var messages = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);
|
||||||
window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;
|
// If argsJson was received by Java as null, try again with the PROMPT bridge mode.
|
||||||
|
// This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666.
|
||||||
|
if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && messages === "@Null arguments.") {
|
||||||
|
androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
|
||||||
|
androidExec(success, fail, service, action, args);
|
||||||
|
androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
var messages = nativeApiProvider.get().exec(service, action, callbackId, argsJson);
|
androidExec.processMessages(messages, true);
|
||||||
// If argsJson was received by Java as null, try again with the PROMPT bridge mode.
|
|
||||||
// This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666.
|
|
||||||
if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && messages === "@Null arguments.") {
|
|
||||||
androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
|
|
||||||
androidExec(success, fail, service, action, args);
|
|
||||||
androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
androidExec.processMessages(messages, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
androidExec.init = function() {
|
||||||
|
bridgeSecret = +prompt('', 'gap_init:' + nativeToJsBridgeMode);
|
||||||
|
channel.onNativeReady.fire();
|
||||||
|
};
|
||||||
|
|
||||||
function pollOnceFromOnlineEvent() {
|
function pollOnceFromOnlineEvent() {
|
||||||
pollOnce(true);
|
pollOnce(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pollOnce(opt_fromOnlineEvent) {
|
function pollOnce(opt_fromOnlineEvent) {
|
||||||
var msg = nativeApiProvider.get().retrieveJsMessages(!!opt_fromOnlineEvent);
|
if (bridgeSecret < 0) {
|
||||||
|
// This can happen when the NativeToJsMessageQueue resets the online state on page transitions.
|
||||||
|
// We know there's nothing to retrieve, so no need to poll.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var msg = nativeApiProvider.get().retrieveJsMessages(bridgeSecret, !!opt_fromOnlineEvent);
|
||||||
androidExec.processMessages(msg);
|
androidExec.processMessages(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -989,7 +1002,10 @@ androidExec.setNativeToJsBridgeMode = function(mode) {
|
|||||||
|
|
||||||
nativeToJsBridgeMode = mode;
|
nativeToJsBridgeMode = mode;
|
||||||
// Tell the native side to switch modes.
|
// Tell the native side to switch modes.
|
||||||
nativeApiProvider.get().setNativeToJsBridgeMode(mode);
|
// Otherwise, it will be set by androidExec.init()
|
||||||
|
if (bridgeSecret >= 0) {
|
||||||
|
nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode);
|
||||||
|
}
|
||||||
|
|
||||||
if (mode == nativeToJsModes.POLLING) {
|
if (mode == nativeToJsModes.POLLING) {
|
||||||
pollEnabled = true;
|
pollEnabled = true;
|
||||||
@@ -1168,6 +1184,16 @@ function replaceNavigator(origNavigator) {
|
|||||||
for (var key in origNavigator) {
|
for (var key in origNavigator) {
|
||||||
if (typeof origNavigator[key] == 'function') {
|
if (typeof origNavigator[key] == 'function') {
|
||||||
newNavigator[key] = origNavigator[key].bind(origNavigator);
|
newNavigator[key] = origNavigator[key].bind(origNavigator);
|
||||||
|
} else {
|
||||||
|
(function(k) {
|
||||||
|
Object.defineProperty(newNavigator, k, {
|
||||||
|
get: function() {
|
||||||
|
return origNavigator[k];
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
})(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1245,6 +1271,121 @@ channel.join(function() {
|
|||||||
}, platformInitChannelsArray);
|
}, platformInitChannelsArray);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// file: src/common/init_b.js
|
||||||
|
define("cordova/init_b", function(require, exports, module) {
|
||||||
|
|
||||||
|
var channel = require('cordova/channel');
|
||||||
|
var cordova = require('cordova');
|
||||||
|
var platform = require('cordova/platform');
|
||||||
|
|
||||||
|
var platformInitChannelsArray = [channel.onDOMContentLoaded, channel.onNativeReady];
|
||||||
|
|
||||||
|
// setting exec
|
||||||
|
cordova.exec = require('cordova/exec');
|
||||||
|
|
||||||
|
function logUnfiredChannels(arr) {
|
||||||
|
for (var i = 0; i < arr.length; ++i) {
|
||||||
|
if (arr[i].state != 2) {
|
||||||
|
console.log('Channel not fired: ' + arr[i].type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setTimeout(function() {
|
||||||
|
if (channel.onDeviceReady.state != 2) {
|
||||||
|
console.log('deviceready has not fired after 5 seconds.');
|
||||||
|
logUnfiredChannels(platformInitChannelsArray);
|
||||||
|
logUnfiredChannels(channel.deviceReadyChannelsArray);
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
// Replace navigator before any modules are required(), to ensure it happens as soon as possible.
|
||||||
|
// We replace it so that properties that can't be clobbered can instead be overridden.
|
||||||
|
function replaceNavigator(origNavigator) {
|
||||||
|
var CordovaNavigator = function() {};
|
||||||
|
CordovaNavigator.prototype = origNavigator;
|
||||||
|
var newNavigator = new CordovaNavigator();
|
||||||
|
// This work-around really only applies to new APIs that are newer than Function.bind.
|
||||||
|
// Without it, APIs such as getGamepads() break.
|
||||||
|
if (CordovaNavigator.bind) {
|
||||||
|
for (var key in origNavigator) {
|
||||||
|
if (typeof origNavigator[key] == 'function') {
|
||||||
|
newNavigator[key] = origNavigator[key].bind(origNavigator);
|
||||||
|
} else {
|
||||||
|
(function(k) {
|
||||||
|
Object.defineProperty(newNavigator, k, {
|
||||||
|
get: function() {
|
||||||
|
return origNavigator[k];
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
})(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newNavigator;
|
||||||
|
}
|
||||||
|
if (window.navigator) {
|
||||||
|
window.navigator = replaceNavigator(window.navigator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window.console) {
|
||||||
|
window.console = {
|
||||||
|
log: function(){}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!window.console.warn) {
|
||||||
|
window.console.warn = function(msg) {
|
||||||
|
this.log("warn: " + msg);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register pause, resume and deviceready channels as events on document.
|
||||||
|
channel.onPause = cordova.addDocumentEventHandler('pause');
|
||||||
|
channel.onResume = cordova.addDocumentEventHandler('resume');
|
||||||
|
channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready');
|
||||||
|
|
||||||
|
// Listen for DOMContentLoaded and notify our channel subscribers.
|
||||||
|
if (document.readyState == 'complete' || document.readyState == 'interactive') {
|
||||||
|
channel.onDOMContentLoaded.fire();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
channel.onDOMContentLoaded.fire();
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _nativeReady is global variable that the native side can set
|
||||||
|
// to signify that the native code is ready. It is a global since
|
||||||
|
// it may be called before any cordova JS is ready.
|
||||||
|
if (window._nativeReady) {
|
||||||
|
channel.onNativeReady.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the platform-specific initialization.
|
||||||
|
platform.bootstrap && platform.bootstrap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create all cordova objects once native side is ready.
|
||||||
|
*/
|
||||||
|
channel.join(function() {
|
||||||
|
|
||||||
|
platform.initialize && platform.initialize();
|
||||||
|
|
||||||
|
// Fire event to notify that all objects are created
|
||||||
|
channel.onCordovaReady.fire();
|
||||||
|
|
||||||
|
// Fire onDeviceReady event once page has fully loaded, all
|
||||||
|
// constructors have run and cordova info has been received from native
|
||||||
|
// side.
|
||||||
|
channel.join(function() {
|
||||||
|
require('cordova').fireDocumentEvent('deviceready');
|
||||||
|
}, channel.deviceReadyChannelsArray);
|
||||||
|
|
||||||
|
}, platformInitChannelsArray);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// file: src/common/modulemapper.js
|
// file: src/common/modulemapper.js
|
||||||
@@ -1359,10 +1500,8 @@ module.exports = {
|
|||||||
exec = require('cordova/exec'),
|
exec = require('cordova/exec'),
|
||||||
modulemapper = require('cordova/modulemapper');
|
modulemapper = require('cordova/modulemapper');
|
||||||
|
|
||||||
// Tell the native code that a page change has occurred.
|
// Get the shared secret needed to use the bridge.
|
||||||
exec(null, null, 'PluginManager', 'startup', []);
|
exec.init();
|
||||||
// Tell the JS that the native side is ready.
|
|
||||||
channel.onNativeReady.fire();
|
|
||||||
|
|
||||||
// TODO: Extract this as a proper plugin.
|
// TODO: Extract this as a proper plugin.
|
||||||
modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app');
|
modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app');
|
||||||
@@ -1379,6 +1518,17 @@ module.exports = {
|
|||||||
cordova.addDocumentEventHandler('menubutton');
|
cordova.addDocumentEventHandler('menubutton');
|
||||||
cordova.addDocumentEventHandler('searchbutton');
|
cordova.addDocumentEventHandler('searchbutton');
|
||||||
|
|
||||||
|
function bindButtonChannel(buttonName) {
|
||||||
|
// generic button bind used for volumeup/volumedown buttons
|
||||||
|
var volumeButtonChannel = cordova.addDocumentEventHandler(buttonName + 'button');
|
||||||
|
volumeButtonChannel.onHasSubscribersChange = function() {
|
||||||
|
exec(null, null, "App", "overrideButton", [buttonName, this.numHandlers == 1]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Inject a listener for the volume buttons on the document.
|
||||||
|
bindButtonChannel('volumeup');
|
||||||
|
bindButtonChannel('volumedown');
|
||||||
|
|
||||||
// Let native code know we are all done on the JS side.
|
// Let native code know we are all done on the JS side.
|
||||||
// Native code will then un-hide the WebView.
|
// Native code will then un-hide the WebView.
|
||||||
channel.onCordovaReady.subscribe(function() {
|
channel.onCordovaReady.subscribe(function() {
|
||||||
@@ -1456,6 +1606,21 @@ module.exports = {
|
|||||||
exec(null, null, "App", "overrideBackbutton", [override]);
|
exec(null, null, "App", "overrideBackbutton", [override]);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the default behavior of the Android volume button.
|
||||||
|
* If overridden, when the volume button is pressed, the "volume[up|down]button"
|
||||||
|
* JavaScript event will be fired.
|
||||||
|
*
|
||||||
|
* Note: The user should not have to call this method. Instead, when the user
|
||||||
|
* registers for the "volume[up|down]button" event, this is automatically done.
|
||||||
|
*
|
||||||
|
* @param button volumeup, volumedown
|
||||||
|
* @param override T=override, F=cancel override
|
||||||
|
*/
|
||||||
|
overrideButton:function(button, override) {
|
||||||
|
exec(null, null, "App", "overrideButton", [button, override]);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exit and terminate the application.
|
* Exit and terminate the application.
|
||||||
*/
|
*/
|
||||||
@@ -1549,11 +1714,11 @@ function handlePluginsObject(path, moduleList, finishPluginLoading) {
|
|||||||
function findCordovaPath() {
|
function findCordovaPath() {
|
||||||
var path = null;
|
var path = null;
|
||||||
var scripts = document.getElementsByTagName('script');
|
var scripts = document.getElementsByTagName('script');
|
||||||
var term = 'cordova.js';
|
var term = '/cordova.js';
|
||||||
for (var n = scripts.length-1; n>-1; n--) {
|
for (var n = scripts.length-1; n>-1; n--) {
|
||||||
var src = scripts[n].src.replace(/\?.*$/, ''); // Strip any query param (CB-6007).
|
var src = scripts[n].src.replace(/\?.*$/, ''); // Strip any query param (CB-6007).
|
||||||
if (src.indexOf(term) == (src.length - term.length)) {
|
if (src.indexOf(term) == (src.length - term.length)) {
|
||||||
path = src.substring(0, src.length - term.length);
|
path = src.substring(0, src.length - term.length) + '/';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
or more contributor license agreements. See the NOTICE file
|
or more contributor license agreements. See the NOTICE file
|
||||||
@@ -19,21 +17,38 @@
|
|||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var build = require('./build'),
|
|
||||||
spawn = require('./spawn'),
|
|
||||||
path = require('path');
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Cleans the project using ant
|
buildscript {
|
||||||
* Returns a promise.
|
repositories {
|
||||||
*/
|
mavenCentral()
|
||||||
module.exports.run = function() {
|
}
|
||||||
var args = build.getAntArgs('clean');
|
|
||||||
return spawn('ant', args);
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:0.10.+'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.help = function() {
|
apply plugin: 'android-library'
|
||||||
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
|
|
||||||
console.log('Cleans the project directory.');
|
android {
|
||||||
process.exit(0);
|
compileSdkVersion 19
|
||||||
|
buildToolsVersion "19.0.0"
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_7
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_7
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
|
java.srcDirs = ['src']
|
||||||
|
resources.srcDirs = ['src']
|
||||||
|
aidl.srcDirs = ['src']
|
||||||
|
renderscript.srcDirs = ['src']
|
||||||
|
res.srcDirs = ['res']
|
||||||
|
assets.srcDirs = ['assets']
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,20 @@
|
|||||||
Apache Cordova Team
|
Apache Cordova Team
|
||||||
</author>
|
</author>
|
||||||
|
|
||||||
<access origin="*"/>
|
<!-- Allow access to arbitrary URLs in the Cordova WebView. This is a
|
||||||
|
development mode setting, and should be changed for production. -->
|
||||||
|
<access origin="http://*/*"/>
|
||||||
|
<access origin="https://*/*"/>
|
||||||
|
|
||||||
|
<!-- Grant certain URLs the ability to launch external applications. This
|
||||||
|
behaviour is set to match that of Cordova versions before 3.6.0, and
|
||||||
|
should be reviewed before launching an application in production. It
|
||||||
|
may be changed in the future. -->
|
||||||
|
<access origin="tel:*" launch-external="yes"/>
|
||||||
|
<access origin="geo:*" launch-external="yes"/>
|
||||||
|
<access origin="mailto:*" launch-external="yes"/>
|
||||||
|
<access origin="sms:*" launch-external="yes"/>
|
||||||
|
<access origin="market:*" launch-external="yes"/>
|
||||||
|
|
||||||
<!-- <content src="http://mysite.com/myapp.html" /> for external pages -->
|
<!-- <content src="http://mysite.com/myapp.html" /> for external pages -->
|
||||||
<content src="index.html" />
|
<content src="index.html" />
|
||||||
@@ -43,8 +56,4 @@
|
|||||||
<preference name="InAppBrowserStorageEnabled" value="true" />
|
<preference name="InAppBrowserStorageEnabled" value="true" />
|
||||||
<preference name="disallowOverscroll" value="true" />
|
<preference name="disallowOverscroll" value="true" />
|
||||||
-->
|
-->
|
||||||
<!-- This is required for native Android hooks -->
|
|
||||||
<feature name="App">
|
|
||||||
<param name="android-package" value="org.apache.cordova.App" />
|
|
||||||
</feature>
|
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Square, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
public enum ErrorCode {
|
public enum ErrorCode {
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Square, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Square, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
import com.squareup.okhttp.internal.Util;
|
import com.squareup.okhttp.internal.Util;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
@@ -46,16 +47,12 @@ public class App extends CordovaPlugin {
|
|||||||
/**
|
/**
|
||||||
* Sets the context of the Command. This can then be used to do things like
|
* Sets the context of the Command. This can then be used to do things like
|
||||||
* get file paths associated with the Activity.
|
* get file paths associated with the Activity.
|
||||||
*
|
|
||||||
* @param cordova The context of the main Activity.
|
|
||||||
* @param webView The CordovaWebView Cordova is running in.
|
|
||||||
*/
|
*/
|
||||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
@Override
|
||||||
super.initialize(cordova, webView);
|
public void pluginInitialize() {
|
||||||
this.initTelephonyReceiver();
|
this.initTelephonyReceiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
*
|
*
|
||||||
@@ -190,7 +187,11 @@ public class App extends CordovaPlugin {
|
|||||||
* Clear page history for the app.
|
* Clear page history for the app.
|
||||||
*/
|
*/
|
||||||
public void clearHistory() {
|
public void clearHistory() {
|
||||||
this.webView.clearHistory();
|
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
webView.clearHistory();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,7 +214,7 @@ public class App extends CordovaPlugin {
|
|||||||
*/
|
*/
|
||||||
public void overrideBackbutton(boolean override) {
|
public void overrideBackbutton(boolean override) {
|
||||||
LOG.i("App", "WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!");
|
LOG.i("App", "WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!");
|
||||||
webView.bindButton(override);
|
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, override);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -225,7 +226,12 @@ public class App extends CordovaPlugin {
|
|||||||
*/
|
*/
|
||||||
public void overrideButton(String button, boolean override) {
|
public void overrideButton(String button, boolean override) {
|
||||||
LOG.i("App", "WARNING: Volume Button Default Behavior will be overridden. The volume event will be fired!");
|
LOG.i("App", "WARNING: Volume Button Default Behavior will be overridden. The volume event will be fired!");
|
||||||
webView.bindButton(button, override);
|
if (button.equals("volumeup")) {
|
||||||
|
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, override);
|
||||||
|
}
|
||||||
|
else if (button.equals("volumedown")) {
|
||||||
|
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, override);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -234,7 +240,7 @@ public class App extends CordovaPlugin {
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public boolean isBackbuttonOverridden() {
|
public boolean isBackbuttonOverridden() {
|
||||||
return webView.isBackButtonBound();
|
return webView.isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,183 +19,34 @@
|
|||||||
|
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.util.List;
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.apache.cordova.LOG;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
import android.content.res.XmlResourceParser;
|
|
||||||
import android.graphics.Color;
|
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
@Deprecated // Use Whitelist, CordovaPrefences, etc. directly.
|
||||||
public class Config {
|
public class Config {
|
||||||
|
private static final String TAG = "Config";
|
||||||
|
|
||||||
public static final String TAG = "Config";
|
static ConfigXmlParser parser;
|
||||||
|
|
||||||
private Whitelist whitelist = new Whitelist();
|
private Config() {
|
||||||
private String startUrl;
|
}
|
||||||
|
|
||||||
private static Config self = null;
|
|
||||||
|
|
||||||
public static void init(Activity action) {
|
public static void init(Activity action) {
|
||||||
//Just re-initialize this! Seriously, we lose this all the time
|
parser = new ConfigXmlParser();
|
||||||
self = new Config(action);
|
parser.parse(action);
|
||||||
|
parser.getPreferences().setPreferencesBundle(action.getIntent().getExtras());
|
||||||
|
parser.getPreferences().copyIntoIntentExtras(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intended to be used for testing only; creates an empty configuration.
|
// Intended to be used for testing only; creates an empty configuration.
|
||||||
public static void init() {
|
public static void init() {
|
||||||
if (self == null) {
|
if (parser == null) {
|
||||||
self = new Config();
|
parser = new ConfigXmlParser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intended to be used for testing only; creates an empty configuration.
|
|
||||||
private Config() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private Config(Activity action) {
|
|
||||||
if (action == null) {
|
|
||||||
LOG.i("CordovaLog", "There is no activity. Is this on the lock screen?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// First checking the class namespace for config.xml
|
|
||||||
int id = action.getResources().getIdentifier("config", "xml", action.getClass().getPackage().getName());
|
|
||||||
if (id == 0) {
|
|
||||||
// If we couldn't find config.xml there, we'll look in the namespace from AndroidManifest.xml
|
|
||||||
id = action.getResources().getIdentifier("config", "xml", action.getPackageName());
|
|
||||||
if (id == 0) {
|
|
||||||
LOG.i("CordovaLog", "config.xml missing. Ignoring...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add implicitly allowed URLs
|
|
||||||
whitelist.addWhiteListEntry("file:///*", false);
|
|
||||||
whitelist.addWhiteListEntry("content:///*", false);
|
|
||||||
whitelist.addWhiteListEntry("data:*", false);
|
|
||||||
|
|
||||||
XmlResourceParser xml = action.getResources().getXml(id);
|
|
||||||
int eventType = -1;
|
|
||||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
|
||||||
if (eventType == XmlResourceParser.START_TAG) {
|
|
||||||
String strNode = xml.getName();
|
|
||||||
|
|
||||||
if (strNode.equals("access")) {
|
|
||||||
String origin = xml.getAttributeValue(null, "origin");
|
|
||||||
String subdomains = xml.getAttributeValue(null, "subdomains");
|
|
||||||
if (origin != null) {
|
|
||||||
whitelist.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strNode.equals("log")) {
|
|
||||||
String level = xml.getAttributeValue(null, "level");
|
|
||||||
Log.d(TAG, "The <log> tag is deprecated. Use <preference name=\"loglevel\" value=\"" + level + "\"/> instead.");
|
|
||||||
if (level != null) {
|
|
||||||
LOG.setLogLevel(level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strNode.equals("preference")) {
|
|
||||||
String name = xml.getAttributeValue(null, "name").toLowerCase(Locale.getDefault());
|
|
||||||
/* Java 1.6 does not support switch-based strings
|
|
||||||
Java 7 does, but we're using Dalvik, which is apparently not Java.
|
|
||||||
Since we're reading XML, this has to be an ugly if/else.
|
|
||||||
|
|
||||||
Also, due to cast issues, each of them has to call their separate putExtra!
|
|
||||||
Wheee!!! Isn't Java FUN!?!?!?
|
|
||||||
|
|
||||||
Note: We should probably pass in the classname for the variable splash on splashscreen!
|
|
||||||
*/
|
|
||||||
if (name.equalsIgnoreCase("LogLevel")) {
|
|
||||||
String level = xml.getAttributeValue(null, "value");
|
|
||||||
LOG.setLogLevel(level);
|
|
||||||
} else if (name.equalsIgnoreCase("SplashScreen")) {
|
|
||||||
String value = xml.getAttributeValue(null, "value");
|
|
||||||
int resource = 0;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
value = "splash";
|
|
||||||
}
|
|
||||||
resource = action.getResources().getIdentifier(value, "drawable", action.getClass().getPackage().getName());
|
|
||||||
|
|
||||||
action.getIntent().putExtra(name, resource);
|
|
||||||
}
|
|
||||||
else if(name.equalsIgnoreCase("BackgroundColor")) {
|
|
||||||
int value = xml.getAttributeIntValue(null, "value", Color.BLACK);
|
|
||||||
action.getIntent().putExtra(name, value);
|
|
||||||
}
|
|
||||||
else if(name.equalsIgnoreCase("LoadUrlTimeoutValue")) {
|
|
||||||
int value = xml.getAttributeIntValue(null, "value", 20000);
|
|
||||||
action.getIntent().putExtra(name, value);
|
|
||||||
}
|
|
||||||
else if(name.equalsIgnoreCase("SplashScreenDelay")) {
|
|
||||||
int value = xml.getAttributeIntValue(null, "value", 3000);
|
|
||||||
action.getIntent().putExtra(name, value);
|
|
||||||
}
|
|
||||||
else if(name.equalsIgnoreCase("KeepRunning"))
|
|
||||||
{
|
|
||||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
|
||||||
action.getIntent().putExtra(name, value);
|
|
||||||
}
|
|
||||||
else if(name.equalsIgnoreCase("InAppBrowserStorageEnabled"))
|
|
||||||
{
|
|
||||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
|
||||||
action.getIntent().putExtra(name, value);
|
|
||||||
}
|
|
||||||
else if(name.equalsIgnoreCase("DisallowOverscroll"))
|
|
||||||
{
|
|
||||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
|
||||||
action.getIntent().putExtra(name, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
String value = xml.getAttributeValue(null, "value");
|
|
||||||
action.getIntent().putExtra(name, value);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
LOG.i("CordovaLog", "Found preference for %s=%s", name, value);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
else if (strNode.equals("content")) {
|
|
||||||
String src = xml.getAttributeValue(null, "src");
|
|
||||||
|
|
||||||
LOG.i("CordovaLog", "Found start page location: %s", src);
|
|
||||||
|
|
||||||
if (src != null) {
|
|
||||||
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
|
|
||||||
Matcher matcher = schemeRegex.matcher(src);
|
|
||||||
if (matcher.find()) {
|
|
||||||
startUrl = src;
|
|
||||||
} else {
|
|
||||||
if (src.charAt(0) == '/') {
|
|
||||||
src = src.substring(1);
|
|
||||||
}
|
|
||||||
startUrl = "file:///android_asset/www/" + src;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
eventType = xml.next();
|
|
||||||
} catch (XmlPullParserException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add entry to approved list of URLs (whitelist)
|
* Add entry to approved list of URLs (whitelist)
|
||||||
*
|
*
|
||||||
@@ -203,11 +54,11 @@ public class Config {
|
|||||||
* @param subdomains T=include all subdomains under origin
|
* @param subdomains T=include all subdomains under origin
|
||||||
*/
|
*/
|
||||||
public static void addWhiteListEntry(String origin, boolean subdomains) {
|
public static void addWhiteListEntry(String origin, boolean subdomains) {
|
||||||
if (self == null) {
|
if (parser == null) {
|
||||||
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
|
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.whitelist.addWhiteListEntry(origin, subdomains);
|
parser.getInternalWhitelist().addWhiteListEntry(origin, subdomains);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,17 +68,55 @@ public class Config {
|
|||||||
* @return true if whitelisted
|
* @return true if whitelisted
|
||||||
*/
|
*/
|
||||||
public static boolean isUrlWhiteListed(String url) {
|
public static boolean isUrlWhiteListed(String url) {
|
||||||
if (self == null) {
|
if (parser == null) {
|
||||||
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
|
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return self.whitelist.isUrlWhiteListed(url);
|
return parser.getInternalWhitelist().isUrlWhiteListed(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if URL is in approved list of URLs to launch external applications.
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return true if whitelisted
|
||||||
|
*/
|
||||||
|
public static boolean isUrlExternallyWhiteListed(String url) {
|
||||||
|
if (parser == null) {
|
||||||
|
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return parser.getExternalWhitelist().isUrlWhiteListed(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getStartUrl() {
|
public static String getStartUrl() {
|
||||||
if (self == null || self.startUrl == null) {
|
if (parser == null) {
|
||||||
return "file:///android_asset/www/index.html";
|
return "file:///android_asset/www/index.html";
|
||||||
}
|
}
|
||||||
return self.startUrl;
|
return parser.getLaunchUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getErrorUrl() {
|
||||||
|
return parser.getPreferences().getString("errorurl", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Whitelist getWhitelist() {
|
||||||
|
return parser.getInternalWhitelist();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Whitelist getExternalWhitelist() {
|
||||||
|
return parser.getExternalWhitelist();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<PluginEntry> getPluginEntries() {
|
||||||
|
return parser.getPluginEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CordovaPreferences getPreferences() {
|
||||||
|
return parser.getPreferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isInitialized() {
|
||||||
|
return parser != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.cordova.LOG;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.res.XmlResourceParser;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class ConfigXmlParser {
|
||||||
|
private static String TAG = "ConfigXmlParser";
|
||||||
|
|
||||||
|
private String launchUrl = "file:///android_asset/www/index.html";
|
||||||
|
private CordovaPreferences prefs = new CordovaPreferences();
|
||||||
|
private Whitelist internalWhitelist = new Whitelist();
|
||||||
|
private Whitelist externalWhitelist = new Whitelist();
|
||||||
|
private ArrayList<PluginEntry> pluginEntries = new ArrayList<PluginEntry>(20);
|
||||||
|
|
||||||
|
public Whitelist getInternalWhitelist() {
|
||||||
|
return internalWhitelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Whitelist getExternalWhitelist() {
|
||||||
|
return externalWhitelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CordovaPreferences getPreferences() {
|
||||||
|
return prefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<PluginEntry> getPluginEntries() {
|
||||||
|
return pluginEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLaunchUrl() {
|
||||||
|
return launchUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void parse(Activity action) {
|
||||||
|
// First checking the class namespace for config.xml
|
||||||
|
int id = action.getResources().getIdentifier("config", "xml", action.getClass().getPackage().getName());
|
||||||
|
if (id == 0) {
|
||||||
|
// If we couldn't find config.xml there, we'll look in the namespace from AndroidManifest.xml
|
||||||
|
id = action.getResources().getIdentifier("config", "xml", action.getPackageName());
|
||||||
|
if (id == 0) {
|
||||||
|
LOG.e(TAG, "res/xml/config.xml is missing!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parse(action.getResources().getXml(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void parse(XmlResourceParser xml) {
|
||||||
|
int eventType = -1;
|
||||||
|
String service = "", pluginClass = "", paramType = "";
|
||||||
|
boolean onload = false;
|
||||||
|
boolean insideFeature = false;
|
||||||
|
ArrayList<String> urlMap = null;
|
||||||
|
|
||||||
|
// Add implicitly allowed URLs
|
||||||
|
internalWhitelist.addWhiteListEntry("file:///*", false);
|
||||||
|
internalWhitelist.addWhiteListEntry("content:///*", false);
|
||||||
|
internalWhitelist.addWhiteListEntry("data:*", false);
|
||||||
|
|
||||||
|
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||||
|
if (eventType == XmlResourceParser.START_TAG) {
|
||||||
|
String strNode = xml.getName();
|
||||||
|
if (strNode.equals("url-filter")) {
|
||||||
|
Log.w(TAG, "Plugin " + service + " is using deprecated tag <url-filter>");
|
||||||
|
if (urlMap == null) {
|
||||||
|
urlMap = new ArrayList<String>(2);
|
||||||
|
}
|
||||||
|
urlMap.add(xml.getAttributeValue(null, "value"));
|
||||||
|
} else if (strNode.equals("feature")) {
|
||||||
|
//Check for supported feature sets aka. plugins (Accelerometer, Geolocation, etc)
|
||||||
|
//Set the bit for reading params
|
||||||
|
insideFeature = true;
|
||||||
|
service = xml.getAttributeValue(null, "name");
|
||||||
|
}
|
||||||
|
else if (insideFeature && strNode.equals("param")) {
|
||||||
|
paramType = xml.getAttributeValue(null, "name");
|
||||||
|
if (paramType.equals("service")) // check if it is using the older service param
|
||||||
|
service = xml.getAttributeValue(null, "value");
|
||||||
|
else if (paramType.equals("package") || paramType.equals("android-package"))
|
||||||
|
pluginClass = xml.getAttributeValue(null,"value");
|
||||||
|
else if (paramType.equals("onload"))
|
||||||
|
onload = "true".equals(xml.getAttributeValue(null, "value"));
|
||||||
|
}
|
||||||
|
else if (strNode.equals("access")) {
|
||||||
|
String origin = xml.getAttributeValue(null, "origin");
|
||||||
|
String subdomains = xml.getAttributeValue(null, "subdomains");
|
||||||
|
boolean external = (xml.getAttributeValue(null, "launch-external") != null);
|
||||||
|
if (origin != null) {
|
||||||
|
if (external) {
|
||||||
|
externalWhitelist.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
||||||
|
} else {
|
||||||
|
if ("*".equals(origin)) {
|
||||||
|
// Special-case * origin to mean http and https when used for internal
|
||||||
|
// whitelist. This prevents external urls like sms: and geo: from being
|
||||||
|
// handled internally.
|
||||||
|
internalWhitelist.addWhiteListEntry("http://*/*", false);
|
||||||
|
internalWhitelist.addWhiteListEntry("https://*/*", false);
|
||||||
|
} else {
|
||||||
|
internalWhitelist.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strNode.equals("preference")) {
|
||||||
|
String name = xml.getAttributeValue(null, "name").toLowerCase(Locale.ENGLISH);
|
||||||
|
String value = xml.getAttributeValue(null, "value");
|
||||||
|
prefs.set(name, value);
|
||||||
|
}
|
||||||
|
else if (strNode.equals("content")) {
|
||||||
|
String src = xml.getAttributeValue(null, "src");
|
||||||
|
if (src != null) {
|
||||||
|
setStartUrl(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (eventType == XmlResourceParser.END_TAG)
|
||||||
|
{
|
||||||
|
String strNode = xml.getName();
|
||||||
|
if (strNode.equals("feature")) {
|
||||||
|
pluginEntries.add(new PluginEntry(service, pluginClass, onload, urlMap));
|
||||||
|
|
||||||
|
service = "";
|
||||||
|
pluginClass = "";
|
||||||
|
insideFeature = false;
|
||||||
|
onload = false;
|
||||||
|
urlMap = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
eventType = xml.next();
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setStartUrl(String src) {
|
||||||
|
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
|
||||||
|
Matcher matcher = schemeRegex.matcher(src);
|
||||||
|
if (matcher.find()) {
|
||||||
|
launchUrl = src;
|
||||||
|
} else {
|
||||||
|
if (src.charAt(0) == '/') {
|
||||||
|
src = src.substring(1);
|
||||||
|
}
|
||||||
|
launchUrl = "file:///android_asset/www/" + src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
@@ -37,7 +37,6 @@ import android.app.ProgressDialog;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -50,16 +49,16 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewParent;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
import android.webkit.WebViewClient;
|
import android.webkit.WebViewClient;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is the main Android activity that represents the Cordova
|
* This class is the main Android activity that represents the Cordova
|
||||||
* application. It should be extended by the user to load the specific
|
* application. It should be extended by the user to load the specific
|
||||||
* html file that contains the application.
|
* html file that contains the application.
|
||||||
*
|
*
|
||||||
* As an example:
|
* As an example:
|
||||||
@@ -76,7 +75,7 @@ import android.widget.LinearLayout;
|
|||||||
* super.onCreate(savedInstanceState);
|
* super.onCreate(savedInstanceState);
|
||||||
* super.init();
|
* super.init();
|
||||||
* // Load your application
|
* // Load your application
|
||||||
* super.loadUrl(Config.getStartUrl());
|
* loadUrl(launchUrl);
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
@@ -93,18 +92,16 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
// The webview for our app
|
// The webview for our app
|
||||||
protected CordovaWebView appView;
|
protected CordovaWebView appView;
|
||||||
|
|
||||||
|
@Deprecated // unused.
|
||||||
protected CordovaWebViewClient webViewClient;
|
protected CordovaWebViewClient webViewClient;
|
||||||
|
|
||||||
|
@Deprecated // Will be removed. Use findViewById() to retrieve views.
|
||||||
protected LinearLayout root;
|
protected LinearLayout root;
|
||||||
protected boolean cancelLoadUrl = false;
|
|
||||||
protected ProgressDialog spinnerDialog = null;
|
protected ProgressDialog spinnerDialog = null;
|
||||||
private final ExecutorService threadPool = Executors.newCachedThreadPool();
|
private final ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
|
|
||||||
// The initial URL for our app
|
|
||||||
// ie http://server/path/index.html#abc?query
|
|
||||||
//private String url = null;
|
|
||||||
|
|
||||||
private static int ACTIVITY_STARTING = 0;
|
private static int ACTIVITY_STARTING = 0;
|
||||||
private static int ACTIVITY_RUNNING = 1;
|
private static int ACTIVITY_RUNNING = 1;
|
||||||
private static int ACTIVITY_EXITING = 2;
|
private static int ACTIVITY_EXITING = 2;
|
||||||
@@ -114,10 +111,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
protected CordovaPlugin activityResultCallback = null;
|
protected CordovaPlugin activityResultCallback = null;
|
||||||
protected boolean activityResultKeepRunning;
|
protected boolean activityResultKeepRunning;
|
||||||
|
|
||||||
// Default background color for activity
|
|
||||||
// (this is not the color for the webview, which is set in HTML)
|
|
||||||
private int backgroundColor = Color.BLACK;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The variables below are used to cache some of the activity properties.
|
* The variables below are used to cache some of the activity properties.
|
||||||
*/
|
*/
|
||||||
@@ -135,17 +128,14 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
// when another application (activity) is started.
|
// when another application (activity) is started.
|
||||||
protected boolean keepRunning = true;
|
protected boolean keepRunning = true;
|
||||||
|
|
||||||
private int lastRequestCode;
|
|
||||||
|
|
||||||
private Object responseCode;
|
|
||||||
|
|
||||||
private Intent lastIntent;
|
|
||||||
|
|
||||||
private Object lastResponseCode;
|
|
||||||
|
|
||||||
private String initCallbackClass;
|
private String initCallbackClass;
|
||||||
|
|
||||||
private Object LOG_TAG;
|
// Read from config.xml:
|
||||||
|
protected CordovaPreferences preferences;
|
||||||
|
protected Whitelist internalWhitelist;
|
||||||
|
protected Whitelist externalWhitelist;
|
||||||
|
protected String launchUrl;
|
||||||
|
protected ArrayList<PluginEntry> pluginEntries;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the authentication token.
|
* Sets the authentication token.
|
||||||
@@ -207,58 +197,96 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the activity is first created.
|
* Called when the activity is first created.
|
||||||
*
|
|
||||||
* @param savedInstanceState
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
Config.init(this);
|
LOG.i(TAG, "Apache Cordova native platform version " + CordovaWebView.CORDOVA_VERSION + " is starting");
|
||||||
LOG.d(TAG, "CordovaActivity.onCreate()");
|
LOG.d(TAG, "CordovaActivity.onCreate()");
|
||||||
|
|
||||||
|
// need to activate preferences before super.onCreate to avoid "requestFeature() must be called before adding content" exception
|
||||||
|
loadConfig();
|
||||||
|
if(!preferences.getBoolean("ShowTitle", false))
|
||||||
|
{
|
||||||
|
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(preferences.getBoolean("SetFullscreen", false))
|
||||||
|
{
|
||||||
|
Log.d(TAG, "The SetFullscreen configuration is deprecated in favor of Fullscreen, and will be removed in a future version.");
|
||||||
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||||
|
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
} else if (preferences.getBoolean("Fullscreen", false)) {
|
||||||
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||||
|
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
} else {
|
||||||
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
|
||||||
|
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||||
|
}
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
if(savedInstanceState != null)
|
if(savedInstanceState != null)
|
||||||
{
|
{
|
||||||
initCallbackClass = savedInstanceState.getString("callbackClass");
|
initCallbackClass = savedInstanceState.getString("callbackClass");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if(!this.getBooleanProperty("ShowTitle", false))
|
|
||||||
{
|
|
||||||
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.getBooleanProperty("SetFullscreen", false))
|
@SuppressWarnings("deprecation")
|
||||||
{
|
protected void loadConfig() {
|
||||||
Log.d(TAG, "The SetFullscreen configuration is deprecated in favor of Fullscreen, and will be removed in a future version.");
|
ConfigXmlParser parser = new ConfigXmlParser();
|
||||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
parser.parse(this);
|
||||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
preferences = parser.getPreferences();
|
||||||
}
|
preferences.setPreferencesBundle(getIntent().getExtras());
|
||||||
else
|
preferences.copyIntoIntentExtras(this);
|
||||||
{
|
internalWhitelist = parser.getInternalWhitelist();
|
||||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
|
externalWhitelist = parser.getExternalWhitelist();
|
||||||
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
launchUrl = parser.getLaunchUrl();
|
||||||
}
|
pluginEntries = parser.getPluginEntries();
|
||||||
|
Config.parser = parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
protected void createViews() {
|
||||||
// This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket!
|
// This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket!
|
||||||
|
|
||||||
|
LOG.d(TAG, "CordovaActivity.createViews()");
|
||||||
|
|
||||||
Display display = getWindowManager().getDefaultDisplay();
|
Display display = getWindowManager().getDefaultDisplay();
|
||||||
int width = display.getWidth();
|
int width = display.getWidth();
|
||||||
int height = display.getHeight();
|
int height = display.getHeight();
|
||||||
|
|
||||||
root = new LinearLayoutSoftKeyboardDetect(this, width, height);
|
root = new LinearLayoutSoftKeyboardDetect(this, width, height);
|
||||||
root.setOrientation(LinearLayout.VERTICAL);
|
root.setOrientation(LinearLayout.VERTICAL);
|
||||||
root.setBackgroundColor(this.backgroundColor);
|
|
||||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
|
ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
|
||||||
|
|
||||||
// Setup the hardware volume controls to handle volume control
|
appView.setId(100);
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
appView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
1.0F));
|
||||||
|
|
||||||
|
// Add web view but make it invisible while loading URL
|
||||||
|
appView.setVisibility(View.INVISIBLE);
|
||||||
|
|
||||||
|
// need to remove appView from any existing parent before invoking root.addView(appView)
|
||||||
|
ViewParent parent = appView.getParent();
|
||||||
|
if ((parent != null) && (parent != root)) {
|
||||||
|
LOG.d(TAG, "removing appView from existing parent");
|
||||||
|
ViewGroup parentGroup = (ViewGroup) parent;
|
||||||
|
parentGroup.removeView(appView);
|
||||||
|
}
|
||||||
|
root.addView((View) appView);
|
||||||
|
setContentView(root);
|
||||||
|
|
||||||
|
int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
|
||||||
|
root.setBackgroundColor(backgroundColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Android activity.
|
* Get the Android activity.
|
||||||
*
|
|
||||||
* @return the Activity
|
|
||||||
*/
|
*/
|
||||||
public Activity getActivity() {
|
@Override public Activity getActivity() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,11 +309,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
* @param webView the default constructed web view object
|
* @param webView the default constructed web view object
|
||||||
*/
|
*/
|
||||||
protected CordovaWebViewClient makeWebViewClient(CordovaWebView webView) {
|
protected CordovaWebViewClient makeWebViewClient(CordovaWebView webView) {
|
||||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
|
return webView.makeWebViewClient(this);
|
||||||
return new CordovaWebViewClient(this, webView);
|
|
||||||
} else {
|
|
||||||
return new IceCreamCordovaWebViewClient(this, webView);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -297,86 +321,56 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
* @param webView the default constructed web view object
|
* @param webView the default constructed web view object
|
||||||
*/
|
*/
|
||||||
protected CordovaChromeClient makeChromeClient(CordovaWebView webView) {
|
protected CordovaChromeClient makeChromeClient(CordovaWebView webView) {
|
||||||
return new CordovaChromeClient(this, webView);
|
return webView.makeWebChromeClient(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and initialize web container with default web view objects.
|
|
||||||
*/
|
|
||||||
public void init() {
|
public void init() {
|
||||||
CordovaWebView webView = makeWebView();
|
this.init(appView, null, null);
|
||||||
this.init(webView, makeWebViewClient(webView), makeChromeClient(webView));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize web container with web view objects.
|
|
||||||
*
|
|
||||||
* @param webView
|
|
||||||
* @param webViewClient
|
|
||||||
* @param webChromeClient
|
|
||||||
*/
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
|
@Deprecated // Call init() instead and override makeWebView() to customize.
|
||||||
public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
|
public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
|
||||||
LOG.d(TAG, "CordovaActivity.init()");
|
LOG.d(TAG, "CordovaActivity.init()");
|
||||||
|
|
||||||
// Set up web container
|
appView = webView != null ? webView : makeWebView();
|
||||||
this.appView = webView;
|
if (appView.pluginManager == null) {
|
||||||
this.appView.setId(100);
|
appView.init(this, webViewClient != null ? webViewClient : makeWebViewClient(appView),
|
||||||
|
webChromeClient != null ? webChromeClient : makeChromeClient(appView),
|
||||||
this.appView.setWebViewClient(webViewClient);
|
pluginEntries, internalWhitelist, externalWhitelist, preferences);
|
||||||
this.appView.setWebChromeClient(webChromeClient);
|
|
||||||
webViewClient.setWebView(this.appView);
|
|
||||||
webChromeClient.setWebView(this.appView);
|
|
||||||
|
|
||||||
this.appView.setLayoutParams(new LinearLayout.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
1.0F));
|
|
||||||
|
|
||||||
if (this.getBooleanProperty("DisallowOverscroll", false)) {
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
|
|
||||||
this.appView.setOverScrollMode(CordovaWebView.OVER_SCROLL_NEVER);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add web view but make it invisible while loading URL
|
// TODO: Have the views set this themselves.
|
||||||
this.appView.setVisibility(View.INVISIBLE);
|
if (preferences.getBoolean("DisallowOverscroll", false)) {
|
||||||
this.root.addView(this.appView);
|
appView.setOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||||
setContentView(this.root);
|
}
|
||||||
|
createViews();
|
||||||
|
|
||||||
// Clear cancel flag
|
// TODO: Make this a preference (CB-6153)
|
||||||
this.cancelLoadUrl = false;
|
// Setup the hardware volume controls to handle volume control
|
||||||
|
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the url into the webview.
|
* Load the url into the webview.
|
||||||
*
|
|
||||||
* @param url
|
|
||||||
*/
|
*/
|
||||||
public void loadUrl(String url) {
|
public void loadUrl(String url) {
|
||||||
|
if (appView == null) {
|
||||||
// Init web view if not already done
|
init();
|
||||||
if (this.appView == null) {
|
|
||||||
this.init();
|
|
||||||
}
|
}
|
||||||
|
this.splashscreenTime = preferences.getInteger("SplashScreenDelay", this.splashscreenTime);
|
||||||
this.splashscreenTime = this.getIntegerProperty("SplashScreenDelay", this.splashscreenTime);
|
String splash = preferences.getString("SplashScreen", null);
|
||||||
if(this.splashscreenTime > 0)
|
if(this.splashscreenTime > 0 && splash != null)
|
||||||
{
|
{
|
||||||
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
this.splashscreen = getResources().getIdentifier(splash, "drawable", getClass().getPackage().getName());;
|
||||||
if(this.splashscreen != 0)
|
if(this.splashscreen != 0)
|
||||||
{
|
{
|
||||||
this.showSplashScreen(this.splashscreenTime);
|
this.showSplashScreen(this.splashscreenTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set backgroundColor
|
|
||||||
this.backgroundColor = this.getIntegerProperty("BackgroundColor", Color.BLACK);
|
|
||||||
this.root.setBackgroundColor(this.backgroundColor);
|
|
||||||
|
|
||||||
// If keepRunning
|
// If keepRunning
|
||||||
this.keepRunning = this.getBooleanProperty("KeepRunning", true);
|
this.keepRunning = preferences.getBoolean("KeepRunning", true);
|
||||||
|
|
||||||
//Check if the view is attached to anything
|
//Check if the view is attached to anything
|
||||||
if(appView.getParent() != null)
|
if(appView.getParent() != null)
|
||||||
@@ -407,18 +401,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
this.splashscreenTime = time;
|
this.splashscreenTime = time;
|
||||||
this.loadUrl(url);
|
this.loadUrl(url);
|
||||||
|
|
||||||
/*
|
|
||||||
// Init web view if not already done
|
|
||||||
if (this.appView == null) {
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.splashscreenTime = time;
|
|
||||||
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
|
||||||
this.showSplashScreen(this.splashscreenTime);
|
|
||||||
this.appView.loadUrl(url, time);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -429,10 +411,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
// If loadingDialog property, then show the App loading dialog for first page of app
|
// If loadingDialog property, then show the App loading dialog for first page of app
|
||||||
String loading = null;
|
String loading = null;
|
||||||
if ((this.appView == null) || !this.appView.canGoBack()) {
|
if ((this.appView == null) || !this.appView.canGoBack()) {
|
||||||
loading = this.getStringProperty("LoadingDialog", null);
|
loading = preferences.getString("LoadingDialog", null);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
loading = this.getStringProperty("LoadingPageDialog", null);
|
loading = preferences.getString("LoadingPageDialog", null);
|
||||||
}
|
}
|
||||||
if (loading != null) {
|
if (loading != null) {
|
||||||
|
|
||||||
@@ -454,22 +436,17 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel loadUrl before it has been loaded.
|
|
||||||
*/
|
|
||||||
// TODO NO-OP
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void cancelLoadUrl() {
|
public void cancelLoadUrl() {
|
||||||
this.cancelLoadUrl = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the resource cache.
|
* Clear the resource cache.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Call method on appView directly.
|
||||||
public void clearCache() {
|
public void clearCache() {
|
||||||
if (this.appView == null) {
|
if (appView == null) {
|
||||||
this.init();
|
init();
|
||||||
}
|
}
|
||||||
this.appView.clearCache(true);
|
this.appView.clearCache(true);
|
||||||
}
|
}
|
||||||
@@ -477,6 +454,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
/**
|
/**
|
||||||
* Clear web history in this web view.
|
* Clear web history in this web view.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Call method on appView directly.
|
||||||
public void clearHistory() {
|
public void clearHistory() {
|
||||||
this.appView.clearHistory();
|
this.appView.clearHistory();
|
||||||
}
|
}
|
||||||
@@ -486,6 +464,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
*
|
*
|
||||||
* @return true if we went back, false if we are already at top
|
* @return true if we went back, false if we are already at top
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Call method on appView directly.
|
||||||
public boolean backHistory() {
|
public boolean backHistory() {
|
||||||
if (this.appView != null) {
|
if (this.appView != null) {
|
||||||
return appView.backHistory();
|
return appView.backHistory();
|
||||||
@@ -493,116 +472,36 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
/**
|
|
||||||
* Called by the system when the device configuration changes while your activity is running.
|
|
||||||
*
|
|
||||||
* @param Configuration newConfig
|
|
||||||
*/
|
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
|
||||||
//don't reload the current page when the orientation is changed
|
|
||||||
super.onConfigurationChanged(newConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get boolean property for activity.
|
* Get boolean property for activity.
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @param defaultValue
|
|
||||||
* @return the boolean value of the named property
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Call method on preferences directly.
|
||||||
public boolean getBooleanProperty(String name, boolean defaultValue) {
|
public boolean getBooleanProperty(String name, boolean defaultValue) {
|
||||||
Bundle bundle = this.getIntent().getExtras();
|
return preferences.getBoolean(name, defaultValue);
|
||||||
if (bundle == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
name = name.toLowerCase(Locale.getDefault());
|
|
||||||
Boolean p;
|
|
||||||
try {
|
|
||||||
p = (Boolean) bundle.get(name);
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
String s = bundle.get(name).toString();
|
|
||||||
if ("true".equals(s)) {
|
|
||||||
p = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
p = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (p == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
return p.booleanValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get int property for activity.
|
* Get int property for activity.
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @param defaultValue
|
|
||||||
* @return the int value for the named property
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Call method on preferences directly.
|
||||||
public int getIntegerProperty(String name, int defaultValue) {
|
public int getIntegerProperty(String name, int defaultValue) {
|
||||||
Bundle bundle = this.getIntent().getExtras();
|
return preferences.getInteger(name, defaultValue);
|
||||||
if (bundle == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
name = name.toLowerCase(Locale.getDefault());
|
|
||||||
Integer p;
|
|
||||||
try {
|
|
||||||
p = (Integer) bundle.get(name);
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
p = Integer.parseInt(bundle.get(name).toString());
|
|
||||||
}
|
|
||||||
if (p == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
return p.intValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get string property for activity.
|
* Get string property for activity.
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @param defaultValue
|
|
||||||
* @return the String value for the named property
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Call method on preferences directly.
|
||||||
public String getStringProperty(String name, String defaultValue) {
|
public String getStringProperty(String name, String defaultValue) {
|
||||||
Bundle bundle = this.getIntent().getExtras();
|
return preferences.getString(name, defaultValue);
|
||||||
if (bundle == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
name = name.toLowerCase(Locale.getDefault());
|
|
||||||
String p = bundle.getString(name);
|
|
||||||
if (p == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get double property for activity.
|
* Get double property for activity.
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @param defaultValue
|
|
||||||
* @return the double value for the named property
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Call method on preferences directly.
|
||||||
public double getDoubleProperty(String name, double defaultValue) {
|
public double getDoubleProperty(String name, double defaultValue) {
|
||||||
Bundle bundle = this.getIntent().getExtras();
|
return preferences.getDouble(name, defaultValue);
|
||||||
if (bundle == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
name = name.toLowerCase(Locale.getDefault());
|
|
||||||
Double p;
|
|
||||||
try {
|
|
||||||
p = (Double) bundle.get(name);
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
p = Double.parseDouble(bundle.get(name).toString());
|
|
||||||
}
|
|
||||||
if (p == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
return p.doubleValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -665,10 +564,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
/**
|
/**
|
||||||
* Called when the system is about to start resuming a previous activity.
|
* Called when the system is about to start resuming a previous activity.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
|
|
||||||
@@ -691,10 +590,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
this.removeSplashScreen();
|
this.removeSplashScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
/**
|
/**
|
||||||
* Called when the activity receives a new intent
|
* Called when the activity receives a new intent
|
||||||
**/
|
**/
|
||||||
|
@Override
|
||||||
protected void onNewIntent(Intent intent) {
|
protected void onNewIntent(Intent intent) {
|
||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
//Forward to plugins
|
//Forward to plugins
|
||||||
@@ -702,22 +601,14 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
this.appView.onNewIntent(intent);
|
this.appView.onNewIntent(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
/**
|
/**
|
||||||
* Called when the activity will start interacting with the user.
|
* Called when the activity will start interacting with the user.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
//Reload the configuration
|
|
||||||
Config.init(this);
|
|
||||||
|
|
||||||
LOG.d(TAG, "Resuming the App");
|
LOG.d(TAG, "Resuming the App");
|
||||||
|
|
||||||
|
|
||||||
//Code to test CB-3064
|
|
||||||
String errorUrl = this.getStringProperty("ErrorUrl", null);
|
|
||||||
LOG.d(TAG, "CB-3064: The errorUrl is " + errorUrl);
|
|
||||||
|
|
||||||
if (this.activityState == ACTIVITY_STARTING) {
|
if (this.activityState == ACTIVITY_STARTING) {
|
||||||
this.activityState = ACTIVITY_RUNNING;
|
this.activityState = ACTIVITY_RUNNING;
|
||||||
return;
|
return;
|
||||||
@@ -726,6 +617,9 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
if (this.appView == null) {
|
if (this.appView == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Force window to have focus, so application always
|
||||||
|
// receive user input. Workaround for some devices (Samsung Galaxy Note 3 at least)
|
||||||
|
this.getWindow().getDecorView().requestFocus();
|
||||||
|
|
||||||
this.appView.handleResume(this.keepRunning, this.activityResultKeepRunning);
|
this.appView.handleResume(this.keepRunning, this.activityResultKeepRunning);
|
||||||
|
|
||||||
@@ -740,10 +634,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
/**
|
/**
|
||||||
* The final call you receive before your activity is destroyed.
|
* The final call you receive before your activity is destroyed.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
LOG.d(TAG, "CordovaActivity.onDestroy()");
|
LOG.d(TAG, "CordovaActivity.onDestroy()");
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
@@ -761,9 +655,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message to all plugins.
|
* Send a message to all plugins.
|
||||||
*
|
|
||||||
* @param id The message id
|
|
||||||
* @param data The message data
|
|
||||||
*/
|
*/
|
||||||
public void postMessage(String id, Object data) {
|
public void postMessage(String id, Object data) {
|
||||||
if (this.appView != null) {
|
if (this.appView != null) {
|
||||||
@@ -776,9 +667,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
* Add services to res/xml/plugins.xml instead.
|
* Add services to res/xml/plugins.xml instead.
|
||||||
*
|
*
|
||||||
* Add a class that implements a service.
|
* Add a class that implements a service.
|
||||||
*
|
|
||||||
* @param serviceType
|
|
||||||
* @param className
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void addService(String serviceType, String className) {
|
public void addService(String serviceType, String className) {
|
||||||
@@ -793,9 +681,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
*
|
*
|
||||||
* @param statement
|
* @param statement
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Call method on appView directly.
|
||||||
public void sendJavascript(String statement) {
|
public void sendJavascript(String statement) {
|
||||||
if (this.appView != null) {
|
if (this.appView != null) {
|
||||||
this.appView.jsMessageQueue.addJavaScript(statement);
|
this.appView.bridge.getMessageQueue().addJavaScript(statement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -859,7 +748,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
super.startActivityForResult(intent, requestCode);
|
super.startActivityForResult(intent, requestCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
/**
|
/**
|
||||||
* Called when an activity you launched exits, giving you the requestCode you started it with,
|
* Called when an activity you launched exits, giving you the requestCode you started it with,
|
||||||
* the resultCode it returned, and any additional data from it.
|
* the resultCode it returned, and any additional data from it.
|
||||||
@@ -869,6 +757,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||||
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
LOG.d(TAG, "Incoming Result");
|
LOG.d(TAG, "Incoming Result");
|
||||||
super.onActivityResult(requestCode, resultCode, intent);
|
super.onActivityResult(requestCode, resultCode, intent);
|
||||||
@@ -880,8 +769,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
return;
|
return;
|
||||||
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
|
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
|
||||||
Log.d(TAG, "result = " + result);
|
Log.d(TAG, "result = " + result);
|
||||||
// Uri filepath = Uri.parse("file://" + FileUtils.getRealPathFromURI(result, this));
|
|
||||||
// Log.d(TAG, "result = " + filepath);
|
|
||||||
mUploadMessage.onReceiveValue(result);
|
mUploadMessage.onReceiveValue(result);
|
||||||
mUploadMessage = null;
|
mUploadMessage = null;
|
||||||
}
|
}
|
||||||
@@ -914,8 +801,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
final CordovaActivity me = this;
|
final CordovaActivity me = this;
|
||||||
|
|
||||||
// If errorUrl specified, then load it
|
// If errorUrl specified, then load it
|
||||||
final String errorUrl = me.getStringProperty("errorUrl", null);
|
final String errorUrl = preferences.getString("errorUrl", null);
|
||||||
if ((errorUrl != null) && (errorUrl.startsWith("file://") || Config.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
if ((errorUrl != null) && (errorUrl.startsWith("file://") || internalWhitelist.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
||||||
|
|
||||||
// Load URL on UI thread
|
// Load URL on UI thread
|
||||||
me.runOnUiThread(new Runnable() {
|
me.runOnUiThread(new Runnable() {
|
||||||
@@ -942,11 +829,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Display an error dialog and optionally exit application.
|
* Display an error dialog and optionally exit application.
|
||||||
*
|
|
||||||
* @param title
|
|
||||||
* @param message
|
|
||||||
* @param button
|
|
||||||
* @param exit
|
|
||||||
*/
|
*/
|
||||||
public void displayError(final String title, final String message, final String button, final boolean exit) {
|
public void displayError(final String title, final String message, final String button, final boolean exit) {
|
||||||
final CordovaActivity me = this;
|
final CordovaActivity me = this;
|
||||||
@@ -977,17 +859,14 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if URL is in approved list of URLs to load.
|
* Determine if URL is in approved list of URLs to load.
|
||||||
*
|
|
||||||
* @param url
|
|
||||||
* @return true if the url is whitelisted
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Use whitelist object directly.
|
||||||
public boolean isUrlWhiteListed(String url) {
|
public boolean isUrlWhiteListed(String url) {
|
||||||
return Config.isUrlWhiteListed(url);
|
return internalWhitelist.isUrlWhiteListed(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hook in Cordova for menu plugins
|
* Hook in Cordova for menu plugins
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
@@ -1009,9 +888,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Activity context.
|
* Get Activity context.
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
* @deprecated
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
@@ -1029,6 +905,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
* @param clearHistory Clear the history stack, so new page becomes top of history
|
* @param clearHistory Clear the history stack, so new page becomes top of history
|
||||||
* @param params Parameters for new app
|
* @param params Parameters for new app
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Call method on appView directly.
|
||||||
public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
|
public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
|
||||||
if (this.appView != null) {
|
if (this.appView != null) {
|
||||||
appView.showWebPage(url, openExternal, clearHistory, params);
|
appView.showWebPage(url, openExternal, clearHistory, params);
|
||||||
@@ -1064,7 +941,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
root.setMinimumHeight(display.getHeight());
|
root.setMinimumHeight(display.getHeight());
|
||||||
root.setMinimumWidth(display.getWidth());
|
root.setMinimumWidth(display.getWidth());
|
||||||
root.setOrientation(LinearLayout.VERTICAL);
|
root.setOrientation(LinearLayout.VERTICAL);
|
||||||
root.setBackgroundColor(that.getIntegerProperty("backgroundColor", Color.BLACK));
|
root.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK));
|
||||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
|
ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
|
||||||
root.setBackgroundResource(that.splashscreen);
|
root.setBackgroundResource(that.splashscreen);
|
||||||
@@ -1142,7 +1019,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
else {
|
else {
|
||||||
// If the splash dialog is showing don't try to show it again
|
// If the splash dialog is showing don't try to show it again
|
||||||
if (this.splashDialog == null || !this.splashDialog.isShowing()) {
|
if (this.splashDialog == null || !this.splashDialog.isShowing()) {
|
||||||
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
String splashResource = preferences.getString("SplashScreen", null);
|
||||||
|
if (splashResource != null) {
|
||||||
|
splashscreen = getResources().getIdentifier(splashResource, "drawable", getClass().getPackage().getName());
|
||||||
|
}
|
||||||
this.showSplashScreen(this.splashscreenTime);
|
this.showSplashScreen(this.splashscreenTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import org.apache.cordova.PluginManager;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains APIs that the JS can call. All functions in here should also have
|
||||||
|
* an equivalent entry in CordovaChromeClient.java, and be added to
|
||||||
|
* cordova-js/lib/android/plugin/android/promptbasednativeapi.js
|
||||||
|
*/
|
||||||
|
public class CordovaBridge {
|
||||||
|
private static final String LOG_TAG = "CordovaBridge";
|
||||||
|
private PluginManager pluginManager;
|
||||||
|
private NativeToJsMessageQueue jsMessageQueue;
|
||||||
|
private volatile int expectedBridgeSecret = -1; // written by UI thread, read by JS thread.
|
||||||
|
private String loadedUrl;
|
||||||
|
|
||||||
|
public CordovaBridge(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) {
|
||||||
|
this.pluginManager = pluginManager;
|
||||||
|
this.jsMessageQueue = jsMessageQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String jsExec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
|
||||||
|
if (!verifySecret("exec()", bridgeSecret)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// If the arguments weren't received, send a message back to JS. It will switch bridge modes and try again. See CB-2666.
|
||||||
|
// We send a message meant specifically for this case. It starts with "@" so no other message can be encoded into the same string.
|
||||||
|
if (arguments == null) {
|
||||||
|
return "@Null arguments.";
|
||||||
|
}
|
||||||
|
|
||||||
|
jsMessageQueue.setPaused(true);
|
||||||
|
try {
|
||||||
|
// Tell the resourceApi what thread the JS is running on.
|
||||||
|
CordovaResourceApi.jsThread = Thread.currentThread();
|
||||||
|
|
||||||
|
pluginManager.exec(service, action, callbackId, arguments);
|
||||||
|
String ret = null;
|
||||||
|
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) {
|
||||||
|
ret = jsMessageQueue.popAndEncode(false);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return "";
|
||||||
|
} finally {
|
||||||
|
jsMessageQueue.setPaused(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void jsSetNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException {
|
||||||
|
if (!verifySecret("setNativeToJsBridgeMode()", bridgeSecret)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
jsMessageQueue.setBridgeMode(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String jsRetrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException {
|
||||||
|
if (!verifySecret("retrieveJsMessages()", bridgeSecret)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return jsMessageQueue.popAndEncode(fromOnlineEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verifySecret(String action, int bridgeSecret) throws IllegalAccessException {
|
||||||
|
if (!jsMessageQueue.isBridgeEnabled()) {
|
||||||
|
if (bridgeSecret == -1) {
|
||||||
|
Log.d(LOG_TAG, action + " call made before bridge was enabled.");
|
||||||
|
} else {
|
||||||
|
Log.d(LOG_TAG, "Ignoring " + action + " from previous page load.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Bridge secret wrong and bridge not due to it being from the previous page.
|
||||||
|
if (expectedBridgeSecret < 0 || bridgeSecret != expectedBridgeSecret) {
|
||||||
|
throw new IllegalAccessException();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called on page transitions */
|
||||||
|
void clearBridgeSecret() {
|
||||||
|
expectedBridgeSecret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called by cordova.js to initialize the bridge. */
|
||||||
|
int generateBridgeSecret() {
|
||||||
|
expectedBridgeSecret = (int)(Math.random() * Integer.MAX_VALUE);
|
||||||
|
return expectedBridgeSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset(String loadedUrl) {
|
||||||
|
jsMessageQueue.reset();
|
||||||
|
clearBridgeSecret();
|
||||||
|
this.loadedUrl = loadedUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String promptOnJsPrompt(String origin, String message, String defaultValue) {
|
||||||
|
if (defaultValue != null && defaultValue.length() > 3 && defaultValue.startsWith("gap:")) {
|
||||||
|
JSONArray array;
|
||||||
|
try {
|
||||||
|
array = new JSONArray(defaultValue.substring(4));
|
||||||
|
int bridgeSecret = array.getInt(0);
|
||||||
|
String service = array.getString(1);
|
||||||
|
String action = array.getString(2);
|
||||||
|
String callbackId = array.getString(3);
|
||||||
|
String r = jsExec(bridgeSecret, service, action, callbackId, message);
|
||||||
|
return r == null ? "" : r;
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// Sets the native->JS bridge mode.
|
||||||
|
else if (defaultValue != null && defaultValue.startsWith("gap_bridge_mode:")) {
|
||||||
|
try {
|
||||||
|
int bridgeSecret = Integer.parseInt(defaultValue.substring(16));
|
||||||
|
jsSetNativeToJsBridgeMode(bridgeSecret, Integer.parseInt(message));
|
||||||
|
} catch (NumberFormatException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// Polling for JavaScript messages
|
||||||
|
else if (defaultValue != null && defaultValue.startsWith("gap_poll:")) {
|
||||||
|
int bridgeSecret = Integer.parseInt(defaultValue.substring(9));
|
||||||
|
try {
|
||||||
|
String r = jsRetrieveJsMessages(bridgeSecret, "1".equals(message));
|
||||||
|
return r == null ? "" : r;
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
else if (defaultValue != null && defaultValue.startsWith("gap_init:")) {
|
||||||
|
// Protect against random iframes being able to talk through the bridge.
|
||||||
|
// Trust only file URLs and the start URL's domain.
|
||||||
|
// The extra origin.startsWith("http") is to protect against iframes with data: having "" as origin.
|
||||||
|
if (origin.startsWith("file:") || (origin.startsWith("http") && loadedUrl.startsWith(origin))) {
|
||||||
|
// Enable the bridge
|
||||||
|
int bridgeMode = Integer.parseInt(defaultValue.substring(9));
|
||||||
|
jsMessageQueue.setBridgeMode(bridgeMode);
|
||||||
|
// Tell JS the bridge secret.
|
||||||
|
int secret = generateBridgeSecret();
|
||||||
|
return ""+secret;
|
||||||
|
} else {
|
||||||
|
Log.e(LOG_TAG, "gap_init called from restricted origin: " + origin);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NativeToJsMessageQueue getMessageQueue() {
|
||||||
|
return jsMessageQueue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,8 +20,6 @@ package org.apache.cordova;
|
|||||||
|
|
||||||
import org.apache.cordova.CordovaInterface;
|
import org.apache.cordova.CordovaInterface;
|
||||||
import org.apache.cordova.LOG;
|
import org.apache.cordova.LOG;
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONException;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
@@ -59,7 +57,6 @@ import android.widget.RelativeLayout;
|
|||||||
public class CordovaChromeClient extends WebChromeClient {
|
public class CordovaChromeClient extends WebChromeClient {
|
||||||
|
|
||||||
public static final int FILECHOOSER_RESULTCODE = 5173;
|
public static final int FILECHOOSER_RESULTCODE = 5173;
|
||||||
private static final String LOG_TAG = "CordovaChromeClient";
|
|
||||||
private String TAG = "CordovaLog";
|
private String TAG = "CordovaLog";
|
||||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||||
protected CordovaInterface cordova;
|
protected CordovaInterface cordova;
|
||||||
@@ -71,31 +68,17 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
// File Chooser
|
// File Chooser
|
||||||
public ValueCallback<Uri> mUploadMessage;
|
public ValueCallback<Uri> mUploadMessage;
|
||||||
|
|
||||||
/**
|
@Deprecated
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param cordova
|
|
||||||
*/
|
|
||||||
public CordovaChromeClient(CordovaInterface cordova) {
|
public CordovaChromeClient(CordovaInterface cordova) {
|
||||||
this.cordova = cordova;
|
this.cordova = cordova;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param ctx
|
|
||||||
* @param app
|
|
||||||
*/
|
|
||||||
public CordovaChromeClient(CordovaInterface ctx, CordovaWebView app) {
|
public CordovaChromeClient(CordovaInterface ctx, CordovaWebView app) {
|
||||||
this.cordova = ctx;
|
this.cordova = ctx;
|
||||||
this.appView = app;
|
this.appView = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Deprecated
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param view
|
|
||||||
*/
|
|
||||||
public void setWebView(CordovaWebView view) {
|
public void setWebView(CordovaWebView view) {
|
||||||
this.appView = view;
|
this.appView = view;
|
||||||
}
|
}
|
||||||
@@ -107,6 +90,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
* @param url
|
* @param url
|
||||||
* @param message
|
* @param message
|
||||||
* @param result
|
* @param result
|
||||||
|
* @see Other implementation in the Dialogs plugin.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
|
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
|
||||||
@@ -150,6 +134,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
* @param url
|
* @param url
|
||||||
* @param message
|
* @param message
|
||||||
* @param result
|
* @param result
|
||||||
|
* @see Other implementation in the Dialogs plugin.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
|
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
|
||||||
@@ -199,63 +184,16 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
* Since we are hacking prompts for our own purposes, we should not be using them for
|
* Since we are hacking prompts for our own purposes, we should not be using them for
|
||||||
* this purpose, perhaps we should hack console.log to do this instead!
|
* this purpose, perhaps we should hack console.log to do this instead!
|
||||||
*
|
*
|
||||||
* @param view
|
* @see Other implementation in the Dialogs plugin.
|
||||||
* @param url
|
|
||||||
* @param message
|
|
||||||
* @param defaultValue
|
|
||||||
* @param result
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
|
public boolean onJsPrompt(WebView view, String origin, String message, String defaultValue, JsPromptResult result) {
|
||||||
|
// Unlike the @JavascriptInterface bridge, this method is always called on the UI thread.
|
||||||
// Security check to make sure any requests are coming from the page initially
|
String handledRet = appView.bridge.promptOnJsPrompt(origin, message, defaultValue);
|
||||||
// loaded in webview and not another loaded in an iframe.
|
if (handledRet != null) {
|
||||||
boolean reqOk = false;
|
result.confirm(handledRet);
|
||||||
if (url.startsWith("file://") || Config.isUrlWhiteListed(url)) {
|
} else {
|
||||||
reqOk = true;
|
// Returning false would also show a dialog, but the default one shows the origin (ugly).
|
||||||
}
|
|
||||||
|
|
||||||
// Calling PluginManager.exec() to call a native service using
|
|
||||||
// prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true]));
|
|
||||||
if (reqOk && defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) {
|
|
||||||
JSONArray array;
|
|
||||||
try {
|
|
||||||
array = new JSONArray(defaultValue.substring(4));
|
|
||||||
String service = array.getString(0);
|
|
||||||
String action = array.getString(1);
|
|
||||||
String callbackId = array.getString(2);
|
|
||||||
String r = this.appView.exposedJsApi.exec(service, action, callbackId, message);
|
|
||||||
result.confirm(r == null ? "" : r);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the native->JS bridge mode.
|
|
||||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_bridge_mode:")) {
|
|
||||||
try {
|
|
||||||
this.appView.exposedJsApi.setNativeToJsBridgeMode(Integer.parseInt(message));
|
|
||||||
result.confirm("");
|
|
||||||
} catch (NumberFormatException e){
|
|
||||||
result.confirm("");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Polling for JavaScript messages
|
|
||||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) {
|
|
||||||
String r = this.appView.exposedJsApi.retrieveJsMessages("1".equals(message));
|
|
||||||
result.confirm(r == null ? "" : r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do NO-OP so older code doesn't display dialog
|
|
||||||
else if (defaultValue != null && defaultValue.equals("gap_init:")) {
|
|
||||||
result.confirm("OK");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show dialog
|
|
||||||
else {
|
|
||||||
final JsPromptResult res = result;
|
final JsPromptResult res = result;
|
||||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
||||||
dlg.setMessage(message);
|
dlg.setMessage(message);
|
||||||
@@ -335,10 +273,10 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
this.appView.showCustomView(view, callback);
|
this.appView.showCustomView(view, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onHideCustomView() {
|
public void onHideCustomView() {
|
||||||
this.appView.hideCustomView();
|
this.appView.hideCustomView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
/**
|
/**
|
||||||
@@ -348,24 +286,24 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
*/
|
*/
|
||||||
public View getVideoLoadingProgressView() {
|
public View getVideoLoadingProgressView() {
|
||||||
|
|
||||||
if (mVideoProgressView == null) {
|
if (mVideoProgressView == null) {
|
||||||
// Create a new Loading view programmatically.
|
// Create a new Loading view programmatically.
|
||||||
|
|
||||||
// create the linear layout
|
// create the linear layout
|
||||||
LinearLayout layout = new LinearLayout(this.appView.getContext());
|
LinearLayout layout = new LinearLayout(this.appView.getContext());
|
||||||
layout.setOrientation(LinearLayout.VERTICAL);
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
||||||
layout.setLayoutParams(layoutParams);
|
layout.setLayoutParams(layoutParams);
|
||||||
// the proress bar
|
// the proress bar
|
||||||
ProgressBar bar = new ProgressBar(this.appView.getContext());
|
ProgressBar bar = new ProgressBar(this.appView.getContext());
|
||||||
LinearLayout.LayoutParams barLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
LinearLayout.LayoutParams barLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||||
barLayoutParams.gravity = Gravity.CENTER;
|
barLayoutParams.gravity = Gravity.CENTER;
|
||||||
bar.setLayoutParams(barLayoutParams);
|
bar.setLayoutParams(barLayoutParams);
|
||||||
layout.addView(bar);
|
layout.addView(bar);
|
||||||
|
|
||||||
mVideoProgressView = layout;
|
mVideoProgressView = layout;
|
||||||
}
|
}
|
||||||
return mVideoProgressView;
|
return mVideoProgressView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,20 +32,39 @@ import android.net.Uri;
|
|||||||
* Plugins must extend this class and override one of the execute methods.
|
* Plugins must extend this class and override one of the execute methods.
|
||||||
*/
|
*/
|
||||||
public class CordovaPlugin {
|
public class CordovaPlugin {
|
||||||
|
@Deprecated // This is never set.
|
||||||
public String id;
|
public String id;
|
||||||
public CordovaWebView webView; // WebView object
|
public CordovaWebView webView;
|
||||||
public CordovaInterface cordova;
|
public CordovaInterface cordova;
|
||||||
|
protected CordovaPreferences preferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param cordova The context of the main Activity.
|
* Call this after constructing to initialize the plugin.
|
||||||
* @param webView The associated CordovaWebView.
|
* Final because we want to be able to change args without breaking plugins.
|
||||||
*/
|
*/
|
||||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
public final void privateInitialize(CordovaInterface cordova, CordovaWebView webView, CordovaPreferences preferences) {
|
||||||
assert this.cordova == null;
|
assert this.cordova == null;
|
||||||
this.cordova = cordova;
|
this.cordova = cordova;
|
||||||
this.webView = webView;
|
this.webView = webView;
|
||||||
|
this.preferences = preferences;
|
||||||
|
initialize(cordova, webView);
|
||||||
|
pluginInitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after plugin construction and fields have been initialized.
|
||||||
|
* Prefer to use pluginInitialize instead since there is no value in
|
||||||
|
* having parameters on the initialize() function.
|
||||||
|
*/
|
||||||
|
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after plugin construction and fields have been initialized.
|
||||||
|
*/
|
||||||
|
protected void pluginInitialize() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the request.
|
* Executes the request.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.cordova.LOG;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
public class CordovaPreferences {
|
||||||
|
private HashMap<String, String> prefs = new HashMap<String, String>(20);
|
||||||
|
private Bundle preferencesBundleExtras;
|
||||||
|
|
||||||
|
public void setPreferencesBundle(Bundle extras) {
|
||||||
|
preferencesBundleExtras = extras;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String name, String value) {
|
||||||
|
prefs.put(name.toLowerCase(Locale.ENGLISH), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String name, boolean value) {
|
||||||
|
set(name, "" + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String name, int value) {
|
||||||
|
set(name, "" + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String name, double value) {
|
||||||
|
set(name, "" + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getAll() {
|
||||||
|
return prefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getBoolean(String name, boolean defaultValue) {
|
||||||
|
name = name.toLowerCase(Locale.ENGLISH);
|
||||||
|
String value = prefs.get(name);
|
||||||
|
if (value != null) {
|
||||||
|
return Boolean.parseBoolean(value);
|
||||||
|
} else if (preferencesBundleExtras != null) {
|
||||||
|
Object bundleValue = preferencesBundleExtras.get(name);
|
||||||
|
if (bundleValue instanceof String) {
|
||||||
|
return "true".equals(bundleValue);
|
||||||
|
}
|
||||||
|
// Gives a nice warning if type is wrong.
|
||||||
|
return preferencesBundleExtras.getBoolean(name, defaultValue);
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInteger(String name, int defaultValue) {
|
||||||
|
name = name.toLowerCase(Locale.ENGLISH);
|
||||||
|
String value = prefs.get(name);
|
||||||
|
if (value != null) {
|
||||||
|
// Use Integer.decode() can't handle it if the highest bit is set.
|
||||||
|
return (int)(long)Long.decode(value);
|
||||||
|
} else if (preferencesBundleExtras != null) {
|
||||||
|
Object bundleValue = preferencesBundleExtras.get(name);
|
||||||
|
if (bundleValue instanceof String) {
|
||||||
|
return Integer.valueOf((String)bundleValue);
|
||||||
|
}
|
||||||
|
// Gives a nice warning if type is wrong.
|
||||||
|
return preferencesBundleExtras.getInt(name, defaultValue);
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDouble(String name, double defaultValue) {
|
||||||
|
name = name.toLowerCase(Locale.ENGLISH);
|
||||||
|
String value = prefs.get(name);
|
||||||
|
if (value != null) {
|
||||||
|
return Double.valueOf(value);
|
||||||
|
} else if (preferencesBundleExtras != null) {
|
||||||
|
Object bundleValue = preferencesBundleExtras.get(name);
|
||||||
|
if (bundleValue instanceof String) {
|
||||||
|
return Double.valueOf((String)bundleValue);
|
||||||
|
}
|
||||||
|
// Gives a nice warning if type is wrong.
|
||||||
|
return preferencesBundleExtras.getDouble(name, defaultValue);
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(String name, String defaultValue) {
|
||||||
|
name = name.toLowerCase(Locale.ENGLISH);
|
||||||
|
String value = prefs.get(name);
|
||||||
|
if (value != null) {
|
||||||
|
return value;
|
||||||
|
} else if (preferencesBundleExtras != null && !"errorurl".equals(name)) {
|
||||||
|
Object bundleValue = preferencesBundleExtras.get(name);
|
||||||
|
if (bundleValue != null) {
|
||||||
|
return bundleValue.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugins should not rely on values within the intent since this does not work
|
||||||
|
// for apps with multiple webviews. Instead, they should retrieve prefs from the
|
||||||
|
// Config object associated with their webview.
|
||||||
|
public void copyIntoIntentExtras(Activity action) {
|
||||||
|
for (String name : prefs.keySet()) {
|
||||||
|
String value = prefs.get(name);
|
||||||
|
if (value == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (name.equals("loglevel")) {
|
||||||
|
LOG.setLogLevel(value);
|
||||||
|
} else if (name.equals("splashscreen")) {
|
||||||
|
// Note: We should probably pass in the classname for the variable splash on splashscreen!
|
||||||
|
int resource = action.getResources().getIdentifier(value, "drawable", action.getClass().getPackage().getName());
|
||||||
|
action.getIntent().putExtra(name, resource);
|
||||||
|
}
|
||||||
|
else if(name.equals("backgroundcolor")) {
|
||||||
|
int asInt = (int)(long)Long.decode(value);
|
||||||
|
action.getIntent().putExtra(name, asInt);
|
||||||
|
}
|
||||||
|
else if(name.equals("loadurltimeoutvalue")) {
|
||||||
|
int asInt = Integer.decode(value);
|
||||||
|
action.getIntent().putExtra(name, asInt);
|
||||||
|
}
|
||||||
|
else if(name.equals("splashscreendelay")) {
|
||||||
|
int asInt = Integer.decode(value);
|
||||||
|
action.getIntent().putExtra(name, asInt);
|
||||||
|
}
|
||||||
|
else if(name.equals("keeprunning"))
|
||||||
|
{
|
||||||
|
boolean asBool = Boolean.parseBoolean(value);
|
||||||
|
action.getIntent().putExtra(name, asBool);
|
||||||
|
}
|
||||||
|
else if(name.equals("inappbrowserstorageenabled"))
|
||||||
|
{
|
||||||
|
boolean asBool = Boolean.parseBoolean(value);
|
||||||
|
action.getIntent().putExtra(name, asBool);
|
||||||
|
}
|
||||||
|
else if(name.equals("disallowoverscroll"))
|
||||||
|
{
|
||||||
|
boolean asBool = Boolean.parseBoolean(value);
|
||||||
|
action.getIntent().putExtra(name, asBool);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
action.getIntent().putExtra(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// In the normal case, the intent extras are null until the first call to putExtra().
|
||||||
|
if (preferencesBundleExtras == null) {
|
||||||
|
preferencesBundleExtras = action.getIntent().getExtras();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -106,6 +106,7 @@ public class CordovaResourceApi {
|
|||||||
return threadCheckingEnabled;
|
return threadCheckingEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static int getUriType(Uri uri) {
|
public static int getUriType(Uri uri) {
|
||||||
assertNonRelative(uri);
|
assertNonRelative(uri);
|
||||||
String scheme = uri.getScheme();
|
String scheme = uri.getScheme();
|
||||||
@@ -199,6 +200,8 @@ public class CordovaResourceApi {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//This already exists
|
||||||
private String getMimeTypeFromPath(String path) {
|
private String getMimeTypeFromPath(String path) {
|
||||||
String extension = path;
|
String extension = path;
|
||||||
int lastDot = extension.lastIndexOf('.');
|
int lastDot = extension.lastIndexOf('.');
|
||||||
@@ -217,7 +220,7 @@ public class CordovaResourceApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a stream to the givne URI, also providing the MIME type & length.
|
* Opens a stream to the given URI, also providing the MIME type & length.
|
||||||
* @return Never returns null.
|
* @return Never returns null.
|
||||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||||
* resolved before being passed into this function.
|
* resolved before being passed into this function.
|
||||||
@@ -229,7 +232,7 @@ public class CordovaResourceApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a stream to the givne URI, also providing the MIME type & length.
|
* Opens a stream to the given URI, also providing the MIME type & length.
|
||||||
* @return Never returns null.
|
* @return Never returns null.
|
||||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||||
* resolved before being passed into this function.
|
* resolved before being passed into this function.
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
class CordovaUriHelper {
|
||||||
|
|
||||||
|
private static final String TAG = "CordovaUriHelper";
|
||||||
|
|
||||||
|
private CordovaWebView appView;
|
||||||
|
private CordovaInterface cordova;
|
||||||
|
|
||||||
|
CordovaUriHelper(CordovaInterface cdv, CordovaWebView webView)
|
||||||
|
{
|
||||||
|
appView = webView;
|
||||||
|
cordova = cdv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give the host application a chance to take over the control when a new url
|
||||||
|
* is about to be loaded in the current WebView.
|
||||||
|
*
|
||||||
|
* @param view The WebView that is initiating the callback.
|
||||||
|
* @param url The url to be loaded.
|
||||||
|
* @return true to override, false for default behavior
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
|
||||||
|
boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||||
|
// Give plugins the chance to handle the url
|
||||||
|
if (this.appView.pluginManager.onOverrideUrlLoading(url)) {
|
||||||
|
// Do nothing other than what the plugins wanted.
|
||||||
|
// If any returned true, then the request was handled.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if(url.startsWith("file://") | url.startsWith("data:"))
|
||||||
|
{
|
||||||
|
//This directory on WebKit/Blink based webviews contains SQLite databases!
|
||||||
|
//DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
|
||||||
|
return url.contains("app_webview");
|
||||||
|
}
|
||||||
|
else if (appView.getWhitelist().isUrlWhiteListed(url)) {
|
||||||
|
// Allow internal navigation
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (appView.getExternalWhitelist().isUrlWhiteListed(url))
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(url));
|
||||||
|
intent.addCategory(Intent.CATEGORY_BROWSABLE);
|
||||||
|
intent.setComponent(null);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
|
||||||
|
intent.setSelector(null);
|
||||||
|
}
|
||||||
|
this.cordova.getActivity().startActivity(intent);
|
||||||
|
return true;
|
||||||
|
} catch (android.content.ActivityNotFoundException e) {
|
||||||
|
LOG.e(TAG, "Error loading url " + url, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Intercept the request and do nothing with it -- block it
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,16 +21,11 @@ package org.apache.cordova;
|
|||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.apache.cordova.Config;
|
|
||||||
import org.apache.cordova.CordovaInterface;
|
|
||||||
import org.apache.cordova.LOG;
|
|
||||||
import org.apache.cordova.PluginManager;
|
|
||||||
import org.apache.cordova.PluginResult;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
@@ -38,8 +33,6 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -49,7 +42,6 @@ import android.view.Gravity;
|
|||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.webkit.WebBackForwardList;
|
import android.webkit.WebBackForwardList;
|
||||||
import android.webkit.WebHistoryItem;
|
import android.webkit.WebHistoryItem;
|
||||||
@@ -57,6 +49,7 @@ import android.webkit.WebChromeClient;
|
|||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -68,10 +61,9 @@ import android.widget.FrameLayout;
|
|||||||
public class CordovaWebView extends WebView {
|
public class CordovaWebView extends WebView {
|
||||||
|
|
||||||
public static final String TAG = "CordovaWebView";
|
public static final String TAG = "CordovaWebView";
|
||||||
public static final String CORDOVA_VERSION = "3.5.0-dev";
|
public static final String CORDOVA_VERSION = "3.6.4";
|
||||||
|
|
||||||
private ArrayList<Integer> keyDownCodes = new ArrayList<Integer>();
|
private HashSet<Integer> boundKeyCodes = new HashSet<Integer>();
|
||||||
private ArrayList<Integer> keyUpCodes = new ArrayList<Integer>();
|
|
||||||
|
|
||||||
public PluginManager pluginManager;
|
public PluginManager pluginManager;
|
||||||
private boolean paused;
|
private boolean paused;
|
||||||
@@ -82,30 +74,26 @@ public class CordovaWebView extends WebView {
|
|||||||
/** Activities and other important classes **/
|
/** Activities and other important classes **/
|
||||||
private CordovaInterface cordova;
|
private CordovaInterface cordova;
|
||||||
CordovaWebViewClient viewClient;
|
CordovaWebViewClient viewClient;
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private CordovaChromeClient chromeClient;
|
private CordovaChromeClient chromeClient;
|
||||||
|
|
||||||
private String url;
|
|
||||||
|
|
||||||
// Flag to track that a loadUrl timeout occurred
|
// Flag to track that a loadUrl timeout occurred
|
||||||
int loadUrlTimeout = 0;
|
int loadUrlTimeout = 0;
|
||||||
|
|
||||||
private boolean bound;
|
|
||||||
|
|
||||||
private boolean handleButton = false;
|
|
||||||
|
|
||||||
private long lastMenuEventTime = 0;
|
private long lastMenuEventTime = 0;
|
||||||
|
|
||||||
NativeToJsMessageQueue jsMessageQueue;
|
CordovaBridge bridge;
|
||||||
ExposedJsApi exposedJsApi;
|
|
||||||
|
|
||||||
/** custom view created by the browser (a video player for example) */
|
/** custom view created by the browser (a video player for example) */
|
||||||
private View mCustomView;
|
private View mCustomView;
|
||||||
private WebChromeClient.CustomViewCallback mCustomViewCallback;
|
private WebChromeClient.CustomViewCallback mCustomViewCallback;
|
||||||
|
|
||||||
private ActivityResult mResult = null;
|
|
||||||
|
|
||||||
private CordovaResourceApi resourceApi;
|
private CordovaResourceApi resourceApi;
|
||||||
|
private Whitelist internalWhitelist;
|
||||||
|
private Whitelist externalWhitelist;
|
||||||
|
|
||||||
|
// The URL passed to loadUrl(), not necessarily the URL of the current page.
|
||||||
|
String loadedUrl;
|
||||||
|
private CordovaPreferences preferences;
|
||||||
|
|
||||||
class ActivityResult {
|
class ActivityResult {
|
||||||
|
|
||||||
@@ -128,118 +116,69 @@ public class CordovaWebView extends WebView {
|
|||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
Gravity.CENTER);
|
Gravity.CENTER);
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
*/
|
|
||||||
public CordovaWebView(Context context) {
|
public CordovaWebView(Context context) {
|
||||||
super(context);
|
this(context, null);
|
||||||
if (CordovaInterface.class.isInstance(context))
|
|
||||||
{
|
|
||||||
this.cordova = (CordovaInterface) context;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.d(TAG, "Your activity must implement CordovaInterface to work");
|
|
||||||
}
|
|
||||||
this.loadConfiguration();
|
|
||||||
this.setup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param attrs
|
|
||||||
*/
|
|
||||||
public CordovaWebView(Context context, AttributeSet attrs) {
|
public CordovaWebView(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
if (CordovaInterface.class.isInstance(context))
|
|
||||||
{
|
|
||||||
this.cordova = (CordovaInterface) context;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.d(TAG, "Your activity must implement CordovaInterface to work");
|
|
||||||
}
|
|
||||||
this.setWebChromeClient(new CordovaChromeClient(this.cordova, this));
|
|
||||||
this.initWebViewClient(this.cordova);
|
|
||||||
this.loadConfiguration();
|
|
||||||
this.setup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Deprecated
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param attrs
|
|
||||||
* @param defStyle
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public CordovaWebView(Context context, AttributeSet attrs, int defStyle) {
|
public CordovaWebView(Context context, AttributeSet attrs, int defStyle) {
|
||||||
super(context, attrs, defStyle);
|
super(context, attrs, defStyle);
|
||||||
if (CordovaInterface.class.isInstance(context))
|
|
||||||
{
|
|
||||||
this.cordova = (CordovaInterface) context;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.d(TAG, "Your activity must implement CordovaInterface to work");
|
|
||||||
}
|
|
||||||
this.setWebChromeClient(new CordovaChromeClient(this.cordova, this));
|
|
||||||
this.loadConfiguration();
|
|
||||||
this.setup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param attrs
|
|
||||||
* @param defStyle
|
|
||||||
* @param privateBrowsing
|
|
||||||
*/
|
|
||||||
@TargetApi(11)
|
@TargetApi(11)
|
||||||
|
@Deprecated
|
||||||
public CordovaWebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) {
|
public CordovaWebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) {
|
||||||
super(context, attrs, defStyle, privateBrowsing);
|
super(context, attrs, defStyle, privateBrowsing);
|
||||||
if (CordovaInterface.class.isInstance(context))
|
|
||||||
{
|
|
||||||
this.cordova = (CordovaInterface) context;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.d(TAG, "Your activity must implement CordovaInterface to work");
|
|
||||||
}
|
|
||||||
this.setWebChromeClient(new CordovaChromeClient(this.cordova));
|
|
||||||
this.initWebViewClient(this.cordova);
|
|
||||||
this.loadConfiguration();
|
|
||||||
this.setup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Use two-phase init so that the control will work with XML layouts.
|
||||||
* set the WebViewClient, but provide special case handling for IceCreamSandwich.
|
public void init(CordovaInterface cordova, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient,
|
||||||
*/
|
List<PluginEntry> pluginEntries, Whitelist internalWhitelist, Whitelist externalWhitelist,
|
||||||
private void initWebViewClient(CordovaInterface cordova) {
|
CordovaPreferences preferences) {
|
||||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB ||
|
if (this.cordova != null) {
|
||||||
android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.JELLY_BEAN_MR1)
|
throw new IllegalStateException();
|
||||||
{
|
|
||||||
this.setWebViewClient(new CordovaWebViewClient(this.cordova, this));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.setWebViewClient(new IceCreamCordovaWebViewClient(this.cordova, this));
|
|
||||||
}
|
}
|
||||||
|
this.cordova = cordova;
|
||||||
|
this.viewClient = webViewClient;
|
||||||
|
this.chromeClient = webChromeClient;
|
||||||
|
this.internalWhitelist = internalWhitelist;
|
||||||
|
this.externalWhitelist = externalWhitelist;
|
||||||
|
this.preferences = preferences;
|
||||||
|
super.setWebChromeClient(webChromeClient);
|
||||||
|
super.setWebViewClient(webViewClient);
|
||||||
|
|
||||||
|
pluginManager = new PluginManager(this, this.cordova, pluginEntries);
|
||||||
|
bridge = new CordovaBridge(pluginManager, new NativeToJsMessageQueue(this, cordova));
|
||||||
|
resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
|
||||||
|
|
||||||
|
pluginManager.addService("App", "org.apache.cordova.App");
|
||||||
|
initWebViewSettings();
|
||||||
|
exposeJsInterface();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize webview.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@SuppressLint("NewApi")
|
private void initIfNecessary() {
|
||||||
private void setup() {
|
if (pluginManager == null) {
|
||||||
|
Log.w(TAG, "CordovaWebView.init() was not called. This will soon be required.");
|
||||||
|
// Before the refactor to a two-phase init, the Context needed to implement CordovaInterface.
|
||||||
|
CordovaInterface cdv = (CordovaInterface)getContext();
|
||||||
|
if (!Config.isInitialized()) {
|
||||||
|
Config.init(cdv.getActivity());
|
||||||
|
}
|
||||||
|
init(cdv, makeWebViewClient(cdv), makeWebChromeClient(cdv), Config.getPluginEntries(), Config.getWhitelist(), Config.getExternalWhitelist(), Config.getPreferences());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private void initWebViewSettings() {
|
||||||
this.setInitialScale(0);
|
this.setInitialScale(0);
|
||||||
this.setVerticalScrollBarEnabled(false);
|
this.setVerticalScrollBarEnabled(false);
|
||||||
|
// TODO: The Activity is the one that should call requestFocus().
|
||||||
if (shouldRequestFocusOnInit()) {
|
if (shouldRequestFocusOnInit()) {
|
||||||
this.requestFocusFromTouch();
|
this.requestFocusFromTouch();
|
||||||
}
|
}
|
||||||
@@ -280,31 +219,17 @@ public class CordovaWebView extends WebView {
|
|||||||
Level16Apis.enableUniversalAccess(settings);
|
Level16Apis.enableUniversalAccess(settings);
|
||||||
// Enable database
|
// Enable database
|
||||||
// We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16
|
// We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16
|
||||||
String databasePath = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
String databasePath = getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||||
settings.setDatabaseEnabled(true);
|
settings.setDatabaseEnabled(true);
|
||||||
settings.setDatabasePath(databasePath);
|
settings.setDatabasePath(databasePath);
|
||||||
|
|
||||||
|
|
||||||
//Determine whether we're in debug or release mode, and turn on Debugging!
|
//Determine whether we're in debug or release mode, and turn on Debugging!
|
||||||
try {
|
ApplicationInfo appInfo = getContext().getApplicationContext().getApplicationInfo();
|
||||||
final String packageName = this.cordova.getActivity().getPackageName();
|
if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 &&
|
||||||
final PackageManager pm = this.cordova.getActivity().getPackageManager();
|
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
||||||
ApplicationInfo appInfo;
|
enableRemoteDebugging();
|
||||||
|
}
|
||||||
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
|
|
||||||
|
|
||||||
if((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 &&
|
|
||||||
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT)
|
|
||||||
{
|
|
||||||
setWebContentsDebuggingEnabled(true);
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
Log.d(TAG, "You have one job! To turn on Remote Web Debugging! YOU HAVE FAILED! ");
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
Log.d(TAG, "This should never happen: Your application's package can't be found.");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.setGeolocationDatabasePath(databasePath);
|
settings.setGeolocationDatabasePath(databasePath);
|
||||||
|
|
||||||
@@ -317,13 +242,12 @@ public class CordovaWebView extends WebView {
|
|||||||
// Enable AppCache
|
// Enable AppCache
|
||||||
// Fix for CB-2282
|
// Fix for CB-2282
|
||||||
settings.setAppCacheMaxSize(5 * 1048576);
|
settings.setAppCacheMaxSize(5 * 1048576);
|
||||||
String pathToCache = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
settings.setAppCachePath(databasePath);
|
||||||
settings.setAppCachePath(pathToCache);
|
|
||||||
settings.setAppCacheEnabled(true);
|
settings.setAppCacheEnabled(true);
|
||||||
|
|
||||||
// Fix for CB-1405
|
// Fix for CB-1405
|
||||||
// Google issue 4641
|
// Google issue 4641
|
||||||
this.updateUserAgentString();
|
settings.getUserAgentString();
|
||||||
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
|
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
|
||||||
@@ -331,18 +255,33 @@ public class CordovaWebView extends WebView {
|
|||||||
this.receiver = new BroadcastReceiver() {
|
this.receiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
updateUserAgentString();
|
getSettings().getUserAgentString();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.cordova.getActivity().registerReceiver(this.receiver, intentFilter);
|
getContext().registerReceiver(this.receiver, intentFilter);
|
||||||
}
|
}
|
||||||
// end CB-1405
|
// end CB-1405
|
||||||
|
}
|
||||||
|
|
||||||
pluginManager = new PluginManager(this, this.cordova);
|
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||||
jsMessageQueue = new NativeToJsMessageQueue(this, cordova);
|
private void enableRemoteDebugging() {
|
||||||
exposedJsApi = new ExposedJsApi(pluginManager, jsMessageQueue);
|
try {
|
||||||
resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
|
WebView.setWebContentsDebuggingEnabled(true);
|
||||||
exposeJsInterface();
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.d(TAG, "You have one job! To turn on Remote Web Debugging! YOU HAVE FAILED! ");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CordovaChromeClient makeWebChromeClient(CordovaInterface cordova) {
|
||||||
|
return new CordovaChromeClient(cordova, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CordovaWebViewClient makeWebViewClient(CordovaInterface cordova) {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
|
return new CordovaWebViewClient(cordova, this);
|
||||||
|
}
|
||||||
|
return new IceCreamCordovaWebViewClient(cordova, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -355,39 +294,26 @@ public class CordovaWebView extends WebView {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUserAgentString() {
|
|
||||||
this.getSettings().getUserAgentString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void exposeJsInterface() {
|
private void exposeJsInterface() {
|
||||||
int SDK_INT = Build.VERSION.SDK_INT;
|
if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)) {
|
||||||
if ((SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)) {
|
|
||||||
Log.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old.");
|
Log.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old.");
|
||||||
// Bug being that Java Strings do not get converted to JS strings automatically.
|
// Bug being that Java Strings do not get converted to JS strings automatically.
|
||||||
// This isn't hard to work-around on the JS side, but it's easier to just
|
// This isn't hard to work-around on the JS side, but it's easier to just
|
||||||
// use the prompt bridge instead.
|
// use the prompt bridge instead.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.addJavascriptInterface(exposedJsApi, "_cordovaNative");
|
this.addJavascriptInterface(new ExposedJsApi(bridge), "_cordovaNative");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Set the WebViewClient.
|
public void setWebViewClient(WebViewClient client) {
|
||||||
*
|
this.viewClient = (CordovaWebViewClient)client;
|
||||||
* @param client
|
|
||||||
*/
|
|
||||||
public void setWebViewClient(CordovaWebViewClient client) {
|
|
||||||
this.viewClient = client;
|
|
||||||
super.setWebViewClient(client);
|
super.setWebViewClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Set the WebChromeClient.
|
public void setWebChromeClient(WebChromeClient client) {
|
||||||
*
|
this.chromeClient = (CordovaChromeClient)client;
|
||||||
* @param client
|
|
||||||
*/
|
|
||||||
public void setWebChromeClient(CordovaChromeClient client) {
|
|
||||||
this.chromeClient = client;
|
|
||||||
super.setWebChromeClient(client);
|
super.setWebChromeClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +321,15 @@ public class CordovaWebView extends WebView {
|
|||||||
return this.chromeClient;
|
return this.chromeClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Whitelist getWhitelist() {
|
||||||
|
return this.internalWhitelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Whitelist getExternalWhitelist() {
|
||||||
|
return this.externalWhitelist;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the url into the webview.
|
* Load the url into the webview.
|
||||||
*
|
*
|
||||||
@@ -406,17 +341,7 @@ public class CordovaWebView extends WebView {
|
|||||||
this.loadUrlNow(url);
|
this.loadUrlNow(url);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
this.loadUrlIntoView(url);
|
||||||
String initUrl = this.getProperty("url", null);
|
|
||||||
|
|
||||||
// If first page of app, then set URL to load to be the one passed in
|
|
||||||
if (initUrl == null) {
|
|
||||||
this.loadUrlIntoView(url);
|
|
||||||
}
|
|
||||||
// Otherwise use the URL specified in the activity's extras bundle
|
|
||||||
else {
|
|
||||||
this.loadUrlIntoView(initUrl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,16 +352,15 @@ public class CordovaWebView extends WebView {
|
|||||||
* @param url
|
* @param url
|
||||||
* @param time The number of ms to wait before loading webview
|
* @param time The number of ms to wait before loading webview
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void loadUrl(final String url, int time) {
|
public void loadUrl(final String url, int time) {
|
||||||
String initUrl = this.getProperty("url", null);
|
if(url == null)
|
||||||
|
{
|
||||||
// If first page of app, then set URL to load to be the one passed in
|
this.loadUrlIntoView(Config.getStartUrl());
|
||||||
if (initUrl == null) {
|
|
||||||
this.loadUrlIntoView(url, time);
|
|
||||||
}
|
}
|
||||||
// Otherwise use the URL specified in the activity's extras bundle
|
else
|
||||||
else {
|
{
|
||||||
this.loadUrlIntoView(initUrl);
|
this.loadUrlIntoView(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,8 +376,10 @@ public class CordovaWebView extends WebView {
|
|||||||
public void loadUrlIntoView(final String url, boolean recreatePlugins) {
|
public void loadUrlIntoView(final String url, boolean recreatePlugins) {
|
||||||
LOG.d(TAG, ">>> loadUrl(" + url + ")");
|
LOG.d(TAG, ">>> loadUrl(" + url + ")");
|
||||||
|
|
||||||
|
initIfNecessary();
|
||||||
|
|
||||||
if (recreatePlugins) {
|
if (recreatePlugins) {
|
||||||
this.url = url;
|
this.loadedUrl = url;
|
||||||
this.pluginManager.init();
|
this.pluginManager.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +435,7 @@ public class CordovaWebView extends WebView {
|
|||||||
if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
|
if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
|
||||||
LOG.d(TAG, ">>> loadUrlNow()");
|
LOG.d(TAG, ">>> loadUrlNow()");
|
||||||
}
|
}
|
||||||
if (url.startsWith("file://") || url.startsWith("javascript:") || Config.isUrlWhiteListed(url)) {
|
if (url.startsWith("file://") || url.startsWith("javascript:") || internalWhitelist.isUrlWhiteListed(url)) {
|
||||||
super.loadUrl(url);
|
super.loadUrl(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -557,12 +483,27 @@ public class CordovaWebView extends WebView {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Send JavaScript statement back to JavaScript.
|
* Send JavaScript statement back to JavaScript.
|
||||||
* (This is a convenience method)
|
* Deprecated (https://issues.apache.org/jira/browse/CB-6851)
|
||||||
*
|
* Instead of executing snippets of JS, you should use the exec bridge
|
||||||
* @param statement
|
* to create a Java->JS communication channel.
|
||||||
|
* To do this:
|
||||||
|
* 1. Within plugin.xml (to have your JS run before deviceready):
|
||||||
|
* <js-module><runs/></js-module>
|
||||||
|
* 2. Within your .js (call exec on start-up):
|
||||||
|
* require('cordova/channel').onCordovaReady.subscribe(function() {
|
||||||
|
* require('cordova/exec')(win, null, 'Plugin', 'method', []);
|
||||||
|
* function win(message) {
|
||||||
|
* ... process message from java here ...
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* 3. Within your .java:
|
||||||
|
* PluginResult dataResult = new PluginResult(PluginResult.Status.OK, CODE);
|
||||||
|
* dataResult.setKeepCallback(true);
|
||||||
|
* savedCallbackContext.sendPluginResult(dataResult);
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void sendJavascript(String statement) {
|
public void sendJavascript(String statement) {
|
||||||
this.jsMessageQueue.addJavaScript(statement);
|
this.bridge.getMessageQueue().addJavaScript(statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -573,7 +514,7 @@ public class CordovaWebView extends WebView {
|
|||||||
* @param callbackId
|
* @param callbackId
|
||||||
*/
|
*/
|
||||||
public void sendPluginResult(PluginResult result, String callbackId) {
|
public void sendPluginResult(PluginResult result, String callbackId) {
|
||||||
this.jsMessageQueue.addPluginResult(result, callbackId);
|
this.bridge.getMessageQueue().addPluginResult(result, callbackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -595,13 +536,10 @@ public class CordovaWebView extends WebView {
|
|||||||
* @return true if we went back, false if we are already at top
|
* @return true if we went back, false if we are already at top
|
||||||
*/
|
*/
|
||||||
public boolean backHistory() {
|
public boolean backHistory() {
|
||||||
|
|
||||||
// Check webview first to see if there is a history
|
// Check webview first to see if there is a history
|
||||||
// This is needed to support curPage#diffLink, since they are added to appView's history, but not our history url array (JQMobile behavior)
|
// This is needed to support curPage#diffLink, since they are added to appView's history, but not our history url array (JQMobile behavior)
|
||||||
if (super.canGoBack()) {
|
if (super.canGoBack()) {
|
||||||
printBackForwardList();
|
|
||||||
super.goBack();
|
super.goBack();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -630,7 +568,7 @@ public class CordovaWebView extends WebView {
|
|||||||
if (!openExternal) {
|
if (!openExternal) {
|
||||||
|
|
||||||
// Make sure url is in whitelist
|
// Make sure url is in whitelist
|
||||||
if (url.startsWith("file://") || Config.isUrlWhiteListed(url)) {
|
if (url.startsWith("file://") || internalWhitelist.isUrlWhiteListed(url)) {
|
||||||
// TODO: What about params?
|
// TODO: What about params?
|
||||||
// Load new URL
|
// Load new URL
|
||||||
this.loadUrl(url);
|
this.loadUrl(url);
|
||||||
@@ -655,21 +593,6 @@ public class CordovaWebView extends WebView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check configuration parameters from Config.
|
|
||||||
* Approved list of URLs that can be loaded into Cordova
|
|
||||||
* <access origin="http://server regexp" subdomains="true" />
|
|
||||||
* Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
|
|
||||||
* <log level="DEBUG" />
|
|
||||||
*/
|
|
||||||
private void loadConfiguration() {
|
|
||||||
|
|
||||||
if ("true".equals(this.getProperty("Fullscreen", "false"))) {
|
|
||||||
this.cordova.getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
|
||||||
this.cordova.getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get string property for activity.
|
* Get string property for activity.
|
||||||
*
|
*
|
||||||
@@ -690,23 +613,17 @@ public class CordovaWebView extends WebView {
|
|||||||
return p.toString();
|
return p.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* onKeyDown
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||||
{
|
{
|
||||||
if(keyDownCodes.contains(keyCode))
|
if(boundKeyCodes.contains(keyCode))
|
||||||
{
|
{
|
||||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
||||||
// only override default behavior is event bound
|
|
||||||
LOG.d(TAG, "Down Key Hit");
|
|
||||||
this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');");
|
this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// If volumeup key
|
// If volumeup key
|
||||||
else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||||
LOG.d(TAG, "Up Key Hit");
|
|
||||||
this.loadUrl("javascript:cordova.fireDocumentEvent('volumeupbutton');");
|
this.loadUrl("javascript:cordova.fireDocumentEvent('volumeupbutton');");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -717,7 +634,7 @@ public class CordovaWebView extends WebView {
|
|||||||
}
|
}
|
||||||
else if(keyCode == KeyEvent.KEYCODE_BACK)
|
else if(keyCode == KeyEvent.KEYCODE_BACK)
|
||||||
{
|
{
|
||||||
return !(this.startOfHistory()) || this.bound;
|
return !(this.startOfHistory()) || isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK);
|
||||||
}
|
}
|
||||||
else if(keyCode == KeyEvent.KEYCODE_MENU)
|
else if(keyCode == KeyEvent.KEYCODE_MENU)
|
||||||
{
|
{
|
||||||
@@ -734,10 +651,8 @@ public class CordovaWebView extends WebView {
|
|||||||
return super.onKeyDown(keyCode, event);
|
return super.onKeyDown(keyCode, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onKeyDown(keyCode, event);
|
return super.onKeyDown(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event)
|
public boolean onKeyUp(int keyCode, KeyEvent event)
|
||||||
@@ -747,10 +662,11 @@ public class CordovaWebView extends WebView {
|
|||||||
// A custom view is currently displayed (e.g. playing a video)
|
// A custom view is currently displayed (e.g. playing a video)
|
||||||
if(mCustomView != null) {
|
if(mCustomView != null) {
|
||||||
this.hideCustomView();
|
this.hideCustomView();
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// The webview is currently displayed
|
// The webview is currently displayed
|
||||||
// If back key is bound, then send event to JavaScript
|
// If back key is bound, then send event to JavaScript
|
||||||
if (this.bound) {
|
if (isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK)) {
|
||||||
this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');");
|
this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -760,12 +676,6 @@ public class CordovaWebView extends WebView {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// If not, then invoke default behavior
|
// If not, then invoke default behavior
|
||||||
else {
|
|
||||||
//this.activityState = ACTIVITY_EXITING;
|
|
||||||
//return false;
|
|
||||||
// If they hit back button when app is initializing, app should exit instead of hang until initialization (CB2-458)
|
|
||||||
this.cordova.getActivity().finish();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -782,48 +692,60 @@ public class CordovaWebView extends WebView {
|
|||||||
this.loadUrl("javascript:cordova.fireDocumentEvent('searchbutton');");
|
this.loadUrl("javascript:cordova.fireDocumentEvent('searchbutton');");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if(keyUpCodes.contains(keyCode))
|
|
||||||
{
|
|
||||||
//What the hell should this do?
|
|
||||||
return super.onKeyUp(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Does webkit change this behavior?
|
//Does webkit change this behavior?
|
||||||
return super.onKeyUp(keyCode, event);
|
return super.onKeyUp(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setButtonPlumbedToJs(int keyCode, boolean override) {
|
||||||
|
switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||||
|
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||||
|
case KeyEvent.KEYCODE_BACK:
|
||||||
|
// TODO: Why are search and menu buttons handled separately?
|
||||||
|
if (override) {
|
||||||
|
boundKeyCodes.add(keyCode);
|
||||||
|
} else {
|
||||||
|
boundKeyCodes.remove(keyCode);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unsupported keycode: " + keyCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated // Use setButtonPlumbedToJs() instead.
|
||||||
public void bindButton(boolean override)
|
public void bindButton(boolean override)
|
||||||
{
|
{
|
||||||
this.bound = override;
|
setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, override);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated // Use setButtonPlumbedToJs() instead.
|
||||||
public void bindButton(String button, boolean override) {
|
public void bindButton(String button, boolean override) {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
if (button.compareTo("volumeup")==0) {
|
if (button.compareTo("volumeup")==0) {
|
||||||
keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_UP);
|
setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, override);
|
||||||
}
|
}
|
||||||
else if (button.compareTo("volumedown")==0) {
|
else if (button.compareTo("volumedown")==0) {
|
||||||
keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_DOWN);
|
setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, override);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void bindButton(int keyCode, boolean keyDown, boolean override) {
|
|
||||||
if(keyDown)
|
|
||||||
{
|
|
||||||
keyDownCodes.add(keyCode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
keyUpCodes.add(keyCode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated // Use setButtonPlumbedToJs() instead.
|
||||||
|
public void bindButton(int keyCode, boolean keyDown, boolean override) {
|
||||||
|
setButtonPlumbedToJs(keyCode, override);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated // Use isButtonPlumbedToJs
|
||||||
public boolean isBackButtonBound()
|
public boolean isBackButtonBound()
|
||||||
{
|
{
|
||||||
return this.bound;
|
return isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isButtonPlumbedToJs(int keyCode)
|
||||||
|
{
|
||||||
|
return boundKeyCodes.contains(keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
public void handlePause(boolean keepRunning)
|
public void handlePause(boolean keepRunning)
|
||||||
{
|
{
|
||||||
LOG.d(TAG, "Handle the pause");
|
LOG.d(TAG, "Handle the pause");
|
||||||
@@ -875,7 +797,7 @@ public class CordovaWebView extends WebView {
|
|||||||
// unregister the receiver
|
// unregister the receiver
|
||||||
if (this.receiver != null) {
|
if (this.receiver != null) {
|
||||||
try {
|
try {
|
||||||
this.cordova.getActivity().unregisterReceiver(this.receiver);
|
getContext().unregisterReceiver(this.receiver);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Error unregistering configuration receiver: " + e.getMessage(), e);
|
Log.e(TAG, "Error unregistering configuration receiver: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -895,8 +817,9 @@ public class CordovaWebView extends WebView {
|
|||||||
return paused;
|
return paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated // This never did anything.
|
||||||
public boolean hadKeyEvent() {
|
public boolean hadKeyEvent() {
|
||||||
return handleButton;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapping these functions in their own class prevents warnings in adb like:
|
// Wrapping these functions in their own class prevents warnings in adb like:
|
||||||
@@ -997,11 +920,15 @@ public class CordovaWebView extends WebView {
|
|||||||
return myList;
|
return myList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated // This never did anything
|
||||||
public void storeResult(int requestCode, int resultCode, Intent intent) {
|
public void storeResult(int requestCode, int resultCode, Intent intent) {
|
||||||
mResult = new ActivityResult(requestCode, resultCode, intent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CordovaResourceApi getResourceApi() {
|
public CordovaResourceApi getResourceApi() {
|
||||||
return resourceApi;
|
return resourceApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CordovaPreferences getPreferences() {
|
||||||
|
return preferences;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
|
|
||||||
import org.apache.cordova.CordovaInterface;
|
import org.apache.cordova.CordovaInterface;
|
||||||
@@ -28,18 +27,14 @@ import org.json.JSONException;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
|
||||||
import android.net.http.SslError;
|
import android.net.http.SslError;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.webkit.HttpAuthHandler;
|
import android.webkit.HttpAuthHandler;
|
||||||
import android.webkit.SslErrorHandler;
|
import android.webkit.SslErrorHandler;
|
||||||
import android.webkit.WebResourceResponse;
|
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebViewClient;
|
import android.webkit.WebViewClient;
|
||||||
|
|
||||||
@@ -58,20 +53,16 @@ import android.webkit.WebViewClient;
|
|||||||
public class CordovaWebViewClient extends WebViewClient {
|
public class CordovaWebViewClient extends WebViewClient {
|
||||||
|
|
||||||
private static final String TAG = "CordovaWebViewClient";
|
private static final String TAG = "CordovaWebViewClient";
|
||||||
private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/";
|
|
||||||
CordovaInterface cordova;
|
CordovaInterface cordova;
|
||||||
CordovaWebView appView;
|
CordovaWebView appView;
|
||||||
|
CordovaUriHelper helper;
|
||||||
private boolean doClearHistory = false;
|
private boolean doClearHistory = false;
|
||||||
boolean isCurrentlyLoading;
|
boolean isCurrentlyLoading;
|
||||||
|
|
||||||
/** The authorization tokens. */
|
/** The authorization tokens. */
|
||||||
private Hashtable<String, AuthenticationToken> authenticationTokens = new Hashtable<String, AuthenticationToken>();
|
private Hashtable<String, AuthenticationToken> authenticationTokens = new Hashtable<String, AuthenticationToken>();
|
||||||
|
|
||||||
/**
|
@Deprecated
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param cordova
|
|
||||||
*/
|
|
||||||
public CordovaWebViewClient(CordovaInterface cordova) {
|
public CordovaWebViewClient(CordovaInterface cordova) {
|
||||||
this.cordova = cordova;
|
this.cordova = cordova;
|
||||||
}
|
}
|
||||||
@@ -85,6 +76,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
public CordovaWebViewClient(CordovaInterface cordova, CordovaWebView view) {
|
public CordovaWebViewClient(CordovaInterface cordova, CordovaWebView view) {
|
||||||
this.cordova = cordova;
|
this.cordova = cordova;
|
||||||
this.appView = view;
|
this.appView = view;
|
||||||
|
helper = new CordovaUriHelper(cordova, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,29 +84,12 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
*
|
*
|
||||||
* @param view
|
* @param view
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setWebView(CordovaWebView view) {
|
public void setWebView(CordovaWebView view) {
|
||||||
this.appView = view;
|
this.appView = view;
|
||||||
|
helper = new CordovaUriHelper(cordova, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Parses commands sent by setting the webView's URL to:
|
|
||||||
// cdvbrg:service/action/callbackId#jsonArgs
|
|
||||||
private void handleExecUrl(String url) {
|
|
||||||
int idx1 = CORDOVA_EXEC_URL_PREFIX.length();
|
|
||||||
int idx2 = url.indexOf('#', idx1 + 1);
|
|
||||||
int idx3 = url.indexOf('#', idx2 + 1);
|
|
||||||
int idx4 = url.indexOf('#', idx3 + 1);
|
|
||||||
if (idx1 == -1 || idx2 == -1 || idx3 == -1 || idx4 == -1) {
|
|
||||||
Log.e(TAG, "Could not decode URL command: " + url);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String service = url.substring(idx1, idx2);
|
|
||||||
String action = url.substring(idx2 + 1, idx3);
|
|
||||||
String callbackId = url.substring(idx3 + 1, idx4);
|
|
||||||
String jsonArgs = url.substring(idx4 + 1);
|
|
||||||
appView.pluginManager.exec(service, action, callbackId, jsonArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Give the host application a chance to take over the control when a new url
|
* Give the host application a chance to take over the control when a new url
|
||||||
* is about to be loaded in the current WebView.
|
* is about to be loaded in the current WebView.
|
||||||
@@ -125,112 +100,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||||
// Check if it's an exec() bridge command message.
|
return helper.shouldOverrideUrlLoading(view, url);
|
||||||
if (NativeToJsMessageQueue.ENABLE_LOCATION_CHANGE_EXEC_MODE && url.startsWith(CORDOVA_EXEC_URL_PREFIX)) {
|
|
||||||
handleExecUrl(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give plugins the chance to handle the url
|
|
||||||
else if ((this.appView.pluginManager != null) && this.appView.pluginManager.onOverrideUrlLoading(url)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// If dialing phone (tel:5551212)
|
|
||||||
else if (url.startsWith(WebView.SCHEME_TEL)) {
|
|
||||||
try {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_DIAL);
|
|
||||||
intent.setData(Uri.parse(url));
|
|
||||||
this.cordova.getActivity().startActivity(intent);
|
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
|
||||||
LOG.e(TAG, "Error dialing " + url + ": " + e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If displaying map (geo:0,0?q=address)
|
|
||||||
else if (url.startsWith("geo:")) {
|
|
||||||
try {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setData(Uri.parse(url));
|
|
||||||
this.cordova.getActivity().startActivity(intent);
|
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
|
||||||
LOG.e(TAG, "Error showing map " + url + ": " + e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If sending email (mailto:abc@corp.com)
|
|
||||||
else if (url.startsWith(WebView.SCHEME_MAILTO)) {
|
|
||||||
try {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setData(Uri.parse(url));
|
|
||||||
this.cordova.getActivity().startActivity(intent);
|
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
|
||||||
LOG.e(TAG, "Error sending email " + url + ": " + e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If sms:5551212?body=This is the message
|
|
||||||
else if (url.startsWith("sms:")) {
|
|
||||||
try {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
|
|
||||||
// Get address
|
|
||||||
String address = null;
|
|
||||||
int parmIndex = url.indexOf('?');
|
|
||||||
if (parmIndex == -1) {
|
|
||||||
address = url.substring(4);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
address = url.substring(4, parmIndex);
|
|
||||||
|
|
||||||
// If body, then set sms body
|
|
||||||
Uri uri = Uri.parse(url);
|
|
||||||
String query = uri.getQuery();
|
|
||||||
if (query != null) {
|
|
||||||
if (query.startsWith("body=")) {
|
|
||||||
intent.putExtra("sms_body", query.substring(5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
intent.setData(Uri.parse("sms:" + address));
|
|
||||||
intent.putExtra("address", address);
|
|
||||||
intent.setType("vnd.android-dir/mms-sms");
|
|
||||||
this.cordova.getActivity().startActivity(intent);
|
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
|
||||||
LOG.e(TAG, "Error sending sms " + url + ":" + e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Android Market
|
|
||||||
else if(url.startsWith("market:")) {
|
|
||||||
try {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setData(Uri.parse(url));
|
|
||||||
this.cordova.getActivity().startActivity(intent);
|
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
|
||||||
LOG.e(TAG, "Error loading Google Play Store: " + url, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All else
|
|
||||||
else {
|
|
||||||
|
|
||||||
// If our app or file:, then load into a new Cordova webview container by starting a new instance of our activity.
|
|
||||||
// Our app continues to run. When BACK is pressed, our app is redisplayed.
|
|
||||||
if (url.startsWith("file://") || url.startsWith("data:") || Config.isUrlWhiteListed(url)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not our application, let default viewer handle
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setData(Uri.parse(url));
|
|
||||||
this.cordova.getActivity().startActivity(intent);
|
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
|
||||||
LOG.e(TAG, "Error loading url " + url, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -271,7 +141,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
isCurrentlyLoading = true;
|
isCurrentlyLoading = true;
|
||||||
LOG.d(TAG, "onPageStarted(" + url + ")");
|
LOG.d(TAG, "onPageStarted(" + url + ")");
|
||||||
// Flush stale messages.
|
// Flush stale messages.
|
||||||
this.appView.jsMessageQueue.reset();
|
this.appView.bridge.reset(url);
|
||||||
|
|
||||||
// Broadcast message that page has loaded
|
// Broadcast message that page has loaded
|
||||||
this.appView.postMessage("onPageStarted", url);
|
this.appView.postMessage("onPageStarted", url);
|
||||||
@@ -361,7 +231,19 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
// Clear timeout flag
|
// Clear timeout flag
|
||||||
this.appView.loadUrlTimeout++;
|
this.appView.loadUrlTimeout++;
|
||||||
|
|
||||||
// Handle error
|
// If this is a "Protocol Not Supported" error, then revert to the previous
|
||||||
|
// page. If there was no previous page, then punt. The application's config
|
||||||
|
// is likely incorrect (start page set to sms: or something like that)
|
||||||
|
if (errorCode == WebViewClient.ERROR_UNSUPPORTED_SCHEME) {
|
||||||
|
if (view.canGoBack()) {
|
||||||
|
view.goBack();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
super.onReceivedError(view, errorCode, description, failingUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle other errors by passing them to the webview in JS
|
||||||
JSONObject data = new JSONObject();
|
JSONObject data = new JSONObject();
|
||||||
try {
|
try {
|
||||||
data.put("errorCode", errorCode);
|
data.put("errorCode", errorCode);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import android.webkit.JavascriptInterface;
|
import android.webkit.JavascriptInterface;
|
||||||
import org.apache.cordova.PluginManager;
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,48 +29,24 @@ import org.json.JSONException;
|
|||||||
*/
|
*/
|
||||||
/* package */ class ExposedJsApi {
|
/* package */ class ExposedJsApi {
|
||||||
|
|
||||||
private PluginManager pluginManager;
|
private CordovaBridge bridge;
|
||||||
private NativeToJsMessageQueue jsMessageQueue;
|
|
||||||
|
|
||||||
public ExposedJsApi(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) {
|
public ExposedJsApi(CordovaBridge bridge) {
|
||||||
this.pluginManager = pluginManager;
|
this.bridge = bridge;
|
||||||
this.jsMessageQueue = jsMessageQueue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
|
public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
|
||||||
// If the arguments weren't received, send a message back to JS. It will switch bridge modes and try again. See CB-2666.
|
return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments);
|
||||||
// We send a message meant specifically for this case. It starts with "@" so no other message can be encoded into the same string.
|
|
||||||
if (arguments == null) {
|
|
||||||
return "@Null arguments.";
|
|
||||||
}
|
|
||||||
|
|
||||||
jsMessageQueue.setPaused(true);
|
|
||||||
try {
|
|
||||||
// Tell the resourceApi what thread the JS is running on.
|
|
||||||
CordovaResourceApi.jsThread = Thread.currentThread();
|
|
||||||
|
|
||||||
pluginManager.exec(service, action, callbackId, arguments);
|
|
||||||
String ret = "";
|
|
||||||
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) {
|
|
||||||
ret = jsMessageQueue.popAndEncode(false);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return "";
|
|
||||||
} finally {
|
|
||||||
jsMessageQueue.setPaused(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
public void setNativeToJsBridgeMode(int value) {
|
public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException {
|
||||||
jsMessageQueue.setBridgeMode(value);
|
bridge.jsSetNativeToJsBridgeMode(bridgeSecret, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
public String retrieveJsMessages(boolean fromOnlineEvent) {
|
public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException {
|
||||||
return jsMessageQueue.popAndEncode(fromOnlineEvent);
|
return bridge.jsRetrieveJsMessages(bridgeSecret, fromOnlineEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import android.webkit.WebView;
|
|||||||
public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||||
|
|
||||||
private static final String TAG = "IceCreamCordovaWebViewClient";
|
private static final String TAG = "IceCreamCordovaWebViewClient";
|
||||||
|
private CordovaUriHelper helper;
|
||||||
|
|
||||||
public IceCreamCordovaWebViewClient(CordovaInterface cordova) {
|
public IceCreamCordovaWebViewClient(CordovaInterface cordova) {
|
||||||
super(cordova);
|
super(cordova);
|
||||||
@@ -47,8 +48,9 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
|||||||
@Override
|
@Override
|
||||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||||
try {
|
try {
|
||||||
// Check the against the white-list.
|
// Check the against the whitelist and lock out access to the WebView directory
|
||||||
if ((url.startsWith("http:") || url.startsWith("https:")) && !Config.isUrlWhiteListed(url)) {
|
// Changing this will cause problems for your application
|
||||||
|
if (isUrlHarmful(url)) {
|
||||||
LOG.w(TAG, "URL blocked by whitelist: " + url);
|
LOG.w(TAG, "URL blocked by whitelist: " + url);
|
||||||
// Results in a 404.
|
// Results in a 404.
|
||||||
return new WebResourceResponse("text/plain", "UTF-8", null);
|
return new WebResourceResponse("text/plain", "UTF-8", null);
|
||||||
@@ -74,6 +76,11 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isUrlHarmful(String url) {
|
||||||
|
return ((url.startsWith("http:") || url.startsWith("https:")) && !appView.getWhitelist().isUrlWhiteListed(url))
|
||||||
|
|| url.contains("app_webview");
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean needsKitKatContentUrlFix(Uri uri) {
|
private static boolean needsKitKatContentUrlFix(Uri uri) {
|
||||||
return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && "content".equals(uri.getScheme());
|
return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && "content".equals(uri.getScheme());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,31 +35,19 @@ import android.webkit.WebView;
|
|||||||
public class NativeToJsMessageQueue {
|
public class NativeToJsMessageQueue {
|
||||||
private static final String LOG_TAG = "JsMessageQueue";
|
private static final String LOG_TAG = "JsMessageQueue";
|
||||||
|
|
||||||
// This must match the default value in cordova-js/lib/android/exec.js
|
|
||||||
private static final int DEFAULT_BRIDGE_MODE = 2;
|
|
||||||
|
|
||||||
// Set this to true to force plugin results to be encoding as
|
// Set this to true to force plugin results to be encoding as
|
||||||
// JS instead of the custom format (useful for benchmarking).
|
// JS instead of the custom format (useful for benchmarking).
|
||||||
private static final boolean FORCE_ENCODE_USING_EVAL = false;
|
private static final boolean FORCE_ENCODE_USING_EVAL = false;
|
||||||
|
|
||||||
// Disable URL-based exec() bridge by default since it's a bit of a
|
|
||||||
// security concern.
|
|
||||||
static final boolean ENABLE_LOCATION_CHANGE_EXEC_MODE = false;
|
|
||||||
|
|
||||||
// Disable sending back native->JS messages during an exec() when the active
|
// Disable sending back native->JS messages during an exec() when the active
|
||||||
// exec() is asynchronous. Set this to true when running bridge benchmarks.
|
// exec() is asynchronous. Set this to true when running bridge benchmarks.
|
||||||
static final boolean DISABLE_EXEC_CHAINING = false;
|
static final boolean DISABLE_EXEC_CHAINING = false;
|
||||||
|
|
||||||
// Arbitrarily chosen upper limit for how much data to send to JS in one shot.
|
// Arbitrarily chosen upper limit for how much data to send to JS in one shot.
|
||||||
// This currently only chops up on message boundaries. It may be useful
|
// This currently only chops up on message boundaries. It may be useful
|
||||||
// to allow it to break up messages.
|
// to allow it to break up messages.
|
||||||
private static int MAX_PAYLOAD_SIZE = 50 * 1024 * 10240;
|
private static int MAX_PAYLOAD_SIZE = 50 * 1024 * 10240;
|
||||||
|
|
||||||
/**
|
|
||||||
* The index into registeredListeners to treat as active.
|
|
||||||
*/
|
|
||||||
private int activeListenerIndex;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When true, the active listener is not fired upon enqueue. When set to false,
|
* When true, the active listener is not fired upon enqueue. When set to false,
|
||||||
* the active listener will be fired if the queue is non-empty.
|
* the active listener will be fired if the queue is non-empty.
|
||||||
@@ -76,6 +64,13 @@ public class NativeToJsMessageQueue {
|
|||||||
*/
|
*/
|
||||||
private final BridgeMode[] registeredListeners;
|
private final BridgeMode[] registeredListeners;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When null, the bridge is disabled. This occurs during page transitions.
|
||||||
|
* When disabled, all callbacks are dropped since they are assumed to be
|
||||||
|
* relevant to the previous page.
|
||||||
|
*/
|
||||||
|
private BridgeMode activeBridgeMode;
|
||||||
|
|
||||||
private final CordovaInterface cordova;
|
private final CordovaInterface cordova;
|
||||||
private final CordovaWebView webView;
|
private final CordovaWebView webView;
|
||||||
|
|
||||||
@@ -89,22 +84,28 @@ public class NativeToJsMessageQueue {
|
|||||||
registeredListeners[3] = new PrivateApiBridgeMode();
|
registeredListeners[3] = new PrivateApiBridgeMode();
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBridgeEnabled() {
|
||||||
|
return activeBridgeMode != null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the bridge mode.
|
* Changes the bridge mode.
|
||||||
*/
|
*/
|
||||||
public void setBridgeMode(int value) {
|
public void setBridgeMode(int value) {
|
||||||
if (value < 0 || value >= registeredListeners.length) {
|
if (value < -1 || value >= registeredListeners.length) {
|
||||||
Log.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value);
|
Log.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value);
|
||||||
} else {
|
} else {
|
||||||
if (value != activeListenerIndex) {
|
BridgeMode newMode = value < 0 ? null : registeredListeners[value];
|
||||||
Log.d(LOG_TAG, "Set native->JS mode to " + value);
|
if (newMode != activeBridgeMode) {
|
||||||
|
Log.d(LOG_TAG, "Set native->JS mode to " + (newMode == null ? "null" : newMode.getClass().getSimpleName()));
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
activeListenerIndex = value;
|
activeBridgeMode = newMode;
|
||||||
BridgeMode activeListener = registeredListeners[value];
|
if (newMode != null) {
|
||||||
activeListener.reset();
|
newMode.reset();
|
||||||
if (!paused && !queue.isEmpty()) {
|
if (!paused && !queue.isEmpty()) {
|
||||||
activeListener.onNativeToJsMessageAvailable();
|
newMode.onNativeToJsMessageAvailable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,8 +118,7 @@ public class NativeToJsMessageQueue {
|
|||||||
public void reset() {
|
public void reset() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
queue.clear();
|
queue.clear();
|
||||||
setBridgeMode(DEFAULT_BRIDGE_MODE);
|
setBridgeMode(-1);
|
||||||
registeredListeners[activeListenerIndex].reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,10 @@ public class NativeToJsMessageQueue {
|
|||||||
*/
|
*/
|
||||||
public String popAndEncode(boolean fromOnlineEvent) {
|
public String popAndEncode(boolean fromOnlineEvent) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
registeredListeners[activeListenerIndex].notifyOfFlush(fromOnlineEvent);
|
if (activeBridgeMode == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
activeBridgeMode.notifyOfFlush(fromOnlineEvent);
|
||||||
if (queue.isEmpty()) {
|
if (queue.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -247,16 +250,20 @@ public class NativeToJsMessageQueue {
|
|||||||
|
|
||||||
enqueueMessage(message);
|
enqueueMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enqueueMessage(JsMessage message) {
|
private void enqueueMessage(JsMessage message) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
if (activeBridgeMode == null) {
|
||||||
|
Log.d(LOG_TAG, "Dropping Native->JS message due to disabled bridge");
|
||||||
|
return;
|
||||||
|
}
|
||||||
queue.add(message);
|
queue.add(message);
|
||||||
if (!paused) {
|
if (!paused) {
|
||||||
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
|
activeBridgeMode.onNativeToJsMessageAvailable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPaused(boolean value) {
|
public void setPaused(boolean value) {
|
||||||
if (paused && value) {
|
if (paused && value) {
|
||||||
// This should never happen. If a use-case for it comes up, we should
|
// This should never happen. If a use-case for it comes up, we should
|
||||||
@@ -266,16 +273,12 @@ public class NativeToJsMessageQueue {
|
|||||||
paused = value;
|
paused = value;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!queue.isEmpty()) {
|
if (!queue.isEmpty() && activeBridgeMode != null) {
|
||||||
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
|
activeBridgeMode.onNativeToJsMessageAvailable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getPaused() {
|
|
||||||
return paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract class BridgeMode {
|
private abstract class BridgeMode {
|
||||||
abstract void onNativeToJsMessageAvailable();
|
abstract void onNativeToJsMessageAvailable();
|
||||||
@@ -308,23 +311,33 @@ public class NativeToJsMessageQueue {
|
|||||||
/** Uses online/offline events to tell the JS when to poll for messages. */
|
/** Uses online/offline events to tell the JS when to poll for messages. */
|
||||||
private class OnlineEventsBridgeMode extends BridgeMode {
|
private class OnlineEventsBridgeMode extends BridgeMode {
|
||||||
private boolean online;
|
private boolean online;
|
||||||
final Runnable runnable = new Runnable() {
|
private boolean ignoreNextFlush;
|
||||||
|
|
||||||
|
final Runnable toggleNetworkRunnable = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!queue.isEmpty()) {
|
if (!queue.isEmpty()) {
|
||||||
|
ignoreNextFlush = false;
|
||||||
webView.setNetworkAvailable(online);
|
webView.setNetworkAvailable(online);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
final Runnable resetNetworkRunnable = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
online = false;
|
||||||
|
// If the following call triggers a notifyOfFlush, then ignore it.
|
||||||
|
ignoreNextFlush = true;
|
||||||
|
webView.setNetworkAvailable(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@Override void reset() {
|
@Override void reset() {
|
||||||
online = false;
|
cordova.getActivity().runOnUiThread(resetNetworkRunnable);
|
||||||
webView.setNetworkAvailable(true);
|
|
||||||
}
|
}
|
||||||
@Override void onNativeToJsMessageAvailable() {
|
@Override void onNativeToJsMessageAvailable() {
|
||||||
cordova.getActivity().runOnUiThread(runnable);
|
cordova.getActivity().runOnUiThread(toggleNetworkRunnable);
|
||||||
}
|
}
|
||||||
// Track when online/offline events are fired so that we don't fire excess events.
|
// Track when online/offline events are fired so that we don't fire excess events.
|
||||||
@Override void notifyOfFlush(boolean fromOnlineEvent) {
|
@Override void notifyOfFlush(boolean fromOnlineEvent) {
|
||||||
if (fromOnlineEvent) {
|
if (fromOnlineEvent && !ignoreNextFlush) {
|
||||||
online = !online;
|
online = !online;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import org.apache.cordova.CordovaWebView;
|
import java.util.List;
|
||||||
import org.apache.cordova.CordovaInterface;
|
|
||||||
import org.apache.cordova.CordovaPlugin;
|
|
||||||
|
|
||||||
//import android.content.Context;
|
import org.apache.cordova.CordovaPlugin;
|
||||||
//import android.webkit.WebView;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a service entry object.
|
* This class represents a service entry object.
|
||||||
@@ -33,100 +30,60 @@ public class PluginEntry {
|
|||||||
/**
|
/**
|
||||||
* The name of the service that this plugin implements
|
* The name of the service that this plugin implements
|
||||||
*/
|
*/
|
||||||
public String service = "";
|
public String service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The plugin class name that implements the service.
|
* The plugin class name that implements the service.
|
||||||
*/
|
*/
|
||||||
public String pluginClass = "";
|
public String pluginClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The plugin object.
|
* The pre-instantiated plugin to use for this entry.
|
||||||
* Plugin objects are only created when they are called from JavaScript. (see PluginManager.exec)
|
|
||||||
* The exception is if the onload flag is set, then they are created when PluginManager is initialized.
|
|
||||||
*/
|
*/
|
||||||
public CordovaPlugin plugin = null;
|
public CordovaPlugin plugin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag that indicates the plugin object should be created when PluginManager is initialized.
|
* Flag that indicates the plugin object should be created when PluginManager is initialized.
|
||||||
*/
|
*/
|
||||||
public boolean onload = false;
|
public boolean onload;
|
||||||
|
|
||||||
|
private List<String> urlFilters;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs with a CordovaPlugin already instantiated.
|
||||||
|
*/
|
||||||
|
public PluginEntry(String service, CordovaPlugin plugin) {
|
||||||
|
this(service, plugin.getClass().getName(), true, plugin, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param service The name of the service
|
* @param service The name of the service
|
||||||
* @param pluginClass The plugin class name
|
* @param pluginClass The plugin class name
|
||||||
* @param onload Create plugin object when HTML page is loaded
|
* @param onload Create plugin object when HTML page is loaded
|
||||||
*/
|
*/
|
||||||
public PluginEntry(String service, String pluginClass, boolean onload) {
|
public PluginEntry(String service, String pluginClass, boolean onload) {
|
||||||
|
this(service, pluginClass, onload, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated // urlFilters are going away
|
||||||
|
public PluginEntry(String service, String pluginClass, boolean onload, List<String> urlFilters) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
this.pluginClass = pluginClass;
|
this.pluginClass = pluginClass;
|
||||||
this.onload = onload;
|
this.onload = onload;
|
||||||
|
this.urlFilters = urlFilters;
|
||||||
|
plugin = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private PluginEntry(String service, String pluginClass, boolean onload, CordovaPlugin plugin, List<String> urlFilters) {
|
||||||
* Alternate constructor
|
|
||||||
*
|
|
||||||
* @param service The name of the service
|
|
||||||
* @param plugin The plugin associated with this entry
|
|
||||||
*/
|
|
||||||
public PluginEntry(String service, CordovaPlugin plugin) {
|
|
||||||
this.service = service;
|
this.service = service;
|
||||||
|
this.pluginClass = pluginClass;
|
||||||
|
this.onload = onload;
|
||||||
|
this.urlFilters = urlFilters;
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.pluginClass = plugin.getClass().getName();
|
|
||||||
this.onload = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public List<String> getUrlFilters() {
|
||||||
* Create plugin object.
|
return urlFilters;
|
||||||
* If plugin is already created, then just return it.
|
|
||||||
*
|
|
||||||
* @return The plugin object
|
|
||||||
*/
|
|
||||||
public CordovaPlugin createPlugin(CordovaWebView webView, CordovaInterface ctx) {
|
|
||||||
if (this.plugin != null) {
|
|
||||||
return this.plugin;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
Class c = getClassByName(this.pluginClass);
|
|
||||||
if (isCordovaPlugin(c)) {
|
|
||||||
this.plugin = (CordovaPlugin) c.newInstance();
|
|
||||||
this.plugin.initialize(ctx, webView);
|
|
||||||
return plugin;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.out.println("Error adding plugin " + this.pluginClass + ".");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the class.
|
|
||||||
*
|
|
||||||
* @param clazz
|
|
||||||
* @return a reference to the named class
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private Class getClassByName(final String clazz) throws ClassNotFoundException {
|
|
||||||
Class c = null;
|
|
||||||
if ((clazz != null) && !("".equals(clazz))) {
|
|
||||||
c = Class.forName(clazz);
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the given class extends CordovaPlugin.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private boolean isCordovaPlugin(Class c) {
|
|
||||||
if (c != null) {
|
|
||||||
return org.apache.cordova.CordovaPlugin.class.isAssignableFrom(c);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import org.apache.cordova.CordovaArgs;
|
|
||||||
import org.apache.cordova.CordovaWebView;
|
import org.apache.cordova.CordovaWebView;
|
||||||
import org.apache.cordova.CallbackContext;
|
import org.apache.cordova.CallbackContext;
|
||||||
import org.apache.cordova.CordovaInterface;
|
import org.apache.cordova.CordovaInterface;
|
||||||
@@ -33,11 +28,8 @@ import org.apache.cordova.CordovaPlugin;
|
|||||||
import org.apache.cordova.PluginEntry;
|
import org.apache.cordova.PluginEntry;
|
||||||
import org.apache.cordova.PluginResult;
|
import org.apache.cordova.PluginResult;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.XmlResourceParser;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Debug;
|
import android.os.Debug;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -53,31 +45,40 @@ public class PluginManager {
|
|||||||
private static final int SLOW_EXEC_WARNING_THRESHOLD = Debug.isDebuggerConnected() ? 60 : 16;
|
private static final int SLOW_EXEC_WARNING_THRESHOLD = Debug.isDebuggerConnected() ? 60 : 16;
|
||||||
|
|
||||||
// List of service entries
|
// List of service entries
|
||||||
private final HashMap<String, PluginEntry> entries = new HashMap<String, PluginEntry>();
|
private final HashMap<String, CordovaPlugin> pluginMap = new HashMap<String, CordovaPlugin>();
|
||||||
|
private final HashMap<String, PluginEntry> entryMap = new HashMap<String, PluginEntry>();
|
||||||
|
|
||||||
private final CordovaInterface ctx;
|
private final CordovaInterface ctx;
|
||||||
private final CordovaWebView app;
|
private final CordovaWebView app;
|
||||||
|
|
||||||
// Flag to track first time through
|
|
||||||
private boolean firstRun;
|
|
||||||
|
|
||||||
// Stores mapping of Plugin Name -> <url-filter> values.
|
// Stores mapping of Plugin Name -> <url-filter> values.
|
||||||
// Using <url-filter> is deprecated.
|
// Using <url-filter> is deprecated.
|
||||||
protected HashMap<String, List<String>> urlMap = new HashMap<String, List<String>>();
|
protected HashMap<String, List<String>> urlMap = new HashMap<String, List<String>>();
|
||||||
|
|
||||||
private AtomicInteger numPendingUiExecs;
|
@Deprecated
|
||||||
|
PluginManager(CordovaWebView cordovaWebView, CordovaInterface cordova) {
|
||||||
|
this(cordovaWebView, cordova, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
PluginManager(CordovaWebView cordovaWebView, CordovaInterface cordova, List<PluginEntry> pluginEntries) {
|
||||||
* Constructor.
|
this.ctx = cordova;
|
||||||
*
|
this.app = cordovaWebView;
|
||||||
* @param app
|
if (pluginEntries == null) {
|
||||||
* @param ctx
|
ConfigXmlParser parser = new ConfigXmlParser();
|
||||||
*/
|
parser.parse(ctx.getActivity());
|
||||||
public PluginManager(CordovaWebView app, CordovaInterface ctx) {
|
pluginEntries = parser.getPluginEntries();
|
||||||
this.ctx = ctx;
|
}
|
||||||
this.app = app;
|
setPluginEntries(pluginEntries);
|
||||||
this.firstRun = true;
|
}
|
||||||
this.numPendingUiExecs = new AtomicInteger(0);
|
|
||||||
|
public void setPluginEntries(List<PluginEntry> pluginEntries) {
|
||||||
|
this.onPause(false);
|
||||||
|
this.onDestroy();
|
||||||
|
pluginMap.clear();
|
||||||
|
urlMap.clear();
|
||||||
|
for (PluginEntry entry : pluginEntries) {
|
||||||
|
addService(entry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,114 +86,32 @@ public class PluginManager {
|
|||||||
*/
|
*/
|
||||||
public void init() {
|
public void init() {
|
||||||
LOG.d(TAG, "init()");
|
LOG.d(TAG, "init()");
|
||||||
|
this.onPause(false);
|
||||||
// If first time, then load plugins from config.xml file
|
this.onDestroy();
|
||||||
if (this.firstRun) {
|
pluginMap.clear();
|
||||||
this.loadPlugins();
|
|
||||||
this.firstRun = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop plugins on current HTML page and discard plugin objects
|
|
||||||
else {
|
|
||||||
this.onPause(false);
|
|
||||||
this.onDestroy();
|
|
||||||
this.clearPluginObjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert PluginManager service
|
|
||||||
this.addService(new PluginEntry("PluginManager", new PluginManagerService()));
|
|
||||||
|
|
||||||
// Start up all plugins that have onload specified
|
|
||||||
this.startupPlugins();
|
this.startupPlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Deprecated
|
||||||
* Load plugins from res/xml/config.xml
|
|
||||||
*/
|
|
||||||
public void loadPlugins() {
|
public void loadPlugins() {
|
||||||
// First checking the class namespace for config.xml
|
|
||||||
int id = this.ctx.getActivity().getResources().getIdentifier("config", "xml", this.ctx.getActivity().getClass().getPackage().getName());
|
|
||||||
if (id == 0) {
|
|
||||||
// If we couldn't find config.xml there, we'll look in the namespace from AndroidManifest.xml
|
|
||||||
id = this.ctx.getActivity().getResources().getIdentifier("config", "xml", this.ctx.getActivity().getPackageName());
|
|
||||||
if (id == 0) {
|
|
||||||
this.pluginConfigurationMissing();
|
|
||||||
//We have the error, we need to exit without crashing!
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
XmlResourceParser xml = this.ctx.getActivity().getResources().getXml(id);
|
|
||||||
int eventType = -1;
|
|
||||||
String service = "", pluginClass = "", paramType = "";
|
|
||||||
boolean onload = false;
|
|
||||||
boolean insideFeature = false;
|
|
||||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
|
||||||
if (eventType == XmlResourceParser.START_TAG) {
|
|
||||||
String strNode = xml.getName();
|
|
||||||
if (strNode.equals("url-filter")) {
|
|
||||||
Log.w(TAG, "Plugin " + service + " is using deprecated tag <url-filter>");
|
|
||||||
if (urlMap.get(service) == null) {
|
|
||||||
urlMap.put(service, new ArrayList<String>(2));
|
|
||||||
}
|
|
||||||
List<String> filters = urlMap.get(service);
|
|
||||||
filters.add(xml.getAttributeValue(null, "value"));
|
|
||||||
}
|
|
||||||
else if (strNode.equals("feature")) {
|
|
||||||
//Check for supported feature sets aka. plugins (Accelerometer, Geolocation, etc)
|
|
||||||
//Set the bit for reading params
|
|
||||||
insideFeature = true;
|
|
||||||
service = xml.getAttributeValue(null, "name");
|
|
||||||
}
|
|
||||||
else if (insideFeature && strNode.equals("param")) {
|
|
||||||
paramType = xml.getAttributeValue(null, "name");
|
|
||||||
if (paramType.equals("service")) // check if it is using the older service param
|
|
||||||
service = xml.getAttributeValue(null, "value");
|
|
||||||
else if (paramType.equals("package") || paramType.equals("android-package"))
|
|
||||||
pluginClass = xml.getAttributeValue(null,"value");
|
|
||||||
else if (paramType.equals("onload"))
|
|
||||||
onload = "true".equals(xml.getAttributeValue(null, "value"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (eventType == XmlResourceParser.END_TAG)
|
|
||||||
{
|
|
||||||
String strNode = xml.getName();
|
|
||||||
if (strNode.equals("feature") || strNode.equals("plugin"))
|
|
||||||
{
|
|
||||||
PluginEntry entry = new PluginEntry(service, pluginClass, onload);
|
|
||||||
this.addService(entry);
|
|
||||||
|
|
||||||
//Empty the strings to prevent plugin loading bugs
|
|
||||||
service = "";
|
|
||||||
pluginClass = "";
|
|
||||||
insideFeature = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
eventType = xml.next();
|
|
||||||
} catch (XmlPullParserException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all plugin objects.
|
* Delete all plugin objects.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Should not be exposed as public.
|
||||||
public void clearPluginObjects() {
|
public void clearPluginObjects() {
|
||||||
for (PluginEntry entry : this.entries.values()) {
|
pluginMap.clear();
|
||||||
entry.plugin = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create plugins objects that have onload set.
|
* Create plugins objects that have onload set.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // Should not be exposed as public.
|
||||||
public void startupPlugins() {
|
public void startupPlugins() {
|
||||||
for (PluginEntry entry : this.entries.values()) {
|
for (PluginEntry entry : entryMap.values()) {
|
||||||
if (entry.onload) {
|
if (entry.onload) {
|
||||||
entry.createPlugin(this.app, this.ctx);
|
getPlugin(entry.service);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,20 +134,6 @@ public class PluginManager {
|
|||||||
* plugin execute method.
|
* plugin execute method.
|
||||||
*/
|
*/
|
||||||
public void exec(final String service, final String action, final String callbackId, final String rawArgs) {
|
public void exec(final String service, final String action, final String callbackId, final String rawArgs) {
|
||||||
if (numPendingUiExecs.get() > 0) {
|
|
||||||
numPendingUiExecs.getAndIncrement();
|
|
||||||
this.ctx.getActivity().runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
execHelper(service, action, callbackId, rawArgs);
|
|
||||||
numPendingUiExecs.getAndDecrement();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
execHelper(service, action, callbackId, rawArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void execHelper(final String service, final String action, final String callbackId, final String rawArgs) {
|
|
||||||
CordovaPlugin plugin = getPlugin(service);
|
CordovaPlugin plugin = getPlugin(service);
|
||||||
if (plugin == null) {
|
if (plugin == null) {
|
||||||
Log.d(TAG, "exec() call to unknown plugin: " + service);
|
Log.d(TAG, "exec() call to unknown plugin: " + service);
|
||||||
@@ -272,15 +177,21 @@ public class PluginManager {
|
|||||||
* @return CordovaPlugin or null
|
* @return CordovaPlugin or null
|
||||||
*/
|
*/
|
||||||
public CordovaPlugin getPlugin(String service) {
|
public CordovaPlugin getPlugin(String service) {
|
||||||
PluginEntry entry = this.entries.get(service);
|
CordovaPlugin ret = pluginMap.get(service);
|
||||||
if (entry == null) {
|
if (ret == null) {
|
||||||
return null;
|
PluginEntry pe = entryMap.get(service);
|
||||||
|
if (pe == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (pe.plugin != null) {
|
||||||
|
ret = pe.plugin;
|
||||||
|
} else {
|
||||||
|
ret = instantiatePlugin(pe.pluginClass);
|
||||||
|
}
|
||||||
|
ret.privateInitialize(ctx, app, app.getPreferences());
|
||||||
|
pluginMap.put(service, ret);
|
||||||
}
|
}
|
||||||
CordovaPlugin plugin = entry.plugin;
|
return ret;
|
||||||
if (plugin == null) {
|
|
||||||
plugin = entry.createPlugin(this.app, this.ctx);
|
|
||||||
}
|
|
||||||
return plugin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -302,7 +213,16 @@ public class PluginManager {
|
|||||||
* @param entry The plugin entry
|
* @param entry The plugin entry
|
||||||
*/
|
*/
|
||||||
public void addService(PluginEntry entry) {
|
public void addService(PluginEntry entry) {
|
||||||
this.entries.put(entry.service, entry);
|
this.entryMap.put(entry.service, entry);
|
||||||
|
List<String> urlFilters = entry.getUrlFilters();
|
||||||
|
if (urlFilters != null) {
|
||||||
|
urlMap.put(entry.service, urlFilters);
|
||||||
|
}
|
||||||
|
if (entry.plugin != null) {
|
||||||
|
entry.plugin.privateInitialize(ctx, app, app.getPreferences());
|
||||||
|
pluginMap.put(entry.service, entry.plugin);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -311,10 +231,8 @@ public class PluginManager {
|
|||||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||||
*/
|
*/
|
||||||
public void onPause(boolean multitasking) {
|
public void onPause(boolean multitasking) {
|
||||||
for (PluginEntry entry : this.entries.values()) {
|
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||||
if (entry.plugin != null) {
|
plugin.onPause(multitasking);
|
||||||
entry.plugin.onPause(multitasking);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,10 +242,8 @@ public class PluginManager {
|
|||||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||||
*/
|
*/
|
||||||
public void onResume(boolean multitasking) {
|
public void onResume(boolean multitasking) {
|
||||||
for (PluginEntry entry : this.entries.values()) {
|
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||||
if (entry.plugin != null) {
|
plugin.onResume(multitasking);
|
||||||
entry.plugin.onResume(multitasking);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,10 +251,8 @@ public class PluginManager {
|
|||||||
* The final call you receive before your activity is destroyed.
|
* The final call you receive before your activity is destroyed.
|
||||||
*/
|
*/
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
for (PluginEntry entry : this.entries.values()) {
|
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||||
if (entry.plugin != null) {
|
plugin.onDestroy();
|
||||||
entry.plugin.onDestroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,12 +268,10 @@ public class PluginManager {
|
|||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
for (PluginEntry entry : this.entries.values()) {
|
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||||
if (entry.plugin != null) {
|
obj = plugin.onMessage(id, data);
|
||||||
obj = entry.plugin.onMessage(id, data);
|
if (obj != null) {
|
||||||
if (obj != null) {
|
return obj;
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -369,10 +281,8 @@ public class PluginManager {
|
|||||||
* Called when the activity receives a new intent.
|
* Called when the activity receives a new intent.
|
||||||
*/
|
*/
|
||||||
public void onNewIntent(Intent intent) {
|
public void onNewIntent(Intent intent) {
|
||||||
for (PluginEntry entry : this.entries.values()) {
|
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||||
if (entry.plugin != null) {
|
plugin.onNewIntent(intent);
|
||||||
entry.plugin.onNewIntent(intent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,7 +297,7 @@ public class PluginManager {
|
|||||||
// Instead, plugins should not include <url-filter> and instead ensure
|
// Instead, plugins should not include <url-filter> and instead ensure
|
||||||
// that they are loaded before this function is called (either by setting
|
// that they are loaded before this function is called (either by setting
|
||||||
// the onload <param> or by making an exec() call to them)
|
// the onload <param> or by making an exec() call to them)
|
||||||
for (PluginEntry entry : this.entries.values()) {
|
for (PluginEntry entry : this.entryMap.values()) {
|
||||||
List<String> urlFilters = urlMap.get(entry.service);
|
List<String> urlFilters = urlMap.get(entry.service);
|
||||||
if (urlFilters != null) {
|
if (urlFilters != null) {
|
||||||
for (String s : urlFilters) {
|
for (String s : urlFilters) {
|
||||||
@@ -395,8 +305,9 @@ public class PluginManager {
|
|||||||
return getPlugin(entry.service).onOverrideUrlLoading(url);
|
return getPlugin(entry.service).onOverrideUrlLoading(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (entry.plugin != null) {
|
} else {
|
||||||
if (entry.plugin.onOverrideUrlLoading(url)) {
|
CordovaPlugin plugin = pluginMap.get(entry.service);
|
||||||
|
if (plugin != null && plugin.onOverrideUrlLoading(url)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,54 +319,38 @@ public class PluginManager {
|
|||||||
* Called when the app navigates or refreshes.
|
* Called when the app navigates or refreshes.
|
||||||
*/
|
*/
|
||||||
public void onReset() {
|
public void onReset() {
|
||||||
Iterator<PluginEntry> it = this.entries.values().iterator();
|
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||||
while (it.hasNext()) {
|
plugin.onReset();
|
||||||
CordovaPlugin plugin = it.next().plugin;
|
|
||||||
if (plugin != null) {
|
|
||||||
plugin.onReset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void pluginConfigurationMissing() {
|
|
||||||
LOG.e(TAG, "=====================================================================================");
|
|
||||||
LOG.e(TAG, "ERROR: config.xml is missing. Add res/xml/config.xml to your project.");
|
|
||||||
LOG.e(TAG, "https://git-wip-us.apache.org/repos/asf?p=cordova-android.git;a=blob;f=framework/res/xml/config.xml");
|
|
||||||
LOG.e(TAG, "=====================================================================================");
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri remapUri(Uri uri) {
|
Uri remapUri(Uri uri) {
|
||||||
for (PluginEntry entry : this.entries.values()) {
|
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||||
if (entry.plugin != null) {
|
Uri ret = plugin.remapUri(uri);
|
||||||
Uri ret = entry.plugin.remapUri(uri);
|
if (ret != null) {
|
||||||
if (ret != null) {
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PluginManagerService extends CordovaPlugin {
|
/**
|
||||||
@Override
|
* Create a plugin based on class name.
|
||||||
public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
|
*/
|
||||||
if ("startup".equals(action)) {
|
private CordovaPlugin instantiatePlugin(String className) {
|
||||||
// The onPageStarted event of CordovaWebViewClient resets the queue of messages to be returned to javascript in response
|
CordovaPlugin ret = null;
|
||||||
// to exec calls. Since this event occurs on the UI thread and exec calls happen on the WebCore thread it is possible
|
try {
|
||||||
// that onPageStarted occurs after exec calls have started happening on a new page, which can cause the message queue
|
Class<?> c = null;
|
||||||
// to be reset between the queuing of a new message and its retrieval by javascript. To avoid this from happening,
|
if ((className != null) && !("".equals(className))) {
|
||||||
// javascript always sends a "startup" exec upon loading a new page which causes all future exec calls to happen on the UI
|
c = Class.forName(className);
|
||||||
// thread (and hence after onPageStarted) until there are no more pending exec calls remaining.
|
|
||||||
numPendingUiExecs.getAndIncrement();
|
|
||||||
ctx.getActivity().runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
numPendingUiExecs.getAndDecrement();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
if (c != null & CordovaPlugin.class.isAssignableFrom(c)) {
|
||||||
|
ret = (CordovaPlugin) c.newInstance();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.out.println("Error adding plugin " + className + ".");
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,15 +120,15 @@ public class Whitelist {
|
|||||||
whiteList = null;
|
whiteList = null;
|
||||||
}
|
}
|
||||||
else { // specific access
|
else { // specific access
|
||||||
Pattern parts = Pattern.compile("^((\\*|[A-Za-z-]+)://)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?");
|
Pattern parts = Pattern.compile("^((\\*|[A-Za-z-]+):(//)?)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?");
|
||||||
Matcher m = parts.matcher(origin);
|
Matcher m = parts.matcher(origin);
|
||||||
if (m.matches()) {
|
if (m.matches()) {
|
||||||
String scheme = m.group(2);
|
String scheme = m.group(2);
|
||||||
String host = m.group(3);
|
String host = m.group(4);
|
||||||
// Special case for two urls which are allowed to have empty hosts
|
// Special case for two urls which are allowed to have empty hosts
|
||||||
if (("file".equals(scheme) || "content".equals(scheme)) && host == null) host = "*";
|
if (("file".equals(scheme) || "content".equals(scheme)) && host == null) host = "*";
|
||||||
String port = m.group(7);
|
String port = m.group(8);
|
||||||
String path = m.group(8);
|
String path = m.group(9);
|
||||||
if (scheme == null) {
|
if (scheme == null) {
|
||||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||||
whiteList.add(new URLPattern("http", host, port, path));
|
whiteList.add(new URLPattern("http", host, port, path));
|
||||||
|
|||||||
+27
-20
@@ -1,21 +1,28 @@
|
|||||||
{
|
{
|
||||||
"name": "cordova-android",
|
"name": "cordova-android",
|
||||||
"version": "3.4.0",
|
"version": "3.6.4",
|
||||||
"description": "cordova-android release",
|
"description": "cordova-android release",
|
||||||
"main": "bin/create",
|
"main": "bin/create",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git-wip-us.apache.org/repos/asf/cordova-android.git"
|
"url": "https://git-wip-us.apache.org/repos/asf/cordova-android.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"android",
|
"android",
|
||||||
"cordova",
|
"cordova",
|
||||||
"apache"
|
"apache"
|
||||||
],
|
],
|
||||||
"author": "Apache Software Foundation",
|
"scripts": {
|
||||||
"license": "Apache version 2.0",
|
"test": "jasmine-node --color spec"
|
||||||
"dependencies": {
|
},
|
||||||
"q": "^0.9.0",
|
"author": "Apache Software Foundation",
|
||||||
"shelljs": "^0.2.6"
|
"license": "Apache version 2.0",
|
||||||
}
|
"dependencies": {
|
||||||
}
|
"q": "^0.9.0",
|
||||||
|
"shelljs": "^0.2.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"jasmine-node": "~1",
|
||||||
|
"promise-matchers": "~0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
/* jshint laxcomma:true */
|
||||||
|
|
||||||
|
require("promise-matchers");
|
||||||
|
|
||||||
|
var create = require("../bin/lib/create");
|
||||||
|
|
||||||
|
describe("create", function () {
|
||||||
|
describe("validatePackageName", function() {
|
||||||
|
var valid = [
|
||||||
|
"org.apache.mobilespec"
|
||||||
|
, "com.example"
|
||||||
|
, "com.42floors.package"
|
||||||
|
];
|
||||||
|
var invalid = [
|
||||||
|
""
|
||||||
|
, "com.class.is.bad"
|
||||||
|
, "0com.example.mobilespec"
|
||||||
|
, "c-m.e@a!p%e.mobilespec"
|
||||||
|
, "notenoughdots"
|
||||||
|
, ".starts.with.a.dot"
|
||||||
|
, "ends.with.a.dot."
|
||||||
|
, "_underscore.anything"
|
||||||
|
, "underscore._something"
|
||||||
|
, "_underscore._all._the._things"
|
||||||
|
];
|
||||||
|
|
||||||
|
valid.forEach(function(package_name) {
|
||||||
|
it("should accept " + package_name, function(done) {
|
||||||
|
expect(create.validatePackageName(package_name)).toHaveBeenResolved(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
invalid.forEach(function(package_name) {
|
||||||
|
it("should reject " + package_name, function(done) {
|
||||||
|
expect(create.validatePackageName(package_name)).toHaveBeenRejected(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("validateProjectName", function() {
|
||||||
|
var valid = [
|
||||||
|
"mobilespec"
|
||||||
|
, "package_name"
|
||||||
|
, "PackageName"
|
||||||
|
, "CordovaLib"
|
||||||
|
];
|
||||||
|
var invalid = [
|
||||||
|
""
|
||||||
|
, "0startswithdigit"
|
||||||
|
, "CordovaActivity"
|
||||||
|
];
|
||||||
|
|
||||||
|
valid.forEach(function(project_name) {
|
||||||
|
it("should accept " + project_name, function(done) {
|
||||||
|
expect(create.validateProjectName(project_name)).toHaveBeenResolved(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
invalid.forEach(function(project_name) {
|
||||||
|
it("should reject " + project_name, function(done) {
|
||||||
|
expect(create.validateProjectName(project_name)).toHaveBeenRejected(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
|
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||||
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="gen"/>
|
<classpathentry kind="src" path="gen"/>
|
||||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
|
||||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
|
||||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
|
||||||
<classpathentry kind="output" path="bin/classes"/>
|
<classpathentry kind="output" path="bin/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19"/>
|
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="19"/>
|
||||||
|
|
||||||
<instrumentation
|
<instrumentation
|
||||||
android:name="android.test.InstrumentationTestRunner"
|
android:name="android.test.InstrumentationTestRunner"
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
android:windowSoftInputMode="adjustPan"
|
android:windowSoftInputMode="adjustPan"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:name="org.apache.cordova.test.CordovaActivity" >
|
android:name="org.apache.cordova.test.MainTestActivity" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.SAMPLE_CODE" />
|
<category android:name="android.intent.category.SAMPLE_CODE" />
|
||||||
@@ -255,5 +255,15 @@
|
|||||||
<category android:name="android.intent.category.SAMPLE_CODE" />
|
<category android:name="android.intent.category.SAMPLE_CODE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:windowSoftInputMode="adjustPan"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android:name="org.apache.cordova.test.SabotagedActivity" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.SAMPLE_CODE" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
+4
-2
@@ -26,8 +26,10 @@ These tests are designed to verify Android native features and other Android spe
|
|||||||
|
|
||||||
There really isn't any manual setup to do. The ant script takes care of that.
|
There really isn't any manual setup to do. The ant script takes care of that.
|
||||||
You don't even need to compile cordova-x.y.z.jar or copy it, because
|
You don't even need to compile cordova-x.y.z.jar or copy it, because
|
||||||
project.properties has a library reference to ../framework. However, Robotium has to be
|
project.properties has a library reference to ../framework. However, Robotium
|
||||||
installed for the onScrollChanged tests to work correctly. It can be found at https://code.google.com/p/robotium/
|
has to be installed for the onScrollChanged tests to work correctly. It can be
|
||||||
|
found at https://code.google.com/p/robotium/ and the jar should be put in the
|
||||||
|
'libs' directory'.
|
||||||
|
|
||||||
To run manually from command line:
|
To run manually from command line:
|
||||||
|
|
||||||
|
|||||||
Vendored
+21
-2
@@ -1,3 +1,22 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
cordova.define('cordova/plugin_list', function(require, exports, module) {
|
cordova.define('cordova/plugin_list', function(require, exports, module) {
|
||||||
module.exports = []
|
module.exports = [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,25 @@
|
|||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
KIND, either express or implied. See the License for the
|
KIND, either express or implied. See the License for the
|
||||||
specific language governing permissions and limitations
|
specific language governing permissions and limitations
|
||||||
under the License.$
|
under the License.
|
||||||
-->
|
-->
|
||||||
This is an error page.
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=320, user-scalable=no" />
|
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||||
|
<title>Expected Error</title>
|
||||||
|
<link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title">
|
||||||
|
<script type="text/javascript" charset="utf-8" src="../cordova.js"></script>
|
||||||
|
<script type="text/javascript" charset="utf-8" src="../main.js"></script>
|
||||||
|
</head>
|
||||||
|
<body onload="init();" id="stage" class="theme">
|
||||||
|
<h1>Expected Error</h1>
|
||||||
|
<div id="info">
|
||||||
|
<h4>Cordova: <span id="cordova"> </span></h4>
|
||||||
|
<h4>Deviceready: <span id="deviceready"> </span></h4>
|
||||||
|
</div>
|
||||||
|
<div id="info">
|
||||||
|
This is an expected error page because the initial href doesn't exist.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body onload="init();" id="stage" class="theme">
|
<body onload="init();" id="stage" class="theme">
|
||||||
<h1>Cordova Android Tests</h1>
|
<h1>Cordova Android Native Tests</h1>
|
||||||
<div id="info">
|
<div id="info">
|
||||||
<h4>Cordova: <span id="cordova"> </span></h4>
|
<h4>Cordova: <span id="cordova"> </span></h4>
|
||||||
<h4>Deviceready: <span id="deviceready"> </span></h4>
|
<h4>Deviceready: <span id="deviceready"> </span></h4>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<h4>Deviceready: <span id="deviceready"> </span></h4>
|
<h4>Deviceready: <span id="deviceready"> </span></h4>
|
||||||
</div>
|
</div>
|
||||||
<div id="info">
|
<div id="info">
|
||||||
<h4>The menu items should be:</h4>
|
<h4>The options menu items should be:</h4>
|
||||||
<li>Item1<br>
|
<li>Item1<br>
|
||||||
<li>Item2<br>
|
<li>Item2<br>
|
||||||
<li>Item3<br>
|
<li>Item3<br>
|
||||||
|
|||||||
@@ -18,5 +18,5 @@
|
|||||||
under the License.
|
under the License.
|
||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">CordovaTests</string>
|
<string name="app_name">CordovaNativeTests</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
+23
-3
@@ -1,4 +1,22 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
-->
|
||||||
<widget id="io.cordova.helloCordova" version="2.0.0" xmlns="http://www.w3.org/ns/widgets">
|
<widget id="io.cordova.helloCordova" version="2.0.0" xmlns="http://www.w3.org/ns/widgets">
|
||||||
<name>Hello Cordova</name>
|
<name>Hello Cordova</name>
|
||||||
<description>
|
<description>
|
||||||
@@ -8,17 +26,19 @@
|
|||||||
Apache Cordova Team
|
Apache Cordova Team
|
||||||
</author>
|
</author>
|
||||||
<access origin="*.apache.org" />
|
<access origin="*.apache.org" />
|
||||||
|
<access origin="http://*.google.com/*" />
|
||||||
|
<access origin="https://*.google.com/*" />
|
||||||
|
<access origin="https://*.googleapis.com/*" />
|
||||||
|
<access origin="https://*.gstatic.com/*" />
|
||||||
<content src="index.html" />
|
<content src="index.html" />
|
||||||
<preference name="loglevel" value="DEBUG" />
|
<preference name="loglevel" value="DEBUG" />
|
||||||
<preference name="useBrowserHistory" value="true" />
|
<preference name="useBrowserHistory" value="true" />
|
||||||
<preference name="exit-on-suspend" value="false" />
|
<preference name="exit-on-suspend" value="false" />
|
||||||
|
<preference name="showTitle" value="true" />
|
||||||
<feature name="Activity">
|
<feature name="Activity">
|
||||||
<param name="android-package" value="org.apache.cordova.test.ActivityPlugin" />
|
<param name="android-package" value="org.apache.cordova.test.ActivityPlugin" />
|
||||||
</feature>
|
</feature>
|
||||||
<feature name="PluginStub">
|
<feature name="PluginStub">
|
||||||
<param name="android-package" value="org.apache.cordova.pluginApi.pluginStub" />
|
<param name="android-package" value="org.apache.cordova.pluginApi.pluginStub" />
|
||||||
</feature>
|
</feature>
|
||||||
<feature name="App">
|
|
||||||
<param name="android-package" value="org.apache.cordova.App" />
|
|
||||||
</feature>
|
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ package org.apache.cordova.test;
|
|||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import org.apache.cordova.Config;
|
||||||
|
import org.apache.cordova.CordovaChromeClient;
|
||||||
import org.apache.cordova.CordovaWebView;
|
import org.apache.cordova.CordovaWebView;
|
||||||
import org.apache.cordova.CordovaInterface;
|
import org.apache.cordova.CordovaInterface;
|
||||||
import org.apache.cordova.CordovaPlugin;
|
import org.apache.cordova.CordovaPlugin;
|
||||||
import org.apache.cordova.LOG;
|
import org.apache.cordova.CordovaWebViewClient;
|
||||||
import org.apache.cordova.test.R;
|
import org.apache.cordova.test.R;
|
||||||
import org.apache.cordova.test.R.id;
|
|
||||||
import org.apache.cordova.test.R.layout;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -47,7 +47,12 @@ public class CordovaWebViewTestActivity extends Activity implements CordovaInter
|
|||||||
|
|
||||||
setContentView(R.layout.main);
|
setContentView(R.layout.main);
|
||||||
|
|
||||||
|
//CB-7238: This has to be added now, because it got removed from somewhere else
|
||||||
|
Config.init(this);
|
||||||
|
|
||||||
cordovaWebView = (CordovaWebView) findViewById(R.id.cordovaWebView);
|
cordovaWebView = (CordovaWebView) findViewById(R.id.cordovaWebView);
|
||||||
|
cordovaWebView.init(this, new CordovaWebViewClient(this, cordovaWebView), new CordovaChromeClient(this, cordovaWebView),
|
||||||
|
Config.getPluginEntries(), Config.getWhitelist(), Config.getExternalWhitelist(), Config.getPreferences());
|
||||||
|
|
||||||
cordovaWebView.loadUrl("file:///android_asset/www/index.html");
|
cordovaWebView.loadUrl("file:///android_asset/www/index.html");
|
||||||
|
|
||||||
@@ -100,4 +105,4 @@ public class CordovaWebViewTestActivity extends Activity implements CordovaInter
|
|||||||
cordovaWebView.handleDestroy();
|
cordovaWebView.handleDestroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -18,12 +18,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova.test;
|
package org.apache.cordova.test;
|
||||||
|
|
||||||
import org.apache.cordova.DroidGap;
|
import org.apache.cordova.CordovaActivity;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
public class CordovaActivity extends DroidGap {
|
public class MainTestActivity extends CordovaActivity {
|
||||||
/** Called when the activity is first created. */
|
/** Called when the activity is first created. */
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cordova.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
|
import org.apache.cordova.Config;
|
||||||
|
import org.apache.cordova.CordovaActivity;
|
||||||
|
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class SabotagedActivity extends CordovaActivity {
|
||||||
|
|
||||||
|
private String BAD_ASSET = "www/error.html";
|
||||||
|
private String LOG_TAG = "SabotagedActivity";
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// copyErrorAsset();
|
||||||
|
super.init();
|
||||||
|
super.loadUrl(Config.getStartUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sometimes we need to move code around before we can do anything. This will
|
||||||
|
* copy the bad code out of the assets before we initalize Cordova so that when Cordova actually
|
||||||
|
* initializes, we have something for it to navigate to.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private void copyErrorAsset () {
|
||||||
|
AssetManager assetManager = getAssets();
|
||||||
|
String[] files = null;
|
||||||
|
try {
|
||||||
|
files = assetManager.list(BAD_ASSET);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(LOG_TAG, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String filename : files) {
|
||||||
|
InputStream in = null;
|
||||||
|
OutputStream out = null;
|
||||||
|
try {
|
||||||
|
in = assetManager.open(BAD_ASSET);
|
||||||
|
out = new FileOutputStream(Environment.getExternalStorageDirectory().toString() +"/" + filename);
|
||||||
|
copy(in, out);
|
||||||
|
in.close();
|
||||||
|
in = null;
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
out = null;
|
||||||
|
} catch(Exception e) {
|
||||||
|
Log.e("tag", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Quick and Dirty Copy!
|
||||||
|
private void copy(InputStream in, OutputStream out) throws IOException {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int read;
|
||||||
|
while((read = in.read(buffer)) != -1){
|
||||||
|
out.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class backbuttonmultipage extends DroidGap {
|
public class backbuttonmultipage extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ import android.os.Bundle;
|
|||||||
|
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class background extends DroidGap {
|
public class background extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
//super.init(new FixWebView(this), new CordovaWebViewClient(this), new CordovaChromeClient(this));
|
//super.init(new FixWebView(this), new CordovaWebViewClient(this), new CordovaChromeClient(this));
|
||||||
super.setBooleanProperty("keepRunning", false);
|
preferences.set("keepRunning", false);
|
||||||
super.loadUrl("file:///android_asset/www/background/index.html");
|
super.loadUrl("file:///android_asset/www/background/index.html");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,18 +22,15 @@ import android.graphics.Color;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class backgroundcolor extends DroidGap {
|
public class backgroundcolor extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Properties must be set before init() is called, since some are processed during init().
|
|
||||||
|
|
||||||
// backgroundColor can also be set in cordova.xml, but you must use the number equivalent of the color. For example, Color.RED is
|
// backgroundColor can also be set in cordova.xml, but you must use the number equivalent of the color. For example, Color.RED is
|
||||||
// <preference name="backgroundColor" value="-65536" />
|
// <preference name="backgroundColor" value="-65536" />
|
||||||
super.setIntegerProperty("backgroundColor", Color.GREEN);
|
preferences.set("backgroundColor", Color.GREEN);
|
||||||
|
|
||||||
super.init();
|
|
||||||
super.loadUrl("file:///android_asset/www/backgroundcolor/index.html");
|
super.loadUrl("file:///android_asset/www/backgroundcolor/index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class basicauth extends DroidGap {
|
public class basicauth extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -31,11 +31,13 @@ public class basicauth extends DroidGap {
|
|||||||
AuthenticationToken token = new AuthenticationToken();
|
AuthenticationToken token = new AuthenticationToken();
|
||||||
token.setUserName("test");
|
token.setUserName("test");
|
||||||
token.setPassword("test");
|
token.setPassword("test");
|
||||||
super.setAuthenticationToken(token, "browserspy.dk:80", "BrowserSpy.dk - HTTP Password Test");
|
// classic webview includes port in hostname, Chromium webview does not. Handle both here.
|
||||||
|
// BTW, the realm is optional.
|
||||||
|
setAuthenticationToken(token, "browserspy.dk:80", "BrowserSpy.dk - HTTP Password Test");
|
||||||
|
setAuthenticationToken(token, "browserspy.dk", "BrowserSpy.dk - HTTP Password Test");
|
||||||
|
|
||||||
// Add web site to whitelist
|
// Add web site to whitelist
|
||||||
Config.init();
|
Config.getWhitelist().addWhiteListEntry("http://browserspy.dk/*", true);
|
||||||
Config.addWhiteListEntry("http://browserspy.dk*", true);
|
|
||||||
|
|
||||||
// Load test
|
// Load test
|
||||||
super.loadUrl("file:///android_asset/www/basicauth/index.html");
|
super.loadUrl("file:///android_asset/www/basicauth/index.html");
|
||||||
|
|||||||
@@ -21,12 +21,11 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class errorurl extends DroidGap {
|
public class errorurl extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
super.init();
|
preferences.set("errorUrl", "file:///android_asset/www/htmlnotfound/error.html");
|
||||||
this.setStringProperty("errorUrl", "file:///android_asset/www/htmlnotfound/error.html");
|
|
||||||
super.loadUrl("file:///android_asset/www/htmlnotfound/index.html");
|
super.loadUrl("file:///android_asset/www/htmlnotfound/index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class fullscreen extends DroidGap {
|
public class fullscreen extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -30,7 +30,7 @@ public class fullscreen extends DroidGap {
|
|||||||
|
|
||||||
// fullscreen can also be set in cordova.xml. For example,
|
// fullscreen can also be set in cordova.xml. For example,
|
||||||
// <preference name="fullscreen" value="true" />
|
// <preference name="fullscreen" value="true" />
|
||||||
super.setBooleanProperty("fullscreen", true);
|
preferences.set("fullscreen", true);
|
||||||
|
|
||||||
super.init();
|
super.init();
|
||||||
super.loadUrl("file:///android_asset/www/fullscreen/index.html");
|
super.loadUrl("file:///android_asset/www/fullscreen/index.html");
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class htmlnotfound extends DroidGap {
|
public class htmlnotfound extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class iframe extends DroidGap {
|
public class iframe extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public class BackButtonMultiPageTest extends ActivityInstrumentationTestCase2<ba
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
String url = testView.getUrl();
|
String url = testView.getUrl();
|
||||||
assertTrue(url.endsWith("sample2.html"));
|
assertEquals("file:///android_asset/www/backbuttonmultipage/sample2.html", url);
|
||||||
testView.sendJavascript("window.location = 'sample3.html';"); }
|
testView.sendJavascript("window.location = 'sample3.html';"); }
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -84,8 +84,8 @@ public class BackButtonMultiPageTest extends ActivityInstrumentationTestCase2<ba
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
String url = testView.getUrl();
|
String url = testView.getUrl();
|
||||||
assertTrue(url.endsWith("sample3.html"));
|
assertEquals("file:///android_asset/www/backbuttonmultipage/sample3.html", url);
|
||||||
testView.backHistory();
|
assertTrue(testView.backHistory());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
sleep();
|
sleep();
|
||||||
@@ -93,8 +93,8 @@ public class BackButtonMultiPageTest extends ActivityInstrumentationTestCase2<ba
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
String url = testView.getUrl();
|
String url = testView.getUrl();
|
||||||
assertTrue(url.endsWith("sample2.html"));
|
assertEquals("file:///android_asset/www/backbuttonmultipage/sample2.html", url);
|
||||||
testView.backHistory();
|
assertTrue(testView.backHistory());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
sleep();
|
sleep();
|
||||||
@@ -102,7 +102,7 @@ public class BackButtonMultiPageTest extends ActivityInstrumentationTestCase2<ba
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
String url = testView.getUrl();
|
String url = testView.getUrl();
|
||||||
assertTrue(url.endsWith("index.html"));
|
assertEquals("file:///android_asset/www/backbuttonmultipage/index.html", url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,16 +21,16 @@ package org.apache.cordova.test.junit;
|
|||||||
|
|
||||||
import org.apache.cordova.CordovaWebView;
|
import org.apache.cordova.CordovaWebView;
|
||||||
import org.apache.cordova.PluginManager;
|
import org.apache.cordova.PluginManager;
|
||||||
import org.apache.cordova.test.CordovaActivity;
|
import org.apache.cordova.test.MainTestActivity;
|
||||||
|
|
||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
import android.test.ActivityInstrumentationTestCase2;
|
import android.test.ActivityInstrumentationTestCase2;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
public class CordovaActivityTest extends ActivityInstrumentationTestCase2<CordovaActivity> {
|
public class CordovaActivityTest extends ActivityInstrumentationTestCase2<MainTestActivity> {
|
||||||
|
|
||||||
private CordovaActivity testActivity;
|
private MainTestActivity testActivity;
|
||||||
private FrameLayout containerView;
|
private FrameLayout containerView;
|
||||||
private LinearLayout innerContainer;
|
private LinearLayout innerContainer;
|
||||||
private CordovaWebView testView;
|
private CordovaWebView testView;
|
||||||
@@ -40,7 +40,7 @@ public class CordovaActivityTest extends ActivityInstrumentationTestCase2<Cordov
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public CordovaActivityTest()
|
public CordovaActivityTest()
|
||||||
{
|
{
|
||||||
super("org.apache.cordova.test",CordovaActivity.class);
|
super("org.apache.cordova.test",MainTestActivity.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
@@ -68,33 +68,7 @@ public class CordovaActivityTest extends ActivityInstrumentationTestCase2<Cordov
|
|||||||
String className = innerContainer.getClass().getSimpleName();
|
String className = innerContainer.getClass().getSimpleName();
|
||||||
assertTrue(className.equals("LinearLayoutSoftKeyboardDetect"));
|
assertTrue(className.equals("LinearLayoutSoftKeyboardDetect"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void testPauseAndResume() throws Throwable
|
|
||||||
{
|
|
||||||
runTestOnUiThread(new Runnable() {
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
mInstr.callActivityOnPause(testActivity);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
sleep();
|
|
||||||
runTestOnUiThread(new Runnable() {
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
assertTrue(testView.isPaused());
|
|
||||||
mInstr.callActivityOnResume(testActivity);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
sleep();
|
|
||||||
runTestOnUiThread(new Runnable() {
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
assertFalse(testView.isPaused());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sleep() {
|
private void sleep() {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(TIMEOUT);
|
Thread.sleep(TIMEOUT);
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class ErrorUrlTest extends ActivityInstrumentationTestCase2<errorurl> {
|
|||||||
String good_url = "file:///android_asset/www/htmlnotfound/error.html";
|
String good_url = "file:///android_asset/www/htmlnotfound/error.html";
|
||||||
String url = testView.getUrl();
|
String url = testView.getUrl();
|
||||||
assertNotNull(url);
|
assertNotNull(url);
|
||||||
assertTrue(url.equals(good_url));
|
assertEquals(good_url, url);
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.cordova.test.junit;
|
|
||||||
|
|
||||||
import org.apache.cordova.CordovaWebView;
|
|
||||||
import org.apache.cordova.CordovaChromeClient;
|
|
||||||
import org.apache.cordova.PluginManager;
|
|
||||||
import org.apache.cordova.test.CordovaWebViewTestActivity;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.test.ActivityInstrumentationTestCase2;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
public class GapClientTest extends ActivityInstrumentationTestCase2<CordovaWebViewTestActivity> {
|
|
||||||
|
|
||||||
private CordovaWebViewTestActivity testActivity;
|
|
||||||
private FrameLayout containerView;
|
|
||||||
private LinearLayout innerContainer;
|
|
||||||
private View testView;
|
|
||||||
private String rString;
|
|
||||||
private CordovaChromeClient appCode;
|
|
||||||
|
|
||||||
public GapClientTest() {
|
|
||||||
super("org.apache.cordova.test.activities",CordovaWebViewTestActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setUp() throws Exception{
|
|
||||||
super.setUp();
|
|
||||||
testActivity = this.getActivity();
|
|
||||||
containerView = (FrameLayout) testActivity.findViewById(android.R.id.content);
|
|
||||||
innerContainer = (LinearLayout) containerView.getChildAt(0);
|
|
||||||
testView = innerContainer.getChildAt(0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPreconditions(){
|
|
||||||
assertNotNull(innerContainer);
|
|
||||||
assertNotNull(testView);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testForCordovaView() {
|
|
||||||
String className = testView.getClass().getSimpleName();
|
|
||||||
assertTrue(className.equals("CordovaWebView"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cordova.test.junit;
|
||||||
|
|
||||||
|
import org.apache.cordova.CordovaWebView;
|
||||||
|
import org.apache.cordova.test.SabotagedActivity;
|
||||||
|
import org.apache.cordova.test.splashscreen;
|
||||||
|
|
||||||
|
import android.app.Instrumentation;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.test.ActivityInstrumentationTestCase2;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
|
||||||
|
public class IntentUriOverrideTest extends ActivityInstrumentationTestCase2<SabotagedActivity> {
|
||||||
|
|
||||||
|
private int TIMEOUT = 1000;
|
||||||
|
|
||||||
|
private SabotagedActivity testActivity;
|
||||||
|
private FrameLayout containerView;
|
||||||
|
private LinearLayout innerContainer;
|
||||||
|
private CordovaWebView testView;
|
||||||
|
private Instrumentation mInstr;
|
||||||
|
private String BAD_URL = "file:///sdcard/download/wl-exploit.htm";
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public IntentUriOverrideTest()
|
||||||
|
{
|
||||||
|
super("org.apache.cordova.test",SabotagedActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
mInstr = this.getInstrumentation();
|
||||||
|
Intent badIntent = new Intent();
|
||||||
|
badIntent.setClassName("org.apache.cordova.test", "org.apache.cordova.test.SabotagedActivity");
|
||||||
|
badIntent.putExtra("url", BAD_URL);
|
||||||
|
setActivityIntent(badIntent);
|
||||||
|
testActivity = getActivity();
|
||||||
|
containerView = (FrameLayout) testActivity.findViewById(android.R.id.content);
|
||||||
|
innerContainer = (LinearLayout) containerView.getChildAt(0);
|
||||||
|
testView = (CordovaWebView) innerContainer.getChildAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testPreconditions(){
|
||||||
|
assertNotNull(innerContainer);
|
||||||
|
assertNotNull(testView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testChangeStartUrl() throws Throwable
|
||||||
|
{
|
||||||
|
runTestOnUiThread(new Runnable() {
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
sleep();
|
||||||
|
boolean isBadUrl = testView.getUrl().equals(BAD_URL);
|
||||||
|
assertFalse(isBadUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sleep() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(TIMEOUT);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
fail("Unexpected Timeout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
*/
|
|
||||||
package org.apache.cordova.test.junit;
|
|
||||||
|
|
||||||
import org.apache.cordova.CordovaPlugin;
|
|
||||||
import org.apache.cordova.CordovaWebView;
|
|
||||||
import org.apache.cordova.ScrollEvent;
|
|
||||||
import org.apache.cordova.pluginApi.pluginStub;
|
|
||||||
import org.apache.cordova.test.CordovaWebViewTestActivity;
|
|
||||||
import org.apache.cordova.test.R;
|
|
||||||
|
|
||||||
import com.jayway.android.robotium.solo.By;
|
|
||||||
import com.jayway.android.robotium.solo.Solo;
|
|
||||||
|
|
||||||
import android.test.ActivityInstrumentationTestCase2;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
public class MessageTest extends
|
|
||||||
ActivityInstrumentationTestCase2<CordovaWebViewTestActivity> {
|
|
||||||
private CordovaWebViewTestActivity testActivity;
|
|
||||||
private CordovaWebView testView;
|
|
||||||
private pluginStub testPlugin;
|
|
||||||
private int TIMEOUT = 1000;
|
|
||||||
|
|
||||||
private Solo solo;
|
|
||||||
|
|
||||||
public MessageTest() {
|
|
||||||
super("org.apache.cordova.test.activities", CordovaWebViewTestActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
testActivity = this.getActivity();
|
|
||||||
testView = (CordovaWebView) testActivity.findViewById(R.id.cordovaWebView);
|
|
||||||
testPlugin = (pluginStub) testView.pluginManager.getPlugin("PluginStub");
|
|
||||||
solo = new Solo(getInstrumentation(), getActivity());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testOnScrollChanged()
|
|
||||||
{
|
|
||||||
solo.waitForWebElement(By.textContent("Cordova Android Tests"));
|
|
||||||
solo.scrollDown();
|
|
||||||
sleep();
|
|
||||||
Object data = testPlugin.data;
|
|
||||||
assertTrue(data.getClass().getSimpleName().equals("ScrollEvent"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void sleep() {
|
|
||||||
try {
|
|
||||||
Thread.sleep(TIMEOUT);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
fail("Unexpected Timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
package org.apache.cordova.test.junit;
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import org.apache.cordova.CordovaWebView;
|
|
||||||
import org.apache.cordova.CordovaWebViewClient;
|
|
||||||
import org.apache.cordova.CordovaChromeClient;
|
|
||||||
import org.apache.cordova.test.userwebview;
|
|
||||||
|
|
||||||
import android.test.ActivityInstrumentationTestCase2;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
public class UserWebViewTest extends ActivityInstrumentationTestCase2<userwebview> {
|
|
||||||
|
|
||||||
public UserWebViewTest ()
|
|
||||||
{
|
|
||||||
super(userwebview.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int TIMEOUT = 1000;
|
|
||||||
userwebview testActivity;
|
|
||||||
private FrameLayout containerView;
|
|
||||||
private LinearLayout innerContainer;
|
|
||||||
private CordovaWebView testView;
|
|
||||||
|
|
||||||
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
testActivity = this.getActivity();
|
|
||||||
containerView = (FrameLayout) testActivity.findViewById(android.R.id.content);
|
|
||||||
innerContainer = (LinearLayout) containerView.getChildAt(0);
|
|
||||||
testView = (CordovaWebView) innerContainer.getChildAt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPreconditions(){
|
|
||||||
assertNotNull(innerContainer);
|
|
||||||
assertNotNull(testView);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCustom()
|
|
||||||
{
|
|
||||||
assertTrue(CordovaWebView.class.isInstance(testView));
|
|
||||||
assertTrue(CordovaWebViewClient.class.isInstance(testActivity.testViewClient));
|
|
||||||
assertTrue(CordovaChromeClient.class.isInstance(testActivity.testChromeClient));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void sleep() {
|
|
||||||
try {
|
|
||||||
Thread.sleep(TIMEOUT);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
fail("Unexpected Timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -21,7 +21,7 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class lifecycle extends DroidGap {
|
public class lifecycle extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class loading extends DroidGap {
|
public class loading extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
super.setStringProperty("loadingDialog", "Testing,Loading...");
|
preferences.set("loadingDialog", "Testing,Loading...");
|
||||||
super.loadUrl("http://www.google.com");
|
super.loadUrl("http://www.google.com");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,11 @@ import android.view.ContextMenu.ContextMenuInfo;
|
|||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
import org.apache.cordova.LOG;
|
import org.apache.cordova.LOG;
|
||||||
|
|
||||||
public class menus extends DroidGap {
|
public class menus extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
// need the title to be shown (config.xml) for the options menu to be visible
|
||||||
super.init();
|
super.init();
|
||||||
super.registerForContextMenu(super.appView);
|
super.registerForContextMenu(super.appView);
|
||||||
super.loadUrl("file:///android_asset/www/menus/index.html");
|
super.loadUrl("file:///android_asset/www/menus/index.html");
|
||||||
|
|||||||
@@ -20,17 +20,15 @@ package org.apache.cordova.test;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
import org.apache.cordova.test.R;
|
|
||||||
import org.apache.cordova.test.R.drawable;
|
|
||||||
|
|
||||||
public class splashscreen extends DroidGap {
|
public class splashscreen extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
super.init();
|
super.init();
|
||||||
|
|
||||||
// Show splashscreen
|
// Show splashscreen
|
||||||
this.setIntegerProperty("splashscreen", R.drawable.sandy);
|
preferences.set("splashscreen", "sandy");
|
||||||
|
|
||||||
super.loadUrl("file:///android_asset/www/splashscreen/index.html", 2000);
|
super.loadUrl("file:///android_asset/www/splashscreen/index.html", 2000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class tests extends DroidGap {
|
public class tests extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class timeout extends DroidGap {
|
public class timeout extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
super.init();
|
super.init();
|
||||||
|
|
||||||
// Short timeout to cause error
|
// Short timeout to cause error
|
||||||
this.setIntegerProperty("loadUrlTimeoutValue", 10);
|
preferences.set("loadUrlTimeoutValue", 10);
|
||||||
super.loadUrl("http://www.google.com");
|
super.loadUrl("http://www.google.com");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,8 @@ import android.webkit.WebView;
|
|||||||
import android.webkit.GeolocationPermissions.Callback;
|
import android.webkit.GeolocationPermissions.Callback;
|
||||||
|
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
import org.apache.cordova.LOG;
|
|
||||||
|
|
||||||
public class userwebview extends DroidGap {
|
public class userwebview extends MainTestActivity {
|
||||||
|
|
||||||
public TestViewClient testViewClient;
|
public TestViewClient testViewClient;
|
||||||
public TestChromeClient testChromeClient;
|
public TestChromeClient testChromeClient;
|
||||||
@@ -33,15 +32,17 @@ public class userwebview extends DroidGap {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
testViewClient = new TestViewClient(this);
|
testViewClient = new TestViewClient(this, appView);
|
||||||
testChromeClient = new TestChromeClient(this);
|
testChromeClient = new TestChromeClient(this, appView);
|
||||||
super.init(new CordovaWebView(this), new TestViewClient(this), new TestChromeClient(this));
|
super.init();
|
||||||
|
appView.setWebViewClient(testViewClient);
|
||||||
|
appView.setWebChromeClient(testChromeClient);
|
||||||
super.loadUrl("file:///android_asset/www/userwebview/index.html");
|
super.loadUrl("file:///android_asset/www/userwebview/index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestChromeClient extends CordovaChromeClient {
|
public class TestChromeClient extends CordovaChromeClient {
|
||||||
public TestChromeClient(DroidGap arg0) {
|
public TestChromeClient(CordovaInterface ctx, CordovaWebView app) {
|
||||||
super(arg0);
|
super(ctx, app);
|
||||||
LOG.d("userwebview", "TestChromeClient()");
|
LOG.d("userwebview", "TestChromeClient()");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,8 +58,8 @@ public class userwebview extends DroidGap {
|
|||||||
* This class can be used to override the GapViewClient and receive notification of webview events.
|
* This class can be used to override the GapViewClient and receive notification of webview events.
|
||||||
*/
|
*/
|
||||||
public class TestViewClient extends CordovaWebViewClient {
|
public class TestViewClient extends CordovaWebViewClient {
|
||||||
public TestViewClient(DroidGap arg0) {
|
public TestViewClient(CordovaInterface ctx, CordovaWebView app) {
|
||||||
super(arg0);
|
super(ctx, app);
|
||||||
LOG.d("userwebview", "TestViewClient()");
|
LOG.d("userwebview", "TestViewClient()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,12 @@ import android.webkit.WebView;
|
|||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
import org.apache.cordova.LOG;
|
import org.apache.cordova.LOG;
|
||||||
|
|
||||||
public class whitelist extends DroidGap {
|
public class whitelist extends MainTestActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
super.init(new CordovaWebView(this), new TestViewClient(this), new CordovaChromeClient(this));
|
super.init();
|
||||||
|
appView.setWebViewClient(new TestViewClient(this, appView));
|
||||||
super.loadUrl("file:///android_asset/www/whitelist/index.html");
|
super.loadUrl("file:///android_asset/www/whitelist/index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +38,8 @@ public class whitelist extends DroidGap {
|
|||||||
*/
|
*/
|
||||||
public class TestViewClient extends CordovaWebViewClient {
|
public class TestViewClient extends CordovaWebViewClient {
|
||||||
|
|
||||||
public TestViewClient(DroidGap arg0) {
|
public TestViewClient(CordovaInterface ctx, CordovaWebView app) {
|
||||||
super(arg0);
|
super(ctx, app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package org.apache.cordova.test;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class xhr extends DroidGap {
|
public class xhr extends CordovaActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|||||||
Reference in New Issue
Block a user