Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8c755b95a | ||
|
|
187c6a6bd1 | ||
|
|
bc04ce4ce3 | ||
|
|
b2b1ae2b33 | ||
|
|
fbd6b559d4 | ||
|
|
89002514f5 | ||
|
|
a96d340b12 | ||
|
|
a044e88a1a | ||
|
|
56587bfb46 | ||
|
|
0264019c87 | ||
|
|
fbeba54740 | ||
|
|
338216beca | ||
|
|
d36b1cc89d | ||
|
|
6b5535e91e | ||
|
|
f64cd2ee8a | ||
|
|
aa81966e0b | ||
|
|
f5232f4c26 | ||
|
|
38b769a2c5 | ||
|
|
d7e962dba0 |
@@ -34,6 +34,7 @@ Requires
|
||||
- Java JDK 1.5 or greater
|
||||
- Apache ANT 1.8.0 or greater
|
||||
- Android SDK [http://developer.android.com](http://developer.android.com)
|
||||
- Apache Commons Codec [http://commons.apache.org/codec/](http://commons.apache.org/codec/)
|
||||
|
||||
|
||||
Cordova Android Developer Tools
|
||||
@@ -52,6 +53,7 @@ the dependencies:
|
||||
General Commands
|
||||
|
||||
./bin/create [path package activity] ... create the ./example app or a cordova android project
|
||||
./bin/bench ............................ generate a bench proj
|
||||
./bin/autotest ......................... test the cli tools
|
||||
./bin/test ............................. run mobile-spec
|
||||
|
||||
@@ -101,7 +103,11 @@ Note: The Developer Tools handle this. This is only to be done if the tooling f
|
||||
you are developing directly against the framework.
|
||||
|
||||
|
||||
To create your `cordova.jar` file, run in the framework directory:
|
||||
To create your `cordova.jar` file, copy the commons codec:
|
||||
|
||||
mv commons-codec-1.7.jar framework/libs
|
||||
|
||||
then run in the framework directory:
|
||||
|
||||
android update project -p . -t android-17
|
||||
ant jar
|
||||
|
||||
@@ -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-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."
|
||||
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."
|
||||
exit 2
|
||||
else
|
||||
cmd="android update project -p $ROOT -t android-18 1> /dev/null 2>&1"
|
||||
cmd="android update project -p $ROOT -t android-17 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,28 +54,22 @@ 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.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);
|
||||
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);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else {
|
||||
var cmd = '%comspec% /c android update project -p ' + ROOT + '\\framework -t ' + target;
|
||||
var cmd = '%comspec% /c android update project -p ' + ROOT + '\\framework -t android-17';
|
||||
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);
|
||||
@@ -84,19 +78,4 @@ function 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();
|
||||
|
||||
check_requirements();
|
||||
20
bin/create
@@ -38,8 +38,7 @@ VERSION=$(cat "$BUILD_PATH"/VERSION)
|
||||
|
||||
PROJECT_PATH="${1:-'./example'}"
|
||||
PACKAGE=${2:-"org.apache.cordova.example"}
|
||||
ACTIVITY=$(echo ${3:-"cordovaExample"} | tr -d '[:blank:][:punct:]')
|
||||
APP_LABEL=${3:-"Cordova Example"};
|
||||
ACTIVITY=${3:-"cordovaExample"}
|
||||
|
||||
# clobber any existing example
|
||||
if [ -d "$PROJECT_PATH" ]
|
||||
@@ -68,7 +67,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"
|
||||
@@ -83,7 +82,6 @@ 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 ' ')
|
||||
@@ -101,6 +99,16 @@ then
|
||||
# update the cordova-android framework for the desired target
|
||||
"$ANDROID_BIN" update project --target $TARGET --path "$BUILD_PATH"/framework &> /dev/null
|
||||
|
||||
if [ ! -e "$BUILD_PATH"/framework/libs/commons-codec-1.7.jar ]; then
|
||||
# Use curl to get the jar (TODO: Support Apache Mirrors)
|
||||
curl -OL http://archive.apache.org/dist/commons/codec/binaries/commons-codec-1.7-bin.zip &> /dev/null
|
||||
unzip commons-codec-1.7-bin.zip &> /dev/null
|
||||
mkdir -p "$BUILD_PATH"/framework/libs
|
||||
cp commons-codec-1.7/commons-codec-1.7.jar "$BUILD_PATH"/framework/libs
|
||||
# cleanup yo
|
||||
rm commons-codec-1.7-bin.zip && rm -rf commons-codec-1.7
|
||||
fi
|
||||
|
||||
# compile cordova.js and cordova.jar
|
||||
pushd "$BUILD_PATH"/framework > /dev/null
|
||||
ant jar > /dev/null
|
||||
@@ -131,9 +139,6 @@ 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"
|
||||
@@ -149,6 +154,7 @@ cp "$BUILD_PATH"/bin/templates/cordova/clean "$PROJECT_PATH"/cordova/clean
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/log "$PROJECT_PATH"/cordova/log
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/run "$PROJECT_PATH"/cordova/run
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/version "$PROJECT_PATH"/cordova/version
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/cordova "$PROJECT_PATH"/cordova/lib/cordova
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/install-device "$PROJECT_PATH"/cordova/lib/install-device
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/install-emulator "$PROJECT_PATH"/cordova/lib/install-emulator
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/list-devices "$PROJECT_PATH"/cordova/lib/list-devices
|
||||
|
||||
481
bin/create.js
@@ -1,214 +1,267 @@
|
||||
/*
|
||||
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 + '<');
|
||||
/*
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
// Cleanup
|
||||
// if(fso.FileExists(ROOT + '\\framework\\libs\\commons-codec-1.6.jar')) {
|
||||
// fso.DeleteFile(ROOT + '\\framework\\libs\\commons-codec-1.6.jar');
|
||||
// fso.DeleteFolder(ROOT + '\\framework\\libs', true);
|
||||
// }
|
||||
if(fso.FileExists(ROOT + '\\framework\\cordova-'+VERSION+'.jar')) {
|
||||
fso.DeleteFile(ROOT + '\\framework\\cordova-'+VERSION+'.jar');
|
||||
}
|
||||
if(fso.FileExists(ROOT + '\\framework\\assets\\www\\cordova-'+VERSION+'.js')) {
|
||||
fso.DeleteFile(ROOT + '\\framework\\assets\\www\\cordova-'+VERSION+'.js');
|
||||
}
|
||||
}
|
||||
|
||||
function downloadCommonsCodec() {
|
||||
if (!fso.FileExists(ROOT + '\\framework\\libs\\commons-codec-1.7.jar')) {
|
||||
// We need the .jar
|
||||
var url = 'http://archive.apache.org/dist/commons/codec/binaries/commons-codec-1.7-bin.zip';
|
||||
var libsPath = ROOT + '\\framework\\libs';
|
||||
var savePath = libsPath + '\\commons-codec-1.7-bin.zip';
|
||||
if (!fso.FileExists(savePath)) {
|
||||
if(!fso.FolderExists(ROOT + '\\framework\\libs')) {
|
||||
fso.CreateFolder(libsPath);
|
||||
}
|
||||
// We need the zip to get the jar
|
||||
var xhr = WScript.CreateObject('MSXML2.XMLHTTP');
|
||||
xhr.open('GET', url, false);
|
||||
xhr.send();
|
||||
if (xhr.status == 200) {
|
||||
var stream = WScript.CreateObject('ADODB.Stream');
|
||||
stream.Open();
|
||||
stream.Type = 1;
|
||||
stream.Write(xhr.ResponseBody);
|
||||
stream.Position = 0;
|
||||
stream.SaveToFile(savePath);
|
||||
stream.Close();
|
||||
} else {
|
||||
Log('Could not retrieve the commons-codec. Please download it yourself and put into the framework/libs directory. This process may fail now. Sorry.');
|
||||
}
|
||||
}
|
||||
var app = WScript.CreateObject('Shell.Application');
|
||||
var source = app.NameSpace(savePath).Items();
|
||||
var target = app.NameSpace(ROOT + '\\framework\\libs');
|
||||
target.CopyHere(source, 256);
|
||||
|
||||
// Move the jar into libs
|
||||
fso.MoveFile(ROOT + '\\framework\\libs\\commons-codec-1.7\\commons-codec-1.7.jar', ROOT + '\\framework\\libs\\commons-codec-1.7.jar');
|
||||
|
||||
// Clean up
|
||||
fso.DeleteFile(ROOT + '\\framework\\libs\\commons-codec-1.7-bin.zip');
|
||||
fso.DeleteFolder(ROOT + '\\framework\\libs\\commons-codec-1.7', true);
|
||||
}
|
||||
}
|
||||
|
||||
// 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"');
|
||||
// pull down commons codec if necessary
|
||||
downloadCommonsCodec();
|
||||
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');
|
||||
|
||||
// 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);
|
||||
|
||||
cleanup();
|
||||
|
||||
@@ -16,24 +16,8 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$DIR/.." && pwd )
|
||||
set -e
|
||||
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
if [[ $1 == "--debug" ]] ; then
|
||||
$DIR/clean
|
||||
ant debug -f "$PROJECT_PATH"/build.xml
|
||||
elif [[ $1 == "--release" ]] ; then
|
||||
$DIR/clean
|
||||
ant release -f "$PROJECT_PATH"/build.xml
|
||||
elif [[ $1 == "--nobuild" ]] ; then
|
||||
echo "Skipping build..."
|
||||
else
|
||||
echo "Error : Build command '$1' not recognized."
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
echo "Warning : [ --debug | --release | --nobuild ] not specified, defaulting to --debug"
|
||||
$DIR/clean
|
||||
ant debug -f "$PROJECT_PATH"/build.xml
|
||||
fi
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash "$CORDOVA_PATH"/lib/cordova build "$@"
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$DIR/.." && pwd )
|
||||
echo "Cleaning project..."
|
||||
ant -f "$PROJECT_PATH/build.xml" clean
|
||||
set -e
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash "$CORDOVA_PATH"/lib/cordova clean "$@"
|
||||
|
||||
386
bin/templates/cordova/lib/cordova
Executable file
@@ -0,0 +1,386 @@
|
||||
#!/bin/bash
|
||||
# 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.
|
||||
|
||||
PROJECT_PATH=$( cd "$( dirname "$0" )/../.." && pwd )
|
||||
|
||||
function list_devices {
|
||||
IFS=$'\n'
|
||||
devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator'`
|
||||
device_list=($devices)
|
||||
if [[ ${#device_list[@]} > 0 ]] ; then
|
||||
for i in ${devices[@]}
|
||||
do
|
||||
# remove space and 'device'
|
||||
echo ${i/[^a-zA-Z0-9._]device/}
|
||||
done
|
||||
else
|
||||
echo "No devices found."
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
function list_started_emulators {
|
||||
IFS=$'\n'
|
||||
devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
|
||||
emulator_list=($devices)
|
||||
if [[ ${#emulator_list[@]} > 0 ]] ; then
|
||||
for i in ${emulator_list[@]}
|
||||
do
|
||||
# remove space and 'device'
|
||||
echo ${i/[^a-zA-Z0-9._]device/}
|
||||
done
|
||||
else
|
||||
echo "No started emulators found, you can start an emulator by using the command"
|
||||
echo " 'cordova/lib/start-emulator'"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
function list_emulator_images {
|
||||
emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
|
||||
emulator_list=($emulator_images)
|
||||
if [[ ${#emulator_list[@]} > 0 ]] ; then
|
||||
for i in ${emulator_list[@]}
|
||||
do
|
||||
echo ${i/[^a-zA-Z0-9._]/}
|
||||
done
|
||||
else
|
||||
echo "No emulators found, if you would like to create an emulator follow the instructions"
|
||||
echo " provided here : http://developer.android.com/tools/devices/index.html"
|
||||
echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
function start_emulator {
|
||||
emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
|
||||
# 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 &
|
||||
else
|
||||
echo "Could not find the provided emulator, make sure the emulator exists"
|
||||
echo " by checking 'cordova/lib/list-emulator-images'"
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
# start first emulator
|
||||
emulator_list=($emulator_images)
|
||||
if [[ ${#emulator_list[@]} > 0 ]] ; then
|
||||
#xterm -e emulator -avd ${emulator_list[0]} &
|
||||
emulator -avd ${emulator_list[0]/[^a-zA-Z0-9._]/} 1> /dev/null 2>&1 &
|
||||
else
|
||||
echo "No emulators found, if you would like to create an emulator follow the instructions"
|
||||
echo " provided here : http://developer.android.com/tools/devices/index.html"
|
||||
echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function install_device {
|
||||
IFS=$'\n'
|
||||
devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator'`
|
||||
device_list=($devices)
|
||||
if [[ ${#device_list[@]} > 0 ]] ; then
|
||||
apks=`find $PROJECT_PATH/bin -type f -maxdepth 1 | egrep '\.apk$'`
|
||||
apk_list=($apks)
|
||||
if [[ ${#apk_list[@]} > 0 ]] ; then
|
||||
local target
|
||||
# handle target emulator
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
# deploy to given target
|
||||
target=${1/--target=/}
|
||||
else
|
||||
# delete trailing space and 'device' after device ID
|
||||
target=${device_list[0]/[^a-zA-Z0-9._]device/}
|
||||
fi
|
||||
echo "Installing ${apk_list[0]} onto device $target..."
|
||||
adb -s $target install -r ${apk_list[0]};
|
||||
echo "Launching application..."
|
||||
local launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
|
||||
adb -s $target shell am start -W -a android.intent.action.MAIN -n $launch_str
|
||||
else
|
||||
echo "Application package not found, could not install to device"
|
||||
echo " make sure your application is built before deploying."
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
echo "No devices found to deploy to. Please make sure your device is connected"
|
||||
echo " and you can view it using the 'cordova/lib/list-devices' command."
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
function install_emulator {
|
||||
IFS=$'\n'
|
||||
# check that there is an emulator to deploy to
|
||||
emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'emulator'`
|
||||
emulator_list=($emulator_string)
|
||||
if [[ ${#emulator_list[@]} > 0 ]] ; then
|
||||
apks=`find $PROJECT_PATH/bin -type f -maxdepth 1 | egrep '\.apk$'`
|
||||
apk_list=($apks)
|
||||
if [[ ${#apk_list[@]} > 0 ]] ; then
|
||||
local target
|
||||
# handle target emulator
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
# deploy to given target
|
||||
target=${1/--target=/}
|
||||
else
|
||||
# delete trailing space and 'device' after emulator ID
|
||||
target=${emulator_list[0]/[^a-zA-Z0-9._]device/}
|
||||
fi
|
||||
echo "Installing ${apk_list[0]} onto $target..."
|
||||
adb -s $target install -r ${apk_list[0]};
|
||||
echo "Launching application..."
|
||||
local launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
|
||||
adb -s $target shell am start -W -a android.intent.action.MAIN -n $launch_str
|
||||
|
||||
else
|
||||
echo "Application package not found, could not install to device"
|
||||
echo " make sure your application is built before deploying."
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
echo "No emulators found to deploy to. Please make sure your emulator is started"
|
||||
echo " and you can view it using the 'cordova/lib/list-started-emulators' command."
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
# cleans the project
|
||||
function clean {
|
||||
echo "Cleaning project..."
|
||||
ant clean
|
||||
}
|
||||
|
||||
# has to be used independently and not in conjunction with other commands
|
||||
function log {
|
||||
# filter out nativeGetEnabledTags spam from latest sdk bug.
|
||||
adb logcat | grep -v nativeGetEnabledTags
|
||||
}
|
||||
|
||||
|
||||
function build {
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
if [[ $1 == "--debug" ]] ; then
|
||||
clean
|
||||
ant debug -f "$PROJECT_PATH"/build.xml
|
||||
elif [[ $1 == "--release" ]] ; then
|
||||
clean
|
||||
ant release -f "$PROJECT_PATH"/build.xml
|
||||
elif [[ $1 == "--nobuild" ]] ; then
|
||||
echo "Skipping build..."
|
||||
else
|
||||
echo "Error : Build command '$1' not recognized."
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
echo "Warning : [ --debug | --release | --nobuild ] not specified, defaulting to --debug"
|
||||
clean
|
||||
ant debug -f "$PROJECT_PATH"/build.xml
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function wait_for_emulator {
|
||||
emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
|
||||
old_started=($emulator_string)
|
||||
local new_started
|
||||
local new_emulator_name
|
||||
local i="0"
|
||||
echo -n "Waiting for emulator..."
|
||||
while [ $i -lt 300 ]
|
||||
do
|
||||
emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
|
||||
new_started=($emulator_string)
|
||||
if [[ ${#new_started[@]} > ${#old_started[@]} && -z "$new_emulator_name" ]] ; then
|
||||
# get the name of the started emulator
|
||||
local count="0"
|
||||
if [[ ${#old_started[@]} == 0 ]] ; then
|
||||
new_emulator_name=${new_started[$count]/[^a-zA-Z0-9._]device/}
|
||||
else
|
||||
for count in {0...${#old_started[@]}}
|
||||
do
|
||||
if [[ ! ${new_started[$count]} == ${old_started[$count]} ]] ; then
|
||||
new_emulator_name=${new_started[$count]/[^a-zA-Z0-9._]device/}
|
||||
fi
|
||||
done
|
||||
if [[ -z "$new_emulator_name" ]] ; then
|
||||
count=$[count+1]
|
||||
new_emulator_name=${new_started[$count]/[^a-zA-Z0-9._]device/}
|
||||
fi
|
||||
fi
|
||||
elif [[ "$new_emulator_name" ]] ; then
|
||||
boot_anim=`adb -s $new_emulator_name shell getprop init.svc.bootanim`
|
||||
if [[ $boot_anim =~ "stopped" ]] ; then
|
||||
break
|
||||
else
|
||||
sleep 1
|
||||
i=$[i+1]
|
||||
echo -n "."
|
||||
fi
|
||||
else
|
||||
sleep 1
|
||||
i=$[i+1]
|
||||
echo -n "."
|
||||
fi
|
||||
done
|
||||
# Device timeout: emulator has not started in time
|
||||
if [ $i -eq 300 ]
|
||||
then
|
||||
echo "emulator timeout!"
|
||||
exit 69
|
||||
else
|
||||
echo "connected!"
|
||||
fi
|
||||
}
|
||||
|
||||
function run {
|
||||
IFS=$'\n'
|
||||
if [[ "$#" -eq 2 ]] ; then
|
||||
build $2
|
||||
if [[ $1 == "--device" ]] ; then
|
||||
install_device
|
||||
elif [[ $1 == "--emulator" ]] ; then
|
||||
install_emulator
|
||||
elif [[ $1 =~ "--target=" ]]; then
|
||||
install_device $1
|
||||
else
|
||||
echo "Error : '$1' is not recognized as an install option"
|
||||
fi
|
||||
elif [[ "$#" -eq 1 ]] ; then
|
||||
if [[ $1 == "--debug" || $1 == "--release" || $1 == "--nobuild" ]] ; then
|
||||
build $1
|
||||
elif [[ $1 == "--device" ]] ; then
|
||||
install_device
|
||||
elif [[ $1 == "--emulator" ]] ; then
|
||||
install_emulator
|
||||
elif [[ $1 =~ "--target=" ]]; then
|
||||
install_device $1
|
||||
else
|
||||
echo "Error : '$1' is not recognized as an install option"
|
||||
fi
|
||||
else
|
||||
echo "Warning : [ --device | --emulate | --target=<targetID> ] not specified, using defaults."
|
||||
build
|
||||
devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator'`
|
||||
device_list=($devices)
|
||||
emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
|
||||
emulator_list=($emulator_string)
|
||||
if [[ ${#device_list[@]} > 0 ]] ; then
|
||||
install_device
|
||||
elif [[ ${#emulator_list[@]} > 0 ]] ; then
|
||||
install_emulator
|
||||
else
|
||||
emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
|
||||
echo $emulator_images
|
||||
emulator_image_list=($emulator_images)
|
||||
if [[ ${#emulator_image_list[@]} > 0 ]] ; then
|
||||
echo "Starting emulator : ${emulator_image_list[0]}"
|
||||
emulator -avd ${emulator_image_list[0]/[^.\w]/} 1> /dev/null 2>&1 &
|
||||
wait_for_emulator
|
||||
install_emulator
|
||||
else
|
||||
# TODO : look for emulator images and start one if it's availible
|
||||
echo "Error : there are no availible devices or emulators to deploy to."
|
||||
echo " create an emulator or connect your device to run this command."
|
||||
echo "If you would like to create an emulator follow the instructions"
|
||||
echo " provided here : http://developer.android.com/tools/devices/index.html"
|
||||
echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# parse command line arguments
|
||||
|
||||
if [[ $# > 3 ]] ; then
|
||||
echo "Error : too many arguments."
|
||||
exit 2
|
||||
elif [[ $# == 3 ]] ; then
|
||||
if [[ $1 == "run" ]] ; then
|
||||
run $2 $3
|
||||
else
|
||||
echo "Error : too many arguments for '$1'"
|
||||
exit 2
|
||||
fi
|
||||
elif [[ $# == 2 ]] ; then
|
||||
if [[ $1 == "run" ]] ; then
|
||||
if [[ $2 == "--emulator" || $2 == "--device" || $2 =~ "--target=" ]] ; then
|
||||
run $2 ''
|
||||
elif [[ $2 == "--debug" || $2 == "--release" || $2 == "--nobuild" ]] ; then
|
||||
run '' $2
|
||||
else
|
||||
echo "Error : '$2' is not recognized as a run option."
|
||||
exit 2
|
||||
fi
|
||||
elif [[ $1 == "build" ]] ; then
|
||||
build $2
|
||||
elif [[ $1 == "start-emulator" ]] ; then
|
||||
start_emulator $2
|
||||
elif [[ $1 == "install-device" ]] ; then
|
||||
if [[ $2 =~ "--target=" ]] ; then
|
||||
install_device $2
|
||||
else
|
||||
echo "Error : '$2' is not recognized as an install option"
|
||||
exit 2
|
||||
fi
|
||||
elif [[ $1 == "install-emulator" ]] ; then
|
||||
if [[ $2 =~ "--target=" ]] ; then
|
||||
install_emulator $2
|
||||
else
|
||||
echo "Error : '$2' is not recognized as an install option"
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
echo "Error : '$1' is not recognized as an option that takes arguments"
|
||||
exit 2
|
||||
fi
|
||||
elif [[ $# == 1 ]] ; then
|
||||
if [[ $1 == "run" ]] ; then
|
||||
run
|
||||
elif [[ $1 == "build" ]]; then
|
||||
build
|
||||
elif [[ $1 == "clean" ]]; then
|
||||
clean
|
||||
elif [[ $1 == "log" ]]; then
|
||||
log
|
||||
elif [[ $1 == "list-devices" ]]; then
|
||||
list_devices
|
||||
elif [[ $1 == "list-emulator-images" ]]; then
|
||||
list_emulator_images
|
||||
elif [[ $1 == "list-started-emulators" ]]; then
|
||||
list_started_emulators
|
||||
elif [[ $1 == "install-device" ]]; then
|
||||
install_device
|
||||
elif [[ $1 == "install-emulator" ]]; then
|
||||
install_emulator
|
||||
elif [[ $1 == "start-emulator" ]]; then
|
||||
start_emulator
|
||||
else
|
||||
echo "Error : '$1' is not recognized as a tooling command."
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
echo "Error : No command recieved, exiting..."
|
||||
exit 2
|
||||
fi
|
||||
554
bin/templates/cordova/lib/cordova.js
vendored
@@ -18,21 +18,6 @@
|
||||
var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\cordova.js').join(''),
|
||||
shell = WScript.CreateObject("WScript.Shell"),
|
||||
fso = WScript.CreateObject('Scripting.FileSystemObject');
|
||||
//device_id for targeting specific device
|
||||
var device_id;
|
||||
//build types
|
||||
var NONE = 0,
|
||||
DEBUG = '--debug',
|
||||
RELEASE = '--release',
|
||||
NO_BUILD = '--nobuild';
|
||||
var build_type = NONE;
|
||||
|
||||
//deploy tpyes
|
||||
var NONE = 0,
|
||||
EMULATOR = 1,
|
||||
DEVICE = 2,
|
||||
TARGET = 3;
|
||||
var deploy_type = NONE;
|
||||
|
||||
|
||||
// log to stdout or stderr
|
||||
@@ -101,25 +86,6 @@ function exec_verbose(command) {
|
||||
}
|
||||
}
|
||||
|
||||
function version(path) {
|
||||
var cordovajs_path = path + "\\assets\\www\\cordova.js";
|
||||
if(fso.FileExists(cordovajs_path)) {
|
||||
var f = fso.OpenTextFile(cordovajs_path, 1,2);
|
||||
var cordovajs = f.ReadAll();
|
||||
f.Close();
|
||||
var version_regex = /^.*CORDOVA_JS_BUILD_LABEL.*$/m;
|
||||
var version_line = cordovajs.match(version_regex) + "";
|
||||
var version = version_line.match(/(\d+)\.(\d+)\.(\d+)(rc\d)?/) + "";
|
||||
// TODO : figure out why this isn't matching properly so we can remove this substring workaround.
|
||||
Log(version.substr(0, ((version.length/2) -1)));
|
||||
} else {
|
||||
Log("Error : Could not find cordova js.", true);
|
||||
Log("Expected Location : " + cordovajs_path, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function get_devices() {
|
||||
var device_list = []
|
||||
var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
|
||||
@@ -151,18 +117,38 @@ function list_devices() {
|
||||
}
|
||||
|
||||
function get_emulator_images() {
|
||||
// discription contains all data recieved squashed onto one line
|
||||
var add_description = true;
|
||||
var avd_list = [];
|
||||
var local_emulators = shell.Exec("%comspec% /c android list avds").StdOut.ReadAll();
|
||||
if (local_emulators.match(/Name\:/)) {
|
||||
emulators = local_emulators.split('\n');
|
||||
//format (ID DESCRIPTION)
|
||||
var count = 0;
|
||||
var output = '';
|
||||
for (i in emulators) {
|
||||
// Find the line with the emulator name.
|
||||
if (emulators[i].match(/Name\:/)) {
|
||||
// strip description
|
||||
var emulator_name = emulators[i].replace(/\s*Name\:\s/, '') + ' ';
|
||||
avd_list.push(emulator_name);
|
||||
if (add_description) {
|
||||
count = 1;
|
||||
output += emulator_name
|
||||
}
|
||||
else {
|
||||
avd_list.push(emulator_name);
|
||||
}
|
||||
}
|
||||
// add description if indicated (all data squeezed onto one line)
|
||||
if (count > 0) {
|
||||
var emulator_description = emulators[i].replace(/\s*/g, '');
|
||||
if (count > 4) {
|
||||
avd_list.push(output + emulator_description);
|
||||
count = 0;
|
||||
output = '';
|
||||
}
|
||||
else {
|
||||
count++;
|
||||
output += emulator_description + ' '
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,21 +192,8 @@ function list_started_emulators() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log('No started emulators found, if you would like to start an emulator call ');
|
||||
Log('\'list-emulator-images\'');
|
||||
Log(' to get the name of an emulator and then start the emulator with');
|
||||
Log('\'start-emulator <Name>\'');
|
||||
}
|
||||
}
|
||||
|
||||
function create_emulator() {
|
||||
//get targets
|
||||
var targets = shell.Exec('android.bat list targets').StdOut.ReadAll().match(/id:\s\d+/g);
|
||||
if(targets) {
|
||||
exec('%comspec% /c android create avd --name cordova_emulator --target ' + targets[targets.length - 1].replace(/id: /, ""));
|
||||
} else {
|
||||
Log("You do not have any android targets setup. Please create at least one target with the `android` command so that an emulator can be created.", true);
|
||||
WScript.Quit(69);
|
||||
Log('No started emulators found, if you would like to start an emulator call \'list-emulator-images\'');
|
||||
Log(' to get the name of an emulator and then start the emulator with \'start-emulator <Name>\'');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,14 +207,14 @@ function start_emulator(name) {
|
||||
for (i in emulators) {
|
||||
if (emulators[i].substr(0,name.length) == name) {
|
||||
Log("Starting emulator : " + name);
|
||||
shell.Exec("%comspec% /c emulator -avd " + name + " &");
|
||||
shell.Run("%comspec% /c start cmd /c emulator -avd " + name);
|
||||
//shell.Run("%comspec% /c start cmd /c emulator -cpu-delay 0 -no-boot-anim -cache %Temp%\cache -avd " + name);
|
||||
started = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (emulators.length > 0 && started_emulators.length == 0) {
|
||||
if (emulators.length > 0 && started_emulators < 1) {
|
||||
emulator_name = emulators[0].split(' ', 1)[0];
|
||||
start_emulator(emulator_name);
|
||||
return;
|
||||
@@ -257,16 +230,15 @@ function start_emulator(name) {
|
||||
Log("Error : unable to start emulator, ensure you have emulators availible by checking \'list-emulator-images\'", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else {
|
||||
// wait for emulator to get the ID
|
||||
Log('Waiting for emulator...');
|
||||
else { // wait for emulator to boot before returning
|
||||
WScript.Stdout.Write('Booting up emulator..');
|
||||
var boot_anim = null;
|
||||
var emulator_ID = null;
|
||||
var new_started = null;
|
||||
var new_started = get_started_emulators();
|
||||
var i = 0;
|
||||
while(emulator_ID == null && i < 10) {
|
||||
new_started = get_started_emulators();
|
||||
if(new_started.length > started_emulators.length) {
|
||||
// use boot animation property to tell when boot is complete.
|
||||
while ((boot_anim == null || !boot_anim.output.match(/stopped/)) && i < 100) {
|
||||
if (new_started.length > started_emulators.length && emulator_ID == null) {
|
||||
// find new emulator that was just started to get it's ID
|
||||
for(var i = 0; i < new_started.length; i++) {
|
||||
if (new_started[i] != started_emulators[i]) {
|
||||
@@ -276,25 +248,18 @@ function start_emulator(name) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == 10) {
|
||||
Log('\nEmulator start timed out.');
|
||||
WScript.Quit(2);
|
||||
}
|
||||
i = 0;
|
||||
WScript.Stdout.Write('Booting up emulator (this may take a while).');
|
||||
// use boot animation property to tell when boot is complete.
|
||||
while (!boot_anim.output.match(/stopped/) && i < 100) {
|
||||
boot_anim = exec_out('%comspec% /c adb -s ' + emulator_ID + ' shell getprop init.svc.bootanim');
|
||||
else if (boot_anim == null) {
|
||||
new_started = get_started_emulators();
|
||||
}
|
||||
else {
|
||||
boot_anim = exec_out('%comspec% /c adb -s ' + emulator_ID + ' shell getprop init.svc.bootanim');
|
||||
}
|
||||
i++;
|
||||
WScript.Stdout.Write('.');
|
||||
WScript.Sleep(2000);
|
||||
}
|
||||
|
||||
if (i < 100) {
|
||||
Log('\nBoot Complete!');
|
||||
// Unlock the device
|
||||
shell.Exec("%comspec% /c adb -s " + emulator_ID + " shell input keyevent 82");
|
||||
} else {
|
||||
Log('\nEmulator boot timed out. Failed to load emulator');
|
||||
WScript.Quit(2);
|
||||
@@ -302,11 +267,34 @@ function start_emulator(name) {
|
||||
}
|
||||
}
|
||||
|
||||
function get_apk(path) {
|
||||
function install_device(target) {
|
||||
var devices = get_devices();
|
||||
var use_target = false;
|
||||
if (devices.length < 1) {
|
||||
Log("Error : No devices found to install to, make sure there are devices", true);
|
||||
Log(" availible by checking \'<project_dir>\\cordova\\lib\\list-devices\'", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
if (target) {
|
||||
var exists = false;
|
||||
for (i in devices) {
|
||||
if (devices[i].substr(0,target.length) == target)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists) {
|
||||
Log("Error : Unable to find target " + target, true);
|
||||
Log("Please ensure the target exists by checking \'<project>\\cordova\\lib\\list-devices'");
|
||||
WScript.Quit(2);
|
||||
}
|
||||
use_target = true;
|
||||
}
|
||||
// check if file .apk has been created
|
||||
if (fso.FolderExists(path + '\\bin')) {
|
||||
if (fso.FolderExists(ROOT + '\\bin')) {
|
||||
var path_to_apk;
|
||||
var out_folder = fso.GetFolder(path + '\\bin');
|
||||
var out_folder = fso.GetFolder(ROOT + '\\bin');
|
||||
var out_files = new Enumerator(out_folder.Files);
|
||||
for (;!out_files.atEnd(); out_files.moveNext()) {
|
||||
var path = out_files.item() + '';
|
||||
@@ -316,7 +304,38 @@ function get_apk(path) {
|
||||
}
|
||||
}
|
||||
if (path_to_apk) {
|
||||
return path_to_apk;
|
||||
var launch_name = exec_out("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
|
||||
if (launch_name.error) {
|
||||
Log("Failed to get application name from appinfo.jar + AndroidManifest : ", true);
|
||||
Log("Output : " + launch_name.output, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
// install on device (-d)
|
||||
Log("Installing app on device...");
|
||||
var cmd;
|
||||
if (use_target) {
|
||||
cmd = '%comspec% /c adb -s ' + target + ' install -r ' + path_to_apk;
|
||||
} else {
|
||||
cmd = '%comspec% /c adb -s ' + devices[0].split(' ', 1)[0] + ' install -r ' + path_to_apk;
|
||||
}
|
||||
var install = exec_out(cmd);
|
||||
if ( install.error && install.output.match(/Failure/)) {
|
||||
Log("Error : Could not install apk to device : ", true);
|
||||
Log(install.output, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else {
|
||||
Log(install.output);
|
||||
}
|
||||
// run on device
|
||||
Log("Launching application...");
|
||||
cmd;
|
||||
if (use_target) {
|
||||
cmd = '%comspec% /c adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
|
||||
} else {
|
||||
cmd = '%comspec% /c adb -s ' + devices[0].split(' ', 1)[0] + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
|
||||
}
|
||||
exec_verbose(cmd);
|
||||
}
|
||||
else {
|
||||
Log('Failed to find apk, make sure you project is built and there is an ', true);
|
||||
@@ -326,18 +345,7 @@ function get_apk(path) {
|
||||
}
|
||||
}
|
||||
|
||||
function install_device(path) {
|
||||
var devices = get_devices();
|
||||
var use_target = false;
|
||||
if (devices.length < 1) {
|
||||
Log("Error : No devices found to install to, make sure there are devices", true);
|
||||
Log(" availible by checking \'<project_dir>\\cordova\\lib\\list-devices\'", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
launch(path, devices[0].split(' ', 1)[0], true);
|
||||
}
|
||||
|
||||
function install_emulator(path) {
|
||||
function install_emulator(target) {
|
||||
var emulators = get_started_emulators();
|
||||
var use_target = false;
|
||||
if (emulators.length < 1) {
|
||||
@@ -345,59 +353,46 @@ function install_emulator(path) {
|
||||
Log(" availible by checking \'<project_dir>\\cordova\\lib\\list-started-emulators\'", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
launch(path, emulators[0].split(' ', 1)[0], false);
|
||||
}
|
||||
|
||||
function install_target(path) {
|
||||
if(device_id) {
|
||||
var device = false;
|
||||
var emulators = get_started_emulators();
|
||||
var devices = get_devices();
|
||||
if (target) {
|
||||
var exists = false;
|
||||
for (i in emulators) {
|
||||
if (emulators[i].substr(0,device_id.length) == device_id) {
|
||||
if (emulators[i].substr(0,target.length) == target)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i in devices) {
|
||||
if (devices[i].substr(0,device_id.length) == device_id) {
|
||||
exists = true;
|
||||
device = true
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists) {
|
||||
Log("Error : Unable to find target " + device_id, true);
|
||||
Log("Please ensure the target exists by checking \'<project>\\cordova\\lib\\list-started-emulators'");
|
||||
Log(" Or \'<project>\\cordova\\lib\\list-devices'");
|
||||
Log("Error : Unable to find target " + target, true);
|
||||
Log("Please ensure the target exists by checking \'<project>\\cordova\\lib\\list-started-emulators'")
|
||||
}
|
||||
launch(path, device_id, device);
|
||||
use_target = true;
|
||||
} else {
|
||||
target = emulators[0].split(' ', 1)[0];
|
||||
Log("Deploying to emulator : " + target);
|
||||
}
|
||||
else {
|
||||
Log("You cannot install to a target without providing a valid target ID.", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
|
||||
function launch(path, id, device) {
|
||||
if(id) {
|
||||
var path_to_apk = get_apk(path);
|
||||
// check if file .apk has been created
|
||||
if (fso.FolderExists(ROOT + '\\bin')) {
|
||||
var path_to_apk;
|
||||
var out_folder = fso.GetFolder(ROOT + '\\bin');
|
||||
var out_files = new Enumerator(out_folder.Files);
|
||||
for (;!out_files.atEnd(); out_files.moveNext()) {
|
||||
var path = out_files.item() + '';
|
||||
if (fso.GetExtensionName(path) == 'apk' && !path.match(/unaligned/)) {
|
||||
path_to_apk = out_files.item();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (path_to_apk) {
|
||||
var launch_name = exec_out("%comspec% /c java -jar "+path+"\\cordova\\appinfo.jar "+path+"\\AndroidManifest.xml");
|
||||
var launch_name = exec_out("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
|
||||
if (launch_name.error) {
|
||||
Log("Failed to get application name from appinfo.jar + AndroidManifest : ", true);
|
||||
Log("Output : " + launch_name.output, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
if (device) {
|
||||
// install on device (-d)
|
||||
Log("Installing app on device...");
|
||||
} else {
|
||||
// install on emulator (-e)
|
||||
Log("Installing app on emulator...");
|
||||
}
|
||||
var cmd = '%comspec% /c adb -s ' + id + ' install -r ' + path_to_apk;
|
||||
// install on emulator (-e)
|
||||
Log("Installing app on emulator...");
|
||||
var cmd = '%comspec% /c adb -s ' + target + ' install -r ' + path_to_apk;
|
||||
var install = exec_out(cmd);
|
||||
if ( install.error && install.output.match(/Failure/)) {
|
||||
Log("Error : Could not install apk to emulator : ", true);
|
||||
@@ -407,9 +402,14 @@ function launch(path, id, device) {
|
||||
else {
|
||||
Log(install.output);
|
||||
}
|
||||
// launch the application
|
||||
// run on emulator
|
||||
Log("Launching application...");
|
||||
cmd = '%comspec% /c adb -s ' + id + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
|
||||
cmd;
|
||||
if (use_target) {
|
||||
cmd = '%comspec% /c adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
|
||||
} else {
|
||||
cmd = '%comspec% /c adb -s ' + emulators[0].split(' ', 1)[0] + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output
|
||||
}
|
||||
exec_verbose(cmd);
|
||||
}
|
||||
else {
|
||||
@@ -419,14 +419,43 @@ function launch(path, id, device) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log("You cannot install to a target without providing a valid target ID.", true);
|
||||
Log('Failed to find apk, make sure you project is built and there is an ', true);
|
||||
Log(' apk in <project>\\bin\\. To build your project use \'<project>\\cordova\\build\'', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
|
||||
function clean(path) {
|
||||
function clean() {
|
||||
Log("Cleaning project...");
|
||||
exec("%comspec% /c ant.bat clean -f "+path+"\\build.xml 2>&1");
|
||||
exec("%comspec% /c ant.bat clean -f "+ROOT+"\\build.xml 2>&1");
|
||||
}
|
||||
|
||||
function build(build_type) {
|
||||
if (build_type) {
|
||||
switch (build_type) {
|
||||
case "--debug" :
|
||||
clean();
|
||||
Log("Building project...");
|
||||
exec_verbose("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
|
||||
break;
|
||||
case "--release" :
|
||||
clean();
|
||||
Log("Building project...");
|
||||
exec_verbose("%comspec% /c ant.bat release -f "+ROOT+"\\build.xml 2>&1");
|
||||
break;
|
||||
case "--nobuild" :
|
||||
Log("Skipping build process.");
|
||||
break;
|
||||
default :
|
||||
Log("Build option not recognized: " + build_type, true);
|
||||
WScript.Quit(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log("WARNING: [ --debug | --release | --nobuild ] not specified, defaulting to --debug.");
|
||||
exec_verbose("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
|
||||
}
|
||||
}
|
||||
|
||||
function log() {
|
||||
@@ -434,176 +463,131 @@ function log() {
|
||||
shell.Run("%comspec% /c adb logcat | grep -v nativeGetEnabledTags");
|
||||
}
|
||||
|
||||
function build(path) {
|
||||
switch (build_type) {
|
||||
case DEBUG :
|
||||
clean(path);
|
||||
Log("Building project...");
|
||||
exec_verbose("%comspec% /c ant.bat debug -f "+path+"\\build.xml 2>&1");
|
||||
break;
|
||||
case RELEASE :
|
||||
clean(path);
|
||||
Log("Building project...");
|
||||
exec_verbose("%comspec% /c ant.bat release -f "+path+"\\build.xml 2>&1");
|
||||
break;
|
||||
case NO_BUILD :
|
||||
Log("Skipping build process.");
|
||||
break;
|
||||
case NONE :
|
||||
clean(path);
|
||||
Log("WARNING: [ --debug | --release | --nobuild ] not specified, defaulting to --debug.");
|
||||
exec_verbose("%comspec% /c ant.bat debug -f "+path+"\\build.xml 2>&1");
|
||||
break;
|
||||
default :
|
||||
Log("Build option not recognized: " + build_type, true);
|
||||
WScript.Quit(2);
|
||||
break;
|
||||
function run(target, build_type) {
|
||||
var use_target = false;
|
||||
if (!target) {
|
||||
Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, using defaults");
|
||||
}
|
||||
}
|
||||
|
||||
function run(path) {
|
||||
switch(deploy_type) {
|
||||
case EMULATOR :
|
||||
build(path);
|
||||
if(get_started_emulators().length == 0) {
|
||||
start_emulator();
|
||||
}
|
||||
//TODO : Start emulator if one isn't started, and create one if none exists.
|
||||
install_emulator(path);
|
||||
break;
|
||||
case DEVICE :
|
||||
build(path);
|
||||
install_device(path);
|
||||
break;
|
||||
case TARGET :
|
||||
build(path);
|
||||
install_target(path);
|
||||
break;
|
||||
case NONE :
|
||||
if (get_devices().length > 0) {
|
||||
Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, defaulting to --device");
|
||||
deploy_type = DEVICE;
|
||||
// build application
|
||||
build(build_type);
|
||||
// attempt to deploy to connected device
|
||||
var devices = get_devices();
|
||||
if (devices.length > 0 || target == "--device") {
|
||||
if (target) {
|
||||
if (target.substr(0,9) == "--target=") {
|
||||
install_device(target.split('--target=').join(''))
|
||||
} else if (target == "--device") {
|
||||
install_device();
|
||||
} else {
|
||||
Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, defaulting to --emulator");
|
||||
deploy_type = EMULATOR;
|
||||
Log("Did not regognize " + target + " as a run option.", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
run(path);
|
||||
break;
|
||||
default :
|
||||
Log("Deploy option not recognized: " + deploy_type, true);
|
||||
WScript.Quit(2);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, using defaults");
|
||||
install_device();
|
||||
}
|
||||
}
|
||||
else {
|
||||
var emulators = get_started_emulators();
|
||||
if (emulators.length > 0) {
|
||||
install_emulator();
|
||||
}
|
||||
else {
|
||||
var emulator_images = get_emulator_images();
|
||||
if (emulator_images.length < 1) {
|
||||
Log('No emulators found, if you would like to create an emulator follow the instructions', true);
|
||||
Log(' provided here : http://developer.android.com/tools/devices/index.html', true);
|
||||
Log(' Or run \'android create avd --name <name> --target <targetID>\' in on the command line.', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
start_emulator(emulator_images[0].split(' ')[0]);
|
||||
emulators = get_started_emulators();
|
||||
if (emulators.length > 0) {
|
||||
install_emulator();
|
||||
}
|
||||
else {
|
||||
Log("Error : emulator failed to start.", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var args = WScript.Arguments;
|
||||
if (args.count() == 0) {
|
||||
Log("Error: no args provided.");
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else {
|
||||
// parse command
|
||||
switch(args(0)) {
|
||||
case "version" :
|
||||
version(ROOT);
|
||||
break;
|
||||
case "build" :
|
||||
if(args.Count() > 1) {
|
||||
if (args(1) == "--release") {
|
||||
build_type = RELEASE;
|
||||
}
|
||||
else if (args(1) == "--debug") {
|
||||
build_type = DEBUG;
|
||||
}
|
||||
else if (args(1) == "--nobuild") {
|
||||
build_type = NO_BUILD;
|
||||
}
|
||||
else {
|
||||
Log('Error: \"' + args(i) + '\" is not recognized as a build option', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
build(ROOT);
|
||||
break;
|
||||
case "clean" :
|
||||
clean();
|
||||
break;
|
||||
case "list-devices" :
|
||||
list_devices();
|
||||
break;
|
||||
case "list-emulator-images" :
|
||||
list_emulator_images();
|
||||
break;
|
||||
case "list-started-emulators" :
|
||||
list_started_emulators();
|
||||
break;
|
||||
case "start-emulator" :
|
||||
if (args.Count() > 1) {
|
||||
start_emulator(args(1))
|
||||
if (args(0) == "build") {
|
||||
if (args.Count() > 1) {
|
||||
build(args(1))
|
||||
} else {
|
||||
build();
|
||||
}
|
||||
} else if (args(0) == "clean") {
|
||||
clean();
|
||||
} else if (args(0) == "list-devices") {
|
||||
list_devices();
|
||||
} else if (args(0) == "list-emulator-images") {
|
||||
list_emulator_images();
|
||||
} else if (args(0) == "list-started-emulators") {
|
||||
list_started_emulators();
|
||||
} else if (args(0) == "start-emulator") {
|
||||
if (args.Count() > 1) {
|
||||
start_emulator(args(1))
|
||||
} else {
|
||||
start_emulator();
|
||||
}
|
||||
} else if (args(0) == "log") {
|
||||
log();
|
||||
} else if (args(0) == "install-emulator") {
|
||||
if (args.Count() == 2) {
|
||||
if (args(1).substr(0,9) == "--target=") {
|
||||
install_emulator(args(1).split('--target=').join(''));
|
||||
} else {
|
||||
start_emulator();
|
||||
Log('Error: \"' + args(1) + '\" is not recognized as an install option', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
break;
|
||||
case "install-emulator" :
|
||||
if (args.Count() == 2) {
|
||||
if (args(1).substr(0,9) == "--target=") {
|
||||
device_id = args(1).split('--target=').join('');
|
||||
install_emulator(ROOT);
|
||||
} else {
|
||||
Log('Error: \"' + args(1) + '\" is not recognized as an install option', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
} else {
|
||||
install_emulator();
|
||||
}
|
||||
} else if (args(0) == "install-device") {
|
||||
if (args.Count() == 2) {
|
||||
if (args(1).substr(0,9) == "--target=") {
|
||||
install_device(args(1).split('--target=').join(''));
|
||||
} else {
|
||||
install_emulator(ROOT);
|
||||
Log('Error: \"' + args(1) + '\" is not recognized as an install option', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
break;
|
||||
case "install-device" :
|
||||
if (args.Count() == 2) {
|
||||
if (args(1).substr(0,9) == "--target=") {
|
||||
device_id = args(1).split('--target=').join('');
|
||||
install_target(ROOT);
|
||||
} else {
|
||||
Log('Error: \"' + args(1) + '\" is not recognized as an install option', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
} else {
|
||||
install_device();
|
||||
}
|
||||
} else if (args(0) == "run") {
|
||||
if (args.Count() == 3) {
|
||||
run(args(1), args(2));
|
||||
}
|
||||
else if (args.Count() == 2) {
|
||||
if (args(1).substr(0,9) == "--target=" ||
|
||||
args(1) == "--emulator" ||
|
||||
args(1) == "--device") {
|
||||
run(args(1));
|
||||
} else if (args(1) == "--debug" ||
|
||||
args(1) == "--release" ||
|
||||
args(1) == "--nobuild") {
|
||||
run(null, args(1))
|
||||
} else {
|
||||
install_device(ROOT);
|
||||
Log('Error: \"' + args(1) + '\" is not recognized as a run option', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
break;
|
||||
case "run" :
|
||||
//parse args
|
||||
for(var i = 1; i < args.Count(); i++) {
|
||||
if (args(i) == "--release") {
|
||||
build_type = RELEASE;
|
||||
}
|
||||
else if (args(i) == "--debug") {
|
||||
build_type = DEBUG;
|
||||
}
|
||||
else if (args(i) == "--nobuild") {
|
||||
build_type = NO_BUILD;
|
||||
}
|
||||
else if (args(i) == "--emulator" || args(i) == "-e") {
|
||||
deploy_type = EMULATOR;
|
||||
}
|
||||
else if (args(i) == "--device" || args(i) == "-d") {
|
||||
deploy_type = DEVICE;
|
||||
}
|
||||
else if (args(i).substr(0,9) == "--target=") {
|
||||
device_id = args(i).split("--target=").join("");
|
||||
deploy_type = TARGET;
|
||||
}
|
||||
else {
|
||||
Log('Error: \"' + args(i) + '\" is not recognized as a run option', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
run(ROOT);
|
||||
break;
|
||||
default :
|
||||
Log("Cordova does not regognize the command " + args(0), true);
|
||||
WScript.Quit(2);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
run();
|
||||
}
|
||||
} else {
|
||||
Log('Error: \"' + args(0) + '\" is not recognized as a tooling command', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,34 +16,8 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$DIR/../.." && pwd )
|
||||
device_list=$("$DIR/list-devices")
|
||||
if [ $? != 0 ]; then
|
||||
echo "No devices found to deploy to. Please make sure your device is connected"
|
||||
echo " and you can view it using the 'cordova/lib/list-devices' command."
|
||||
exit 2
|
||||
fi
|
||||
set -e
|
||||
|
||||
apks=`find $PROJECT_PATH/bin -type f -maxdepth 1 | egrep '\.apk$'`
|
||||
apk_list=($apks)
|
||||
if [[ ${#apk_list[@]} > 0 ]] ; then
|
||||
# handle target
|
||||
read -ra device_array <<< "$device_list"
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
# deploy to given target
|
||||
target=${1/--target=/}
|
||||
else
|
||||
# delete trailing space and 'device' after device ID
|
||||
target=${device_array[0]}
|
||||
fi
|
||||
echo "Installing ${apk_list[0]} onto device $target..."
|
||||
adb -s $target install -r ${apk_list[0]};
|
||||
echo "Launching application..."
|
||||
launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
|
||||
adb -s $target shell am start -W -a android.intent.action.MAIN -n $launch_str
|
||||
else
|
||||
echo "Application package not found, could not install to device"
|
||||
echo " make sure your application is built before deploying."
|
||||
exit 2
|
||||
fi
|
||||
CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash "$CORDOVA_LIB_PATH"/cordova install-device "$@"
|
||||
@@ -16,35 +16,8 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$DIR/../.." && pwd )
|
||||
emulator_list=$("$DIR/list-started-emulators")
|
||||
if [ $? != 0 ]; then
|
||||
echo "No emulators found to deploy to. Please make sure your emulator is started"
|
||||
echo " You can view it using the 'cordova/lib/list-started-emulators' command."
|
||||
echo " You can view created emulator images using the 'cordova/lib/list-emulator-images' command."
|
||||
echo " You can start an emulator image using the 'cordova/lib/start-emulator' command."
|
||||
exit 2
|
||||
fi
|
||||
set -e
|
||||
|
||||
apks=`find $PROJECT_PATH/bin -type f -maxdepth 1 | egrep '\.apk$'`
|
||||
apk_list=($apks)
|
||||
if [[ ${#apk_list[@]} > 0 ]] ; then
|
||||
# handle target emulator
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
# deploy to given target
|
||||
target=${1/--target=/}
|
||||
else
|
||||
# delete trailing space and 'device' after emulator ID
|
||||
target=${emulator_list[0]}
|
||||
fi
|
||||
echo "Installing ${apk_list[0]} onto emulator $target..."
|
||||
adb -s $target install -r ${apk_list[0]};
|
||||
echo "Launching application..."
|
||||
launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
|
||||
adb -s $target shell am start -W -a android.intent.action.MAIN -n $launch_str
|
||||
else
|
||||
echo "Application package not found, could not install to device"
|
||||
echo " make sure your application is built before deploying."
|
||||
exit 2
|
||||
fi
|
||||
CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash "$CORDOVA_LIB_PATH"/cordova install-emulator "$@"
|
||||
@@ -16,15 +16,8 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator' | awk '{ print $1; }'`
|
||||
device_list=($devices)
|
||||
if [[ ${#device_list[@]} > 0 ]] ; then
|
||||
for i in ${devices[@]}
|
||||
do
|
||||
echo $i
|
||||
done
|
||||
exit 0
|
||||
else
|
||||
echo "No devices found."
|
||||
exit 2
|
||||
fi
|
||||
set -e
|
||||
|
||||
CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash "$CORDOVA_LIB_PATH"/cordova list-devices
|
||||
@@ -16,17 +16,8 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
|
||||
emulator_list=($emulator_images)
|
||||
if [[ ${#emulator_list[@]} > 0 ]] ; then
|
||||
for i in ${emulator_list[@]}
|
||||
do
|
||||
echo $i
|
||||
done
|
||||
exit 0
|
||||
else
|
||||
echo "No emulators found, if you would like to create an emulator follow the instructions"
|
||||
echo " provided here : http://developer.android.com/tools/devices/index.html"
|
||||
echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
|
||||
exit 2
|
||||
fi
|
||||
set -e
|
||||
|
||||
CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash "$CORDOVA_LIB_PATH"/cordova list-emulator-images
|
||||
@@ -16,17 +16,8 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print $1;}}' | grep 'emulator' | grep -v 'offline'`
|
||||
read -ra emulator_list <<< "$devices"
|
||||
if [[ ${#emulator_list[@]} > 0 ]] ; then
|
||||
for i in ${emulator_list[@]}
|
||||
do
|
||||
# remove space and 'device'
|
||||
echo $i
|
||||
done
|
||||
exit 0
|
||||
else
|
||||
echo "No started emulators found (it may still be booting up), you can start an emulator by using the command"
|
||||
echo " 'cordova/lib/start-emulator'"
|
||||
exit 2
|
||||
fi
|
||||
set -e
|
||||
|
||||
CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash "$CORDOVA_LIB_PATH"/cordova list-started-emulators
|
||||
@@ -16,96 +16,8 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$( dirname "$0" )/../.." && pwd )
|
||||
set -e
|
||||
|
||||
function dot {
|
||||
sleep 1
|
||||
echo -n "."
|
||||
}
|
||||
CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
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)
|
||||
while [ $? != 0 ]
|
||||
do
|
||||
dot
|
||||
i=$[i+1]
|
||||
emulator_string=$($DIR/list-started-emulators)
|
||||
done
|
||||
read -ra target <<< "$emulator_string"
|
||||
echo ""
|
||||
echo -n "Waiting for it to boot up (this can take a while)"
|
||||
while [ $i -lt 300 ]
|
||||
do
|
||||
boot_anim=$(adb -s $target shell getprop init.svc.bootanim 2>&1)
|
||||
if [[ "$boot_anim" =~ "stopped" ]] ; then
|
||||
break
|
||||
else
|
||||
i=$[i+1]
|
||||
dot
|
||||
fi
|
||||
done
|
||||
# Device timeout: emulator has not started in time
|
||||
if [ $i -eq 300 ]
|
||||
then
|
||||
echo ""
|
||||
echo "Emulator timeout!"
|
||||
exit 69
|
||||
else
|
||||
echo ""
|
||||
echo "Connected!"
|
||||
fi
|
||||
# Unlock the device
|
||||
adb -s $target shell input keyevent 82
|
||||
exit 0
|
||||
}
|
||||
|
||||
emulator_images=$("$DIR/list-emulator-images")
|
||||
if [ $? != 0 ]; then
|
||||
echo "No emulators found, if you would like to create an emulator follow the instructions"
|
||||
echo " provided here : http://developer.android.com/tools/devices/index.html"
|
||||
echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
|
||||
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> "${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
|
||||
read -ra emulator_list <<< "$emulator_images"
|
||||
#xterm -e emulator -avd ${emulator_list[0]} &
|
||||
emulator -avd ${emulator_list[0]} 1> "${log_path}" 2>&1 &
|
||||
fi
|
||||
|
||||
echo "Saving emulator log to: ${log_path}"
|
||||
wait_for_emulator "$log_path"
|
||||
bash "$CORDOVA_LIB_PATH"/cordova start-emulator "$@"
|
||||
@@ -16,5 +16,8 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# filter out nativeGetEnabledTags spam from latest sdk bug.
|
||||
adb logcat | grep -v nativeGetEnabledTags
|
||||
set -e
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
||||
|
||||
bash "$CORDOVA_PATH"/cordova/lib/cordova log "$@"
|
||||
|
||||
@@ -16,66 +16,8 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$DIR/.." && pwd )
|
||||
set -e
|
||||
|
||||
function run_on_device_or_emulator {
|
||||
devices=`$DIR/lib/list-devices`
|
||||
if [ $? = 0 ]; then
|
||||
$DIR/lib/install-device
|
||||
else
|
||||
run_on_emulator
|
||||
fi
|
||||
}
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
function run_on_emulator {
|
||||
emulators=`$DIR/lib/list-started-emulators`
|
||||
if [ $? = 0 ] ; then
|
||||
$DIR/lib/install-emulator
|
||||
else
|
||||
images=`$DIR/lib/list-emulator-images`
|
||||
if [ $? = 0 ] ; then
|
||||
$DIR/lib/start-emulator
|
||||
$DIR/lib/install-emulator
|
||||
else
|
||||
echo "No devices/emulators started nor images available to start. How are we supposed to do this, then?"
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
if [[ "$#" -eq 2 ]] ; then
|
||||
# TODO: the order of arguments here may be reversed from the assumption below
|
||||
$DIR/build $2
|
||||
if [[ $1 == "--device" ]] ; then
|
||||
$DIR/lib/install-device
|
||||
elif [[ $1 == "--emulator" ]] ; then
|
||||
run_on_emulator
|
||||
elif [[ $1 =~ "--target=" ]]; then
|
||||
$DIR/lib/install-device $1
|
||||
else
|
||||
echo "Error : '$1' is not recognized as an install option"
|
||||
fi
|
||||
elif [[ "$#" -eq 1 ]] ; then
|
||||
if [[ $1 == "--debug" || $1 == "--release" || $1 == "--nobuild" ]] ; then
|
||||
$DIR/build $1
|
||||
run_on_device_or_emulator
|
||||
elif [[ $1 == "--device" ]] ; then
|
||||
$DIR/build
|
||||
$DIR/lib/install-device
|
||||
elif [[ $1 == "--emulator" ]] ; then
|
||||
$DIR/build
|
||||
run_on_emulator
|
||||
elif [[ $1 =~ "--target=" ]]; then
|
||||
$DIR/build
|
||||
$DIR/lib/install-device $1
|
||||
else
|
||||
echo "Error : '$1' is not recognized as an install option"
|
||||
fi
|
||||
else
|
||||
echo "Warning : [ --device | --emulate | --target=<targetID> ] not specified, using defaults."
|
||||
$DIR/build
|
||||
run_on_device_or_emulator
|
||||
fi
|
||||
bash "$CORDOVA_PATH"/lib/cordova run "$@"
|
||||
|
||||
@@ -16,4 +16,17 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
echo "2.9.0"
|
||||
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' assets/www/cordova.js)
|
||||
echo $JSVersion | sed -e 's/\/\/ //'| cut -f 1 -d '-'
|
||||
else
|
||||
echo "The file \"$VERSION_FILE_PATH\" does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -1,18 +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.
|
||||
@ECHO OFF
|
||||
%~dp0\cordova.bat version %*
|
||||
@@ -59,5 +59,5 @@
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="__APILEVEL__"/>
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="__APILEVEL__"/>
|
||||
</manifest>
|
||||
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 478 KiB |
|
After Width: | Height: | Size: 493 KiB |
68
bin/templates/project/assets/www/spec.html
Normal file
@@ -0,0 +1,68 @@
|
||||
<!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>
|
||||
33
bin/templates/project/assets/www/spec/helper.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
};
|
||||
67
bin/templates/project/assets/www/spec/index.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
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.
|
||||
@@ -0,0 +1,616 @@
|
||||
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;
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
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; }
|
||||
2529
bin/templates/project/assets/www/spec/lib/jasmine-1.2.0/jasmine.js
Normal file
23
bin/update
@@ -45,6 +45,10 @@ fi
|
||||
|
||||
# cleanup after exit and/or on error
|
||||
function on_exit {
|
||||
if [ -f "$BUILD_PATH"/framework/assets/www/cordova.js ]
|
||||
then
|
||||
rm "$BUILD_PATH"/framework/assets/www/cordova.js
|
||||
fi
|
||||
if [ -f "$BUILD_PATH"/framework/cordova-$VERSION.jar ]
|
||||
then
|
||||
rm "$BUILD_PATH"/framework/cordova-$VERSION.jar
|
||||
@@ -99,6 +103,16 @@ then
|
||||
# update the cordova-android framework for the desired target
|
||||
"$ANDROID_BIN" update project --target $TARGET --path "$BUILD_PATH"/framework &> /dev/null
|
||||
|
||||
if [ ! -e "$BUILD_PATH"/framework/libs/commons-codec-1.7.jar ]; then
|
||||
# Use curl to get the jar (TODO: Support Apache Mirrors)
|
||||
curl -OL http://archive.apache.org/dist/commons/codec/binaries/commons-codec-1.7-bin.zip &> /dev/null
|
||||
unzip commons-codec-1.7-bin.zip &> /dev/null
|
||||
mkdir -p "$BUILD_PATH"/framework/libs
|
||||
cp commons-codec-1.7/commons-codec-1.7.jar "$BUILD_PATH"/framework/libs
|
||||
# cleanup yo
|
||||
rm commons-codec-1.7-bin.zip && rm -rf commons-codec-1.7
|
||||
fi
|
||||
|
||||
# compile cordova.js and cordova.jar
|
||||
(cd "$BUILD_PATH"/framework && ant jar &> /dev/null )
|
||||
fi
|
||||
@@ -114,17 +128,14 @@ else
|
||||
fi
|
||||
|
||||
# creating cordova folder and copying run/build/log/launch scripts
|
||||
if [ ! -e "$PROJECT_PATH/cordova" ]
|
||||
then
|
||||
mkdir "$PROJECT_PATH"/cordova
|
||||
mkdir "$PROJECT_PATH"/cordova/lib
|
||||
fi
|
||||
mkdir "$PROJECT_PATH"/cordova
|
||||
mkdir "$PROJECT_PATH"/cordova/lib
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/appinfo.jar "$PROJECT_PATH"/cordova/appinfo.jar
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/build "$PROJECT_PATH"/cordova/build
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/clean "$PROJECT_PATH"/cordova/clean
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/log "$PROJECT_PATH"/cordova/log
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/run "$PROJECT_PATH"/cordova/run
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/cordova.js "$PROJECT_PATH"/cordova/lib/cordova.js
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/cordova "$PROJECT_PATH"/cordova/lib/cordova
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/install-device "$PROJECT_PATH"/cordova/lib/install-device
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/install-emulator "$PROJECT_PATH"/cordova/lib/install-emulator
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/list-devices "$PROJECT_PATH"/cordova/lib/list-devices
|
||||
|
||||
@@ -96,6 +96,46 @@ function cleanup() {
|
||||
}
|
||||
}
|
||||
|
||||
function downloadCommonsCodec() {
|
||||
if (!fso.FileExists(ROOT + '\\framework\\libs\\commons-codec-1.7.jar')) {
|
||||
// We need the .jar
|
||||
var url = 'http://archive.apache.org/dist/commons/codec/binaries/commons-codec-1.7-bin.zip';
|
||||
var libsPath = ROOT + '\\framework\\libs';
|
||||
var savePath = libsPath + '\\commons-codec-1.7-bin.zip';
|
||||
if (!fso.FileExists(savePath)) {
|
||||
if(!fso.FolderExists(ROOT + '\\framework\\libs')) {
|
||||
fso.CreateFolder(libsPath);
|
||||
}
|
||||
// We need the zip to get the jar
|
||||
var xhr = WScript.CreateObject('MSXML2.XMLHTTP');
|
||||
xhr.open('GET', url, false);
|
||||
xhr.send();
|
||||
if (xhr.status == 200) {
|
||||
var stream = WScript.CreateObject('ADODB.Stream');
|
||||
stream.Open();
|
||||
stream.Type = 1;
|
||||
stream.Write(xhr.ResponseBody);
|
||||
stream.Position = 0;
|
||||
stream.SaveToFile(savePath);
|
||||
stream.Close();
|
||||
} else {
|
||||
WScript.Echo('Could not retrieve the commons-codec. Please download it yourself and put into the framework/libs directory. This process may fail now. Sorry.');
|
||||
}
|
||||
}
|
||||
var app = WScript.CreateObject('Shell.Application');
|
||||
var source = app.NameSpace(savePath).Items();
|
||||
var target = app.NameSpace(ROOT + '\\framework\\libs');
|
||||
target.CopyHere(source, 256);
|
||||
|
||||
// Move the jar into libs
|
||||
fso.MoveFile(ROOT + '\\framework\\libs\\commons-codec-1.7\\commons-codec-1.7.jar', ROOT + '\\framework\\libs\\commons-codec-1.7.jar');
|
||||
|
||||
// Clean up
|
||||
fso.DeleteFile(ROOT + '\\framework\\libs\\commons-codec-1.7-bin.zip');
|
||||
fso.DeleteFolder(ROOT + '\\framework\\libs\\commons-codec-1.7', true);
|
||||
}
|
||||
}
|
||||
|
||||
var args = WScript.Arguments, PROJECT_PATH="example",
|
||||
shell=WScript.CreateObject("WScript.Shell");
|
||||
|
||||
@@ -121,6 +161,8 @@ if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
|
||||
WScript.Echo("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');
|
||||
// pull down commons codec if necessary
|
||||
downloadCommonsCodec();
|
||||
exec('ant.bat -f \"'+ ROOT +'\\framework\\build.xml\" jar');
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="lib" path="libs/commons-codec-1.7.jar"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
|
||||
616
framework/assets/www/cordova.js
vendored
@@ -1,5 +1,5 @@
|
||||
// Platform: android
|
||||
// 2.9.1
|
||||
// 2.8.0-0-g6208c95
|
||||
/*
|
||||
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.1';
|
||||
var CORDOVA_JS_BUILD_LABEL = '2.8.0-0-g6208c95';
|
||||
// file: lib/scripts/require.js
|
||||
|
||||
var require,
|
||||
@@ -100,7 +100,6 @@ define("cordova", function(require, exports, module) {
|
||||
|
||||
|
||||
var channel = require('cordova/channel');
|
||||
var platform = require('cordova/platform');
|
||||
|
||||
/**
|
||||
* Listen for DOMContentLoaded and notify our channel subscribers.
|
||||
@@ -183,18 +182,10 @@ 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.
|
||||
*/
|
||||
@@ -230,16 +221,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);
|
||||
@@ -356,7 +347,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) {
|
||||
@@ -385,7 +376,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 unit tests.
|
||||
// Don't log when running jake test.
|
||||
if (typeof jasmine == 'undefined') {
|
||||
console.error(errMsg);
|
||||
}
|
||||
@@ -402,62 +393,6 @@ 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
|
||||
@@ -500,36 +435,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];
|
||||
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);
|
||||
} else {
|
||||
// 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];
|
||||
}
|
||||
// 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 + '"');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -873,7 +808,6 @@ 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,
|
||||
@@ -912,7 +846,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] = base64.fromArrayBuffer(args[i]);
|
||||
args[i] = window.btoa(String.fromCharCode.apply(null, new Uint8Array(args[i])));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -940,12 +874,8 @@ function androidExec(success, fail, service, action, args) {
|
||||
}
|
||||
}
|
||||
|
||||
function pollOnceFromOnlineEvent() {
|
||||
pollOnce(true);
|
||||
}
|
||||
|
||||
function pollOnce(opt_fromOnlineEvent) {
|
||||
var msg = nativeApiProvider.get().retrieveJsMessages(!!opt_fromOnlineEvent);
|
||||
function pollOnce() {
|
||||
var msg = nativeApiProvider.get().retrieveJsMessages();
|
||||
androidExec.processMessages(msg);
|
||||
}
|
||||
|
||||
@@ -964,8 +894,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', pollOnceFromOnlineEvent, false);
|
||||
window.addEventListener('offline', pollOnceFromOnlineEvent, false);
|
||||
window.addEventListener('online', pollOnce, false);
|
||||
window.addEventListener('offline', pollOnce, false);
|
||||
cordova.addWindowEventHandler('online');
|
||||
cordova.addWindowEventHandler('offline');
|
||||
document.addEventListener('online', proxyEvent, false);
|
||||
@@ -1126,10 +1056,6 @@ 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;
|
||||
@@ -1148,16 +1074,12 @@ 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];
|
||||
@@ -2006,7 +1928,6 @@ var exec = require('cordova/exec'),
|
||||
*/
|
||||
function DirectoryReader(path) {
|
||||
this.path = path || null;
|
||||
this.hasReadEntries = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2016,12 +1937,6 @@ 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++) {
|
||||
@@ -2038,7 +1953,6 @@ 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) {
|
||||
@@ -3145,32 +3059,9 @@ FileWriter.prototype.abort = function() {
|
||||
/**
|
||||
* Writes data to the file
|
||||
*
|
||||
* @param data text or blob to be written
|
||||
* @param text to be written
|
||||
*/
|
||||
FileWriter.prototype.write = function(data) {
|
||||
|
||||
var that=this;
|
||||
var supportsBinary = (typeof window.Blob !== 'undefined' && typeof window.ArrayBuffer !== 'undefined');
|
||||
var isBinary;
|
||||
|
||||
// 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);
|
||||
} else {
|
||||
fileReader.readAsText(data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark data type for safer transport over the binary bridge
|
||||
isBinary = supportsBinary && (data instanceof ArrayBuffer);
|
||||
|
||||
FileWriter.prototype.write = function(text) {
|
||||
// Throw an exception if we are already writing a file
|
||||
if (this.readyState === FileWriter.WRITING) {
|
||||
throw new FileError(FileError.INVALID_STATE_ERR);
|
||||
@@ -3236,7 +3127,7 @@ FileWriter.prototype.write = function(data) {
|
||||
if (typeof me.onwriteend === "function") {
|
||||
me.onwriteend(new ProgressEvent("writeend", {"target":me}));
|
||||
}
|
||||
}, "File", "write", [this.fileName, data, this.position, isBinary]);
|
||||
}, "File", "write", [this.fileName, text, this.position]);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3403,7 +3294,6 @@ 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 = {
|
||||
@@ -3412,7 +3302,6 @@ function InAppBrowser() {
|
||||
'loaderror' : channel.create('loaderror'),
|
||||
'exit' : channel.create('exit')
|
||||
};
|
||||
this._alive = true;
|
||||
}
|
||||
|
||||
InAppBrowser.prototype = {
|
||||
@@ -3422,13 +3311,7 @@ InAppBrowser.prototype = {
|
||||
}
|
||||
},
|
||||
close: function (eventname) {
|
||||
if (this._alive) {
|
||||
this._alive = false;
|
||||
exec(null, null, "InAppBrowser", "close", []);
|
||||
}
|
||||
},
|
||||
show: function (eventname) {
|
||||
exec(null, null, "InAppBrowser", "show", []);
|
||||
exec(null, null, "InAppBrowser", "close", []);
|
||||
},
|
||||
addEventListener: function (eventname,f) {
|
||||
if (eventname in this.channels) {
|
||||
@@ -3463,18 +3346,17 @@ 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;
|
||||
};
|
||||
@@ -4066,73 +3948,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", []);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
@@ -4279,8 +4161,8 @@ module.exports = {
|
||||
setNativeToJsBridgeMode: function(value) {
|
||||
prompt(value, 'gap_bridge_mode:');
|
||||
},
|
||||
retrieveJsMessages: function(fromOnlineEvent) {
|
||||
return prompt(+fromOnlineEvent, 'gap_poll:');
|
||||
retrieveJsMessages: function() {
|
||||
return prompt('', 'gap_poll:');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4575,9 +4457,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
|
||||
};
|
||||
|
||||
});
|
||||
@@ -6608,127 +6490,6 @@ 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
|
||||
@@ -6744,23 +6505,6 @@ 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) {
|
||||
|
||||
@@ -6941,8 +6685,6 @@ 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) {
|
||||
@@ -7011,16 +6753,126 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to inject a <script> tag.
|
||||
function injectScript(path) {
|
||||
scriptCounter++;
|
||||
var script = document.createElement("script");
|
||||
script.onload = scriptLoadedCallback;
|
||||
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.json 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 XHR 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) continue;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 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() {
|
||||
finishPluginLoading();
|
||||
};
|
||||
var plugins_json = path + 'cordova_plugins.json';
|
||||
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){
|
||||
finishPluginLoading();
|
||||
}
|
||||
}(window));
|
||||
|
||||
|
||||
})();
|
||||
@@ -31,6 +31,22 @@
|
||||
<fail message="The required minimum version of ant is 1.8.0, you have ${ant.version}"
|
||||
unless="thisantversion" />
|
||||
|
||||
<!-- check that commons codec is available. You should copy the codec jar to
|
||||
framework/libs, as it is not included in the Cordova distribution.
|
||||
The name of the jar file in framework/libs does not matter. -->
|
||||
<available classname="org.apache.commons.codec.binary.Base64"
|
||||
property="exists.base64"
|
||||
ignoresystemclasses="true">
|
||||
<classpath>
|
||||
<pathelement path="${classpath}" />
|
||||
<fileset dir="libs">
|
||||
<include name="*.jar" />
|
||||
</fileset>
|
||||
</classpath>
|
||||
</available>
|
||||
<fail message="You need to put a copy of Apache Commons Codec jar in the framework/libs directory"
|
||||
unless="exists.base64" />
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android'
|
||||
tool. (For example "sdkdir/tools/android update project -p ." inside
|
||||
of this directory where the AndroidManifest.xml file exists. This
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.json.JSONObject;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This class exposes methods in Cordova that can be called from JavaScript.
|
||||
* This class exposes methods in DroidGap that can be called from JavaScript.
|
||||
*/
|
||||
public class App extends CordovaPlugin {
|
||||
|
||||
@@ -97,18 +97,14 @@ public class App extends CordovaPlugin {
|
||||
* Clear the resource cache.
|
||||
*/
|
||||
public void clearCache() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
webView.clearCache(true);
|
||||
}
|
||||
});
|
||||
this.webView.clearCache(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
*
|
||||
* @param url
|
||||
* @param props Properties that can be passed in to the Cordova activity (i.e. loadingDialog, wait, ...)
|
||||
* @param props Properties that can be passed in to the DroidGap activity (i.e. loadingDialog, wait, ...)
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void loadUrl(String url, JSONObject props) throws JSONException {
|
||||
@@ -168,11 +164,7 @@ public class App extends CordovaPlugin {
|
||||
* Clear page history for the app.
|
||||
*/
|
||||
public void clearHistory() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
webView.clearHistory();
|
||||
}
|
||||
});
|
||||
this.webView.clearHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,7 +198,7 @@ public class App extends CordovaPlugin {
|
||||
* @param override T=override, F=cancel override
|
||||
*/
|
||||
public void overrideButton(String button, boolean override) {
|
||||
LOG.i("App", "WARNING: Volume Button Default Behaviour will be overridden. The volume event will be fired!");
|
||||
LOG.i("DroidGap", "WARNING: Volume Button Default Behaviour will be overridden. The volume event will be fired!");
|
||||
webView.bindButton(button, override);
|
||||
}
|
||||
|
||||
|
||||
@@ -537,7 +537,6 @@ 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);
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
@@ -41,12 +42,12 @@ 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;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
@@ -73,7 +74,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)
|
||||
@@ -92,6 +93,23 @@ 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.
|
||||
*
|
||||
@@ -133,26 +151,15 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
this.targetHeight = -1;
|
||||
}
|
||||
|
||||
try {
|
||||
if (srcType == CAMERA) {
|
||||
this.takePicture(destType, encodingType);
|
||||
}
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
this.getImage(srcType, destType);
|
||||
}
|
||||
if (srcType == CAMERA) {
|
||||
this.takePicture(destType, encodingType);
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
callbackContext.error("Illegal Argument Exception");
|
||||
PluginResult r = new PluginResult(PluginResult.Status.ERROR);
|
||||
callbackContext.sendPluginResult(r);
|
||||
return true;
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
this.getImage(srcType, destType);
|
||||
}
|
||||
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(r);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -162,24 +169,6 @@ 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
|
||||
@@ -222,9 +211,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
private File createCaptureFile(int encodingType) {
|
||||
File photo = null;
|
||||
if (encodingType == JPEG) {
|
||||
photo = new File(getTempDirectoryPath(), ".Pic.jpg");
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.jpg");
|
||||
} else if (encodingType == PNG) {
|
||||
photo = new File(getTempDirectoryPath(), ".Pic.png");
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.png");
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType);
|
||||
}
|
||||
@@ -288,7 +277,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(getTempDirectoryPath() + "/.Pic.jpg");
|
||||
exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg");
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
}
|
||||
@@ -329,7 +318,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(getTempDirectoryPath(), System.currentTimeMillis() + ".jpg"));
|
||||
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
@@ -449,7 +438,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 = getTempDirectoryPath() + "/resize.jpg";
|
||||
String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/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();
|
||||
@@ -747,7 +736,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
Uri uri = Uri.parse(contentStore + "/" + id);
|
||||
this.cordova.getActivity().getContentResolver().delete(uri, null, null);
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,7 +761,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.NO_WRAP);
|
||||
byte[] output = Base64.encodeBase64(code);
|
||||
String js_out = new String(output);
|
||||
this.callbackContext.success(js_out);
|
||||
js_out = null;
|
||||
|
||||
@@ -62,7 +62,7 @@ public class Capture extends CordovaPlugin {
|
||||
|
||||
private CallbackContext callbackContext; // The callback context from which we were invoked.
|
||||
private long limit; // the number of pics/vids/clips to take
|
||||
private int duration; // optional max duration of video recording in seconds
|
||||
private double duration; // optional duration parameter for video recording
|
||||
private JSONArray results; // The array of results to be returned to the user
|
||||
private int numPics; // Number of pictures before capture activity
|
||||
|
||||
@@ -80,13 +80,13 @@ public class Capture extends CordovaPlugin {
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
this.callbackContext = callbackContext;
|
||||
this.limit = 1;
|
||||
this.duration = 0;
|
||||
this.duration = 0.0f;
|
||||
this.results = new JSONArray();
|
||||
|
||||
JSONObject options = args.optJSONObject(0);
|
||||
if (options != null) {
|
||||
limit = options.optLong("limit", 1);
|
||||
duration = options.optInt("duration", 0);
|
||||
duration = options.optDouble("duration", 0.0f);
|
||||
}
|
||||
|
||||
if (action.equals("getFormatData")) {
|
||||
@@ -215,10 +215,10 @@ public class Capture extends CordovaPlugin {
|
||||
/**
|
||||
* Sets up an intent to capture video. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureVideo(int duration) {
|
||||
private void captureVideo(double duration) {
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
|
||||
|
||||
if(Build.VERSION.SDK_INT > 7){
|
||||
if(Build.VERSION.SDK_INT > 8){
|
||||
intent.putExtra("android.intent.extra.durationLimit", duration);
|
||||
}
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_VIDEO);
|
||||
@@ -305,20 +305,14 @@ public class Capture extends CordovaPlugin {
|
||||
// Get the uri of the video clip
|
||||
Uri data = intent.getData();
|
||||
// create a file object from the uri
|
||||
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);
|
||||
}
|
||||
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,7 +24,6 @@ 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;
|
||||
@@ -72,7 +71,7 @@ public class Config {
|
||||
return;
|
||||
}
|
||||
|
||||
int id = action.getResources().getIdentifier("config", "xml", action.getClass().getPackage().getName());
|
||||
int id = action.getResources().getIdentifier("config", "xml", action.getPackageName());
|
||||
if (id == 0) {
|
||||
id = action.getResources().getIdentifier("cordova", "xml", action.getPackageName());
|
||||
LOG.i("CordovaLog", "config.xml missing, reverting to cordova.xml");
|
||||
@@ -103,7 +102,7 @@ public class Config {
|
||||
}
|
||||
}
|
||||
else if (strNode.equals("preference")) {
|
||||
String name = xml.getAttributeValue(null, "name").toLowerCase(Locale.getDefault());
|
||||
String name = xml.getAttributeValue(null, "name");
|
||||
/* 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.
|
||||
@@ -113,39 +112,39 @@ public class Config {
|
||||
|
||||
Note: We should probably pass in the classname for the variable splash on splashscreen!
|
||||
*/
|
||||
if (name.equalsIgnoreCase("LogLevel")) {
|
||||
if (name.equals("loglevel")) {
|
||||
String level = xml.getAttributeValue(null, "value");
|
||||
LOG.setLogLevel(level);
|
||||
} else if (name.equalsIgnoreCase("SplashScreen")) {
|
||||
} else if (name.equals("splashscreen")) {
|
||||
String value = xml.getAttributeValue(null, "value");
|
||||
int resource = 0;
|
||||
if (value == null)
|
||||
{
|
||||
value = "splash";
|
||||
}
|
||||
resource = action.getResources().getIdentifier(value, "drawable", action.getClass().getPackage().getName());
|
||||
resource = action.getResources().getIdentifier(value, "drawable", action.getPackageName());
|
||||
|
||||
action.getIntent().putExtra(name, resource);
|
||||
}
|
||||
else if(name.equalsIgnoreCase("BackgroundColor")) {
|
||||
else if(name.equals("backgroundColor")) {
|
||||
int value = xml.getAttributeIntValue(null, "value", Color.BLACK);
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equalsIgnoreCase("LoadUrlTimeoutValue")) {
|
||||
else if(name.equals("loadUrlTimeoutValue")) {
|
||||
int value = xml.getAttributeIntValue(null, "value", 20000);
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equalsIgnoreCase("KeepRunning"))
|
||||
else if(name.equals("keepRunning"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equalsIgnoreCase("InAppBrowserStorageEnabled"))
|
||||
else if(name.equals("InAppBrowserStorageEnabled"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equalsIgnoreCase("DisallowOverscroll"))
|
||||
else if(name.equals("disallowOverscroll"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
@@ -204,19 +203,6 @@ public class Config {
|
||||
self._addWhiteListEntry(origin, subdomains);
|
||||
}
|
||||
|
||||
/*
|
||||
* Trying to figure out how to match * is a pain
|
||||
* So, we don't use a regex here
|
||||
*/
|
||||
|
||||
private boolean originHasWildcard(String origin){
|
||||
//First, check for a protocol, then split it if it has one.
|
||||
if(origin.contains("//"))
|
||||
{
|
||||
origin = origin.split("//")[1];
|
||||
}
|
||||
return origin.startsWith("*");
|
||||
}
|
||||
|
||||
private void _addWhiteListEntry(String origin, boolean subdomains) {
|
||||
try {
|
||||
@@ -224,16 +210,8 @@ public class Config {
|
||||
if (origin.compareTo("*") == 0) {
|
||||
LOG.d(TAG, "Unlimited access to network resources");
|
||||
this.whiteList.add(Pattern.compile(".*"));
|
||||
}
|
||||
else { // specific access
|
||||
} else { // specific access
|
||||
// check if subdomains should be included
|
||||
if(originHasWildcard(origin))
|
||||
{
|
||||
subdomains = true;
|
||||
//Remove the wildcard so this works properly
|
||||
origin = origin.replace("*.", "");
|
||||
}
|
||||
|
||||
// TODO: we should not add more domains if * has already been added
|
||||
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
|
||||
Matcher matcher = schemeRegex.matcher(origin);
|
||||
@@ -272,7 +250,6 @@ public class Config {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if URL is in approved list of URLs to load.
|
||||
*
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@@ -65,10 +64,11 @@ import android.widget.LinearLayout;
|
||||
* As an example:
|
||||
*
|
||||
* package org.apache.cordova.examples;
|
||||
* import android.app.Activity;
|
||||
* import android.os.Bundle;
|
||||
* import org.apache.cordova.*;
|
||||
*
|
||||
* public class Example extends CordovaActivity {
|
||||
* public class Examples extends DroidGap {
|
||||
* @Override
|
||||
* public void onCreate(Bundle savedInstanceState) {
|
||||
* super.onCreate(savedInstanceState);
|
||||
@@ -77,6 +77,9 @@ import android.widget.LinearLayout;
|
||||
* super.setStringProperty("loadingDialog", "Title,Message"); // show loading dialog
|
||||
* super.setStringProperty("errorUrl", "file:///android_asset/www/error.html"); // if error loading file in super.loadUrl().
|
||||
*
|
||||
* // Initialize activity
|
||||
* super.init();
|
||||
*
|
||||
* // Clear cache if you want
|
||||
* super.appView.clearCache(true);
|
||||
*
|
||||
@@ -118,7 +121,7 @@ import android.widget.LinearLayout;
|
||||
* Cordova.xml configuration:
|
||||
* Cordova uses a configuration file at res/xml/cordova.xml to specify the following settings.
|
||||
*
|
||||
* Approved list of URLs that can be loaded into Cordova
|
||||
* Approved list of URLs that can be loaded into DroidGap
|
||||
* <access origin="http://server regexp" subdomains="true" />
|
||||
* Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
|
||||
* <log level="DEBUG" />
|
||||
@@ -135,7 +138,7 @@ import android.widget.LinearLayout;
|
||||
* </plugins>
|
||||
*/
|
||||
public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
public static String TAG = "CordovaActivity";
|
||||
public static String TAG = "DroidGap";
|
||||
|
||||
// The webview for our app
|
||||
protected CordovaWebView appView;
|
||||
@@ -251,19 +254,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
}
|
||||
}
|
||||
|
||||
//CB-3949: Workaround for weird Android Launcher Bug!
|
||||
private void checkIntents()
|
||||
{
|
||||
Intent intent = getIntent();
|
||||
String intentAction = intent.getAction();
|
||||
if (!isTaskRoot() && intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null) {
|
||||
if(intentAction.equals(Intent.ACTION_MAIN)) {
|
||||
Log.d("Cordova", "This isn't the root activity. Clearing it and returning to the root activity.");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*
|
||||
@@ -272,9 +262,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
checkIntents();
|
||||
Config.init(this);
|
||||
LOG.d(TAG, "CordovaActivity.onCreate()");
|
||||
LOG.d(TAG, "DroidGap.onCreate()");
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if(savedInstanceState != null)
|
||||
@@ -282,12 +271,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);
|
||||
@@ -347,7 +336,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
|
||||
LOG.d(TAG, "CordovaActivity.init()");
|
||||
LOG.d(TAG, "DroidGap.init()");
|
||||
|
||||
// Set up web container
|
||||
this.appView = webView;
|
||||
@@ -363,7 +352,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);
|
||||
}
|
||||
@@ -392,11 +381,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();
|
||||
@@ -412,10 +401,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) {
|
||||
|
||||
@@ -452,7 +441,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);
|
||||
}
|
||||
@@ -518,7 +507,6 @@ 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);
|
||||
@@ -549,7 +537,6 @@ 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);
|
||||
@@ -574,7 +561,6 @@ 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;
|
||||
@@ -594,7 +580,6 @@ 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);
|
||||
@@ -614,8 +599,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
* @param value
|
||||
*/
|
||||
public void setBooleanProperty(String name, boolean value) {
|
||||
Log.d(TAG, "Setting boolean properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
Log.d(TAG, "Setting boolean properties in DroidGap will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -625,8 +610,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
* @param value
|
||||
*/
|
||||
public void setIntegerProperty(String name, int value) {
|
||||
Log.d(TAG, "Setting integer properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
Log.d(TAG, "Setting integer properties in DroidGap will be deprecated in 3.1 on August 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -636,8 +621,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
* @param value
|
||||
*/
|
||||
public void setStringProperty(String name, String value) {
|
||||
Log.d(TAG, "Setting string properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
Log.d(TAG, "Setting string properties in DroidGap will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -647,8 +632,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
* @param value
|
||||
*/
|
||||
public void setDoubleProperty(String name, double value) {
|
||||
Log.d(TAG, "Setting double properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
Log.d(TAG, "Setting double properties in DroidGap will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -701,7 +686,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) {
|
||||
@@ -731,7 +716,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
LOG.d(TAG, "CordovaActivity.onDestroy()");
|
||||
LOG.d(TAG, "onDestroy()");
|
||||
super.onDestroy();
|
||||
|
||||
// hide the splash screen to avoid leaking a window
|
||||
@@ -858,8 +843,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
LOG.d(TAG, "Incoming Result");
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
Log.d(TAG, "Request code = " + requestCode);
|
||||
if (appView != null && requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
|
||||
ValueCallback<Uri> mUploadMessage = this.appView.getWebChromeClient().getValueCallback();
|
||||
ValueCallback<Uri> mUploadMessage = this.appView.getWebChromeClient().getValueCallback();
|
||||
if (requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
|
||||
Log.d(TAG, "did we get here?");
|
||||
if (null == mUploadMessage)
|
||||
return;
|
||||
@@ -871,13 +856,18 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
mUploadMessage = null;
|
||||
}
|
||||
CordovaPlugin callback = this.activityResultCallback;
|
||||
if(callback == null && initCallbackClass != null) {
|
||||
// The application was restarted, but had defined an initial callback
|
||||
// before being shut down.
|
||||
this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
|
||||
callback = this.activityResultCallback;
|
||||
if(callback == null)
|
||||
{
|
||||
if(initCallbackClass != null)
|
||||
{
|
||||
this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
|
||||
callback = activityResultCallback;
|
||||
LOG.d(TAG, "We have a callback to send this result to");
|
||||
callback.onActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
}
|
||||
if(callback != null) {
|
||||
else
|
||||
{
|
||||
LOG.d(TAG, "We have a callback to send this result to");
|
||||
callback.onActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
@@ -971,7 +961,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
}
|
||||
|
||||
/*
|
||||
* Hook in Cordova for menu plugins
|
||||
* Hook in DroidGap for menu plugins
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
@@ -1010,7 +1000,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
* @param url The url to load.
|
||||
* @param openExternal Load url in browser instead of Cordova webview.
|
||||
* @param clearHistory Clear the history stack, so new page becomes top of history
|
||||
* @param params Parameters for new app
|
||||
* @param params DroidGap parameters for new app
|
||||
*/
|
||||
public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
|
||||
if (this.appView != null) {
|
||||
@@ -1079,7 +1069,9 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event)
|
||||
{
|
||||
if (appView != null && (appView.isCustomViewShowing() || appView.getFocusedChild() != null ) &&
|
||||
//Get whatever has focus!
|
||||
View childView = appView.getFocusedChild();
|
||||
if ((appView.isCustomViewShowing() || childView != null ) &&
|
||||
(keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
return appView.onKeyUp(keyCode, event);
|
||||
} else {
|
||||
@@ -1097,8 +1089,10 @@ 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 (appView != null && appView.getFocusedChild() != null && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
if (childView != null && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
return appView.onKeyDown(keyCode, event);
|
||||
}
|
||||
else
|
||||
@@ -1122,7 +1116,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +222,6 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
result.confirm(r == null ? "" : r);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +233,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("1".equals(message));
|
||||
String r = this.appView.exposedJsApi.retrieveJsMessages();
|
||||
result.confirm(r == null ? "" : r);
|
||||
}
|
||||
|
||||
@@ -275,13 +274,33 @@ 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);
|
||||
quotaUpdater.updateQuota(MAX_QUOTA);
|
||||
LOG.d(TAG, "DroidGap: 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);
|
||||
}
|
||||
}
|
||||
|
||||
// console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html
|
||||
|
||||
@@ -1,362 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.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,7 +23,6 @@ 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;
|
||||
|
||||
@@ -97,8 +96,6 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
private ActivityResult mResult = null;
|
||||
|
||||
private CordovaResourceApi resourceApi;
|
||||
|
||||
class ActivityResult {
|
||||
|
||||
int request;
|
||||
@@ -230,10 +227,9 @@ public class CordovaWebView extends WebView {
|
||||
private void setup() {
|
||||
this.setInitialScale(0);
|
||||
this.setVerticalScrollBarEnabled(false);
|
||||
if (shouldRequestFocusOnInit()) {
|
||||
this.requestFocusFromTouch();
|
||||
}
|
||||
// Enable JavaScript
|
||||
this.requestFocusFromTouch();
|
||||
|
||||
// Enable JavaScript
|
||||
WebSettings settings = this.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
@@ -309,21 +305,10 @@ 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
private void updateUserAgentString() {
|
||||
this.getSettings().getUserAgentString();
|
||||
}
|
||||
|
||||
@@ -428,7 +413,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() {
|
||||
@@ -500,7 +485,7 @@ public class CordovaWebView extends WebView {
|
||||
// If first page, then show splashscreen
|
||||
else {
|
||||
|
||||
LOG.d(TAG, "loadUrlIntoView(%s, %d)", url, time);
|
||||
LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time);
|
||||
|
||||
// Send message to show splashscreen now if desired
|
||||
this.postMessage("splashscreen", "show");
|
||||
@@ -570,7 +555,7 @@ public class CordovaWebView extends WebView {
|
||||
* @param url The url to load.
|
||||
* @param openExternal Load url in browser instead of Cordova webview.
|
||||
* @param clearHistory Clear the history stack, so new page becomes top of history
|
||||
* @param params Parameters for new app
|
||||
* @param params DroidGap parameters for new app
|
||||
*/
|
||||
public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
|
||||
LOG.d(TAG, "showWebPage(%s, %b, %b, HashMap", url, openExternal, clearHistory);
|
||||
@@ -616,14 +601,14 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Check configuration parameters from Config.
|
||||
* Approved list of URLs that can be loaded into Cordova
|
||||
* Approved list of URLs that can be loaded into DroidGap
|
||||
* <access origin="http://server regexp" subdomains="true" />
|
||||
* Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
|
||||
* <log level="DEBUG" />
|
||||
*/
|
||||
private void loadConfiguration() {
|
||||
|
||||
if ("true".equals(this.getProperty("Fullscreen", "false"))) {
|
||||
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);
|
||||
}
|
||||
@@ -641,7 +626,6 @@ 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;
|
||||
@@ -821,7 +805,8 @@ public class CordovaWebView extends WebView {
|
||||
public void handleDestroy()
|
||||
{
|
||||
// Send destroy event to JavaScript
|
||||
this.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
|
||||
// Since baseUrl is set in loadUrlIntoView, if user hit Back button before loadUrl was called, we'll get an NPE on baseUrl (CB-2458)
|
||||
this.loadUrlIntoView("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
|
||||
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.loadUrl("about:blank");
|
||||
@@ -959,8 +944,4 @@ public class CordovaWebView extends WebView {
|
||||
public void storeResult(int requestCode, int resultCode, Intent intent) {
|
||||
mResult = new ActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
|
||||
public CordovaResourceApi getResourceApi() {
|
||||
return resourceApi;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ import android.webkit.WebViewClient;
|
||||
*/
|
||||
public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
private static final String TAG = "CordovaWebViewClient";
|
||||
private static final String TAG = "Cordova";
|
||||
private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/";
|
||||
CordovaInterface cordova;
|
||||
CordovaWebView appView;
|
||||
@@ -188,17 +188,6 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
LOG.e(TAG, "Error sending sms " + url + ":" + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
//Android Market
|
||||
else if(url.startsWith("market:")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error loading Google Play Store: " + url, e);
|
||||
}
|
||||
}
|
||||
|
||||
// All else
|
||||
else {
|
||||
@@ -222,7 +211,35 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for intercepting any requests for resources.
|
||||
* This includes images and scripts and so on, not just top-level pages.
|
||||
* @param view The WebView.
|
||||
* @param url The URL to be loaded.
|
||||
* @return Either null to proceed as normal, or a WebResourceResponse.
|
||||
*/
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||
//If something isn't whitelisted, just send a blank response
|
||||
if(!Config.isUrlWhiteListed(url) && (url.startsWith("http://") || url.startsWith("https://")))
|
||||
{
|
||||
return getWhitelistResponse();
|
||||
}
|
||||
if (this.appView.pluginManager != null) {
|
||||
return this.appView.pluginManager.shouldInterceptRequest(url);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private WebResourceResponse getWhitelistResponse()
|
||||
{
|
||||
WebResourceResponse emptyResponse;
|
||||
String empty = "";
|
||||
ByteArrayInputStream data = new ByteArrayInputStream(empty.getBytes());
|
||||
return new WebResourceResponse("text/plain", "UTF-8", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* On received http auth request.
|
||||
* The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination
|
||||
|
||||
@@ -38,7 +38,7 @@ import android.telephony.TelephonyManager;
|
||||
public class Device extends CordovaPlugin {
|
||||
public static final String TAG = "Device";
|
||||
|
||||
public static String cordovaVersion = "2.9.1-dev"; // Cordova version
|
||||
public static String cordovaVersion = "2.8.1"; // Cordova version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
@@ -101,7 +101,7 @@ public class Device extends CordovaPlugin {
|
||||
/**
|
||||
* Listen for telephony events: RINGING, OFFHOOK and IDLE
|
||||
* Send these events to all plugins using
|
||||
* CordovaActivity.onMessage("telephone", "ringing" | "offhook" | "idle")
|
||||
* DroidGap.onMessage("telephone", "ringing" | "offhook" | "idle")
|
||||
*/
|
||||
private void initTelephonyReceiver() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
|
||||
@@ -48,18 +48,12 @@ 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);
|
||||
boolean wasSync = pluginManager.exec(service, action, callbackId, arguments);
|
||||
String ret = "";
|
||||
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) {
|
||||
ret = jsMessageQueue.popAndEncode(false);
|
||||
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING || wasSync) {
|
||||
ret = jsMessageQueue.popAndEncode();
|
||||
}
|
||||
return ret;
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
} finally {
|
||||
jsMessageQueue.setPaused(false);
|
||||
}
|
||||
@@ -71,7 +65,7 @@ import org.json.JSONException;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String retrieveJsMessages(boolean fromOnlineEvent) {
|
||||
return jsMessageQueue.popAndEncode(fromOnlineEvent);
|
||||
public String retrieveJsMessages() {
|
||||
return jsMessageQueue.popAndEncode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,9 @@ import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.LOG;
|
||||
|
||||
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.nio.charset.Charset;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Locale;
|
||||
|
||||
public class FileHelper {
|
||||
@@ -96,18 +93,10 @@ public class FileHelper {
|
||||
if (uriString.startsWith("content")) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
return cordova.getActivity().getContentResolver().openInputStream(uri);
|
||||
} else if (uriString.startsWith("file://")) {
|
||||
int question = uriString.indexOf("?");
|
||||
if (question > -1) {
|
||||
uriString = uriString.substring(0,question);
|
||||
}
|
||||
if (uriString.startsWith("file:///android_asset/")) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
String relativePath = uri.getPath().substring(15);
|
||||
return cordova.getActivity().getAssets().open(relativePath);
|
||||
} else {
|
||||
return new FileInputStream(getRealPath(uriString, cordova));
|
||||
}
|
||||
} else if (uriString.startsWith("file:///android_asset/")) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
String relativePath = uri.getPath().substring(15);
|
||||
return cordova.getActivity().getAssets().open(relativePath);
|
||||
} else {
|
||||
return new FileInputStream(getRealPath(uriString, cordova));
|
||||
}
|
||||
@@ -127,20 +116,6 @@ public class FileHelper {
|
||||
return uriString;
|
||||
}
|
||||
|
||||
public static String getMimeTypeForExtension(String path) {
|
||||
String extension = path;
|
||||
int lastDot = extension.lastIndexOf('.');
|
||||
if (lastDot != -1) {
|
||||
extension = extension.substring(lastDot + 1);
|
||||
}
|
||||
// Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185).
|
||||
extension = extension.toLowerCase(Locale.getDefault());
|
||||
if (extension.equals("3ga")) {
|
||||
return "audio/3gpp";
|
||||
}
|
||||
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime type of the data specified by the given URI string.
|
||||
*
|
||||
@@ -154,7 +129,19 @@ public class FileHelper {
|
||||
if (uriString.startsWith("content://")) {
|
||||
mimeType = cordova.getActivity().getContentResolver().getType(uri);
|
||||
} else {
|
||||
mimeType = getMimeTypeForExtension(uri.getPath());
|
||||
// MimeTypeMap.getFileExtensionFromUrl() fails when there are query parameters.
|
||||
String extension = uri.getPath();
|
||||
int lastDot = extension.lastIndexOf('.');
|
||||
if (lastDot != -1) {
|
||||
extension = extension.substring(lastDot + 1);
|
||||
}
|
||||
// Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185).
|
||||
extension = extension.toLowerCase();
|
||||
if (extension.equals("3ga")) {
|
||||
mimeType = "audio/3gpp";
|
||||
} else {
|
||||
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
}
|
||||
}
|
||||
|
||||
return mimeType;
|
||||
|
||||
@@ -22,14 +22,19 @@ 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;
|
||||
@@ -47,11 +52,9 @@ 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;
|
||||
@@ -61,6 +64,8 @@ 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";
|
||||
@@ -76,6 +81,8 @@ 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;
|
||||
@@ -104,20 +111,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
|
||||
@@ -125,30 +132,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 SimpleTrackingInputStream extends TrackingInputStream {
|
||||
private static class TrackingHTTPInputStream extends TrackingInputStream {
|
||||
private long bytesRead = 0;
|
||||
public SimpleTrackingInputStream(InputStream stream) {
|
||||
public TrackingHTTPInputStream(InputStream stream) {
|
||||
super(stream);
|
||||
}
|
||||
|
||||
private int updateBytesRead(int newBytesRead) {
|
||||
if (newBytesRead != -1) {
|
||||
bytesRead += newBytesRead;
|
||||
}
|
||||
return newBytesRead;
|
||||
if (newBytesRead != -1) {
|
||||
bytesRead += newBytesRead;
|
||||
}
|
||||
return newBytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -167,7 +174,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
}
|
||||
|
||||
public long getTotalRawBytesRead() {
|
||||
return bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,8 +251,6 @@ 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);
|
||||
@@ -257,20 +262,16 @@ public class FileTransfer extends CordovaPlugin {
|
||||
Log.d(LOG_TAG, "objectId: " + objectId);
|
||||
Log.d(LOG_TAG, "httpMethod: " + httpMethod);
|
||||
|
||||
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) {
|
||||
final URL url;
|
||||
try {
|
||||
url = new URL(target);
|
||||
} catch (MalformedURLException e) {
|
||||
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
|
||||
Log.e(LOG_TAG, "Unsupported URI: " + targetUri);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
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) {
|
||||
@@ -294,15 +295,27 @@ public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
//------------------ CLIENT REQUEST
|
||||
// Open a HTTP connection to the URL based on protocol
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
// Allow Inputs
|
||||
@@ -359,11 +372,11 @@ public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
|
||||
// Get a input stream of the file on the phone
|
||||
OpenForReadResult readResult = resourceApi.openForRead(sourceUri);
|
||||
InputStream sourceInputStream = getPathFromUri(source);
|
||||
|
||||
int stringLength = beforeDataBytes.length + tailParamsBytes.length;
|
||||
if (readResult.length >= 0) {
|
||||
fixedLength = (int)readResult.length + stringLength;
|
||||
if (sourceInputStream instanceof FileInputStream) {
|
||||
fixedLength = (int) ((FileInputStream)sourceInputStream).getChannel().size() + stringLength;
|
||||
progress.setLengthComputable(true);
|
||||
progress.setTotal(fixedLength);
|
||||
}
|
||||
@@ -399,12 +412,12 @@ public class FileTransfer extends CordovaPlugin {
|
||||
totalBytes += beforeDataBytes.length;
|
||||
|
||||
// create a buffer of maximum size
|
||||
int bytesAvailable = readResult.inputStream.available();
|
||||
int bytesAvailable = sourceInputStream.available();
|
||||
int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
|
||||
// read file and write it into form...
|
||||
int bytesRead = readResult.inputStream.read(buffer, 0, bufferSize);
|
||||
int bytesRead = sourceInputStream.read(buffer, 0, bufferSize);
|
||||
|
||||
long prevBytesRead = 0;
|
||||
while (bytesRead > 0) {
|
||||
@@ -415,9 +428,9 @@ public class FileTransfer extends CordovaPlugin {
|
||||
prevBytesRead = totalBytes;
|
||||
Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes");
|
||||
}
|
||||
bytesAvailable = readResult.inputStream.available();
|
||||
bytesAvailable = sourceInputStream.available();
|
||||
bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
|
||||
bytesRead = readResult.inputStream.read(buffer, 0, bufferSize);
|
||||
bytesRead = sourceInputStream.read(buffer, 0, bufferSize);
|
||||
|
||||
// Send a progress event.
|
||||
progress.setLoaded(totalBytes);
|
||||
@@ -431,7 +444,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
totalBytes += tailParamsBytes.length;
|
||||
sendStream.flush();
|
||||
} finally {
|
||||
safeClose(readResult.inputStream);
|
||||
safeClose(sourceInputStream);
|
||||
safeClose(sendStream);
|
||||
}
|
||||
context.currentOutputStream = null;
|
||||
@@ -521,9 +534,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 SimpleTrackingInputStream(conn.getInputStream());
|
||||
return new TrackingHTTPInputStream(conn.getInputStream());
|
||||
}
|
||||
|
||||
// always verify the host - don't check for certificate
|
||||
@@ -594,8 +607,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
body = bodyBuilder.toString();
|
||||
}
|
||||
}
|
||||
// IOException can leave connection object in a bad state, so catch all exceptions.
|
||||
} catch (Throwable e) {
|
||||
} catch (IOException e) {
|
||||
Log.w(LOG_TAG, "Error getting HTTP status code from connection.", e);
|
||||
}
|
||||
}
|
||||
@@ -605,7 +617,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) {
|
||||
@@ -630,8 +642,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
|
||||
*/
|
||||
@@ -650,35 +662,27 @@ 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)));
|
||||
|
||||
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) {
|
||||
final URL url;
|
||||
try {
|
||||
url = new URL(source);
|
||||
} catch (MalformedURLException e) {
|
||||
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
|
||||
Log.e(LOG_TAG, "Unsupported URI: " + targetUri);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: refactor to also allow resources & content:
|
||||
if (!isLocalTransfer && !Config.isUrlWhiteListed(source)) {
|
||||
final boolean useHttps = url.getProtocol().equals("https");
|
||||
|
||||
if (!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));
|
||||
@@ -696,75 +700,81 @@ public class FileTransfer extends CordovaPlugin {
|
||||
if (context.aborted) {
|
||||
return;
|
||||
}
|
||||
HttpURLConnection connection = null;
|
||||
URLConnection connection = null;
|
||||
HostnameVerifier oldHostnameVerifier = null;
|
||||
SSLSocketFactory oldSocketFactory = null;
|
||||
File file = null;
|
||||
PluginResult result = null;
|
||||
TrackingInputStream inputStream = null;
|
||||
|
||||
OutputStream outputStream = null;
|
||||
try {
|
||||
OpenForReadResult readResult = null;
|
||||
outputStream = resourceApi.openOutputStream(targetUri);
|
||||
|
||||
file = resourceApi.mapUriToFile(targetUri);
|
||||
file = getFileFromPath(target);
|
||||
context.targetFile = file;
|
||||
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
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) {
|
||||
// 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)connection;
|
||||
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);
|
||||
connection = https;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
// Return a standard HTTP connection
|
||||
else {
|
||||
connection = httpClient.open(url);
|
||||
|
||||
}
|
||||
|
||||
// Handle the other headers
|
||||
if (headers != null) {
|
||||
addHeadersToRequest(connection, headers);
|
||||
}
|
||||
|
||||
connection.connect();
|
||||
if (connection instanceof HttpURLConnection) {
|
||||
((HttpURLConnection)connection).setRequestMethod("GET");
|
||||
}
|
||||
|
||||
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);
|
||||
//Add cookie support
|
||||
String cookie = CookieManager.getInstance().getCookie(source);
|
||||
if(cookie != null)
|
||||
{
|
||||
connection.setRequestProperty("cookie", cookie);
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -811,7 +821,6 @@ 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);
|
||||
}
|
||||
@@ -838,6 +847,54 @@ 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 {
|
||||
if (path.startsWith("content:")) {
|
||||
Uri uri = Uri.parse(path);
|
||||
return cordova.getActivity().getContentResolver().openInputStream(uri);
|
||||
}
|
||||
else if (path.startsWith("file://")) {
|
||||
int question = path.indexOf("?");
|
||||
if (question == -1) {
|
||||
return new FileInputStream(path.substring(7));
|
||||
} else {
|
||||
return new FileInputStream(path.substring(7, question));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return new FileInputStream(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -22,15 +22,17 @@ import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.apache.cordova.file.*;
|
||||
|
||||
|
||||
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.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -132,7 +134,7 @@ public class FileUtils extends CordovaPlugin {
|
||||
this.readFileAs(args.getString(0), start, end, callbackContext, null, PluginResult.MESSAGE_TYPE_BINARYSTRING);
|
||||
}
|
||||
else if (action.equals("write")) {
|
||||
long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2), args.getBoolean(3));
|
||||
long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2));
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize));
|
||||
}
|
||||
else if (action.equals("truncate")) {
|
||||
@@ -487,13 +489,12 @@ 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, destination);
|
||||
copyDirectory(file, destinationDir);
|
||||
} else {
|
||||
File destination = new File(destinationDir.getAbsoluteFile() + File.separator + file.getName());
|
||||
copyFile(file, destination);
|
||||
}
|
||||
}
|
||||
@@ -913,6 +914,11 @@ 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.
|
||||
@@ -944,7 +950,7 @@ public class FileUtils extends CordovaPlugin {
|
||||
break;
|
||||
default: // Base64.
|
||||
String contentType = FileHelper.getMimeType(filename, cordova);
|
||||
byte[] base64 = Base64.encode(bytes, Base64.NO_WRAP);
|
||||
byte[] base64 = Base64.encodeBase64(bytes);
|
||||
String s = "data:" + contentType + ";base64," + new String(base64, "US-ASCII");
|
||||
result = new PluginResult(PluginResult.Status.OK, s);
|
||||
}
|
||||
@@ -993,12 +999,11 @@ public class FileUtils extends CordovaPlugin {
|
||||
* @param filename The name of the file.
|
||||
* @param data The contents of the file.
|
||||
* @param offset The position to begin writing the file.
|
||||
* @param isBinary True if the file contents are base64-encoded binary data
|
||||
* @throws FileNotFoundException, IOException
|
||||
* @throws NoModificationAllowedException
|
||||
*/
|
||||
/**/
|
||||
public long write(String filename, String data, int offset, boolean isBinary) throws FileNotFoundException, IOException, NoModificationAllowedException {
|
||||
public long write(String filename, String data, int offset) throws FileNotFoundException, IOException, NoModificationAllowedException {
|
||||
if (filename.startsWith("content://")) {
|
||||
throw new NoModificationAllowedException("Couldn't write to file given its content URI");
|
||||
}
|
||||
@@ -1011,28 +1016,14 @@ public class FileUtils extends CordovaPlugin {
|
||||
append = true;
|
||||
}
|
||||
|
||||
byte[] rawData;
|
||||
if (isBinary) {
|
||||
rawData = Base64.decode(data, Base64.DEFAULT);
|
||||
} else {
|
||||
rawData = data.getBytes();
|
||||
}
|
||||
byte[] rawData = data.getBytes();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(rawData);
|
||||
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;
|
||||
}
|
||||
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();
|
||||
|
||||
return rawData.length;
|
||||
}
|
||||
|
||||
@@ -55,18 +55,14 @@ 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 (locationManager == null) {
|
||||
locationManager = (LocationManager) this.cordova.getActivity().getSystemService(Context.LOCATION_SERVICE);
|
||||
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.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);
|
||||
|
||||
@@ -260,13 +260,12 @@ public class Globalization extends CordovaPlugin {
|
||||
String fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern(); //default SHORT date/time format. ex. dd/MM/yyyy h:mm a
|
||||
|
||||
//get Date value + options (if available)
|
||||
boolean test = options.getJSONObject(0).has(OPTIONS);
|
||||
if (options.getJSONObject(0).has(OPTIONS)){
|
||||
if (options.getJSONObject(0).length() > 1){
|
||||
//options were included
|
||||
JSONObject innerOptions = options.getJSONObject(0).getJSONObject(OPTIONS);
|
||||
|
||||
//get formatLength option
|
||||
if (!innerOptions.isNull(FORMATLENGTH)){
|
||||
String fmtOpt = innerOptions.getString(FORMATLENGTH);
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(FORMATLENGTH)){
|
||||
String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(FORMATLENGTH);
|
||||
if (fmtOpt.equalsIgnoreCase(MEDIUM)){//medium
|
||||
fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getMediumDateFormat(this.cordova.getActivity());
|
||||
}else if (fmtOpt.equalsIgnoreCase(LONG) || fmtOpt.equalsIgnoreCase(FULL)){ //long/full
|
||||
@@ -276,8 +275,8 @@ public class Globalization extends CordovaPlugin {
|
||||
|
||||
//return pattern type
|
||||
fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern();
|
||||
if (!innerOptions.isNull(SELECTOR)){
|
||||
String selOpt = innerOptions.getString(SELECTOR);
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(SELECTOR)){
|
||||
String selOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(SELECTOR);
|
||||
if (selOpt.equalsIgnoreCase(DATE)){
|
||||
fmt = fmtDate.toLocalizedPattern();
|
||||
}else if (selOpt.equalsIgnoreCase(TIME)){
|
||||
|
||||
@@ -18,15 +18,13 @@
|
||||
*/
|
||||
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;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
@@ -34,7 +32,6 @@ 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);
|
||||
@@ -46,43 +43,31 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||
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);
|
||||
//Check if plugins intercept the request
|
||||
WebResourceResponse ret = super.shouldInterceptRequest(view, url);
|
||||
if(ret == null && (url.contains("?") || url.contains("#") || needsIceCreamSpaceInAssetUrlFix(url))){
|
||||
ret = generateWebResourceResponse(url);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static boolean needsSpecialsInAssetUrlFix(Uri uri) {
|
||||
if (CordovaResourceApi.getUriType(uri) != CordovaResourceApi.URI_TYPE_ASSET) {
|
||||
return false;
|
||||
private WebResourceResponse generateWebResourceResponse(String url) {
|
||||
if (url.startsWith("file:///android_asset/")) {
|
||||
String mimetype = FileHelper.getMimeType(url, cordova);
|
||||
|
||||
try {
|
||||
InputStream stream = FileHelper.getInputStreamFromUriString(url, cordova);
|
||||
WebResourceResponse response = new WebResourceResponse(mimetype, "UTF-8", stream);
|
||||
return response;
|
||||
} catch (IOException e) {
|
||||
LOG.e("generateWebResourceResponse", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (uri.getQuery() != null || uri.getFragment() != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!uri.toString().contains("%")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean needsIceCreamSpaceInAssetUrlFix(String url) {
|
||||
if (!url.contains("%20")){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -90,7 +75,9 @@ 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,12 +18,24 @@
|
||||
*/
|
||||
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;
|
||||
@@ -37,8 +49,11 @@ import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.Button;
|
||||
@@ -46,19 +61,6 @@ 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 {
|
||||
|
||||
@@ -67,26 +69,21 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
private static final String SELF = "_self";
|
||||
private static final String SYSTEM = "_system";
|
||||
// private static final String BLANK = "_blank";
|
||||
private static final String EXIT_EVENT = "exit";
|
||||
private static final String LOCATION = "location";
|
||||
private static final String HIDDEN = "hidden";
|
||||
private static final String EXIT_EVENT = "exit";
|
||||
private static final String LOAD_START_EVENT = "loadstart";
|
||||
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 static final String CLEAR_ALL_CACHE = "clearcache";
|
||||
private static final String CLEAR_SESSION_CACHE = "clearsessioncache";
|
||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||
|
||||
private Dialog dialog;
|
||||
private WebView inAppWebView;
|
||||
private EditText edittext;
|
||||
private CallbackContext callbackContext;
|
||||
private boolean showLocationBar = true;
|
||||
private boolean openWindowHidden = false;
|
||||
private CallbackContext callbackContext;
|
||||
private String buttonLabel = "Done";
|
||||
private boolean clearAllCache= false;
|
||||
private boolean clearSessionCache=false;
|
||||
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
@@ -95,134 +92,108 @@ 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, 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);
|
||||
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());
|
||||
}
|
||||
}
|
||||
// SYSTEM
|
||||
else if (SYSTEM.equals(target)) {
|
||||
Log.d(LOG_TAG, "in system");
|
||||
result = openExternal(url);
|
||||
}
|
||||
// BLANK - or anything else
|
||||
// load in InAppBrowser
|
||||
else {
|
||||
Log.d(LOG_TAG, "in blank");
|
||||
result = showWebPage(url, features);
|
||||
result = this.showWebPage(url, features);
|
||||
}
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
|
||||
pluginResult.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
});
|
||||
}
|
||||
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();
|
||||
// SYSTEM
|
||||
else if (SYSTEM.equals(target)) {
|
||||
Log.d(LOG_TAG, "in system");
|
||||
result = this.openExternal(url);
|
||||
}
|
||||
});
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
|
||||
pluginResult.setKeepCallback(true);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
// 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 {
|
||||
return false;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
|
||||
}
|
||||
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.
|
||||
*
|
||||
@@ -250,14 +221,8 @@ 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.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
inAppWebView.loadUrl("javascript:" + finalScriptToInject);
|
||||
}
|
||||
});
|
||||
this.inAppWebView.loadUrl("javascript:" + scriptToInject);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,6 +254,20 @@ 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.
|
||||
*
|
||||
@@ -312,30 +291,20 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
/**
|
||||
* Closes the dialog
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
private void closeDialog() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,25 +361,11 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
public String showWebPage(final String url, HashMap<String, Boolean> features) {
|
||||
// Determine if we should hide the location bar.
|
||||
showLocationBar = true;
|
||||
openWindowHidden = false;
|
||||
if (features != null) {
|
||||
Boolean show = features.get(LOCATION);
|
||||
if (show != null) {
|
||||
showLocationBar = show.booleanValue();
|
||||
}
|
||||
Boolean hidden = features.get(HIDDEN);
|
||||
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;
|
||||
@@ -439,7 +394,14 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
dialog.setCancelable(true);
|
||||
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
closeDialog();
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("type", EXIT_EVENT);
|
||||
|
||||
sendUpdate(obj, false);
|
||||
} catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Should never happen");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -449,8 +411,6 @@ 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);
|
||||
@@ -538,24 +498,24 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
settings.setBuiltInZoomControls(true);
|
||||
settings.setPluginState(android.webkit.WebSettings.PluginState.ON);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
//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);
|
||||
@@ -589,11 +549,6 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
dialog.setContentView(main);
|
||||
dialog.show();
|
||||
dialog.getWindow().setAttributes(lp);
|
||||
// the goal of openhidden is to load the url and not display it
|
||||
// Show() needs to be called to cause the URL to be loaded
|
||||
if(openWindowHidden) {
|
||||
dialog.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
this.cordova.getActivity().runOnUiThread(runnable);
|
||||
@@ -614,19 +569,114 @@ 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) {
|
||||
if (callbackContext != null) {
|
||||
PluginResult result = new PluginResult(status, obj);
|
||||
result.setKeepCallback(keepCallback);
|
||||
callbackContext.sendPluginResult(result);
|
||||
if (!keepCallback) {
|
||||
callbackContext = null;
|
||||
}
|
||||
}
|
||||
*/ private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) {
|
||||
PluginResult result = new PluginResult(status, obj);
|
||||
result.setKeepCallback(keepCallback);
|
||||
this.callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
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,9 +138,8 @@ 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(boolean fromOnlineEvent) {
|
||||
public String popAndEncode() {
|
||||
synchronized (this) {
|
||||
registeredListeners[activeListenerIndex].notifyOfFlush(fromOnlineEvent);
|
||||
if (queue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@@ -275,13 +274,12 @@ public class NativeToJsMessageQueue {
|
||||
return paused;
|
||||
}
|
||||
|
||||
private abstract class BridgeMode {
|
||||
abstract void onNativeToJsMessageAvailable();
|
||||
void notifyOfFlush(boolean fromOnlineEvent) {}
|
||||
private interface BridgeMode {
|
||||
void onNativeToJsMessageAvailable();
|
||||
}
|
||||
|
||||
/** Uses webView.loadUrl("javascript:") to execute messages. */
|
||||
private class LoadUrlBridgeMode extends BridgeMode {
|
||||
private class LoadUrlBridgeMode implements BridgeMode {
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
String js = popAndEncodeAsJs();
|
||||
@@ -291,17 +289,18 @@ public class NativeToJsMessageQueue {
|
||||
}
|
||||
};
|
||||
|
||||
@Override void onNativeToJsMessageAvailable() {
|
||||
public void onNativeToJsMessageAvailable() {
|
||||
cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
/** Uses online/offline events to tell the JS when to poll for messages. */
|
||||
private class OnlineEventsBridgeMode extends BridgeMode {
|
||||
boolean online = false;
|
||||
private class OnlineEventsBridgeMode implements BridgeMode {
|
||||
boolean online = true;
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
if (!queue.isEmpty()) {
|
||||
online = !online;
|
||||
webView.setNetworkAvailable(online);
|
||||
}
|
||||
}
|
||||
@@ -309,22 +308,16 @@ public class NativeToJsMessageQueue {
|
||||
OnlineEventsBridgeMode() {
|
||||
webView.setNetworkAvailable(true);
|
||||
}
|
||||
@Override void onNativeToJsMessageAvailable() {
|
||||
public 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 extends BridgeMode {
|
||||
private class PrivateApiBridgeMode implements BridgeMode {
|
||||
// Message added in commit:
|
||||
// http://omapzoom.org/?p=platform/frameworks/base.git;a=commitdiff;h=9497c5f8c4bc7c47789e5ccde01179abc31ffeb2
|
||||
// Which first appeared in 3.2.4ish.
|
||||
@@ -362,7 +355,7 @@ public class NativeToJsMessageQueue {
|
||||
}
|
||||
}
|
||||
|
||||
@Override void onNativeToJsMessageAvailable() {
|
||||
public void onNativeToJsMessageAvailable() {
|
||||
if (sendMessageMethod == null && !initFailed) {
|
||||
initReflection();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ 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,12 +21,10 @@ package org.apache.cordova.api;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
* The Activity interface that is implemented by CordovaActivity.
|
||||
* The Cordova activity abstract class that is extended by DroidGap.
|
||||
* It is used to isolate plugin development, and remove dependency on entire Cordova library.
|
||||
*/
|
||||
public interface CordovaInterface {
|
||||
|
||||
@@ -20,13 +20,14 @@ package org.apache.cordova.api;
|
||||
|
||||
import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebResourceResponse;
|
||||
|
||||
/**
|
||||
* Plugins must extend this class and override one of the execute methods.
|
||||
@@ -164,12 +165,16 @@ public class CordovaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for redirecting requests. Applies to WebView requests as well as requests made by plugins.
|
||||
* By specifying a <url-filter> in config.xml you can map a URL prefix to this method. It applies to all resources loaded in the WebView, not just top-level navigation.
|
||||
*
|
||||
* @param url The URL of the resource to be loaded.
|
||||
* @return Return a WebResourceResponse for the resource, or null to let the WebView handle it normally.
|
||||
*/
|
||||
public Uri remapUri(Uri uri) {
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public WebResourceResponse shouldInterceptRequest(String url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the WebView does a top-level navigation or refreshes.
|
||||
*
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
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;
|
||||
@@ -65,19 +63,6 @@ public class PluginEntry {
|
||||
this.onload = onload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternate constructor
|
||||
*
|
||||
* @param service The name of the service
|
||||
* @param plugin The plugin associated with this entry
|
||||
*/
|
||||
public PluginEntry(String service, CordovaPlugin plugin) {
|
||||
this.service = service;
|
||||
this.plugin = plugin;
|
||||
this.pluginClass = plugin.getClass().getName();
|
||||
this.onload = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create plugin object.
|
||||
* If plugin is already created, then just return it.
|
||||
|
||||
@@ -22,23 +22,16 @@ import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
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;
|
||||
|
||||
import android.content.Intent;
|
||||
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.
|
||||
@@ -62,8 +55,6 @@ public class PluginManager {
|
||||
// This would allow how all URLs are handled to be offloaded to a plugin
|
||||
protected HashMap<String, String> urlMap = new HashMap<String, String>();
|
||||
|
||||
private AtomicInteger numPendingUiExecs;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -74,7 +65,6 @@ public class PluginManager {
|
||||
this.ctx = ctx;
|
||||
this.app = app;
|
||||
this.firstRun = true;
|
||||
this.numPendingUiExecs = new AtomicInteger(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +73,7 @@ public class PluginManager {
|
||||
public void init() {
|
||||
LOG.d(TAG, "init()");
|
||||
|
||||
// If first time, then load plugins from config.xml file
|
||||
// If first time, then load plugins from plugins.xml file
|
||||
if (this.firstRun) {
|
||||
this.loadPlugins();
|
||||
this.firstRun = false;
|
||||
@@ -96,18 +86,20 @@ public class PluginManager {
|
||||
this.clearPluginObjects();
|
||||
}
|
||||
|
||||
// Insert PluginManager service
|
||||
this.addService(new PluginEntry("PluginManager", new PluginManagerService()));
|
||||
|
||||
// Start up all plugins that have onload specified
|
||||
this.startupPlugins();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugins from res/xml/config.xml
|
||||
* Load plugins from res/xml/plugins.xml
|
||||
*/
|
||||
public void loadPlugins() {
|
||||
int id = this.ctx.getActivity().getResources().getIdentifier("config", "xml", this.ctx.getActivity().getClass().getPackage().getName());
|
||||
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");
|
||||
}
|
||||
if (id == 0) {
|
||||
this.pluginConfigurationMissing();
|
||||
//We have the error, we need to exit without crashing!
|
||||
@@ -208,28 +200,15 @@ public class PluginManager {
|
||||
* this is an async plugin call.
|
||||
* @param rawArgs An Array literal string containing any arguments needed in the
|
||||
* plugin execute method.
|
||||
* @return Whether the task completed synchronously.
|
||||
*/
|
||||
public void exec(final String service, final String action, final String callbackId, final String rawArgs) {
|
||||
if (numPendingUiExecs.get() > 0) {
|
||||
numPendingUiExecs.getAndIncrement();
|
||||
this.ctx.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
execHelper(service, action, callbackId, rawArgs);
|
||||
numPendingUiExecs.getAndDecrement();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
execHelper(service, action, callbackId, rawArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private void execHelper(final String service, final String action, final String callbackId, final String rawArgs) {
|
||||
CordovaPlugin plugin = getPlugin(service);
|
||||
public boolean exec(String service, String action, String callbackId, String rawArgs) {
|
||||
CordovaPlugin plugin = this.getPlugin(service);
|
||||
if (plugin == null) {
|
||||
Log.d(TAG, "exec() call to unknown plugin: " + service);
|
||||
PluginResult cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION);
|
||||
app.sendPluginResult(cr, callbackId);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
CallbackContext callbackContext = new CallbackContext(callbackId, app);
|
||||
@@ -237,16 +216,19 @@ public class PluginManager {
|
||||
if (!wasValidAction) {
|
||||
PluginResult cr = new PluginResult(PluginResult.Status.INVALID_ACTION);
|
||||
app.sendPluginResult(cr, callbackId);
|
||||
return true;
|
||||
}
|
||||
return callbackContext.isFinished();
|
||||
} catch (JSONException e) {
|
||||
PluginResult cr = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
app.sendPluginResult(cr, callbackId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void exec(String service, String action, String callbackId, String jsonArgs, boolean async) {
|
||||
exec(service, action, callbackId, jsonArgs);
|
||||
public boolean exec(String service, String action, String callbackId, String jsonArgs, boolean async) {
|
||||
return exec(service, action, callbackId, jsonArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,6 +361,25 @@ public class PluginManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the WebView is loading any resource, top-level or not.
|
||||
*
|
||||
* Uses the same url-filter tag as onOverrideUrlLoading.
|
||||
*
|
||||
* @param url The URL of the resource to be loaded.
|
||||
* @return Return a WebResourceResponse with the resource, or null if the WebView should handle it.
|
||||
*/
|
||||
public WebResourceResponse shouldInterceptRequest(String url) {
|
||||
Iterator<Entry<String, String>> it = this.urlMap.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
HashMap.Entry<String, String> pairs = it.next();
|
||||
if (url.startsWith(pairs.getKey())) {
|
||||
return this.getPlugin(pairs.getValue()).shouldInterceptRequest(url);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the app navigates or refreshes.
|
||||
*/
|
||||
@@ -395,42 +396,8 @@ public class PluginManager {
|
||||
|
||||
private void pluginConfigurationMissing() {
|
||||
LOG.e(TAG, "=====================================================================================");
|
||||
LOG.e(TAG, "ERROR: config.xml is missing. Add res/xml/config.xml to your project.");
|
||||
LOG.e(TAG, "ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");
|
||||
LOG.e(TAG, "https://git-wip-us.apache.org/repos/asf?p=incubator-cordova-android.git;a=blob;f=framework/res/xml/plugins.xml");
|
||||
LOG.e(TAG, "=====================================================================================");
|
||||
}
|
||||
|
||||
public Uri remapUri(Uri uri) {
|
||||
for (PluginEntry entry : this.entries.values()) {
|
||||
if (entry.plugin != null) {
|
||||
Uri ret = entry.plugin.remapUri(uri);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private class PluginManagerService extends CordovaPlugin {
|
||||
@Override
|
||||
public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
|
||||
if ("startup".equals(action)) {
|
||||
// The onPageStarted event of CordovaWebViewClient resets the queue of messages to be returned to javascript in response
|
||||
// to exec calls. Since this event occurs on the UI thread and exec calls happen on the WebCore thread it is possible
|
||||
// that onPageStarted occurs after exec calls have started happening on a new page, which can cause the message queue
|
||||
// to be reset between the queuing of a new message and its retrieval by javascript. To avoid this from happening,
|
||||
// javascript always sends a "startup" exec upon loading a new page which causes all future exec calls to happen on the UI
|
||||
// thread (and hence after onPageStarted) until there are no more pending exec calls remaining.
|
||||
numPendingUiExecs.getAndIncrement();
|
||||
ctx.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
numPendingUiExecs.getAndDecrement();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="8" />
|
||||
<uses-sdk android:minSdkVersion="7" />
|
||||
|
||||
<instrumentation
|
||||
android:name="android.test.InstrumentationTestRunner"
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
Apache Cordova Team
|
||||
</author>
|
||||
|
||||
<access origin="*.apache.org"/>
|
||||
<access origin="*"/>
|
||||
|
||||
|
||||
<!-- <content src="http://mysite.com/myapp.html" /> for external pages -->
|
||||
<content src="index.html" />
|
||||
@@ -41,9 +42,6 @@
|
||||
<preference name="useBrowserHistory" value="true" />
|
||||
<preference name="exit-on-suspend" value="false" />
|
||||
|
||||
<feature name="Activity">
|
||||
<param name="android-package" value="org.apache.cordova.test.ActivityPlugin" />
|
||||
</feature>
|
||||
<feature name="App">
|
||||
<param name="android-package" value="org.apache.cordova.App"/>
|
||||
</feature>
|
||||
|
||||
@@ -36,7 +36,7 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class CordovaWebViewTestActivity extends Activity implements CordovaInterface {
|
||||
public CordovaWebView cordovaWebView;
|
||||
CordovaWebView cordovaWebView;
|
||||
|
||||
private final ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||
|
||||
@@ -97,6 +97,7 @@ public class CordovaWebViewTestActivity extends Activity implements CordovaInter
|
||||
super.onDestroy();
|
||||
if (cordovaWebView != null) {
|
||||
// Send destroy event to JavaScript
|
||||
cordovaWebView.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
|
||||
cordovaWebView.handleDestroy();
|
||||
}
|
||||
}
|
||||
|
||||