mirror of
https://github.com/apache/cordova-android.git
synced 2026-04-04 00:02:03 +08:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04b3fc0268 | ||
|
|
105ccc81a5 | ||
|
|
3571307df5 | ||
|
|
df05f3a3c0 | ||
|
|
8e31ef7be6 | ||
|
|
f4555f7c96 | ||
|
|
8408da55ea | ||
|
|
4a67dd2e28 | ||
|
|
bd806a34d8 | ||
|
|
2f7e833a79 | ||
|
|
c17503ab78 | ||
|
|
19f76d34db | ||
|
|
25c8b2fabb | ||
|
|
bfd8bf9ca4 | ||
|
|
7a5405d2ab | ||
|
|
b9a24f00ad | ||
|
|
dbfc292353 | ||
|
|
a09255b2ff | ||
|
|
9d1c72cc07 | ||
|
|
09ac30ef2e | ||
|
|
79e313a0c0 | ||
|
|
9f4c75d1c2 | ||
|
|
b37492644c | ||
|
|
04a792a8c2 | ||
|
|
35ec24c3f0 | ||
|
|
61b23677d1 | ||
|
|
90037dc6cd |
@@ -1,37 +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.
|
|
||||||
#
|
|
||||||
-->
|
|
||||||
|
|
||||||
# Contributing to Apache Cordova
|
|
||||||
|
|
||||||
Anyone can contribute to Cordova. And we need your contributions.
|
|
||||||
|
|
||||||
There are multiple ways to contribute: report bugs, improve the docs, and
|
|
||||||
contribute code.
|
|
||||||
|
|
||||||
For instructions on this, start with the
|
|
||||||
[contribution overview](http://cordova.apache.org/#contribute).
|
|
||||||
|
|
||||||
The details are explained there, but the important items are:
|
|
||||||
- Sign and submit an Apache ICLA (Contributor License Agreement).
|
|
||||||
- Have a Jira issue open that corresponds to your contribution.
|
|
||||||
- Run the tests so your patch doesn't break existing functionality.
|
|
||||||
|
|
||||||
We look forward to your contributions!
|
|
||||||
87
LICENSE
87
LICENSE
@@ -199,89 +199,4 @@
|
|||||||
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.
|
|
||||||
|
|
||||||
16
NOTICE
16
NOTICE
@@ -1,17 +1,5 @@
|
|||||||
Apache Cordova
|
Apache Cordova
|
||||||
Copyright 2014 The Apache Software Foundation
|
Copyright 2012 The Apache Software Foundation
|
||||||
|
|
||||||
This product includes software developed at
|
This product includes software developed at
|
||||||
The Apache Software Foundation (http://www.apache.org)
|
The Apache Software Foundation (http://www.apache.org/).
|
||||||
|
|
||||||
=========================================================================
|
|
||||||
== NOTICE file corresponding to the section 4 d of ==
|
|
||||||
== the Apache License, Version 2.0, ==
|
|
||||||
== in this case for the Android-specific code. ==
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This product includes software developed as part of
|
|
||||||
The Android Open Source Project (http://source.android.com).
|
|
||||||
|
|
||||||
This software includes software developed at Square, Inc.
|
|
||||||
Copyright (C) 2013 Square, Inc.
|
|
||||||
|
|||||||
2
README.md
Normal file → Executable file
2
README.md
Normal file → Executable file
@@ -84,7 +84,7 @@ Running Tests
|
|||||||
Please see details under test/README.md.
|
Please see details under test/README.md.
|
||||||
|
|
||||||
Further Reading
|
Further Reading
|
||||||
----
|
---
|
||||||
|
|
||||||
- [http://developer.android.com](http://developer.android.com)
|
- [http://developer.android.com](http://developer.android.com)
|
||||||
- [http://cordova.apache.org/](http://cordova.apache.org)
|
- [http://cordova.apache.org/](http://cordova.apache.org)
|
||||||
|
|||||||
175
RELEASENOTES.md
175
RELEASENOTES.md
@@ -20,181 +20,6 @@
|
|||||||
-->
|
-->
|
||||||
## 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/) || stderr.match(/'android' is not recognized/)) {
|
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.'));
|
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'));
|
||||||
|
|||||||
@@ -19,166 +19,78 @@
|
|||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var shelljs = require('shelljs'),
|
var shell = 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 = shelljs.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
|
var target = shell.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 = shelljs.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')) ||
|
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')) ||
|
||||||
shelljs.grep(/target=Google Inc.:Google APIs:[\d+]/, path.join(ROOT, 'project.properties'));
|
shell.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. Called only by build and clean commands.
|
// Returns a promise.
|
||||||
module.exports.check_ant = function() {
|
module.exports.check_ant = function() {
|
||||||
return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.');
|
var d = Q.defer();
|
||||||
};
|
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.'));
|
||||||
// Returns a promise. Called only by build and clean commands.
|
else d.resolve();
|
||||||
module.exports.check_gradle = function() {
|
});
|
||||||
var sdkDir = process.env['ANDROID_HOME'];
|
return d.promise;
|
||||||
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 javacPath = forgivingWhichSync('javac');
|
var d = Q.defer();
|
||||||
var hasJavaHome = !!process.env['JAVA_HOME'];
|
child_process.exec('java -version', function(err, stdout, stderr) {
|
||||||
return Q().then(function() {
|
if(err) {
|
||||||
if (hasJavaHome) {
|
var msg =
|
||||||
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
|
'Failed to run \'java -version\', make sure your java environment is set up\n' +
|
||||||
if (!javacPath) {
|
'including JDK and JRE.\n' +
|
||||||
process.env['PATH'] += path.delimiter + path.join(process.env['JAVA_HOME'], 'bin');
|
'Your JAVA_HOME variable is ' + process.env.JAVA_HOME + '\n';
|
||||||
}
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}).then(function() {
|
else d.resolve();
|
||||||
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() {
|
||||||
return Q().then(function() {
|
var valid_target = this.get_target();
|
||||||
var androidCmdPath = forgivingWhichSync('android');
|
var d = Q.defer();
|
||||||
var adbInPath = !!forgivingWhichSync('adb');
|
child_process.exec('android list targets', function(err, stdout, stderr) {
|
||||||
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
|
if (err) d.reject(stderr);
|
||||||
if (hasAndroidHome && !androidCmdPath) {
|
else d.resolve(stdout);
|
||||||
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());
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.check_android_target = function(valid_target) {
|
return d.promise.then(function(output) {
|
||||||
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)) {
|
||||||
throw new Error('Please install Android target "' + valid_target + '".\n' +
|
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.'));
|
||||||
'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_java(), this.check_android()]);
|
return Q.all([this.check_ant(), this.check_java(), this.check_android()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ 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.
|
||||||
@@ -83,46 +82,14 @@ function copyJsAndLibrary(projectPath, shared, projectName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractSubProjectPaths(data) {
|
function runAndroidUpdate(projectPath, target_api, shared) {
|
||||||
var ret = {};
|
var targetFrameworkDir = getFrameworkDir(projectPath, shared);
|
||||||
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
|
return exec('android update project --subprojects --path "' + projectPath + '" --target ' + target_api + ' --library "' + path.relative(projectPath, targetFrameworkDir) + '"');
|
||||||
var m;
|
|
||||||
while (m = r.exec(data)) {
|
|
||||||
ret[m[1]] = 1;
|
|
||||||
}
|
|
||||||
return Object.keys(ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeProjectProperties(projectPath, target_api, shared) {
|
function copyAntRules(projectPath) {
|
||||||
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) {
|
||||||
@@ -139,50 +106,6 @@ 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]
|
||||||
*
|
*
|
||||||
@@ -210,11 +133,9 @@ 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');
|
||||||
@@ -224,15 +145,17 @@ 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');
|
||||||
}
|
}
|
||||||
|
|
||||||
//Make the package conform to Java package types
|
if (!/[a-zA-Z0-9_]+\.[a-zA-Z0-9_](.[a-zA-Z0-9_])*/.test(package_name)) {
|
||||||
return validatePackageName(package_name)
|
return Q.reject('Package name must look like: com.company.Name');
|
||||||
.then(function() {
|
}
|
||||||
validateProjectName(project_name);
|
|
||||||
})
|
if (project_name === 'CordovaActivity') {
|
||||||
|
return Q.reject('Project name cannot be CordovaActivity');
|
||||||
|
}
|
||||||
|
|
||||||
// Check that requirements are met and proper targets are installed
|
// Check that requirements are met and proper targets are installed
|
||||||
|
return check_reqs.run()
|
||||||
.then(function() {
|
.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);
|
||||||
@@ -247,7 +170,6 @@ 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'));
|
||||||
@@ -279,10 +201,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);
|
||||||
copyBuildRules(project_path);
|
copyAntRules(project_path);
|
||||||
});
|
});
|
||||||
// Link it to local android install.
|
// Link it to local android install.
|
||||||
writeProjectProperties(project_path, target_api);
|
return runAndroidUpdate(project_path, target_api, use_shared_project);
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
console.log('Project successfully created.');
|
console.log('Project successfully created.');
|
||||||
});
|
});
|
||||||
@@ -305,24 +227,22 @@ function extractProjectNameFromManifest(projectPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise.
|
// Returns a promise.
|
||||||
exports.updateProject = function(projectPath, shared) {
|
exports.updateProject = function(projectPath) {
|
||||||
var newVersion = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
|
var version = 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, shared, projectName);
|
copyJsAndLibrary(projectPath, false, projectName);
|
||||||
copyScripts(projectPath);
|
copyScripts(projectPath);
|
||||||
copyBuildRules(projectPath);
|
copyAntRules(projectPath);
|
||||||
removeDebuggableFromManifest(projectPath);
|
removeDebuggableFromManifest(projectPath);
|
||||||
writeProjectProperties(projectPath, target_api, shared);
|
return runAndroidUpdate(projectPath, target_api, false)
|
||||||
console.log('Android project is now at version ' + newVersion);
|
.then(function() {
|
||||||
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('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.');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// For testing
|
|
||||||
exports.validatePackageName = validatePackageName;
|
|
||||||
exports.validateProjectName = validateProjectName;
|
|
||||||
|
|||||||
23
bin/node_modules/which/LICENSE
generated
vendored
23
bin/node_modules/which/LICENSE
generated
vendored
@@ -1,23 +0,0 @@
|
|||||||
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
bin/node_modules/which/README.md
generated
vendored
5
bin/node_modules/which/README.md
generated
vendored
@@ -1,5 +0,0 @@
|
|||||||
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
bin/node_modules/which/bin/which
generated
vendored
14
bin/node_modules/which/bin/which
generated
vendored
@@ -1,14 +0,0 @@
|
|||||||
#!/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
bin/node_modules/which/package.json
generated
vendored
31
bin/node_modules/which/package.json
generated
vendored
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"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
bin/node_modules/which/which.js
generated
vendored
104
bin/node_modules/which/which.js
generated
vendored
@@ -1,104 +0,0 @@
|
|||||||
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,17 +24,13 @@ var build = require('./lib/build'),
|
|||||||
args = process.argv;
|
args = process.argv;
|
||||||
|
|
||||||
// Support basic help commands
|
// Support basic help commands
|
||||||
if(args[2] == '--help' ||
|
if(args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
|
||||||
args[2] == '/?' ||
|
args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
|
||||||
args[2] == '-h' ||
|
|
||||||
args[2] == 'help' ||
|
|
||||||
args[2] == '-help' ||
|
|
||||||
args[2] == '/help') {
|
|
||||||
build.help();
|
build.help();
|
||||||
} else {
|
} else {
|
||||||
reqs.run().done(function() {
|
reqs.run().then(function() {
|
||||||
return build.run(args.slice(2));
|
return build.run(args[2]);
|
||||||
}, function(err) {
|
}).done(null, function(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,26 +19,18 @@
|
|||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var build = require('./lib/build'),
|
var clean = require('./lib/clean'),
|
||||||
reqs = require('./lib/check_reqs'),
|
reqs = require('./lib/check_reqs'),
|
||||||
args = process.argv;
|
args = process.argv;
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
// Support basic help commands
|
// Usage support for when args are given
|
||||||
if(args[2] == '--help' ||
|
if(args.length > 2) {
|
||||||
args[2] == '/?' ||
|
clean.help();
|
||||||
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 build.runClean(args.slice(2));
|
return clean.run();
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
console.error(err);
|
console.error('ERROR: ' + err);
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,10 @@
|
|||||||
|
|
||||||
<!-- 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>
|
||||||
|
|||||||
400
bin/templates/cordova/lib/build.js
vendored
400
bin/templates/cordova/lib/build.js
vendored
@@ -20,366 +20,94 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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) {
|
||||||
function extractProjectNameFromManifest(projectPath) {
|
var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
|
||||||
var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
|
// custom_rules.xml is required for incremental builds.
|
||||||
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
if (hasCustomRules()) {
|
||||||
var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
|
args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
|
||||||
if (!m) {
|
|
||||||
throw new Error('Could not find activity name in ' + manifestPath);
|
|
||||||
}
|
}
|
||||||
return m[1];
|
return args;
|
||||||
}
|
};
|
||||||
|
|
||||||
function extractSubProjectPaths() {
|
/*
|
||||||
var data = fs.readFileSync(path.join(ROOT, 'project.properties'), 'utf8');
|
* Builds the project with ant.
|
||||||
var ret = {};
|
* Returns a promise.
|
||||||
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
|
*/
|
||||||
var m;
|
module.exports.run = function(build_type) {
|
||||||
while (m = r.exec(data)) {
|
//default build type
|
||||||
ret[m[1]] = 1;
|
build_type = typeof build_type !== 'undefined' ? build_type : "--debug";
|
||||||
}
|
var args = module.exports.getAntArgs('debug');
|
||||||
return Object.keys(ret);
|
switch(build_type) {
|
||||||
}
|
case '--debug' :
|
||||||
|
break;
|
||||||
var builders = {
|
case '--release' :
|
||||||
ant: {
|
args[0] = 'release';
|
||||||
getArgs: function(cmd) {
|
break;
|
||||||
var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
|
case '--nobuild' :
|
||||||
// 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...');
|
console.log('Skipping build...');
|
||||||
return Q(null);
|
|
||||||
},
|
|
||||||
clean: function() {
|
|
||||||
return Q();
|
return Q();
|
||||||
},
|
default :
|
||||||
findOutputApks: function(build_type) {
|
return Q.reject('Build option \'' + build_type + '\' not recognized.');
|
||||||
return sortFilesByDate(builders.ant.findOutputApks(build_type).concat(builders.gradle.findOutputApks(build_type)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
// Without our custom_rules.xml, we need to clean before building.
|
||||||
|
var ret = Q();
|
||||||
function parseOpts(options) {
|
if (!hasCustomRules()) {
|
||||||
// Backwards-compatibility: Allow a single string argument
|
ret = require('./clean').run();
|
||||||
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;
|
return ret.then(function() {
|
||||||
|
return spawn('ant', args);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Builds the project with the specifed options
|
* Gets the path to the apk file, if not such file exists then
|
||||||
* Returns a promise.
|
* the script will error out. (should we error or just return undefined?)
|
||||||
*/
|
*/
|
||||||
module.exports.runClean = function(options) {
|
module.exports.get_apk = function() {
|
||||||
var opts = parseOpts(options);
|
var binDir = '';
|
||||||
var builder = builders[opts.buildMethod];
|
if(!hasCustomRules()) {
|
||||||
return builder.prepEnv()
|
binDir = path.join(ROOT, 'bin');
|
||||||
.then(function() {
|
} else {
|
||||||
return builder.clean();
|
binDir = path.join(ROOT, 'ant-build');
|
||||||
}).then(function() {
|
|
||||||
shell.rm('-rf', path.join(ROOT, 'out'));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Builds the project with the specifed options
|
|
||||||
* Returns a promise.
|
|
||||||
*/
|
|
||||||
module.exports.run = function(options) {
|
|
||||||
var opts = parseOpts(options);
|
|
||||||
|
|
||||||
var builder = builders[opts.buildMethod];
|
|
||||||
return builder.prepEnv()
|
|
||||||
.then(function() {
|
|
||||||
return builder.build(opts.buildType);
|
|
||||||
}).then(function() {
|
|
||||||
var apkPaths = builder.findOutputApks(opts.buildType);
|
|
||||||
console.log('Built the following apk(s):');
|
|
||||||
console.log(' ' + apkPaths.join('\n '));
|
|
||||||
return {
|
|
||||||
apkPaths: apkPaths,
|
|
||||||
buildType: opts.buildType,
|
|
||||||
buildMethod: opts.buildMethod
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Detects the architecture of a device/emulator
|
|
||||||
* Returns "arm" or "x86".
|
|
||||||
*/
|
|
||||||
module.exports.detectArchitecture = function(target) {
|
|
||||||
return exec('adb -s ' + target + ' shell cat /proc/cpuinfo')
|
|
||||||
.then(function(output) {
|
|
||||||
if (/intel/i.exec(output)) {
|
|
||||||
return 'x86';
|
|
||||||
}
|
|
||||||
return 'arm';
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.findBestApkForArchitecture = function(buildResults, arch) {
|
|
||||||
var paths = buildResults.apkPaths.filter(function(p) {
|
|
||||||
if (buildResults.buildType == 'debug') {
|
|
||||||
return /-debug/.exec(p);
|
|
||||||
}
|
|
||||||
return !/-debug/.exec(p);
|
|
||||||
});
|
|
||||||
var archPattern = new RegExp('-' + arch);
|
|
||||||
var hasArchPattern = /-x86|-arm/;
|
|
||||||
for (var i = 0; i < paths.length; ++i) {
|
|
||||||
if (hasArchPattern.exec(paths[i])) {
|
|
||||||
if (archPattern.exec(paths[i])) {
|
|
||||||
return paths[i];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return paths[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType);
|
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';
|
||||||
|
}).map(function(p) {
|
||||||
|
p = path.join(binDir, p);
|
||||||
|
return { p: p, t: fs.statSync(p).mtime };
|
||||||
|
}).sort(function(a,b) {
|
||||||
|
return a.t > b.t ? -1 :
|
||||||
|
a.t < b.t ? 1 : 0;
|
||||||
|
});
|
||||||
|
if (candidates.length === 0) {
|
||||||
|
console.error('ERROR : No .apk found in ' + binDir + ' directory');
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 debug mode');
|
console.log(' \'--debug\': Default build, will build project in using ant debug');
|
||||||
console.log(' \'--release\': will build project for release');
|
console.log(' \'--release\': will build project using ant 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);
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#!/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
|
||||||
@@ -17,38 +19,21 @@
|
|||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var build = require('./build'),
|
||||||
|
spawn = require('./spawn'),
|
||||||
|
path = require('path');
|
||||||
|
|
||||||
|
/*
|
||||||
buildscript {
|
* Cleans the project using ant
|
||||||
repositories {
|
* Returns a promise.
|
||||||
mavenCentral()
|
*/
|
||||||
}
|
module.exports.run = function() {
|
||||||
|
var args = build.getAntArgs('clean');
|
||||||
dependencies {
|
return spawn('ant', args);
|
||||||
classpath 'com.android.tools.build:gradle:0.10.+'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'android-library'
|
module.exports.help = function() {
|
||||||
|
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
|
||||||
android {
|
console.log('Cleans the project directory.');
|
||||||
compileSdkVersion 19
|
process.exit(0);
|
||||||
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']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
18
bin/templates/cordova/lib/device.js
vendored
18
bin/templates/cordova/lib/device.js
vendored
@@ -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, buildResults) {
|
module.exports.install = function(target) {
|
||||||
var launchName;
|
var launchName;
|
||||||
return this.list()
|
return this.list()
|
||||||
.then(function(device_list) {
|
.then(function(device_list) {
|
||||||
@@ -56,20 +56,16 @@ module.exports.install = function(target, buildResults) {
|
|||||||
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 = target || device_list[0];
|
target = typeof target !== 'undefined' ? 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 + '\'.');
|
||||||
|
|
||||||
return build.detectArchitecture(target)
|
var apk_path = build.get_apk();
|
||||||
.then(function(arch) {
|
launchName = appinfo.getActivityName();
|
||||||
var apk_path = build.findBestApkForArchitecture(buildResults, arch);
|
console.log('Installing app on device...');
|
||||||
launchName = appinfo.getActivityName();
|
var cmd = 'adb -s ' + target + ' install -r "' + apk_path + '"';
|
||||||
console.log('Using apk: ' + apk_path);
|
return exec(cmd);
|
||||||
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);
|
||||||
|
|
||||||
|
|||||||
14
bin/templates/cordova/lib/emulator.js
vendored
14
bin/templates/cordova/lib/emulator.js
vendored
@@ -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, buildResults) {
|
module.exports.install = function(target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.list_started()
|
return this.list_started()
|
||||||
.then(function(emulator_list) {
|
.then(function(emulator_list) {
|
||||||
@@ -292,18 +292,14 @@ module.exports.install = function(target, buildResults) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// default emulator
|
// default emulator
|
||||||
target = target || emulator_list[0];
|
target = typeof target !== 'undefined' ? 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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.detectArchitecture(target)
|
console.log('Installing app on emulator...');
|
||||||
.then(function(arch) {
|
var apk_path = build.get_apk();
|
||||||
var apk_path = build.findBestApkForArchitecture(buildResults, arch);
|
return exec('adb -s ' + target + ' install -r "' + apk_path + '"');
|
||||||
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);
|
||||||
|
|||||||
30
bin/templates/cordova/lib/run.js
vendored
30
bin/templates/cordova/lib/run.js
vendored
@@ -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 buildFlags = [];
|
var build_type;
|
||||||
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') {
|
||||||
buildFlags.push('--debug');
|
build_type = '--debug';
|
||||||
} else if (args[i] == '--release') {
|
} else if (args[i] == '--release') {
|
||||||
buildFlags.push('--release');
|
build_type = '--release';
|
||||||
} else if (args[i] == '--nobuild') {
|
} else if (args[i] == '--nobuild') {
|
||||||
buildFlags.push('--nobuild');
|
build_type = '--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(buildFlags).then(function(buildResults) {
|
return build.run(build_type).then(function() {
|
||||||
if (install_target == '--device') {
|
if (install_target == '--device') {
|
||||||
return device.install(null, buildResults);
|
return device.install();
|
||||||
} 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() { return emulator.install(null, buildResults); });
|
return p.then(function() { emulator.install(); });
|
||||||
});
|
});
|
||||||
} 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, buildResults);
|
return device.install(install_target);
|
||||||
} else if (started_emulators.indexOf(install_target) > -1) {
|
} else if (started_emulators.indexOf(install_target) > -1) {
|
||||||
return emulator.install(install_target, buildResults);
|
return emulator.install(install_target);
|
||||||
} 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, buildResults); });
|
.then(function() { emulator.install(emulator_ID); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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], buildResults);
|
return device.install(device_list[0]);
|
||||||
} 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], buildResults);
|
return emulator.install(emulator_list[0]);
|
||||||
} 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, buildResults);
|
return emulator.install(emulator_ID);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return emulator.start();
|
return emulator.start();
|
||||||
@@ -125,8 +125,8 @@ var path = require('path'),
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.help = function(args) {
|
module.exports.help = function() {
|
||||||
console.log('Usage: ' + path.relative(process.cwd(), args[1]) + ' [options]');
|
console.log('Usage: ' + path.relative(process.cwd(), args[0]) + ' [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(args);
|
run.help();
|
||||||
} 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.6.4";
|
var VERSION = "3.5.0-dev";
|
||||||
|
|
||||||
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,6 +30,8 @@ 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
|
||||||
loadUrl(launchUrl);
|
super.loadUrl(Config.getStartUrl());
|
||||||
|
//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"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
|
||||||
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"
|
||||||
@@ -29,16 +29,15 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
|
||||||
<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__"
|
<activity android:name="__ACTIVITY__" android:label="@string/app_name" android:launchMode="singleTop"
|
||||||
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 android:label="@string/launcher_name">
|
<intent-filter>
|
||||||
<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>
|
||||||
@@ -46,4 +45,4 @@
|
|||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="__APILEVEL__"/>
|
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="__APILEVEL__"/>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
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,7 +13,6 @@
|
|||||||
</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" />
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# 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,9 +1,4 @@
|
|||||||
<?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>
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
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="10" />
|
<uses-sdk android:minSdkVersion="8" />
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
241
framework/assets/www/cordova.js
vendored
241
framework/assets/www/cordova.js
vendored
@@ -1,5 +1,5 @@
|
|||||||
// Platform: android
|
// Platform: android
|
||||||
// 8ca0f3b2b87e0759c5236b91c80f18438544409c
|
// 3.5.0-dev-81f9a00
|
||||||
/*
|
/*
|
||||||
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 PLATFORM_VERSION_BUILD_LABEL = '3.6.4';
|
var CORDOVA_JS_BUILD_LABEL = '3.5.0-dev-81f9a00';
|
||||||
// file: src/scripts/require.js
|
// file: src/scripts/require.js
|
||||||
|
|
||||||
/*jshint -W079 */
|
/*jshint -W079 */
|
||||||
@@ -175,8 +175,7 @@ function createEvent(type, data) {
|
|||||||
var cordova = {
|
var cordova = {
|
||||||
define:define,
|
define:define,
|
||||||
require:require,
|
require:require,
|
||||||
version:PLATFORM_VERSION_BUILD_LABEL,
|
version:CORDOVA_JS_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.
|
||||||
@@ -266,7 +265,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 success callback: " + callbackId + " = "+e);
|
console.log("Error in error callback: " + callbackId + " = "+e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -345,18 +344,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 pre-JellyBean, where addJavascriptInterface() is disabled.
|
* This is used only on the 2.3 simulator, where addJavascriptInterface() is broken.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
exec: function(bridgeSecret, service, action, callbackId, argsJson) {
|
exec: function(service, action, callbackId, argsJson) {
|
||||||
return prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId]));
|
return prompt(argsJson, 'gap:'+JSON.stringify([service, action, callbackId]));
|
||||||
},
|
},
|
||||||
setNativeToJsBridgeMode: function(bridgeSecret, value) {
|
setNativeToJsBridgeMode: function(value) {
|
||||||
prompt(value, 'gap_bridge_mode:' + bridgeSecret);
|
prompt(value, 'gap_bridge_mode:');
|
||||||
},
|
},
|
||||||
retrieveJsMessages: function(bridgeSecret, fromOnlineEvent) {
|
retrieveJsMessages: function(fromOnlineEvent) {
|
||||||
return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret);
|
return prompt(+fromOnlineEvent, 'gap_poll:');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -826,7 +825,6 @@ 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
|
||||||
@@ -870,10 +868,13 @@ 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.
|
||||||
@@ -893,17 +894,9 @@ 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) {
|
||||||
@@ -924,35 +917,29 @@ function androidExec(success, fail, service, action, args) {
|
|||||||
cordova.callbacks[callbackId] = {success:success, fail:fail};
|
cordova.callbacks[callbackId] = {success:success, fail:fail};
|
||||||
}
|
}
|
||||||
|
|
||||||
var messages = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);
|
if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) {
|
||||||
// If argsJson was received by Java as null, try again with the PROMPT bridge mode.
|
window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;
|
||||||
// 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 {
|
||||||
androidExec.processMessages(messages, true);
|
var messages = nativeApiProvider.get().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 {
|
||||||
|
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) {
|
||||||
if (bridgeSecret < 0) {
|
var msg = nativeApiProvider.get().retrieveJsMessages(!!opt_fromOnlineEvent);
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,10 +989,7 @@ androidExec.setNativeToJsBridgeMode = function(mode) {
|
|||||||
|
|
||||||
nativeToJsBridgeMode = mode;
|
nativeToJsBridgeMode = mode;
|
||||||
// Tell the native side to switch modes.
|
// Tell the native side to switch modes.
|
||||||
// Otherwise, it will be set by androidExec.init()
|
nativeApiProvider.get().setNativeToJsBridgeMode(mode);
|
||||||
if (bridgeSecret >= 0) {
|
|
||||||
nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == nativeToJsModes.POLLING) {
|
if (mode == nativeToJsModes.POLLING) {
|
||||||
pollEnabled = true;
|
pollEnabled = true;
|
||||||
@@ -1184,16 +1168,6 @@ 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1271,121 +1245,6 @@ 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
|
||||||
@@ -1500,8 +1359,10 @@ module.exports = {
|
|||||||
exec = require('cordova/exec'),
|
exec = require('cordova/exec'),
|
||||||
modulemapper = require('cordova/modulemapper');
|
modulemapper = require('cordova/modulemapper');
|
||||||
|
|
||||||
// Get the shared secret needed to use the bridge.
|
// Tell the native code that a page change has occurred.
|
||||||
exec.init();
|
exec(null, null, 'PluginManager', 'startup', []);
|
||||||
|
// 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');
|
||||||
@@ -1518,17 +1379,6 @@ 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() {
|
||||||
@@ -1606,21 +1456,6 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -1714,11 +1549,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,25 +30,13 @@
|
|||||||
Apache Cordova Team
|
Apache Cordova Team
|
||||||
</author>
|
</author>
|
||||||
|
|
||||||
<!-- Allow access to arbitrary URLs in the Cordova WebView. This is a
|
<access origin="*"/>
|
||||||
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" />
|
||||||
|
|
||||||
<preference name="loglevel" value="DEBUG" />
|
<preference name="loglevel" value="DEBUG" />
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<preference name="splashscreen" value="resourceName" />
|
<preference name="splashscreen" value="resourceName" />
|
||||||
<preference name="backgroundColor" value="0xFFF" />
|
<preference name="backgroundColor" value="0xFFF" />
|
||||||
@@ -56,4 +44,8 @@
|
|||||||
<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,19 +1,3 @@
|
|||||||
/*
|
|
||||||
* 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,19 +1,3 @@
|
|||||||
/*
|
|
||||||
* 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,19 +1,3 @@
|
|||||||
/*
|
|
||||||
* 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;
|
||||||
|
|||||||
400
framework/src/org/apache/cordova/AndroidChromeClient.java
Executable file
400
framework/src/org/apache/cordova/AndroidChromeClient.java
Executable file
@@ -0,0 +1,400 @@
|
|||||||
|
/*
|
||||||
|
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.CordovaInterface;
|
||||||
|
import org.apache.cordova.LOG;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup.LayoutParams;
|
||||||
|
import android.webkit.ConsoleMessage;
|
||||||
|
import android.webkit.JsPromptResult;
|
||||||
|
import android.webkit.JsResult;
|
||||||
|
import android.webkit.ValueCallback;
|
||||||
|
import android.webkit.WebChromeClient;
|
||||||
|
import android.webkit.WebStorage;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.GeolocationPermissions.Callback;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is the WebChromeClient that implements callbacks for our web view.
|
||||||
|
* The kind of callbacks that happen here are on the chrome outside the document,
|
||||||
|
* such as onCreateWindow(), onConsoleMessage(), onProgressChanged(), etc. Related
|
||||||
|
* to but different than CordovaWebViewClient.
|
||||||
|
*
|
||||||
|
* @see <a href="http://developer.android.com/reference/android/webkit/WebChromeClient.html">WebChromeClient</a>
|
||||||
|
* @see <a href="http://developer.android.com/guide/webapps/webview.html">WebView guide</a>
|
||||||
|
* @see CordovaWebViewClient
|
||||||
|
* @see CordovaWebView
|
||||||
|
*/
|
||||||
|
public class AndroidChromeClient extends WebChromeClient implements CordovaChromeClient {
|
||||||
|
|
||||||
|
public static final int FILECHOOSER_RESULTCODE = 5173;
|
||||||
|
private static final String LOG_TAG = "CordovaChromeClient";
|
||||||
|
private String TAG = "CordovaLog";
|
||||||
|
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||||
|
protected CordovaInterface cordova;
|
||||||
|
protected CordovaWebView appView;
|
||||||
|
|
||||||
|
// the video progress view
|
||||||
|
private View mVideoProgressView;
|
||||||
|
|
||||||
|
// File Chooser
|
||||||
|
public ValueCallback<Uri> mUploadMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param cordova
|
||||||
|
*/
|
||||||
|
public AndroidChromeClient(CordovaInterface cordova) {
|
||||||
|
this.cordova = cordova;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param ctx
|
||||||
|
* @param app
|
||||||
|
*/
|
||||||
|
public AndroidChromeClient(CordovaInterface ctx, CordovaWebView app) {
|
||||||
|
this.cordova = ctx;
|
||||||
|
this.appView = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param view
|
||||||
|
*/
|
||||||
|
public void setWebView(CordovaWebView view) {
|
||||||
|
this.appView = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the client to display a javascript alert dialog.
|
||||||
|
*
|
||||||
|
* @param view
|
||||||
|
* @param url
|
||||||
|
* @param message
|
||||||
|
* @param result
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
|
||||||
|
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
||||||
|
dlg.setMessage(message);
|
||||||
|
dlg.setTitle("Alert");
|
||||||
|
//Don't let alerts break the back button
|
||||||
|
dlg.setCancelable(true);
|
||||||
|
dlg.setPositiveButton(android.R.string.ok,
|
||||||
|
new AlertDialog.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
result.confirm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dlg.setOnCancelListener(
|
||||||
|
new DialogInterface.OnCancelListener() {
|
||||||
|
public void onCancel(DialogInterface dialog) {
|
||||||
|
result.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||||
|
//DO NOTHING
|
||||||
|
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK)
|
||||||
|
{
|
||||||
|
result.confirm();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dlg.create();
|
||||||
|
dlg.show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the client to display a confirm dialog to the user.
|
||||||
|
*
|
||||||
|
* @param view
|
||||||
|
* @param url
|
||||||
|
* @param message
|
||||||
|
* @param result
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
|
||||||
|
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
||||||
|
dlg.setMessage(message);
|
||||||
|
dlg.setTitle("Confirm");
|
||||||
|
dlg.setCancelable(true);
|
||||||
|
dlg.setPositiveButton(android.R.string.ok,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
result.confirm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dlg.setNegativeButton(android.R.string.cancel,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
result.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dlg.setOnCancelListener(
|
||||||
|
new DialogInterface.OnCancelListener() {
|
||||||
|
public void onCancel(DialogInterface dialog) {
|
||||||
|
result.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||||
|
//DO NOTHING
|
||||||
|
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK)
|
||||||
|
{
|
||||||
|
result.cancel();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dlg.create();
|
||||||
|
dlg.show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the client to display a prompt dialog to the user.
|
||||||
|
* If the client returns true, WebView will assume that the client will
|
||||||
|
* handle the prompt dialog and call the appropriate JsPromptResult method.
|
||||||
|
*
|
||||||
|
* 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!
|
||||||
|
*
|
||||||
|
* @param view
|
||||||
|
* @param url
|
||||||
|
* @param message
|
||||||
|
* @param defaultValue
|
||||||
|
* @param result
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
|
||||||
|
|
||||||
|
// Security check to make sure any requests are coming from the page initially
|
||||||
|
// loaded in webview and not another loaded in an iframe.
|
||||||
|
boolean reqOk = false;
|
||||||
|
if (url.startsWith("file://") || Config.isUrlWhiteListed(url)) {
|
||||||
|
reqOk = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
String r = this.appView.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));
|
||||||
|
this.appView.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));
|
||||||
|
String r = this.appView.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;
|
||||||
|
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
||||||
|
dlg.setMessage(message);
|
||||||
|
final EditText input = new EditText(this.cordova.getActivity());
|
||||||
|
if (defaultValue != null) {
|
||||||
|
input.setText(defaultValue);
|
||||||
|
}
|
||||||
|
dlg.setView(input);
|
||||||
|
dlg.setCancelable(false);
|
||||||
|
dlg.setPositiveButton(android.R.string.ok,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
String usertext = input.getText().toString();
|
||||||
|
res.confirm(usertext);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dlg.setNegativeButton(android.R.string.cancel,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
res.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dlg.create();
|
||||||
|
dlg.show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle database quota exceeded notification.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
|
||||||
|
long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
|
||||||
|
{
|
||||||
|
LOG.d(TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota);
|
||||||
|
quotaUpdater.updateQuota(MAX_QUOTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html
|
||||||
|
// Expect this to not compile in a future Android release!
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
public void onConsoleMessage(String message, int lineNumber, String sourceID)
|
||||||
|
{
|
||||||
|
//This is only for Android 2.1
|
||||||
|
if(android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.ECLAIR_MR1)
|
||||||
|
{
|
||||||
|
LOG.d(TAG, "%s: Line %d : %s", sourceID, lineNumber, message);
|
||||||
|
super.onConsoleMessage(message, lineNumber, sourceID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(8)
|
||||||
|
@Override
|
||||||
|
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
|
||||||
|
{
|
||||||
|
if (consoleMessage.message() != null)
|
||||||
|
LOG.d(TAG, "%s: Line %d : %s" , consoleMessage.sourceId() , consoleMessage.lineNumber(), consoleMessage.message());
|
||||||
|
return super.onConsoleMessage(consoleMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/**
|
||||||
|
* Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin.
|
||||||
|
*
|
||||||
|
* @param origin
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
|
||||||
|
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
||||||
|
callback.invoke(origin, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// API level 7 is required for this, see if we could lower this using something else
|
||||||
|
@Override
|
||||||
|
public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
|
||||||
|
this.appView.showCustomView(view, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHideCustomView() {
|
||||||
|
this.appView.hideCustomView();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/**
|
||||||
|
* Ask the host application for a custom progress view to show while
|
||||||
|
* a <video> is loading.
|
||||||
|
* @return View The progress view.
|
||||||
|
*/
|
||||||
|
public View getVideoLoadingProgressView() {
|
||||||
|
|
||||||
|
if (mVideoProgressView == null) {
|
||||||
|
// Create a new Loading view programmatically.
|
||||||
|
|
||||||
|
// create the linear layout
|
||||||
|
LinearLayout layout = new LinearLayout(this.appView.getContext());
|
||||||
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||||
|
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
||||||
|
layout.setLayoutParams(layoutParams);
|
||||||
|
// the proress bar
|
||||||
|
ProgressBar bar = new ProgressBar(this.appView.getContext());
|
||||||
|
LinearLayout.LayoutParams barLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||||
|
barLayoutParams.gravity = Gravity.CENTER;
|
||||||
|
bar.setLayoutParams(barLayoutParams);
|
||||||
|
layout.addView(bar);
|
||||||
|
|
||||||
|
mVideoProgressView = layout;
|
||||||
|
}
|
||||||
|
return mVideoProgressView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
|
||||||
|
this.openFileChooser(uploadMsg, "*/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType ) {
|
||||||
|
this.openFileChooser(uploadMsg, acceptType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
|
||||||
|
{
|
||||||
|
mUploadMessage = uploadMsg;
|
||||||
|
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
i.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
i.setType("*/*");
|
||||||
|
this.cordova.getActivity().startActivityForResult(Intent.createChooser(i, "File Browser"),
|
||||||
|
FILECHOOSER_RESULTCODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueCallback<Uri> getValueCallback() {
|
||||||
|
return this.mUploadMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
76
framework/src/org/apache/cordova/AndroidExposedJsApi.java
Executable file
76
framework/src/org/apache/cordova/AndroidExposedJsApi.java
Executable file
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
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.webkit.JavascriptInterface;
|
||||||
|
import org.apache.cordova.PluginManager;
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 /* package */ class AndroidExposedJsApi implements ExposedJsApi {
|
||||||
|
|
||||||
|
private PluginManager pluginManager;
|
||||||
|
private NativeToJsMessageQueue jsMessageQueue;
|
||||||
|
|
||||||
|
public AndroidExposedJsApi(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) {
|
||||||
|
this.pluginManager = pluginManager;
|
||||||
|
this.jsMessageQueue = jsMessageQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
|
||||||
|
// 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 = "";
|
||||||
|
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) {
|
||||||
|
ret = jsMessageQueue.popAndEncode(false);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return "";
|
||||||
|
} finally {
|
||||||
|
jsMessageQueue.setPaused(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public void setNativeToJsBridgeMode(int value) {
|
||||||
|
jsMessageQueue.setBridgeMode(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public String retrieveJsMessages(boolean fromOnlineEvent) {
|
||||||
|
return jsMessageQueue.popAndEncode(fromOnlineEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
1099
framework/src/org/apache/cordova/AndroidWebView.java
Executable file
1099
framework/src/org/apache/cordova/AndroidWebView.java
Executable file
File diff suppressed because it is too large
Load Diff
496
framework/src/org/apache/cordova/AndroidWebViewClient.java
Executable file
496
framework/src/org/apache/cordova/AndroidWebViewClient.java
Executable file
@@ -0,0 +1,496 @@
|
|||||||
|
/*
|
||||||
|
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.ByteArrayInputStream;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import org.apache.cordova.CordovaInterface;
|
||||||
|
import org.apache.cordova.LOG;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.net.http.SslError;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.webkit.HttpAuthHandler;
|
||||||
|
import android.webkit.SslErrorHandler;
|
||||||
|
import android.webkit.WebResourceResponse;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is the WebViewClient that implements callbacks for our web view.
|
||||||
|
* The kind of callbacks that happen here are regarding the rendering of the
|
||||||
|
* document instead of the chrome surrounding it, such as onPageStarted(),
|
||||||
|
* shouldOverrideUrlLoading(), etc. Related to but different than
|
||||||
|
* CordovaChromeClient.
|
||||||
|
*
|
||||||
|
* @see <a href="http://developer.android.com/reference/android/webkit/WebViewClient.html">WebViewClient</a>
|
||||||
|
* @see <a href="http://developer.android.com/guide/webapps/webview.html">WebView guide</a>
|
||||||
|
* @see CordovaChromeClient
|
||||||
|
* @see CordovaWebView
|
||||||
|
*/
|
||||||
|
public class AndroidWebViewClient extends WebViewClient implements CordovaWebViewClient{
|
||||||
|
|
||||||
|
private static final String TAG = "CordovaWebViewClient";
|
||||||
|
private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/";
|
||||||
|
CordovaInterface cordova;
|
||||||
|
CordovaWebView appView;
|
||||||
|
private boolean doClearHistory = false;
|
||||||
|
boolean isCurrentlyLoading;
|
||||||
|
|
||||||
|
/** The authorization tokens. */
|
||||||
|
private Hashtable<String, AuthenticationToken> authenticationTokens = new Hashtable<String, AuthenticationToken>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param cordova
|
||||||
|
*/
|
||||||
|
public AndroidWebViewClient(CordovaInterface cordova) {
|
||||||
|
this.cordova = cordova;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param cordova
|
||||||
|
* @param view
|
||||||
|
*/
|
||||||
|
public AndroidWebViewClient(CordovaInterface cordova, CordovaWebView view) {
|
||||||
|
this.cordova = cordova;
|
||||||
|
this.appView = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param view
|
||||||
|
*/
|
||||||
|
public void setWebView(CordovaWebView view) {
|
||||||
|
this.appView = 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);
|
||||||
|
try {
|
||||||
|
appView.exec(service, action, callbackId, jsonArgs);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||||
|
// Check if it's an exec() bridge command message.
|
||||||
|
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.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On received http auth request.
|
||||||
|
* The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination
|
||||||
|
*
|
||||||
|
* @param view
|
||||||
|
* @param handler
|
||||||
|
* @param host
|
||||||
|
* @param realm
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
|
||||||
|
|
||||||
|
// Get the authentication token
|
||||||
|
AuthenticationToken token = this.getAuthenticationToken(host, realm);
|
||||||
|
if (token != null) {
|
||||||
|
handler.proceed(token.getUserName(), token.getPassword());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Handle 401 like we'd normally do!
|
||||||
|
super.onReceivedHttpAuthRequest(view, handler, host, realm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the host application that a page has started loading.
|
||||||
|
* This method is called once for each main frame load so a page with iframes or framesets will call onPageStarted
|
||||||
|
* one time for the main frame. This also means that onPageStarted will not be called when the contents of an
|
||||||
|
* embedded frame changes, i.e. clicking a link whose target is an iframe.
|
||||||
|
*
|
||||||
|
* @param view The webview initiating the callback.
|
||||||
|
* @param url The url of the page.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||||
|
super.onPageStarted(view, url, favicon);
|
||||||
|
isCurrentlyLoading = true;
|
||||||
|
LOG.d(TAG, "onPageStarted(" + url + ")");
|
||||||
|
// Flush stale messages.
|
||||||
|
this.appView.resetJsMessageQueue();
|
||||||
|
|
||||||
|
// Broadcast message that page has loaded
|
||||||
|
this.appView.postMessage("onPageStarted", url);
|
||||||
|
|
||||||
|
// Notify all plugins of the navigation, so they can clean up if necessary.
|
||||||
|
this.appView.onReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the host application that a page has finished loading.
|
||||||
|
* This method is called only for main frame. When onPageFinished() is called, the rendering picture may not be updated yet.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param view The webview initiating the callback.
|
||||||
|
* @param url The url of the page.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onPageFinished(WebView view, String url) {
|
||||||
|
super.onPageFinished(view, url);
|
||||||
|
// Ignore excessive calls.
|
||||||
|
if (!isCurrentlyLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isCurrentlyLoading = false;
|
||||||
|
LOG.d(TAG, "onPageFinished(" + url + ")");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because of a timing issue we need to clear this history in onPageFinished as well as
|
||||||
|
* onPageStarted. However we only want to do this if the doClearHistory boolean is set to
|
||||||
|
* true. You see when you load a url with a # in it which is common in jQuery applications
|
||||||
|
* onPageStared is not called. Clearing the history at that point would break jQuery apps.
|
||||||
|
*/
|
||||||
|
if (this.doClearHistory) {
|
||||||
|
view.clearHistory();
|
||||||
|
this.doClearHistory = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear timeout flag
|
||||||
|
this.appView.incUrlTimeout();
|
||||||
|
|
||||||
|
// Broadcast message that page has loaded
|
||||||
|
this.appView.postMessage("onPageFinished", url);
|
||||||
|
|
||||||
|
// Make app visible after 2 sec in case there was a JS error and Cordova JS never initialized correctly
|
||||||
|
if (this.appView.getVisibility() == View.INVISIBLE) {
|
||||||
|
Thread t = new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
|
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
appView.postMessage("spinner", "stop");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown if blank loaded
|
||||||
|
if (url.equals("about:blank")) {
|
||||||
|
appView.postMessage("exit", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable).
|
||||||
|
* The errorCode parameter corresponds to one of the ERROR_* constants.
|
||||||
|
*
|
||||||
|
* @param view The WebView that is initiating the callback.
|
||||||
|
* @param errorCode The error code corresponding to an ERROR_* value.
|
||||||
|
* @param description A String describing the error.
|
||||||
|
* @param failingUrl The url that failed to load.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
||||||
|
// Ignore error due to stopLoading().
|
||||||
|
if (!isCurrentlyLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG.d(TAG, "CordovaWebViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl);
|
||||||
|
|
||||||
|
// Clear timeout flag
|
||||||
|
this.appView.incUrlTimeout();
|
||||||
|
|
||||||
|
// Handle error
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
try {
|
||||||
|
data.put("errorCode", errorCode);
|
||||||
|
data.put("description", description);
|
||||||
|
data.put("url", failingUrl);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
this.appView.postMessage("onReceivedError", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the host application that an SSL error occurred while loading a resource.
|
||||||
|
* The host application must call either handler.cancel() or handler.proceed().
|
||||||
|
* Note that the decision may be retained for use in response to future SSL errors.
|
||||||
|
* The default behavior is to cancel the load.
|
||||||
|
*
|
||||||
|
* @param view The WebView that is initiating the callback.
|
||||||
|
* @param handler An SslErrorHandler object that will handle the user's response.
|
||||||
|
* @param error The SSL error object.
|
||||||
|
*/
|
||||||
|
@TargetApi(8)
|
||||||
|
@Override
|
||||||
|
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
||||||
|
|
||||||
|
final String packageName = this.cordova.getActivity().getPackageName();
|
||||||
|
final PackageManager pm = this.cordova.getActivity().getPackageManager();
|
||||||
|
|
||||||
|
ApplicationInfo appInfo;
|
||||||
|
try {
|
||||||
|
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
|
||||||
|
if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||||
|
// debug = true
|
||||||
|
handler.proceed();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// debug = false
|
||||||
|
super.onReceivedSslError(view, handler, error);
|
||||||
|
}
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
// When it doubt, lock it out!
|
||||||
|
super.onReceivedSslError(view, handler, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the authentication token.
|
||||||
|
*
|
||||||
|
* @param authenticationToken
|
||||||
|
* @param host
|
||||||
|
* @param realm
|
||||||
|
*/
|
||||||
|
public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) {
|
||||||
|
if (host == null) {
|
||||||
|
host = "";
|
||||||
|
}
|
||||||
|
if (realm == null) {
|
||||||
|
realm = "";
|
||||||
|
}
|
||||||
|
this.authenticationTokens.put(host.concat(realm), authenticationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the authentication token.
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* @param realm
|
||||||
|
*
|
||||||
|
* @return the authentication token or null if did not exist
|
||||||
|
*/
|
||||||
|
public AuthenticationToken removeAuthenticationToken(String host, String realm) {
|
||||||
|
return this.authenticationTokens.remove(host.concat(realm));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the authentication token.
|
||||||
|
*
|
||||||
|
* In order it tries:
|
||||||
|
* 1- host + realm
|
||||||
|
* 2- host
|
||||||
|
* 3- realm
|
||||||
|
* 4- no host, no realm
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* @param realm
|
||||||
|
*
|
||||||
|
* @return the authentication token
|
||||||
|
*/
|
||||||
|
public AuthenticationToken getAuthenticationToken(String host, String realm) {
|
||||||
|
AuthenticationToken token = null;
|
||||||
|
token = this.authenticationTokens.get(host.concat(realm));
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
// try with just the host
|
||||||
|
token = this.authenticationTokens.get(host);
|
||||||
|
|
||||||
|
// Try the realm
|
||||||
|
if (token == null) {
|
||||||
|
token = this.authenticationTokens.get(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no host found, just query for default
|
||||||
|
if (token == null) {
|
||||||
|
token = this.authenticationTokens.get("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all authentication tokens.
|
||||||
|
*/
|
||||||
|
public void clearAuthenticationTokens() {
|
||||||
|
this.authenticationTokens.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceivedError(CordovaWebView me, int i, String string,
|
||||||
|
String url) {
|
||||||
|
// Only deal with this if we're dealing with a proper classic webview.
|
||||||
|
if(WebView.class.isInstance(me))
|
||||||
|
{
|
||||||
|
this.onReceivedError(me, i, string, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -32,7 +32,6 @@ 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;
|
||||||
|
|
||||||
@@ -47,12 +46,16 @@ 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.
|
||||||
*/
|
*/
|
||||||
@Override
|
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||||
public void pluginInitialize() {
|
super.initialize(cordova, webView);
|
||||||
this.initTelephonyReceiver();
|
this.initTelephonyReceiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
*
|
*
|
||||||
@@ -187,11 +190,7 @@ public class App extends CordovaPlugin {
|
|||||||
* Clear page history for the app.
|
* Clear page history for the app.
|
||||||
*/
|
*/
|
||||||
public void clearHistory() {
|
public void clearHistory() {
|
||||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
this.webView.clearHistory();
|
||||||
public void run() {
|
|
||||||
webView.clearHistory();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -214,7 +213,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.setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, override);
|
webView.bindButton(override);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -226,12 +225,7 @@ 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!");
|
||||||
if (button.equals("volumeup")) {
|
webView.bindButton(button, override);
|
||||||
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, override);
|
|
||||||
}
|
|
||||||
else if (button.equals("volumedown")) {
|
|
||||||
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, override);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,7 +234,7 @@ public class App extends CordovaPlugin {
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public boolean isBackbuttonOverridden() {
|
public boolean isBackbuttonOverridden() {
|
||||||
return webView.isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK);
|
return webView.isBackButtonBound();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,34 +19,183 @@
|
|||||||
|
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.util.List;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
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";
|
|
||||||
|
|
||||||
static ConfigXmlParser parser;
|
public static final String TAG = "Config";
|
||||||
|
|
||||||
private Config() {
|
private Whitelist whitelist = new Whitelist();
|
||||||
}
|
private String startUrl;
|
||||||
|
|
||||||
|
private static Config self = null;
|
||||||
|
|
||||||
public static void init(Activity action) {
|
public static void init(Activity action) {
|
||||||
parser = new ConfigXmlParser();
|
//Just re-initialize this! Seriously, we lose this all the time
|
||||||
parser.parse(action);
|
self = new Config(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 (parser == null) {
|
if (self == null) {
|
||||||
parser = new ConfigXmlParser();
|
self = new Config();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
*
|
*
|
||||||
@@ -54,11 +203,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 (parser == null) {
|
if (self == 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;
|
||||||
}
|
}
|
||||||
parser.getInternalWhitelist().addWhiteListEntry(origin, subdomains);
|
self.whitelist.addWhiteListEntry(origin, subdomains);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,55 +217,17 @@ public class Config {
|
|||||||
* @return true if whitelisted
|
* @return true if whitelisted
|
||||||
*/
|
*/
|
||||||
public static boolean isUrlWhiteListed(String url) {
|
public static boolean isUrlWhiteListed(String url) {
|
||||||
if (parser == null) {
|
if (self == 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 parser.getInternalWhitelist().isUrlWhiteListed(url);
|
return self.whitelist.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 (parser == null) {
|
if (self == null || self.startUrl == null) {
|
||||||
return "file:///android_asset/www/index.html";
|
return "file:///android_asset/www/index.html";
|
||||||
}
|
}
|
||||||
return parser.getLaunchUrl();
|
return self.startUrl;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,181 +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;
|
|
||||||
|
|
||||||
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,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
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,6 +39,7 @@ 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;
|
||||||
@@ -49,16 +52,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:
|
||||||
@@ -75,7 +78,7 @@ import android.widget.LinearLayout;
|
|||||||
* super.onCreate(savedInstanceState);
|
* super.onCreate(savedInstanceState);
|
||||||
* super.init();
|
* super.init();
|
||||||
* // Load your application
|
* // Load your application
|
||||||
* loadUrl(launchUrl);
|
* super.loadUrl(Config.getStartUrl());
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
@@ -92,16 +95,18 @@ 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;
|
||||||
@@ -111,6 +116,10 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -128,165 +137,73 @@ 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;
|
||||||
|
|
||||||
// Read from config.xml:
|
private Object LOG_TAG;
|
||||||
protected CordovaPreferences preferences;
|
|
||||||
protected Whitelist internalWhitelist;
|
|
||||||
protected Whitelist externalWhitelist;
|
|
||||||
protected String launchUrl;
|
|
||||||
protected ArrayList<PluginEntry> pluginEntries;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the authentication token.
|
|
||||||
*
|
|
||||||
* @param authenticationToken
|
|
||||||
* @param host
|
|
||||||
* @param realm
|
|
||||||
*/
|
|
||||||
public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) {
|
|
||||||
if (this.appView != null && this.appView.viewClient != null) {
|
|
||||||
this.appView.viewClient.setAuthenticationToken(authenticationToken, host, realm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the authentication token.
|
|
||||||
*
|
|
||||||
* @param host
|
|
||||||
* @param realm
|
|
||||||
*
|
|
||||||
* @return the authentication token or null if did not exist
|
|
||||||
*/
|
|
||||||
public AuthenticationToken removeAuthenticationToken(String host, String realm) {
|
|
||||||
if (this.appView != null && this.appView.viewClient != null) {
|
|
||||||
return this.appView.viewClient.removeAuthenticationToken(host, realm);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the authentication token.
|
|
||||||
*
|
|
||||||
* In order it tries:
|
|
||||||
* 1- host + realm
|
|
||||||
* 2- host
|
|
||||||
* 3- realm
|
|
||||||
* 4- no host, no realm
|
|
||||||
*
|
|
||||||
* @param host
|
|
||||||
* @param realm
|
|
||||||
*
|
|
||||||
* @return the authentication token
|
|
||||||
*/
|
|
||||||
public AuthenticationToken getAuthenticationToken(String host, String realm) {
|
|
||||||
if (this.appView != null && this.appView.viewClient != null) {
|
|
||||||
return this.appView.viewClient.getAuthenticationToken(host, realm);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all authentication tokens.
|
|
||||||
*/
|
|
||||||
public void clearAuthenticationTokens() {
|
|
||||||
if (this.appView != null && this.appView.viewClient != null) {
|
|
||||||
this.appView.viewClient.clearAuthenticationTokens();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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) {
|
||||||
LOG.i(TAG, "Apache Cordova native platform version " + CordovaWebView.CORDOVA_VERSION + " is starting");
|
Config.init(this);
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
if(this.getBooleanProperty("SetFullscreen", false))
|
||||||
protected void loadConfig() {
|
{
|
||||||
ConfigXmlParser parser = new ConfigXmlParser();
|
Log.d(TAG, "The SetFullscreen configuration is deprecated in favor of Fullscreen, and will be removed in a future version.");
|
||||||
parser.parse(this);
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||||
preferences = parser.getPreferences();
|
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
preferences.setPreferencesBundle(getIntent().getExtras());
|
}
|
||||||
preferences.copyIntoIntentExtras(this);
|
else
|
||||||
internalWhitelist = parser.getInternalWhitelist();
|
{
|
||||||
externalWhitelist = parser.getExternalWhitelist();
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
|
||||||
launchUrl = parser.getLaunchUrl();
|
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||||
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));
|
||||||
|
|
||||||
appView.setId(100);
|
// Setup the hardware volume controls to handle volume control
|
||||||
appView.setLayoutParams(new LinearLayout.LayoutParams(
|
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||||
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
|
||||||
*/
|
*/
|
||||||
@Override public Activity getActivity() {
|
public Activity getActivity() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,80 +214,142 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
* require a more specialized web view.
|
* require a more specialized web view.
|
||||||
*/
|
*/
|
||||||
protected CordovaWebView makeWebView() {
|
protected CordovaWebView makeWebView() {
|
||||||
return new CordovaWebView(CordovaActivity.this);
|
String r = this.getStringProperty("webView", "org.apache.cordova.AndroidWebView");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Class webViewClass = Class.forName(r);
|
||||||
|
Constructor<CordovaWebView> [] webViewConstructors = webViewClass.getConstructors();
|
||||||
|
if(CordovaWebView.class.isAssignableFrom(webViewClass)) {
|
||||||
|
for (Constructor<CordovaWebView> constructor : webViewConstructors) {
|
||||||
|
try {
|
||||||
|
CordovaWebView webView = (CordovaWebView) constructor.newInstance(this);
|
||||||
|
return webView;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOG.d(TAG, "Illegal arguments; trying next constructor.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.e(TAG, "The WebView Engine is NOT a proper WebView, defaulting to system WebView");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
LOG.e(TAG, "The WebView Engine was not found, defaulting to system WebView");
|
||||||
|
} catch (InstantiationException e) {
|
||||||
|
LOG.e(TAG, "Unable to instantiate the WebView, defaulting to system WebView");
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
LOG.e(TAG, "Illegal Access to Constructor. This should never happen, defaulting to system WebView");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOG.e(TAG, "The WebView does not implement the default constructor, defaulting to system WebView");
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
LOG.e(TAG, "Invocation Target Exception! Reflection is hard, defaulting to system WebView");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all else fails, return a default WebView
|
||||||
|
return (CordovaWebView) new AndroidWebView(CordovaActivity.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct the client for the default web view object.
|
* Construct the client for the default web view object.
|
||||||
*
|
*
|
||||||
* This is intended to be overridable by subclasses of CordovaIntent which
|
* This is intended to be overridable by subclasses of CordovaActivity which
|
||||||
* require a more specialized web view.
|
* require a more specialized web view. By default, it allows the webView
|
||||||
|
* to create its own client objects.
|
||||||
*
|
*
|
||||||
* @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) {
|
||||||
return webView.makeWebViewClient(this);
|
return webView.makeWebViewClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct the chrome client for the default web view object.
|
* Construct the chrome client for the default web view object.
|
||||||
*
|
*
|
||||||
* This is intended to be overridable by subclasses of CordovaIntent which
|
* This is intended to be overridable by subclasses of CordovaActivity which
|
||||||
* require a more specialized web view.
|
* require a more specialized web view. By default, it allows the webView
|
||||||
|
* to create its own client objects.
|
||||||
*
|
*
|
||||||
* @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 webView.makeWebChromeClient(this);
|
return webView.makeWebChromeClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and initialize web container with default web view objects.
|
||||||
|
*/
|
||||||
public void init() {
|
public void init() {
|
||||||
this.init(appView, null, null);
|
CordovaWebView webView = makeWebView();
|
||||||
|
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()");
|
||||||
|
|
||||||
appView = webView != null ? webView : makeWebView();
|
// Set up web container
|
||||||
if (appView.pluginManager == null) {
|
this.appView = webView;
|
||||||
appView.init(this, webViewClient != null ? webViewClient : makeWebViewClient(appView),
|
this.appView.setId(100);
|
||||||
webChromeClient != null ? webChromeClient : makeChromeClient(appView),
|
|
||||||
pluginEntries, internalWhitelist, externalWhitelist, preferences);
|
this.appView.setWebViewClient(webViewClient);
|
||||||
|
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) {
|
||||||
|
//Note: We're using the parent class, because all we know is that this will be a view
|
||||||
|
this.appView.setOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Have the views set this themselves.
|
// Add web view but make it invisible while loading URL
|
||||||
if (preferences.getBoolean("DisallowOverscroll", false)) {
|
this.appView.setVisibility(View.INVISIBLE);
|
||||||
appView.setOverScrollMode(View.OVER_SCROLL_NEVER);
|
this.root.addView((View) this.appView.getView());
|
||||||
}
|
setContentView(this.root);
|
||||||
createViews();
|
|
||||||
|
|
||||||
// TODO: Make this a preference (CB-6153)
|
// Clear cancel flag
|
||||||
// Setup the hardware volume controls to handle volume control
|
this.cancelLoadUrl = false;
|
||||||
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();
|
// Init web view if not already done
|
||||||
|
if (this.appView == null) {
|
||||||
|
this.init();
|
||||||
}
|
}
|
||||||
this.splashscreenTime = preferences.getInteger("SplashScreenDelay", this.splashscreenTime);
|
|
||||||
String splash = preferences.getString("SplashScreen", null);
|
this.splashscreenTime = this.getIntegerProperty("SplashScreenDelay", this.splashscreenTime);
|
||||||
if(this.splashscreenTime > 0 && splash != null)
|
if(this.splashscreenTime > 0)
|
||||||
{
|
{
|
||||||
this.splashscreen = getResources().getIdentifier(splash, "drawable", getClass().getPackage().getName());;
|
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
||||||
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 = preferences.getBoolean("KeepRunning", true);
|
this.keepRunning = this.getBooleanProperty("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)
|
||||||
@@ -401,6 +380,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
this.splashscreenTime = time;
|
this.splashscreenTime = time;
|
||||||
this.loadUrl(url);
|
this.loadUrl(url);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -411,10 +391,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 = preferences.getString("LoadingDialog", null);
|
loading = this.getStringProperty("LoadingDialog", null);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
loading = preferences.getString("LoadingPageDialog", null);
|
loading = this.getStringProperty("LoadingPageDialog", null);
|
||||||
}
|
}
|
||||||
if (loading != null) {
|
if (loading != null) {
|
||||||
|
|
||||||
@@ -436,17 +416,13 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public void cancelLoadUrl() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the resource cache.
|
* Clear the resource cache.
|
||||||
*/
|
*/
|
||||||
@Deprecated // Call method on appView directly.
|
|
||||||
public void clearCache() {
|
public void clearCache() {
|
||||||
if (appView == null) {
|
if (this.appView == null) {
|
||||||
init();
|
this.init();
|
||||||
}
|
}
|
||||||
this.appView.clearCache(true);
|
this.appView.clearCache(true);
|
||||||
}
|
}
|
||||||
@@ -454,7 +430,6 @@ 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();
|
||||||
}
|
}
|
||||||
@@ -464,7 +439,6 @@ 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();
|
||||||
@@ -472,102 +446,122 @@ 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) {
|
||||||
return preferences.getBoolean(name, defaultValue);
|
Bundle bundle = this.getIntent().getExtras();
|
||||||
|
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) {
|
||||||
return preferences.getInteger(name, defaultValue);
|
Bundle bundle = this.getIntent().getExtras();
|
||||||
|
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) {
|
||||||
return preferences.getString(name, defaultValue);
|
Bundle bundle = this.getIntent().getExtras();
|
||||||
|
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) {
|
||||||
return preferences.getDouble(name, defaultValue);
|
Bundle bundle = this.getIntent().getExtras();
|
||||||
}
|
if (bundle == null) {
|
||||||
|
return defaultValue;
|
||||||
/**
|
}
|
||||||
* Set boolean property on activity.
|
name = name.toLowerCase(Locale.getDefault());
|
||||||
* This method has been deprecated in 3.0 and will be removed at a future
|
Double p;
|
||||||
* time. Please use config.xml instead.
|
try {
|
||||||
*
|
p = (Double) bundle.get(name);
|
||||||
* @param name
|
} catch (ClassCastException e) {
|
||||||
* @param value
|
p = Double.parseDouble(bundle.get(name).toString());
|
||||||
* @deprecated
|
}
|
||||||
*/
|
if (p == null) {
|
||||||
@Deprecated
|
return defaultValue;
|
||||||
public void setBooleanProperty(String name, boolean value) {
|
}
|
||||||
Log.d(TAG, "Setting boolean properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
return p.doubleValue();
|
||||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set int property on activity.
|
|
||||||
* This method has been deprecated in 3.0 and will be removed at a future
|
|
||||||
* time. Please use config.xml instead.
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @param value
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setIntegerProperty(String name, int value) {
|
|
||||||
Log.d(TAG, "Setting integer properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
|
||||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set string property on activity.
|
|
||||||
* This method has been deprecated in 3.0 and will be removed at a future
|
|
||||||
* time. Please use config.xml instead.
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @param value
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setStringProperty(String name, String value) {
|
|
||||||
Log.d(TAG, "Setting string properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
|
||||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set double property on activity.
|
|
||||||
* This method has been deprecated in 3.0 and will be removed at a future
|
|
||||||
* time. Please use config.xml instead.
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @param value
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setDoubleProperty(String name, double value) {
|
|
||||||
Log.d(TAG, "Setting double properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
|
||||||
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();
|
||||||
|
|
||||||
@@ -590,10 +584,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
|
||||||
@@ -601,14 +595,22 @@ 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;
|
||||||
@@ -617,9 +619,6 @@ 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);
|
||||||
|
|
||||||
@@ -634,10 +633,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();
|
||||||
@@ -655,6 +654,9 @@ 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) {
|
||||||
@@ -662,29 +664,18 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* Add services to res/xml/plugins.xml instead.
|
|
||||||
*
|
|
||||||
* Add a class that implements a service.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void addService(String serviceType, String className) {
|
|
||||||
if (this.appView != null && this.appView.pluginManager != null) {
|
|
||||||
this.appView.pluginManager.addService(serviceType, className);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send JavaScript statement back to JavaScript.
|
* Send JavaScript statement back to JavaScript.
|
||||||
* (This is a convenience method)
|
* (This is a convenience method)
|
||||||
*
|
*
|
||||||
* @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.bridge.getMessageQueue().addJavaScript(statement);
|
this.appView.addJavascript(statement);
|
||||||
|
//this.appView.jsMessageQueue.addJavaScript(statement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -748,6 +739,7 @@ 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.
|
||||||
@@ -757,18 +749,19 @@ 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);
|
||||||
Log.d(TAG, "Request code = " + requestCode);
|
Log.d(TAG, "Request code = " + requestCode);
|
||||||
if (appView != null && requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
|
if (appView != null && requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
|
||||||
ValueCallback<Uri> mUploadMessage = this.appView.getWebChromeClient().getValueCallback();
|
ValueCallback<Uri> mUploadMessage = ((CordovaChromeClient) this.appView.getWebChromeClient()).getValueCallback();
|
||||||
Log.d(TAG, "did we get here?");
|
Log.d(TAG, "did we get here?");
|
||||||
if (null == mUploadMessage)
|
if (null == mUploadMessage)
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -776,7 +769,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
if(callback == null && initCallbackClass != null) {
|
if(callback == null && initCallbackClass != null) {
|
||||||
// The application was restarted, but had defined an initial callback
|
// The application was restarted, but had defined an initial callback
|
||||||
// before being shut down.
|
// before being shut down.
|
||||||
this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
|
//this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
|
||||||
|
this.activityResultCallback = appView.getPlugin(initCallbackClass);
|
||||||
callback = this.activityResultCallback;
|
callback = this.activityResultCallback;
|
||||||
}
|
}
|
||||||
if(callback != null) {
|
if(callback != null) {
|
||||||
@@ -801,8 +795,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 = preferences.getString("errorUrl", null);
|
final String errorUrl = me.getStringProperty("errorUrl", null);
|
||||||
if ((errorUrl != null) && (errorUrl.startsWith("file://") || internalWhitelist.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
if ((errorUrl != null) && (errorUrl.startsWith("file://") || Config.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
||||||
|
|
||||||
// Load URL on UI thread
|
// Load URL on UI thread
|
||||||
me.runOnUiThread(new Runnable() {
|
me.runOnUiThread(new Runnable() {
|
||||||
@@ -829,6 +823,11 @@ 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;
|
||||||
@@ -859,14 +858,17 @@ 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 internalWhitelist.isUrlWhiteListed(url);
|
return Config.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) {
|
||||||
@@ -888,6 +890,9 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Activity context.
|
* Get Activity context.
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
@@ -905,7 +910,6 @@ 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);
|
||||||
@@ -941,7 +945,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(preferences.getInteger("backgroundColor", Color.BLACK));
|
root.setBackgroundColor(that.getIntegerProperty("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);
|
||||||
@@ -1019,10 +1023,7 @@ 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()) {
|
||||||
String splashResource = preferences.getString("SplashScreen", null);
|
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
||||||
if (splashResource != null) {
|
|
||||||
splashscreen = getResources().getIdentifier(splashResource, "drawable", getClass().getPackage().getName());
|
|
||||||
}
|
|
||||||
this.showSplashScreen(this.splashscreenTime);
|
this.showSplashScreen(this.splashscreenTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1060,4 +1061,66 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
outState.putString("callbackClass", cClass);
|
outState.putString("callbackClass", cClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set boolean property on activity.
|
||||||
|
* This method has been deprecated in 3.0 and will be removed at a future
|
||||||
|
* time. Please use config.xml instead.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param value
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setBooleanProperty(String name, boolean value) {
|
||||||
|
Log.d(TAG, "Setting boolean properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||||
|
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set int property on activity.
|
||||||
|
* This method has been deprecated in 3.0 and will be removed at a future
|
||||||
|
* time. Please use config.xml instead.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param value
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setIntegerProperty(String name, int value) {
|
||||||
|
Log.d(TAG, "Setting integer properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||||
|
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set string property on activity.
|
||||||
|
* This method has been deprecated in 3.0 and will be removed at a future
|
||||||
|
* time. Please use config.xml instead.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param value
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setStringProperty(String name, String value) {
|
||||||
|
Log.d(TAG, "Setting string properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||||
|
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set double property on activity.
|
||||||
|
* This method has been deprecated in 3.0 and will be removed at a future
|
||||||
|
* time. Please use config.xml instead.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param value
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setDoubleProperty(String name, double value) {
|
||||||
|
Log.d(TAG, "Setting double properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||||
|
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,183 +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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
325
framework/src/org/apache/cordova/CordovaChromeClient.java
Executable file → Normal file
325
framework/src/org/apache/cordova/CordovaChromeClient.java
Executable file → Normal file
@@ -1,331 +1,14 @@
|
|||||||
/*
|
|
||||||
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;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import org.apache.cordova.CordovaInterface;
|
|
||||||
import org.apache.cordova.LOG;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup.LayoutParams;
|
|
||||||
import android.webkit.ConsoleMessage;
|
|
||||||
import android.webkit.JsPromptResult;
|
|
||||||
import android.webkit.JsResult;
|
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
import android.webkit.WebChromeClient;
|
|
||||||
import android.webkit.WebStorage;
|
|
||||||
import android.webkit.WebView;
|
|
||||||
import android.webkit.GeolocationPermissions.Callback;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
|
|
||||||
/**
|
public interface CordovaChromeClient {
|
||||||
* This class is the WebChromeClient that implements callbacks for our web view.
|
|
||||||
* The kind of callbacks that happen here are on the chrome outside the document,
|
|
||||||
* such as onCreateWindow(), onConsoleMessage(), onProgressChanged(), etc. Related
|
|
||||||
* to but different than CordovaWebViewClient.
|
|
||||||
*
|
|
||||||
* @see <a href="http://developer.android.com/reference/android/webkit/WebChromeClient.html">WebChromeClient</a>
|
|
||||||
* @see <a href="http://developer.android.com/guide/webapps/webview.html">WebView guide</a>
|
|
||||||
* @see CordovaWebViewClient
|
|
||||||
* @see CordovaWebView
|
|
||||||
*/
|
|
||||||
public class CordovaChromeClient extends WebChromeClient {
|
|
||||||
|
|
||||||
public static final int FILECHOOSER_RESULTCODE = 5173;
|
int FILECHOOSER_RESULTCODE = 0;
|
||||||
private String TAG = "CordovaLog";
|
|
||||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
|
||||||
protected CordovaInterface cordova;
|
|
||||||
protected CordovaWebView appView;
|
|
||||||
|
|
||||||
// the video progress view
|
void setWebView(CordovaWebView appView);
|
||||||
private View mVideoProgressView;
|
|
||||||
|
|
||||||
// File Chooser
|
|
||||||
public ValueCallback<Uri> mUploadMessage;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public CordovaChromeClient(CordovaInterface cordova) {
|
|
||||||
this.cordova = cordova;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CordovaChromeClient(CordovaInterface ctx, CordovaWebView app) {
|
ValueCallback<Uri> getValueCallback();
|
||||||
this.cordova = ctx;
|
|
||||||
this.appView = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public void setWebView(CordovaWebView view) {
|
|
||||||
this.appView = view;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell the client to display a javascript alert dialog.
|
|
||||||
*
|
|
||||||
* @param view
|
|
||||||
* @param url
|
|
||||||
* @param message
|
|
||||||
* @param result
|
|
||||||
* @see Other implementation in the Dialogs plugin.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
|
|
||||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
|
||||||
dlg.setMessage(message);
|
|
||||||
dlg.setTitle("Alert");
|
|
||||||
//Don't let alerts break the back button
|
|
||||||
dlg.setCancelable(true);
|
|
||||||
dlg.setPositiveButton(android.R.string.ok,
|
|
||||||
new AlertDialog.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
result.confirm();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dlg.setOnCancelListener(
|
|
||||||
new DialogInterface.OnCancelListener() {
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
result.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
|
||||||
//DO NOTHING
|
|
||||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK)
|
|
||||||
{
|
|
||||||
result.confirm();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dlg.show();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell the client to display a confirm dialog to the user.
|
|
||||||
*
|
|
||||||
* @param view
|
|
||||||
* @param url
|
|
||||||
* @param message
|
|
||||||
* @param result
|
|
||||||
* @see Other implementation in the Dialogs plugin.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
|
|
||||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
|
||||||
dlg.setMessage(message);
|
|
||||||
dlg.setTitle("Confirm");
|
|
||||||
dlg.setCancelable(true);
|
|
||||||
dlg.setPositiveButton(android.R.string.ok,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
result.confirm();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dlg.setNegativeButton(android.R.string.cancel,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
result.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dlg.setOnCancelListener(
|
|
||||||
new DialogInterface.OnCancelListener() {
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
result.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
|
||||||
//DO NOTHING
|
|
||||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK)
|
|
||||||
{
|
|
||||||
result.cancel();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dlg.show();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell the client to display a prompt dialog to the user.
|
|
||||||
* If the client returns true, WebView will assume that the client will
|
|
||||||
* handle the prompt dialog and call the appropriate JsPromptResult method.
|
|
||||||
*
|
|
||||||
* 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!
|
|
||||||
*
|
|
||||||
* @see Other implementation in the Dialogs plugin.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
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.
|
|
||||||
String handledRet = appView.bridge.promptOnJsPrompt(origin, message, defaultValue);
|
|
||||||
if (handledRet != null) {
|
|
||||||
result.confirm(handledRet);
|
|
||||||
} else {
|
|
||||||
// Returning false would also show a dialog, but the default one shows the origin (ugly).
|
|
||||||
final JsPromptResult res = result;
|
|
||||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
|
||||||
dlg.setMessage(message);
|
|
||||||
final EditText input = new EditText(this.cordova.getActivity());
|
|
||||||
if (defaultValue != null) {
|
|
||||||
input.setText(defaultValue);
|
|
||||||
}
|
|
||||||
dlg.setView(input);
|
|
||||||
dlg.setCancelable(false);
|
|
||||||
dlg.setPositiveButton(android.R.string.ok,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
String usertext = input.getText().toString();
|
|
||||||
res.confirm(usertext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dlg.setNegativeButton(android.R.string.cancel,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
res.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dlg.show();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle database quota exceeded notification.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
|
|
||||||
long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
|
|
||||||
{
|
|
||||||
LOG.d(TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota);
|
|
||||||
quotaUpdater.updateQuota(MAX_QUOTA);
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html
|
|
||||||
// Expect this to not compile in a future Android release!
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
|
||||||
public void onConsoleMessage(String message, int lineNumber, String sourceID)
|
|
||||||
{
|
|
||||||
//This is only for Android 2.1
|
|
||||||
if(android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.ECLAIR_MR1)
|
|
||||||
{
|
|
||||||
LOG.d(TAG, "%s: Line %d : %s", sourceID, lineNumber, message);
|
|
||||||
super.onConsoleMessage(message, lineNumber, sourceID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(8)
|
|
||||||
@Override
|
|
||||||
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
|
|
||||||
{
|
|
||||||
if (consoleMessage.message() != null)
|
|
||||||
LOG.d(TAG, "%s: Line %d : %s" , consoleMessage.sourceId() , consoleMessage.lineNumber(), consoleMessage.message());
|
|
||||||
return super.onConsoleMessage(consoleMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
/**
|
|
||||||
* Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin.
|
|
||||||
*
|
|
||||||
* @param origin
|
|
||||||
* @param callback
|
|
||||||
*/
|
|
||||||
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
|
|
||||||
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
|
||||||
callback.invoke(origin, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// API level 7 is required for this, see if we could lower this using something else
|
|
||||||
@Override
|
|
||||||
public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
|
|
||||||
this.appView.showCustomView(view, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onHideCustomView() {
|
|
||||||
this.appView.hideCustomView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
/**
|
|
||||||
* Ask the host application for a custom progress view to show while
|
|
||||||
* a <video> is loading.
|
|
||||||
* @return View The progress view.
|
|
||||||
*/
|
|
||||||
public View getVideoLoadingProgressView() {
|
|
||||||
|
|
||||||
if (mVideoProgressView == null) {
|
|
||||||
// Create a new Loading view programmatically.
|
|
||||||
|
|
||||||
// create the linear layout
|
|
||||||
LinearLayout layout = new LinearLayout(this.appView.getContext());
|
|
||||||
layout.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
|
||||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
|
||||||
layout.setLayoutParams(layoutParams);
|
|
||||||
// the proress bar
|
|
||||||
ProgressBar bar = new ProgressBar(this.appView.getContext());
|
|
||||||
LinearLayout.LayoutParams barLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
|
||||||
barLayoutParams.gravity = Gravity.CENTER;
|
|
||||||
bar.setLayoutParams(barLayoutParams);
|
|
||||||
layout.addView(bar);
|
|
||||||
|
|
||||||
mVideoProgressView = layout;
|
|
||||||
}
|
|
||||||
return mVideoProgressView;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
|
|
||||||
this.openFileChooser(uploadMsg, "*/*");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType ) {
|
|
||||||
this.openFileChooser(uploadMsg, acceptType, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
|
|
||||||
{
|
|
||||||
mUploadMessage = uploadMsg;
|
|
||||||
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
|
|
||||||
i.addCategory(Intent.CATEGORY_OPENABLE);
|
|
||||||
i.setType("*/*");
|
|
||||||
this.cordova.getActivity().startActivityForResult(Intent.createChooser(i, "File Browser"),
|
|
||||||
FILECHOOSER_RESULTCODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueCallback<Uri> getValueCallback() {
|
|
||||||
return this.mUploadMessage;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,39 +32,20 @@ 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;
|
public CordovaWebView webView; // WebView object
|
||||||
public CordovaInterface cordova;
|
public CordovaInterface cordova;
|
||||||
protected CordovaPreferences preferences;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this after constructing to initialize the plugin.
|
* @param cordova The context of the main Activity.
|
||||||
* Final because we want to be able to change args without breaking plugins.
|
* @param webView The associated CordovaWebView.
|
||||||
*/
|
*/
|
||||||
public final void privateInitialize(CordovaInterface cordova, CordovaWebView webView, CordovaPreferences preferences) {
|
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||||
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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,175 +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;
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -84,7 +84,7 @@ public class CordovaResourceApi {
|
|||||||
// Creating this is light-weight.
|
// Creating this is light-weight.
|
||||||
private static OkHttpClient httpClient = new OkHttpClient();
|
private static OkHttpClient httpClient = new OkHttpClient();
|
||||||
|
|
||||||
static Thread jsThread;
|
public static Thread jsThread;
|
||||||
|
|
||||||
private final AssetManager assetManager;
|
private final AssetManager assetManager;
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
@@ -106,7 +106,6 @@ 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();
|
||||||
@@ -200,8 +199,6 @@ 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('.');
|
||||||
@@ -220,7 +217,7 @@ public class CordovaResourceApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a stream to the given URI, also providing the MIME type & length.
|
* Opens a stream to the givne 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.
|
||||||
@@ -232,7 +229,7 @@ public class CordovaResourceApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a stream to the given URI, also providing the MIME type & length.
|
* Opens a stream to the givne 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.
|
||||||
|
|||||||
@@ -1,86 +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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
948
framework/src/org/apache/cordova/CordovaWebView.java
Executable file → Normal file
948
framework/src/org/apache/cordova/CordovaWebView.java
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
362
framework/src/org/apache/cordova/CordovaWebViewClient.java
Executable file → Normal file
362
framework/src/org/apache/cordova/CordovaWebViewClient.java
Executable file → Normal file
@@ -1,365 +1,9 @@
|
|||||||
/*
|
|
||||||
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;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.util.Hashtable;
|
public interface CordovaWebViewClient {
|
||||||
|
|
||||||
import org.apache.cordova.CordovaInterface;
|
void setWebView(CordovaWebView appView);
|
||||||
|
|
||||||
import org.apache.cordova.LOG;
|
void onReceivedError(CordovaWebView me, int i, String string, String url);
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.net.http.SslError;
|
|
||||||
import android.view.View;
|
|
||||||
import android.webkit.HttpAuthHandler;
|
|
||||||
import android.webkit.SslErrorHandler;
|
|
||||||
import android.webkit.WebView;
|
|
||||||
import android.webkit.WebViewClient;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is the WebViewClient that implements callbacks for our web view.
|
|
||||||
* The kind of callbacks that happen here are regarding the rendering of the
|
|
||||||
* document instead of the chrome surrounding it, such as onPageStarted(),
|
|
||||||
* shouldOverrideUrlLoading(), etc. Related to but different than
|
|
||||||
* CordovaChromeClient.
|
|
||||||
*
|
|
||||||
* @see <a href="http://developer.android.com/reference/android/webkit/WebViewClient.html">WebViewClient</a>
|
|
||||||
* @see <a href="http://developer.android.com/guide/webapps/webview.html">WebView guide</a>
|
|
||||||
* @see CordovaChromeClient
|
|
||||||
* @see CordovaWebView
|
|
||||||
*/
|
|
||||||
public class CordovaWebViewClient extends WebViewClient {
|
|
||||||
|
|
||||||
private static final String TAG = "CordovaWebViewClient";
|
|
||||||
CordovaInterface cordova;
|
|
||||||
CordovaWebView appView;
|
|
||||||
CordovaUriHelper helper;
|
|
||||||
private boolean doClearHistory = false;
|
|
||||||
boolean isCurrentlyLoading;
|
|
||||||
|
|
||||||
/** The authorization tokens. */
|
|
||||||
private Hashtable<String, AuthenticationToken> authenticationTokens = new Hashtable<String, AuthenticationToken>();
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public CordovaWebViewClient(CordovaInterface cordova) {
|
|
||||||
this.cordova = cordova;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param cordova
|
|
||||||
* @param view
|
|
||||||
*/
|
|
||||||
public CordovaWebViewClient(CordovaInterface cordova, CordovaWebView view) {
|
|
||||||
this.cordova = cordova;
|
|
||||||
this.appView = view;
|
|
||||||
helper = new CordovaUriHelper(cordova, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param view
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setWebView(CordovaWebView view) {
|
|
||||||
this.appView = view;
|
|
||||||
helper = new CordovaUriHelper(cordova, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
|
||||||
return helper.shouldOverrideUrlLoading(view, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On received http auth request.
|
|
||||||
* The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination
|
|
||||||
*
|
|
||||||
* @param view
|
|
||||||
* @param handler
|
|
||||||
* @param host
|
|
||||||
* @param realm
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
|
|
||||||
|
|
||||||
// Get the authentication token
|
|
||||||
AuthenticationToken token = this.getAuthenticationToken(host, realm);
|
|
||||||
if (token != null) {
|
|
||||||
handler.proceed(token.getUserName(), token.getPassword());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Handle 401 like we'd normally do!
|
|
||||||
super.onReceivedHttpAuthRequest(view, handler, host, realm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify the host application that a page has started loading.
|
|
||||||
* This method is called once for each main frame load so a page with iframes or framesets will call onPageStarted
|
|
||||||
* one time for the main frame. This also means that onPageStarted will not be called when the contents of an
|
|
||||||
* embedded frame changes, i.e. clicking a link whose target is an iframe.
|
|
||||||
*
|
|
||||||
* @param view The webview initiating the callback.
|
|
||||||
* @param url The url of the page.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
|
||||||
super.onPageStarted(view, url, favicon);
|
|
||||||
isCurrentlyLoading = true;
|
|
||||||
LOG.d(TAG, "onPageStarted(" + url + ")");
|
|
||||||
// Flush stale messages.
|
|
||||||
this.appView.bridge.reset(url);
|
|
||||||
|
|
||||||
// Broadcast message that page has loaded
|
|
||||||
this.appView.postMessage("onPageStarted", url);
|
|
||||||
|
|
||||||
// Notify all plugins of the navigation, so they can clean up if necessary.
|
|
||||||
if (this.appView.pluginManager != null) {
|
|
||||||
this.appView.pluginManager.onReset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify the host application that a page has finished loading.
|
|
||||||
* This method is called only for main frame. When onPageFinished() is called, the rendering picture may not be updated yet.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param view The webview initiating the callback.
|
|
||||||
* @param url The url of the page.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onPageFinished(WebView view, String url) {
|
|
||||||
super.onPageFinished(view, url);
|
|
||||||
// Ignore excessive calls.
|
|
||||||
if (!isCurrentlyLoading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isCurrentlyLoading = false;
|
|
||||||
LOG.d(TAG, "onPageFinished(" + url + ")");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Because of a timing issue we need to clear this history in onPageFinished as well as
|
|
||||||
* onPageStarted. However we only want to do this if the doClearHistory boolean is set to
|
|
||||||
* true. You see when you load a url with a # in it which is common in jQuery applications
|
|
||||||
* onPageStared is not called. Clearing the history at that point would break jQuery apps.
|
|
||||||
*/
|
|
||||||
if (this.doClearHistory) {
|
|
||||||
view.clearHistory();
|
|
||||||
this.doClearHistory = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear timeout flag
|
|
||||||
this.appView.loadUrlTimeout++;
|
|
||||||
|
|
||||||
// Broadcast message that page has loaded
|
|
||||||
this.appView.postMessage("onPageFinished", url);
|
|
||||||
|
|
||||||
// Make app visible after 2 sec in case there was a JS error and Cordova JS never initialized correctly
|
|
||||||
if (this.appView.getVisibility() == View.INVISIBLE) {
|
|
||||||
Thread t = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
appView.postMessage("spinner", "stop");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
t.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown if blank loaded
|
|
||||||
if (url.equals("about:blank")) {
|
|
||||||
appView.postMessage("exit", null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable).
|
|
||||||
* The errorCode parameter corresponds to one of the ERROR_* constants.
|
|
||||||
*
|
|
||||||
* @param view The WebView that is initiating the callback.
|
|
||||||
* @param errorCode The error code corresponding to an ERROR_* value.
|
|
||||||
* @param description A String describing the error.
|
|
||||||
* @param failingUrl The url that failed to load.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
|
||||||
// Ignore error due to stopLoading().
|
|
||||||
if (!isCurrentlyLoading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LOG.d(TAG, "CordovaWebViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl);
|
|
||||||
|
|
||||||
// Clear timeout flag
|
|
||||||
this.appView.loadUrlTimeout++;
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
try {
|
|
||||||
data.put("errorCode", errorCode);
|
|
||||||
data.put("description", description);
|
|
||||||
data.put("url", failingUrl);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
this.appView.postMessage("onReceivedError", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify the host application that an SSL error occurred while loading a resource.
|
|
||||||
* The host application must call either handler.cancel() or handler.proceed().
|
|
||||||
* Note that the decision may be retained for use in response to future SSL errors.
|
|
||||||
* The default behavior is to cancel the load.
|
|
||||||
*
|
|
||||||
* @param view The WebView that is initiating the callback.
|
|
||||||
* @param handler An SslErrorHandler object that will handle the user's response.
|
|
||||||
* @param error The SSL error object.
|
|
||||||
*/
|
|
||||||
@TargetApi(8)
|
|
||||||
@Override
|
|
||||||
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
|
||||||
|
|
||||||
final String packageName = this.cordova.getActivity().getPackageName();
|
|
||||||
final PackageManager pm = this.cordova.getActivity().getPackageManager();
|
|
||||||
|
|
||||||
ApplicationInfo appInfo;
|
|
||||||
try {
|
|
||||||
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
|
|
||||||
if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
|
||||||
// debug = true
|
|
||||||
handler.proceed();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// debug = false
|
|
||||||
super.onReceivedSslError(view, handler, error);
|
|
||||||
}
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
// When it doubt, lock it out!
|
|
||||||
super.onReceivedSslError(view, handler, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the authentication token.
|
|
||||||
*
|
|
||||||
* @param authenticationToken
|
|
||||||
* @param host
|
|
||||||
* @param realm
|
|
||||||
*/
|
|
||||||
public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) {
|
|
||||||
if (host == null) {
|
|
||||||
host = "";
|
|
||||||
}
|
|
||||||
if (realm == null) {
|
|
||||||
realm = "";
|
|
||||||
}
|
|
||||||
this.authenticationTokens.put(host.concat(realm), authenticationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the authentication token.
|
|
||||||
*
|
|
||||||
* @param host
|
|
||||||
* @param realm
|
|
||||||
*
|
|
||||||
* @return the authentication token or null if did not exist
|
|
||||||
*/
|
|
||||||
public AuthenticationToken removeAuthenticationToken(String host, String realm) {
|
|
||||||
return this.authenticationTokens.remove(host.concat(realm));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the authentication token.
|
|
||||||
*
|
|
||||||
* In order it tries:
|
|
||||||
* 1- host + realm
|
|
||||||
* 2- host
|
|
||||||
* 3- realm
|
|
||||||
* 4- no host, no realm
|
|
||||||
*
|
|
||||||
* @param host
|
|
||||||
* @param realm
|
|
||||||
*
|
|
||||||
* @return the authentication token
|
|
||||||
*/
|
|
||||||
public AuthenticationToken getAuthenticationToken(String host, String realm) {
|
|
||||||
AuthenticationToken token = null;
|
|
||||||
token = this.authenticationTokens.get(host.concat(realm));
|
|
||||||
|
|
||||||
if (token == null) {
|
|
||||||
// try with just the host
|
|
||||||
token = this.authenticationTokens.get(host);
|
|
||||||
|
|
||||||
// Try the realm
|
|
||||||
if (token == null) {
|
|
||||||
token = this.authenticationTokens.get(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no host found, just query for default
|
|
||||||
if (token == null) {
|
|
||||||
token = this.authenticationTokens.get("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all authentication tokens.
|
|
||||||
*/
|
|
||||||
public void clearAuthenticationTokens() {
|
|
||||||
this.authenticationTokens.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
53
framework/src/org/apache/cordova/ExposedJsApi.java
Executable file → Normal file
53
framework/src/org/apache/cordova/ExposedJsApi.java
Executable file → Normal file
@@ -1,52 +1,17 @@
|
|||||||
/*
|
|
||||||
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;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import android.webkit.JavascriptInterface;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
||||||
/**
|
import android.webkit.JavascriptInterface;
|
||||||
* 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
|
* Any exposed Javascript API MUST implement these three things!
|
||||||
*/
|
*/
|
||||||
/* package */ class ExposedJsApi {
|
|
||||||
|
public interface ExposedJsApi {
|
||||||
private CordovaBridge bridge;
|
|
||||||
|
|
||||||
public ExposedJsApi(CordovaBridge bridge) {
|
|
||||||
this.bridge = bridge;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
|
public String exec(String service, String action, String callbackId, String arguments) throws JSONException;
|
||||||
return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments);
|
public void setNativeToJsBridgeMode(int value);
|
||||||
}
|
public String retrieveJsMessages(boolean fromOnlineEvent);
|
||||||
|
|
||||||
@JavascriptInterface
|
|
||||||
public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException {
|
|
||||||
bridge.jsSetNativeToJsBridgeMode(bridgeSecret, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JavascriptInterface
|
|
||||||
public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException {
|
|
||||||
return bridge.jsRetrieveJsMessages(bridgeSecret, fromOnlineEvent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,10 +32,9 @@ import android.webkit.WebResourceResponse;
|
|||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
public class IceCreamCordovaWebViewClient extends AndroidWebViewClient implements 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);
|
||||||
@@ -48,9 +47,8 @@ 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 whitelist and lock out access to the WebView directory
|
// Check the against the white-list.
|
||||||
// Changing this will cause problems for your application
|
if ((url.startsWith("http:") || url.startsWith("https:")) && !Config.isUrlWhiteListed(url)) {
|
||||||
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);
|
||||||
@@ -76,11 +74,6 @@ 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());
|
||||||
}
|
}
|
||||||
@@ -104,4 +97,10 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceivedError(CordovaWebView me, int i, String string,
|
||||||
|
String url) {
|
||||||
|
super.onReceivedError(me, i, string, url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,13 +88,13 @@ public class LinearLayoutSoftKeyboardDetect extends LinearLayout {
|
|||||||
// gone away.
|
// gone away.
|
||||||
else if (height > oldHeight) {
|
else if (height > oldHeight) {
|
||||||
if (app != null)
|
if (app != null)
|
||||||
app.appView.sendJavascript("cordova.fireDocumentEvent('hidekeyboard');");
|
app.sendJavascript("cordova.fireDocumentEvent('hidekeyboard');");
|
||||||
}
|
}
|
||||||
// If the height as gotten smaller then we will assume the soft keyboard has
|
// If the height as gotten smaller then we will assume the soft keyboard has
|
||||||
// been displayed.
|
// been displayed.
|
||||||
else if (height < oldHeight) {
|
else if (height < oldHeight) {
|
||||||
if (app != null)
|
if (app != null)
|
||||||
app.appView.sendJavascript("cordova.fireDocumentEvent('showkeyboard');");
|
app.sendJavascript("cordova.fireDocumentEvent('showkeyboard');");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the old height for the next event
|
// Update the old height for the next event
|
||||||
|
|||||||
@@ -35,19 +35,31 @@ 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.
|
||||||
|
public 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;
|
public 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.
|
||||||
@@ -64,13 +76,6 @@ 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;
|
||||||
|
|
||||||
@@ -84,28 +89,22 @@ 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 < -1 || value >= registeredListeners.length) {
|
if (value < 0 || value >= registeredListeners.length) {
|
||||||
Log.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value);
|
Log.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value);
|
||||||
} else {
|
} else {
|
||||||
BridgeMode newMode = value < 0 ? null : registeredListeners[value];
|
if (value != activeListenerIndex) {
|
||||||
if (newMode != activeBridgeMode) {
|
Log.d(LOG_TAG, "Set native->JS mode to " + value);
|
||||||
Log.d(LOG_TAG, "Set native->JS mode to " + (newMode == null ? "null" : newMode.getClass().getSimpleName()));
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
activeBridgeMode = newMode;
|
activeListenerIndex = value;
|
||||||
if (newMode != null) {
|
BridgeMode activeListener = registeredListeners[value];
|
||||||
newMode.reset();
|
activeListener.reset();
|
||||||
if (!paused && !queue.isEmpty()) {
|
if (!paused && !queue.isEmpty()) {
|
||||||
newMode.onNativeToJsMessageAvailable();
|
activeListener.onNativeToJsMessageAvailable();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +117,8 @@ public class NativeToJsMessageQueue {
|
|||||||
public void reset() {
|
public void reset() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
queue.clear();
|
queue.clear();
|
||||||
setBridgeMode(-1);
|
setBridgeMode(DEFAULT_BRIDGE_MODE);
|
||||||
|
registeredListeners[activeListenerIndex].reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,10 +142,7 @@ public class NativeToJsMessageQueue {
|
|||||||
*/
|
*/
|
||||||
public String popAndEncode(boolean fromOnlineEvent) {
|
public String popAndEncode(boolean fromOnlineEvent) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (activeBridgeMode == null) {
|
registeredListeners[activeListenerIndex].notifyOfFlush(fromOnlineEvent);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
activeBridgeMode.notifyOfFlush(fromOnlineEvent);
|
|
||||||
if (queue.isEmpty()) {
|
if (queue.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -250,20 +247,16 @@ 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) {
|
||||||
activeBridgeMode.onNativeToJsMessageAvailable();
|
registeredListeners[activeListenerIndex].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
|
||||||
@@ -273,12 +266,16 @@ public class NativeToJsMessageQueue {
|
|||||||
paused = value;
|
paused = value;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!queue.isEmpty() && activeBridgeMode != null) {
|
if (!queue.isEmpty()) {
|
||||||
activeBridgeMode.onNativeToJsMessageAvailable();
|
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getPaused() {
|
||||||
|
return paused;
|
||||||
|
}
|
||||||
|
|
||||||
private abstract class BridgeMode {
|
private abstract class BridgeMode {
|
||||||
abstract void onNativeToJsMessageAvailable();
|
abstract void onNativeToJsMessageAvailable();
|
||||||
@@ -311,33 +308,23 @@ 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;
|
||||||
private boolean ignoreNextFlush;
|
final Runnable runnable = new Runnable() {
|
||||||
|
|
||||||
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() {
|
||||||
cordova.getActivity().runOnUiThread(resetNetworkRunnable);
|
online = false;
|
||||||
|
webView.setNetworkAvailable(true);
|
||||||
}
|
}
|
||||||
@Override void onNativeToJsMessageAvailable() {
|
@Override void onNativeToJsMessageAvailable() {
|
||||||
cordova.getActivity().runOnUiThread(toggleNetworkRunnable);
|
cordova.getActivity().runOnUiThread(runnable);
|
||||||
}
|
}
|
||||||
// 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 && !ignoreNextFlush) {
|
if (fromOnlineEvent) {
|
||||||
online = !online;
|
online = !online;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.util.List;
|
import org.apache.cordova.CordovaWebView;
|
||||||
|
import org.apache.cordova.CordovaInterface;
|
||||||
import org.apache.cordova.CordovaPlugin;
|
import org.apache.cordova.CordovaPlugin;
|
||||||
|
|
||||||
|
//import android.content.Context;
|
||||||
|
//import android.webkit.WebView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a service entry object.
|
* This class represents a service entry object.
|
||||||
*/
|
*/
|
||||||
@@ -30,60 +33,100 @@ 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 pre-instantiated plugin to use for this entry.
|
* The plugin object.
|
||||||
|
* 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;
|
public CordovaPlugin plugin = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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;
|
public boolean onload = false;
|
||||||
|
|
||||||
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() {
|
/**
|
||||||
return urlFilters;
|
* Create plugin object.
|
||||||
|
* 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,9 +18,14 @@
|
|||||||
*/
|
*/
|
||||||
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;
|
||||||
@@ -28,8 +33,11 @@ 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;
|
||||||
@@ -45,40 +53,31 @@ 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, CordovaPlugin> pluginMap = new HashMap<String, CordovaPlugin>();
|
private final HashMap<String, PluginEntry> entries = new HashMap<String, PluginEntry>();
|
||||||
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>>();
|
||||||
|
|
||||||
@Deprecated
|
private AtomicInteger numPendingUiExecs;
|
||||||
PluginManager(CordovaWebView cordovaWebView, CordovaInterface cordova) {
|
|
||||||
this(cordovaWebView, cordova, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
PluginManager(CordovaWebView cordovaWebView, CordovaInterface cordova, List<PluginEntry> pluginEntries) {
|
/**
|
||||||
this.ctx = cordova;
|
* Constructor.
|
||||||
this.app = cordovaWebView;
|
*
|
||||||
if (pluginEntries == null) {
|
* @param app
|
||||||
ConfigXmlParser parser = new ConfigXmlParser();
|
* @param ctx
|
||||||
parser.parse(ctx.getActivity());
|
*/
|
||||||
pluginEntries = parser.getPluginEntries();
|
public PluginManager(CordovaWebView app, CordovaInterface ctx) {
|
||||||
}
|
this.ctx = ctx;
|
||||||
setPluginEntries(pluginEntries);
|
this.app = app;
|
||||||
}
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,32 +85,114 @@ public class PluginManager {
|
|||||||
*/
|
*/
|
||||||
public void init() {
|
public void init() {
|
||||||
LOG.d(TAG, "init()");
|
LOG.d(TAG, "init()");
|
||||||
this.onPause(false);
|
|
||||||
this.onDestroy();
|
// If first time, then load plugins from config.xml file
|
||||||
pluginMap.clear();
|
if (this.firstRun) {
|
||||||
|
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() {
|
||||||
pluginMap.clear();
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
|
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 : entryMap.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
if (entry.onload) {
|
if (entry.onload) {
|
||||||
getPlugin(entry.service);
|
entry.createPlugin(this.app, this.ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,6 +215,20 @@ 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);
|
||||||
@@ -177,21 +272,15 @@ public class PluginManager {
|
|||||||
* @return CordovaPlugin or null
|
* @return CordovaPlugin or null
|
||||||
*/
|
*/
|
||||||
public CordovaPlugin getPlugin(String service) {
|
public CordovaPlugin getPlugin(String service) {
|
||||||
CordovaPlugin ret = pluginMap.get(service);
|
PluginEntry entry = this.entries.get(service);
|
||||||
if (ret == null) {
|
if (entry == null) {
|
||||||
PluginEntry pe = entryMap.get(service);
|
return null;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
return ret;
|
CordovaPlugin plugin = entry.plugin;
|
||||||
|
if (plugin == null) {
|
||||||
|
plugin = entry.createPlugin(this.app, this.ctx);
|
||||||
|
}
|
||||||
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,16 +302,7 @@ public class PluginManager {
|
|||||||
* @param entry The plugin entry
|
* @param entry The plugin entry
|
||||||
*/
|
*/
|
||||||
public void addService(PluginEntry entry) {
|
public void addService(PluginEntry entry) {
|
||||||
this.entryMap.put(entry.service, entry);
|
this.entries.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -231,8 +311,10 @@ 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 (CordovaPlugin plugin : this.pluginMap.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
plugin.onPause(multitasking);
|
if (entry.plugin != null) {
|
||||||
|
entry.plugin.onPause(multitasking);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,8 +324,10 @@ 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 (CordovaPlugin plugin : this.pluginMap.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
plugin.onResume(multitasking);
|
if (entry.plugin != null) {
|
||||||
|
entry.plugin.onResume(multitasking);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,8 +335,10 @@ 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 (CordovaPlugin plugin : this.pluginMap.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
plugin.onDestroy();
|
if (entry.plugin != null) {
|
||||||
|
entry.plugin.onDestroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,10 +354,12 @@ public class PluginManager {
|
|||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
obj = plugin.onMessage(id, data);
|
if (entry.plugin != null) {
|
||||||
if (obj != null) {
|
obj = entry.plugin.onMessage(id, data);
|
||||||
return obj;
|
if (obj != null) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -281,8 +369,10 @@ 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 (CordovaPlugin plugin : this.pluginMap.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
plugin.onNewIntent(intent);
|
if (entry.plugin != null) {
|
||||||
|
entry.plugin.onNewIntent(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +387,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.entryMap.values()) {
|
for (PluginEntry entry : this.entries.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) {
|
||||||
@@ -305,9 +395,8 @@ public class PluginManager {
|
|||||||
return getPlugin(entry.service).onOverrideUrlLoading(url);
|
return getPlugin(entry.service).onOverrideUrlLoading(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (entry.plugin != null) {
|
||||||
CordovaPlugin plugin = pluginMap.get(entry.service);
|
if (entry.plugin.onOverrideUrlLoading(url)) {
|
||||||
if (plugin != null && plugin.onOverrideUrlLoading(url)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,38 +408,54 @@ public class PluginManager {
|
|||||||
* Called when the app navigates or refreshes.
|
* Called when the app navigates or refreshes.
|
||||||
*/
|
*/
|
||||||
public void onReset() {
|
public void onReset() {
|
||||||
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
Iterator<PluginEntry> it = this.entries.values().iterator();
|
||||||
plugin.onReset();
|
while (it.hasNext()) {
|
||||||
|
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 (CordovaPlugin plugin : this.pluginMap.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
Uri ret = plugin.remapUri(uri);
|
if (entry.plugin != null) {
|
||||||
if (ret != null) {
|
Uri ret = entry.plugin.remapUri(uri);
|
||||||
return ret;
|
if (ret != null) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private class PluginManagerService extends CordovaPlugin {
|
||||||
* Create a plugin based on class name.
|
@Override
|
||||||
*/
|
public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
|
||||||
private CordovaPlugin instantiatePlugin(String className) {
|
if ("startup".equals(action)) {
|
||||||
CordovaPlugin ret = null;
|
// The onPageStarted event of CordovaWebViewClient resets the queue of messages to be returned to javascript in response
|
||||||
try {
|
// to exec calls. Since this event occurs on the UI thread and exec calls happen on the WebCore thread it is possible
|
||||||
Class<?> c = null;
|
// that onPageStarted occurs after exec calls have started happening on a new page, which can cause the message queue
|
||||||
if ((className != null) && !("".equals(className))) {
|
// to be reset between the queuing of a new message and its retrieval by javascript. To avoid this from happening,
|
||||||
c = Class.forName(className);
|
// javascript always sends a "startup" exec upon loading a new page which causes all future exec calls to happen on the UI
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
if (c != null & CordovaPlugin.class.isAssignableFrom(c)) {
|
return false;
|
||||||
ret = (CordovaPlugin) c.newInstance();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.out.println("Error adding plugin " + className + ".");
|
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class ScrollEvent {
|
|||||||
* @param view
|
* @param view
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ScrollEvent(int nx, int ny, int x, int y, View view)
|
public ScrollEvent(int nx, int ny, int x, int y, View view)
|
||||||
{
|
{
|
||||||
l = x; y = t; nl = nx; nt = ny;
|
l = x; y = t; nl = nx; nt = ny;
|
||||||
targetView = view;
|
targetView = view;
|
||||||
|
|||||||
@@ -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(4);
|
String host = m.group(3);
|
||||||
// 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(8);
|
String port = m.group(7);
|
||||||
String path = m.group(9);
|
String path = m.group(8);
|
||||||
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));
|
||||||
|
|||||||
28
package.json
28
package.json
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "cordova-android",
|
|
||||||
"version": "3.6.4",
|
|
||||||
"description": "cordova-android release",
|
|
||||||
"main": "bin/create",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://git-wip-us.apache.org/repos/asf/cordova-android.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"android",
|
|
||||||
"cordova",
|
|
||||||
"apache"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"test": "jasmine-node --color spec"
|
|
||||||
},
|
|
||||||
"author": "Apache Software Foundation",
|
|
||||||
"license": "Apache version 2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"q": "^0.9.0",
|
|
||||||
"shelljs": "^0.2.6"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"jasmine-node": "~1",
|
|
||||||
"promise-matchers": "~0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +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.
|
|
||||||
*/
|
|
||||||
/* 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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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="10" android:targetSdkVersion="19"/>
|
<uses-sdk android:minSdkVersion="8" 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.MainTestActivity" >
|
android:name="org.apache.cordova.test.CordovaActivity" >
|
||||||
<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,15 +255,5 @@
|
|||||||
<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>
|
||||||
|
|||||||
@@ -26,10 +26,8 @@ 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
|
project.properties has a library reference to ../framework. However, Robotium has to be
|
||||||
has to be installed for the onScrollChanged tests to work correctly. It can be
|
installed for the onScrollChanged tests to work correctly. It can be found at https://code.google.com/p/robotium/
|
||||||
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:
|
||||||
|
|
||||||
|
|||||||
23
test/assets/www/cordova_plugins.js
vendored
23
test/assets/www/cordova_plugins.js
vendored
@@ -1,22 +1,3 @@
|
|||||||
/*
|
|
||||||
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,25 +14,6 @@
|
|||||||
"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.$
|
||||||
-->
|
-->
|
||||||
<!DOCTYPE HTML>
|
This is an error page.
|
||||||
<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 Native Tests</h1>
|
<h1>Cordova Android 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 options menu items should be:</h4>
|
<h4>The 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">CordovaNativeTests</string>
|
<string name="app_name">CordovaTests</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,22 +1,4 @@
|
|||||||
<?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>
|
||||||
@@ -26,19 +8,17 @@
|
|||||||
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>
|
||||||
|
|||||||
@@ -18,11 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova.test;
|
package org.apache.cordova.test;
|
||||||
|
|
||||||
import org.apache.cordova.CordovaActivity;
|
import org.apache.cordova.DroidGap;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
public class MainTestActivity extends CordovaActivity {
|
public class CordovaActivity extends DroidGap {
|
||||||
/** 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) {
|
||||||
@@ -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.CordovaWebViewClient;
|
import org.apache.cordova.LOG;
|
||||||
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,12 +47,7 @@ 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");
|
||||||
|
|
||||||
@@ -105,4 +100,4 @@ public class CordovaWebViewTestActivity extends Activity implements CordovaInter
|
|||||||
cordovaWebView.handleDestroy();
|
cordovaWebView.handleDestroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,92 +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;
|
|
||||||
|
|
||||||
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 CordovaActivity {
|
public class backbuttonmultipage extends DroidGap {
|
||||||
@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 CordovaActivity {
|
public class background extends DroidGap {
|
||||||
@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));
|
||||||
preferences.set("keepRunning", false);
|
super.setBooleanProperty("keepRunning", false);
|
||||||
super.loadUrl("file:///android_asset/www/background/index.html");
|
super.loadUrl("file:///android_asset/www/background/index.html");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,15 +22,18 @@ import android.graphics.Color;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
public class backgroundcolor extends CordovaActivity {
|
public class backgroundcolor extends DroidGap {
|
||||||
@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" />
|
||||||
preferences.set("backgroundColor", Color.GREEN);
|
super.setIntegerProperty("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 CordovaActivity {
|
public class basicauth extends DroidGap {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -31,13 +31,11 @@ public class basicauth extends CordovaActivity {
|
|||||||
AuthenticationToken token = new AuthenticationToken();
|
AuthenticationToken token = new AuthenticationToken();
|
||||||
token.setUserName("test");
|
token.setUserName("test");
|
||||||
token.setPassword("test");
|
token.setPassword("test");
|
||||||
// classic webview includes port in hostname, Chromium webview does not. Handle both here.
|
super.setAuthenticationToken(token, "browserspy.dk:80", "BrowserSpy.dk - HTTP Password Test");
|
||||||
// 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.getWhitelist().addWhiteListEntry("http://browserspy.dk/*", true);
|
Config.init();
|
||||||
|
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,11 +21,12 @@ 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 CordovaActivity {
|
public class errorurl extends DroidGap {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
preferences.set("errorUrl", "file:///android_asset/www/htmlnotfound/error.html");
|
super.init();
|
||||||
|
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 CordovaActivity {
|
public class fullscreen extends DroidGap {
|
||||||
@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 CordovaActivity {
|
|||||||
|
|
||||||
// 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" />
|
||||||
preferences.set("fullscreen", true);
|
super.setBooleanProperty("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 CordovaActivity {
|
public class htmlnotfound extends DroidGap {
|
||||||
@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 CordovaActivity {
|
public class iframe extends DroidGap {
|
||||||
@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();
|
||||||
assertEquals("file:///android_asset/www/backbuttonmultipage/sample2.html", url);
|
assertTrue(url.endsWith("sample2.html"));
|
||||||
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();
|
||||||
assertEquals("file:///android_asset/www/backbuttonmultipage/sample3.html", url);
|
assertTrue(url.endsWith("sample3.html"));
|
||||||
assertTrue(testView.backHistory());
|
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();
|
||||||
assertEquals("file:///android_asset/www/backbuttonmultipage/sample2.html", url);
|
assertTrue(url.endsWith("sample2.html"));
|
||||||
assertTrue(testView.backHistory());
|
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();
|
||||||
assertEquals("file:///android_asset/www/backbuttonmultipage/index.html", url);
|
assertTrue(url.endsWith("index.html"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.MainTestActivity;
|
import org.apache.cordova.test.CordovaActivity;
|
||||||
|
|
||||||
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<MainTestActivity> {
|
public class CordovaActivityTest extends ActivityInstrumentationTestCase2<CordovaActivity> {
|
||||||
|
|
||||||
private MainTestActivity testActivity;
|
private CordovaActivity 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<MainTe
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public CordovaActivityTest()
|
public CordovaActivityTest()
|
||||||
{
|
{
|
||||||
super("org.apache.cordova.test",MainTestActivity.class);
|
super("org.apache.cordova.test",CordovaActivity.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
@@ -68,7 +68,33 @@ public class CordovaActivityTest extends ActivityInstrumentationTestCase2<MainTe
|
|||||||
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);
|
||||||
assertEquals(good_url, url);
|
assertTrue(url.equals(good_url));
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
68
test/src/org/apache/cordova/test/junit/GapClientTest.java
Normal file
68
test/src/org/apache/cordova/test/junit/GapClientTest.java
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,94 +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.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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
73
test/src/org/apache/cordova/test/junit/MessageTest.java
Normal file
73
test/src/org/apache/cordova/test/junit/MessageTest.java
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
test/src/org/apache/cordova/test/junit/UserWebViewTest.java
Normal file
76
test/src/org/apache/cordova/test/junit/UserWebViewTest.java
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
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 CordovaActivity {
|
public class lifecycle extends DroidGap {
|
||||||
@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 CordovaActivity {
|
public class loading extends DroidGap {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
preferences.set("loadingDialog", "Testing,Loading...");
|
super.setStringProperty("loadingDialog", "Testing,Loading...");
|
||||||
super.loadUrl("http://www.google.com");
|
super.loadUrl("http://www.google.com");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,11 +28,10 @@ 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 CordovaActivity {
|
public class menus extends DroidGap {
|
||||||
@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,15 +20,17 @@ 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 CordovaActivity {
|
public class splashscreen extends DroidGap {
|
||||||
@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
|
||||||
preferences.set("splashscreen", "sandy");
|
this.setIntegerProperty("splashscreen", R.drawable.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 CordovaActivity {
|
public class tests extends DroidGap {
|
||||||
@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 CordovaActivity {
|
public class timeout extends DroidGap {
|
||||||
@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
|
||||||
preferences.set("loadUrlTimeoutValue", 10);
|
this.setIntegerProperty("loadUrlTimeoutValue", 10);
|
||||||
super.loadUrl("http://www.google.com");
|
super.loadUrl("http://www.google.com");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ 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 MainTestActivity {
|
public class userwebview extends DroidGap {
|
||||||
|
|
||||||
public TestViewClient testViewClient;
|
public TestViewClient testViewClient;
|
||||||
public TestChromeClient testChromeClient;
|
public TestChromeClient testChromeClient;
|
||||||
@@ -32,17 +33,15 @@ public class userwebview extends MainTestActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
testViewClient = new TestViewClient(this, appView);
|
testViewClient = new TestViewClient(this);
|
||||||
testChromeClient = new TestChromeClient(this, appView);
|
testChromeClient = new TestChromeClient(this);
|
||||||
super.init();
|
super.init(new CordovaWebView(this), new TestViewClient(this), new TestChromeClient(this));
|
||||||
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(CordovaInterface ctx, CordovaWebView app) {
|
public TestChromeClient(DroidGap arg0) {
|
||||||
super(ctx, app);
|
super(arg0);
|
||||||
LOG.d("userwebview", "TestChromeClient()");
|
LOG.d("userwebview", "TestChromeClient()");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,8 +57,8 @@ public class userwebview extends MainTestActivity {
|
|||||||
* 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(CordovaInterface ctx, CordovaWebView app) {
|
public TestViewClient(DroidGap arg0) {
|
||||||
super(ctx, app);
|
super(arg0);
|
||||||
LOG.d("userwebview", "TestViewClient()");
|
LOG.d("userwebview", "TestViewClient()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,11 @@ 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 MainTestActivity {
|
public class whitelist extends DroidGap {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
super.init();
|
super.init(new CordovaWebView(this), new TestViewClient(this), new CordovaChromeClient(this));
|
||||||
appView.setWebViewClient(new TestViewClient(this, appView));
|
|
||||||
super.loadUrl("file:///android_asset/www/whitelist/index.html");
|
super.loadUrl("file:///android_asset/www/whitelist/index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,8 +37,8 @@ public class whitelist extends MainTestActivity {
|
|||||||
*/
|
*/
|
||||||
public class TestViewClient extends CordovaWebViewClient {
|
public class TestViewClient extends CordovaWebViewClient {
|
||||||
|
|
||||||
public TestViewClient(CordovaInterface ctx, CordovaWebView app) {
|
public TestViewClient(DroidGap arg0) {
|
||||||
super(ctx, app);
|
super(arg0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user