Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2a68fd4394 | ||
![]() |
54996900b5 | ||
![]() |
bb08abc0b9 | ||
![]() |
1dfbebf98e | ||
![]() |
2e0f40ff51 | ||
![]() |
bdecdea568 | ||
![]() |
a88b8de820 | ||
![]() |
bb6f75c0a8 | ||
![]() |
57e92e8a56 | ||
![]() |
93b9b53acb | ||
![]() |
edb35b5a6d | ||
![]() |
bb7bc33a8a | ||
![]() |
64c617d8ab | ||
![]() |
bcccb0c620 | ||
![]() |
e1de55cc82 | ||
![]() |
4b501a5b63 | ||
![]() |
dc494c85f2 | ||
![]() |
624a8d370c | ||
![]() |
5814d666ab | ||
![]() |
53e3a12441 | ||
![]() |
dd770ef303 | ||
![]() |
8b3fa5c91e | ||
![]() |
43bf47ea7b | ||
![]() |
4be84fbf12 | ||
![]() |
e9b46e5cf6 | ||
![]() |
6a57a3c452 | ||
![]() |
cdb49eaa1f | ||
![]() |
cb11486f66 | ||
![]() |
0e9b446a81 | ||
![]() |
2cf79a1adb | ||
![]() |
5550ec8f2c | ||
![]() |
70f3785b47 | ||
![]() |
8c4c22c72a | ||
![]() |
cd4a316a91 | ||
![]() |
654d489c01 | ||
![]() |
f3acf9002e | ||
![]() |
e03bd0a40d | ||
![]() |
1167da2027 | ||
![]() |
7cde444ffe | ||
![]() |
67e97a89ce | ||
![]() |
c5e83b1079 | ||
![]() |
df1536ea77 | ||
![]() |
6aff54be76 | ||
![]() |
24a0e8956c | ||
![]() |
e01678da93 | ||
![]() |
2b6a683198 | ||
![]() |
1f58d8ee27 | ||
![]() |
fb4527d91b | ||
![]() |
a83bbcaf1d |
@ -19,16 +19,16 @@
|
||||
ROOT="$( cd "$( dirname "$0" )/.." && pwd )"
|
||||
cmd=`android list target`
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "The command `android` failed. Make sure you have the latest Android SDK installed, and the `android` command (inside the tools/ folder) added to your path."
|
||||
echo "The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) added to your path."
|
||||
exit 2
|
||||
elif [[ ! $cmd =~ "android-17" ]]; then
|
||||
echo "Please install Android target 17 (the Android 4.2 SDK). Make sure you have the latest Android tools installed as well. Run `android` from your command-line to install/update any missing SDKs or tools."
|
||||
elif [[ ! $cmd =~ "android-18" ]]; then
|
||||
echo "Please install Android target 18 (the Android 4.3 SDK). Make sure you have the latest Android tools installed as well. Run \"android\" from your command-line to install/update any missing SDKs or tools."
|
||||
exit 2
|
||||
else
|
||||
cmd="android update project -p $ROOT -t android-17 1> /dev/null 2>&1"
|
||||
cmd="android update project -p $ROOT -t android-18 1> /dev/null 2>&1"
|
||||
eval $cmd
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "Error updating the Cordova library to work with your Android environment."
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
@ -54,22 +54,28 @@ function Log(msg, error) {
|
||||
else {
|
||||
WScript.StdOut.WriteLine(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checks that android requirements are met
|
||||
function check_requirements() {
|
||||
var target = get_target();
|
||||
if(target==null) {
|
||||
Log('Unable to find android target in project.properties');
|
||||
WScript.Quit(2);
|
||||
}
|
||||
var result = exec_out('%comspec% /c android list target');
|
||||
if(result.error) {
|
||||
Log('The command `android` failed. Make sure you have the latest Android SDK installed, and the `android` command (inside the tools/ folder) added to your path. Output: ' + result.output, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else if(!result.output.match(/android[-]17/)) {
|
||||
Log('Please install Android target 17 (the Android 4.2 SDK). Make sure you have the latest Android tools installed as well. Run `android` from your command-line to install/update any missing SDKs or tools.', true);
|
||||
Log('Output : ' + result.output);
|
||||
else if(result.output.indexOf(target) == -1) {
|
||||
Log(result.output.indexOf(target));
|
||||
Log('Please install the latest Android target (' + target + '). Make sure you have the latest Android tools installed as well. Run `android` from your command-line to install/update any missing SDKs or tools.', true);
|
||||
Log(result.output);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else {
|
||||
var cmd = '%comspec% /c android update project -p ' + ROOT + '\\framework -t android-17';
|
||||
var cmd = '%comspec% /c android update project -p ' + ROOT + '\\framework -t ' + target;
|
||||
result = exec_out(cmd);
|
||||
if(result.error) {
|
||||
Log('Error updating the Cordova library to work with your Android environment. Command run: "' + cmd + '", output: ' + result.output, true);
|
||||
@ -78,4 +84,19 @@ function check_requirements() {
|
||||
}
|
||||
}
|
||||
|
||||
check_requirements();
|
||||
function get_target() {
|
||||
var fso=WScript.CreateObject("Scripting.FileSystemObject");
|
||||
var f=fso.OpenTextFile(ROOT + '\\framework\\project.properties', 1);
|
||||
var s=f.ReadAll();
|
||||
var lines = s.split('\n');
|
||||
for (var line in lines) {
|
||||
if(lines[line].match(/target=/))
|
||||
{
|
||||
return lines[line].split('=')[1].replace(' ', '').replace('\r', '');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
check_requirements();
|
||||
|
||||
|
@ -38,7 +38,8 @@ VERSION=$(cat "$BUILD_PATH"/VERSION)
|
||||
|
||||
PROJECT_PATH="${1:-'./example'}"
|
||||
PACKAGE=${2:-"org.apache.cordova.example"}
|
||||
ACTIVITY=${3:-"cordovaExample"}
|
||||
ACTIVITY=$(echo ${3:-"cordovaExample"} | tr -d '[:blank:][:punct:]')
|
||||
APP_LABEL=${3:-"Cordova Example"};
|
||||
|
||||
# clobber any existing example
|
||||
if [ -d "$PROJECT_PATH" ]
|
||||
@ -67,7 +68,7 @@ function replace {
|
||||
# Mac OS X requires -i argument
|
||||
if [[ "$OSTYPE" =~ "darwin" ]]
|
||||
then
|
||||
/usr/bin/sed -i '' -e $pattern "$filename"
|
||||
/usr/bin/sed -i '' -e "$pattern" "$filename"
|
||||
elif [[ "$OSTYPE" =~ "linux" ]]
|
||||
then
|
||||
/bin/sed -i -e $pattern "$filename"
|
||||
@ -82,6 +83,7 @@ ANDROID_BIN="${ANDROID_BIN:=$( which android )}"
|
||||
PACKAGE_AS_PATH=$(echo $PACKAGE | sed 's/\./\//g')
|
||||
ACTIVITY_PATH="$PROJECT_PATH"/src/$PACKAGE_AS_PATH/$ACTIVITY.java
|
||||
MANIFEST_PATH="$PROJECT_PATH"/AndroidManifest.xml
|
||||
STRINGS_PATH="$PROJECT_PATH"/res/values/strings.xml
|
||||
|
||||
TARGET=$("$ANDROID_BIN" list targets | grep id: | tail -1 | cut -f 2 -d ' ' )
|
||||
API_LEVEL=$("$ANDROID_BIN" list target | grep "API level:" | tail -n 1 | cut -f 2 -d ':' | tr -d ' ')
|
||||
@ -129,6 +131,9 @@ cp "$BUILD_PATH"/bin/templates/project/Activity.java "$ACTIVITY_PATH"
|
||||
replace "s/__ACTIVITY__/${ACTIVITY}/g" "$ACTIVITY_PATH"
|
||||
replace "s/__ID__/${PACKAGE}/g" "$ACTIVITY_PATH"
|
||||
|
||||
# interpolate the app name into strings.xml
|
||||
replace "s/>${ACTIVITY}</>${APP_LABEL}</g" "$STRINGS_PATH"
|
||||
|
||||
cp "$BUILD_PATH"/bin/templates/project/AndroidManifest.xml "$MANIFEST_PATH"
|
||||
replace "s/__ACTIVITY__/${ACTIVITY}/g" "$MANIFEST_PATH"
|
||||
replace "s/__PACKAGE__/${PACKAGE}/g" "$MANIFEST_PATH"
|
||||
|
424
bin/create.js
@ -1,210 +1,214 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* create a cordova/android project
|
||||
*
|
||||
* USAGE
|
||||
* ./create [path package activity]
|
||||
*/
|
||||
|
||||
var args = WScript.Arguments, PROJECT_PATH="example",
|
||||
PACKAGE="org.apache.cordova.example", ACTIVITY="cordovaExample",
|
||||
shell=WScript.CreateObject("WScript.Shell"),
|
||||
fso = WScript.CreateObject('Scripting.FileSystemObject');
|
||||
|
||||
function Usage() {
|
||||
Log("Usage: create PathTONewProject [ PackageName AppName ]");
|
||||
Log(" PathTONewProject : The path to where you wish to create the project");
|
||||
Log(" PackageName : The package for the project (default is org.apache.cordova.example)")
|
||||
Log(" AppName : The name of the application/activity (default is cordovaExample)");
|
||||
Log("examples:");
|
||||
Log(" create C:\\Users\\anonymous\\Desktop\\MyProject");
|
||||
Log(" create C:\\Users\\anonymous\\Desktop\\MyProject io.Cordova.Example AnApp");
|
||||
}
|
||||
|
||||
// logs messaged to stdout and stderr
|
||||
function Log(msg, error) {
|
||||
if (error) {
|
||||
WScript.StdErr.WriteLine(msg);
|
||||
}
|
||||
else {
|
||||
WScript.StdOut.WriteLine(msg);
|
||||
}
|
||||
}
|
||||
|
||||
function read(filename) {
|
||||
var fso=WScript.CreateObject("Scripting.FileSystemObject");
|
||||
var f=fso.OpenTextFile(filename, 1);
|
||||
var s=f.ReadAll();
|
||||
f.Close();
|
||||
return s;
|
||||
}
|
||||
|
||||
function checkTargets(targets) {
|
||||
if(!targets) {
|
||||
Log("You do not have any android targets setup. Please create at least one target with the `android` command", true);
|
||||
WScript.Quit(69);
|
||||
}
|
||||
}
|
||||
|
||||
function setTarget() {
|
||||
var targets = shell.Exec('android.bat list targets').StdOut.ReadAll().match(/id:\s\d+/g);
|
||||
checkTargets(targets);
|
||||
return targets[targets.length - 1].replace(/id: /, ""); // TODO: give users the option to set their target
|
||||
}
|
||||
function setApiLevel() {
|
||||
var targets = shell.Exec('android.bat list targets').StdOut.ReadAll().match(/API level:\s\d+/g);
|
||||
checkTargets(targets);
|
||||
return targets[targets.length - 1].replace(/API level: /, "");
|
||||
}
|
||||
function write(filename, contents) {
|
||||
var fso=WScript.CreateObject("Scripting.FileSystemObject");
|
||||
var f=fso.OpenTextFile(filename, 2, true);
|
||||
f.Write(contents);
|
||||
f.Close();
|
||||
}
|
||||
function replaceInFile(filename, regexp, replacement) {
|
||||
write(filename, read(filename).replace(regexp, replacement));
|
||||
}
|
||||
function exec(command) {
|
||||
var oShell=shell.Exec(command);
|
||||
while (oShell.Status == 0) {
|
||||
if(!oShell.StdOut.AtEndOfStream) {
|
||||
var line = oShell.StdOut.ReadLine();
|
||||
// XXX: Change to verbose mode
|
||||
// WScript.StdOut.WriteLine(line);
|
||||
}
|
||||
WScript.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
function createAppInfoJar() {
|
||||
if(!fso.FileExists(ROOT+"\\bin\\templates\\cordova\\appinfo.jar")) {
|
||||
Log("Creating appinfo.jar...");
|
||||
var cur = shell.CurrentDirectory;
|
||||
shell.CurrentDirectory = ROOT+"\\bin\\templates\\cordova\\ApplicationInfo";
|
||||
exec("javac ApplicationInfo.java");
|
||||
exec("jar -cfe ..\\appinfo.jar ApplicationInfo ApplicationInfo.class");
|
||||
shell.CurrentDirectory = cur;
|
||||
}
|
||||
}
|
||||
|
||||
// working dir
|
||||
var ROOT = WScript.ScriptFullName.split('\\bin\\create.js').join('');
|
||||
if (args.Count() > 0) {
|
||||
// support help flags
|
||||
if (args(0) == "--help" || args(0) == "/?" ||
|
||||
args(0) == "help" || args(0) == "-help" || args(0) == "/help" || args(0) == "-h") {
|
||||
Usage();
|
||||
WScript.Quit(2);
|
||||
}
|
||||
|
||||
PROJECT_PATH=args(0);
|
||||
if (args.Count() > 1) {
|
||||
PACKAGE = args(1);
|
||||
}
|
||||
if (args.Count() > 2) {
|
||||
ACTIVITY = args(2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log("Error : No project path provided.");
|
||||
Usage();
|
||||
WScript.Quit(2);
|
||||
}
|
||||
|
||||
if(fso.FolderExists(PROJECT_PATH)) {
|
||||
Log("Project path already exists!", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
|
||||
var PACKAGE_AS_PATH=PACKAGE.replace(/\./g, '\\');
|
||||
var ACTIVITY_DIR=PROJECT_PATH + '\\src\\' + PACKAGE_AS_PATH;
|
||||
var ACTIVITY_PATH=ACTIVITY_DIR+'\\'+ACTIVITY+'.java';
|
||||
var MANIFEST_PATH=PROJECT_PATH+'\\AndroidManifest.xml';
|
||||
var TARGET=setTarget();
|
||||
var API_LEVEL=setApiLevel();
|
||||
var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
|
||||
// create the project
|
||||
Log("Creating new android project...");
|
||||
exec('android.bat create project --target "'+TARGET+'" --path "'+PROJECT_PATH+'" --package "'+PACKAGE+'" --activity "'+ACTIVITY+'"');
|
||||
|
||||
// build from source. distro should have these files
|
||||
if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
|
||||
!fso.FileExists(ROOT+'\\cordova.js')) {
|
||||
Log("Building jar and js files...");
|
||||
// update the cordova framework project to a target that exists on this machine
|
||||
exec('android.bat update project --target "'+TARGET+'" --path "'+ROOT+'\\framework"');
|
||||
exec('ant.bat -f "'+ ROOT +'\\framework\\build.xml" jar');
|
||||
}
|
||||
|
||||
// copy in the project template
|
||||
Log("Copying template files...");
|
||||
exec('%comspec% /c xcopy "'+ ROOT + '\\bin\\templates\\project\\res" "'+PROJECT_PATH+'\\res\\" /E /Y');
|
||||
exec('%comspec% /c xcopy "'+ ROOT + '\\bin\\templates\\project\\assets" "'+PROJECT_PATH+'\\assets\\" /E /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\project\\AndroidManifest.xml" "' + PROJECT_PATH + '\\AndroidManifest.xml" /Y');
|
||||
exec('%comspec% /c mkdir "' + ACTIVITY_DIR + '"');
|
||||
exec('%comspec% /c copy "' + ROOT + '"\\bin\\templates\\project\\Activity.java "' + ACTIVITY_PATH + '" /Y');
|
||||
|
||||
// check if we have the source or the distro files
|
||||
Log("Copying js, jar & config.xml files...");
|
||||
if(fso.FolderExists(ROOT + '\\framework')) {
|
||||
exec('%comspec% /c copy "'+ROOT+'\\framework\\assets\\www\\cordova.js" "'+PROJECT_PATH+'\\assets\\www\\cordova.js" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\framework\\cordova-'+VERSION+'.jar" "'+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar" /Y');
|
||||
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\framework\\res\\xml\\config.xml" "' + PROJECT_PATH + '\\res\\xml\\config.xml" /Y');
|
||||
} else {
|
||||
// copy in cordova.js
|
||||
exec('%comspec% /c copy "'+ROOT+'\\cordova.js" "'+PROJECT_PATH+'\\assets\\www\\cordova.js" /Y');
|
||||
// copy in cordova.jar
|
||||
exec('%comspec% /c copy "'+ROOT+'\\cordova-'+VERSION+'.jar" "'+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar" /Y');
|
||||
// copy in xml
|
||||
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\xml\\config.xml" "' + PROJECT_PATH + '\\res\\xml\\config.xml" /Y');
|
||||
}
|
||||
|
||||
// copy cordova scripts
|
||||
fso.CreateFolder(PROJECT_PATH + '\\cordova');
|
||||
fso.CreateFolder(PROJECT_PATH + '\\cordova\\lib');
|
||||
createAppInfoJar();
|
||||
Log("Copying cordova command tools...");
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\appinfo.jar" "' + PROJECT_PATH + '\\cordova\\appinfo.jar" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\cordova.js" "' + PROJECT_PATH + '\\cordova\\lib\\cordova.js" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\install-device.bat" "' + PROJECT_PATH + '\\cordova\\lib\\install-device.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\install-emulator.bat" "' + PROJECT_PATH + '\\cordova\\lib\\install-emulator.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-emulator-images.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-emulator-images.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-devices.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-devices.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-started-emulators.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-started-emulators.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\start-emulator.bat" "' + PROJECT_PATH + '\\cordova\\lib\\start-emulator.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\cordova.bat" "' + PROJECT_PATH + '\\cordova\\cordova.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\clean.bat" "' + PROJECT_PATH + '\\cordova\\clean.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\build.bat" "' + PROJECT_PATH + '\\cordova\\build.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\log.bat" "' + PROJECT_PATH + '\\cordova\\log.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\run.bat" "' + PROJECT_PATH + '\\cordova\\run.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\version.bat" "' + PROJECT_PATH + '\\cordova\\version.bat" /Y');
|
||||
|
||||
// interpolate the activity name and package
|
||||
Log("Updating AndroidManifest.xml and Main Activity...");
|
||||
replaceInFile(ACTIVITY_PATH, /__ACTIVITY__/, ACTIVITY);
|
||||
replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
|
||||
|
||||
replaceInFile(MANIFEST_PATH, /__ACTIVITY__/, ACTIVITY);
|
||||
replaceInFile(MANIFEST_PATH, /__PACKAGE__/, PACKAGE);
|
||||
replaceInFile(MANIFEST_PATH, /__APILEVEL__/, API_LEVEL);
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* create a cordova/android project
|
||||
*
|
||||
* USAGE
|
||||
* ./create [path package activity]
|
||||
*/
|
||||
|
||||
var args = WScript.Arguments, PROJECT_PATH="example",
|
||||
PACKAGE="org.apache.cordova.example", ACTIVITY="cordovaExample",
|
||||
shell=WScript.CreateObject("WScript.Shell"),
|
||||
fso = WScript.CreateObject('Scripting.FileSystemObject');
|
||||
|
||||
function Usage() {
|
||||
Log("Usage: create PathTONewProject [ PackageName AppName ]");
|
||||
Log(" PathTONewProject : The path to where you wish to create the project");
|
||||
Log(" PackageName : The package for the project (default is org.apache.cordova.example)")
|
||||
Log(" AppName : The name of the application/activity (default is cordovaExample)");
|
||||
Log("examples:");
|
||||
Log(" create C:\\Users\\anonymous\\Desktop\\MyProject");
|
||||
Log(" create C:\\Users\\anonymous\\Desktop\\MyProject io.Cordova.Example AnApp");
|
||||
}
|
||||
|
||||
// logs messaged to stdout and stderr
|
||||
function Log(msg, error) {
|
||||
if (error) {
|
||||
WScript.StdErr.WriteLine(msg);
|
||||
}
|
||||
else {
|
||||
WScript.StdOut.WriteLine(msg);
|
||||
}
|
||||
}
|
||||
|
||||
function read(filename) {
|
||||
var fso=WScript.CreateObject("Scripting.FileSystemObject");
|
||||
var f=fso.OpenTextFile(filename, 1);
|
||||
var s=f.ReadAll();
|
||||
f.Close();
|
||||
return s;
|
||||
}
|
||||
|
||||
function checkTargets(targets) {
|
||||
if(!targets) {
|
||||
Log("You do not have any android targets setup. Please create at least one target with the `android` command", true);
|
||||
WScript.Quit(69);
|
||||
}
|
||||
}
|
||||
|
||||
function setTarget() {
|
||||
var targets = shell.Exec('android.bat list targets').StdOut.ReadAll().match(/id:\s\d+/g);
|
||||
checkTargets(targets);
|
||||
return targets[targets.length - 1].replace(/id: /, ""); // TODO: give users the option to set their target
|
||||
}
|
||||
function setApiLevel() {
|
||||
var targets = shell.Exec('android.bat list targets').StdOut.ReadAll().match(/API level:\s\d+/g);
|
||||
checkTargets(targets);
|
||||
return targets[targets.length - 1].replace(/API level: /, "");
|
||||
}
|
||||
function write(filename, contents) {
|
||||
var fso=WScript.CreateObject("Scripting.FileSystemObject");
|
||||
var f=fso.OpenTextFile(filename, 2, true);
|
||||
f.Write(contents);
|
||||
f.Close();
|
||||
}
|
||||
function replaceInFile(filename, regexp, replacement) {
|
||||
write(filename, read(filename).replace(regexp, replacement));
|
||||
}
|
||||
function exec(command) {
|
||||
var oShell=shell.Exec(command);
|
||||
while (oShell.Status == 0) {
|
||||
if(!oShell.StdOut.AtEndOfStream) {
|
||||
var line = oShell.StdOut.ReadLine();
|
||||
// XXX: Change to verbose mode
|
||||
// WScript.StdOut.WriteLine(line);
|
||||
}
|
||||
WScript.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
function createAppInfoJar() {
|
||||
if(!fso.FileExists(ROOT+"\\bin\\templates\\cordova\\appinfo.jar")) {
|
||||
Log("Creating appinfo.jar...");
|
||||
var cur = shell.CurrentDirectory;
|
||||
shell.CurrentDirectory = ROOT+"\\bin\\templates\\cordova\\ApplicationInfo";
|
||||
exec("javac ApplicationInfo.java");
|
||||
exec("jar -cfe ..\\appinfo.jar ApplicationInfo ApplicationInfo.class");
|
||||
shell.CurrentDirectory = cur;
|
||||
}
|
||||
}
|
||||
|
||||
// working dir
|
||||
var ROOT = WScript.ScriptFullName.split('\\bin\\create.js').join('');
|
||||
if (args.Count() > 0) {
|
||||
// support help flags
|
||||
if (args(0) == "--help" || args(0) == "/?" ||
|
||||
args(0) == "help" || args(0) == "-help" || args(0) == "/help" || args(0) == "-h") {
|
||||
Usage();
|
||||
WScript.Quit(2);
|
||||
}
|
||||
|
||||
PROJECT_PATH=args(0);
|
||||
if (args.Count() > 1) {
|
||||
PACKAGE = args(1);
|
||||
}
|
||||
if (args.Count() > 2) {
|
||||
ACTIVITY = args(2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log("Error : No project path provided.");
|
||||
Usage();
|
||||
WScript.Quit(2);
|
||||
}
|
||||
|
||||
if(fso.FolderExists(PROJECT_PATH)) {
|
||||
Log("Project path already exists!", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
|
||||
var PACKAGE_AS_PATH=PACKAGE.replace(/\./g, '\\');
|
||||
var ACTIVITY_DIR=PROJECT_PATH + '\\src\\' + PACKAGE_AS_PATH;
|
||||
var SAFE_ACTIVITY = ACTIVITY.replace(/\W/g, '');
|
||||
var ACTIVITY_PATH=ACTIVITY_DIR+'\\'+SAFE_ACTIVITY+'.java';
|
||||
var MANIFEST_PATH=PROJECT_PATH+'\\AndroidManifest.xml';
|
||||
var STRINGS_PATH=PROJECT_PATH+'\\res\\values\\strings.xml';
|
||||
var TARGET=setTarget();
|
||||
var API_LEVEL=setApiLevel();
|
||||
var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
|
||||
// create the project
|
||||
Log("Creating new android project...");
|
||||
exec('android.bat create project --target "'+TARGET+'" --path "'+PROJECT_PATH+'" --package "'+PACKAGE+'" --activity "'+SAFE_ACTIVITY+'"');
|
||||
|
||||
// build from source. distro should have these files
|
||||
if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
|
||||
!fso.FileExists(ROOT+'\\cordova.js')) {
|
||||
Log("Building jar and js files...");
|
||||
// update the cordova framework project to a target that exists on this machine
|
||||
exec('android.bat update project --target "'+TARGET+'" --path "'+ROOT+'\\framework"');
|
||||
exec('ant.bat -f "'+ ROOT +'\\framework\\build.xml" jar');
|
||||
}
|
||||
|
||||
// copy in the project template
|
||||
Log("Copying template files...");
|
||||
exec('%comspec% /c xcopy "'+ ROOT + '\\bin\\templates\\project\\res" "'+PROJECT_PATH+'\\res\\" /E /Y');
|
||||
exec('%comspec% /c xcopy "'+ ROOT + '\\bin\\templates\\project\\assets" "'+PROJECT_PATH+'\\assets\\" /E /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\project\\AndroidManifest.xml" "' + PROJECT_PATH + '\\AndroidManifest.xml" /Y');
|
||||
exec('%comspec% /c mkdir "' + ACTIVITY_DIR + '"');
|
||||
exec('%comspec% /c copy "' + ROOT + '"\\bin\\templates\\project\\Activity.java "' + ACTIVITY_PATH + '" /Y');
|
||||
|
||||
// check if we have the source or the distro files
|
||||
Log("Copying js, jar & config.xml files...");
|
||||
if(fso.FolderExists(ROOT + '\\framework')) {
|
||||
exec('%comspec% /c copy "'+ROOT+'\\framework\\assets\\www\\cordova.js" "'+PROJECT_PATH+'\\assets\\www\\cordova.js" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\framework\\cordova-'+VERSION+'.jar" "'+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar" /Y');
|
||||
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\framework\\res\\xml\\config.xml" "' + PROJECT_PATH + '\\res\\xml\\config.xml" /Y');
|
||||
} else {
|
||||
// copy in cordova.js
|
||||
exec('%comspec% /c copy "'+ROOT+'\\cordova.js" "'+PROJECT_PATH+'\\assets\\www\\cordova.js" /Y');
|
||||
// copy in cordova.jar
|
||||
exec('%comspec% /c copy "'+ROOT+'\\cordova-'+VERSION+'.jar" "'+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar" /Y');
|
||||
// copy in xml
|
||||
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\xml\\config.xml" "' + PROJECT_PATH + '\\res\\xml\\config.xml" /Y');
|
||||
}
|
||||
|
||||
// copy cordova scripts
|
||||
fso.CreateFolder(PROJECT_PATH + '\\cordova');
|
||||
fso.CreateFolder(PROJECT_PATH + '\\cordova\\lib');
|
||||
createAppInfoJar();
|
||||
Log("Copying cordova command tools...");
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\appinfo.jar" "' + PROJECT_PATH + '\\cordova\\appinfo.jar" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\cordova.js" "' + PROJECT_PATH + '\\cordova\\lib\\cordova.js" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\install-device.bat" "' + PROJECT_PATH + '\\cordova\\lib\\install-device.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\install-emulator.bat" "' + PROJECT_PATH + '\\cordova\\lib\\install-emulator.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-emulator-images.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-emulator-images.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-devices.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-devices.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-started-emulators.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-started-emulators.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\start-emulator.bat" "' + PROJECT_PATH + '\\cordova\\lib\\start-emulator.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\cordova.bat" "' + PROJECT_PATH + '\\cordova\\cordova.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\clean.bat" "' + PROJECT_PATH + '\\cordova\\clean.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\build.bat" "' + PROJECT_PATH + '\\cordova\\build.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\log.bat" "' + PROJECT_PATH + '\\cordova\\log.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\run.bat" "' + PROJECT_PATH + '\\cordova\\run.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\version.bat" "' + PROJECT_PATH + '\\cordova\\version.bat" /Y');
|
||||
|
||||
// interpolate the activity name and package
|
||||
Log("Updating AndroidManifest.xml and Main Activity...");
|
||||
replaceInFile(ACTIVITY_PATH, /__ACTIVITY__/, ACTIVITY);
|
||||
replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
|
||||
|
||||
replaceInFile(MANIFEST_PATH, /__ACTIVITY__/, ACTIVITY);
|
||||
replaceInFile(MANIFEST_PATH, /__PACKAGE__/, PACKAGE);
|
||||
replaceInFile(MANIFEST_PATH, /__APILEVEL__/, API_LEVEL);
|
||||
|
||||
replaceInFile(STRINGS_PATH, new RegExp('>' + SAFE_ACTIVITY + '<'), '>' + ACTIVITY + '<');
|
@ -24,7 +24,24 @@ function dot {
|
||||
echo -n "."
|
||||
}
|
||||
|
||||
function wait_for_emulator {
|
||||
function wait_for_emulator() {
|
||||
local emulator_log_path="$1"
|
||||
local error_string
|
||||
local status
|
||||
|
||||
# Try to detect fatal errors early
|
||||
sleep 1.5
|
||||
error_string=$(grep -F "ERROR: " ${emulator_log_path})
|
||||
status=$?
|
||||
|
||||
if [ $status -eq 0 ]; then
|
||||
echo "Emulator failed to start, fatal error detected"
|
||||
echo "Error: ${error_string}"
|
||||
echo "Full log available at: ${emulator_log_path}"
|
||||
echo "Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local i="0"
|
||||
echo -n "Waiting for emulator"
|
||||
emulator_string=$($DIR/list-started-emulators)
|
||||
@ -70,22 +87,25 @@ if [ $? != 0 ]; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# start first emulator
|
||||
log_path=$(mktemp -t android_emulator)
|
||||
|
||||
# if target emulator is provided
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
# check that it exists
|
||||
if [[ $emulator_images =~ $1 ]] ; then
|
||||
#xterm -e emulator -avd $1 &
|
||||
emulator -avd $1 1> /dev/null 2>&1 &
|
||||
emulator -avd $1 1> "${log_path}" 2>&1 &
|
||||
else
|
||||
echo "Could not find the provided emulator '$1', make sure the emulator exists"
|
||||
echo " by checking 'cordova/lib/list-emulator-images'"
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
# start first emulator
|
||||
read -ra emulator_list <<< "$emulator_images"
|
||||
#xterm -e emulator -avd ${emulator_list[0]} &
|
||||
emulator -avd ${emulator_list[0]} 1> /dev/null 2>&1 &
|
||||
emulator -avd ${emulator_list[0]} 1> "${log_path}" 2>&1 &
|
||||
fi
|
||||
|
||||
wait_for_emulator
|
||||
echo "Saving emulator log to: ${log_path}"
|
||||
wait_for_emulator "$log_path"
|
||||
|
@ -16,17 +16,4 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
set -e
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd -P)
|
||||
PROJECT_PATH="$(dirname "$CORDOVA_PATH")"
|
||||
|
||||
VERSION_FILE_PATH="$PROJECT_PATH/assets/www/cordova.js"
|
||||
|
||||
if [ -f "$VERSION_FILE_PATH" ]; then
|
||||
JSVersion=$(sed -n '2,2p' $VERSION_FILE_PATH)
|
||||
echo $JSVersion | sed -e 's/\/\/ //'| cut -f 1 -d '-'
|
||||
else
|
||||
echo "The file \"$VERSION_FILE_PATH\" does not exist."
|
||||
exit 1
|
||||
fi
|
||||
echo "2.9.0"
|
||||
|
@ -59,5 +59,5 @@
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="__APILEVEL__"/>
|
||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="__APILEVEL__"/>
|
||||
</manifest>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 213 KiB |
Before Width: | Height: | Size: 217 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 478 KiB |
Before Width: | Height: | Size: 493 KiB |
@ -1,68 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Jasmine Spec Runner</title>
|
||||
|
||||
<!-- jasmine source -->
|
||||
<link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
|
||||
<script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
|
||||
<script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
|
||||
|
||||
<!-- include source files here... -->
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
|
||||
<!-- include spec files here... -->
|
||||
<script type="text/javascript" src="spec/helper.js"></script>
|
||||
<script type="text/javascript" src="spec/index.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var jasmineEnv = jasmine.getEnv();
|
||||
jasmineEnv.updateInterval = 1000;
|
||||
|
||||
var htmlReporter = new jasmine.HtmlReporter();
|
||||
|
||||
jasmineEnv.addReporter(htmlReporter);
|
||||
|
||||
jasmineEnv.specFilter = function(spec) {
|
||||
return htmlReporter.specFilter(spec);
|
||||
};
|
||||
|
||||
var currentWindowOnload = window.onload;
|
||||
|
||||
window.onload = function() {
|
||||
if (currentWindowOnload) {
|
||||
currentWindowOnload();
|
||||
}
|
||||
execJasmine();
|
||||
};
|
||||
|
||||
function execJasmine() {
|
||||
jasmineEnv.execute();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="stage" style="display:none;"></div>
|
||||
</body>
|
||||
</html>
|
@ -1,33 +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.
|
||||
*/
|
||||
afterEach(function() {
|
||||
document.getElementById('stage').innerHTML = '';
|
||||
});
|
||||
|
||||
var helper = {
|
||||
trigger: function(obj, name) {
|
||||
var e = document.createEvent('Event');
|
||||
e.initEvent(name, true, true);
|
||||
obj.dispatchEvent(e);
|
||||
},
|
||||
getComputedStyle: function(querySelector, property) {
|
||||
var element = document.querySelector(querySelector);
|
||||
return window.getComputedStyle(element).getPropertyValue(property);
|
||||
}
|
||||
};
|
@ -1,67 +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.
|
||||
*/
|
||||
describe('app', function() {
|
||||
describe('initialize', function() {
|
||||
it('should bind deviceready', function() {
|
||||
runs(function() {
|
||||
spyOn(app, 'onDeviceReady');
|
||||
app.initialize();
|
||||
helper.trigger(window.document, 'deviceready');
|
||||
});
|
||||
|
||||
waitsFor(function() {
|
||||
return (app.onDeviceReady.calls.length > 0);
|
||||
}, 'onDeviceReady should be called once', 500);
|
||||
|
||||
runs(function() {
|
||||
expect(app.onDeviceReady).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('onDeviceReady', function() {
|
||||
it('should report that it fired', function() {
|
||||
spyOn(app, 'receivedEvent');
|
||||
app.onDeviceReady();
|
||||
expect(app.receivedEvent).toHaveBeenCalledWith('deviceready');
|
||||
});
|
||||
});
|
||||
|
||||
describe('receivedEvent', function() {
|
||||
beforeEach(function() {
|
||||
var el = document.getElementById('stage');
|
||||
el.innerHTML = ['<div id="deviceready">',
|
||||
' <p class="event listening">Listening</p>',
|
||||
' <p class="event received">Received</p>',
|
||||
'</div>'].join('\n');
|
||||
});
|
||||
|
||||
it('should hide the listening element', function() {
|
||||
app.receivedEvent('deviceready');
|
||||
var displayStyle = helper.getComputedStyle('#deviceready .listening', 'display');
|
||||
expect(displayStyle).toEqual('none');
|
||||
});
|
||||
|
||||
it('should show the received element', function() {
|
||||
app.receivedEvent('deviceready');
|
||||
var displayStyle = helper.getComputedStyle('#deviceready .received', 'display');
|
||||
expect(displayStyle).toEqual('block');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,20 +0,0 @@
|
||||
Copyright (c) 2008-2011 Pivotal Labs
|
||||
|
||||
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.
|
@ -1,616 +0,0 @@
|
||||
jasmine.HtmlReporterHelpers = {};
|
||||
|
||||
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) {
|
||||
el.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
|
||||
var results = child.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
|
||||
var parentDiv = this.dom.summary;
|
||||
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
|
||||
var parent = child[parentSuite];
|
||||
|
||||
if (parent) {
|
||||
if (typeof this.views.suites[parent.id] == 'undefined') {
|
||||
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
|
||||
}
|
||||
parentDiv = this.views.suites[parent.id].element;
|
||||
}
|
||||
|
||||
parentDiv.appendChild(childElement);
|
||||
};
|
||||
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
|
||||
for(var fn in jasmine.HtmlReporterHelpers) {
|
||||
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter = function(_doc) {
|
||||
var self = this;
|
||||
var doc = _doc || window.document;
|
||||
|
||||
var reporterView;
|
||||
|
||||
var dom = {};
|
||||
|
||||
// Jasmine Reporter Public Interface
|
||||
self.logRunningSpecs = false;
|
||||
|
||||
self.reportRunnerStarting = function(runner) {
|
||||
var specs = runner.specs() || [];
|
||||
|
||||
if (specs.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
createReporterDom(runner.env.versionString());
|
||||
doc.body.appendChild(dom.reporter);
|
||||
|
||||
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
|
||||
reporterView.addSpecs(specs, self.specFilter);
|
||||
};
|
||||
|
||||
self.reportRunnerResults = function(runner) {
|
||||
reporterView && reporterView.complete();
|
||||
};
|
||||
|
||||
self.reportSuiteResults = function(suite) {
|
||||
reporterView.suiteComplete(suite);
|
||||
};
|
||||
|
||||
self.reportSpecStarting = function(spec) {
|
||||
if (self.logRunningSpecs) {
|
||||
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
self.reportSpecResults = function(spec) {
|
||||
reporterView.specComplete(spec);
|
||||
};
|
||||
|
||||
self.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.specFilter = function(spec) {
|
||||
if (!focusedSpecName()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return spec.getFullName().indexOf(focusedSpecName()) === 0;
|
||||
};
|
||||
|
||||
return self;
|
||||
|
||||
function focusedSpecName() {
|
||||
var specName;
|
||||
|
||||
(function memoizeFocusedSpec() {
|
||||
if (specName) {
|
||||
return;
|
||||
}
|
||||
|
||||
var paramMap = [];
|
||||
var params = doc.location.search.substring(1).split('&');
|
||||
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
specName = paramMap.spec;
|
||||
})();
|
||||
|
||||
return specName;
|
||||
}
|
||||
|
||||
function createReporterDom(version) {
|
||||
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
|
||||
dom.banner = self.createDom('div', { className: 'banner' },
|
||||
self.createDom('span', { className: 'title' }, "Jasmine "),
|
||||
self.createDom('span', { className: 'version' }, version)),
|
||||
|
||||
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
|
||||
dom.alert = self.createDom('div', {className: 'alert'}),
|
||||
dom.results = self.createDom('div', {className: 'results'},
|
||||
dom.summary = self.createDom('div', { className: 'summary' }),
|
||||
dom.details = self.createDom('div', { id: 'details' }))
|
||||
);
|
||||
}
|
||||
};
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) {
|
||||
this.startedAt = new Date();
|
||||
this.runningSpecCount = 0;
|
||||
this.completeSpecCount = 0;
|
||||
this.passedCount = 0;
|
||||
this.failedCount = 0;
|
||||
this.skippedCount = 0;
|
||||
|
||||
this.createResultsMenu = function() {
|
||||
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
|
||||
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
|
||||
' | ',
|
||||
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
|
||||
|
||||
this.summaryMenuItem.onclick = function() {
|
||||
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
|
||||
};
|
||||
|
||||
this.detailsMenuItem.onclick = function() {
|
||||
showDetails();
|
||||
};
|
||||
};
|
||||
|
||||
this.addSpecs = function(specs, specFilter) {
|
||||
this.totalSpecCount = specs.length;
|
||||
|
||||
this.views = {
|
||||
specs: {},
|
||||
suites: {}
|
||||
};
|
||||
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
var spec = specs[i];
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
|
||||
if (specFilter(spec)) {
|
||||
this.runningSpecCount++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.specComplete = function(spec) {
|
||||
this.completeSpecCount++;
|
||||
|
||||
if (isUndefined(this.views.specs[spec.id])) {
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
|
||||
}
|
||||
|
||||
var specView = this.views.specs[spec.id];
|
||||
|
||||
switch (specView.status()) {
|
||||
case 'passed':
|
||||
this.passedCount++;
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.failedCount++;
|
||||
break;
|
||||
|
||||
case 'skipped':
|
||||
this.skippedCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
specView.refresh();
|
||||
this.refresh();
|
||||
};
|
||||
|
||||
this.suiteComplete = function(suite) {
|
||||
var suiteView = this.views.suites[suite.id];
|
||||
if (isUndefined(suiteView)) {
|
||||
return;
|
||||
}
|
||||
suiteView.refresh();
|
||||
};
|
||||
|
||||
this.refresh = function() {
|
||||
|
||||
if (isUndefined(this.resultsMenu)) {
|
||||
this.createResultsMenu();
|
||||
}
|
||||
|
||||
// currently running UI
|
||||
if (isUndefined(this.runningAlert)) {
|
||||
this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
|
||||
dom.alert.appendChild(this.runningAlert);
|
||||
}
|
||||
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
|
||||
|
||||
// skipped specs UI
|
||||
if (isUndefined(this.skippedAlert)) {
|
||||
this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
|
||||
}
|
||||
|
||||
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.skippedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.skippedAlert);
|
||||
}
|
||||
|
||||
// passing specs UI
|
||||
if (isUndefined(this.passedAlert)) {
|
||||
this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
|
||||
}
|
||||
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
|
||||
|
||||
// failing specs UI
|
||||
if (isUndefined(this.failedAlert)) {
|
||||
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
|
||||
}
|
||||
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
|
||||
|
||||
if (this.failedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.failedAlert);
|
||||
dom.alert.appendChild(this.resultsMenu);
|
||||
}
|
||||
|
||||
// summary info
|
||||
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
|
||||
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
|
||||
};
|
||||
|
||||
this.complete = function() {
|
||||
dom.alert.removeChild(this.runningAlert);
|
||||
|
||||
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.failedCount === 0) {
|
||||
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
|
||||
} else {
|
||||
showDetails();
|
||||
}
|
||||
|
||||
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function showDetails() {
|
||||
if (dom.reporter.className.search(/showDetails/) === -1) {
|
||||
dom.reporter.className += " showDetails";
|
||||
}
|
||||
}
|
||||
|
||||
function isUndefined(obj) {
|
||||
return typeof obj === 'undefined';
|
||||
}
|
||||
|
||||
function isDefined(obj) {
|
||||
return !isUndefined(obj);
|
||||
}
|
||||
|
||||
function specPluralizedFor(count) {
|
||||
var str = count + " spec";
|
||||
if (count > 1) {
|
||||
str += "s"
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
|
||||
|
||||
|
||||
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
|
||||
this.spec = spec;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.symbol = this.createDom('li', { className: 'pending' });
|
||||
this.dom.symbolSummary.appendChild(this.symbol);
|
||||
|
||||
this.summary = this.createDom('div', { className: 'specSummary' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.description)
|
||||
);
|
||||
|
||||
this.detail = this.createDom('div', { className: 'specDetail' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.getFullName())
|
||||
);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.spec);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
|
||||
this.symbol.className = this.status();
|
||||
|
||||
switch (this.status()) {
|
||||
case 'skipped':
|
||||
break;
|
||||
|
||||
case 'passed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
this.appendFailureDetail();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
|
||||
this.summary.className += ' ' + this.status();
|
||||
this.appendToSummary(this.spec, this.summary);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
|
||||
this.detail.className += ' ' + this.status();
|
||||
|
||||
var resultItems = this.spec.results().getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
this.detail.appendChild(messagesDiv);
|
||||
this.dom.details.appendChild(this.detail);
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
|
||||
this.suite = suite;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.element = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
|
||||
);
|
||||
|
||||
this.appendToSummary(this.suite, this.element);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.suite);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
|
||||
this.element.className += " " + this.status();
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
|
||||
|
||||
/* @deprecated Use jasmine.HtmlReporter instead
|
||||
*/
|
||||
jasmine.TrivialReporter = function(doc) {
|
||||
this.document = doc || document;
|
||||
this.suiteDivs = {};
|
||||
this.logRunningSpecs = false;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) { el.appendChild(child); }
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
|
||||
var showPassed, showSkipped;
|
||||
|
||||
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
|
||||
this.createDom('div', { className: 'banner' },
|
||||
this.createDom('div', { className: 'logo' },
|
||||
this.createDom('span', { className: 'title' }, "Jasmine"),
|
||||
this.createDom('span', { className: 'version' }, runner.env.versionString())),
|
||||
this.createDom('div', { className: 'options' },
|
||||
"Show ",
|
||||
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
|
||||
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
|
||||
)
|
||||
),
|
||||
|
||||
this.runnerDiv = this.createDom('div', { className: 'runner running' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
|
||||
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
|
||||
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
|
||||
);
|
||||
|
||||
this.document.body.appendChild(this.outerDiv);
|
||||
|
||||
var suites = runner.suites();
|
||||
for (var i = 0; i < suites.length; i++) {
|
||||
var suite = suites[i];
|
||||
var suiteDiv = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
|
||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
|
||||
this.suiteDivs[suite.id] = suiteDiv;
|
||||
var parentDiv = this.outerDiv;
|
||||
if (suite.parentSuite) {
|
||||
parentDiv = this.suiteDivs[suite.parentSuite.id];
|
||||
}
|
||||
parentDiv.appendChild(suiteDiv);
|
||||
}
|
||||
|
||||
this.startedAt = new Date();
|
||||
|
||||
var self = this;
|
||||
showPassed.onclick = function(evt) {
|
||||
if (showPassed.checked) {
|
||||
self.outerDiv.className += ' show-passed';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
|
||||
}
|
||||
};
|
||||
|
||||
showSkipped.onclick = function(evt) {
|
||||
if (showSkipped.checked) {
|
||||
self.outerDiv.className += ' show-skipped';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
|
||||
var results = runner.results();
|
||||
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
|
||||
this.runnerDiv.setAttribute("class", className);
|
||||
//do it twice for IE
|
||||
this.runnerDiv.setAttribute("className", className);
|
||||
var specs = runner.specs();
|
||||
var specCount = 0;
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
if (this.specFilter(specs[i])) {
|
||||
specCount++;
|
||||
}
|
||||
}
|
||||
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
|
||||
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
|
||||
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
|
||||
|
||||
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
|
||||
var results = suite.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.totalCount === 0) { // todo: change this to check results.skipped
|
||||
status = 'skipped';
|
||||
}
|
||||
this.suiteDivs[suite.id].className += " " + status;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
|
||||
if (this.logRunningSpecs) {
|
||||
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
|
||||
var results = spec.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
var specDiv = this.createDom('div', { className: 'spec ' + status },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(spec.getFullName()),
|
||||
title: spec.getFullName()
|
||||
}, spec.description));
|
||||
|
||||
|
||||
var resultItems = results.getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
specDiv.appendChild(messagesDiv);
|
||||
}
|
||||
|
||||
this.suiteDivs[spec.suite.id].appendChild(specDiv);
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.getLocation = function() {
|
||||
return this.document.location;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
|
||||
var paramMap = {};
|
||||
var params = this.getLocation().search.substring(1).split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
if (!paramMap.spec) {
|
||||
return true;
|
||||
}
|
||||
return spec.getFullName().indexOf(paramMap.spec) === 0;
|
||||
};
|
@ -1,81 +0,0 @@
|
||||
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
|
||||
|
||||
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
|
||||
#HTMLReporter a { text-decoration: none; }
|
||||
#HTMLReporter a:hover { text-decoration: underline; }
|
||||
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
|
||||
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
|
||||
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#HTMLReporter .version { color: #aaaaaa; }
|
||||
#HTMLReporter .banner { margin-top: 14px; }
|
||||
#HTMLReporter .duration { color: #aaaaaa; float: right; }
|
||||
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
|
||||
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
|
||||
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
|
||||
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
|
||||
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
|
||||
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
|
||||
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
|
||||
#HTMLReporter .runningAlert { background-color: #666666; }
|
||||
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
|
||||
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
|
||||
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
|
||||
#HTMLReporter .passingAlert { background-color: #a6b779; }
|
||||
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
|
||||
#HTMLReporter .failingAlert { background-color: #cf867e; }
|
||||
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
|
||||
#HTMLReporter .results { margin-top: 14px; }
|
||||
#HTMLReporter #details { display: none; }
|
||||
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .summary { display: none; }
|
||||
#HTMLReporter.showDetails #details { display: block; }
|
||||
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter .summary { margin-top: 14px; }
|
||||
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
|
||||
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
|
||||
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
|
||||
#HTMLReporter .description + .suite { margin-top: 0; }
|
||||
#HTMLReporter .suite { margin-top: 14px; }
|
||||
#HTMLReporter .suite a { color: #333333; }
|
||||
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
|
||||
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
|
||||
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
|
||||
#HTMLReporter .resultMessage span.result { display: block; }
|
||||
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
|
||||
|
||||
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
|
||||
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
|
||||
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
|
||||
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
|
||||
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
|
||||
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
|
||||
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
|
||||
#TrivialReporter .runner.running { background-color: yellow; }
|
||||
#TrivialReporter .options { text-align: right; font-size: .8em; }
|
||||
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
|
||||
#TrivialReporter .suite .suite { margin: 5px; }
|
||||
#TrivialReporter .suite.passed { background-color: #dfd; }
|
||||
#TrivialReporter .suite.failed { background-color: #fdd; }
|
||||
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
|
||||
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
|
||||
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
|
||||
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
|
||||
#TrivialReporter .spec.skipped { background-color: #bbb; }
|
||||
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
|
||||
#TrivialReporter .passed { background-color: #cfc; display: none; }
|
||||
#TrivialReporter .failed { background-color: #fbb; }
|
||||
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
|
||||
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
|
||||
#TrivialReporter .resultMessage .mismatch { color: black; }
|
||||
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
|
||||
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
|
||||
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
|
||||
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
|
661
framework/assets/www/cordova.js
vendored
@ -1,5 +1,5 @@
|
||||
// Platform: android
|
||||
// 2.9.0rc1-0-g002f33d
|
||||
// 2.9.1
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@ -19,7 +19,7 @@
|
||||
under the License.
|
||||
*/
|
||||
;(function() {
|
||||
var CORDOVA_JS_BUILD_LABEL = '2.9.0rc1-0-g002f33d';
|
||||
var CORDOVA_JS_BUILD_LABEL = '2.9.1';
|
||||
// file: lib/scripts/require.js
|
||||
|
||||
var require,
|
||||
@ -100,6 +100,7 @@ define("cordova", function(require, exports, module) {
|
||||
|
||||
|
||||
var channel = require('cordova/channel');
|
||||
var platform = require('cordova/platform');
|
||||
|
||||
/**
|
||||
* Listen for DOMContentLoaded and notify our channel subscribers.
|
||||
@ -182,10 +183,18 @@ if(typeof window.console === "undefined") {
|
||||
log:function(){}
|
||||
};
|
||||
}
|
||||
// there are places in the framework where we call `warn` also, so we should make sure it exists
|
||||
if(typeof window.console.warn === "undefined") {
|
||||
window.console.warn = function(msg) {
|
||||
this.log("warn: " + msg);
|
||||
};
|
||||
}
|
||||
|
||||
var cordova = {
|
||||
define:define,
|
||||
require:require,
|
||||
version:CORDOVA_JS_BUILD_LABEL,
|
||||
platformId:platform.id,
|
||||
/**
|
||||
* Methods to add/remove your own addEventListener hijacking on document + window.
|
||||
*/
|
||||
@ -221,16 +230,16 @@ var cordova = {
|
||||
var evt = createEvent(type, data);
|
||||
if (typeof documentEventHandlers[type] != 'undefined') {
|
||||
if( bNoDetach ) {
|
||||
documentEventHandlers[type].fire(evt);
|
||||
documentEventHandlers[type].fire(evt);
|
||||
}
|
||||
else {
|
||||
setTimeout(function() {
|
||||
// Fire deviceready on listeners that were registered before cordova.js was loaded.
|
||||
if (type == 'deviceready') {
|
||||
document.dispatchEvent(evt);
|
||||
}
|
||||
documentEventHandlers[type].fire(evt);
|
||||
}, 0);
|
||||
setTimeout(function() {
|
||||
// Fire deviceready on listeners that were registered before cordova.js was loaded.
|
||||
if (type == 'deviceready') {
|
||||
document.dispatchEvent(evt);
|
||||
}
|
||||
documentEventHandlers[type].fire(evt);
|
||||
}, 0);
|
||||
}
|
||||
} else {
|
||||
document.dispatchEvent(evt);
|
||||
@ -347,7 +356,7 @@ var typeMap = {
|
||||
};
|
||||
|
||||
function extractParamName(callee, argIndex) {
|
||||
return (/.*?\((.*?)\)/).exec(callee)[1].split(', ')[argIndex];
|
||||
return (/.*?\((.*?)\)/).exec(callee)[1].split(', ')[argIndex];
|
||||
}
|
||||
|
||||
function checkArgs(spec, functionName, args, opt_callee) {
|
||||
@ -376,7 +385,7 @@ function checkArgs(spec, functionName, args, opt_callee) {
|
||||
if (errMsg) {
|
||||
errMsg += ', but got ' + typeName + '.';
|
||||
errMsg = 'Wrong type for parameter "' + extractParamName(opt_callee || args.callee, i) + '" of ' + functionName + ': ' + errMsg;
|
||||
// Don't log when running jake test.
|
||||
// Don't log when running unit tests.
|
||||
if (typeof jasmine == 'undefined') {
|
||||
console.error(errMsg);
|
||||
}
|
||||
@ -393,6 +402,62 @@ moduleExports.getValue = getValue;
|
||||
moduleExports.enableChecks = true;
|
||||
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/base64.js
|
||||
define("cordova/base64", function(require, exports, module) {
|
||||
|
||||
var base64 = exports;
|
||||
|
||||
base64.fromArrayBuffer = function(arrayBuffer) {
|
||||
var array = new Uint8Array(arrayBuffer);
|
||||
return uint8ToBase64(array);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* This code is based on the performance tests at http://jsperf.com/b64tests
|
||||
* This 12-bit-at-a-time algorithm was the best performing version on all
|
||||
* platforms tested.
|
||||
*/
|
||||
|
||||
var b64_6bit = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var b64_12bit;
|
||||
|
||||
var b64_12bitTable = function() {
|
||||
b64_12bit = [];
|
||||
for (var i=0; i<64; i++) {
|
||||
for (var j=0; j<64; j++) {
|
||||
b64_12bit[i*64+j] = b64_6bit[i] + b64_6bit[j];
|
||||
}
|
||||
}
|
||||
b64_12bitTable = function() { return b64_12bit; };
|
||||
return b64_12bit;
|
||||
};
|
||||
|
||||
function uint8ToBase64(rawData) {
|
||||
var numBytes = rawData.byteLength;
|
||||
var output="";
|
||||
var segment;
|
||||
var table = b64_12bitTable();
|
||||
for (var i=0;i<numBytes-2;i+=3) {
|
||||
segment = (rawData[i] << 16) + (rawData[i+1] << 8) + rawData[i+2];
|
||||
output += table[segment >> 12];
|
||||
output += table[segment & 0xfff];
|
||||
}
|
||||
if (numBytes - i == 2) {
|
||||
segment = (rawData[i] << 16) + (rawData[i+1] << 8);
|
||||
output += table[segment >> 12];
|
||||
output += b64_6bit[(segment & 0xfff) >> 6];
|
||||
output += '=';
|
||||
} else if (numBytes - i == 1) {
|
||||
segment = (rawData[i] << 16);
|
||||
output += table[segment >> 12];
|
||||
output += '==';
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/builder.js
|
||||
@ -435,36 +500,36 @@ function assignOrWrapInDeprecateGetter(obj, key, value, message) {
|
||||
function include(parent, objects, clobber, merge) {
|
||||
each(objects, function (obj, key) {
|
||||
try {
|
||||
var result = obj.path ? require(obj.path) : {};
|
||||
var result = obj.path ? require(obj.path) : {};
|
||||
|
||||
if (clobber) {
|
||||
// Clobber if it doesn't exist.
|
||||
if (typeof parent[key] === 'undefined') {
|
||||
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
|
||||
} else if (typeof obj.path !== 'undefined') {
|
||||
// If merging, merge properties onto parent, otherwise, clobber.
|
||||
if (merge) {
|
||||
recursiveMerge(parent[key], result);
|
||||
} else {
|
||||
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
|
||||
}
|
||||
}
|
||||
result = parent[key];
|
||||
} else {
|
||||
// Overwrite if not currently defined.
|
||||
if (typeof parent[key] == 'undefined') {
|
||||
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
|
||||
if (clobber) {
|
||||
// Clobber if it doesn't exist.
|
||||
if (typeof parent[key] === 'undefined') {
|
||||
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
|
||||
} else if (typeof obj.path !== 'undefined') {
|
||||
// If merging, merge properties onto parent, otherwise, clobber.
|
||||
if (merge) {
|
||||
recursiveMerge(parent[key], result);
|
||||
} else {
|
||||
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
|
||||
}
|
||||
}
|
||||
result = parent[key];
|
||||
} else {
|
||||
// Set result to what already exists, so we can build children into it if they exist.
|
||||
result = parent[key];
|
||||
// Overwrite if not currently defined.
|
||||
if (typeof parent[key] == 'undefined') {
|
||||
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
|
||||
} else {
|
||||
// Set result to what already exists, so we can build children into it if they exist.
|
||||
result = parent[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.children) {
|
||||
include(result, obj.children, clobber, merge);
|
||||
}
|
||||
if (obj.children) {
|
||||
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 + '"');
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -808,6 +873,7 @@ define("cordova/exec", function(require, exports, module) {
|
||||
var cordova = require('cordova'),
|
||||
nativeApiProvider = require('cordova/plugin/android/nativeapiprovider'),
|
||||
utils = require('cordova/utils'),
|
||||
base64 = require('cordova/base64'),
|
||||
jsToNativeModes = {
|
||||
PROMPT: 0,
|
||||
JS_OBJECT: 1,
|
||||
@ -846,7 +912,7 @@ function androidExec(success, fail, service, action, args) {
|
||||
// Process any ArrayBuffers in the args into a string.
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
if (utils.typeName(args[i]) == 'ArrayBuffer') {
|
||||
args[i] = window.btoa(String.fromCharCode.apply(null, new Uint8Array(args[i])));
|
||||
args[i] = base64.fromArrayBuffer(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -874,8 +940,12 @@ function androidExec(success, fail, service, action, args) {
|
||||
}
|
||||
}
|
||||
|
||||
function pollOnce() {
|
||||
var msg = nativeApiProvider.get().retrieveJsMessages();
|
||||
function pollOnceFromOnlineEvent() {
|
||||
pollOnce(true);
|
||||
}
|
||||
|
||||
function pollOnce(opt_fromOnlineEvent) {
|
||||
var msg = nativeApiProvider.get().retrieveJsMessages(!!opt_fromOnlineEvent);
|
||||
androidExec.processMessages(msg);
|
||||
}
|
||||
|
||||
@ -894,8 +964,8 @@ function hookOnlineApis() {
|
||||
// It currently fires them only on document though, so we bridge them
|
||||
// to window here (while first listening for exec()-releated online/offline
|
||||
// events).
|
||||
window.addEventListener('online', pollOnce, false);
|
||||
window.addEventListener('offline', pollOnce, false);
|
||||
window.addEventListener('online', pollOnceFromOnlineEvent, false);
|
||||
window.addEventListener('offline', pollOnceFromOnlineEvent, false);
|
||||
cordova.addWindowEventHandler('online');
|
||||
cordova.addWindowEventHandler('offline');
|
||||
document.addEventListener('online', proxyEvent, false);
|
||||
@ -1056,6 +1126,10 @@ exports.defaults = function(moduleName, symbolPath, opt_deprecationMessage) {
|
||||
addEntry('d', moduleName, symbolPath, opt_deprecationMessage);
|
||||
};
|
||||
|
||||
exports.runs = function(moduleName) {
|
||||
addEntry('r', moduleName, null);
|
||||
};
|
||||
|
||||
function prepareNamespace(symbolPath, context) {
|
||||
if (!symbolPath) {
|
||||
return context;
|
||||
@ -1074,12 +1148,16 @@ exports.mapModules = function(context) {
|
||||
for (var i = 0, len = symbolList.length; i < len; i += 3) {
|
||||
var strategy = symbolList[i];
|
||||
var moduleName = symbolList[i + 1];
|
||||
var module = require(moduleName);
|
||||
// <runs/>
|
||||
if (strategy == 'r') {
|
||||
continue;
|
||||
}
|
||||
var symbolPath = symbolList[i + 2];
|
||||
var lastDot = symbolPath.lastIndexOf('.');
|
||||
var namespace = symbolPath.substr(0, lastDot);
|
||||
var lastName = symbolPath.substr(lastDot + 1);
|
||||
|
||||
var module = require(moduleName);
|
||||
var deprecationMsg = symbolPath in deprecationMap ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg : null;
|
||||
var parentObj = prepareNamespace(namespace, context);
|
||||
var target = parentObj[lastName];
|
||||
@ -1928,6 +2006,7 @@ var exec = require('cordova/exec'),
|
||||
*/
|
||||
function DirectoryReader(path) {
|
||||
this.path = path || null;
|
||||
this.hasReadEntries = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1937,6 +2016,12 @@ function DirectoryReader(path) {
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
|
||||
// If we've already read and passed on this directory's entries, return an empty list.
|
||||
if (this.hasReadEntries) {
|
||||
successCallback([]);
|
||||
return;
|
||||
}
|
||||
var reader = this;
|
||||
var win = typeof successCallback !== 'function' ? null : function(result) {
|
||||
var retVal = [];
|
||||
for (var i=0; i<result.length; i++) {
|
||||
@ -1953,6 +2038,7 @@ DirectoryReader.prototype.readEntries = function(successCallback, errorCallback)
|
||||
entry.fullPath = result[i].fullPath;
|
||||
retVal.push(entry);
|
||||
}
|
||||
reader.hasReadEntries = true;
|
||||
successCallback(retVal);
|
||||
};
|
||||
var fail = typeof errorCallback !== 'function' ? null : function(code) {
|
||||
@ -3063,27 +3149,28 @@ FileWriter.prototype.abort = function() {
|
||||
*/
|
||||
FileWriter.prototype.write = function(data) {
|
||||
|
||||
var isBinary = false;
|
||||
var that=this;
|
||||
var supportsBinary = (typeof window.Blob !== 'undefined' && typeof window.ArrayBuffer !== 'undefined');
|
||||
var isBinary;
|
||||
|
||||
// If we don't have Blob or ArrayBuffer support, don't bother.
|
||||
if (typeof window.Blob !== 'undefined' && typeof window.ArrayBuffer !== 'undefined') {
|
||||
|
||||
// Check to see if the incoming data is a blob
|
||||
if (data instanceof Blob) {
|
||||
var that=this;
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function() {
|
||||
// Call this method again, with the arraybuffer as argument
|
||||
FileWriter.prototype.write.call(that, this.result);
|
||||
};
|
||||
// Check to see if the incoming data is a blob
|
||||
if (data instanceof File || (supportsBinary && data instanceof Blob)) {
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function() {
|
||||
// Call this method again, with the arraybuffer as argument
|
||||
FileWriter.prototype.write.call(that, this.result);
|
||||
};
|
||||
if (supportsBinary) {
|
||||
fileReader.readAsArrayBuffer(data);
|
||||
return;
|
||||
} else {
|
||||
fileReader.readAsText(data);
|
||||
}
|
||||
|
||||
// Mark data type for safer transport over the binary bridge
|
||||
isBinary = (data instanceof ArrayBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark data type for safer transport over the binary bridge
|
||||
isBinary = supportsBinary && (data instanceof ArrayBuffer);
|
||||
|
||||
// Throw an exception if we are already writing a file
|
||||
if (this.readyState === FileWriter.WRITING) {
|
||||
throw new FileError(FileError.INVALID_STATE_ERR);
|
||||
@ -3316,6 +3403,7 @@ define("cordova/plugin/InAppBrowser", function(require, exports, module) {
|
||||
var exec = require('cordova/exec');
|
||||
var channel = require('cordova/channel');
|
||||
var modulemapper = require('cordova/modulemapper');
|
||||
var urlutil = require('cordova/urlutil');
|
||||
|
||||
function InAppBrowser() {
|
||||
this.channels = {
|
||||
@ -3324,6 +3412,7 @@ function InAppBrowser() {
|
||||
'loaderror' : channel.create('loaderror'),
|
||||
'exit' : channel.create('exit')
|
||||
};
|
||||
this._alive = true;
|
||||
}
|
||||
|
||||
InAppBrowser.prototype = {
|
||||
@ -3333,7 +3422,10 @@ InAppBrowser.prototype = {
|
||||
}
|
||||
},
|
||||
close: function (eventname) {
|
||||
exec(null, null, "InAppBrowser", "close", []);
|
||||
if (this._alive) {
|
||||
this._alive = false;
|
||||
exec(null, null, "InAppBrowser", "close", []);
|
||||
}
|
||||
},
|
||||
show: function (eventname) {
|
||||
exec(null, null, "InAppBrowser", "show", []);
|
||||
@ -3371,17 +3463,18 @@ InAppBrowser.prototype = {
|
||||
};
|
||||
|
||||
module.exports = function(strUrl, strWindowName, strWindowFeatures) {
|
||||
var iab = new InAppBrowser();
|
||||
var cb = function(eventname) {
|
||||
iab._eventHandler(eventname);
|
||||
};
|
||||
|
||||
// Don't catch calls that write to existing frames (e.g. named iframes).
|
||||
if (window.frames && window.frames[strWindowName]) {
|
||||
var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
|
||||
return origOpenFunc.apply(window, arguments);
|
||||
}
|
||||
|
||||
strUrl = urlutil.makeAbsolute(strUrl);
|
||||
var iab = new InAppBrowser();
|
||||
var cb = function(eventname) {
|
||||
iab._eventHandler(eventname);
|
||||
};
|
||||
|
||||
exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
|
||||
return iab;
|
||||
};
|
||||
@ -3973,73 +4066,73 @@ define("cordova/plugin/android/app", function(require, exports, module) {
|
||||
var exec = require('cordova/exec');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Clear the resource cache.
|
||||
*/
|
||||
clearCache:function() {
|
||||
exec(null, null, "App", "clearCache", []);
|
||||
},
|
||||
/**
|
||||
* Clear the resource cache.
|
||||
*/
|
||||
clearCache:function() {
|
||||
exec(null, null, "App", "clearCache", []);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the url into the webview or into new browser instance.
|
||||
*
|
||||
* @param url The URL to load
|
||||
* @param props Properties that can be passed in to the activity:
|
||||
* wait: int => wait msec before loading URL
|
||||
* loadingDialog: "Title,Message" => display a native loading dialog
|
||||
* loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
|
||||
* clearHistory: boolean => clear webview history (default=false)
|
||||
* openExternal: boolean => open in a new browser (default=false)
|
||||
*
|
||||
* Example:
|
||||
* navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
|
||||
*/
|
||||
loadUrl:function(url, props) {
|
||||
exec(null, null, "App", "loadUrl", [url, props]);
|
||||
},
|
||||
/**
|
||||
* Load the url into the webview or into new browser instance.
|
||||
*
|
||||
* @param url The URL to load
|
||||
* @param props Properties that can be passed in to the activity:
|
||||
* wait: int => wait msec before loading URL
|
||||
* loadingDialog: "Title,Message" => display a native loading dialog
|
||||
* loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
|
||||
* clearHistory: boolean => clear webview history (default=false)
|
||||
* openExternal: boolean => open in a new browser (default=false)
|
||||
*
|
||||
* Example:
|
||||
* navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
|
||||
*/
|
||||
loadUrl:function(url, props) {
|
||||
exec(null, null, "App", "loadUrl", [url, props]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancel loadUrl that is waiting to be loaded.
|
||||
*/
|
||||
cancelLoadUrl:function() {
|
||||
exec(null, null, "App", "cancelLoadUrl", []);
|
||||
},
|
||||
/**
|
||||
* Cancel loadUrl that is waiting to be loaded.
|
||||
*/
|
||||
cancelLoadUrl:function() {
|
||||
exec(null, null, "App", "cancelLoadUrl", []);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear web history in this web view.
|
||||
* Instead of BACK button loading the previous web page, it will exit the app.
|
||||
*/
|
||||
clearHistory:function() {
|
||||
exec(null, null, "App", "clearHistory", []);
|
||||
},
|
||||
/**
|
||||
* Clear web history in this web view.
|
||||
* Instead of BACK button loading the previous web page, it will exit the app.
|
||||
*/
|
||||
clearHistory:function() {
|
||||
exec(null, null, "App", "clearHistory", []);
|
||||
},
|
||||
|
||||
/**
|
||||
* Go to previous page displayed.
|
||||
* This is the same as pressing the backbutton on Android device.
|
||||
*/
|
||||
backHistory:function() {
|
||||
exec(null, null, "App", "backHistory", []);
|
||||
},
|
||||
/**
|
||||
* Go to previous page displayed.
|
||||
* This is the same as pressing the backbutton on Android device.
|
||||
*/
|
||||
backHistory:function() {
|
||||
exec(null, null, "App", "backHistory", []);
|
||||
},
|
||||
|
||||
/**
|
||||
* Override the default behavior of the Android back button.
|
||||
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
|
||||
*
|
||||
* Note: The user should not have to call this method. Instead, when the user
|
||||
* registers for the "backbutton" event, this is automatically done.
|
||||
*
|
||||
* @param override T=override, F=cancel override
|
||||
*/
|
||||
overrideBackbutton:function(override) {
|
||||
exec(null, null, "App", "overrideBackbutton", [override]);
|
||||
},
|
||||
/**
|
||||
* Override the default behavior of the Android back button.
|
||||
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
|
||||
*
|
||||
* Note: The user should not have to call this method. Instead, when the user
|
||||
* registers for the "backbutton" event, this is automatically done.
|
||||
*
|
||||
* @param override T=override, F=cancel override
|
||||
*/
|
||||
overrideBackbutton:function(override) {
|
||||
exec(null, null, "App", "overrideBackbutton", [override]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Exit and terminate the application.
|
||||
*/
|
||||
exitApp:function() {
|
||||
return exec(null, null, "App", "exitApp", []);
|
||||
}
|
||||
/**
|
||||
* Exit and terminate the application.
|
||||
*/
|
||||
exitApp:function() {
|
||||
return exec(null, null, "App", "exitApp", []);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
@ -4186,8 +4279,8 @@ module.exports = {
|
||||
setNativeToJsBridgeMode: function(value) {
|
||||
prompt(value, 'gap_bridge_mode:');
|
||||
},
|
||||
retrieveJsMessages: function() {
|
||||
return prompt('', 'gap_poll:');
|
||||
retrieveJsMessages: function(fromOnlineEvent) {
|
||||
return prompt(+fromOnlineEvent, 'gap_poll:');
|
||||
}
|
||||
};
|
||||
|
||||
@ -4482,9 +4575,9 @@ var DroidDB_openDatabase = function(name, version, display_name, size) {
|
||||
|
||||
|
||||
module.exports = {
|
||||
openDatabase:DroidDB_openDatabase,
|
||||
failQuery:failQuery,
|
||||
completeQuery:completeQuery
|
||||
openDatabase:DroidDB_openDatabase,
|
||||
failQuery:failQuery,
|
||||
completeQuery:completeQuery
|
||||
};
|
||||
|
||||
});
|
||||
@ -6515,6 +6608,127 @@ var modulemapper = require('cordova/modulemapper');
|
||||
|
||||
modulemapper.clobbers('cordova/plugin/splashscreen', 'navigator.splashscreen');
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/pluginloader.js
|
||||
define("cordova/pluginloader", function(require, exports, module) {
|
||||
|
||||
var channel = require('cordova/channel');
|
||||
var modulemapper = require('cordova/modulemapper');
|
||||
|
||||
// Helper function to inject a <script> tag.
|
||||
function injectScript(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;
|
||||
script.src = url;
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
function onScriptLoadingComplete(moduleList) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
// error with module, most likely clobbers, should we continue?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finishPluginLoading();
|
||||
}
|
||||
|
||||
// Called when:
|
||||
// * There are plugins defined and all plugins are finished loading.
|
||||
// * There are no plugins to load.
|
||||
function finishPluginLoading() {
|
||||
channel.onPluginsReady.fire();
|
||||
}
|
||||
|
||||
// Handler for the cordova_plugins.js content.
|
||||
// See plugman's plugin_loader.js for the details of this object.
|
||||
// This function is only called if the really is a plugins array that isn't empty.
|
||||
// Otherwise the onerror response handler will just call finishPluginLoading().
|
||||
function handlePluginsObject(path, moduleList) {
|
||||
// Now inject the scripts.
|
||||
var scriptCounter = moduleList.length;
|
||||
|
||||
if (!scriptCounter) {
|
||||
finishPluginLoading();
|
||||
return;
|
||||
}
|
||||
function scriptLoadedCallback() {
|
||||
if (!--scriptCounter) {
|
||||
onScriptLoadingComplete(moduleList);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < moduleList.length; i++) {
|
||||
injectScript(path + moduleList[i].file, scriptLoadedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
function injectPluginScript(pathPrefix) {
|
||||
injectScript(pathPrefix + 'cordova_plugins.js', function(){
|
||||
try {
|
||||
var moduleList = require("cordova/plugin_list");
|
||||
handlePluginsObject(pathPrefix, moduleList);
|
||||
} 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;
|
||||
if (src.indexOf(term) == (src.length - term.length)) {
|
||||
path = src.substring(0, src.length - term.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// Tries to load all plugins' js-modules.
|
||||
// This is an async process, but onDeviceReady is blocked on onPluginsReady.
|
||||
// onPluginsReady is fired when there are no plugins to load, or they are all done.
|
||||
exports.load = function() {
|
||||
var pathPrefix = findCordovaPath();
|
||||
if (pathPrefix === null) {
|
||||
console.log('Could not find cordova.js script tag. Plugin loading may fail.');
|
||||
pathPrefix = '';
|
||||
}
|
||||
injectPluginScript(pathPrefix);
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/symbols.js
|
||||
@ -6530,6 +6744,23 @@ modulemapper.clobbers('cordova/exec', 'Cordova.exec');
|
||||
|
||||
});
|
||||
|
||||
// file: lib/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) {
|
||||
anchorEl.href = url;
|
||||
return anchorEl.href;
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/utils.js
|
||||
define("cordova/utils", function(require, exports, module) {
|
||||
|
||||
@ -6710,6 +6941,8 @@ window.cordova = require('cordova');
|
||||
context._cordovaJsLoaded = true;
|
||||
|
||||
var channel = require('cordova/channel');
|
||||
var pluginloader = require('cordova/pluginloader');
|
||||
|
||||
var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady];
|
||||
|
||||
function logUnfiredChannels(arr) {
|
||||
@ -6778,170 +7011,16 @@ window.cordova = require('cordova');
|
||||
|
||||
}, platformInitChannelsArray);
|
||||
|
||||
// Don't attempt to load when running unit tests.
|
||||
if (typeof XMLHttpRequest != 'undefined') {
|
||||
pluginloader.load();
|
||||
}
|
||||
}(window));
|
||||
|
||||
// file: lib/scripts/bootstrap-android.js
|
||||
|
||||
// Tell the native code that a page change has occurred.
|
||||
require('cordova/exec')(null, null, 'PluginManager', 'startup', []);
|
||||
require('cordova/channel').onNativeReady.fire();
|
||||
|
||||
// file: lib/scripts/plugin_loader.js
|
||||
|
||||
// Tries to load all plugins' js-modules.
|
||||
// This is an async process, but onDeviceReady is blocked on onPluginsReady.
|
||||
// onPluginsReady is fired when there are no plugins to load, or they are all done.
|
||||
(function (context) {
|
||||
// To be populated with the handler by handlePluginsObject.
|
||||
var onScriptLoadingComplete;
|
||||
|
||||
var scriptCounter = 0;
|
||||
function scriptLoadedCallback() {
|
||||
scriptCounter--;
|
||||
if (scriptCounter === 0) {
|
||||
onScriptLoadingComplete && onScriptLoadingComplete();
|
||||
}
|
||||
}
|
||||
|
||||
function scriptErrorCallback(err) {
|
||||
// Open Question: If a script path specified in cordova_plugins.js does not exist, do we fail for all?
|
||||
// this is currently just continuing.
|
||||
scriptCounter--;
|
||||
if (scriptCounter === 0) {
|
||||
onScriptLoadingComplete && onScriptLoadingComplete();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to inject a <script> tag.
|
||||
function injectScript(path) {
|
||||
scriptCounter++;
|
||||
var script = document.createElement("script");
|
||||
script.onload = scriptLoadedCallback;
|
||||
script.onerror = scriptErrorCallback;
|
||||
script.src = path;
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
// Called when:
|
||||
// * There are plugins defined and all plugins are finished loading.
|
||||
// * There are no plugins to load.
|
||||
function finishPluginLoading() {
|
||||
context.cordova.require('cordova/channel').onPluginsReady.fire();
|
||||
}
|
||||
|
||||
// Handler for the cordova_plugins.js content.
|
||||
// See plugman's plugin_loader.js for the details of this object.
|
||||
// This function is only called if the really is a plugins array that isn't empty.
|
||||
// Otherwise the onerror response handler will just call finishPluginLoading().
|
||||
function handlePluginsObject(modules, path) {
|
||||
// First create the callback for when all plugins are loaded.
|
||||
var mapper = context.cordova.require('cordova/modulemapper');
|
||||
onScriptLoadingComplete = function() {
|
||||
// Loop through all the plugins and then through their clobbers and merges.
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
var module = modules[i];
|
||||
if (module) {
|
||||
try {
|
||||
if (module.clobbers && module.clobbers.length) {
|
||||
for (var j = 0; j < module.clobbers.length; j++) {
|
||||
mapper.clobbers(module.id, module.clobbers[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (module.merges && module.merges.length) {
|
||||
for (var k = 0; k < module.merges.length; k++) {
|
||||
mapper.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)) {
|
||||
context.cordova.require(module.id);
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
// error with module, most likely clobbers, should we continue?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finishPluginLoading();
|
||||
};
|
||||
|
||||
// Now inject the scripts.
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
injectScript(path + modules[i].file);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the root of the app
|
||||
var path = '';
|
||||
var scripts = document.getElementsByTagName('script');
|
||||
var term = 'cordova.js';
|
||||
for (var n = scripts.length-1; n>-1; n--) {
|
||||
var src = scripts[n].src;
|
||||
if (src.indexOf(term) == (src.length - term.length)) {
|
||||
path = src.substring(0, src.length - term.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var plugins_json = path + 'cordova_plugins.json';
|
||||
var plugins_js = path + 'cordova_plugins.js';
|
||||
|
||||
// One some phones (Windows) this xhr.open throws an Access Denied exception
|
||||
// So lets keep trying, but with a script tag injection technique instead of XHR
|
||||
var injectPluginScript = function injectPluginScript() {
|
||||
try {
|
||||
var script = document.createElement("script");
|
||||
script.onload = function(){
|
||||
var list = cordova.require("cordova/plugin_list");
|
||||
handlePluginsObject(list,path);
|
||||
};
|
||||
script.onerror = function() {
|
||||
// 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();
|
||||
};
|
||||
script.src = plugins_js;
|
||||
document.head.appendChild(script);
|
||||
|
||||
} catch(err){
|
||||
finishPluginLoading();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Try to XHR the cordova_plugins.json file asynchronously.
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onload = function() {
|
||||
// If the response is a JSON string which composes an array, call handlePluginsObject.
|
||||
// If the request fails, or the response is not a JSON array, just call finishPluginLoading.
|
||||
var obj;
|
||||
try {
|
||||
obj = (this.status == 0 || this.status == 200) && this.responseText && JSON.parse(this.responseText);
|
||||
} catch (err) {
|
||||
// obj will be undefined.
|
||||
}
|
||||
if (Array.isArray(obj) && obj.length > 0) {
|
||||
handlePluginsObject(obj, path);
|
||||
} else {
|
||||
finishPluginLoading();
|
||||
}
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
// In this case, the json file was not present, but XHR was allowed,
|
||||
// so we should still try the script injection technique with the js file
|
||||
// in case that is there.
|
||||
injectPluginScript();
|
||||
};
|
||||
try { // we commented we were going to try, so let us actually try and catch
|
||||
xhr.open('GET', plugins_json, true); // Async
|
||||
xhr.send();
|
||||
} catch(err){
|
||||
injectPluginScript();
|
||||
}
|
||||
}(window));
|
||||
|
||||
|
||||
})();
|
@ -97,7 +97,11 @@ public class App extends CordovaPlugin {
|
||||
* Clear the resource cache.
|
||||
*/
|
||||
public void clearCache() {
|
||||
this.webView.clearCache(true);
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
webView.clearCache(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,7 +168,11 @@ public class App extends CordovaPlugin {
|
||||
* Clear page history for the app.
|
||||
*/
|
||||
public void clearHistory() {
|
||||
this.webView.clearHistory();
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
webView.clearHistory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -537,6 +537,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
if (fp.exists()) {
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
this.player.setDataSource(fileInputStream.getFD());
|
||||
fileInputStream.close();
|
||||
}
|
||||
else {
|
||||
this.player.setDataSource("/sdcard/" + file);
|
||||
|
@ -41,7 +41,6 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
||||
import android.net.Uri;
|
||||
@ -74,7 +73,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
private static final String GET_PICTURE = "Get Picture";
|
||||
private static final String GET_VIDEO = "Get Video";
|
||||
private static final String GET_All = "Get All";
|
||||
|
||||
|
||||
private static final String LOG_TAG = "CameraLauncher";
|
||||
|
||||
private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
@ -93,23 +92,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
private MediaScannerConnection conn; // Used to update gallery app with newly-written files
|
||||
private Uri scanMe; // Uri of image to be added to content store
|
||||
|
||||
//This should never be null!
|
||||
//private CordovaInterface cordova;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CameraLauncher() {
|
||||
}
|
||||
|
||||
// public void setContext(CordovaInterface mCtx) {
|
||||
// super.setContext(mCtx);
|
||||
// if (CordovaInterface.class.isInstance(mCtx))
|
||||
// cordova = (CordovaInterface) mCtx;
|
||||
// else
|
||||
// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
|
||||
// }
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
@ -151,15 +133,26 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
this.targetHeight = -1;
|
||||
}
|
||||
|
||||
if (srcType == CAMERA) {
|
||||
this.takePicture(destType, encodingType);
|
||||
try {
|
||||
if (srcType == CAMERA) {
|
||||
this.takePicture(destType, encodingType);
|
||||
}
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
this.getImage(srcType, destType);
|
||||
}
|
||||
}
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
this.getImage(srcType, destType);
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
callbackContext.error("Illegal Argument Exception");
|
||||
PluginResult r = new PluginResult(PluginResult.Status.ERROR);
|
||||
callbackContext.sendPluginResult(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(r);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -169,6 +162,24 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private String getTempDirectoryPath() {
|
||||
File cache = null;
|
||||
|
||||
// SD Card Mounted
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
||||
"/Android/data/" + cordova.getActivity().getPackageName() + "/cache/");
|
||||
}
|
||||
// Use internal storage
|
||||
else {
|
||||
cache = cordova.getActivity().getCacheDir();
|
||||
}
|
||||
|
||||
// Create the cache directory if it doesn't exist
|
||||
cache.mkdirs();
|
||||
return cache.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a picture with the camera.
|
||||
* When an image is captured or the camera view is cancelled, the result is returned
|
||||
@ -211,9 +222,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
private File createCaptureFile(int encodingType) {
|
||||
File photo = null;
|
||||
if (encodingType == JPEG) {
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.jpg");
|
||||
photo = new File(getTempDirectoryPath(), ".Pic.jpg");
|
||||
} else if (encodingType == PNG) {
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.png");
|
||||
photo = new File(getTempDirectoryPath(), ".Pic.png");
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType);
|
||||
}
|
||||
@ -277,7 +288,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg");
|
||||
exif.createInFile(getTempDirectoryPath() + "/.Pic.jpg");
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
}
|
||||
@ -318,7 +329,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
//Just because we have a media URI doesn't mean we have a real file, we need to make it
|
||||
uri = Uri.fromFile(new File(FileHelper.getRealPath(inputUri, this.cordova)));
|
||||
} else {
|
||||
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
|
||||
uri = Uri.fromFile(new File(getTempDirectoryPath(), System.currentTimeMillis() + ".jpg"));
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
@ -438,7 +449,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
if (this.targetHeight > 0 && this.targetWidth > 0) {
|
||||
try {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
|
||||
String resizePath = getTempDirectoryPath() + "/resize.jpg";
|
||||
// Some content: URIs do not map to file paths (e.g. picasa).
|
||||
String realPath = FileHelper.getRealPath(uri, this.cordova);
|
||||
ExifHelper exif = new ExifHelper();
|
||||
@ -736,6 +747,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
Uri uri = Uri.parse(contentStore + "/" + id);
|
||||
this.cordova.getActivity().getContentResolver().delete(uri, null, null);
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -761,7 +773,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
try {
|
||||
if (bitmap.compress(CompressFormat.JPEG, mQuality, jpeg_data)) {
|
||||
byte[] code = jpeg_data.toByteArray();
|
||||
byte[] output = Base64.encode(code, Base64.DEFAULT);
|
||||
byte[] output = Base64.encode(code, Base64.NO_WRAP);
|
||||
String js_out = new String(output);
|
||||
this.callbackContext.success(js_out);
|
||||
js_out = null;
|
||||
|
@ -305,14 +305,20 @@ public class Capture extends CordovaPlugin {
|
||||
// Get the uri of the video clip
|
||||
Uri data = intent.getData();
|
||||
// create a file object from the uri
|
||||
results.put(createMediaFile(data));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing video
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
} else {
|
||||
// still need to capture more video clips
|
||||
captureVideo(duration);
|
||||
if(data == null)
|
||||
{
|
||||
this.fail(createErrorObject(CAPTURE_NO_MEDIA_FILES, "Error: data is null"));
|
||||
}
|
||||
else
|
||||
{
|
||||
results.put(createMediaFile(data));
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing video
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
} else {
|
||||
// still need to capture more video clips
|
||||
captureVideo(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -71,7 +72,7 @@ public class Config {
|
||||
return;
|
||||
}
|
||||
|
||||
int id = action.getResources().getIdentifier("config", "xml", action.getPackageName());
|
||||
int id = action.getResources().getIdentifier("config", "xml", action.getClass().getPackage().getName());
|
||||
if (id == 0) {
|
||||
id = action.getResources().getIdentifier("cordova", "xml", action.getPackageName());
|
||||
LOG.i("CordovaLog", "config.xml missing, reverting to cordova.xml");
|
||||
@ -102,7 +103,7 @@ public class Config {
|
||||
}
|
||||
}
|
||||
else if (strNode.equals("preference")) {
|
||||
String name = xml.getAttributeValue(null, "name");
|
||||
String name = xml.getAttributeValue(null, "name").toLowerCase(Locale.getDefault());
|
||||
/* Java 1.6 does not support switch-based strings
|
||||
Java 7 does, but we're using Dalvik, which is apparently not Java.
|
||||
Since we're reading XML, this has to be an ugly if/else.
|
||||
@ -112,39 +113,39 @@ public class Config {
|
||||
|
||||
Note: We should probably pass in the classname for the variable splash on splashscreen!
|
||||
*/
|
||||
if (name.equals("loglevel")) {
|
||||
if (name.equalsIgnoreCase("LogLevel")) {
|
||||
String level = xml.getAttributeValue(null, "value");
|
||||
LOG.setLogLevel(level);
|
||||
} else if (name.equals("splashscreen")) {
|
||||
} else if (name.equalsIgnoreCase("SplashScreen")) {
|
||||
String value = xml.getAttributeValue(null, "value");
|
||||
int resource = 0;
|
||||
if (value == null)
|
||||
{
|
||||
value = "splash";
|
||||
}
|
||||
resource = action.getResources().getIdentifier(value, "drawable", action.getPackageName());
|
||||
resource = action.getResources().getIdentifier(value, "drawable", action.getClass().getPackage().getName());
|
||||
|
||||
action.getIntent().putExtra(name, resource);
|
||||
}
|
||||
else if(name.equals("backgroundColor")) {
|
||||
else if(name.equalsIgnoreCase("BackgroundColor")) {
|
||||
int value = xml.getAttributeIntValue(null, "value", Color.BLACK);
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equals("loadUrlTimeoutValue")) {
|
||||
else if(name.equalsIgnoreCase("LoadUrlTimeoutValue")) {
|
||||
int value = xml.getAttributeIntValue(null, "value", 20000);
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equals("keepRunning"))
|
||||
else if(name.equalsIgnoreCase("KeepRunning"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equals("InAppBrowserStorageEnabled"))
|
||||
else if(name.equalsIgnoreCase("InAppBrowserStorageEnabled"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equals("disallowOverscroll"))
|
||||
else if(name.equalsIgnoreCase("DisallowOverscroll"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@ -281,12 +282,12 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
initCallbackClass = savedInstanceState.getString("callbackClass");
|
||||
}
|
||||
|
||||
if(!this.getBooleanProperty("showTitle", false))
|
||||
if(!this.getBooleanProperty("ShowTitle", false))
|
||||
{
|
||||
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
}
|
||||
|
||||
if(this.getBooleanProperty("setFullscreen", false))
|
||||
if(this.getBooleanProperty("SetFullscreen", false))
|
||||
{
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
@ -362,7 +363,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
1.0F));
|
||||
|
||||
if (this.getBooleanProperty("disallowOverscroll", false)) {
|
||||
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);
|
||||
}
|
||||
@ -391,11 +392,11 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
}
|
||||
|
||||
// Set backgroundColor
|
||||
this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK);
|
||||
this.backgroundColor = this.getIntegerProperty("BackgroundColor", Color.BLACK);
|
||||
this.root.setBackgroundColor(this.backgroundColor);
|
||||
|
||||
// If keepRunning
|
||||
this.keepRunning = this.getBooleanProperty("keepRunning", true);
|
||||
this.keepRunning = this.getBooleanProperty("KeepRunning", true);
|
||||
|
||||
// Then load the spinner
|
||||
this.loadSpinner();
|
||||
@ -411,10 +412,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
// If loadingDialog property, then show the App loading dialog for first page of app
|
||||
String loading = null;
|
||||
if ((this.appView == null) || !this.appView.canGoBack()) {
|
||||
loading = this.getStringProperty("loadingDialog", null);
|
||||
loading = this.getStringProperty("LoadingDialog", null);
|
||||
}
|
||||
else {
|
||||
loading = this.getStringProperty("loadingPageDialog", null);
|
||||
loading = this.getStringProperty("LoadingPageDialog", null);
|
||||
}
|
||||
if (loading != null) {
|
||||
|
||||
@ -451,7 +452,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
}
|
||||
|
||||
this.splashscreenTime = time;
|
||||
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
||||
this.showSplashScreen(this.splashscreenTime);
|
||||
this.appView.loadUrl(url, time);
|
||||
}
|
||||
@ -517,6 +518,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
Boolean p;
|
||||
try {
|
||||
p = (Boolean) bundle.get(name);
|
||||
@ -547,6 +549,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
Integer p;
|
||||
try {
|
||||
p = (Integer) bundle.get(name);
|
||||
@ -571,6 +574,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
String p = bundle.getString(name);
|
||||
if (p == null) {
|
||||
return defaultValue;
|
||||
@ -590,6 +594,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
Double p;
|
||||
try {
|
||||
p = (Double) bundle.get(name);
|
||||
@ -610,7 +615,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
*/
|
||||
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, value);
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -621,7 +626,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
*/
|
||||
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, value);
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -632,7 +637,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
*/
|
||||
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, value);
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -643,7 +648,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
*/
|
||||
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, value);
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -696,7 +701,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
|
||||
|
||||
//Code to test CB-3064
|
||||
String errorUrl = this.getStringProperty("errorUrl", null);
|
||||
String errorUrl = this.getStringProperty("ErrorUrl", null);
|
||||
LOG.d(TAG, "CB-3064: The errorUrl is " + errorUrl);
|
||||
|
||||
if (this.activityState == ACTIVITY_STARTING) {
|
||||
@ -853,8 +858,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
LOG.d(TAG, "Incoming Result");
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
Log.d(TAG, "Request code = " + requestCode);
|
||||
ValueCallback<Uri> mUploadMessage = this.appView.getWebChromeClient().getValueCallback();
|
||||
if (requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
|
||||
if (appView != null && requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
|
||||
ValueCallback<Uri> mUploadMessage = this.appView.getWebChromeClient().getValueCallback();
|
||||
Log.d(TAG, "did we get here?");
|
||||
if (null == mUploadMessage)
|
||||
return;
|
||||
@ -1074,9 +1079,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event)
|
||||
{
|
||||
//Get whatever has focus!
|
||||
View childView = appView.getFocusedChild();
|
||||
if ((appView.isCustomViewShowing() || childView != null ) &&
|
||||
if (appView != null && (appView.isCustomViewShowing() || appView.getFocusedChild() != null ) &&
|
||||
(keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
return appView.onKeyUp(keyCode, event);
|
||||
} else {
|
||||
@ -1094,10 +1097,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||
{
|
||||
//Get whatever has focus!
|
||||
View childView = appView.getFocusedChild();
|
||||
//Determine if the focus is on the current view or not
|
||||
if (childView != null && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
if (appView != null && appView.getFocusedChild() != null && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
return appView.onKeyDown(keyCode, event);
|
||||
}
|
||||
else
|
||||
@ -1121,7 +1122,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
else {
|
||||
// If the splash dialog is showing don't try to show it again
|
||||
if (this.splashDialog == null || !this.splashDialog.isShowing()) {
|
||||
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
||||
this.showSplashScreen(this.splashscreenTime);
|
||||
}
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
|
||||
// Polling for JavaScript messages
|
||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) {
|
||||
String r = this.appView.exposedJsApi.retrieveJsMessages();
|
||||
String r = this.appView.exposedJsApi.retrieveJsMessages("1".equals(message));
|
||||
result.confirm(r == null ? "" : r);
|
||||
}
|
||||
|
||||
@ -275,33 +275,13 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
|
||||
/**
|
||||
* Handle database quota exceeded notification.
|
||||
*
|
||||
* @param url
|
||||
* @param databaseIdentifier
|
||||
* @param currentQuota
|
||||
* @param estimatedSize
|
||||
* @param totalUsedQuota
|
||||
* @param quotaUpdater
|
||||
*/
|
||||
@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);
|
||||
|
||||
if (estimatedSize < MAX_QUOTA)
|
||||
{
|
||||
//increase for 1Mb
|
||||
long newQuota = estimatedSize;
|
||||
LOG.d(TAG, "calling quotaUpdater.updateQuota newQuota: %d", newQuota);
|
||||
quotaUpdater.updateQuota(newQuota);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the quota to whatever it is and force an error
|
||||
// TODO: get docs on how to handle this properly
|
||||
quotaUpdater.updateQuota(currentQuota);
|
||||
}
|
||||
quotaUpdater.updateQuota(MAX_QUOTA);
|
||||
}
|
||||
|
||||
// console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html
|
||||
|
362
framework/src/org/apache/cordova/CordovaResourceApi.java
Normal file
@ -0,0 +1,362 @@
|
||||
/*
|
||||
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.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.AssetManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Looper;
|
||||
import android.util.Base64;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
|
||||
import org.apache.cordova.api.PluginManager;
|
||||
import org.apache.http.util.EncodingUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public class CordovaResourceApi {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String LOG_TAG = "CordovaResourceApi";
|
||||
|
||||
public static final int URI_TYPE_FILE = 0;
|
||||
public static final int URI_TYPE_ASSET = 1;
|
||||
public static final int URI_TYPE_CONTENT = 2;
|
||||
public static final int URI_TYPE_RESOURCE = 3;
|
||||
public static final int URI_TYPE_DATA = 4;
|
||||
public static final int URI_TYPE_HTTP = 5;
|
||||
public static final int URI_TYPE_HTTPS = 6;
|
||||
public static final int URI_TYPE_UNKNOWN = -1;
|
||||
|
||||
private static final String[] LOCAL_FILE_PROJECTION = { "_data" };
|
||||
|
||||
// Creating this is light-weight.
|
||||
private static OkHttpClient httpClient = new OkHttpClient();
|
||||
|
||||
static Thread jsThread;
|
||||
|
||||
private final AssetManager assetManager;
|
||||
private final ContentResolver contentResolver;
|
||||
private final PluginManager pluginManager;
|
||||
private boolean threadCheckingEnabled = true;
|
||||
|
||||
|
||||
public CordovaResourceApi(Context context, PluginManager pluginManager) {
|
||||
this.contentResolver = context.getContentResolver();
|
||||
this.assetManager = context.getAssets();
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
public void setThreadCheckingEnabled(boolean value) {
|
||||
threadCheckingEnabled = value;
|
||||
}
|
||||
|
||||
public boolean isThreadCheckingEnabled() {
|
||||
return threadCheckingEnabled;
|
||||
}
|
||||
|
||||
public static int getUriType(Uri uri) {
|
||||
assertNonRelative(uri);
|
||||
String scheme = uri.getScheme();
|
||||
if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
|
||||
return URI_TYPE_CONTENT;
|
||||
}
|
||||
if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
|
||||
return URI_TYPE_RESOURCE;
|
||||
}
|
||||
if (ContentResolver.SCHEME_FILE.equals(scheme)) {
|
||||
if (uri.getPath().startsWith("/android_asset/")) {
|
||||
return URI_TYPE_ASSET;
|
||||
}
|
||||
return URI_TYPE_FILE;
|
||||
}
|
||||
if ("data".equals(scheme)) {
|
||||
return URI_TYPE_DATA;
|
||||
}
|
||||
if ("http".equals(scheme)) {
|
||||
return URI_TYPE_HTTP;
|
||||
}
|
||||
if ("https".equals(scheme)) {
|
||||
return URI_TYPE_HTTPS;
|
||||
}
|
||||
return URI_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
public Uri remapUri(Uri uri) {
|
||||
assertNonRelative(uri);
|
||||
Uri pluginUri = pluginManager.remapUri(uri);
|
||||
return pluginUri != null ? pluginUri : uri;
|
||||
}
|
||||
|
||||
public String remapPath(String path) {
|
||||
return remapUri(Uri.fromFile(new File(path))).getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a File that points to the resource, or null if the resource
|
||||
* is not on the local filesystem.
|
||||
*/
|
||||
public File mapUriToFile(Uri uri) {
|
||||
assertBackgroundThread();
|
||||
switch (getUriType(uri)) {
|
||||
case URI_TYPE_FILE:
|
||||
return new File(uri.getPath());
|
||||
case URI_TYPE_CONTENT: {
|
||||
Cursor cursor = contentResolver.query(uri, LOCAL_FILE_PROJECTION, null, null, null);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
int columnIndex = cursor.getColumnIndex(LOCAL_FILE_PROJECTION[0]);
|
||||
if (columnIndex != -1 && cursor.getCount() > 0) {
|
||||
cursor.moveToFirst();
|
||||
String realPath = cursor.getString(columnIndex);
|
||||
if (realPath != null) {
|
||||
return new File(realPath);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a stream to the givne URI, also providing the MIME type & length.
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
* @throws Throws an IllegalStateException if called on a foreground thread.
|
||||
*/
|
||||
public OpenForReadResult openForRead(Uri uri) throws IOException {
|
||||
return openForRead(uri, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a stream to the givne URI, also providing the MIME type & length.
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
* @throws Throws an IllegalStateException if called on a foreground thread and skipThreadCheck is false.
|
||||
*/
|
||||
public OpenForReadResult openForRead(Uri uri, boolean skipThreadCheck) throws IOException {
|
||||
if (!skipThreadCheck) {
|
||||
assertBackgroundThread();
|
||||
}
|
||||
switch (getUriType(uri)) {
|
||||
case URI_TYPE_FILE: {
|
||||
FileInputStream inputStream = new FileInputStream(uri.getPath());
|
||||
String mimeType = FileHelper.getMimeTypeForExtension(uri.getPath());
|
||||
long length = inputStream.getChannel().size();
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, null);
|
||||
}
|
||||
case URI_TYPE_ASSET: {
|
||||
String assetPath = uri.getPath().substring(15);
|
||||
AssetFileDescriptor assetFd = null;
|
||||
InputStream inputStream;
|
||||
long length = -1;
|
||||
try {
|
||||
assetFd = assetManager.openFd(assetPath);
|
||||
inputStream = assetFd.createInputStream();
|
||||
length = assetFd.getLength();
|
||||
} catch (FileNotFoundException e) {
|
||||
// Will occur if the file is compressed.
|
||||
inputStream = assetManager.open(assetPath);
|
||||
}
|
||||
String mimeType = FileHelper.getMimeTypeForExtension(assetPath);
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, assetFd);
|
||||
}
|
||||
case URI_TYPE_CONTENT:
|
||||
case URI_TYPE_RESOURCE: {
|
||||
String mimeType = contentResolver.getType(uri);
|
||||
AssetFileDescriptor assetFd = contentResolver.openAssetFileDescriptor(uri, "r");
|
||||
InputStream inputStream = assetFd.createInputStream();
|
||||
long length = assetFd.getLength();
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, assetFd);
|
||||
}
|
||||
case URI_TYPE_DATA: {
|
||||
OpenForReadResult ret = readDataUri(uri);
|
||||
if (ret == null) {
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case URI_TYPE_HTTP:
|
||||
case URI_TYPE_HTTPS: {
|
||||
HttpURLConnection conn = httpClient.open(new URL(uri.toString()));
|
||||
conn.setDoInput(true);
|
||||
String mimeType = conn.getHeaderField("Content-Type");
|
||||
int length = conn.getContentLength();
|
||||
InputStream inputStream = conn.getInputStream();
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, null);
|
||||
}
|
||||
}
|
||||
throw new FileNotFoundException("URI not supported by CordovaResourceApi: " + uri);
|
||||
}
|
||||
|
||||
public OutputStream openOutputStream(Uri uri) throws IOException {
|
||||
return openOutputStream(uri, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a stream to the given URI.
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
*/
|
||||
public OutputStream openOutputStream(Uri uri, boolean append) throws IOException {
|
||||
assertBackgroundThread();
|
||||
switch (getUriType(uri)) {
|
||||
case URI_TYPE_FILE: {
|
||||
File localFile = new File(uri.getPath());
|
||||
File parent = localFile.getParentFile();
|
||||
if (parent != null) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
return new FileOutputStream(localFile, append);
|
||||
}
|
||||
case URI_TYPE_CONTENT:
|
||||
case URI_TYPE_RESOURCE: {
|
||||
AssetFileDescriptor assetFd = contentResolver.openAssetFileDescriptor(uri, append ? "wa" : "w");
|
||||
return assetFd.createOutputStream();
|
||||
}
|
||||
}
|
||||
throw new FileNotFoundException("URI not supported by CordovaResourceApi: " + uri);
|
||||
}
|
||||
|
||||
public HttpURLConnection createHttpConnection(Uri uri) throws IOException {
|
||||
assertBackgroundThread();
|
||||
return httpClient.open(new URL(uri.toString()));
|
||||
}
|
||||
|
||||
// Copies the input to the output in the most efficient manner possible.
|
||||
// Closes both streams.
|
||||
public void copyResource(OpenForReadResult input, OutputStream outputStream) throws IOException {
|
||||
assertBackgroundThread();
|
||||
try {
|
||||
InputStream inputStream = input.inputStream;
|
||||
if (inputStream instanceof FileInputStream && outputStream instanceof FileOutputStream) {
|
||||
FileChannel inChannel = ((FileInputStream)input.inputStream).getChannel();
|
||||
FileChannel outChannel = ((FileOutputStream)outputStream).getChannel();
|
||||
long offset = 0;
|
||||
long length = input.length;
|
||||
if (input.assetFd != null) {
|
||||
offset = input.assetFd.getStartOffset();
|
||||
}
|
||||
outChannel.transferFrom(inChannel, offset, length);
|
||||
} else {
|
||||
final int BUFFER_SIZE = 8192;
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
for (;;) {
|
||||
int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE);
|
||||
|
||||
if (bytesRead <= 0) {
|
||||
break;
|
||||
}
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
input.inputStream.close();
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void copyResource(Uri sourceUri, OutputStream outputStream) throws IOException {
|
||||
copyResource(openForRead(sourceUri), outputStream);
|
||||
}
|
||||
|
||||
|
||||
private void assertBackgroundThread() {
|
||||
if (threadCheckingEnabled) {
|
||||
Thread curThread = Thread.currentThread();
|
||||
if (curThread == Looper.getMainLooper().getThread()) {
|
||||
throw new IllegalStateException("Do not perform IO operations on the UI thread. Use CordovaInterface.getThreadPool() instead.");
|
||||
}
|
||||
if (curThread == jsThread) {
|
||||
throw new IllegalStateException("Tried to perform an IO operation on the WebCore thread. Use CordovaInterface.getThreadPool() instead.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private OpenForReadResult readDataUri(Uri uri) {
|
||||
String uriAsString = uri.getSchemeSpecificPart();
|
||||
int commaPos = uriAsString.indexOf(',');
|
||||
if (commaPos == -1) {
|
||||
return null;
|
||||
}
|
||||
String[] mimeParts = uriAsString.substring(0, commaPos).split(";");
|
||||
String contentType = null;
|
||||
boolean base64 = false;
|
||||
if (mimeParts.length > 0) {
|
||||
contentType = mimeParts[0];
|
||||
}
|
||||
for (int i = 1; i < mimeParts.length; ++i) {
|
||||
if ("base64".equalsIgnoreCase(mimeParts[i])) {
|
||||
base64 = true;
|
||||
}
|
||||
}
|
||||
String dataPartAsString = uriAsString.substring(commaPos + 1);
|
||||
byte[] data = base64 ? Base64.decode(dataPartAsString, Base64.DEFAULT) : EncodingUtils.getBytes(dataPartAsString, "UTF-8");
|
||||
InputStream inputStream = new ByteArrayInputStream(data);
|
||||
return new OpenForReadResult(uri, inputStream, contentType, data.length, null);
|
||||
}
|
||||
|
||||
private static void assertNonRelative(Uri uri) {
|
||||
if (!uri.isAbsolute()) {
|
||||
throw new IllegalArgumentException("Relative URIs are not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public static final class OpenForReadResult {
|
||||
public final Uri uri;
|
||||
public final InputStream inputStream;
|
||||
public final String mimeType;
|
||||
public final long length;
|
||||
public final AssetFileDescriptor assetFd;
|
||||
|
||||
OpenForReadResult(Uri uri, InputStream inputStream, String mimeType, long length, AssetFileDescriptor assetFd) {
|
||||
this.uri = uri;
|
||||
this.inputStream = inputStream;
|
||||
this.mimeType = mimeType;
|
||||
this.length = length;
|
||||
this.assetFd = assetFd;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -96,6 +97,8 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
private ActivityResult mResult = null;
|
||||
|
||||
private CordovaResourceApi resourceApi;
|
||||
|
||||
class ActivityResult {
|
||||
|
||||
int request;
|
||||
@ -227,9 +230,10 @@ public class CordovaWebView extends WebView {
|
||||
private void setup() {
|
||||
this.setInitialScale(0);
|
||||
this.setVerticalScrollBarEnabled(false);
|
||||
this.requestFocusFromTouch();
|
||||
|
||||
// Enable JavaScript
|
||||
if (shouldRequestFocusOnInit()) {
|
||||
this.requestFocusFromTouch();
|
||||
}
|
||||
// Enable JavaScript
|
||||
WebSettings settings = this.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
@ -305,10 +309,21 @@ public class CordovaWebView extends WebView {
|
||||
pluginManager = new PluginManager(this, this.cordova);
|
||||
jsMessageQueue = new NativeToJsMessageQueue(this, cordova);
|
||||
exposedJsApi = new ExposedJsApi(pluginManager, jsMessageQueue);
|
||||
resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
|
||||
exposeJsInterface();
|
||||
}
|
||||
|
||||
private void updateUserAgentString() {
|
||||
|
||||
/**
|
||||
* Override this method to decide wether or not you need to request the
|
||||
* focus when your application start
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean shouldRequestFocusOnInit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateUserAgentString() {
|
||||
this.getSettings().getUserAgentString();
|
||||
}
|
||||
|
||||
@ -413,7 +428,7 @@ public class CordovaWebView extends WebView {
|
||||
// Create a timeout timer for loadUrl
|
||||
final CordovaWebView me = this;
|
||||
final int currentLoadUrlTimeout = me.loadUrlTimeout;
|
||||
final int loadUrlTimeoutValue = Integer.parseInt(this.getProperty("loadUrlTimeoutValue", "20000"));
|
||||
final int loadUrlTimeoutValue = Integer.parseInt(this.getProperty("LoadUrlTimeoutValue", "20000"));
|
||||
|
||||
// Timeout error method
|
||||
final Runnable loadError = new Runnable() {
|
||||
@ -608,7 +623,7 @@ public class CordovaWebView extends WebView {
|
||||
*/
|
||||
private void loadConfiguration() {
|
||||
|
||||
if ("true".equals(this.getProperty("fullscreen", "false"))) {
|
||||
if ("true".equals(this.getProperty("Fullscreen", "false"))) {
|
||||
this.cordova.getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
this.cordova.getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
@ -626,6 +641,7 @@ public class CordovaWebView extends WebView {
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
Object p = bundle.get(name);
|
||||
if (p == null) {
|
||||
return defaultValue;
|
||||
@ -944,37 +960,7 @@ public class CordovaWebView extends WebView {
|
||||
mResult = new ActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the given URI, giving plugins a chance to re-route or customly handle the URI.
|
||||
* A white-list rejection will be returned if the URI does not pass the white-list.
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
*/
|
||||
public UriResolver resolveUri(Uri uri) {
|
||||
return resolveUri(uri, false);
|
||||
}
|
||||
|
||||
UriResolver resolveUri(Uri uri, boolean fromWebView) {
|
||||
if (!uri.isAbsolute()) {
|
||||
throw new IllegalArgumentException("Relative URIs are not yet supported by resolveUri.");
|
||||
}
|
||||
// Check the against the white-list before delegating to plugins.
|
||||
if (("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) && !Config.isUrlWhiteListed(uri.toString()))
|
||||
{
|
||||
LOG.w(TAG, "resolveUri - URL is not in whitelist: " + uri);
|
||||
return new UriResolvers.ErrorUriResolver(uri, "Whitelist rejection");
|
||||
}
|
||||
|
||||
// Give plugins a chance to handle the request.
|
||||
UriResolver resolver = pluginManager.resolveUri(uri);
|
||||
if (resolver == null && !fromWebView) {
|
||||
resolver = UriResolvers.forUri(uri, cordova.getActivity());
|
||||
if (resolver == null) {
|
||||
resolver = new UriResolvers.ErrorUriResolver(uri, "Unresolvable URI");
|
||||
}
|
||||
}
|
||||
|
||||
return resolver;
|
||||
public CordovaResourceApi getResourceApi() {
|
||||
return resourceApi;
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ import android.webkit.WebViewClient;
|
||||
*/
|
||||
public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
private static final String TAG = "Cordova";
|
||||
private static final String TAG = "CordovaWebViewClient";
|
||||
private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/";
|
||||
CordovaInterface cordova;
|
||||
CordovaWebView appView;
|
||||
|
@ -38,7 +38,7 @@ import android.telephony.TelephonyManager;
|
||||
public class Device extends CordovaPlugin {
|
||||
public static final String TAG = "Device";
|
||||
|
||||
public static String cordovaVersion = "dev"; // Cordova version
|
||||
public static String cordovaVersion = "2.9.1-dev"; // Cordova version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
|
@ -48,10 +48,13 @@ import org.json.JSONException;
|
||||
|
||||
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();
|
||||
ret = jsMessageQueue.popAndEncode(false);
|
||||
}
|
||||
return ret;
|
||||
} catch (Throwable e) {
|
||||
@ -68,7 +71,7 @@ import org.json.JSONException;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String retrieveJsMessages() {
|
||||
return jsMessageQueue.popAndEncode();
|
||||
public String retrieveJsMessages(boolean fromOnlineEvent) {
|
||||
return jsMessageQueue.popAndEncode(fromOnlineEvent);
|
||||
}
|
||||
}
|
||||
|
@ -22,19 +22,14 @@ import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLDecoder;
|
||||
import java.security.cert.CertificateException;
|
||||
@ -52,9 +47,11 @@ import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.apache.cordova.CordovaResourceApi.OpenForReadResult;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@ -64,8 +61,6 @@ import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.webkit.CookieManager;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
|
||||
public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
private static final String LOG_TAG = "FileTransfer";
|
||||
@ -81,8 +76,6 @@ public class FileTransfer extends CordovaPlugin {
|
||||
private static HashMap<String, RequestContext> activeRequests = new HashMap<String, RequestContext>();
|
||||
private static final int MAX_BUFFER_SIZE = 16 * 1024;
|
||||
|
||||
private static OkHttpClient httpClient = new OkHttpClient();
|
||||
|
||||
private static final class RequestContext {
|
||||
String source;
|
||||
String target;
|
||||
@ -111,20 +104,20 @@ public class FileTransfer extends CordovaPlugin {
|
||||
* the HTTP Content-Length header value from the server.
|
||||
*/
|
||||
private static abstract class TrackingInputStream extends FilterInputStream {
|
||||
public TrackingInputStream(final InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
public TrackingInputStream(final InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
public abstract long getTotalRawBytesRead();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExposedGZIPInputStream extends GZIPInputStream {
|
||||
public ExposedGZIPInputStream(final InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
public Inflater getInflater() {
|
||||
return inf;
|
||||
}
|
||||
}
|
||||
public ExposedGZIPInputStream(final InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
public Inflater getInflater() {
|
||||
return inf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides raw bytes-read tracking for a GZIP input stream. Reports the
|
||||
@ -132,30 +125,30 @@ public class FileTransfer extends CordovaPlugin {
|
||||
* number of uncompressed bytes.
|
||||
*/
|
||||
private static class TrackingGZIPInputStream extends TrackingInputStream {
|
||||
private ExposedGZIPInputStream gzin;
|
||||
public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException {
|
||||
super(gzin);
|
||||
this.gzin = gzin;
|
||||
}
|
||||
public long getTotalRawBytesRead() {
|
||||
return gzin.getInflater().getBytesRead();
|
||||
}
|
||||
}
|
||||
private ExposedGZIPInputStream gzin;
|
||||
public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException {
|
||||
super(gzin);
|
||||
this.gzin = gzin;
|
||||
}
|
||||
public long getTotalRawBytesRead() {
|
||||
return gzin.getInflater().getBytesRead();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides simple total-bytes-read tracking for an existing InputStream
|
||||
*/
|
||||
private static class TrackingHTTPInputStream extends TrackingInputStream {
|
||||
private static class SimpleTrackingInputStream extends TrackingInputStream {
|
||||
private long bytesRead = 0;
|
||||
public TrackingHTTPInputStream(InputStream stream) {
|
||||
public SimpleTrackingInputStream(InputStream stream) {
|
||||
super(stream);
|
||||
}
|
||||
|
||||
private int updateBytesRead(int newBytesRead) {
|
||||
if (newBytesRead != -1) {
|
||||
bytesRead += newBytesRead;
|
||||
}
|
||||
return newBytesRead;
|
||||
if (newBytesRead != -1) {
|
||||
bytesRead += newBytesRead;
|
||||
}
|
||||
return newBytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -174,7 +167,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
}
|
||||
|
||||
public long getTotalRawBytesRead() {
|
||||
return bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,6 +244,8 @@ public class FileTransfer extends CordovaPlugin {
|
||||
final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers") : args.optJSONObject(8);
|
||||
final String objectId = args.getString(9);
|
||||
final String httpMethod = getArgument(args, 10, "POST");
|
||||
|
||||
final CordovaResourceApi resourceApi = webView.getResourceApi();
|
||||
|
||||
Log.d(LOG_TAG, "fileKey: " + fileKey);
|
||||
Log.d(LOG_TAG, "fileName: " + fileName);
|
||||
@ -262,16 +257,20 @@ public class FileTransfer extends CordovaPlugin {
|
||||
Log.d(LOG_TAG, "objectId: " + objectId);
|
||||
Log.d(LOG_TAG, "httpMethod: " + httpMethod);
|
||||
|
||||
final URL url;
|
||||
try {
|
||||
url = new URL(target);
|
||||
} catch (MalformedURLException e) {
|
||||
final Uri targetUri = resourceApi.remapUri(Uri.parse(target));
|
||||
// Accept a path or a URI for the source.
|
||||
Uri tmpSrc = Uri.parse(source);
|
||||
final Uri sourceUri = resourceApi.remapUri(
|
||||
tmpSrc.getScheme() != null ? tmpSrc : Uri.fromFile(new File(source)));
|
||||
|
||||
int uriType = CordovaResourceApi.getUriType(targetUri);
|
||||
final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS;
|
||||
if (uriType != CordovaResourceApi.URI_TYPE_HTTP && !useHttps) {
|
||||
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
Log.e(LOG_TAG, "Unsupported URI: " + targetUri);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
return;
|
||||
}
|
||||
final boolean useHttps = url.getProtocol().equals("https");
|
||||
|
||||
final RequestContext context = new RequestContext(source, target, callbackContext);
|
||||
synchronized (activeRequests) {
|
||||
@ -295,27 +294,15 @@ public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
//------------------ CLIENT REQUEST
|
||||
// Open a HTTP connection to the URL based on protocol
|
||||
if (useHttps) {
|
||||
// Using standard HTTPS connection. Will not allow self signed certificate
|
||||
if (!trustEveryone) {
|
||||
conn = (HttpsURLConnection) httpClient.open(url);
|
||||
}
|
||||
// Use our HTTPS connection that blindly trusts everyone.
|
||||
// This should only be used in debug environments
|
||||
else {
|
||||
// Setup the HTTPS connection class to trust everyone
|
||||
HttpsURLConnection https = (HttpsURLConnection) httpClient.open(url);
|
||||
oldSocketFactory = trustAllHosts(https);
|
||||
// Save the current hostnameVerifier
|
||||
oldHostnameVerifier = https.getHostnameVerifier();
|
||||
// Setup the connection not to verify hostnames
|
||||
https.setHostnameVerifier(DO_NOT_VERIFY);
|
||||
conn = https;
|
||||
}
|
||||
}
|
||||
// Return a standard HTTP connection
|
||||
else {
|
||||
conn = httpClient.open(url);
|
||||
conn = resourceApi.createHttpConnection(targetUri);
|
||||
if (useHttps && trustEveryone) {
|
||||
// Setup the HTTPS connection class to trust everyone
|
||||
HttpsURLConnection https = (HttpsURLConnection)conn;
|
||||
oldSocketFactory = trustAllHosts(https);
|
||||
// Save the current hostnameVerifier
|
||||
oldHostnameVerifier = https.getHostnameVerifier();
|
||||
// Setup the connection not to verify hostnames
|
||||
https.setHostnameVerifier(DO_NOT_VERIFY);
|
||||
}
|
||||
|
||||
// Allow Inputs
|
||||
@ -372,11 +359,11 @@ public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
|
||||
// Get a input stream of the file on the phone
|
||||
InputStream sourceInputStream = getPathFromUri(source);
|
||||
OpenForReadResult readResult = resourceApi.openForRead(sourceUri);
|
||||
|
||||
int stringLength = beforeDataBytes.length + tailParamsBytes.length;
|
||||
if (sourceInputStream instanceof FileInputStream) {
|
||||
fixedLength = (int) ((FileInputStream)sourceInputStream).getChannel().size() + stringLength;
|
||||
if (readResult.length >= 0) {
|
||||
fixedLength = (int)readResult.length + stringLength;
|
||||
progress.setLengthComputable(true);
|
||||
progress.setTotal(fixedLength);
|
||||
}
|
||||
@ -412,12 +399,12 @@ public class FileTransfer extends CordovaPlugin {
|
||||
totalBytes += beforeDataBytes.length;
|
||||
|
||||
// create a buffer of maximum size
|
||||
int bytesAvailable = sourceInputStream.available();
|
||||
int bytesAvailable = readResult.inputStream.available();
|
||||
int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
|
||||
// read file and write it into form...
|
||||
int bytesRead = sourceInputStream.read(buffer, 0, bufferSize);
|
||||
int bytesRead = readResult.inputStream.read(buffer, 0, bufferSize);
|
||||
|
||||
long prevBytesRead = 0;
|
||||
while (bytesRead > 0) {
|
||||
@ -428,9 +415,9 @@ public class FileTransfer extends CordovaPlugin {
|
||||
prevBytesRead = totalBytes;
|
||||
Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes");
|
||||
}
|
||||
bytesAvailable = sourceInputStream.available();
|
||||
bytesAvailable = readResult.inputStream.available();
|
||||
bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
|
||||
bytesRead = sourceInputStream.read(buffer, 0, bufferSize);
|
||||
bytesRead = readResult.inputStream.read(buffer, 0, bufferSize);
|
||||
|
||||
// Send a progress event.
|
||||
progress.setLoaded(totalBytes);
|
||||
@ -444,7 +431,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
totalBytes += tailParamsBytes.length;
|
||||
sendStream.flush();
|
||||
} finally {
|
||||
safeClose(sourceInputStream);
|
||||
safeClose(readResult.inputStream);
|
||||
safeClose(sendStream);
|
||||
}
|
||||
context.currentOutputStream = null;
|
||||
@ -534,9 +521,9 @@ public class FileTransfer extends CordovaPlugin {
|
||||
private static TrackingInputStream getInputStream(URLConnection conn) throws IOException {
|
||||
String encoding = conn.getContentEncoding();
|
||||
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
|
||||
return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
|
||||
return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
|
||||
}
|
||||
return new TrackingHTTPInputStream(conn.getInputStream());
|
||||
return new SimpleTrackingInputStream(conn.getInputStream());
|
||||
}
|
||||
|
||||
// always verify the host - don't check for certificate
|
||||
@ -607,7 +594,8 @@ public class FileTransfer extends CordovaPlugin {
|
||||
body = bodyBuilder.toString();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// IOException can leave connection object in a bad state, so catch all exceptions.
|
||||
} catch (Throwable e) {
|
||||
Log.w(LOG_TAG, "Error getting HTTP status code from connection.", e);
|
||||
}
|
||||
}
|
||||
@ -617,7 +605,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
/**
|
||||
* Create an error object based on the passed in errorCode
|
||||
* @param errorCode the error
|
||||
* @param errorCode the error
|
||||
* @return JSONObject containing the error
|
||||
*/
|
||||
private static JSONObject createFileTransferError(int errorCode, String source, String target, String body, Integer httpStatus) {
|
||||
@ -642,8 +630,8 @@ public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
/**
|
||||
* Convenience method to read a parameter from the list of JSON args.
|
||||
* @param args the args passed to the Plugin
|
||||
* @param position the position to retrieve the arg from
|
||||
* @param args the args passed to the Plugin
|
||||
* @param position the position to retrieve the arg from
|
||||
* @param defaultString the default to be used if the arg does not exist
|
||||
* @return String with the retrieved value
|
||||
*/
|
||||
@ -662,27 +650,35 @@ public class FileTransfer extends CordovaPlugin {
|
||||
* Downloads a file form a given URL and saves it to the specified directory.
|
||||
*
|
||||
* @param source URL of the server to receive the file
|
||||
* @param target Full path of the file on the file system
|
||||
* @param target Full path of the file on the file system
|
||||
*/
|
||||
private void download(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
Log.d(LOG_TAG, "download " + source + " to " + target);
|
||||
|
||||
final CordovaResourceApi resourceApi = webView.getResourceApi();
|
||||
|
||||
final boolean trustEveryone = args.optBoolean(2);
|
||||
final String objectId = args.getString(3);
|
||||
final JSONObject headers = args.optJSONObject(4);
|
||||
|
||||
final Uri sourceUri = resourceApi.remapUri(Uri.parse(source));
|
||||
// Accept a path or a URI for the source.
|
||||
Uri tmpTarget = Uri.parse(target);
|
||||
final Uri targetUri = resourceApi.remapUri(
|
||||
tmpTarget.getScheme() != null ? tmpTarget : Uri.fromFile(new File(target)));
|
||||
|
||||
final URL url;
|
||||
try {
|
||||
url = new URL(source);
|
||||
} catch (MalformedURLException e) {
|
||||
int uriType = CordovaResourceApi.getUriType(sourceUri);
|
||||
final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS;
|
||||
final boolean isLocalTransfer = !useHttps && uriType != CordovaResourceApi.URI_TYPE_HTTP;
|
||||
if (uriType == CordovaResourceApi.URI_TYPE_UNKNOWN) {
|
||||
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
Log.e(LOG_TAG, "Unsupported URI: " + targetUri);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
return;
|
||||
}
|
||||
final boolean useHttps = url.getProtocol().equals("https");
|
||||
|
||||
if (!Config.isUrlWhiteListed(source)) {
|
||||
|
||||
// TODO: refactor to also allow resources & content:
|
||||
if (!isLocalTransfer && !Config.isUrlWhiteListed(source)) {
|
||||
Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'");
|
||||
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, null, 401);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
@ -700,81 +696,75 @@ public class FileTransfer extends CordovaPlugin {
|
||||
if (context.aborted) {
|
||||
return;
|
||||
}
|
||||
URLConnection connection = null;
|
||||
HttpURLConnection connection = null;
|
||||
HostnameVerifier oldHostnameVerifier = null;
|
||||
SSLSocketFactory oldSocketFactory = null;
|
||||
File file = null;
|
||||
PluginResult result = null;
|
||||
TrackingInputStream inputStream = null;
|
||||
|
||||
OutputStream outputStream = null;
|
||||
try {
|
||||
file = getFileFromPath(target);
|
||||
OpenForReadResult readResult = null;
|
||||
outputStream = resourceApi.openOutputStream(targetUri);
|
||||
|
||||
file = resourceApi.mapUriToFile(targetUri);
|
||||
context.targetFile = file;
|
||||
// create needed directories
|
||||
file.getParentFile().mkdirs();
|
||||
|
||||
// connect to server
|
||||
// Open a HTTP connection to the URL based on protocol
|
||||
if (useHttps) {
|
||||
// Using standard HTTPS connection. Will not allow self signed certificate
|
||||
if (!trustEveryone) {
|
||||
connection = (HttpsURLConnection) httpClient.open(url);
|
||||
|
||||
Log.d(LOG_TAG, "Download file:" + sourceUri);
|
||||
|
||||
FileProgressResult progress = new FileProgressResult();
|
||||
|
||||
if (isLocalTransfer) {
|
||||
readResult = resourceApi.openForRead(sourceUri);
|
||||
if (readResult.length != -1) {
|
||||
progress.setLengthComputable(true);
|
||||
progress.setTotal(readResult.length);
|
||||
}
|
||||
// Use our HTTPS connection that blindly trusts everyone.
|
||||
// This should only be used in debug environments
|
||||
else {
|
||||
inputStream = new SimpleTrackingInputStream(readResult.inputStream);
|
||||
} else {
|
||||
// connect to server
|
||||
// Open a HTTP connection to the URL based on protocol
|
||||
connection = resourceApi.createHttpConnection(sourceUri);
|
||||
if (useHttps && trustEveryone) {
|
||||
// Setup the HTTPS connection class to trust everyone
|
||||
HttpsURLConnection https = (HttpsURLConnection) httpClient.open(url);
|
||||
HttpsURLConnection https = (HttpsURLConnection)connection;
|
||||
oldSocketFactory = trustAllHosts(https);
|
||||
// Save the current hostnameVerifier
|
||||
oldHostnameVerifier = https.getHostnameVerifier();
|
||||
// Setup the connection not to verify hostnames
|
||||
https.setHostnameVerifier(DO_NOT_VERIFY);
|
||||
connection = https;
|
||||
}
|
||||
}
|
||||
// Return a standard HTTP connection
|
||||
else {
|
||||
connection = httpClient.open(url);
|
||||
|
||||
}
|
||||
|
||||
connection.setRequestMethod("GET");
|
||||
|
||||
// TODO: Make OkHttp use this CookieManager by default.
|
||||
String cookie = CookieManager.getInstance().getCookie(sourceUri.toString());
|
||||
if(cookie != null)
|
||||
{
|
||||
connection.setRequestProperty("cookie", cookie);
|
||||
}
|
||||
|
||||
// This must be explicitly set for gzip progress tracking to work.
|
||||
connection.setRequestProperty("Accept-Encoding", "gzip");
|
||||
|
||||
if (connection instanceof HttpURLConnection) {
|
||||
((HttpURLConnection)connection).setRequestMethod("GET");
|
||||
}
|
||||
// Handle the other headers
|
||||
if (headers != null) {
|
||||
addHeadersToRequest(connection, headers);
|
||||
}
|
||||
|
||||
connection.connect();
|
||||
|
||||
//Add cookie support
|
||||
String cookie = CookieManager.getInstance().getCookie(source);
|
||||
if(cookie != null)
|
||||
{
|
||||
connection.setRequestProperty("cookie", cookie);
|
||||
if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
|
||||
// Only trust content-length header if we understand
|
||||
// the encoding -- identity or gzip
|
||||
progress.setLengthComputable(true);
|
||||
progress.setTotal(connection.getContentLength());
|
||||
}
|
||||
inputStream = getInputStream(connection);
|
||||
}
|
||||
|
||||
// This must be explicitly set for gzip progress tracking to work.
|
||||
connection.setRequestProperty("Accept-Encoding", "gzip");
|
||||
|
||||
// Handle the other headers
|
||||
if (headers != null) {
|
||||
addHeadersToRequest(connection, headers);
|
||||
}
|
||||
|
||||
connection.connect();
|
||||
|
||||
Log.d(LOG_TAG, "Download file:" + url);
|
||||
|
||||
FileProgressResult progress = new FileProgressResult();
|
||||
if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
|
||||
// Only trust content-length header if we understand
|
||||
// the encoding -- identity or gzip
|
||||
progress.setLengthComputable(true);
|
||||
progress.setTotal(connection.getContentLength());
|
||||
}
|
||||
|
||||
FileOutputStream outputStream = null;
|
||||
TrackingInputStream inputStream = null;
|
||||
|
||||
try {
|
||||
inputStream = getInputStream(connection);
|
||||
outputStream = new FileOutputStream(file);
|
||||
synchronized (context) {
|
||||
if (context.aborted) {
|
||||
return;
|
||||
@ -821,6 +811,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} finally {
|
||||
safeClose(outputStream);
|
||||
synchronized (activeRequests) {
|
||||
activeRequests.remove(objectId);
|
||||
}
|
||||
@ -847,49 +838,6 @@ public class FileTransfer extends CordovaPlugin {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an input stream based on file path or content:// uri
|
||||
*
|
||||
* @param path foo
|
||||
* @return an input stream
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
private InputStream getPathFromUri(String path) throws FileNotFoundException {
|
||||
try {
|
||||
InputStream stream = FileHelper.getInputStreamFromUriString(path, cordova);
|
||||
if (stream == null) {
|
||||
return new FileInputStream(path);
|
||||
} else {
|
||||
return stream;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a File object from the passed in path
|
||||
*
|
||||
* @param path file path
|
||||
* @return file object
|
||||
*/
|
||||
private File getFileFromPath(String path) throws FileNotFoundException {
|
||||
File file;
|
||||
String prefix = "file://";
|
||||
|
||||
if (path.startsWith(prefix)) {
|
||||
file = new File(path.substring(prefix.length()));
|
||||
} else {
|
||||
file = new File(path);
|
||||
}
|
||||
|
||||
if (file.getParent() == null) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort an ongoing upload or download.
|
||||
*/
|
||||
|
@ -28,11 +28,9 @@ import android.util.Log;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.apache.cordova.file.EncodingException;
|
||||
import org.apache.cordova.file.FileExistsException;
|
||||
import org.apache.cordova.file.InvalidModificationException;
|
||||
import org.apache.cordova.file.NoModificationAllowedException;
|
||||
import org.apache.cordova.file.TypeMismatchException;
|
||||
import org.apache.cordova.file.*;
|
||||
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@ -489,12 +487,13 @@ public class FileUtils extends CordovaPlugin {
|
||||
throw new NoModificationAllowedException("Couldn't create the destination directory");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (File file : srcDir.listFiles()) {
|
||||
File destination = new File(destinationDir.getAbsoluteFile() + File.separator + file.getName());
|
||||
if (file.isDirectory()) {
|
||||
copyDirectory(file, destinationDir);
|
||||
copyDirectory(file, destination);
|
||||
} else {
|
||||
File destination = new File(destinationDir.getAbsoluteFile() + File.separator + file.getName());
|
||||
copyFile(file, destination);
|
||||
}
|
||||
}
|
||||
@ -914,11 +913,6 @@ public class FileUtils extends CordovaPlugin {
|
||||
return getEntry(new File(path));
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Read the contents of a file.
|
||||
* This is done in a background thread; the result is sent to the callback.
|
||||
@ -950,7 +944,7 @@ public class FileUtils extends CordovaPlugin {
|
||||
break;
|
||||
default: // Base64.
|
||||
String contentType = FileHelper.getMimeType(filename, cordova);
|
||||
byte[] base64 = Base64.encode(bytes, Base64.DEFAULT);
|
||||
byte[] base64 = Base64.encode(bytes, Base64.NO_WRAP);
|
||||
String s = "data:" + contentType + ";base64," + new String(base64, "US-ASCII");
|
||||
result = new PluginResult(PluginResult.Status.OK, s);
|
||||
}
|
||||
@ -1024,12 +1018,21 @@ public class FileUtils extends CordovaPlugin {
|
||||
rawData = data.getBytes();
|
||||
}
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(rawData);
|
||||
FileOutputStream out = new FileOutputStream(filename, append);
|
||||
byte buff[] = new byte[rawData.length];
|
||||
in.read(buff, 0, buff.length);
|
||||
out.write(buff, 0, rawData.length);
|
||||
out.flush();
|
||||
out.close();
|
||||
try
|
||||
{
|
||||
FileOutputStream out = new FileOutputStream(filename, append);
|
||||
byte buff[] = new byte[rawData.length];
|
||||
in.read(buff, 0, buff.length);
|
||||
out.write(buff, 0, rawData.length);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
catch (NullPointerException e)
|
||||
{
|
||||
// This is a bug in the Android implementation of the Java Stack
|
||||
NoModificationAllowedException realException = new NoModificationAllowedException(filename);
|
||||
throw realException;
|
||||
}
|
||||
|
||||
return rawData.length;
|
||||
}
|
||||
|
@ -55,14 +55,18 @@ public class GeoBroker extends CordovaPlugin {
|
||||
* @return True if the action was valid, or false if not.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (this.locationManager == null) {
|
||||
this.locationManager = (LocationManager) this.cordova.getActivity().getSystemService(Context.LOCATION_SERVICE);
|
||||
this.networkListener = new NetworkListener(this.locationManager, this);
|
||||
this.gpsListener = new GPSListener(this.locationManager, this);
|
||||
if (locationManager == null) {
|
||||
locationManager = (LocationManager) this.cordova.getActivity().getSystemService(Context.LOCATION_SERVICE);
|
||||
}
|
||||
|
||||
if ( locationManager.isProviderEnabled( LocationManager.GPS_PROVIDER ) ||
|
||||
locationManager.isProviderEnabled( LocationManager.NETWORK_PROVIDER )) {
|
||||
if (networkListener == null) {
|
||||
networkListener = new NetworkListener(locationManager, this);
|
||||
}
|
||||
if (gpsListener == null) {
|
||||
gpsListener = new GPSListener(locationManager, this);
|
||||
}
|
||||
|
||||
if (action.equals("getLocation")) {
|
||||
boolean enableHighAccuracy = args.getBoolean(0);
|
||||
|
@ -18,9 +18,10 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.cordova.CordovaResourceApi.OpenForReadResult;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.LOG;
|
||||
|
||||
@ -33,6 +34,7 @@ import android.webkit.WebView;
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
|
||||
private static final String TAG = "IceCreamCordovaWebViewClient";
|
||||
|
||||
public IceCreamCordovaWebViewClient(CordovaInterface cordova) {
|
||||
super(cordova);
|
||||
@ -44,31 +46,43 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||
UriResolver uriResolver = appView.resolveUri(Uri.parse(url), true);
|
||||
|
||||
if (uriResolver == null && url.startsWith("file:///android_asset/")) {
|
||||
if (url.contains("?") || url.contains("#") || needsIceCreamSpecialsInAssetUrlFix(url)) {
|
||||
uriResolver = appView.resolveUri(Uri.parse(url), false);
|
||||
}
|
||||
}
|
||||
|
||||
if (uriResolver != null) {
|
||||
try {
|
||||
InputStream stream = uriResolver.getInputStream();
|
||||
String mimeType = uriResolver.getMimeType();
|
||||
// If we don't know how to open this file, let the browser continue loading
|
||||
return new WebResourceResponse(mimeType, "UTF-8", stream);
|
||||
} catch (IOException e) {
|
||||
LOG.e("IceCreamCordovaWebViewClient", "Error occurred while loading a file.", e);
|
||||
try {
|
||||
// Check the against the white-list.
|
||||
if ((url.startsWith("http:") || url.startsWith("https:")) && !Config.isUrlWhiteListed(url)) {
|
||||
LOG.w(TAG, "URL blocked by whitelist: " + url);
|
||||
// Results in a 404.
|
||||
return new WebResourceResponse("text/plain", "UTF-8", null);
|
||||
}
|
||||
|
||||
CordovaResourceApi resourceApi = appView.getResourceApi();
|
||||
Uri origUri = Uri.parse(url);
|
||||
// Allow plugins to intercept WebView requests.
|
||||
Uri remappedUri = resourceApi.remapUri(origUri);
|
||||
|
||||
if (!origUri.equals(remappedUri) || needsSpecialsInAssetUrlFix(origUri)) {
|
||||
OpenForReadResult result = resourceApi.openForRead(remappedUri, true);
|
||||
return new WebResourceResponse(result.mimeType, "UTF-8", result.inputStream);
|
||||
}
|
||||
// If we don't need to special-case the request, let the browser load it.
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
if (!(e instanceof FileNotFoundException)) {
|
||||
LOG.e("IceCreamCordovaWebViewClient", "Error occurred while loading a file (returning a 404).", e);
|
||||
}
|
||||
// Results in a 404.
|
||||
return new WebResourceResponse("text/plain", "UTF-8", null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean needsSpecialsInAssetUrlFix(Uri uri) {
|
||||
if (CordovaResourceApi.getUriType(uri) != CordovaResourceApi.URI_TYPE_ASSET) {
|
||||
return false;
|
||||
}
|
||||
if (uri.getQuery() != null || uri.getFragment() != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean needsIceCreamSpecialsInAssetUrlFix(String url) {
|
||||
if (!url.contains("%20")){
|
||||
if (!uri.toString().contains("%")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -76,8 +90,7 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH:
|
||||
case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -18,24 +18,12 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
@ -49,11 +37,8 @@ import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.Button;
|
||||
@ -61,6 +46,19 @@ import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import org.apache.cordova.Config;
|
||||
import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
public class InAppBrowser extends CordovaPlugin {
|
||||
|
||||
@ -76,7 +74,8 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
private static final String LOAD_STOP_EVENT = "loadstop";
|
||||
private static final String LOAD_ERROR_EVENT = "loaderror";
|
||||
private static final String CLOSE_BUTTON_CAPTION = "closebuttoncaption";
|
||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||
private static final String CLEAR_ALL_CACHE = "clearcache";
|
||||
private static final String CLEAR_SESSION_CACHE = "clearsessioncache";
|
||||
|
||||
private Dialog dialog;
|
||||
private WebView inAppWebView;
|
||||
@ -85,7 +84,9 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
private boolean showLocationBar = true;
|
||||
private boolean openWindowHidden = false;
|
||||
private String buttonLabel = "Done";
|
||||
|
||||
private boolean clearAllCache= false;
|
||||
private boolean clearSessionCache=false;
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
@ -94,118 +95,134 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
try {
|
||||
if (action.equals("open")) {
|
||||
this.callbackContext = callbackContext;
|
||||
String url = args.getString(0);
|
||||
String target = args.optString(1);
|
||||
if (target == null || target.equals("") || target.equals(NULL)) {
|
||||
target = SELF;
|
||||
}
|
||||
HashMap<String, Boolean> features = parseFeature(args.optString(2));
|
||||
|
||||
Log.d(LOG_TAG, "target = " + target);
|
||||
|
||||
url = updateUrl(url);
|
||||
String result = "";
|
||||
|
||||
// SELF
|
||||
if (SELF.equals(target)) {
|
||||
Log.d(LOG_TAG, "in self");
|
||||
// load in webview
|
||||
if (url.startsWith("file://") || url.startsWith("javascript:")
|
||||
|| Config.isUrlWhiteListed(url)) {
|
||||
this.webView.loadUrl(url);
|
||||
}
|
||||
//Load the dialer
|
||||
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(LOG_TAG, "Error dialing " + url + ": " + e.toString());
|
||||
public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("open")) {
|
||||
this.callbackContext = callbackContext;
|
||||
final String url = args.getString(0);
|
||||
String t = args.optString(1);
|
||||
if (t == null || t.equals("") || t.equals(NULL)) {
|
||||
t = SELF;
|
||||
}
|
||||
final String target = t;
|
||||
final HashMap<String, Boolean> features = parseFeature(args.optString(2));
|
||||
|
||||
Log.d(LOG_TAG, "target = " + target);
|
||||
|
||||
this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String result = "";
|
||||
// SELF
|
||||
if (SELF.equals(target)) {
|
||||
Log.d(LOG_TAG, "in self");
|
||||
// load in webview
|
||||
if (url.startsWith("file://") || url.startsWith("javascript:")
|
||||
|| Config.isUrlWhiteListed(url)) {
|
||||
webView.loadUrl(url);
|
||||
}
|
||||
//Load the dialer
|
||||
else if (url.startsWith(WebView.SCHEME_TEL))
|
||||
{
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_DIAL);
|
||||
intent.setData(Uri.parse(url));
|
||||
cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
|
||||
}
|
||||
}
|
||||
// load in InAppBrowser
|
||||
else {
|
||||
result = showWebPage(url, features);
|
||||
}
|
||||
}
|
||||
// load in InAppBrowser
|
||||
// SYSTEM
|
||||
else if (SYSTEM.equals(target)) {
|
||||
Log.d(LOG_TAG, "in system");
|
||||
result = openExternal(url);
|
||||
}
|
||||
// BLANK - or anything else
|
||||
else {
|
||||
result = this.showWebPage(url, features);
|
||||
Log.d(LOG_TAG, "in blank");
|
||||
result = showWebPage(url, features);
|
||||
}
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
|
||||
pluginResult.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
// SYSTEM
|
||||
else if (SYSTEM.equals(target)) {
|
||||
Log.d(LOG_TAG, "in system");
|
||||
result = this.openExternal(url);
|
||||
});
|
||||
}
|
||||
else if (action.equals("close")) {
|
||||
closeDialog();
|
||||
}
|
||||
else if (action.equals("injectScriptCode")) {
|
||||
String jsWrapper = null;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId());
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("injectScriptFile")) {
|
||||
String jsWrapper;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId());
|
||||
} else {
|
||||
jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)";
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("injectStyleCode")) {
|
||||
String jsWrapper;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
|
||||
} else {
|
||||
jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)";
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("injectStyleFile")) {
|
||||
String jsWrapper;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
|
||||
} else {
|
||||
jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)";
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("show")) {
|
||||
this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
dialog.show();
|
||||
}
|
||||
// BLANK - or anything else
|
||||
else {
|
||||
Log.d(LOG_TAG, "in blank");
|
||||
result = this.showWebPage(url, features);
|
||||
}
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
|
||||
pluginResult.setKeepCallback(true);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
else if (action.equals("close")) {
|
||||
closeDialog();
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
|
||||
}
|
||||
else if (action.equals("injectScriptCode")) {
|
||||
String jsWrapper = null;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId());
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("injectScriptFile")) {
|
||||
String jsWrapper;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId());
|
||||
} else {
|
||||
jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)";
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("injectStyleCode")) {
|
||||
String jsWrapper;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
|
||||
} else {
|
||||
jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)";
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("injectStyleFile")) {
|
||||
String jsWrapper;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
|
||||
} else {
|
||||
jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)";
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("show")) {
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
dialog.show();
|
||||
}
|
||||
};
|
||||
this.cordova.getActivity().runOnUiThread(runnable);
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
|
||||
});
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
|
||||
pluginResult.setKeepCallback(true);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the view navigates.
|
||||
*/
|
||||
@Override
|
||||
public void onReset() {
|
||||
closeDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by AccelBroker when listener is to be shut down.
|
||||
* Stop listener.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
closeDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject an object (script or style) into the InAppBrowser WebView.
|
||||
*
|
||||
@ -233,8 +250,14 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
} else {
|
||||
scriptToInject = source;
|
||||
}
|
||||
final String finalScriptToInject = scriptToInject;
|
||||
// This action will have the side-effect of blurring the currently focused element
|
||||
this.inAppWebView.loadUrl("javascript:" + scriptToInject);
|
||||
this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
inAppWebView.loadUrl("javascript:" + finalScriptToInject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -266,20 +289,6 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert relative URL to full path
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
private String updateUrl(String url) {
|
||||
Uri newUrl = Uri.parse(url);
|
||||
if (newUrl.isRelative()) {
|
||||
url = this.webView.getUrl().substring(0, this.webView.getUrl().lastIndexOf("/")+1) + url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a new browser with the specified URL.
|
||||
*
|
||||
@ -303,20 +312,30 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
/**
|
||||
* Closes the dialog
|
||||
*/
|
||||
private void closeDialog() {
|
||||
public void closeDialog() {
|
||||
final WebView childView = this.inAppWebView;
|
||||
// The JS protects against multiple calls, so this should happen only when
|
||||
// closeDialog() is called by other native code.
|
||||
if (childView == null) {
|
||||
return;
|
||||
}
|
||||
this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
childView.loadUrl("about:blank");
|
||||
if (dialog != null) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
this.inAppWebView.loadUrl("about:blank");
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("type", EXIT_EVENT);
|
||||
|
||||
sendUpdate(obj, false);
|
||||
} catch (JSONException ex) {
|
||||
Log.d(LOG_TAG, "Should never happen");
|
||||
}
|
||||
|
||||
if (dialog != null) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -380,9 +399,18 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
showLocationBar = show.booleanValue();
|
||||
}
|
||||
Boolean hidden = features.get(HIDDEN);
|
||||
if(hidden != null) {
|
||||
if (hidden != null) {
|
||||
openWindowHidden = hidden.booleanValue();
|
||||
}
|
||||
Boolean cache = features.get(CLEAR_ALL_CACHE);
|
||||
if (cache != null) {
|
||||
clearAllCache = cache.booleanValue();
|
||||
} else {
|
||||
cache = features.get(CLEAR_SESSION_CACHE);
|
||||
if (cache != null) {
|
||||
clearSessionCache = cache.booleanValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final CordovaWebView thatWebView = this.webView;
|
||||
@ -411,14 +439,7 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
dialog.setCancelable(true);
|
||||
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("type", EXIT_EVENT);
|
||||
|
||||
sendUpdate(obj, false);
|
||||
} catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Should never happen");
|
||||
}
|
||||
closeDialog();
|
||||
}
|
||||
});
|
||||
|
||||
@ -428,6 +449,8 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
|
||||
// Toolbar layout
|
||||
RelativeLayout toolbar = new RelativeLayout(cordova.getActivity());
|
||||
//Please, no more black!
|
||||
toolbar.setBackgroundColor(android.graphics.Color.LTGRAY);
|
||||
toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44)));
|
||||
toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));
|
||||
toolbar.setHorizontalGravity(Gravity.LEFT);
|
||||
@ -515,24 +538,24 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
settings.setBuiltInZoomControls(true);
|
||||
/**
|
||||
* We need to be careful of this line as a future Android release may deprecate it out of existence.
|
||||
* Can't replace it with the API 8 level call right now as our minimum SDK is 7 until May 2013
|
||||
*/
|
||||
// @TODO: replace with settings.setPluginState(android.webkit.WebSettings.PluginState.ON)
|
||||
settings.setPluginsEnabled(true);
|
||||
|
||||
settings.setPluginState(android.webkit.WebSettings.PluginState.ON);
|
||||
|
||||
//Toggle whether this is enabled or not!
|
||||
Bundle appSettings = cordova.getActivity().getIntent().getExtras();
|
||||
boolean enableDatabase = appSettings == null ? true : appSettings.getBoolean("InAppBrowserStorageEnabled", true);
|
||||
if(enableDatabase)
|
||||
{
|
||||
if (enableDatabase) {
|
||||
String databasePath = cordova.getActivity().getApplicationContext().getDir("inAppBrowserDB", Context.MODE_PRIVATE).getPath();
|
||||
settings.setDatabasePath(databasePath);
|
||||
settings.setDatabaseEnabled(true);
|
||||
}
|
||||
settings.setDomStorageEnabled(true);
|
||||
|
||||
|
||||
if (clearAllCache) {
|
||||
CookieManager.getInstance().removeAllCookie();
|
||||
} else if (clearSessionCache) {
|
||||
CookieManager.getInstance().removeSessionCookie();
|
||||
}
|
||||
|
||||
inAppWebView.loadUrl(url);
|
||||
inAppWebView.setId(6);
|
||||
inAppWebView.getSettings().setLoadWithOverviewMode(true);
|
||||
@ -591,114 +614,19 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
*
|
||||
* @param obj a JSONObject contain event payload information
|
||||
* @param status the status code to return to the JavaScript environment
|
||||
*/ private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) {
|
||||
PluginResult result = new PluginResult(status, obj);
|
||||
result.setKeepCallback(keepCallback);
|
||||
this.callbackContext.sendPluginResult(result);
|
||||
*/
|
||||
private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) {
|
||||
if (callbackContext != null) {
|
||||
PluginResult result = new PluginResult(status, obj);
|
||||
result.setKeepCallback(keepCallback);
|
||||
callbackContext.sendPluginResult(result);
|
||||
if (!keepCallback) {
|
||||
callbackContext = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class InAppChromeClient extends WebChromeClient {
|
||||
|
||||
private CordovaWebView webView;
|
||||
|
||||
public InAppChromeClient(CordovaWebView webView) {
|
||||
super();
|
||||
this.webView = webView;
|
||||
}
|
||||
/**
|
||||
* Handle database quota exceeded notification.
|
||||
*
|
||||
* @param url
|
||||
* @param databaseIdentifier
|
||||
* @param currentQuota
|
||||
* @param estimatedSize
|
||||
* @param totalUsedQuota
|
||||
* @param quotaUpdater
|
||||
*/
|
||||
@Override
|
||||
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
|
||||
long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
|
||||
{
|
||||
LOG.d(LOG_TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota);
|
||||
|
||||
if (estimatedSize < MAX_QUOTA)
|
||||
{
|
||||
//increase for 1Mb
|
||||
long newQuota = estimatedSize;
|
||||
LOG.d(LOG_TAG, "calling quotaUpdater.updateQuota newQuota: %d", newQuota);
|
||||
quotaUpdater.updateQuota(newQuota);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the quota to whatever it is and force an error
|
||||
// TODO: get docs on how to handle this properly
|
||||
quotaUpdater.updateQuota(currentQuota);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
|
||||
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
||||
callback.invoke(origin, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* The prompt bridge provided for the InAppBrowser is capable of executing any
|
||||
* oustanding callback belonging to the InAppBrowser plugin. Care has been
|
||||
* taken that other callbacks cannot be triggered, and that no other code
|
||||
* execution is possible.
|
||||
*
|
||||
* To trigger the bridge, the prompt default value should be of the form:
|
||||
*
|
||||
* gap-iab://<callbackId>
|
||||
*
|
||||
* where <callbackId> is the string id of the callback to trigger (something
|
||||
* like "InAppBrowser0123456789")
|
||||
*
|
||||
* If present, the prompt message is expected to be a JSON-encoded value to
|
||||
* pass to the callback. A JSON_EXCEPTION is returned if the JSON is invalid.
|
||||
*
|
||||
* @param view
|
||||
* @param url
|
||||
* @param message
|
||||
* @param defaultValue
|
||||
* @param result
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
|
||||
// See if the prompt string uses the 'gap-iab' protocol. If so, the remainder should be the id of a callback to execute.
|
||||
if (defaultValue != null && defaultValue.startsWith("gap-iab://")) {
|
||||
PluginResult scriptResult;
|
||||
String scriptCallbackId = defaultValue.substring(10);
|
||||
if (scriptCallbackId.startsWith("InAppBrowser")) {
|
||||
if(message == null || message.length() == 0) {
|
||||
scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray());
|
||||
} else {
|
||||
try {
|
||||
scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray(message));
|
||||
} catch(JSONException e) {
|
||||
scriptResult = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
|
||||
}
|
||||
}
|
||||
this.webView.sendPluginResult(scriptResult, scriptCallbackId);
|
||||
result.confirm("");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The webview client receives notifications about appView
|
||||
|
128
framework/src/org/apache/cordova/InAppChromeClient.java
Normal file
@ -0,0 +1,128 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
|
||||
public class InAppChromeClient extends WebChromeClient {
|
||||
|
||||
private CordovaWebView webView;
|
||||
private String LOG_TAG = "InAppChromeClient";
|
||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||
|
||||
public InAppChromeClient(CordovaWebView webView) {
|
||||
super();
|
||||
this.webView = webView;
|
||||
}
|
||||
/**
|
||||
* Handle database quota exceeded notification.
|
||||
*
|
||||
* @param url
|
||||
* @param databaseIdentifier
|
||||
* @param currentQuota
|
||||
* @param estimatedSize
|
||||
* @param totalUsedQuota
|
||||
* @param quotaUpdater
|
||||
*/
|
||||
@Override
|
||||
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
|
||||
long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
|
||||
{
|
||||
LOG.d(LOG_TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota);
|
||||
|
||||
if (estimatedSize < MAX_QUOTA)
|
||||
{
|
||||
//increase for 1Mb
|
||||
long newQuota = estimatedSize;
|
||||
LOG.d(LOG_TAG, "calling quotaUpdater.updateQuota newQuota: %d", newQuota);
|
||||
quotaUpdater.updateQuota(newQuota);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the quota to whatever it is and force an error
|
||||
// TODO: get docs on how to handle this properly
|
||||
quotaUpdater.updateQuota(currentQuota);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
|
||||
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
||||
callback.invoke(origin, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* The prompt bridge provided for the InAppBrowser is capable of executing any
|
||||
* oustanding callback belonging to the InAppBrowser plugin. Care has been
|
||||
* taken that other callbacks cannot be triggered, and that no other code
|
||||
* execution is possible.
|
||||
*
|
||||
* To trigger the bridge, the prompt default value should be of the form:
|
||||
*
|
||||
* gap-iab://<callbackId>
|
||||
*
|
||||
* where <callbackId> is the string id of the callback to trigger (something
|
||||
* like "InAppBrowser0123456789")
|
||||
*
|
||||
* If present, the prompt message is expected to be a JSON-encoded value to
|
||||
* pass to the callback. A JSON_EXCEPTION is returned if the JSON is invalid.
|
||||
*
|
||||
* @param view
|
||||
* @param url
|
||||
* @param message
|
||||
* @param defaultValue
|
||||
* @param result
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
|
||||
// See if the prompt string uses the 'gap-iab' protocol. If so, the remainder should be the id of a callback to execute.
|
||||
if (defaultValue != null && defaultValue.startsWith("gap")) {
|
||||
if(defaultValue.startsWith("gap-iab://")) {
|
||||
PluginResult scriptResult;
|
||||
String scriptCallbackId = defaultValue.substring(10);
|
||||
if (scriptCallbackId.startsWith("InAppBrowser")) {
|
||||
if(message == null || message.length() == 0) {
|
||||
scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray());
|
||||
} else {
|
||||
try {
|
||||
scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray(message));
|
||||
} catch(JSONException e) {
|
||||
scriptResult = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
|
||||
}
|
||||
}
|
||||
this.webView.sendPluginResult(scriptResult, scriptCallbackId);
|
||||
result.confirm("");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Anything else with a gap: prefix should get this message
|
||||
LOG.w(LOG_TAG, "InAppBrowser does not support Cordova API calls: " + url + " " + defaultValue);
|
||||
result.cancel();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -138,8 +138,9 @@ public class NativeToJsMessageQueue {
|
||||
* Combines as many messages as possible, while staying under MAX_PAYLOAD_SIZE.
|
||||
* Returns null if the queue is empty.
|
||||
*/
|
||||
public String popAndEncode() {
|
||||
public String popAndEncode(boolean fromOnlineEvent) {
|
||||
synchronized (this) {
|
||||
registeredListeners[activeListenerIndex].notifyOfFlush(fromOnlineEvent);
|
||||
if (queue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@ -274,12 +275,13 @@ public class NativeToJsMessageQueue {
|
||||
return paused;
|
||||
}
|
||||
|
||||
private interface BridgeMode {
|
||||
void onNativeToJsMessageAvailable();
|
||||
private abstract class BridgeMode {
|
||||
abstract void onNativeToJsMessageAvailable();
|
||||
void notifyOfFlush(boolean fromOnlineEvent) {}
|
||||
}
|
||||
|
||||
/** Uses webView.loadUrl("javascript:") to execute messages. */
|
||||
private class LoadUrlBridgeMode implements BridgeMode {
|
||||
private class LoadUrlBridgeMode extends BridgeMode {
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
String js = popAndEncodeAsJs();
|
||||
@ -289,18 +291,17 @@ public class NativeToJsMessageQueue {
|
||||
}
|
||||
};
|
||||
|
||||
public void onNativeToJsMessageAvailable() {
|
||||
@Override void onNativeToJsMessageAvailable() {
|
||||
cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
/** Uses online/offline events to tell the JS when to poll for messages. */
|
||||
private class OnlineEventsBridgeMode implements BridgeMode {
|
||||
boolean online = true;
|
||||
private class OnlineEventsBridgeMode extends BridgeMode {
|
||||
boolean online = false;
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
if (!queue.isEmpty()) {
|
||||
online = !online;
|
||||
webView.setNetworkAvailable(online);
|
||||
}
|
||||
}
|
||||
@ -308,16 +309,22 @@ public class NativeToJsMessageQueue {
|
||||
OnlineEventsBridgeMode() {
|
||||
webView.setNetworkAvailable(true);
|
||||
}
|
||||
public void onNativeToJsMessageAvailable() {
|
||||
@Override void onNativeToJsMessageAvailable() {
|
||||
cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
// Track when online/offline events are fired so that we don't fire excess events.
|
||||
@Override void notifyOfFlush(boolean fromOnlineEvent) {
|
||||
if (fromOnlineEvent) {
|
||||
online = !online;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Java reflection to access an API that lets us eval JS.
|
||||
* Requires Android 3.2.4 or above.
|
||||
*/
|
||||
private class PrivateApiBridgeMode implements BridgeMode {
|
||||
private class PrivateApiBridgeMode extends BridgeMode {
|
||||
// Message added in commit:
|
||||
// http://omapzoom.org/?p=platform/frameworks/base.git;a=commitdiff;h=9497c5f8c4bc7c47789e5ccde01179abc31ffeb2
|
||||
// Which first appeared in 3.2.4ish.
|
||||
@ -355,7 +362,7 @@ public class NativeToJsMessageQueue {
|
||||
}
|
||||
}
|
||||
|
||||
public void onNativeToJsMessageAvailable() {
|
||||
@Override void onNativeToJsMessageAvailable() {
|
||||
if (sendMessageMethod == null && !initFailed) {
|
||||
initReflection();
|
||||
}
|
||||
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
/*
|
||||
* Interface for a class that can resolve URIs.
|
||||
* See CordovaUriResolver for an example.
|
||||
*/
|
||||
public interface UriResolver {
|
||||
|
||||
/** Returns the URI that this instance will resolve. */
|
||||
Uri getUri();
|
||||
|
||||
/**
|
||||
* Returns the InputStream for the resource.
|
||||
* Throws an exception if it cannot be read.
|
||||
* Never returns null.
|
||||
*/
|
||||
InputStream getInputStream() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the OutputStream for the resource.
|
||||
* Throws an exception if it cannot be written to.
|
||||
* Never returns null.
|
||||
*/
|
||||
OutputStream getOutputStream() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the MIME type of the resource.
|
||||
* Returns null if the MIME type cannot be determined (e.g. content: that doesn't exist).
|
||||
*/
|
||||
String getMimeType();
|
||||
|
||||
/** Returns whether the resource is writable. */
|
||||
boolean isWritable();
|
||||
|
||||
/**
|
||||
* Returns a File that points to the resource, or null if the resource
|
||||
* is not on the local file system.
|
||||
*/
|
||||
File getLocalFile();
|
||||
}
|
@ -1,277 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.cordova.FileHelper;
|
||||
import org.apache.http.util.EncodingUtils;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
|
||||
/*
|
||||
* UriResolver implementations.
|
||||
*/
|
||||
public final class UriResolvers {
|
||||
private UriResolvers() {}
|
||||
|
||||
private static final class FileUriResolver implements UriResolver {
|
||||
private final Uri uri;
|
||||
private String mimeType;
|
||||
private File localFile;
|
||||
|
||||
FileUriResolver(Uri uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new FileInputStream(getLocalFile());
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws FileNotFoundException {
|
||||
return new FileOutputStream(getLocalFile());
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
if (mimeType == null) {
|
||||
mimeType = FileHelper.getMimeTypeForExtension(getLocalFile().getName());
|
||||
}
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public boolean isWritable() {
|
||||
File f = getLocalFile();
|
||||
if (f.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
if (f.exists()) {
|
||||
return f.canWrite();
|
||||
}
|
||||
return f.getParentFile().canWrite();
|
||||
}
|
||||
|
||||
public File getLocalFile() {
|
||||
if (localFile == null) {
|
||||
localFile = new File(uri.getPath());
|
||||
}
|
||||
return localFile;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class AssetUriResolver implements UriResolver {
|
||||
private final Uri uri;
|
||||
private final AssetManager assetManager;
|
||||
private final String assetPath;
|
||||
private String mimeType;
|
||||
|
||||
AssetUriResolver(Uri uri, AssetManager assetManager) {
|
||||
this.uri = uri;
|
||||
this.assetManager = assetManager;
|
||||
this.assetPath = uri.getPath().substring(15);
|
||||
}
|
||||
|
||||
public Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return assetManager.open(assetPath);
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws FileNotFoundException {
|
||||
throw new FileNotFoundException("URI not writable.");
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
if (mimeType == null) {
|
||||
mimeType = FileHelper.getMimeTypeForExtension(assetPath);
|
||||
}
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public boolean isWritable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public File getLocalFile() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ContentUriResolver implements UriResolver {
|
||||
private final Uri uri;
|
||||
private final ContentResolver contentResolver;
|
||||
private String mimeType;
|
||||
|
||||
ContentUriResolver(Uri uri, ContentResolver contentResolver) {
|
||||
this.uri = uri;
|
||||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
public Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return contentResolver.openInputStream(uri);
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws FileNotFoundException {
|
||||
return contentResolver.openOutputStream(uri);
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
if (mimeType == null) {
|
||||
mimeType = contentResolver.getType(uri);
|
||||
}
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public boolean isWritable() {
|
||||
return uri.getScheme().equals(ContentResolver.SCHEME_CONTENT);
|
||||
}
|
||||
|
||||
public File getLocalFile() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ErrorUriResolver implements UriResolver {
|
||||
final Uri uri;
|
||||
final String errorMsg;
|
||||
|
||||
ErrorUriResolver(Uri uri, String errorMsg) {
|
||||
this.uri = uri;
|
||||
this.errorMsg = errorMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLocalFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
throw new FileNotFoundException(errorMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
throw new FileNotFoundException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ReadOnlyResolver implements UriResolver {
|
||||
private Uri uri;
|
||||
private InputStream inputStream;
|
||||
private String mimeType;
|
||||
|
||||
public ReadOnlyResolver(Uri uri, InputStream inputStream, String mimeType) {
|
||||
this.uri = uri;
|
||||
this.inputStream = inputStream;
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLocalFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
throw new FileNotFoundException("URI is not writable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return inputStream;
|
||||
}
|
||||
}
|
||||
|
||||
public static UriResolver createInline(Uri uri, String response, String mimeType) {
|
||||
return createInline(uri, EncodingUtils.getBytes(response, "UTF-8"), mimeType);
|
||||
}
|
||||
|
||||
public static UriResolver createInline(Uri uri, byte[] response, String mimeType) {
|
||||
return new ReadOnlyResolver(uri, new ByteArrayInputStream(response), mimeType);
|
||||
}
|
||||
|
||||
public static UriResolver createReadOnly(Uri uri, InputStream inputStream, String mimeType) {
|
||||
return new ReadOnlyResolver(uri, inputStream, mimeType);
|
||||
}
|
||||
|
||||
/* Package-private to force clients to go through CordovaWebView.resolveUri(). */
|
||||
static UriResolver forUri(Uri uri, Context context) {
|
||||
String scheme = uri.getScheme();
|
||||
if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
|
||||
return new ContentUriResolver(uri, context.getContentResolver());
|
||||
}
|
||||
if (ContentResolver.SCHEME_FILE.equals(scheme)) {
|
||||
if (uri.getPath().startsWith("/android_asset/")) {
|
||||
return new AssetUriResolver(uri, context.getAssets());
|
||||
}
|
||||
return new FileUriResolver(uri);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ import org.json.JSONArray;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class CallbackContext {
|
||||
|
@ -21,6 +21,8 @@ package org.apache.cordova.api;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,8 @@ package org.apache.cordova.api;
|
||||
|
||||
import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.UriResolver;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
@ -163,13 +164,12 @@ public class CordovaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for overriding the default URI handling mechanism.
|
||||
* Applies to WebView requests as well as requests made by plugins.
|
||||
* Hook for redirecting requests. Applies to WebView requests as well as requests made by plugins.
|
||||
*/
|
||||
public UriResolver resolveUri(Uri uri) {
|
||||
public Uri remapUri(Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the WebView does a top-level navigation or refreshes.
|
||||
*
|
||||
|
@ -19,6 +19,8 @@
|
||||
package org.apache.cordova.api;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
|
||||
//import android.content.Context;
|
||||
//import android.webkit.WebView;
|
||||
|
@ -26,7 +26,11 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.UriResolver;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginEntry;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONException;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
@ -35,7 +39,6 @@ import android.content.res.XmlResourceParser;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebResourceResponse;
|
||||
|
||||
/**
|
||||
* PluginManager is exposed to JavaScript in the Cordova WebView.
|
||||
@ -104,12 +107,7 @@ public class PluginManager {
|
||||
* Load plugins from res/xml/config.xml
|
||||
*/
|
||||
public void loadPlugins() {
|
||||
int id = this.ctx.getActivity().getResources().getIdentifier("config", "xml", this.ctx.getActivity().getPackageName());
|
||||
if(id == 0)
|
||||
{
|
||||
id = this.ctx.getActivity().getResources().getIdentifier("plugins", "xml", this.ctx.getActivity().getPackageName());
|
||||
LOG.i(TAG, "Using plugins.xml instead of config.xml. plugins.xml will eventually be deprecated");
|
||||
}
|
||||
int id = this.ctx.getActivity().getResources().getIdentifier("config", "xml", this.ctx.getActivity().getClass().getPackage().getName());
|
||||
if (id == 0) {
|
||||
this.pluginConfigurationMissing();
|
||||
//We have the error, we need to exit without crashing!
|
||||
@ -402,10 +400,10 @@ public class PluginManager {
|
||||
LOG.e(TAG, "=====================================================================================");
|
||||
}
|
||||
|
||||
/* Should be package private */ public UriResolver resolveUri(Uri uri) {
|
||||
public Uri remapUri(Uri uri) {
|
||||
for (PluginEntry entry : this.entries.values()) {
|
||||
if (entry.plugin != null) {
|
||||
UriResolver ret = entry.plugin.resolveUri(uri);
|
||||
Uri ret = entry.plugin.remapUri(uri);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="7" />
|
||||
<uses-sdk android:minSdkVersion="8" />
|
||||
|
||||
<instrumentation
|
||||
android:name="android.test.InstrumentationTestRunner"
|
||||
|
@ -1,263 +0,0 @@
|
||||
|
||||
package org.apache.cordova.test;
|
||||
|
||||
/*
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.UriResolver;
|
||||
import org.apache.cordova.UriResolvers;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginEntry;
|
||||
import org.apache.cordova.test.actions.CordovaWebViewTestActivity;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.util.Log;
|
||||
|
||||
public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWebViewTestActivity> {
|
||||
|
||||
public UriResolversTest()
|
||||
{
|
||||
super(CordovaWebViewTestActivity.class);
|
||||
}
|
||||
|
||||
CordovaWebView cordovaWebView;
|
||||
private CordovaWebViewTestActivity activity;
|
||||
String execPayload;
|
||||
Integer execStatus;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
activity = this.getActivity();
|
||||
cordovaWebView = activity.cordovaWebView;
|
||||
cordovaWebView.pluginManager.addService(new PluginEntry("UriResolverTestPlugin1", new CordovaPlugin() {
|
||||
@Override
|
||||
public UriResolver resolveUri(Uri uri) {
|
||||
if ("plugin-uri".equals(uri.getScheme())) {
|
||||
return cordovaWebView.resolveUri(uri.buildUpon().scheme("file").build());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
synchronized (UriResolversTest.this) {
|
||||
execPayload = args.getString(0);
|
||||
execStatus = args.getInt(1);
|
||||
UriResolversTest.this.notify();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
cordovaWebView.pluginManager.addService(new PluginEntry("UriResolverTestPlugin2", new CordovaPlugin() {
|
||||
@Override
|
||||
public UriResolver resolveUri(Uri uri) {
|
||||
if (uri.getQueryParameter("pluginRewrite") != null) {
|
||||
return UriResolvers.createInline(uri, "pass", "my/mime");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private Uri createTestImageContentUri() {
|
||||
Bitmap imageBitmap = BitmapFactory.decodeResource(activity.getResources(), R.drawable.icon);
|
||||
String stored = MediaStore.Images.Media.insertImage(activity.getContentResolver(),
|
||||
imageBitmap, "app-icon", "desc");
|
||||
return Uri.parse(stored);
|
||||
}
|
||||
|
||||
private void performResolverTest(Uri uri, String expectedMimeType, File expectedLocalFile,
|
||||
boolean expectedIsWritable,
|
||||
boolean expectRead, boolean expectWrite) throws IOException {
|
||||
UriResolver resolver = cordovaWebView.resolveUri(uri);
|
||||
assertEquals(expectedLocalFile, resolver.getLocalFile());
|
||||
assertEquals(expectedMimeType, resolver.getMimeType());
|
||||
if (expectedIsWritable) {
|
||||
assertTrue(resolver.isWritable());
|
||||
} else {
|
||||
assertFalse(resolver.isWritable());
|
||||
}
|
||||
try {
|
||||
resolver.getInputStream().read();
|
||||
if (!expectRead) {
|
||||
fail("Expected getInputStream to throw.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (expectRead) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
try {
|
||||
resolver.getOutputStream().write(123);
|
||||
if (!expectWrite) {
|
||||
fail("Expected getOutputStream to throw.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (expectWrite) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testValidContentUri() throws IOException
|
||||
{
|
||||
Uri contentUri = createTestImageContentUri();
|
||||
performResolverTest(contentUri, "image/jpeg", null, true, true, true);
|
||||
}
|
||||
|
||||
public void testInvalidContentUri() throws IOException
|
||||
{
|
||||
Uri contentUri = Uri.parse("content://media/external/images/media/999999999");
|
||||
performResolverTest(contentUri, null, null, true, false, false);
|
||||
}
|
||||
|
||||
public void testValidAssetUri() throws IOException
|
||||
{
|
||||
Uri assetUri = Uri.parse("file:///android_asset/www/index.html?foo#bar"); // Also check for stripping off ? and # correctly.
|
||||
performResolverTest(assetUri, "text/html", null, false, true, false);
|
||||
}
|
||||
|
||||
public void testInvalidAssetUri() throws IOException
|
||||
{
|
||||
Uri assetUri = Uri.parse("file:///android_asset/www/missing.html");
|
||||
performResolverTest(assetUri, "text/html", null, false, false, false);
|
||||
}
|
||||
|
||||
public void testFileUriToExistingFile() throws IOException
|
||||
{
|
||||
File f = File.createTempFile("te s t", ".txt"); // Also check for dealing with spaces.
|
||||
try {
|
||||
Uri fileUri = Uri.parse(f.toURI().toString() + "?foo#bar"); // Also check for stripping off ? and # correctly.
|
||||
performResolverTest(fileUri, "text/plain", f, true, true, true);
|
||||
} finally {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public void testFileUriToMissingFile() throws IOException
|
||||
{
|
||||
File f = new File(Environment.getExternalStorageDirectory() + "/somefilethatdoesntexist");
|
||||
Uri fileUri = Uri.parse(f.toURI().toString());
|
||||
try {
|
||||
performResolverTest(fileUri, null, f, true, false, true);
|
||||
} finally {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public void testFileUriToMissingFileWithMissingParent() throws IOException
|
||||
{
|
||||
File f = new File(Environment.getExternalStorageDirectory() + "/somedirthatismissing/somefilethatdoesntexist");
|
||||
Uri fileUri = Uri.parse(f.toURI().toString());
|
||||
performResolverTest(fileUri, null, f, false, false, false);
|
||||
}
|
||||
|
||||
public void testUnrecognizedUri() throws IOException
|
||||
{
|
||||
Uri uri = Uri.parse("somescheme://foo");
|
||||
performResolverTest(uri, null, null, false, false, false);
|
||||
}
|
||||
|
||||
public void testRelativeUri()
|
||||
{
|
||||
try {
|
||||
cordovaWebView.resolveUri(Uri.parse("/foo"));
|
||||
fail("Should have thrown for relative URI 1.");
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
try {
|
||||
cordovaWebView.resolveUri(Uri.parse("//foo/bar"));
|
||||
fail("Should have thrown for relative URI 2.");
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
try {
|
||||
cordovaWebView.resolveUri(Uri.parse("foo.png"));
|
||||
fail("Should have thrown for relative URI 3.");
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testPluginOverrides1() throws IOException
|
||||
{
|
||||
Uri uri = Uri.parse("plugin-uri://foohost/android_asset/www/index.html");
|
||||
performResolverTest(uri, "text/html", null, false, true, false);
|
||||
}
|
||||
|
||||
public void testPluginOverrides2() throws IOException
|
||||
{
|
||||
Uri uri = Uri.parse("plugin-uri://foohost/android_asset/www/index.html?pluginRewrite=yes");
|
||||
performResolverTest(uri, "my/mime", null, false, true, false);
|
||||
}
|
||||
|
||||
public void testWhitelistRejection() throws IOException
|
||||
{
|
||||
Uri uri = Uri.parse("http://foohost.com/");
|
||||
performResolverTest(uri, null, null, false, false, false);
|
||||
}
|
||||
|
||||
public void testWebViewRequestIntercept() throws IOException
|
||||
{
|
||||
cordovaWebView.sendJavascript(
|
||||
"var x = new XMLHttpRequest;\n" +
|
||||
"x.open('GET', 'file://foo?pluginRewrite=1', false);\n" +
|
||||
"x.send();\n" +
|
||||
"cordova.require('cordova/exec')(null,null,'UriResolverTestPlugin1', 'foo', [x.responseText, x.status])");
|
||||
execPayload = null;
|
||||
execStatus = null;
|
||||
try {
|
||||
synchronized (this) {
|
||||
this.wait(2000);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
assertEquals("pass", execPayload);
|
||||
assertEquals(execStatus.intValue(), 200);
|
||||
}
|
||||
|
||||
public void testWebViewWhiteListRejection() throws IOException
|
||||
{
|
||||
cordovaWebView.sendJavascript(
|
||||
"var x = new XMLHttpRequest;\n" +
|
||||
"x.open('GET', 'http://foo/bar', false);\n" +
|
||||
"x.send();\n" +
|
||||
"cordova.require('cordova/exec')(null,null,'UriResolverTestPlugin1', 'foo', [x.responseText, x.status])");
|
||||
execPayload = null;
|
||||
execStatus = null;
|
||||
try {
|
||||
synchronized (this) {
|
||||
this.wait(2000);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
assertEquals("", execPayload);
|
||||
assertEquals(execStatus.intValue(), 404);
|
||||
}
|
||||
}
|