Compare commits
76 Commits
3.4.0
...
pluggable_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22b1959333 | ||
|
|
97008305ff | ||
|
|
1a17083e8c | ||
|
|
b6664cc859 | ||
|
|
50c4aef873 | ||
|
|
cf42d31214 | ||
|
|
00caa1c0a0 | ||
|
|
93c8ba920a | ||
|
|
8702c04d39 | ||
|
|
e595c313a1 | ||
|
|
955da2e360 | ||
|
|
04b3fc0268 | ||
|
|
105ccc81a5 | ||
|
|
c349892c5b | ||
|
|
3d4b8ce99b | ||
|
|
3571307df5 | ||
|
|
64fd87134f | ||
|
|
82bba44538 | ||
|
|
df05f3a3c0 | ||
|
|
8e31ef7be6 | ||
|
|
f4555f7c96 | ||
|
|
8408da55ea | ||
|
|
4a67dd2e28 | ||
|
|
bd806a34d8 | ||
|
|
2f7e833a79 | ||
|
|
c17503ab78 | ||
|
|
19f76d34db | ||
|
|
25c8b2fabb | ||
|
|
bfd8bf9ca4 | ||
|
|
e6adbb0e42 | ||
|
|
11fc6be328 | ||
|
|
7a5405d2ab | ||
|
|
b9a24f00ad | ||
|
|
dbfc292353 | ||
|
|
a09255b2ff | ||
|
|
9d1c72cc07 | ||
|
|
09ac30ef2e | ||
|
|
0ec8f5d283 | ||
|
|
b872df0f31 | ||
|
|
0add4af208 | ||
|
|
298cd9e065 | ||
|
|
b715d20385 | ||
|
|
79e313a0c0 | ||
|
|
9f4c75d1c2 | ||
|
|
b37492644c | ||
|
|
04a792a8c2 | ||
|
|
35ec24c3f0 | ||
|
|
9a00ccdacc | ||
|
|
61b23677d1 | ||
|
|
90037dc6cd | ||
|
|
e8d48e1f43 | ||
|
|
a2f8c9c75b | ||
|
|
5fb83e7f52 | ||
|
|
dd6bf568d1 | ||
|
|
00ee164cef | ||
|
|
448071b02d | ||
|
|
4dad9d0e37 | ||
|
|
3a2117c5d3 | ||
|
|
26a3f6ddc3 | ||
|
|
7741312673 | ||
|
|
954a1723f1 | ||
|
|
87285d94f7 | ||
|
|
d260d0c182 | ||
|
|
137eb40fab | ||
|
|
af440460e1 | ||
|
|
a5c8472a37 | ||
|
|
dfae37421d | ||
|
|
438a8d8b75 | ||
|
|
ac2034561d | ||
|
|
c42cd4233d | ||
|
|
5b2a73e3eb | ||
|
|
6f163a6ba5 | ||
|
|
cc94cc7d01 | ||
|
|
94934ae2cf | ||
|
|
e361f88501 | ||
|
|
708c042b61 |
16
CONTRIBUTING.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# 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!
|
||||
8
NOTICE
@@ -1,5 +1,5 @@
|
||||
Apache Cordova
|
||||
Copyright 2012 The Apache Software Foundation
|
||||
Copyright 2014 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org)
|
||||
@@ -10,8 +10,8 @@ The Apache Software Foundation (http://www.apache.org)
|
||||
== in this case for the Android-specific code. ==
|
||||
=========================================================================
|
||||
|
||||
Android Code
|
||||
Copyright 2005-2008 The Android Open Source Project
|
||||
|
||||
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.
|
||||
|
||||
@@ -75,7 +75,7 @@ you are developing directly against the framework.
|
||||
|
||||
To create your `cordova.jar` file, run in the framework directory:
|
||||
|
||||
android update project -p . -t android-18
|
||||
android update project -p . -t android-19
|
||||
ant jar
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ Running Tests
|
||||
Please see details under test/README.md.
|
||||
|
||||
Further Reading
|
||||
---
|
||||
----
|
||||
|
||||
- [http://developer.android.com](http://developer.android.com)
|
||||
- [http://cordova.apache.org/](http://cordova.apache.org)
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
# 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
|
||||
@@ -20,6 +20,25 @@
|
||||
-->
|
||||
## Release Notes for Cordova (Android) ##
|
||||
|
||||
### 3.4.0 (Feb 2014) ###
|
||||
|
||||
43 commits from 10 authors. Highlights include:
|
||||
|
||||
* Removing addJavascriptInterface support from all Android versions lower than 4.2 due to security vulnerability
|
||||
* CB-5917 Add a loadUrlIntoView overload that doesn't recreate plugins.
|
||||
* 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.
|
||||
* CB-5793 Don't clean before build and change output directory to ant-build to avoid conflicts with Eclipse.
|
||||
* CB-5803 Fix cordova/emulate on windows.
|
||||
* CB-5801 exec->spawn in build to make sure compile errors are shown.
|
||||
* CB-5799 Update version of OkHTTP to 1.3
|
||||
* CB-4910 Update CLI project template to point to config.xml at the root now that it's not in www/ by default.
|
||||
* CB-5504 Adding onDestroy to app plugin to deregister telephonyReceiver
|
||||
* CB-5715 Add Eclipse .project file to create template. For CLI projects, it adds refs for root www/ & config.xml and hides platform versions
|
||||
* CB-5447 Removed android:debuggable=“true” from project template.
|
||||
* CB-5714 Fix of android build when too big output stops build with error due to buffer overflow.
|
||||
* CB-5592 Set MIME type for openExternal when scheme is file:
|
||||
|
||||
### 3.3.0 (Dec 2013) ###
|
||||
|
||||
41 commits from 11 authors. Highlights include:
|
||||
|
||||
@@ -149,6 +149,10 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
||||
return Q.reject('Package name must look like: com.company.Name');
|
||||
}
|
||||
|
||||
if (project_name === 'CordovaActivity') {
|
||||
return Q.reject('Project name cannot be CordovaActivity');
|
||||
}
|
||||
|
||||
// Check that requirements are met and proper targets are installed
|
||||
return check_reqs.run()
|
||||
.then(function() {
|
||||
@@ -165,17 +169,23 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
||||
// copy project template
|
||||
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(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
|
||||
|
||||
// Manually create directories that would be empty within the template (since git doesn't track directories).
|
||||
shell.mkdir(path.join(project_path, 'libs'));
|
||||
|
||||
// Add in the proper eclipse project file.
|
||||
if (use_cli_template) {
|
||||
var note = 'To show `assets/www` or `res/xml/config.xml`, go to:\n' +
|
||||
' Project -> Properties -> Resource -> Resource Filters\n' +
|
||||
'And delete the exclusion filter.\n';
|
||||
shell.cp(path.join(project_template_dir, 'eclipse-project-CLI'), path.join(project_path, '.project'));
|
||||
fs.writeFileSync(path.join(project_path, 'assets', '_where-is-www.txt'), note);
|
||||
} else {
|
||||
shell.cp(path.join(project_template_dir, 'eclipse-project'), path.join(project_path, '.project'));
|
||||
}
|
||||
|
||||
// copy cordova.js, cordova.jar and res/xml
|
||||
shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
|
||||
// copy cordova.js, cordova.jar
|
||||
copyJsAndLibrary(project_path, use_shared_project, safe_activity_name);
|
||||
|
||||
// interpolate the activity name and package
|
||||
@@ -202,24 +212,36 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
||||
|
||||
// Attribute removed in Cordova 4.4 (CB-5447).
|
||||
function removeDebuggableFromManifest(projectPath) {
|
||||
var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
|
||||
var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
|
||||
shell.sed('-i', /\s*android:debuggable="true"/, '', manifestPath);
|
||||
}
|
||||
|
||||
function extractProjectNameFromManifest(projectPath) {
|
||||
var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
|
||||
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||
var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||
if (!m) {
|
||||
throw new Error('Could not find activity name in ' + manifestPath);
|
||||
}
|
||||
return m[1];
|
||||
}
|
||||
|
||||
// Returns a promise.
|
||||
exports.updateProject = function(projectPath) {
|
||||
var version = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
|
||||
// Check that requirements are met and proper targets are installed
|
||||
return check_reqs.run()
|
||||
.then(function() {
|
||||
var projectName = extractProjectNameFromManifest(projectPath);
|
||||
var target_api = check_reqs.get_target();
|
||||
copyJsAndLibrary(projectPath, false, null);
|
||||
copyJsAndLibrary(projectPath, false, projectName);
|
||||
copyScripts(projectPath);
|
||||
copyAntRules(projectPath);
|
||||
removeDebuggableFromManifest(projectPath);
|
||||
return runAndroidUpdate(projectPath, target_api, false)
|
||||
.then(function() {
|
||||
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.');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
23
bin/node_modules/which/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
5
bin/node_modules/which/README.md
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The "which" util from npm's guts.
|
||||
|
||||
Finds the first instance of a specified executable in the PATH
|
||||
environment variable. Does not cache the results, so `hash -r` is not
|
||||
needed when the PATH changes.
|
||||
14
bin/node_modules/which/bin/which
generated
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env node
|
||||
var which = require("../")
|
||||
if (process.argv.length < 3) {
|
||||
console.error("Usage: which <thing>")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
which(process.argv[2], function (er, thing) {
|
||||
if (er) {
|
||||
console.error(er.message)
|
||||
process.exit(er.errno || 127)
|
||||
}
|
||||
console.log(thing)
|
||||
})
|
||||
31
bin/node_modules/which/package.json
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"author": {
|
||||
"name": "Isaac Z. Schlueter",
|
||||
"email": "i@izs.me",
|
||||
"url": "http://blog.izs.me"
|
||||
},
|
||||
"name": "which",
|
||||
"description": "Like which(1) unix command. Find the first instance of an executable in the PATH.",
|
||||
"version": "1.0.5",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/isaacs/node-which.git"
|
||||
},
|
||||
"main": "which.js",
|
||||
"bin": {
|
||||
"which": "./bin/which"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {},
|
||||
"readme": "The \"which\" util from npm's guts.\n\nFinds the first instance of a specified executable in the PATH\nenvironment variable. Does not cache the results, so `hash -r` is not\nneeded when the PATH changes.\n",
|
||||
"readmeFilename": "README.md",
|
||||
"bugs": {
|
||||
"url": "https://github.com/isaacs/node-which/issues"
|
||||
},
|
||||
"homepage": "https://github.com/isaacs/node-which",
|
||||
"_id": "which@1.0.5",
|
||||
"_from": "which@^1.0.5"
|
||||
}
|
||||
104
bin/node_modules/which/which.js
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
module.exports = which
|
||||
which.sync = whichSync
|
||||
|
||||
var path = require("path")
|
||||
, fs
|
||||
, COLON = process.platform === "win32" ? ";" : ":"
|
||||
, isExe
|
||||
|
||||
try {
|
||||
fs = require("graceful-fs")
|
||||
} catch (ex) {
|
||||
fs = require("fs")
|
||||
}
|
||||
|
||||
if (process.platform == "win32") {
|
||||
// On windows, there is no good way to check that a file is executable
|
||||
isExe = function isExe () { return true }
|
||||
} else {
|
||||
isExe = function isExe (mod, uid, gid) {
|
||||
//console.error(mod, uid, gid);
|
||||
//console.error("isExe?", (mod & 0111).toString(8))
|
||||
var ret = (mod & 0001)
|
||||
|| (mod & 0010) && process.getgid && gid === process.getgid()
|
||||
|| (mod & 0100) && process.getuid && uid === process.getuid()
|
||||
//console.error("isExe?", ret)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function which (cmd, cb) {
|
||||
if (isAbsolute(cmd)) return cb(null, cmd)
|
||||
var pathEnv = (process.env.PATH || "").split(COLON)
|
||||
, pathExt = [""]
|
||||
if (process.platform === "win32") {
|
||||
pathEnv.push(process.cwd())
|
||||
pathExt = (process.env.PATHEXT || ".EXE").split(COLON)
|
||||
if (cmd.indexOf(".") !== -1) pathExt.unshift("")
|
||||
}
|
||||
//console.error("pathEnv", pathEnv)
|
||||
;(function F (i, l) {
|
||||
if (i === l) return cb(new Error("not found: "+cmd))
|
||||
var p = path.resolve(pathEnv[i], cmd)
|
||||
;(function E (ii, ll) {
|
||||
if (ii === ll) return F(i + 1, l)
|
||||
var ext = pathExt[ii]
|
||||
//console.error(p + ext)
|
||||
fs.stat(p + ext, function (er, stat) {
|
||||
if (!er &&
|
||||
stat &&
|
||||
stat.isFile() &&
|
||||
isExe(stat.mode, stat.uid, stat.gid)) {
|
||||
//console.error("yes, exe!", p + ext)
|
||||
return cb(null, p + ext)
|
||||
}
|
||||
return E(ii + 1, ll)
|
||||
})
|
||||
})(0, pathExt.length)
|
||||
})(0, pathEnv.length)
|
||||
}
|
||||
|
||||
function whichSync (cmd) {
|
||||
if (isAbsolute(cmd)) return cmd
|
||||
var pathEnv = (process.env.PATH || "").split(COLON)
|
||||
, pathExt = [""]
|
||||
if (process.platform === "win32") {
|
||||
pathEnv.push(process.cwd())
|
||||
pathExt = (process.env.PATHEXT || ".EXE").split(COLON)
|
||||
if (cmd.indexOf(".") !== -1) pathExt.unshift("")
|
||||
}
|
||||
for (var i = 0, l = pathEnv.length; i < l; i ++) {
|
||||
var p = path.join(pathEnv[i], cmd)
|
||||
for (var j = 0, ll = pathExt.length; j < ll; j ++) {
|
||||
var cur = p + pathExt[j]
|
||||
var stat
|
||||
try { stat = fs.statSync(cur) } catch (ex) {}
|
||||
if (stat &&
|
||||
stat.isFile() &&
|
||||
isExe(stat.mode, stat.uid, stat.gid)) return cur
|
||||
}
|
||||
}
|
||||
throw new Error("not found: "+cmd)
|
||||
}
|
||||
|
||||
var isAbsolute = process.platform === "win32" ? absWin : absUnix
|
||||
|
||||
function absWin (p) {
|
||||
if (absUnix(p)) return true
|
||||
// pull off the device/UNC bit from a windows path.
|
||||
// from node's lib/path.js
|
||||
var splitDeviceRe =
|
||||
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?/
|
||||
, result = splitDeviceRe.exec(p)
|
||||
, device = result[1] || ''
|
||||
, isUnc = device && device.charAt(1) !== ':'
|
||||
, isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
|
||||
|
||||
return isAbsolute
|
||||
}
|
||||
|
||||
function absUnix (p) {
|
||||
return p.charAt(0) === "/" || p === ""
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
<!-- Preferences for Android -->
|
||||
<preference name="loglevel" value="DEBUG" />
|
||||
<preference name="AndroidLaunchMode" value="singleTop" />
|
||||
|
||||
<!-- This is required for native Android hooks -->
|
||||
<feature name="App">
|
||||
|
||||
18
bin/templates/cordova/lib/build.js
vendored
@@ -24,6 +24,7 @@ var shell = require('shelljs'),
|
||||
Q = require('q'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
which = require('which'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
|
||||
@@ -32,6 +33,12 @@ function hasCustomRules() {
|
||||
}
|
||||
module.exports.getAntArgs = function(cmd) {
|
||||
var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
|
||||
try {
|
||||
// Specify sdk dir in case local properties are missing
|
||||
args.push('-Dsdk.dir='+path.join(which.sync('android'), '../..'));
|
||||
} catch(e) {
|
||||
// Can't find android; don't push arg: assume all is okay
|
||||
}
|
||||
// custom_rules.xml is required for incremental builds.
|
||||
if (hasCustomRules()) {
|
||||
args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
|
||||
@@ -74,7 +81,12 @@ module.exports.run = function(build_type) {
|
||||
* the script will error out. (should we error or just return undefined?)
|
||||
*/
|
||||
module.exports.get_apk = function() {
|
||||
var binDir = path.join(ROOT, 'ant-build');
|
||||
var binDir = '';
|
||||
if(!hasCustomRules()) {
|
||||
binDir = path.join(ROOT, 'bin');
|
||||
} else {
|
||||
binDir = path.join(ROOT, 'ant-build');
|
||||
}
|
||||
if (fs.existsSync(binDir)) {
|
||||
var candidates = fs.readdirSync(binDir).filter(function(p) {
|
||||
// Need to choose between release and debug .apk.
|
||||
@@ -87,13 +99,13 @@ module.exports.get_apk = function() {
|
||||
a.t < b.t ? 1 : 0;
|
||||
});
|
||||
if (candidates.length === 0) {
|
||||
console.error('ERROR : No .apk found in \'ant-build\' directory');
|
||||
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 ant-build directory, could not locate .apk');
|
||||
console.error('ERROR : unable to find project ' + binDir + ' directory, could not locate .apk');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,6 @@
|
||||
*/
|
||||
|
||||
// Coho updates this line:
|
||||
var VERSION = "3.4.0-dev";
|
||||
var VERSION = "3.6.0-dev";
|
||||
|
||||
console.log(VERSION);
|
||||
|
||||
@@ -29,10 +29,12 @@
|
||||
/>
|
||||
|
||||
<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"
|
||||
android:hardwareAccelerated="true">
|
||||
<activity android:name="__ACTIVITY__" android:label="@string/app_name"
|
||||
<activity android:name="__ACTIVITY__" android:label="@string/app_name" android:launchMode="singleTop"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
|
||||
<intent-filter>
|
||||
|
||||
@@ -13,5 +13,9 @@
|
||||
</path>
|
||||
<echo message="Set jars path to: ${toString:project.all.jars.path}"/>
|
||||
</target>
|
||||
<target name="-post-build">
|
||||
<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" />
|
||||
</target>
|
||||
</project>
|
||||
|
||||
|
||||
@@ -32,36 +32,40 @@
|
||||
</natures>
|
||||
<linkedResources>
|
||||
<link>
|
||||
<name>-- Cordova Project --</name>
|
||||
<type>2</type>
|
||||
<locationURI>virtual:/virtual</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>-- Cordova Project --/config.xml</name>
|
||||
<name>config.xml</name>
|
||||
<type>1</type>
|
||||
<locationURI>$%7BPARENT-2-PROJECT_LOC%7D/config.xml</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>-- Cordova Project --/www</name>
|
||||
<name>www</name>
|
||||
<type>2</type>
|
||||
<locationURI>$%7BPARENT-2-PROJECT_LOC%7D/www</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>-- Cordova Project --/merges</name>
|
||||
<name>merges</name>
|
||||
<type>2</type>
|
||||
<locationURI>$%7BPARENT-2-PROJECT_LOC%7D/merges</locationURI>
|
||||
</link>
|
||||
</linkedResources>
|
||||
<filteredResources>
|
||||
<filter>
|
||||
<id>1388696068187</id>
|
||||
<name></name>
|
||||
<type>10</type>
|
||||
<matcher>
|
||||
<id>org.eclipse.ui.ide.multiFilter</id>
|
||||
<arguments>1.0-name-matches-false-true-CordovaLib|platform_www|cordova</arguments>
|
||||
</matcher>
|
||||
</filter>
|
||||
</filteredResources>
|
||||
<filteredResources>
|
||||
<filter>
|
||||
<id>1390880034107</id>
|
||||
<name></name>
|
||||
<type>30</type>
|
||||
<matcher>
|
||||
<id>org.eclipse.ui.ide.multiFilter</id>
|
||||
<arguments>1.0-projectRelativePath-matches-false-true-^(build.xml|ant-gen|ant-build|custom_rules.xml|CordovaLib|platform_www|cordova)</arguments>
|
||||
</matcher>
|
||||
</filter>
|
||||
<filter>
|
||||
<id>1390880034108</id>
|
||||
<name></name>
|
||||
<type>30</type>
|
||||
<matcher>
|
||||
<id>org.eclipse.ui.ide.multiFilter</id>
|
||||
<arguments>1.0-projectRelativePath-matches-false-true-^(assets/www|res/xml/config.xml)</arguments>
|
||||
</matcher>
|
||||
</filter>
|
||||
</filteredResources>
|
||||
</projectDescription>
|
||||
|
||||
|
||||
BIN
bin/templates/project/res/drawable-land-hdpi/screen.png
Normal file
|
After Width: | Height: | Size: 213 KiB |
BIN
bin/templates/project/res/drawable-land-ldpi/screen.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
bin/templates/project/res/drawable-land-mdpi/screen.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
bin/templates/project/res/drawable-land-xhdpi/screen.png
Normal file
|
After Width: | Height: | Size: 478 KiB |
BIN
bin/templates/project/res/drawable-port-hdpi/screen.png
Normal file
|
After Width: | Height: | Size: 217 KiB |
BIN
bin/templates/project/res/drawable-port-ldpi/screen.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
bin/templates/project/res/drawable-port-mdpi/screen.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
bin/templates/project/res/drawable-port-xhdpi/screen.png
Normal file
|
After Width: | Height: | Size: 493 KiB |
@@ -1,8 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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.LIBRARIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<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="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
|
||||
4
framework/.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,4 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
||||
322
framework/assets/www/cordova.js
vendored
@@ -1,5 +1,5 @@
|
||||
// Platform: android
|
||||
// 3.3.0-dev-c9de1bc
|
||||
// 3.6.0-dev-7e845f3
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@@ -19,8 +19,8 @@
|
||||
under the License.
|
||||
*/
|
||||
;(function() {
|
||||
var CORDOVA_JS_BUILD_LABEL = '3.3.0-dev-c9de1bc';
|
||||
// file: lib/scripts/require.js
|
||||
var CORDOVA_JS_BUILD_LABEL = '3.6.0-dev-7e845f3';
|
||||
// file: src/scripts/require.js
|
||||
|
||||
/*jshint -W079 */
|
||||
/*jshint -W020 */
|
||||
@@ -34,7 +34,7 @@ var require,
|
||||
requireStack = [],
|
||||
// Map of module ID -> index into requireStack of modules currently being built.
|
||||
inProgressModules = {},
|
||||
SEPERATOR = ".";
|
||||
SEPARATOR = ".";
|
||||
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ var require,
|
||||
var resultantId = id;
|
||||
//Its a relative path, so lop off the last portion and add the id (minus "./")
|
||||
if (id.charAt(0) === ".") {
|
||||
resultantId = module.id.slice(0, module.id.lastIndexOf(SEPERATOR)) + SEPERATOR + id.slice(2);
|
||||
resultantId = module.id.slice(0, module.id.lastIndexOf(SEPARATOR)) + SEPARATOR + id.slice(2);
|
||||
}
|
||||
return require(resultantId);
|
||||
};
|
||||
@@ -98,7 +98,7 @@ if (typeof module === "object" && typeof require === "function") {
|
||||
module.exports.define = define;
|
||||
}
|
||||
|
||||
// file: lib/cordova.js
|
||||
// file: src/cordova.js
|
||||
define("cordova", function(require, exports, module) {
|
||||
|
||||
|
||||
@@ -316,7 +316,7 @@ module.exports = cordova;
|
||||
|
||||
});
|
||||
|
||||
// file: lib/android/android/nativeapiprovider.js
|
||||
// file: src/android/android/nativeapiprovider.js
|
||||
define("cordova/android/nativeapiprovider", function(require, exports, module) {
|
||||
|
||||
/**
|
||||
@@ -339,7 +339,7 @@ module.exports = {
|
||||
|
||||
});
|
||||
|
||||
// file: lib/android/android/promptbasednativeapi.js
|
||||
// file: src/android/android/promptbasednativeapi.js
|
||||
define("cordova/android/promptbasednativeapi", function(require, exports, module) {
|
||||
|
||||
/**
|
||||
@@ -361,7 +361,7 @@ module.exports = {
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/argscheck.js
|
||||
// file: src/common/argscheck.js
|
||||
define("cordova/argscheck", function(require, exports, module) {
|
||||
|
||||
var exec = require('cordova/exec');
|
||||
@@ -427,7 +427,7 @@ moduleExports.enableChecks = true;
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/base64.js
|
||||
// file: src/common/base64.js
|
||||
define("cordova/base64", function(require, exports, module) {
|
||||
|
||||
var base64 = exports;
|
||||
@@ -437,6 +437,16 @@ base64.fromArrayBuffer = function(arrayBuffer) {
|
||||
return uint8ToBase64(array);
|
||||
};
|
||||
|
||||
base64.toArrayBuffer = function(str) {
|
||||
var decodedStr = typeof atob != 'undefined' ? atob(str) : new Buffer(str,'base64').toString('binary');
|
||||
var arrayBuffer = new ArrayBuffer(decodedStr.length);
|
||||
var array = new Uint8Array(arrayBuffer);
|
||||
for (var i=0, len=decodedStr.length; i < len; i++) {
|
||||
array[i] = decodedStr.charCodeAt(i);
|
||||
}
|
||||
return arrayBuffer;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* This code is based on the performance tests at http://jsperf.com/b64tests
|
||||
@@ -483,7 +493,7 @@ function uint8ToBase64(rawData) {
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/builder.js
|
||||
// file: src/common/builder.js
|
||||
define("cordova/builder", function(require, exports, module) {
|
||||
|
||||
var utils = require('cordova/utils');
|
||||
@@ -552,7 +562,7 @@ function include(parent, objects, clobber, merge) {
|
||||
include(result, obj.children, clobber, merge);
|
||||
}
|
||||
} catch(e) {
|
||||
utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"');
|
||||
utils.alert('Exception building Cordova JS globals: ' + e + ' for key "' + key + '"');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -596,7 +606,7 @@ exports.replaceHookForTesting = function() {};
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/channel.js
|
||||
// file: src/common/channel.js
|
||||
define("cordova/channel", function(require, exports, module) {
|
||||
|
||||
var utils = require('cordova/utils'),
|
||||
@@ -815,6 +825,7 @@ channel.createSticky('onNativeReady');
|
||||
channel.createSticky('onCordovaReady');
|
||||
|
||||
// Event to indicate that all automatically loaded JS plugins are loaded and ready.
|
||||
// FIXME remove this
|
||||
channel.createSticky('onPluginsReady');
|
||||
|
||||
// Event to indicate that Cordova is ready
|
||||
@@ -837,7 +848,7 @@ module.exports = channel;
|
||||
|
||||
});
|
||||
|
||||
// file: lib/android/exec.js
|
||||
// file: src/android/exec.js
|
||||
define("cordova/exec", function(require, exports, module) {
|
||||
|
||||
/**
|
||||
@@ -919,7 +930,7 @@ function androidExec(success, fail, service, action, args) {
|
||||
androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
|
||||
return;
|
||||
} else {
|
||||
androidExec.processMessages(messages);
|
||||
androidExec.processMessages(messages, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -963,7 +974,6 @@ androidExec.nativeToJsModes = nativeToJsModes;
|
||||
|
||||
androidExec.setJsToNativeBridgeMode = function(mode) {
|
||||
if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) {
|
||||
console.log('Falling back on PROMPT mode since _cordovaNative is missing. Expected for Android 3.2 and lower only.');
|
||||
mode = jsToNativeModes.PROMPT;
|
||||
}
|
||||
nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT);
|
||||
@@ -1028,53 +1038,69 @@ function processMessage(message) {
|
||||
}
|
||||
cordova.callbackFromNative(callbackId, success, status, [payload], keepCallback);
|
||||
} else {
|
||||
console.log("processMessage failed: invalid message:" + message);
|
||||
console.log("processMessage failed: invalid message: " + JSON.stringify(message));
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("processMessage failed: Message: " + message);
|
||||
console.log("processMessage failed: Error: " + e);
|
||||
console.log("processMessage failed: Stack: " + e.stack);
|
||||
console.log("processMessage failed: Message: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
var isProcessing = false;
|
||||
|
||||
// This is called from the NativeToJsMessageQueue.java.
|
||||
androidExec.processMessages = function(messages) {
|
||||
androidExec.processMessages = function(messages, opt_useTimeout) {
|
||||
if (messages) {
|
||||
messagesFromNative.push(messages);
|
||||
// Check for the reentrant case, and enqueue the message if that's the case.
|
||||
if (messagesFromNative.length > 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check for the reentrant case.
|
||||
if (isProcessing) {
|
||||
return;
|
||||
}
|
||||
if (opt_useTimeout) {
|
||||
window.setTimeout(androidExec.processMessages, 0);
|
||||
return;
|
||||
}
|
||||
isProcessing = true;
|
||||
try {
|
||||
// TODO: add setImmediate polyfill and process only one message at a time.
|
||||
while (messagesFromNative.length) {
|
||||
// Don't unshift until the end so that reentrancy can be detected.
|
||||
messages = messagesFromNative[0];
|
||||
var msg = popMessageFromQueue();
|
||||
// The Java side can send a * message to indicate that it
|
||||
// still has messages waiting to be retrieved.
|
||||
if (messages == '*') {
|
||||
messagesFromNative.shift();
|
||||
window.setTimeout(pollOnce, 0);
|
||||
if (msg == '*' && messagesFromNative.length === 0) {
|
||||
setTimeout(pollOnce, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
var spaceIdx = messages.indexOf(' ');
|
||||
var msgLen = +messages.slice(0, spaceIdx);
|
||||
var message = messages.substr(spaceIdx + 1, msgLen);
|
||||
messages = messages.slice(spaceIdx + msgLen + 1);
|
||||
processMessage(message);
|
||||
if (messages) {
|
||||
messagesFromNative[0] = messages;
|
||||
} else {
|
||||
messagesFromNative.shift();
|
||||
}
|
||||
processMessage(msg);
|
||||
}
|
||||
} finally {
|
||||
isProcessing = false;
|
||||
}
|
||||
};
|
||||
|
||||
function popMessageFromQueue() {
|
||||
var messageBatch = messagesFromNative.shift();
|
||||
if (messageBatch == '*') {
|
||||
return '*';
|
||||
}
|
||||
|
||||
var spaceIdx = messageBatch.indexOf(' ');
|
||||
var msgLen = +messageBatch.slice(0, spaceIdx);
|
||||
var message = messageBatch.substr(spaceIdx + 1, msgLen);
|
||||
messageBatch = messageBatch.slice(spaceIdx + msgLen + 1);
|
||||
if (messageBatch) {
|
||||
messagesFromNative.unshift(messageBatch);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
module.exports = androidExec;
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/exec/proxy.js
|
||||
// file: src/common/exec/proxy.js
|
||||
define("cordova/exec/proxy", function(require, exports, module) {
|
||||
|
||||
|
||||
@@ -1104,7 +1130,7 @@ module.exports = {
|
||||
};
|
||||
});
|
||||
|
||||
// file: lib/common/init.js
|
||||
// file: src/common/init.js
|
||||
define("cordova/init", function(require, exports, module) {
|
||||
|
||||
var channel = require('cordova/channel');
|
||||
@@ -1191,9 +1217,13 @@ modulemapper.clobbers('cordova/exec', 'Cordova.exec');
|
||||
// Call the platform-specific initialization.
|
||||
platform.bootstrap && platform.bootstrap();
|
||||
|
||||
pluginloader.load(function() {
|
||||
channel.onPluginsReady.fire();
|
||||
});
|
||||
// Wrap in a setTimeout to support the use-case of having plugin JS appended to cordova.js.
|
||||
// The delay allows the attached modules to be defined before the plugin loader looks for them.
|
||||
setTimeout(function() {
|
||||
pluginloader.load(function() {
|
||||
channel.onPluginsReady.fire();
|
||||
});
|
||||
}, 0);
|
||||
|
||||
/**
|
||||
* Create all cordova objects once native side is ready.
|
||||
@@ -1218,7 +1248,112 @@ channel.join(function() {
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/modulemapper.js
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
define("cordova/modulemapper", function(require, exports, module) {
|
||||
|
||||
var builder = require('cordova/builder'),
|
||||
@@ -1319,7 +1454,7 @@ exports.reset();
|
||||
|
||||
});
|
||||
|
||||
// file: lib/android/platform.js
|
||||
// file: src/android/platform.js
|
||||
define("cordova/platform", function(require, exports, module) {
|
||||
|
||||
module.exports = {
|
||||
@@ -1360,7 +1495,7 @@ module.exports = {
|
||||
|
||||
});
|
||||
|
||||
// file: lib/android/plugin/android/app.js
|
||||
// file: src/android/plugin/android/app.js
|
||||
define("cordova/plugin/android/app", function(require, exports, module) {
|
||||
|
||||
var exec = require('cordova/exec');
|
||||
@@ -1437,49 +1572,58 @@ module.exports = {
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/pluginloader.js
|
||||
// file: src/common/pluginloader.js
|
||||
define("cordova/pluginloader", function(require, exports, module) {
|
||||
|
||||
var modulemapper = require('cordova/modulemapper');
|
||||
var urlutil = require('cordova/urlutil');
|
||||
|
||||
// Helper function to inject a <script> tag.
|
||||
function injectScript(url, onload, onerror) {
|
||||
// Exported for testing.
|
||||
exports.injectScript = function(url, onload, onerror) {
|
||||
var script = document.createElement("script");
|
||||
// onload fires even when script fails loads with an error.
|
||||
script.onload = onload;
|
||||
script.onerror = onerror || onload;
|
||||
// onerror fires for malformed URLs.
|
||||
script.onerror = onerror;
|
||||
script.src = url;
|
||||
document.head.appendChild(script);
|
||||
};
|
||||
|
||||
function injectIfNecessary(id, url, onload, onerror) {
|
||||
onerror = onerror || onload;
|
||||
if (id in define.moduleMap) {
|
||||
onload();
|
||||
} else {
|
||||
exports.injectScript(url, function() {
|
||||
if (id in define.moduleMap) {
|
||||
onload();
|
||||
} else {
|
||||
onerror();
|
||||
}
|
||||
}, onerror);
|
||||
}
|
||||
}
|
||||
|
||||
function onScriptLoadingComplete(moduleList, finishPluginLoading) {
|
||||
// Loop through all the plugins and then through their clobbers and merges.
|
||||
for (var i = 0, module; module = moduleList[i]; i++) {
|
||||
if (module) {
|
||||
try {
|
||||
if (module.clobbers && module.clobbers.length) {
|
||||
for (var j = 0; j < module.clobbers.length; j++) {
|
||||
modulemapper.clobbers(module.id, module.clobbers[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (module.merges && module.merges.length) {
|
||||
for (var k = 0; k < module.merges.length; k++) {
|
||||
modulemapper.merges(module.id, module.merges[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, if runs is truthy we want to simply require() the module.
|
||||
// This can be skipped if it had any merges or clobbers, though,
|
||||
// since the mapper will already have required the module.
|
||||
if (module.runs && !(module.clobbers && module.clobbers.length) && !(module.merges && module.merges.length)) {
|
||||
modulemapper.runs(module.id);
|
||||
}
|
||||
if (module.clobbers && module.clobbers.length) {
|
||||
for (var j = 0; j < module.clobbers.length; j++) {
|
||||
modulemapper.clobbers(module.id, module.clobbers[j]);
|
||||
}
|
||||
catch(err) {
|
||||
// error with module, most likely clobbers, should we continue?
|
||||
}
|
||||
|
||||
if (module.merges && module.merges.length) {
|
||||
for (var k = 0; k < module.merges.length; k++) {
|
||||
modulemapper.merges(module.id, module.merges[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, if runs is truthy we want to simply require() the module.
|
||||
if (module.runs) {
|
||||
modulemapper.runs(module.id);
|
||||
}
|
||||
}
|
||||
|
||||
finishPluginLoading();
|
||||
@@ -1504,29 +1648,16 @@ function handlePluginsObject(path, moduleList, finishPluginLoading) {
|
||||
}
|
||||
|
||||
for (var i = 0; i < moduleList.length; i++) {
|
||||
injectScript(path + moduleList[i].file, scriptLoadedCallback);
|
||||
injectIfNecessary(moduleList[i].id, path + moduleList[i].file, scriptLoadedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
function injectPluginScript(pathPrefix, finishPluginLoading) {
|
||||
injectScript(pathPrefix + 'cordova_plugins.js', function(){
|
||||
try {
|
||||
var moduleList = require("cordova/plugin_list");
|
||||
handlePluginsObject(pathPrefix, moduleList, finishPluginLoading);
|
||||
} catch (e) {
|
||||
// Error loading cordova_plugins.js, file not found or something
|
||||
// this is an acceptable error, pre-3.0.0, so we just move on.
|
||||
finishPluginLoading();
|
||||
}
|
||||
}, finishPluginLoading); // also, add script load error handler for file not found
|
||||
}
|
||||
|
||||
function findCordovaPath() {
|
||||
var path = null;
|
||||
var scripts = document.getElementsByTagName('script');
|
||||
var term = 'cordova.js';
|
||||
for (var n = scripts.length-1; n>-1; n--) {
|
||||
var src = scripts[n].src;
|
||||
var src = scripts[n].src.replace(/\?.*$/, ''); // Strip any query param (CB-6007).
|
||||
if (src.indexOf(term) == (src.length - term.length)) {
|
||||
path = src.substring(0, src.length - term.length);
|
||||
break;
|
||||
@@ -1544,30 +1675,33 @@ exports.load = function(callback) {
|
||||
console.log('Could not find cordova.js script tag. Plugin loading may fail.');
|
||||
pathPrefix = '';
|
||||
}
|
||||
injectPluginScript(pathPrefix, callback);
|
||||
injectIfNecessary('cordova/plugin_list', pathPrefix + 'cordova_plugins.js', function() {
|
||||
var moduleList = require("cordova/plugin_list");
|
||||
handlePluginsObject(pathPrefix, moduleList, callback);
|
||||
}, callback);
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/urlutil.js
|
||||
// file: src/common/urlutil.js
|
||||
define("cordova/urlutil", function(require, exports, module) {
|
||||
|
||||
var urlutil = exports;
|
||||
var anchorEl = document.createElement('a');
|
||||
|
||||
/**
|
||||
* For already absolute URLs, returns what is passed in.
|
||||
* For relative URLs, converts them to absolute ones.
|
||||
*/
|
||||
urlutil.makeAbsolute = function(url) {
|
||||
exports.makeAbsolute = function makeAbsolute(url) {
|
||||
var anchorEl = document.createElement('a');
|
||||
anchorEl.href = url;
|
||||
return anchorEl.href;
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/utils.js
|
||||
// file: src/common/utils.js
|
||||
define("cordova/utils", function(require, exports, module) {
|
||||
|
||||
var utils = exports;
|
||||
@@ -1738,7 +1872,7 @@ function UUIDcreatePart(length) {
|
||||
});
|
||||
|
||||
window.cordova = require('cordova');
|
||||
// file: lib/scripts/bootstrap.js
|
||||
// file: src/scripts/bootstrap.js
|
||||
|
||||
require('cordova/init');
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<content src="index.html" />
|
||||
|
||||
<preference name="loglevel" value="DEBUG" />
|
||||
|
||||
<!--
|
||||
<preference name="splashscreen" value="resourceName" />
|
||||
<preference name="backgroundColor" value="0xFFF" />
|
||||
|
||||
397
framework/src/org/apache/cordova/AndroidChromeClient.java
Executable file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
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.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.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.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
@@ -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);
|
||||
}
|
||||
}
|
||||
1096
framework/src/org/apache/cordova/AndroidWebView.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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -204,6 +204,7 @@ public class Config {
|
||||
*/
|
||||
public static void addWhiteListEntry(String origin, boolean subdomains) {
|
||||
if (self == null) {
|
||||
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
|
||||
return;
|
||||
}
|
||||
self.whitelist.addWhiteListEntry(origin, subdomains);
|
||||
@@ -217,6 +218,7 @@ public class Config {
|
||||
*/
|
||||
public static boolean isUrlWhiteListed(String url) {
|
||||
if (self == null) {
|
||||
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
|
||||
return false;
|
||||
}
|
||||
return self.whitelist.isUrlWhiteListed(url);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -147,63 +149,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
|
||||
private Object LOG_TAG;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -269,35 +214,63 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
* require a more specialized web view.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* This is intended to be overridable by subclasses of CordovaIntent which
|
||||
* require a more specialized web view.
|
||||
* This is intended to be overridable by subclasses of CordovaActivity which
|
||||
* 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
|
||||
*/
|
||||
protected CordovaWebViewClient makeWebViewClient(CordovaWebView webView) {
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
|
||||
return new CordovaWebViewClient(this, webView);
|
||||
} else {
|
||||
return new IceCreamCordovaWebViewClient(this, webView);
|
||||
}
|
||||
return webView.makeWebViewClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the chrome client for the default web view object.
|
||||
*
|
||||
* This is intended to be overridable by subclasses of CordovaIntent which
|
||||
* require a more specialized web view.
|
||||
* This is intended to be overridable by subclasses of CordovaActivity which
|
||||
* 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
|
||||
*/
|
||||
protected CordovaChromeClient makeChromeClient(CordovaWebView webView) {
|
||||
return new CordovaChromeClient(this, webView);
|
||||
return webView.makeWebChromeClient();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,13 +308,14 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
|
||||
if (this.getBooleanProperty("DisallowOverscroll", false)) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
|
||||
this.appView.setOverScrollMode(CordovaWebView.OVER_SCROLL_NEVER);
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
// Add web view but make it invisible while loading URL
|
||||
this.appView.setVisibility(View.INVISIBLE);
|
||||
this.root.addView(this.appView);
|
||||
this.root.addView((View) this.appView.getView());
|
||||
setContentView(this.root);
|
||||
|
||||
// Clear cancel flag
|
||||
@@ -408,17 +382,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
this.splashscreenTime = time;
|
||||
this.loadUrl(url);
|
||||
|
||||
/*
|
||||
// Init web view if not already done
|
||||
if (this.appView == null) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
this.splashscreenTime = time;
|
||||
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
||||
this.showSplashScreen(this.splashscreenTime);
|
||||
this.appView.loadUrl(url, time);
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -455,15 +418,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cancel loadUrl before it has been loaded.
|
||||
*/
|
||||
// TODO NO-OP
|
||||
@Deprecated
|
||||
public void cancelLoadUrl() {
|
||||
this.cancelLoadUrl = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the resource cache.
|
||||
*/
|
||||
@@ -605,66 +559,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
return p.doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
@@ -771,31 +665,18 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Add services to res/xml/plugins.xml instead.
|
||||
*
|
||||
* Add a class that implements a service.
|
||||
*
|
||||
* @param serviceType
|
||||
* @param className
|
||||
*/
|
||||
@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.
|
||||
* (This is a convenience method)
|
||||
*
|
||||
* @param statement
|
||||
*
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
if (this.appView != null) {
|
||||
this.appView.jsMessageQueue.addJavaScript(statement);
|
||||
this.appView.addJavascript(statement);
|
||||
//this.appView.jsMessageQueue.addJavaScript(statement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,7 +755,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
Log.d(TAG, "Request code = " + requestCode);
|
||||
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?");
|
||||
if (null == mUploadMessage)
|
||||
return;
|
||||
@@ -889,7 +770,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
if(callback == null && initCallbackClass != null) {
|
||||
// The application was restarted, but had defined an initial callback
|
||||
// before being shut down.
|
||||
this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
|
||||
//this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
|
||||
this.activityResultCallback = appView.getPlugin(initCallbackClass);
|
||||
callback = this.activityResultCallback;
|
||||
}
|
||||
if(callback != null) {
|
||||
@@ -1180,4 +1062,66 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
372
framework/src/org/apache/cordova/CordovaChromeClient.java
Executable file → Normal file
@@ -18,379 +18,15 @@
|
||||
*/
|
||||
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 CordovaChromeClient extends WebChromeClient {
|
||||
public interface 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;
|
||||
int FILECHOOSER_RESULTCODE = 0;
|
||||
|
||||
// the video progress view
|
||||
private View mVideoProgressView;
|
||||
|
||||
// File Chooser
|
||||
public ValueCallback<Uri> mUploadMessage;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param cordova
|
||||
*/
|
||||
public CordovaChromeClient(CordovaInterface cordova) {
|
||||
this.cordova = cordova;
|
||||
}
|
||||
void setWebView(CordovaWebView appView);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ctx
|
||||
* @param app
|
||||
*/
|
||||
public CordovaChromeClient(CordovaInterface ctx, CordovaWebView app) {
|
||||
this.cordova = ctx;
|
||||
this.appView = app;
|
||||
}
|
||||
ValueCallback<Uri> getValueCallback();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
result.confirm(r == null ? "" : r);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the native->JS bridge mode.
|
||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_bridge_mode:")) {
|
||||
try {
|
||||
this.appView.exposedJsApi.setNativeToJsBridgeMode(Integer.parseInt(message));
|
||||
result.confirm("");
|
||||
} catch (NumberFormatException e){
|
||||
result.confirm("");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Polling for JavaScript messages
|
||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) {
|
||||
String r = this.appView.exposedJsApi.retrieveJsMessages("1".equals(message));
|
||||
result.confirm(r == null ? "" : r);
|
||||
}
|
||||
|
||||
// Do NO-OP so older code doesn't display dialog
|
||||
else if (defaultValue != null && defaultValue.equals("gap_init:")) {
|
||||
result.confirm("OK");
|
||||
}
|
||||
|
||||
// Show dialog
|
||||
else {
|
||||
final JsPromptResult res = result;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,27 @@ import java.net.URL;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* What this class provides:
|
||||
* 1. Helpers for reading & writing to URLs.
|
||||
* - E.g. handles assets, resources, content providers, files, data URIs, http[s]
|
||||
* - E.g. Can be used to query for mime-type & content length.
|
||||
*
|
||||
* 2. To allow plugins to redirect URLs (via remapUrl).
|
||||
* - All plugins should call remapUrl() on URLs they receive from JS *before*
|
||||
* passing the URL onto other utility functions in this class.
|
||||
* - For an example usage of this, refer to the org.apache.cordova.file plugin.
|
||||
*
|
||||
* 3. It exposes a way to use the OkHttp library that ships with Cordova.
|
||||
* - Through createHttpConnection().
|
||||
*
|
||||
* Future Work:
|
||||
* - Consider using a Cursor to query content URLs for their size (like the file plugin does).
|
||||
* - Allow plugins to remapUri to "cdv-plugin://plugin-name/$ID", which CordovaResourceApi
|
||||
* would then delegate to pluginManager.getPlugin(plugin-name).openForRead($ID)
|
||||
* - Currently, plugins *can* do this by remapping to a data: URL, but it's inefficient
|
||||
* for large payloads.
|
||||
*/
|
||||
public class CordovaResourceApi {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String LOG_TAG = "CordovaResourceApi";
|
||||
@@ -63,7 +84,7 @@ public class CordovaResourceApi {
|
||||
// Creating this is light-weight.
|
||||
private static OkHttpClient httpClient = new OkHttpClient();
|
||||
|
||||
static Thread jsThread;
|
||||
public static Thread jsThread;
|
||||
|
||||
private final AssetManager assetManager;
|
||||
private final ContentResolver contentResolver;
|
||||
@@ -346,6 +367,10 @@ public class CordovaResourceApi {
|
||||
copyResource(openForRead(sourceUri), outputStream);
|
||||
}
|
||||
|
||||
// Added in 3.5.0.
|
||||
public void copyResource(Uri sourceUri, Uri dstUri) throws IOException {
|
||||
copyResource(openForRead(sourceUri), openOutputStream(dstUri));
|
||||
}
|
||||
|
||||
private void assertBackgroundThread() {
|
||||
if (threadCheckingEnabled) {
|
||||
|
||||
1088
framework/src/org/apache/cordova/CordovaWebView.java
Executable file → Normal file
468
framework/src/org/apache/cordova/CordovaWebViewClient.java
Executable file → Normal file
@@ -1,471 +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;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Hashtable;
|
||||
public interface CordovaWebViewClient {
|
||||
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
void setWebView(CordovaWebView appView);
|
||||
|
||||
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 CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
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;
|
||||
|
||||
/** The authorization tokens. */
|
||||
private Hashtable<String, AuthenticationToken> authenticationTokens = new Hashtable<String, AuthenticationToken>();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param cordova
|
||||
*/
|
||||
public CordovaWebViewClient(CordovaInterface cordova) {
|
||||
this.cordova = cordova;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param cordova
|
||||
* @param view
|
||||
*/
|
||||
public CordovaWebViewClient(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);
|
||||
appView.pluginManager.exec(service, action, callbackId, jsonArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.pluginManager != null) && this.appView.pluginManager.onOverrideUrlLoading(url)) {
|
||||
}
|
||||
|
||||
// If dialing phone (tel:5551212)
|
||||
else if (url.startsWith(WebView.SCHEME_TEL)) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_DIAL);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error dialing " + url + ": " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// If displaying map (geo:0,0?q=address)
|
||||
else if (url.startsWith("geo:")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error showing map " + url + ": " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// If sending email (mailto:abc@corp.com)
|
||||
else if (url.startsWith(WebView.SCHEME_MAILTO)) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error sending email " + url + ": " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// If sms:5551212?body=This is the message
|
||||
else if (url.startsWith("sms:")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
|
||||
// Get address
|
||||
String address = null;
|
||||
int parmIndex = url.indexOf('?');
|
||||
if (parmIndex == -1) {
|
||||
address = url.substring(4);
|
||||
}
|
||||
else {
|
||||
address = url.substring(4, parmIndex);
|
||||
|
||||
// If body, then set sms body
|
||||
Uri uri = Uri.parse(url);
|
||||
String query = uri.getQuery();
|
||||
if (query != null) {
|
||||
if (query.startsWith("body=")) {
|
||||
intent.putExtra("sms_body", query.substring(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
intent.setData(Uri.parse("sms:" + address));
|
||||
intent.putExtra("address", address);
|
||||
intent.setType("vnd.android-dir/mms-sms");
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error sending sms " + url + ":" + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
//Android Market
|
||||
else if(url.startsWith("market:")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error loading Google Play Store: " + url, e);
|
||||
}
|
||||
}
|
||||
|
||||
// All else
|
||||
else {
|
||||
|
||||
// If our app or file:, then load into a new Cordova webview container by starting a new instance of our activity.
|
||||
// Our app continues to run. When BACK is pressed, our app is redisplayed.
|
||||
if (url.startsWith("file://") || url.startsWith("data:") || Config.isUrlWhiteListed(url)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If not our application, let default viewer handle
|
||||
else {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error loading url " + url, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
// Flush stale messages.
|
||||
this.appView.jsMessageQueue.reset();
|
||||
|
||||
// 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);
|
||||
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) {
|
||||
LOG.d(TAG, "CordovaWebViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl);
|
||||
|
||||
// Clear timeout flag
|
||||
this.appView.loadUrlTimeout++;
|
||||
|
||||
// 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();
|
||||
}
|
||||
void onReceivedError(CordovaWebView me, int i, String string, String url);
|
||||
|
||||
}
|
||||
|
||||
77
framework/src/org/apache/cordova/ExposedJsApi.java
Executable file → Normal file
@@ -1,76 +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;
|
||||
|
||||
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
|
||||
import android.webkit.JavascriptInterface;
|
||||
|
||||
/*
|
||||
* Any exposed Javascript API MUST implement these three things!
|
||||
*/
|
||||
/* package */ class ExposedJsApi {
|
||||
|
||||
private PluginManager pluginManager;
|
||||
private NativeToJsMessageQueue jsMessageQueue;
|
||||
|
||||
public ExposedJsApi(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) {
|
||||
this.pluginManager = pluginManager;
|
||||
this.jsMessageQueue = jsMessageQueue;
|
||||
}
|
||||
|
||||
public interface ExposedJsApi {
|
||||
|
||||
@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);
|
||||
}
|
||||
public String exec(String service, String action, String callbackId, String arguments) throws JSONException;
|
||||
public void setNativeToJsBridgeMode(int value);
|
||||
public String retrieveJsMessages(boolean fromOnlineEvent);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
public class IceCreamCordovaWebViewClient extends AndroidWebViewClient implements CordovaWebViewClient{
|
||||
|
||||
private static final String TAG = "IceCreamCordovaWebViewClient";
|
||||
|
||||
@@ -59,7 +59,7 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
// Allow plugins to intercept WebView requests.
|
||||
Uri remappedUri = resourceApi.remapUri(origUri);
|
||||
|
||||
if (!origUri.equals(remappedUri) || needsSpecialsInAssetUrlFix(origUri)) {
|
||||
if (!origUri.equals(remappedUri) || needsSpecialsInAssetUrlFix(origUri) || needsKitKatContentUrlFix(origUri)) {
|
||||
OpenForReadResult result = resourceApi.openForRead(remappedUri, true);
|
||||
return new WebResourceResponse(result.mimeType, "UTF-8", result.inputStream);
|
||||
}
|
||||
@@ -74,6 +74,10 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean needsKitKatContentUrlFix(Uri uri) {
|
||||
return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && "content".equals(uri.getScheme());
|
||||
}
|
||||
|
||||
private static boolean needsSpecialsInAssetUrlFix(Uri uri) {
|
||||
if (CordovaResourceApi.getUriType(uri) != CordovaResourceApi.URI_TYPE_ASSET) {
|
||||
return false;
|
||||
@@ -93,4 +97,10 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
}
|
||||
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.
|
||||
else if (height > oldHeight) {
|
||||
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
|
||||
// been displayed.
|
||||
else if (height < oldHeight) {
|
||||
if (app != null)
|
||||
app.appView.sendJavascript("cordova.fireDocumentEvent('showkeyboard');");
|
||||
app.sendJavascript("cordova.fireDocumentEvent('showkeyboard');");
|
||||
}
|
||||
|
||||
// Update the old height for the next event
|
||||
|
||||
@@ -44,11 +44,11 @@ public class NativeToJsMessageQueue {
|
||||
|
||||
// Disable URL-based exec() bridge by default since it's a bit of a
|
||||
// security concern.
|
||||
static final boolean ENABLE_LOCATION_CHANGE_EXEC_MODE = false;
|
||||
public static final boolean ENABLE_LOCATION_CHANGE_EXEC_MODE = false;
|
||||
|
||||
// Disable sending back native->JS messages during an exec() when the active
|
||||
// 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.
|
||||
// This currently only chops up on message boundaries. It may be useful
|
||||
@@ -83,7 +83,7 @@ public class NativeToJsMessageQueue {
|
||||
this.cordova = cordova;
|
||||
this.webView = webView;
|
||||
registeredListeners = new BridgeMode[4];
|
||||
registeredListeners[0] = null; // Polling. Requires no logic.
|
||||
registeredListeners[0] = new PollingBridgeMode();
|
||||
registeredListeners[1] = new LoadUrlBridgeMode();
|
||||
registeredListeners[2] = new OnlineEventsBridgeMode();
|
||||
registeredListeners[3] = new PrivateApiBridgeMode();
|
||||
@@ -102,7 +102,8 @@ public class NativeToJsMessageQueue {
|
||||
synchronized (this) {
|
||||
activeListenerIndex = value;
|
||||
BridgeMode activeListener = registeredListeners[value];
|
||||
if (!paused && !queue.isEmpty() && activeListener != null) {
|
||||
activeListener.reset();
|
||||
if (!paused && !queue.isEmpty()) {
|
||||
activeListener.onNativeToJsMessageAvailable();
|
||||
}
|
||||
}
|
||||
@@ -117,6 +118,7 @@ public class NativeToJsMessageQueue {
|
||||
synchronized (this) {
|
||||
queue.clear();
|
||||
setBridgeMode(DEFAULT_BRIDGE_MODE);
|
||||
registeredListeners[activeListenerIndex].reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +251,7 @@ public class NativeToJsMessageQueue {
|
||||
private void enqueueMessage(JsMessage message) {
|
||||
synchronized (this) {
|
||||
queue.add(message);
|
||||
if (!paused && registeredListeners[activeListenerIndex] != null) {
|
||||
if (!paused) {
|
||||
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
|
||||
}
|
||||
}
|
||||
@@ -264,7 +266,7 @@ public class NativeToJsMessageQueue {
|
||||
paused = value;
|
||||
if (!value) {
|
||||
synchronized (this) {
|
||||
if (!queue.isEmpty() && registeredListeners[activeListenerIndex] != null) {
|
||||
if (!queue.isEmpty()) {
|
||||
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
|
||||
}
|
||||
}
|
||||
@@ -278,8 +280,15 @@ public class NativeToJsMessageQueue {
|
||||
private abstract class BridgeMode {
|
||||
abstract void onNativeToJsMessageAvailable();
|
||||
void notifyOfFlush(boolean fromOnlineEvent) {}
|
||||
void reset() {}
|
||||
}
|
||||
|
||||
|
||||
/** Uses JS polls for messages on a timer.. */
|
||||
private class PollingBridgeMode extends BridgeMode {
|
||||
@Override void onNativeToJsMessageAvailable() {
|
||||
}
|
||||
}
|
||||
|
||||
/** Uses webView.loadUrl("javascript:") to execute messages. */
|
||||
private class LoadUrlBridgeMode extends BridgeMode {
|
||||
final Runnable runnable = new Runnable() {
|
||||
@@ -298,7 +307,7 @@ public class NativeToJsMessageQueue {
|
||||
|
||||
/** Uses online/offline events to tell the JS when to poll for messages. */
|
||||
private class OnlineEventsBridgeMode extends BridgeMode {
|
||||
boolean online = false;
|
||||
private boolean online;
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
if (!queue.isEmpty()) {
|
||||
@@ -306,7 +315,8 @@ public class NativeToJsMessageQueue {
|
||||
}
|
||||
}
|
||||
};
|
||||
OnlineEventsBridgeMode() {
|
||||
@Override void reset() {
|
||||
online = false;
|
||||
webView.setNetworkAvailable(true);
|
||||
}
|
||||
@Override void onNativeToJsMessageAvailable() {
|
||||
@@ -484,9 +494,22 @@ public class NativeToJsMessageQueue {
|
||||
.append(success)
|
||||
.append(",")
|
||||
.append(status)
|
||||
.append(",[")
|
||||
.append(pluginResult.getMessage())
|
||||
.append("],")
|
||||
.append(",[");
|
||||
switch (pluginResult.getMessageType()) {
|
||||
case PluginResult.MESSAGE_TYPE_BINARYSTRING:
|
||||
sb.append("atob('")
|
||||
.append(pluginResult.getMessage())
|
||||
.append("')");
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_ARRAYBUFFER:
|
||||
sb.append("cordova.require('cordova/base64').toArrayBuffer('")
|
||||
.append(pluginResult.getMessage())
|
||||
.append("')");
|
||||
break;
|
||||
default:
|
||||
sb.append(pluginResult.getMessage());
|
||||
}
|
||||
sb.append("],")
|
||||
.append(pluginResult.getKeepCallback())
|
||||
.append(");");
|
||||
}
|
||||
|
||||
@@ -236,22 +236,25 @@ public class PluginManager {
|
||||
app.sendPluginResult(cr, callbackId);
|
||||
return;
|
||||
}
|
||||
CallbackContext callbackContext = new CallbackContext(callbackId, app);
|
||||
try {
|
||||
CallbackContext callbackContext = new CallbackContext(callbackId, app);
|
||||
long pluginStartTime = System.currentTimeMillis();
|
||||
boolean wasValidAction = plugin.execute(action, rawArgs, callbackContext);
|
||||
long duration = System.currentTimeMillis() - pluginStartTime;
|
||||
|
||||
|
||||
if (duration > SLOW_EXEC_WARNING_THRESHOLD) {
|
||||
Log.w(TAG, "THREAD WARNING: exec() call to " + service + "." + action + " blocked the main thread for " + duration + "ms. Plugin should use CordovaInterface.getThreadPool().");
|
||||
}
|
||||
if (!wasValidAction) {
|
||||
PluginResult cr = new PluginResult(PluginResult.Status.INVALID_ACTION);
|
||||
app.sendPluginResult(cr, callbackId);
|
||||
callbackContext.sendPluginResult(cr);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
PluginResult cr = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
app.sendPluginResult(cr, callbackId);
|
||||
callbackContext.sendPluginResult(cr);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Uncaught exception from plugin", e);
|
||||
callbackContext.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public class ScrollEvent {
|
||||
* @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;
|
||||
targetView = view;
|
||||
|
||||
@@ -55,14 +55,14 @@ public class Whitelist {
|
||||
if (scheme == null || "*".equals(scheme)) {
|
||||
this.scheme = null;
|
||||
} else {
|
||||
this.scheme = Pattern.compile(regexFromPattern(scheme, false));
|
||||
this.scheme = Pattern.compile(regexFromPattern(scheme, false), Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
if ("*".equals(host)) {
|
||||
this.host = null;
|
||||
} else if (host.startsWith("*.")) {
|
||||
this.host = Pattern.compile("([a-z0-9.-]*\\.)?" + regexFromPattern(host.substring(2), false));
|
||||
this.host = Pattern.compile("([a-z0-9.-]*\\.)?" + regexFromPattern(host.substring(2), false), Pattern.CASE_INSENSITIVE);
|
||||
} else {
|
||||
this.host = Pattern.compile(regexFromPattern(host, false));
|
||||
this.host = Pattern.compile(regexFromPattern(host, false), Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
if (port == null || "*".equals(port)) {
|
||||
this.port = null;
|
||||
@@ -120,7 +120,7 @@ public class Whitelist {
|
||||
whiteList = null;
|
||||
}
|
||||
else { // specific access
|
||||
Pattern parts = Pattern.compile("^((\\*|[a-z-]+)://)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?");
|
||||
Pattern parts = Pattern.compile("^((\\*|[A-Za-z-]+)://)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?");
|
||||
Matcher m = parts.matcher(origin);
|
||||
if (m.matches()) {
|
||||
String scheme = m.group(2);
|
||||
|
||||
22
package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "cordova-android",
|
||||
"version": "3.4.0",
|
||||
"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"
|
||||
],
|
||||
"author": "Apache Software Foundation",
|
||||
"license": "Apache version 2.0",
|
||||
"dependencies": {
|
||||
"q": "^0.9.0",
|
||||
"shelljs": "^0.2.6",
|
||||
"which": "^1.0.5"
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<org.apache.cordova.CordovaWebView
|
||||
<org.apache.cordova.AndroidWebView
|
||||
android:id="@+id/cordovaWebView"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
@@ -1,44 +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 android.os.Bundle;
|
||||
import org.apache.cordova.*;
|
||||
|
||||
public class basicauth extends DroidGap {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
super.init();
|
||||
|
||||
// LogCat: onReceivedHttpAuthRequest(browserspy.dk:80,BrowserSpy.dk - HTTP Password Test)
|
||||
AuthenticationToken token = new AuthenticationToken();
|
||||
token.setUserName("test");
|
||||
token.setPassword("test");
|
||||
super.setAuthenticationToken(token, "browserspy.dk:80", "BrowserSpy.dk - HTTP Password Test");
|
||||
|
||||
// Add web site to whitelist
|
||||
Config.init();
|
||||
Config.addWhiteListEntry("http://browserspy.dk*", true);
|
||||
|
||||
// Load test
|
||||
super.loadUrl("file:///android_asset/www/basicauth/index.html");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import org.apache.cordova.test.backbuttonmultipage;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.test.UiThreadTest;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
@@ -174,7 +175,7 @@ public class BackButtonMultiPageTest extends ActivityInstrumentationTestCase2<ba
|
||||
{
|
||||
String url = testView.getUrl();
|
||||
assertTrue(url.endsWith("sample3.html"));
|
||||
BaseInputConnection viewConnection = new BaseInputConnection(testView, true);
|
||||
BaseInputConnection viewConnection = new BaseInputConnection((View) testView, true);
|
||||
KeyEvent backDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
|
||||
KeyEvent backUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
|
||||
viewConnection.sendKeyEvent(backDown);
|
||||
@@ -187,7 +188,7 @@ public class BackButtonMultiPageTest extends ActivityInstrumentationTestCase2<ba
|
||||
{
|
||||
String url = testView.getUrl();
|
||||
assertTrue(url.endsWith("sample2.html"));
|
||||
BaseInputConnection viewConnection = new BaseInputConnection(testView, true);
|
||||
BaseInputConnection viewConnection = new BaseInputConnection((View) testView, true);
|
||||
KeyEvent backDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
|
||||
KeyEvent backUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
|
||||
viewConnection.sendKeyEvent(backDown);
|
||||
|
||||
@@ -65,7 +65,7 @@ public class CordovaResourceApiTest extends ActivityInstrumentationTestCase2<Cor
|
||||
cordovaWebView = activity.cordovaWebView;
|
||||
resourceApi = cordovaWebView.getResourceApi();
|
||||
resourceApi.setThreadCheckingEnabled(false);
|
||||
cordovaWebView.pluginManager.addService(new PluginEntry("CordovaResourceApiTestPlugin1", new CordovaPlugin() {
|
||||
cordovaWebView.getPluginManager().addService(new PluginEntry("CordovaResourceApiTestPlugin1", new CordovaPlugin() {
|
||||
@Override
|
||||
public Uri remapUri(Uri uri) {
|
||||
if (uri.getQuery() != null && uri.getQuery().contains("pluginRewrite")) {
|
||||
|
||||
@@ -1,32 +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.test.menus;
|
||||
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
|
||||
public class MenuTest extends ActivityInstrumentationTestCase2<menus> {
|
||||
|
||||
public MenuTest() {
|
||||
super("org.apache.cordova.test", menus.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova.test.junit;
|
||||
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.ScrollEvent;
|
||||
import org.apache.cordova.pluginApi.pluginStub;
|
||||
import org.apache.cordova.test.CordovaWebViewTestActivity;
|
||||
import org.apache.cordova.test.R;
|
||||
|
||||
import com.jayway.android.robotium.solo.By;
|
||||
import com.jayway.android.robotium.solo.Solo;
|
||||
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.view.View;
|
||||
|
||||
public class MessageTest extends
|
||||
ActivityInstrumentationTestCase2<CordovaWebViewTestActivity> {
|
||||
private CordovaWebViewTestActivity testActivity;
|
||||
private CordovaWebView testView;
|
||||
private pluginStub testPlugin;
|
||||
private int TIMEOUT = 1000;
|
||||
|
||||
private Solo solo;
|
||||
|
||||
public MessageTest() {
|
||||
super("org.apache.cordova.test.activities", CordovaWebViewTestActivity.class);
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
testActivity = this.getActivity();
|
||||
testView = (CordovaWebView) testActivity.findViewById(R.id.cordovaWebView);
|
||||
testPlugin = (pluginStub) testView.pluginManager.getPlugin("PluginStub");
|
||||
solo = new Solo(getInstrumentation(), getActivity());
|
||||
}
|
||||
|
||||
public void testOnScrollChanged()
|
||||
{
|
||||
solo.waitForWebElement(By.textContent("Cordova Android Tests"));
|
||||
solo.scrollDown();
|
||||
sleep();
|
||||
Object data = testPlugin.data;
|
||||
assertTrue(data.getClass().getSimpleName().equals("ScrollEvent"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void sleep() {
|
||||
try {
|
||||
Thread.sleep(TIMEOUT);
|
||||
} catch (InterruptedException e) {
|
||||
fail("Unexpected Timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +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 android.os.Bundle;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
|
||||
import org.apache.cordova.*;
|
||||
import org.apache.cordova.LOG;
|
||||
|
||||
public class menus extends DroidGap {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
super.init();
|
||||
super.registerForContextMenu(super.appView);
|
||||
super.loadUrl("file:///android_asset/www/menus/index.html");
|
||||
}
|
||||
|
||||
// Demonstrate how to add your own menus to app
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
int base = Menu.FIRST;
|
||||
// Group, item id, order, title
|
||||
menu.add(base, base, base, "Item1");
|
||||
menu.add(base, base + 1, base + 1, "Item2");
|
||||
menu.add(base, base + 2, base + 2, "Item3");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
LOG.d("menus", "Item " + item.getItemId() + " pressed.");
|
||||
this.appView.loadUrl("javascript:alert('Menu " + item.getItemId() + " pressed.')");
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
LOG.d("menus", "onPrepareOptionsMenu()");
|
||||
// this.appView.loadUrl("javascript:alert('onPrepareOptionsMenu()')");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo info) {
|
||||
LOG.d("menus", "onCreateContextMenu()");
|
||||
menu.setHeaderTitle("Test Context Menu");
|
||||
menu.add(200, 200, 200, "Context Item1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
this.appView.loadUrl("javascript:alert('Context Menu " + item.getItemId() + " pressed.')");
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import android.webkit.GeolocationPermissions.Callback;
|
||||
import org.apache.cordova.*;
|
||||
import org.apache.cordova.LOG;
|
||||
|
||||
public class userwebview extends DroidGap {
|
||||
public class userwebview extends CordovaActivity {
|
||||
|
||||
public TestViewClient testViewClient;
|
||||
public TestChromeClient testChromeClient;
|
||||
@@ -35,12 +35,12 @@ public class userwebview extends DroidGap {
|
||||
super.onCreate(savedInstanceState);
|
||||
testViewClient = new TestViewClient(this);
|
||||
testChromeClient = new TestChromeClient(this);
|
||||
super.init(new CordovaWebView(this), new TestViewClient(this), new TestChromeClient(this));
|
||||
super.init(new AndroidWebView(this), new TestViewClient(this), new TestChromeClient(this));
|
||||
super.loadUrl("file:///android_asset/www/userwebview/index.html");
|
||||
}
|
||||
|
||||
public class TestChromeClient extends CordovaChromeClient {
|
||||
public TestChromeClient(DroidGap arg0) {
|
||||
public class TestChromeClient extends AndroidChromeClient {
|
||||
public TestChromeClient(CordovaActivity arg0) {
|
||||
super(arg0);
|
||||
LOG.d("userwebview", "TestChromeClient()");
|
||||
}
|
||||
@@ -56,8 +56,8 @@ public class userwebview extends DroidGap {
|
||||
/**
|
||||
* This class can be used to override the GapViewClient and receive notification of webview events.
|
||||
*/
|
||||
public class TestViewClient extends CordovaWebViewClient {
|
||||
public TestViewClient(DroidGap arg0) {
|
||||
public class TestViewClient extends AndroidWebViewClient {
|
||||
public TestViewClient(CordovaActivity arg0) {
|
||||
super(arg0);
|
||||
LOG.d("userwebview", "TestViewClient()");
|
||||
}
|
||||
|
||||
@@ -24,20 +24,20 @@ import android.webkit.WebView;
|
||||
import org.apache.cordova.*;
|
||||
import org.apache.cordova.LOG;
|
||||
|
||||
public class whitelist extends DroidGap {
|
||||
public class whitelist extends CordovaActivity {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
super.init(new CordovaWebView(this), new TestViewClient(this), new CordovaChromeClient(this));
|
||||
super.init(new AndroidWebView(this), new TestViewClient(this), new AndroidChromeClient(this));
|
||||
super.loadUrl("file:///android_asset/www/whitelist/index.html");
|
||||
}
|
||||
|
||||
/**
|
||||
* This class can be used to override the GapViewClient and receive notification of webview events.
|
||||
*/
|
||||
public class TestViewClient extends CordovaWebViewClient {
|
||||
public class TestViewClient extends AndroidWebViewClient {
|
||||
|
||||
public TestViewClient(DroidGap arg0) {
|
||||
public TestViewClient(CordovaActivity arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
|
||||