mirror of
https://github.com/apache/cordova-android.git
synced 2026-01-30 00:05:28 +08:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff6ef1eea5 | ||
|
|
b7e67ffb3c | ||
|
|
4ab4606ad2 | ||
|
|
957f2fd10b | ||
|
|
cdbe9c7204 | ||
|
|
73c7994cd1 | ||
|
|
0c74090953 | ||
|
|
f60d54eae4 | ||
|
|
85c25630a8 | ||
|
|
31bc015cdd | ||
|
|
b028ad3604 | ||
|
|
d2e4e35c37 | ||
|
|
1f37200bb6 | ||
|
|
77178daad3 | ||
|
|
1648f161d9 | ||
|
|
9fa6cea69b | ||
|
|
66b827e502 | ||
|
|
7755a902dd | ||
|
|
d25b73f47d | ||
|
|
ac2969c3f8 | ||
|
|
ee38b2ef03 | ||
|
|
0f70e04e6e | ||
|
|
9fc1e7272e | ||
|
|
0d4d0b8a37 | ||
|
|
fcd2c989a2 | ||
|
|
e0d0d6c455 | ||
|
|
ce1a961b99 | ||
|
|
c71a08a9d9 | ||
|
|
17bfcea65a | ||
|
|
5e8959bab1 | ||
|
|
9924dc0f92 | ||
|
|
7388c036d7 | ||
|
|
ad4512801f | ||
|
|
409b9af398 | ||
|
|
7cc8fd7e87 | ||
|
|
42c8105f13 | ||
|
|
9a71cc5b4e | ||
|
|
c543b7469d | ||
|
|
7caac3265a | ||
|
|
5d68d5a246 | ||
|
|
7187f87eae | ||
|
|
fb81f3e77e | ||
|
|
0ae49ed098 | ||
|
|
b8e5aaf754 | ||
|
|
aa4820c3b7 | ||
|
|
5d79d6e134 | ||
|
|
c668eeba0f | ||
|
|
62421ee49d | ||
|
|
e791f29ce1 | ||
|
|
06947cc453 | ||
|
|
8c97474524 | ||
|
|
77a8568b28 | ||
|
|
e2dadbd7fe | ||
|
|
17b668a115 | ||
|
|
a30c2b6a75 | ||
|
|
2660eebec2 | ||
|
|
f415664b6d | ||
|
|
5092b29312 | ||
|
|
d5be901bc2 | ||
|
|
fdb3679cf5 | ||
|
|
11beb37c50 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -30,3 +30,6 @@ Desktop.ini
|
||||
*.swp
|
||||
*.class
|
||||
*.jar
|
||||
# IntelliJ IDEA files
|
||||
*.iml
|
||||
.idea
|
||||
|
||||
16
bin/create
16
bin/create
@@ -59,10 +59,10 @@ function on_exit {
|
||||
}
|
||||
|
||||
function createAppInfoJar {
|
||||
(cd "$BUILD_PATH"/bin/templates/cordova/ApplicationInfo &&
|
||||
javac ApplicationInfo.java &&
|
||||
jar -cfe ../appinfo.jar ApplicationInfo ApplicationInfo.class
|
||||
)
|
||||
pushd "$BUILD_PATH"/bin/templates/cordova/ApplicationInfo > /dev/null
|
||||
javac ApplicationInfo.java
|
||||
jar -cfe ../appinfo.jar ApplicationInfo ApplicationInfo.class
|
||||
popd > /dev/null
|
||||
}
|
||||
|
||||
function on_error {
|
||||
@@ -108,7 +108,7 @@ fi
|
||||
# if this a distribution release no need to build a jar
|
||||
if [ ! -e "$BUILD_PATH"/cordova-$VERSION.jar ] && [ -d "$BUILD_PATH"/framework ]
|
||||
then
|
||||
# update the cordova-android framework for the desired target
|
||||
# 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
|
||||
@@ -121,8 +121,10 @@ then
|
||||
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 )
|
||||
# compile cordova.js and cordova.jar
|
||||
pushd "$BUILD_PATH"/framework > /dev/null
|
||||
ant jar > /dev/null
|
||||
popd > /dev/null
|
||||
fi
|
||||
|
||||
# create new android project
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<p class="event received">Device is Ready</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="cordova-2.5.0.js"></script>
|
||||
<script type="text/javascript" src="cordova-2.6.0rc1.js"></script>
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
<script type="text/javascript">
|
||||
app.initialize();
|
||||
|
||||
139
bin/update
Executable file
139
bin/update
Executable file
@@ -0,0 +1,139 @@
|
||||
#! /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.
|
||||
#
|
||||
# update a cordova/android project's command line tools
|
||||
#
|
||||
# USAGE
|
||||
# ./update [path]
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$1" ] || [ "$1" == "-h" ]
|
||||
then
|
||||
echo 'usage: update path'
|
||||
echo "Make sure the Android SDK tools folder is in your PATH!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BUILD_PATH="$( cd "$( dirname "$0" )/.." && pwd )"
|
||||
VERSION=$(cat "$BUILD_PATH"/VERSION)
|
||||
|
||||
PROJECT_PATH="${1:-'./example'}"
|
||||
|
||||
if [ ! -d "$PROJECT_PATH" ]
|
||||
then
|
||||
echo "The project path has to exist for it to be updated"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
# cleanup after exit and/or on error
|
||||
function on_exit {
|
||||
if [ -f "$BUILD_PATH"/framework/assets/www/cordova-$VERSION.js ]
|
||||
then
|
||||
rm "$BUILD_PATH"/framework/assets/www/cordova-$VERSION.js
|
||||
fi
|
||||
if [ -f "$BUILD_PATH"/framework/cordova-$VERSION.jar ]
|
||||
then
|
||||
rm "$BUILD_PATH"/framework/cordova-$VERSION.jar
|
||||
fi
|
||||
}
|
||||
|
||||
function createAppInfoJar {
|
||||
(cd "$BUILD_PATH"/bin/templates/cordova/ApplicationInfo &&
|
||||
javac ApplicationInfo.java &&
|
||||
jar -cfe ../appinfo.jar ApplicationInfo ApplicationInfo.class
|
||||
)
|
||||
}
|
||||
|
||||
function on_error {
|
||||
echo "An unexpected error occurred: $previous_command exited with $?"
|
||||
echo "Deleting project..."
|
||||
[ -d "$PROJECT_PATH" ] && rm -rf "$PROJECT_PATH"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function replace {
|
||||
local pattern=$1
|
||||
local filename=$2
|
||||
# Mac OS X requires -i argument
|
||||
if [[ "$OSTYPE" =~ "darwin" ]]
|
||||
then
|
||||
/usr/bin/sed -i '' -e $pattern "$filename"
|
||||
elif [[ "$OSTYPE" =~ "linux" ]]
|
||||
then
|
||||
/bin/sed -i -e $pattern "$filename"
|
||||
fi
|
||||
}
|
||||
|
||||
# we do not want the script to silently fail
|
||||
trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
|
||||
trap on_error ERR
|
||||
trap on_exit EXIT
|
||||
|
||||
ANDROID_BIN="${ANDROID_BIN:=$( which android )}"
|
||||
|
||||
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 ' ')
|
||||
|
||||
# check that build targets exist
|
||||
if [ -z "$TARGET" ] || [ -z "$API_LEVEL" ]
|
||||
then
|
||||
echo "No Android Targets are installed. Please install at least one via the android SDK"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# if this a distribution release no need to build a jar
|
||||
if [ ! -e "$BUILD_PATH"/cordova-$VERSION.jar ] && [ -d "$BUILD_PATH"/framework ]
|
||||
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
|
||||
|
||||
# copy cordova.js, cordova.jar and res/xml
|
||||
if [ -d "$BUILD_PATH"/framework ]
|
||||
then
|
||||
cp "$BUILD_PATH"/framework/assets/www/cordova-$VERSION.js "$PROJECT_PATH"/assets/www/cordova-$VERSION.js
|
||||
cp "$BUILD_PATH"/framework/cordova-$VERSION.jar "$PROJECT_PATH"/libs/cordova-$VERSION.jar
|
||||
else
|
||||
cp "$BUILD_PATH"/cordova-$VERSION.js "$PROJECT_PATH"/assets/www/cordova-$VERSION.js
|
||||
cp "$BUILD_PATH"/cordova-$VERSION.jar "$PROJECT_PATH"/libs/cordova-$VERSION.jar
|
||||
fi
|
||||
|
||||
# creating cordova folder and copying run/build/log/launch scripts
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/appinfo.jar "$PROJECT_PATH"/cordova/appinfo.jar
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/cordova "$PROJECT_PATH"/cordova/cordova
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/build "$PROJECT_PATH"/cordova/build
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/release "$PROJECT_PATH"/cordova/release
|
||||
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
|
||||
32
bin/update.bat
Normal file
32
bin/update.bat
Normal file
@@ -0,0 +1,32 @@
|
||||
:: 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
|
||||
IF NOT DEFINED JAVA_HOME GOTO MISSING
|
||||
FOR %%X in (java.exe javac.exe ant.bat android.bat) do (
|
||||
SET FOUND=%%~$PATH:X
|
||||
IF NOT DEFINED FOUND GOTO MISSING
|
||||
)
|
||||
cscript "%~dp0\update.js" %*
|
||||
GOTO END
|
||||
:MISSING
|
||||
ECHO Missing one of the following:
|
||||
ECHO JDK: http://java.oracle.com
|
||||
ECHO Android SDK: http://developer.android.com
|
||||
ECHO Apache ant: http://ant.apache.org
|
||||
EXIT /B 1
|
||||
:END
|
||||
193
bin/update.js
Normal file
193
bin/update.js
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
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
|
||||
* ./update [path]
|
||||
*/
|
||||
|
||||
var fso = WScript.CreateObject('Scripting.FileSystemObject');
|
||||
|
||||
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) {
|
||||
WScript.Echo("You do not have any android targets setup. Please create at least one target with the `android` command");
|
||||
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")) {
|
||||
WScript.Echo("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() {
|
||||
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 {
|
||||
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");
|
||||
|
||||
// working dir
|
||||
var ROOT = WScript.ScriptFullName.split('\\bin\\update.js').join('');
|
||||
|
||||
if (args.Count() == 1) {
|
||||
PROJECT_PATH=args(0);
|
||||
}
|
||||
|
||||
if(!fso.FolderExists(PROJECT_PATH)) {
|
||||
WScript.Echo("Project doesn't exist!");
|
||||
WScript.Quit(1);
|
||||
}
|
||||
|
||||
var TARGET=setTarget();
|
||||
var API_LEVEL=setApiLevel();
|
||||
var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
|
||||
|
||||
// build from source. distro should have these files
|
||||
if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
|
||||
!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.js')) {
|
||||
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');
|
||||
}
|
||||
|
||||
// check if we have the source or the distro files
|
||||
WScript.Echo("Copying js, jar & config.xml files...");
|
||||
if(fso.FolderExists(ROOT + '\\framework')) {
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
||||
} else {
|
||||
// copy in cordova.js
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
|
||||
// copy in cordova.jar
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
||||
// copy in xml
|
||||
}
|
||||
|
||||
// update cordova scripts
|
||||
createAppInfoJar();
|
||||
WScript.Echo("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\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /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');
|
||||
|
||||
cleanup();
|
||||
@@ -1,8 +1,8 @@
|
||||
// Platform: android
|
||||
|
||||
// commit f50d20a87431c79a54572263729461883f611a53
|
||||
// commit 104709b2130a29e7ad8596d1a6cee1ed48138803
|
||||
|
||||
// File generated at :: Tue Feb 26 2013 13:37:51 GMT-0800 (PST)
|
||||
// File generated at :: Wed Mar 27 2013 13:25:10 GMT-0700 (PDT)
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
@@ -262,7 +262,7 @@ var cordova = {
|
||||
*/
|
||||
callbackSuccess: function(callbackId, args) {
|
||||
try {
|
||||
cordova.callbackFromNative(callbackId, true, args.status, args.message, args.keepCallback);
|
||||
cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
|
||||
} catch (e) {
|
||||
console.log("Error in error callback: " + callbackId + " = "+e);
|
||||
}
|
||||
@@ -275,7 +275,7 @@ var cordova = {
|
||||
// TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative.
|
||||
// Derive success from status.
|
||||
try {
|
||||
cordova.callbackFromNative(callbackId, false, args.status, args.message, args.keepCallback);
|
||||
cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
|
||||
} catch (e) {
|
||||
console.log("Error in error callback: " + callbackId + " = "+e);
|
||||
}
|
||||
@@ -284,13 +284,13 @@ var cordova = {
|
||||
/**
|
||||
* Called by native code when returning the result from an action.
|
||||
*/
|
||||
callbackFromNative: function(callbackId, success, status, message, keepCallback) {
|
||||
callbackFromNative: function(callbackId, success, status, args, keepCallback) {
|
||||
var callback = cordova.callbacks[callbackId];
|
||||
if (callback) {
|
||||
if (success && status == cordova.callbackStatus.OK) {
|
||||
callback.success && callback.success(message);
|
||||
callback.success && callback.success.apply(null, args);
|
||||
} else if (!success) {
|
||||
callback.fail && callback.fail(message);
|
||||
callback.fail && callback.fail.apply(null, args);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
@@ -724,6 +724,9 @@ channel.createSticky('onCordovaInfoReady');
|
||||
// Event to indicate that the connection property has been set.
|
||||
channel.createSticky('onCordovaConnectionReady');
|
||||
|
||||
// Event to indicate that all automatically loaded JS plugins are loaded and ready.
|
||||
channel.createSticky('onPluginsReady');
|
||||
|
||||
// Event to indicate that Cordova is ready
|
||||
channel.createSticky('onDeviceReady');
|
||||
|
||||
@@ -900,7 +903,7 @@ androidExec.nativeToJsModes = nativeToJsModes;
|
||||
|
||||
androidExec.setJsToNativeBridgeMode = function(mode) {
|
||||
if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) {
|
||||
console.log('Falling back on PROMPT mode since _cordovaNative is missing.');
|
||||
console.log('Falling back on PROMPT mode since _cordovaNative is missing. Expected for Android 3.2 and lower only.');
|
||||
mode = jsToNativeModes.PROMPT;
|
||||
}
|
||||
nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT);
|
||||
@@ -958,10 +961,12 @@ function processMessage(message) {
|
||||
arraybuffer[i] = bytes.charCodeAt(i);
|
||||
}
|
||||
payload = arraybuffer.buffer;
|
||||
} else if (payloadKind == 'S') {
|
||||
payload = window.atob(message.slice(nextSpaceIdx + 2));
|
||||
} else {
|
||||
payload = JSON.parse(message.slice(nextSpaceIdx + 1));
|
||||
}
|
||||
cordova.callbackFromNative(callbackId, success, status, payload, keepCallback);
|
||||
cordova.callbackFromNative(callbackId, success, status, [payload], keepCallback);
|
||||
} else {
|
||||
console.log("processMessage failed: invalid message:" + message);
|
||||
}
|
||||
@@ -1203,9 +1208,10 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) {
|
||||
var correctOrientation = !!options.correctOrientation;
|
||||
var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
|
||||
var popoverOptions = getValue(options.popoverOptions, null);
|
||||
var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
|
||||
|
||||
var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
|
||||
mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions];
|
||||
mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
|
||||
|
||||
exec(successCallback, errorCallback, "Camera", "takePicture", args);
|
||||
return new CameraPopoverHandle();
|
||||
@@ -1248,6 +1254,10 @@ module.exports = {
|
||||
ARROW_LEFT : 4,
|
||||
ARROW_RIGHT : 8,
|
||||
ARROW_ANY : 15
|
||||
},
|
||||
Direction:{
|
||||
BACK: 0,
|
||||
FRONT: 1
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2439,14 +2449,7 @@ FileReader.prototype.readAsText = function(file, encoding) {
|
||||
// Default encoding is UTF-8
|
||||
var enc = encoding ? encoding : "UTF-8";
|
||||
var me = this;
|
||||
var execArgs = [this._fileName, enc];
|
||||
|
||||
// Maybe add slice parameters.
|
||||
if (file.end < file.size) {
|
||||
execArgs.push(file.start, file.end);
|
||||
} else if (file.start > 0) {
|
||||
execArgs.push(file.start);
|
||||
}
|
||||
var execArgs = [this._fileName, enc, file.start, file.end];
|
||||
|
||||
// Read file
|
||||
exec(
|
||||
@@ -2515,14 +2518,7 @@ FileReader.prototype.readAsDataURL = function(file) {
|
||||
}
|
||||
|
||||
var me = this;
|
||||
var execArgs = [this._fileName];
|
||||
|
||||
// Maybe add slice parameters.
|
||||
if (file.end < file.size) {
|
||||
execArgs.push(file.start, file.end);
|
||||
} else if (file.start > 0) {
|
||||
execArgs.push(file.start);
|
||||
}
|
||||
var execArgs = [this._fileName, file.start, file.end];
|
||||
|
||||
// Read file
|
||||
exec(
|
||||
@@ -2585,9 +2581,59 @@ FileReader.prototype.readAsBinaryString = function(file) {
|
||||
if (initRead(this, file)) {
|
||||
return this._realReader.readAsBinaryString(file);
|
||||
}
|
||||
// TODO - Can't return binary data to browser.
|
||||
console.log('method "readAsBinaryString" is not supported at this time.');
|
||||
this.abort();
|
||||
|
||||
var me = this;
|
||||
var execArgs = [this._fileName, file.start, file.end];
|
||||
|
||||
// Read file
|
||||
exec(
|
||||
// Success callback
|
||||
function(r) {
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me._readyState === FileReader.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// DONE state
|
||||
me._readyState = FileReader.DONE;
|
||||
|
||||
me._result = r;
|
||||
|
||||
// If onload callback
|
||||
if (typeof me.onload === "function") {
|
||||
me.onload(new ProgressEvent("load", {target:me}));
|
||||
}
|
||||
|
||||
// If onloadend callback
|
||||
if (typeof me.onloadend === "function") {
|
||||
me.onloadend(new ProgressEvent("loadend", {target:me}));
|
||||
}
|
||||
},
|
||||
// Error callback
|
||||
function(e) {
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me._readyState === FileReader.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// DONE state
|
||||
me._readyState = FileReader.DONE;
|
||||
|
||||
me._result = null;
|
||||
|
||||
// Save error
|
||||
me._error = new FileError(e);
|
||||
|
||||
// If onerror callback
|
||||
if (typeof me.onerror === "function") {
|
||||
me.onerror(new ProgressEvent("error", {target:me}));
|
||||
}
|
||||
|
||||
// If onloadend callback
|
||||
if (typeof me.onloadend === "function") {
|
||||
me.onloadend(new ProgressEvent("loadend", {target:me}));
|
||||
}
|
||||
}, "File", "readAsBinaryString", execArgs);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2599,9 +2645,59 @@ FileReader.prototype.readAsArrayBuffer = function(file) {
|
||||
if (initRead(this, file)) {
|
||||
return this._realReader.readAsArrayBuffer(file);
|
||||
}
|
||||
// TODO - Can't return binary data to browser.
|
||||
console.log('This method is not supported at this time.');
|
||||
this.abort();
|
||||
|
||||
var me = this;
|
||||
var execArgs = [this._fileName, file.start, file.end];
|
||||
|
||||
// Read file
|
||||
exec(
|
||||
// Success callback
|
||||
function(r) {
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me._readyState === FileReader.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// DONE state
|
||||
me._readyState = FileReader.DONE;
|
||||
|
||||
me._result = r;
|
||||
|
||||
// If onload callback
|
||||
if (typeof me.onload === "function") {
|
||||
me.onload(new ProgressEvent("load", {target:me}));
|
||||
}
|
||||
|
||||
// If onloadend callback
|
||||
if (typeof me.onloadend === "function") {
|
||||
me.onloadend(new ProgressEvent("loadend", {target:me}));
|
||||
}
|
||||
},
|
||||
// Error callback
|
||||
function(e) {
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me._readyState === FileReader.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// DONE state
|
||||
me._readyState = FileReader.DONE;
|
||||
|
||||
me._result = null;
|
||||
|
||||
// Save error
|
||||
me._error = new FileError(e);
|
||||
|
||||
// If onerror callback
|
||||
if (typeof me.onerror === "function") {
|
||||
me.onerror(new ProgressEvent("error", {target:me}));
|
||||
}
|
||||
|
||||
// If onloadend callback
|
||||
if (typeof me.onloadend === "function") {
|
||||
me.onloadend(new ProgressEvent("loadend", {target:me}));
|
||||
}
|
||||
}, "File", "readAsArrayBuffer", execArgs);
|
||||
};
|
||||
|
||||
module.exports = FileReader;
|
||||
@@ -2647,6 +2743,38 @@ function newProgressEvent(result) {
|
||||
return pe;
|
||||
}
|
||||
|
||||
function getBasicAuthHeader(urlString) {
|
||||
var header = null;
|
||||
|
||||
if (window.btoa) {
|
||||
// parse the url using the Location object
|
||||
var url = document.createElement('a');
|
||||
url.href = urlString;
|
||||
|
||||
var credentials = null;
|
||||
var protocol = url.protocol + "//";
|
||||
var origin = protocol + url.host;
|
||||
|
||||
// check whether there are the username:password credentials in the url
|
||||
if (url.href.indexOf(origin) != 0) { // credentials found
|
||||
var atIndex = url.href.indexOf("@");
|
||||
credentials = url.href.substring(protocol.length, atIndex);
|
||||
}
|
||||
|
||||
if (credentials) {
|
||||
var authHeader = "Authorization";
|
||||
var authHeaderValue = "Basic " + window.btoa(credentials);
|
||||
|
||||
header = {
|
||||
name : authHeader,
|
||||
value : authHeaderValue
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
var idCounter = 0;
|
||||
|
||||
/**
|
||||
@@ -2677,6 +2805,18 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
|
||||
var params = null;
|
||||
var chunkedMode = true;
|
||||
var headers = null;
|
||||
|
||||
var basicAuthHeader = getBasicAuthHeader(server);
|
||||
if (basicAuthHeader) {
|
||||
if (!options) {
|
||||
options = new FileUploadOptions();
|
||||
}
|
||||
if (!options.headers) {
|
||||
options.headers = {};
|
||||
}
|
||||
options.headers[basicAuthHeader.name] = basicAuthHeader.value;
|
||||
}
|
||||
|
||||
if (options) {
|
||||
fileKey = options.fileKey;
|
||||
fileName = options.fileName;
|
||||
@@ -2694,7 +2834,7 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
|
||||
}
|
||||
|
||||
var fail = errorCallback && function(e) {
|
||||
var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
|
||||
var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
|
||||
errorCallback(error);
|
||||
};
|
||||
|
||||
@@ -2718,10 +2858,28 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
|
||||
* @param successCallback (Function} Callback to be invoked when upload has completed
|
||||
* @param errorCallback {Function} Callback to be invoked upon error
|
||||
* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
|
||||
* @param options {FileDownloadOptions} Optional parameters such as headers
|
||||
*/
|
||||
FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts) {
|
||||
FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) {
|
||||
argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
|
||||
var self = this;
|
||||
|
||||
var basicAuthHeader = getBasicAuthHeader(source);
|
||||
if (basicAuthHeader) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (!options.headers) {
|
||||
options.headers = {};
|
||||
}
|
||||
options.headers[basicAuthHeader.name] = basicAuthHeader.value;
|
||||
}
|
||||
|
||||
var headers = null;
|
||||
if (options) {
|
||||
headers = options.headers || null;
|
||||
}
|
||||
|
||||
var win = function(result) {
|
||||
if (typeof result.lengthComputable != "undefined") {
|
||||
if (self.onprogress) {
|
||||
@@ -2744,11 +2902,11 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro
|
||||
};
|
||||
|
||||
var fail = errorCallback && function(e) {
|
||||
var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
|
||||
var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
|
||||
errorCallback(error);
|
||||
};
|
||||
|
||||
exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id]);
|
||||
exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id, headers]);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3146,6 +3304,7 @@ function InAppBrowser() {
|
||||
this.channels = {
|
||||
'loadstart': channel.create('loadstart'),
|
||||
'loadstop' : channel.create('loadstop'),
|
||||
'loaderror' : channel.create('loaderror'),
|
||||
'exit' : channel.create('exit')
|
||||
};
|
||||
}
|
||||
@@ -3176,7 +3335,7 @@ module.exports = function(strUrl, strWindowName, strWindowFeatures) {
|
||||
var cb = function(eventname) {
|
||||
iab._eventHandler(eventname);
|
||||
};
|
||||
exec(cb, null, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
|
||||
exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
|
||||
return iab;
|
||||
};
|
||||
|
||||
@@ -4940,7 +5099,8 @@ modulemapper.merges('cordova/plugin/android/device', 'device');
|
||||
// file: lib/common/plugin/echo.js
|
||||
define("cordova/plugin/echo", function(require, exports, module) {
|
||||
|
||||
var exec = require('cordova/exec');
|
||||
var exec = require('cordova/exec'),
|
||||
utils = require('cordova/utils');
|
||||
|
||||
/**
|
||||
* Sends the given message through exec() to the Echo plugin, which sends it back to the successCallback.
|
||||
@@ -4950,11 +5110,25 @@ var exec = require('cordova/exec');
|
||||
* @param forceAsync Whether to force an async return value (for testing native->js bridge).
|
||||
*/
|
||||
module.exports = function(successCallback, errorCallback, message, forceAsync) {
|
||||
var action = forceAsync ? 'echoAsync' : 'echo';
|
||||
if (!forceAsync && message.constructor == ArrayBuffer) {
|
||||
action = 'echoArrayBuffer';
|
||||
var action = 'echo';
|
||||
var messageIsMultipart = (utils.typeName(message) == "Array");
|
||||
var args = messageIsMultipart ? message : [message];
|
||||
|
||||
if (utils.typeName(message) == 'ArrayBuffer') {
|
||||
if (forceAsync) {
|
||||
console.warn('Cannot echo ArrayBuffer with forced async, falling back to sync.');
|
||||
}
|
||||
action += 'ArrayBuffer';
|
||||
} else if (messageIsMultipart) {
|
||||
if (forceAsync) {
|
||||
console.warn('Cannot echo MultiPart Array with forced async, falling back to sync.');
|
||||
}
|
||||
action += 'MultiPart';
|
||||
} else if (forceAsync) {
|
||||
action += 'Async';
|
||||
}
|
||||
exec(successCallback, errorCallback, "Echo", action, [message]);
|
||||
|
||||
exec(successCallback, errorCallback, "Echo", action, args);
|
||||
};
|
||||
|
||||
|
||||
@@ -5950,6 +6124,7 @@ modulemapper.defaults('cordova/plugin/Connection', 'Connection');
|
||||
define("cordova/plugin/notification", function(require, exports, module) {
|
||||
|
||||
var exec = require('cordova/exec');
|
||||
var platform = require('cordova/platform');
|
||||
|
||||
/**
|
||||
* Provides access to notifications on the device.
|
||||
@@ -5978,14 +6153,52 @@ module.exports = {
|
||||
* @param {String} message Message to print in the body of the alert
|
||||
* @param {Function} resultCallback The callback that is called when user clicks on a button.
|
||||
* @param {String} title Title of the alert dialog (default: Confirm)
|
||||
* @param {String} buttonLabels Comma separated list of the labels of the buttons (default: 'OK,Cancel')
|
||||
* @param {Array} buttonLabels Array of the labels of the buttons (default: ['OK', 'Cancel'])
|
||||
*/
|
||||
confirm: function(message, resultCallback, title, buttonLabels) {
|
||||
var _title = (title || "Confirm");
|
||||
var _buttonLabels = (buttonLabels || "OK,Cancel");
|
||||
var _buttonLabels = (buttonLabels || ["OK", "Cancel"]);
|
||||
|
||||
// Strings are deprecated!
|
||||
if (typeof _buttonLabels === 'string') {
|
||||
console.log("Notification.confirm(string, function, string, string) is deprecated. Use Notification.confirm(string, function, string, array).");
|
||||
}
|
||||
|
||||
// Android and iOS take an array of button label names.
|
||||
// Other platforms take a comma separated list.
|
||||
// For compatibility, we convert to the desired type based on the platform.
|
||||
if (platform.id == "android" || platform.id == "ios") {
|
||||
if (typeof _buttonLabels === 'string') {
|
||||
var buttonLabelString = _buttonLabels;
|
||||
_buttonLabels = buttonLabelString.split(",");
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(_buttonLabels)) {
|
||||
var buttonLabelArray = _buttonLabels;
|
||||
_buttonLabels = buttonLabelArray.toString();
|
||||
}
|
||||
}
|
||||
exec(resultCallback, null, "Notification", "confirm", [message, _title, _buttonLabels]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open a native prompt dialog, with a customizable title and button text.
|
||||
* The following results are returned to the result callback:
|
||||
* buttonIndex Index number of the button selected.
|
||||
* input1 The text entered in the prompt dialog box.
|
||||
*
|
||||
* @param {String} message Dialog message to display (default: "Prompt message")
|
||||
* @param {Function} resultCallback The callback that is called when user clicks on a button.
|
||||
* @param {String} title Title of the dialog (default: "Prompt")
|
||||
* @param {Array} buttonLabels Array of strings for the button labels (default: ["OK","Cancel"])
|
||||
*/
|
||||
prompt: function(message, resultCallback, title, buttonLabels) {
|
||||
var _message = (message || "Prompt message");
|
||||
var _title = (title || "Prompt");
|
||||
var _buttonLabels = (buttonLabels || ["OK","Cancel"]);
|
||||
exec(resultCallback, null, "Notification", "prompt", [_message, _title, _buttonLabels]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Causes the device to vibrate.
|
||||
*
|
||||
@@ -6409,44 +6622,26 @@ window.cordova = require('cordova');
|
||||
(function (context) {
|
||||
// Replace navigator before any modules are required(), to ensure it happens as soon as possible.
|
||||
// We replace it so that properties that can't be clobbered can instead be overridden.
|
||||
if (context.navigator) {
|
||||
function replaceNavigator(origNavigator) {
|
||||
var CordovaNavigator = function() {};
|
||||
CordovaNavigator.prototype = context.navigator;
|
||||
context.navigator = new CordovaNavigator();
|
||||
CordovaNavigator.prototype = origNavigator;
|
||||
var newNavigator = new CordovaNavigator();
|
||||
// This work-around really only applies to new APIs that are newer than Function.bind.
|
||||
// Without it, APIs such as getGamepads() break.
|
||||
if (CordovaNavigator.bind) {
|
||||
for (var key in origNavigator) {
|
||||
if (typeof origNavigator[key] == 'function') {
|
||||
newNavigator[key] = origNavigator[key].bind(origNavigator);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newNavigator;
|
||||
}
|
||||
if (context.navigator) {
|
||||
context.navigator = replaceNavigator(context.navigator);
|
||||
}
|
||||
|
||||
var channel = require("cordova/channel"),
|
||||
_self = {
|
||||
boot: function () {
|
||||
/**
|
||||
* Create all cordova objects once page has fully loaded and native side is ready.
|
||||
*/
|
||||
channel.join(function() {
|
||||
var builder = require('cordova/builder'),
|
||||
platform = require('cordova/platform');
|
||||
|
||||
builder.buildIntoButDoNotClobber(platform.defaults, context);
|
||||
builder.buildIntoAndClobber(platform.clobbers, context);
|
||||
builder.buildIntoAndMerge(platform.merges, context);
|
||||
|
||||
// Call the platform-specific initialization
|
||||
platform.initialize();
|
||||
|
||||
// Fire event to notify that all objects are created
|
||||
channel.onCordovaReady.fire();
|
||||
|
||||
// Fire onDeviceReady event once all constructors have run and
|
||||
// cordova info has been received from native side.
|
||||
channel.join(function() {
|
||||
require('cordova').fireDocumentEvent('deviceready');
|
||||
}, channel.deviceReadyChannelsArray);
|
||||
|
||||
}, [ channel.onDOMContentLoaded, channel.onNativeReady ]);
|
||||
}
|
||||
};
|
||||
|
||||
// boot up once native side is ready
|
||||
channel.onNativeReady.subscribe(_self.boot);
|
||||
var channel = require("cordova/channel");
|
||||
|
||||
// _nativeReady is global variable that the native side can set
|
||||
// to signify that the native code is ready. It is a global since
|
||||
@@ -6455,7 +6650,137 @@ window.cordova = require('cordova');
|
||||
channel.onNativeReady.fire();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all cordova objects once page has fully loaded and native side is ready.
|
||||
*/
|
||||
channel.join(function() {
|
||||
var builder = require('cordova/builder'),
|
||||
platform = require('cordova/platform');
|
||||
|
||||
builder.buildIntoButDoNotClobber(platform.defaults, context);
|
||||
builder.buildIntoAndClobber(platform.clobbers, context);
|
||||
builder.buildIntoAndMerge(platform.merges, context);
|
||||
|
||||
// Call the platform-specific initialization
|
||||
platform.initialize();
|
||||
|
||||
// Fire event to notify that all objects are created
|
||||
channel.onCordovaReady.fire();
|
||||
|
||||
// Fire onDeviceReady event once all constructors have run and
|
||||
// cordova info has been received from native side.
|
||||
channel.join(function() {
|
||||
require('cordova').fireDocumentEvent('deviceready');
|
||||
}, channel.deviceReadyChannelsArray);
|
||||
|
||||
}, [ channel.onDOMContentLoaded, channel.onNativeReady, channel.onPluginsReady ]);
|
||||
|
||||
}(window));
|
||||
|
||||
// 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) {
|
||||
// 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(modules[i].file);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to XHR the cordova_plugins.json file asynchronously.
|
||||
try { // we commented we were going to try, so let us actually try and catch
|
||||
var xhr = new context.XMLHttpRequest();
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState != 4) { // not DONE
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (this.status == 200) {
|
||||
var obj = JSON.parse(this.responseText);
|
||||
if (obj && obj instanceof Array && obj.length > 0) {
|
||||
handlePluginsObject(obj);
|
||||
} else {
|
||||
finishPluginLoading();
|
||||
}
|
||||
} else {
|
||||
finishPluginLoading();
|
||||
}
|
||||
};
|
||||
xhr.open('GET', 'cordova_plugins.json', true); // Async
|
||||
xhr.send();
|
||||
}
|
||||
catch(err) {
|
||||
finishPluginLoading();
|
||||
}
|
||||
}(window));
|
||||
|
||||
|
||||
|
||||
})();
|
||||
@@ -19,7 +19,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="cordova-2.5.0.js"></script>
|
||||
<script src="cordova-2.6.0rc1.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -68,13 +68,13 @@ public class AudioHandler extends CordovaPlugin {
|
||||
String result = "";
|
||||
|
||||
if (action.equals("startRecordingAudio")) {
|
||||
this.startRecordingAudio(args.getString(0), FileUtils.stripFileProtocol(args.getString(1)));
|
||||
this.startRecordingAudio(args.getString(0), FileHelper.stripFileProtocol(args.getString(1)));
|
||||
}
|
||||
else if (action.equals("stopRecordingAudio")) {
|
||||
this.stopRecordingAudio(args.getString(0));
|
||||
}
|
||||
else if (action.equals("startPlayingAudio")) {
|
||||
this.startPlayingAudio(args.getString(0), FileUtils.stripFileProtocol(args.getString(1)));
|
||||
this.startPlayingAudio(args.getString(0), FileHelper.stripFileProtocol(args.getString(1)));
|
||||
}
|
||||
else if (action.equals("seekToAudio")) {
|
||||
this.seekToAudio(args.getString(0), args.getInt(1));
|
||||
@@ -102,7 +102,7 @@ public class AudioHandler extends CordovaPlugin {
|
||||
}
|
||||
else if (action.equals("create")) {
|
||||
String id = args.getString(0);
|
||||
String src = FileUtils.stripFileProtocol(args.getString(1));
|
||||
String src = FileHelper.stripFileProtocol(args.getString(1));
|
||||
AudioPlayer audio = new AudioPlayer(this, id, src);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
|
||||
@@ -167,13 +167,18 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
public void moveFile(String file) {
|
||||
/* this is a hack to save the file as the specified name */
|
||||
File f = new File(this.tempFile);
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
f.renameTo(new File(Environment.getExternalStorageDirectory().getAbsolutePath()
|
||||
+ File.separator + file));
|
||||
} else {
|
||||
f.renameTo(new File("/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/" + file));
|
||||
|
||||
if (!file.startsWith("/")) {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
file = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + file;
|
||||
} else {
|
||||
file = "/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/" + file;
|
||||
}
|
||||
}
|
||||
|
||||
String logMsg = "renaming " + this.tempFile + " to " + file;
|
||||
Log.d(LOG_TAG, logMsg);
|
||||
if (!f.renameTo(new File(file))) Log.e(LOG_TAG, "FAILED " + logMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -289,7 +289,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
bitmap = getScaledBitmap(FileUtils.stripFileProtocol(imageUri.toString()));
|
||||
bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
|
||||
if (bitmap == null) {
|
||||
// Try to get the bitmap from intent.
|
||||
bitmap = (Bitmap)intent.getExtras().get("data");
|
||||
@@ -329,7 +329,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
this.callbackContext.success(uri.toString());
|
||||
} else {
|
||||
bitmap = getScaledBitmap(FileUtils.stripFileProtocol(imageUri.toString()));
|
||||
bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
|
||||
|
||||
if (rotate != 0 && this.correctOrientation) {
|
||||
bitmap = getRotatedBitmap(rotate, bitmap, exif);
|
||||
@@ -344,7 +344,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
if (this.encodingType == JPEG) {
|
||||
String exifPath;
|
||||
if (this.saveToPhotoAlbum) {
|
||||
exifPath = FileUtils.getRealPathFromURI(uri, this.cordova);
|
||||
exifPath = FileHelper.getRealPath(uri, this.cordova);
|
||||
} else {
|
||||
exifPath = uri.getPath();
|
||||
}
|
||||
@@ -395,8 +395,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
this.callbackContext.success(uri.toString());
|
||||
} else {
|
||||
// Get the path to the image. Makes loading so much easier.
|
||||
String imagePath = FileUtils.getRealPathFromURI(uri, this.cordova);
|
||||
String mimeType = FileUtils.getMimeType(imagePath);
|
||||
String imagePath = FileHelper.getRealPath(uri, this.cordova);
|
||||
String mimeType = FileHelper.getMimeType(imagePath, this.cordova);
|
||||
// Log.d(LOG_TAG, "Real path = " + imagePath);
|
||||
// Log.d(LOG_TAG, "mime type = " + mimeType);
|
||||
// If we don't have a valid image so quit.
|
||||
@@ -444,7 +444,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(resizePath);
|
||||
exif.createInFile(FileHelper.getRealPath(uri, this.cordova));
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
}
|
||||
@@ -458,7 +458,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
// Restore exif data to file
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.cordova));
|
||||
exif.createOutFile(resizePath);
|
||||
exif.writeExifData();
|
||||
}
|
||||
|
||||
@@ -521,7 +521,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
*/
|
||||
private void writeUncompressedImage(Uri uri) throws FileNotFoundException,
|
||||
IOException {
|
||||
FileInputStream fis = new FileInputStream(FileUtils.stripFileProtocol(imageUri.toString()));
|
||||
FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString()));
|
||||
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||
byte[] buffer = new byte[4096];
|
||||
int len;
|
||||
@@ -685,7 +685,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
|
||||
// Clean up initial camera-written image file.
|
||||
(new File(FileUtils.stripFileProtocol(oldImage.toString()))).delete();
|
||||
(new File(FileHelper.stripFileProtocol(oldImage.toString()))).delete();
|
||||
|
||||
checkForDuplicateImage(imageType);
|
||||
// Scan for the gallery to update pic refs in gallery
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import android.os.Build;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
@@ -128,7 +129,7 @@ public class Capture extends CordovaPlugin {
|
||||
// If the mimeType isn't set the rest will fail
|
||||
// so let's see if we can determine it.
|
||||
if (mimeType == null || mimeType.equals("") || "null".equals(mimeType)) {
|
||||
mimeType = FileUtils.getMimeType(filePath);
|
||||
mimeType = FileHelper.getMimeType(filePath, cordova);
|
||||
}
|
||||
Log.d(LOG_TAG, "Mime type = " + mimeType);
|
||||
|
||||
@@ -155,7 +156,7 @@ public class Capture extends CordovaPlugin {
|
||||
private JSONObject getImageData(String filePath, JSONObject obj) throws JSONException {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(FileUtils.stripFileProtocol(filePath), options);
|
||||
BitmapFactory.decodeFile(FileHelper.stripFileProtocol(filePath), options);
|
||||
obj.put("height", options.outHeight);
|
||||
obj.put("width", options.outWidth);
|
||||
return obj;
|
||||
@@ -216,9 +217,10 @@ public class Capture extends CordovaPlugin {
|
||||
*/
|
||||
private void captureVideo(double duration) {
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
|
||||
// Introduced in API 8
|
||||
//intent.putExtra(android.provider.MediaStore.EXTRA_DURATION_LIMIT, duration);
|
||||
|
||||
if(Build.VERSION.SDK_INT > 8){
|
||||
intent.putExtra("android.intent.extra.durationLimit", duration);
|
||||
}
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_VIDEO);
|
||||
}
|
||||
|
||||
@@ -346,7 +348,7 @@ public class Capture extends CordovaPlugin {
|
||||
* @throws IOException
|
||||
*/
|
||||
private JSONObject createMediaFile(Uri data) {
|
||||
File fp = new File(FileUtils.getRealPathFromURI(data, this.cordova));
|
||||
File fp = new File(FileHelper.getRealPath(data, this.cordova));
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try {
|
||||
@@ -363,7 +365,7 @@ public class Capture extends CordovaPlugin {
|
||||
obj.put("type", VIDEO_3GPP);
|
||||
}
|
||||
} else {
|
||||
obj.put("type", FileUtils.getMimeType(fp.getAbsolutePath()));
|
||||
obj.put("type", FileHelper.getMimeType(fp.getAbsolutePath(), cordova));
|
||||
}
|
||||
|
||||
obj.put("lastModifiedDate", fp.lastModified());
|
||||
|
||||
@@ -143,6 +143,16 @@ public class Config {
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equals("InAppBrowserStorageEnabled"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equals("disallowOverscroll"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
String value = xml.getAttributeValue(null, "value");
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.apache.cordova;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
@@ -52,7 +53,7 @@ public class CordovaArgs {
|
||||
return baseArgs.getJSONArray(index);
|
||||
}
|
||||
|
||||
public Object getJSONObject(int index) throws JSONException {
|
||||
public JSONObject getJSONObject(int index) throws JSONException {
|
||||
return baseArgs.getJSONObject(index);
|
||||
}
|
||||
|
||||
@@ -85,7 +86,7 @@ public class CordovaArgs {
|
||||
return baseArgs.optJSONArray(index);
|
||||
}
|
||||
|
||||
public Object optJSONObject(int index) {
|
||||
public JSONObject optJSONObject(int index) {
|
||||
return baseArgs.optJSONObject(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
// Security check to make sure any requests are coming from the page initially
|
||||
// loaded in webview and not another loaded in an iframe.
|
||||
boolean reqOk = false;
|
||||
if (url.startsWith("file://") || url.indexOf(this.appView.baseUrl) == 0 || Config.isUrlWhiteListed(url)) {
|
||||
if (url.startsWith("file://") || Config.isUrlWhiteListed(url)) {
|
||||
reqOk = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.WebBackForwardList;
|
||||
import android.webkit.WebHistoryItem;
|
||||
import android.webkit.WebChromeClient;
|
||||
@@ -74,12 +75,7 @@ public class CordovaWebView extends WebView {
|
||||
@SuppressWarnings("unused")
|
||||
private CordovaChromeClient chromeClient;
|
||||
|
||||
//This is for the polyfill history
|
||||
private String url;
|
||||
String baseUrl;
|
||||
private Stack<String> urls = new Stack<String>();
|
||||
|
||||
boolean useBrowserHistory = true;
|
||||
|
||||
// Flag to track that a loadUrl timeout occurred
|
||||
int loadUrlTimeout = 0;
|
||||
@@ -211,7 +207,8 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
|
||||
private void initWebViewClient(CordovaInterface cordova) {
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB ||
|
||||
android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
{
|
||||
this.setWebViewClient(new CordovaWebViewClient(this.cordova, this));
|
||||
}
|
||||
@@ -254,14 +251,23 @@ public class CordovaWebView extends WebView {
|
||||
Log.d(TAG, "This should never happen: InvocationTargetException means this isn't Android anymore.");
|
||||
}
|
||||
|
||||
//We don't save any form data in the application
|
||||
settings.setSaveFormData(false);
|
||||
settings.setSavePassword(false);
|
||||
|
||||
// Jellybean rightfully tried to lock this down. Too bad they didn't give us a whitelist
|
||||
// while we do this
|
||||
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
|
||||
Level16Apis.enableUniversalAccess(settings);
|
||||
// Enable database
|
||||
settings.setDatabaseEnabled(true);
|
||||
// We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16
|
||||
String databasePath = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||
settings.setDatabasePath(databasePath);
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
{
|
||||
settings.setDatabaseEnabled(true);
|
||||
settings.setDatabasePath(databasePath);
|
||||
}
|
||||
|
||||
settings.setGeolocationDatabasePath(databasePath);
|
||||
|
||||
// Enable DOM storage
|
||||
@@ -360,7 +366,7 @@ public class CordovaWebView extends WebView {
|
||||
String initUrl = this.getProperty("url", null);
|
||||
|
||||
// If first page of app, then set URL to load to be the one passed in
|
||||
if (initUrl == null || (this.urls.size() > 0)) {
|
||||
if (initUrl == null) {
|
||||
this.loadUrlIntoView(url);
|
||||
}
|
||||
// Otherwise use the URL specified in the activity's extras bundle
|
||||
@@ -381,7 +387,7 @@ public class CordovaWebView extends WebView {
|
||||
String initUrl = this.getProperty("url", null);
|
||||
|
||||
// If first page of app, then set URL to load to be the one passed in
|
||||
if (initUrl == null || (this.urls.size() > 0)) {
|
||||
if (initUrl == null) {
|
||||
this.loadUrlIntoView(url, time);
|
||||
}
|
||||
// Otherwise use the URL specified in the activity's extras bundle
|
||||
@@ -399,21 +405,8 @@ public class CordovaWebView extends WebView {
|
||||
LOG.d(TAG, ">>> loadUrl(" + url + ")");
|
||||
|
||||
this.url = url;
|
||||
if (this.baseUrl == null) {
|
||||
int i = url.lastIndexOf('/');
|
||||
if (i > 0) {
|
||||
this.baseUrl = url.substring(0, i + 1);
|
||||
}
|
||||
else {
|
||||
this.baseUrl = this.url + "/";
|
||||
}
|
||||
this.pluginManager.init();
|
||||
|
||||
this.pluginManager.init();
|
||||
|
||||
if (!this.useBrowserHistory) {
|
||||
this.urls.push(url);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a timeout timer for loadUrl
|
||||
final CordovaWebView me = this;
|
||||
@@ -468,7 +461,7 @@ public class CordovaWebView extends WebView {
|
||||
if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
|
||||
LOG.d(TAG, ">>> loadUrlNow()");
|
||||
}
|
||||
if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || url.startsWith("javascript:") || Config.isUrlWhiteListed(url)) {
|
||||
if (url.startsWith("file://") || url.startsWith("javascript:") || Config.isUrlWhiteListed(url)) {
|
||||
super.loadUrl(url);
|
||||
}
|
||||
}
|
||||
@@ -484,7 +477,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
// If not first page of app, then load immediately
|
||||
// Add support for browser history if we use it.
|
||||
if ((url.startsWith("javascript:")) || this.urls.size() > 0 || this.canGoBack()) {
|
||||
if ((url.startsWith("javascript:")) || this.canGoBack()) {
|
||||
}
|
||||
|
||||
// If first page, then show splashscreen
|
||||
@@ -532,25 +525,6 @@ public class CordovaWebView extends WebView {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top url on the stack without removing it from
|
||||
* the stack.
|
||||
*/
|
||||
public String peekAtUrlStack() {
|
||||
if (this.urls.size() > 0) {
|
||||
return this.urls.peek();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a url to the stack
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
public void pushUrl(String url) {
|
||||
this.urls.push(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to previous page in history. (We manage our own history)
|
||||
@@ -561,37 +535,15 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
// Check webview first to see if there is a history
|
||||
// This is needed to support curPage#diffLink, since they are added to appView's history, but not our history url array (JQMobile behavior)
|
||||
if (super.canGoBack() && this.useBrowserHistory) {
|
||||
if (super.canGoBack()) {
|
||||
printBackForwardList();
|
||||
super.goBack();
|
||||
|
||||
return true;
|
||||
}
|
||||
// If our managed history has prev url
|
||||
else if (this.urls.size() > 1 && !this.useBrowserHistory) {
|
||||
this.urls.pop(); // Pop current url
|
||||
String url = this.urls.pop(); // Pop prev url that we want to load, since it will be added back by loadUrl()
|
||||
this.loadUrl(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is a history item.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean canGoBack() {
|
||||
if (super.canGoBack() && this.useBrowserHistory) {
|
||||
return true;
|
||||
}
|
||||
else if (this.urls.size() > 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the specified URL in the Cordova webview or a new browser instance.
|
||||
@@ -615,14 +567,8 @@ public class CordovaWebView extends WebView {
|
||||
if (!openExternal) {
|
||||
|
||||
// Make sure url is in whitelist
|
||||
if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || Config.isUrlWhiteListed(url)) {
|
||||
if (url.startsWith("file://") || Config.isUrlWhiteListed(url)) {
|
||||
// TODO: What about params?
|
||||
|
||||
// Clear out current url from history, since it will be replacing it
|
||||
if (clearHistory) {
|
||||
this.urls.clear();
|
||||
}
|
||||
|
||||
// Load new URL
|
||||
this.loadUrl(url);
|
||||
}
|
||||
@@ -659,13 +605,6 @@ public class CordovaWebView extends WebView {
|
||||
* <log level="DEBUG" />
|
||||
*/
|
||||
private void loadConfiguration() {
|
||||
// Config has already been loaded, and it stores these preferences on the Intent.
|
||||
if("false".equals(this.getProperty("useBrowserHistory", "true")))
|
||||
{
|
||||
//Switch back to the old browser history and state the six month policy
|
||||
this.useBrowserHistory = false;
|
||||
Log.w(TAG, "useBrowserHistory=false is deprecated as of Cordova 2.2.0 and will be removed six months after the 2.2.0 release. Please use the browser history and use history.back().");
|
||||
}
|
||||
|
||||
if ("true".equals(this.getProperty("fullscreen", "false"))) {
|
||||
this.cordova.getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
@@ -719,13 +658,20 @@ public class CordovaWebView extends WebView {
|
||||
}
|
||||
else if(keyCode == KeyEvent.KEYCODE_BACK)
|
||||
{
|
||||
//Because exit is fired on the keyDown and not the key up on Android 4.x
|
||||
//we need to check for this.
|
||||
//Also, I really wished "canGoBack" worked!
|
||||
if(this.useBrowserHistory)
|
||||
return !(this.startOfHistory()) || this.bound;
|
||||
else
|
||||
return this.urls.size() > 1 || this.bound;
|
||||
return !(this.startOfHistory()) || this.bound;
|
||||
}
|
||||
else if(keyCode == KeyEvent.KEYCODE_MENU)
|
||||
{
|
||||
//How did we get here? Is there a childView?
|
||||
View childView = this.getFocusedChild();
|
||||
if(childView != null)
|
||||
{
|
||||
//Make sure we close the keyboard if it's present
|
||||
InputMethodManager imm = (InputMethodManager) cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(childView.getWindowToken(), 0);
|
||||
cordova.getActivity().openOptionsMenu();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onKeyDown(keyCode, event);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
@@ -38,6 +39,7 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.webkit.HttpAuthHandler;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
|
||||
@@ -66,7 +68,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*
|
||||
* @param cordova
|
||||
* @param view
|
||||
*/
|
||||
@@ -77,7 +79,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*
|
||||
* @param view
|
||||
*/
|
||||
public void setWebView(CordovaWebView view) {
|
||||
@@ -101,8 +103,8 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
String callbackId = url.substring(idx3 + 1, idx4);
|
||||
String jsonArgs = url.substring(idx4 + 1);
|
||||
appView.pluginManager.exec(service, action, callbackId, jsonArgs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Give the host application a chance to take over the control when a new url
|
||||
* is about to be loaded in the current WebView.
|
||||
@@ -192,12 +194,8 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
// If our app or file:, then load into a new Cordova webview container by starting a new instance of our activity.
|
||||
// Our app continues to run. When BACK is pressed, our app is redisplayed.
|
||||
if (url.startsWith("file://") || url.startsWith("data:") || url.indexOf(this.appView.baseUrl) == 0 || Config.isUrlWhiteListed(url)) {
|
||||
//This will fix iFrames
|
||||
if (appView.useBrowserHistory || url.startsWith("data:"))
|
||||
return false;
|
||||
else
|
||||
this.appView.loadUrl(url);
|
||||
if (url.startsWith("file://") || url.startsWith("data:") || Config.isUrlWhiteListed(url)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If not our application, let default viewer handle
|
||||
@@ -214,6 +212,34 @@ 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
|
||||
@@ -230,7 +256,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
AuthenticationToken token = this.getAuthenticationToken(host, realm);
|
||||
if (token != null) {
|
||||
handler.proceed(token.getUserName(), token.getPassword());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Handle 401 like we'd normally do!
|
||||
super.onReceivedHttpAuthRequest(view, handler, host, realm);
|
||||
@@ -238,22 +264,16 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the host application that a page has started loading.
|
||||
* This method is called once for each main frame load so a page with iframes or framesets will call onPageStarted
|
||||
* one time for the main frame. This also means that onPageStarted will not be called when the contents of an
|
||||
* embedded frame changes, i.e. clicking a link whose target is an iframe.
|
||||
*
|
||||
* Notify the host application that a page has started loading.
|
||||
* This method is called once for each main frame load so a page with iframes or framesets will call onPageStarted
|
||||
* one time for the main frame. This also means that onPageStarted will not be called when the contents of an
|
||||
* embedded frame changes, i.e. clicking a link whose target is an iframe.
|
||||
*
|
||||
* @param view The webview initiating the callback.
|
||||
* @param url The url of the page.
|
||||
*/
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
// Clear history so history.back() doesn't do anything.
|
||||
// So we can reinit() native side CallbackServer & PluginManager.
|
||||
if (!this.appView.useBrowserHistory) {
|
||||
view.clearHistory();
|
||||
this.doClearHistory = true;
|
||||
}
|
||||
|
||||
// Flush stale messages.
|
||||
this.appView.jsMessageQueue.reset();
|
||||
@@ -270,7 +290,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
/**
|
||||
* Notify the host application that a page has finished loading.
|
||||
* This method is called only for main frame. When onPageFinished() is called, the rendering picture may not be updated yet.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param view The webview initiating the callback.
|
||||
* @param url The url of the page.
|
||||
@@ -359,11 +379,11 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the host application that an SSL error occurred while loading a resource.
|
||||
* The host application must call either handler.cancel() or handler.proceed().
|
||||
* Note that the decision may be retained for use in response to future SSL errors.
|
||||
* Notify the host application that an SSL error occurred while loading a resource.
|
||||
* The host application must call either handler.cancel() or handler.proceed().
|
||||
* Note that the decision may be retained for use in response to future SSL errors.
|
||||
* The default behavior is to cancel the load.
|
||||
*
|
||||
*
|
||||
* @param view The WebView that is initiating the callback.
|
||||
* @param handler An SslErrorHandler object that will handle the user's response.
|
||||
* @param error The SSL error object.
|
||||
@@ -392,27 +412,10 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the host application to update its visited links database.
|
||||
*
|
||||
* @param view The WebView that is initiating the callback.
|
||||
* @param url The url being visited.
|
||||
* @param isReload True if this url is being reloaded.
|
||||
*/
|
||||
@Override
|
||||
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
|
||||
/*
|
||||
* If you do a document.location.href the url does not get pushed on the stack
|
||||
* so we do a check here to see if the url should be pushed.
|
||||
*/
|
||||
if (!this.appView.peekAtUrlStack().equals(url)) {
|
||||
this.appView.pushUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication token.
|
||||
*
|
||||
*
|
||||
* @param authenticationToken
|
||||
* @param host
|
||||
* @param realm
|
||||
@@ -429,10 +432,10 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
/**
|
||||
* Removes the authentication token.
|
||||
*
|
||||
*
|
||||
* @param host
|
||||
* @param realm
|
||||
*
|
||||
*
|
||||
* @return the authentication token or null if did not exist
|
||||
*/
|
||||
public AuthenticationToken removeAuthenticationToken(String host, String realm) {
|
||||
@@ -441,16 +444,16 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
/**
|
||||
* Gets the authentication token.
|
||||
*
|
||||
*
|
||||
* In order it tries:
|
||||
* 1- host + realm
|
||||
* 2- host
|
||||
* 3- realm
|
||||
* 4- no host, no realm
|
||||
*
|
||||
*
|
||||
* @param host
|
||||
* @param realm
|
||||
*
|
||||
*
|
||||
* @return the authentication token
|
||||
*/
|
||||
public AuthenticationToken getAuthenticationToken(String host, String realm) {
|
||||
|
||||
@@ -38,7 +38,7 @@ import android.telephony.TelephonyManager;
|
||||
public class Device extends CordovaPlugin {
|
||||
public static final String TAG = "Device";
|
||||
|
||||
public static String cordovaVersion = "2.5.0"; // Cordova version
|
||||
public static String cordovaVersion = "2.6.0rc1"; // Cordova version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.apache.cordova.api.LOG;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
@@ -332,6 +333,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
* @param webViewClient
|
||||
* @param webChromeClient
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
|
||||
LOG.d(TAG, "DroidGap.init()");
|
||||
|
||||
@@ -349,6 +351,12 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
1.0F));
|
||||
|
||||
if (this.getBooleanProperty("disallowOverscroll", false)) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
|
||||
this.appView.setOverScrollMode(CordovaWebView.OVER_SCROLL_NEVER);
|
||||
}
|
||||
}
|
||||
|
||||
// Add web view but make it invisible while loading URL
|
||||
this.appView.setVisibility(View.INVISIBLE);
|
||||
this.root.addView(this.appView);
|
||||
@@ -1054,7 +1062,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
{
|
||||
//Get whatever has focus!
|
||||
View childView = appView.getFocusedChild();
|
||||
if ((appView.isCustomViewShowing() || childView != null ) && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if ((appView.isCustomViewShowing() || childView != null ) &&
|
||||
(keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
return appView.onKeyUp(keyCode, event);
|
||||
} else {
|
||||
return super.onKeyUp(keyCode, event);
|
||||
@@ -1074,7 +1083,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
//Get whatever has focus!
|
||||
View childView = appView.getFocusedChild();
|
||||
//Determine if the focus is on the current view or not
|
||||
if (childView != null && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (childView != null && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
return appView.onKeyDown(keyCode, event);
|
||||
}
|
||||
else
|
||||
|
||||
142
framework/src/org/apache/cordova/FileHelper.java
Normal file
142
framework/src/org/apache/cordova/FileHelper.java
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
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.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.LOG;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class FileHelper {
|
||||
private static final String LOG_TAG = "FileUtils";
|
||||
private static final String _DATA = "_data";
|
||||
|
||||
/**
|
||||
* Returns the real path of the given URI string.
|
||||
* If the given URI string represents a content:// URI, the real path is retrieved from the media store.
|
||||
*
|
||||
* @param uriString the URI string of the audio/image/video
|
||||
* @param cordova the current application context
|
||||
* @return the full path to the file
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static String getRealPath(String uriString, CordovaInterface cordova) {
|
||||
String realPath = null;
|
||||
|
||||
if (uriString.startsWith("content://")) {
|
||||
String[] proj = { _DATA };
|
||||
Cursor cursor = cordova.getActivity().managedQuery(Uri.parse(uriString), proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
||||
cursor.moveToFirst();
|
||||
realPath = cursor.getString(column_index);
|
||||
if (realPath == null) {
|
||||
LOG.e(LOG_TAG, "Could get real path for URI string %s", uriString);
|
||||
}
|
||||
} else if (uriString.startsWith("file://")) {
|
||||
realPath = uriString.substring(7);
|
||||
if (realPath.startsWith("/android_asset/")) {
|
||||
LOG.e(LOG_TAG, "Cannot get real path for URI string %s because it is a file:///android_asset/ URI.", uriString);
|
||||
realPath = null;
|
||||
}
|
||||
} else {
|
||||
realPath = uriString;
|
||||
}
|
||||
|
||||
return realPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the real path of the given URI.
|
||||
* If the given URI is a content:// URI, the real path is retrieved from the media store.
|
||||
*
|
||||
* @param uri the URI of the audio/image/video
|
||||
* @param cordova the current application context
|
||||
* @return the full path to the file
|
||||
*/
|
||||
public static String getRealPath(Uri uri, CordovaInterface cordova) {
|
||||
return FileHelper.getRealPath(uri.toString(), cordova);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input stream based on given URI string.
|
||||
*
|
||||
* @param uriString the URI string from which to obtain the input stream
|
||||
* @param cordova the current application context
|
||||
* @return an input stream into the data at the given URI or null if given an invalid URI string
|
||||
* @throws IOException
|
||||
*/
|
||||
public static InputStream getInputStreamFromUriString(String uriString, CordovaInterface cordova) throws IOException {
|
||||
if (uriString.startsWith("content")) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
return cordova.getActivity().getContentResolver().openInputStream(uri);
|
||||
} else if (uriString.startsWith("file:///android_asset/")) {
|
||||
String relativePath = uriString.substring(22);
|
||||
return cordova.getActivity().getAssets().open(relativePath);
|
||||
} else {
|
||||
return new FileInputStream(getRealPath(uriString, cordova));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the "file://" prefix from the given URI string, if applicable.
|
||||
* If the given URI string doesn't have a "file://" prefix, it is returned unchanged.
|
||||
*
|
||||
* @param uriString the URI string to operate on
|
||||
* @return a path without the "file://" prefix
|
||||
*/
|
||||
public static String stripFileProtocol(String uriString) {
|
||||
if (uriString.startsWith("file://")) {
|
||||
uriString = uriString.substring(7);
|
||||
}
|
||||
return uriString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime type of the data specified by the given URI string.
|
||||
*
|
||||
* @param uriString the URI string of the data
|
||||
* @return the mime type of the specified data
|
||||
*/
|
||||
public static String getMimeType(String uriString, CordovaInterface cordova) {
|
||||
String mimeType = null;
|
||||
|
||||
if (uriString.startsWith("content://")) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
mimeType = cordova.getActivity().getContentResolver().getType(uri);
|
||||
} else {
|
||||
// MimeTypeMap.getFileExtensionFromUrl has a bug that occurs when the filename has a space, so we encode it.
|
||||
// We also convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185).
|
||||
String encodedUriString = uriString.replace(" ", "%20").toLowerCase();
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(encodedUriString);
|
||||
if (extension.equals("3ga")) {
|
||||
mimeType = "audio/3gpp";
|
||||
} else {
|
||||
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
}
|
||||
}
|
||||
|
||||
return mimeType;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
@@ -27,7 +28,9 @@ 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;
|
||||
@@ -155,6 +158,25 @@ public class FileTransfer extends CordovaPlugin {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void addHeadersToRequest(URLConnection connection, JSONObject headers) {
|
||||
try {
|
||||
for (Iterator<?> iter = headers.keys(); iter.hasNext(); ) {
|
||||
String headerKey = iter.next().toString();
|
||||
JSONArray headerValues = headers.optJSONArray(headerKey);
|
||||
if (headerValues == null) {
|
||||
headerValues = new JSONArray();
|
||||
headerValues.put(headers.getString(headerKey));
|
||||
}
|
||||
connection.setRequestProperty(headerKey, headerValues.getString(0));
|
||||
for (int i = 1; i < headerValues.length(); ++i) {
|
||||
connection.addRequestProperty(headerKey, headerValues.getString(i));
|
||||
}
|
||||
}
|
||||
} catch (JSONException e1) {
|
||||
// No headers to be manipulated!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the specified file to the server URL provided using an HTTP multipart request.
|
||||
* @param source Full path of the file on the file system
|
||||
@@ -196,7 +218,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
try {
|
||||
url = new URL(target);
|
||||
} catch (MalformedURLException e) {
|
||||
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, 0);
|
||||
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
return;
|
||||
@@ -269,22 +291,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
// Handle the other headers
|
||||
if (headers != null) {
|
||||
try {
|
||||
for (Iterator<?> iter = headers.keys(); iter.hasNext(); ) {
|
||||
String headerKey = iter.next().toString();
|
||||
JSONArray headerValues = headers.optJSONArray(headerKey);
|
||||
if (headerValues == null) {
|
||||
headerValues = new JSONArray();
|
||||
headerValues.put(headers.getString(headerKey));
|
||||
}
|
||||
conn.setRequestProperty(headerKey, headerValues.getString(0));
|
||||
for (int i = 1; i < headerValues.length(); ++i) {
|
||||
conn.addRequestProperty(headerKey, headerValues.getString(i));
|
||||
}
|
||||
}
|
||||
} catch (JSONException e1) {
|
||||
// No headers to be manipulated!
|
||||
}
|
||||
addHeadersToRequest(conn, headers);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -530,18 +537,33 @@ public class FileTransfer extends CordovaPlugin {
|
||||
private static JSONObject createFileTransferError(int errorCode, String source, String target, URLConnection connection) {
|
||||
|
||||
int httpStatus = 0;
|
||||
|
||||
StringBuilder bodyBuilder = new StringBuilder();
|
||||
String body = null;
|
||||
if (connection != null) {
|
||||
try {
|
||||
if (connection instanceof HttpURLConnection) {
|
||||
httpStatus = ((HttpURLConnection)connection).getResponseCode();
|
||||
InputStream err = ((HttpURLConnection) connection).getErrorStream();
|
||||
if(err != null)
|
||||
{
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(err, "UTF-8"));
|
||||
String line = reader.readLine();
|
||||
while(line != null)
|
||||
{
|
||||
bodyBuilder.append(line);
|
||||
line = reader.readLine();
|
||||
if(line != null)
|
||||
bodyBuilder.append('\n');
|
||||
}
|
||||
body = bodyBuilder.toString();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(LOG_TAG, "Error getting HTTP status code from connection.", e);
|
||||
}
|
||||
}
|
||||
|
||||
return createFileTransferError(errorCode, source, target, httpStatus);
|
||||
return createFileTransferError(errorCode, source, target, body, httpStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -549,13 +571,17 @@ public class FileTransfer extends CordovaPlugin {
|
||||
* @param errorCode the error
|
||||
* @return JSONObject containing the error
|
||||
*/
|
||||
private static JSONObject createFileTransferError(int errorCode, String source, String target, Integer httpStatus) {
|
||||
private static JSONObject createFileTransferError(int errorCode, String source, String target, String body, Integer httpStatus) {
|
||||
JSONObject error = null;
|
||||
try {
|
||||
error = new JSONObject();
|
||||
error.put("code", errorCode);
|
||||
error.put("source", source);
|
||||
error.put("target", target);
|
||||
if(body != null)
|
||||
{
|
||||
error.put("body", body);
|
||||
}
|
||||
if (httpStatus != null) {
|
||||
error.put("http_status", httpStatus);
|
||||
}
|
||||
@@ -594,12 +620,13 @@ public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
final boolean trustEveryone = args.optBoolean(2);
|
||||
final String objectId = args.getString(3);
|
||||
final JSONObject headers = args.optJSONObject(4);
|
||||
|
||||
final URL url;
|
||||
try {
|
||||
url = new URL(source);
|
||||
} catch (MalformedURLException e) {
|
||||
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, 0);
|
||||
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
return;
|
||||
@@ -608,7 +635,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
if (!Config.isUrlWhiteListed(source)) {
|
||||
Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'");
|
||||
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, 401);
|
||||
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, null, 401);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
return;
|
||||
}
|
||||
@@ -671,6 +698,11 @@ public class FileTransfer extends CordovaPlugin {
|
||||
{
|
||||
connection.setRequestProperty("cookie", cookie);
|
||||
}
|
||||
|
||||
// Handle the other headers
|
||||
if (headers != null) {
|
||||
addHeadersToRequest(connection, headers);
|
||||
}
|
||||
|
||||
connection.connect();
|
||||
|
||||
@@ -718,8 +750,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
Log.d(LOG_TAG, "Saved file: " + target);
|
||||
|
||||
// create FileEntry object
|
||||
FileUtils fileUtil = new FileUtils();
|
||||
JSONObject fileEntry = fileUtil.getEntry(file);
|
||||
JSONObject fileEntry = FileUtils.getEntry(file);
|
||||
|
||||
result = new PluginResult(PluginResult.Status.OK, fileEntry);
|
||||
} catch (FileNotFoundException e) {
|
||||
@@ -826,7 +857,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
file.delete();
|
||||
}
|
||||
// Trigger the abort callback immediately to minimize latency between it and abort() being called.
|
||||
JSONObject error = createFileTransferError(ABORTED_ERR, context.source, context.target, -1);
|
||||
JSONObject error = createFileTransferError(ABORTED_ERR, context.source, context.target, null, -1);
|
||||
synchronized (context) {
|
||||
context.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
|
||||
context.aborted = true;
|
||||
|
||||
@@ -15,18 +15,17 @@
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.channels.FileChannel;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.apache.cordova.file.EncodingException;
|
||||
@@ -38,23 +37,25 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
//import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
//import android.app.Activity;
|
||||
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.RandomAccessFile;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
/**
|
||||
* This class provides SD card file and directory services to JavaScript.
|
||||
* Only files on the SD card can be accessed.
|
||||
*/
|
||||
public class FileUtils extends CordovaPlugin {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String LOG_TAG = "FileUtils";
|
||||
private static final String _DATA = "_data"; // The column name where the file path is stored
|
||||
|
||||
public static int NOT_FOUND_ERR = 1;
|
||||
public static int SECURITY_ERR = 2;
|
||||
@@ -75,9 +76,6 @@ public class FileUtils extends CordovaPlugin {
|
||||
public static int RESOURCE = 2;
|
||||
public static int APPLICATION = 3;
|
||||
|
||||
FileReader f_in;
|
||||
FileWriter f_out;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
@@ -111,30 +109,29 @@ public class FileUtils extends CordovaPlugin {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b));
|
||||
}
|
||||
else if (action.equals("readAsText")) {
|
||||
int start = 0;
|
||||
int end = Integer.MAX_VALUE;
|
||||
if (args.length() >= 3) {
|
||||
start = args.getInt(2);
|
||||
}
|
||||
if (args.length() >= 4) {
|
||||
end = args.getInt(3);
|
||||
}
|
||||
String encoding = args.getString(1);
|
||||
int start = args.getInt(2);
|
||||
int end = args.getInt(3);
|
||||
|
||||
String s = this.readAsText(args.getString(0), args.getString(1), start, end);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, s));
|
||||
this.readFileAs(args.getString(0), start, end, callbackContext, encoding, PluginResult.MESSAGE_TYPE_STRING);
|
||||
}
|
||||
else if (action.equals("readAsDataURL")) {
|
||||
int start = 0;
|
||||
int end = Integer.MAX_VALUE;
|
||||
if (args.length() >= 2) {
|
||||
start = args.getInt(1);
|
||||
}
|
||||
if (args.length() >= 3) {
|
||||
end = args.getInt(2);
|
||||
}
|
||||
int start = args.getInt(1);
|
||||
int end = args.getInt(2);
|
||||
|
||||
String s = this.readAsDataURL(args.getString(0), start, end);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, s));
|
||||
this.readFileAs(args.getString(0), start, end, callbackContext, null, -1);
|
||||
}
|
||||
else if (action.equals("readAsArrayBuffer")) {
|
||||
int start = args.getInt(1);
|
||||
int end = args.getInt(2);
|
||||
|
||||
this.readFileAs(args.getString(0), start, end, callbackContext, null, PluginResult.MESSAGE_TYPE_ARRAYBUFFER);
|
||||
}
|
||||
else if (action.equals("readAsBinaryString")) {
|
||||
int start = args.getInt(1);
|
||||
int end = args.getInt(2);
|
||||
|
||||
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));
|
||||
@@ -237,11 +234,11 @@ public class FileUtils extends CordovaPlugin {
|
||||
* @param filePath the path to check
|
||||
*/
|
||||
private void notifyDelete(String filePath) {
|
||||
String newFilePath = getRealPathFromURI(Uri.parse(filePath), cordova);
|
||||
String newFilePath = FileHelper.getRealPath(filePath, cordova);
|
||||
try {
|
||||
this.cordova.getActivity().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
MediaStore.Images.Media.DATA + " = ?",
|
||||
new String[] { newFilePath });
|
||||
MediaStore.Images.Media.DATA + " = ?",
|
||||
new String[] { newFilePath });
|
||||
} catch (UnsupportedOperationException t) {
|
||||
// Was seeing this on the File mobile-spec tests on 4.0.3 x86 emulator.
|
||||
// The ContentResolver applies only when the file was registered in the
|
||||
@@ -344,8 +341,8 @@ public class FileUtils extends CordovaPlugin {
|
||||
* @throws FileExistsException
|
||||
*/
|
||||
private JSONObject transferTo(String fileName, String newParent, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException {
|
||||
String newFileName = getRealPathFromURI(Uri.parse(fileName), cordova);
|
||||
newParent = getRealPathFromURI(Uri.parse(newParent), cordova);
|
||||
String newFileName = FileHelper.getRealPath(fileName, cordova);
|
||||
newParent = FileHelper.getRealPath(newParent, cordova);
|
||||
|
||||
// Check for invalid file name
|
||||
if (newName != null && newName.contains(":")) {
|
||||
@@ -384,14 +381,14 @@ public class FileUtils extends CordovaPlugin {
|
||||
}
|
||||
} else {
|
||||
if (move) {
|
||||
JSONObject newFileEntry = moveFile(source, destination);
|
||||
JSONObject newFileEntry = moveFile(source, destination);
|
||||
|
||||
// If we've moved a file given its content URI, we need to clean up.
|
||||
if (fileName.startsWith("content://")) {
|
||||
notifyDelete(fileName);
|
||||
}
|
||||
// If we've moved a file given its content URI, we need to clean up.
|
||||
if (fileName.startsWith("content://")) {
|
||||
notifyDelete(fileName);
|
||||
}
|
||||
|
||||
return newFileEntry;
|
||||
return newFileEntry;
|
||||
} else {
|
||||
return copyFile(source, destination);
|
||||
}
|
||||
@@ -748,7 +745,7 @@ public class FileUtils extends CordovaPlugin {
|
||||
if (fileName.startsWith("/")) {
|
||||
fp = new File(fileName);
|
||||
} else {
|
||||
dirPath = getRealPathFromURI(Uri.parse(dirPath), cordova);
|
||||
dirPath = FileHelper.getRealPath(dirPath, cordova);
|
||||
fp = new File(dirPath + File.separator + fileName);
|
||||
}
|
||||
return fp;
|
||||
@@ -763,7 +760,7 @@ public class FileUtils extends CordovaPlugin {
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getParent(String filePath) throws JSONException {
|
||||
filePath = getRealPathFromURI(Uri.parse(filePath), cordova);
|
||||
filePath = FileHelper.getRealPath(filePath, cordova);
|
||||
|
||||
if (atRootDirectory(filePath)) {
|
||||
return getEntry(filePath);
|
||||
@@ -779,7 +776,7 @@ public class FileUtils extends CordovaPlugin {
|
||||
* @return true if we are at the root, false otherwise.
|
||||
*/
|
||||
private boolean atRootDirectory(String filePath) {
|
||||
filePath = getRealPathFromURI(Uri.parse(filePath), cordova);
|
||||
filePath = FileHelper.getRealPath(filePath, cordova);
|
||||
|
||||
if (filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + cordova.getActivity().getPackageName() + "/cache") ||
|
||||
filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath()) ||
|
||||
@@ -789,19 +786,6 @@ public class FileUtils extends CordovaPlugin {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes the "file://" from the passed in filePath
|
||||
*
|
||||
* @param filePath to be checked.
|
||||
* @return
|
||||
*/
|
||||
public static String stripFileProtocol(String filePath) {
|
||||
if (filePath.startsWith("file://")) {
|
||||
filePath = filePath.substring(7);
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a File object from the passed in path
|
||||
*
|
||||
@@ -809,7 +793,7 @@ public class FileUtils extends CordovaPlugin {
|
||||
* @return
|
||||
*/
|
||||
private File createFileObject(String filePath) {
|
||||
filePath = getRealPathFromURI(Uri.parse(filePath), cordova);
|
||||
filePath = FileHelper.getRealPath(filePath, cordova);
|
||||
|
||||
File file = new File(filePath);
|
||||
return file;
|
||||
@@ -849,7 +833,7 @@ public class FileUtils extends CordovaPlugin {
|
||||
|
||||
JSONObject metadata = new JSONObject();
|
||||
metadata.put("size", file.length());
|
||||
metadata.put("type", getMimeType(filePath));
|
||||
metadata.put("type", FileHelper.getMimeType(filePath, cordova));
|
||||
metadata.put("name", file.getName());
|
||||
metadata.put("fullPath", filePath);
|
||||
metadata.put("lastModifiedDate", file.lastModified());
|
||||
@@ -900,21 +884,21 @@ public class FileUtils extends CordovaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON Object representing a directory on the device's file system
|
||||
* Returns a JSON object representing the given File.
|
||||
*
|
||||
* @param path to the directory
|
||||
* @return
|
||||
* @param file the File to convert
|
||||
* @return a JSON representation of the given File
|
||||
* @throws JSONException
|
||||
*/
|
||||
public JSONObject getEntry(File file) throws JSONException {
|
||||
public static JSONObject getEntry(File file) throws JSONException {
|
||||
JSONObject entry = new JSONObject();
|
||||
|
||||
entry.put("isFile", file.isFile());
|
||||
entry.put("isDirectory", file.isDirectory());
|
||||
entry.put("name", file.getName());
|
||||
entry.put("fullPath", "file://" + file.getAbsolutePath());
|
||||
// I can't add the next thing it as it would be an infinite loop
|
||||
//entry.put("filesystem", null);
|
||||
// The file system can't be specified, as it would lead to an infinite loop.
|
||||
// entry.put("filesystem", null);
|
||||
|
||||
return entry;
|
||||
}
|
||||
@@ -930,123 +914,83 @@ public class FileUtils extends CordovaPlugin {
|
||||
return getEntry(new File(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
*
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action) {
|
||||
if (action.equals("testSaveLocationExists")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("getFreeDiskSpace")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("testFileExists")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("testDirectoryExists")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Read content of text file.
|
||||
* Read the contents of a file.
|
||||
* This is done in a background thread; the result is sent to the callback.
|
||||
*
|
||||
* @param filename The name of the file.
|
||||
* @param encoding The encoding to return contents as. Typical value is UTF-8.
|
||||
* (see http://www.iana.org/assignments/character-sets)
|
||||
* @param start Start position in the file.
|
||||
* @param end End position to stop at (exclusive).
|
||||
* @return Contents of file.
|
||||
* @throws FileNotFoundException, IOException
|
||||
* @param filename The name of the file.
|
||||
* @param start Start position in the file.
|
||||
* @param end End position to stop at (exclusive).
|
||||
* @param callbackContext The context through which to send the result.
|
||||
* @param encoding The encoding to return contents as. Typical value is UTF-8. (see http://www.iana.org/assignments/character-sets)
|
||||
* @param resultType The desired type of data to send to the callback.
|
||||
* @return Contents of file.
|
||||
*/
|
||||
public String readAsText(String filename, String encoding, int start, int end) throws FileNotFoundException, IOException {
|
||||
int diff = end - start;
|
||||
byte[] bytes = new byte[1000];
|
||||
BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
int numRead = 0;
|
||||
public void readFileAs(final String filename, final int start, final int end, final CallbackContext callbackContext, final String encoding, final int resultType) {
|
||||
this.cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
byte[] bytes = readAsBinaryHelper(filename, start, end);
|
||||
|
||||
PluginResult result;
|
||||
switch (resultType) {
|
||||
case PluginResult.MESSAGE_TYPE_STRING:
|
||||
result = new PluginResult(PluginResult.Status.OK, new String(bytes, encoding));
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_ARRAYBUFFER:
|
||||
result = new PluginResult(PluginResult.Status.OK, bytes);
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_BINARYSTRING:
|
||||
result = new PluginResult(PluginResult.Status.OK, bytes, true);
|
||||
break;
|
||||
default: // Base64.
|
||||
String contentType = FileHelper.getMimeType(filename, cordova);
|
||||
byte[] base64 = Base64.encodeBase64(bytes);
|
||||
String s = "data:" + contentType + ";base64," + new String(base64, "US-ASCII");
|
||||
result = new PluginResult(PluginResult.Status.OK, s);
|
||||
}
|
||||
|
||||
if (start > 0) {
|
||||
bis.skip(start);
|
||||
}
|
||||
|
||||
while ( diff > 0 && (numRead = bis.read(bytes, 0, Math.min(1000, diff))) >= 0) {
|
||||
diff -= numRead;
|
||||
bos.write(bytes, 0, numRead);
|
||||
}
|
||||
|
||||
return new String(bos.toByteArray(), encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read content of text file and return as base64 encoded data url.
|
||||
*
|
||||
* @param filename The name of the file.
|
||||
* @return Contents of file = data:<media type>;base64,<data>
|
||||
* @throws FileNotFoundException, IOException
|
||||
*/
|
||||
public String readAsDataURL(String filename, int start, int end) throws FileNotFoundException, IOException {
|
||||
int diff = end - start;
|
||||
byte[] bytes = new byte[1000];
|
||||
BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
int numRead = 0;
|
||||
|
||||
if (start > 0) {
|
||||
bis.skip(start);
|
||||
}
|
||||
|
||||
while (diff > 0 && (numRead = bis.read(bytes, 0, Math.min(1000, diff))) >= 0) {
|
||||
diff -= numRead;
|
||||
bos.write(bytes, 0, numRead);
|
||||
}
|
||||
|
||||
// Determine content type from file name
|
||||
String contentType = null;
|
||||
if (filename.startsWith("content:")) {
|
||||
Uri fileUri = Uri.parse(filename);
|
||||
contentType = this.cordova.getActivity().getContentResolver().getType(fileUri);
|
||||
}
|
||||
else {
|
||||
contentType = getMimeType(filename);
|
||||
}
|
||||
|
||||
byte[] base64 = Base64.encodeBase64(bos.toByteArray());
|
||||
String data = "data:" + contentType + ";base64," + new String(base64);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the mime type of a given file name.
|
||||
*
|
||||
* @param filename
|
||||
* @return a mime type
|
||||
*/
|
||||
public static String getMimeType(String filename) {
|
||||
if (filename != null) {
|
||||
// Stupid bug in getFileExtensionFromUrl when the file name has a space
|
||||
// So we need to replace the space with a url encoded %20
|
||||
|
||||
// CB-2185: Stupid bug not putting JPG extension in the mime-type map
|
||||
String url = filename.replace(" ", "%20").toLowerCase();
|
||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
|
||||
if (extension.toLowerCase().equals("3ga")) {
|
||||
return "audio/3gpp";
|
||||
} else {
|
||||
return map.getMimeTypeFromExtension(extension);
|
||||
callbackContext.sendPluginResult(result);
|
||||
} catch (FileNotFoundException e) {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_FOUND_ERR));
|
||||
} catch (IOException e) {
|
||||
Log.d(LOG_TAG, e.getLocalizedMessage());
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_READABLE_ERR));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the contents of a file as binary.
|
||||
* This is done synchronously; the result is returned.
|
||||
*
|
||||
* @param filename The name of the file.
|
||||
* @param start Start position in the file.
|
||||
* @param end End position to stop at (exclusive).
|
||||
* @return Contents of the file as a byte[].
|
||||
* @throws IOException
|
||||
*/
|
||||
private byte[] readAsBinaryHelper(String filename, int start, int end) throws IOException {
|
||||
int numBytesToRead = end - start;
|
||||
byte[] bytes = new byte[numBytesToRead];
|
||||
InputStream inputStream = FileHelper.getInputStreamFromUriString(filename, cordova);
|
||||
int numBytesRead = 0;
|
||||
|
||||
if (start > 0) {
|
||||
inputStream.skip(start);
|
||||
}
|
||||
|
||||
while (numBytesToRead > 0 && (numBytesRead = inputStream.read(bytes, numBytesRead, numBytesToRead)) >= 0) {
|
||||
numBytesToRead -= numBytesRead;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1060,11 +1004,11 @@ public class FileUtils extends CordovaPlugin {
|
||||
*/
|
||||
/**/
|
||||
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");
|
||||
}
|
||||
if (filename.startsWith("content://")) {
|
||||
throw new NoModificationAllowedException("Couldn't write to file given its content URI");
|
||||
}
|
||||
|
||||
filename = getRealPathFromURI(Uri.parse(filename), cordova);
|
||||
filename = FileHelper.getRealPath(filename, cordova);
|
||||
|
||||
boolean append = false;
|
||||
if (offset > 0) {
|
||||
@@ -1093,11 +1037,11 @@ public class FileUtils extends CordovaPlugin {
|
||||
* @throws NoModificationAllowedException
|
||||
*/
|
||||
private long truncateFile(String filename, long size) throws FileNotFoundException, IOException, NoModificationAllowedException {
|
||||
if (filename.startsWith("content://")) {
|
||||
throw new NoModificationAllowedException("Couldn't truncate file given its content URI");
|
||||
}
|
||||
if (filename.startsWith("content://")) {
|
||||
throw new NoModificationAllowedException("Couldn't truncate file given its content URI");
|
||||
}
|
||||
|
||||
filename = getRealPathFromURI(Uri.parse(filename), cordova);
|
||||
filename = FileHelper.getRealPath(filename, cordova);
|
||||
|
||||
RandomAccessFile raf = new RandomAccessFile(filename, "rw");
|
||||
try {
|
||||
@@ -1112,48 +1056,4 @@ public class FileUtils extends CordovaPlugin {
|
||||
raf.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an input stream based on file path or content:// uri
|
||||
*
|
||||
* @param path
|
||||
* @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 {
|
||||
path = getRealPathFromURI(Uri.parse(path), cordova);
|
||||
return new FileInputStream(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the media store to find out what the file path is for the Uri we supply
|
||||
*
|
||||
* @param contentUri the Uri of the audio/image/video
|
||||
* @param cordova the current application context
|
||||
* @return the full path to the file
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected static String getRealPathFromURI(Uri contentUri, CordovaInterface cordova) {
|
||||
final String scheme = contentUri.getScheme();
|
||||
|
||||
if (scheme == null) {
|
||||
return contentUri.toString();
|
||||
} else if (scheme.compareTo("content") == 0) {
|
||||
String[] proj = { _DATA };
|
||||
Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(column_index);
|
||||
} else if (scheme.compareTo("file") == 0) {
|
||||
return contentUri.getPath();
|
||||
} else {
|
||||
return contentUri.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ public class GeoBroker extends CordovaPlugin {
|
||||
o.put("altitude", (loc.hasAltitude() ? loc.getAltitude() : null));
|
||||
o.put("accuracy", loc.getAccuracy());
|
||||
o.put("heading", (loc.hasBearing() ? (loc.hasSpeed() ? loc.getBearing() : null) : null));
|
||||
o.put("speed", loc.getSpeed());
|
||||
o.put("velocity", loc.getSpeed());
|
||||
o.put("timestamp", loc.getTime());
|
||||
} catch (JSONException e) {
|
||||
// TODO Auto-generated catch block
|
||||
|
||||
@@ -42,7 +42,7 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||
if(url.contains("?") || url.contains("#")){
|
||||
if(url.contains("?") || url.contains("#") || needsIceCreamSpaceInAssetUrlFix(url)){
|
||||
return generateWebResourceResponse(url);
|
||||
} else {
|
||||
return super.shouldInterceptRequest(view, url);
|
||||
@@ -80,4 +80,18 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean needsIceCreamSpaceInAssetUrlFix(String url) {
|
||||
if (!url.contains("%20")){
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(android.os.Build.VERSION.SDK_INT){
|
||||
case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH:
|
||||
case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ 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;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
@@ -48,6 +50,7 @@ import android.view.WindowManager.LayoutParams;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
@@ -69,6 +72,8 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
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 long MAX_QUOTA = 100 * 1024 * 1024;
|
||||
|
||||
private Dialog dialog;
|
||||
@@ -76,6 +81,7 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
private EditText edittext;
|
||||
private boolean showLocationBar = true;
|
||||
private CallbackContext callbackContext;
|
||||
private String buttonLabel = "Done";
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
@@ -145,6 +151,21 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
pluginResult.setKeepCallback(false);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
else if (action.equals("injectScriptCode")) {
|
||||
String source = args.getString(0);
|
||||
|
||||
org.json.JSONArray jsonEsc = new org.json.JSONArray();
|
||||
jsonEsc.put(source);
|
||||
String jsonRepr = jsonEsc.toString();
|
||||
String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
|
||||
String scriptEnclosure = "(function(d){var c=d.createElement('script');c.type='text/javascript';c.innerText="
|
||||
+ jsonSourceString
|
||||
+ ";d.getElementsByTagName('head')[0].appendChild(c);})(document)";
|
||||
this.inAppWebView.loadUrl("javascript:" + scriptEnclosure);
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
else {
|
||||
status = PluginResult.Status.INVALID_ACTION;
|
||||
}
|
||||
@@ -174,8 +195,12 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
option = new StringTokenizer(features.nextToken(), "=");
|
||||
if (option.hasMoreElements()) {
|
||||
String key = option.nextToken();
|
||||
Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE;
|
||||
map.put(key, value);
|
||||
if (key.equalsIgnoreCase(CLOSE_BUTTON_CAPTION)) {
|
||||
this.buttonLabel = option.nextToken();
|
||||
} else {
|
||||
Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE;
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
@@ -221,6 +246,7 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
*/
|
||||
private void closeDialog() {
|
||||
try {
|
||||
this.inAppWebView.loadUrl("about:blank");
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("type", EXIT_EVENT);
|
||||
|
||||
@@ -289,7 +315,10 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
// Determine if we should hide the location bar.
|
||||
showLocationBar = true;
|
||||
if (features != null) {
|
||||
showLocationBar = features.get(LOCATION).booleanValue();
|
||||
Boolean show = features.get(LOCATION);
|
||||
if (show != null) {
|
||||
showLocationBar = show.booleanValue();
|
||||
}
|
||||
}
|
||||
|
||||
final CordovaWebView thatWebView = this.webView;
|
||||
@@ -405,7 +434,7 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
close.setLayoutParams(closeLayoutParams);
|
||||
forward.setContentDescription("Close Button");
|
||||
close.setId(5);
|
||||
close.setText("Done");
|
||||
close.setText(buttonLabel);
|
||||
close.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
closeDialog();
|
||||
@@ -428,10 +457,18 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
*/
|
||||
// @TODO: replace with settings.setPluginState(android.webkit.WebSettings.PluginState.ON)
|
||||
settings.setPluginsEnabled(true);
|
||||
settings.setDatabaseEnabled(true);
|
||||
String databasePath = cordova.getActivity().getApplicationContext().getDir("inAppBrowserDB", Context.MODE_PRIVATE).getPath();
|
||||
settings.setDatabasePath(databasePath);
|
||||
|
||||
//Toggle whether this is enabled or not!
|
||||
Bundle appSettings = cordova.getActivity().getIntent().getExtras();
|
||||
boolean enableDatabase = appSettings == null ? true : appSettings.getBoolean("InAppBrowserStorageEnabled", true);
|
||||
if(enableDatabase)
|
||||
{
|
||||
String databasePath = cordova.getActivity().getApplicationContext().getDir("inAppBrowserDB", Context.MODE_PRIVATE).getPath();
|
||||
settings.setDatabasePath(databasePath);
|
||||
settings.setDatabaseEnabled(true);
|
||||
}
|
||||
settings.setDomStorageEnabled(true);
|
||||
|
||||
inAppWebView.loadUrl(url);
|
||||
inAppWebView.setId(6);
|
||||
inAppWebView.getSettings().setLoadWithOverviewMode(true);
|
||||
@@ -472,16 +509,24 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new plugin result and send it back to JavaScript
|
||||
* Create a new plugin success result and send it back to JavaScript
|
||||
*
|
||||
* @param obj a JSONObject contain event payload information
|
||||
*/
|
||||
private void sendUpdate(JSONObject obj, boolean keepCallback) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, obj);
|
||||
sendUpdate(obj, keepCallback, PluginResult.Status.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new plugin result and send it back to JavaScript
|
||||
*
|
||||
* @param obj a JSONObject contain event payload information
|
||||
* @param status the status code to return to the JavaScript environment
|
||||
*/ private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) {
|
||||
PluginResult result = new PluginResult(status, obj);
|
||||
result.setKeepCallback(keepCallback);
|
||||
this.callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
public class InAppChromeClient extends WebChromeClient {
|
||||
|
||||
/**
|
||||
@@ -514,6 +559,18 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -543,10 +600,62 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
super.onPageStarted(view, url, favicon);
|
||||
String newloc;
|
||||
String newloc = "";
|
||||
if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
|
||||
newloc = url;
|
||||
} else {
|
||||
}
|
||||
// If dialing phone (tel:5551212)
|
||||
else if (url.startsWith(WebView.SCHEME_TEL)) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_DIAL);
|
||||
intent.setData(Uri.parse(url));
|
||||
cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(LOG_TAG, "Error with " + url + ": " + e.toString());
|
||||
}
|
||||
}
|
||||
// If sms:5551212?body=This is the message
|
||||
else if (url.startsWith("sms:")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
|
||||
// Get address
|
||||
String address = null;
|
||||
int parmIndex = url.indexOf('?');
|
||||
if (parmIndex == -1) {
|
||||
address = url.substring(4);
|
||||
}
|
||||
else {
|
||||
address = url.substring(4, parmIndex);
|
||||
|
||||
// If body, then set sms body
|
||||
Uri uri = Uri.parse(url);
|
||||
String query = uri.getQuery();
|
||||
if (query != null) {
|
||||
if (query.startsWith("body=")) {
|
||||
intent.putExtra("sms_body", query.substring(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
intent.setData(Uri.parse("sms:" + address));
|
||||
intent.putExtra("address", address);
|
||||
intent.setType("vnd.android-dir/mms-sms");
|
||||
cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
|
||||
}
|
||||
}
|
||||
else {
|
||||
newloc = "http://" + url;
|
||||
}
|
||||
|
||||
@@ -578,5 +687,22 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
Log.d(LOG_TAG, "Should never happen");
|
||||
}
|
||||
}
|
||||
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
||||
super.onReceivedError(view, errorCode, description, failingUrl);
|
||||
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("type", LOAD_ERROR_EVENT);
|
||||
obj.put("url", failingUrl);
|
||||
obj.put("code", errorCode);
|
||||
obj.put("message", description);
|
||||
|
||||
sendUpdate(obj, true, PluginResult.Status.ERROR);
|
||||
} catch (JSONException ex) {
|
||||
Log.d(LOG_TAG, "Should never happen");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
framework/src/org/apache/cordova/JSONUtils.java
Normal file
24
framework/src/org/apache/cordova/JSONUtils.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class JSONUtils {
|
||||
public static List<String> toStringList(JSONArray array) throws JSONException {
|
||||
if(array == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
List<String> list = new ArrayList<String>();
|
||||
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
list.add(array.get(i).toString());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -409,6 +409,9 @@ public class NativeToJsMessageQueue {
|
||||
case PluginResult.MESSAGE_TYPE_STRING: // s
|
||||
ret += 1 + pluginResult.getStrMessage().length();
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_BINARYSTRING:
|
||||
ret += 1 + pluginResult.getMessage().length();
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_ARRAYBUFFER:
|
||||
ret += 1 + pluginResult.getMessage().length();
|
||||
break;
|
||||
@@ -451,7 +454,11 @@ public class NativeToJsMessageQueue {
|
||||
sb.append('s');
|
||||
sb.append(pluginResult.getStrMessage());
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_ARRAYBUFFER:
|
||||
case PluginResult.MESSAGE_TYPE_BINARYSTRING: // S
|
||||
sb.append('S');
|
||||
sb.append(pluginResult.getMessage());
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_ARRAYBUFFER: // A
|
||||
sb.append('A');
|
||||
sb.append(pluginResult.getMessage());
|
||||
break;
|
||||
@@ -473,9 +480,9 @@ public class NativeToJsMessageQueue {
|
||||
.append(success)
|
||||
.append(",")
|
||||
.append(status)
|
||||
.append(",")
|
||||
.append(",[")
|
||||
.append(pluginResult.getMessage())
|
||||
.append(",")
|
||||
.append("],")
|
||||
.append(pluginResult.getKeepCallback())
|
||||
.append(");");
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
@@ -32,6 +33,7 @@ import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Vibrator;
|
||||
import android.widget.EditText;
|
||||
|
||||
/**
|
||||
* This class provides access to notifications on the device.
|
||||
@@ -68,7 +70,11 @@ public class Notification extends CordovaPlugin {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("confirm")) {
|
||||
this.confirm(args.getString(0), args.getString(1), args.getString(2), callbackContext);
|
||||
this.confirm(args.getString(0), args.getString(1), args.getJSONArray(2), callbackContext);
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("prompt")) {
|
||||
this.prompt(args.getString(0), args.getString(1), args.getJSONArray(2), callbackContext);
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("activityStart")) {
|
||||
@@ -170,7 +176,7 @@ public class Notification extends CordovaPlugin {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
};
|
||||
@@ -188,10 +194,9 @@ public class Notification extends CordovaPlugin {
|
||||
* @param buttonLabels A comma separated list of button labels (Up to 3 buttons)
|
||||
* @param callbackContext The callback context.
|
||||
*/
|
||||
public synchronized void confirm(final String message, final String title, String buttonLabels, final CallbackContext callbackContext) {
|
||||
public synchronized void confirm(final String message, final String title, final JSONArray buttonLabels, final CallbackContext callbackContext) {
|
||||
|
||||
final CordovaInterface cordova = this.cordova;
|
||||
final String[] fButtons = buttonLabels.split(",");
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
@@ -201,37 +206,43 @@ public class Notification extends CordovaPlugin {
|
||||
dlg.setCancelable(true);
|
||||
|
||||
// First button
|
||||
if (fButtons.length > 0) {
|
||||
dlg.setNegativeButton(fButtons[0],
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 1));
|
||||
}
|
||||
});
|
||||
if (buttonLabels.length() > 0) {
|
||||
try {
|
||||
dlg.setNegativeButton(buttonLabels.getString(0),
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 1));
|
||||
}
|
||||
});
|
||||
} catch (JSONException e) { }
|
||||
}
|
||||
|
||||
// Second button
|
||||
if (fButtons.length > 1) {
|
||||
dlg.setNeutralButton(fButtons[1],
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 2));
|
||||
}
|
||||
});
|
||||
if (buttonLabels.length() > 1) {
|
||||
try {
|
||||
dlg.setNeutralButton(buttonLabels.getString(1),
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 2));
|
||||
}
|
||||
});
|
||||
} catch (JSONException e) { }
|
||||
}
|
||||
|
||||
// Third button
|
||||
if (fButtons.length > 2) {
|
||||
dlg.setPositiveButton(fButtons[2],
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 3));
|
||||
}
|
||||
}
|
||||
);
|
||||
if (buttonLabels.length() > 2) {
|
||||
try {
|
||||
dlg.setPositiveButton(buttonLabels.getString(2),
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 3));
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (JSONException e) { }
|
||||
}
|
||||
dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog)
|
||||
@@ -248,6 +259,104 @@ public class Notification extends CordovaPlugin {
|
||||
this.cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and shows a native Android prompt dialog with given title, message, buttons.
|
||||
* This dialog only shows up to 3 buttons. Any labels after that will be ignored.
|
||||
* The following results are returned to the JavaScript callback identified by callbackId:
|
||||
* buttonIndex Index number of the button selected
|
||||
* input1 The text entered in the prompt dialog box
|
||||
*
|
||||
* @param message The message the dialog should display
|
||||
* @param title The title of the dialog
|
||||
* @param buttonLabels A comma separated list of button labels (Up to 3 buttons)
|
||||
* @param callbackContext The callback context.
|
||||
*/
|
||||
public synchronized void prompt(final String message, final String title, final JSONArray buttonLabels, final CallbackContext callbackContext) {
|
||||
|
||||
final CordovaInterface cordova = this.cordova;
|
||||
final EditText promptInput = new EditText(cordova.getActivity());
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(cordova.getActivity());
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle(title);
|
||||
dlg.setCancelable(true);
|
||||
|
||||
dlg.setView(promptInput);
|
||||
|
||||
final JSONObject result = new JSONObject();
|
||||
|
||||
// First button
|
||||
if (buttonLabels.length() > 0) {
|
||||
try {
|
||||
dlg.setNegativeButton(buttonLabels.getString(0),
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
try {
|
||||
result.put("buttonIndex",1);
|
||||
result.put("input1", promptInput.getText());
|
||||
} catch (JSONException e) { e.printStackTrace(); }
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
|
||||
}
|
||||
});
|
||||
} catch (JSONException e) { }
|
||||
}
|
||||
|
||||
// Second button
|
||||
if (buttonLabels.length() > 1) {
|
||||
try {
|
||||
dlg.setNeutralButton(buttonLabels.getString(1),
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
try {
|
||||
result.put("buttonIndex",2);
|
||||
result.put("input1", promptInput.getText());
|
||||
} catch (JSONException e) { e.printStackTrace(); }
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
|
||||
}
|
||||
});
|
||||
} catch (JSONException e) { }
|
||||
}
|
||||
|
||||
// Third button
|
||||
if (buttonLabels.length() > 2) {
|
||||
try {
|
||||
dlg.setPositiveButton(buttonLabels.getString(2),
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
try {
|
||||
result.put("buttonIndex",3);
|
||||
result.put("input1", promptInput.getText());
|
||||
} catch (JSONException e) { e.printStackTrace(); }
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (JSONException e) { }
|
||||
}
|
||||
dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog)
|
||||
{
|
||||
dialog.dismiss();
|
||||
try {
|
||||
result.put("buttonIndex",0);
|
||||
result.put("input1", promptInput.getText());
|
||||
} catch (JSONException e) { e.printStackTrace(); }
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
|
||||
}
|
||||
});
|
||||
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
|
||||
};
|
||||
};
|
||||
this.cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
/**
|
||||
* Show the spinner.
|
||||
*
|
||||
|
||||
@@ -79,6 +79,15 @@ public class CallbackContext {
|
||||
public void success(byte[] message) {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.OK, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
*/
|
||||
public void success(int message) {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.OK, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
|
||||
@@ -22,7 +22,12 @@ import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebResourceResponse;
|
||||
|
||||
/**
|
||||
* Plugins must extend this class and override one of the execute methods.
|
||||
@@ -150,7 +155,7 @@ public class CordovaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* By specifying a <url-filter> in plugins.xml you can map a URL (using startsWith atm) to this method.
|
||||
* By specifying a <url-filter> in config.xml you can map a URL (using startsWith atm) to this method.
|
||||
*
|
||||
* @param url The URL that is trying to be loaded in the Cordova webview.
|
||||
* @return Return true to prevent the URL from loading. Default is false.
|
||||
@@ -159,6 +164,17 @@ public class CordovaPlugin {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public WebResourceResponse shouldInterceptRequest(String url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the WebView does a top-level navigation or refreshes.
|
||||
*
|
||||
|
||||
@@ -30,6 +30,9 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import android.content.Intent;
|
||||
import android.content.res.XmlResourceParser;
|
||||
|
||||
import android.util.Log;
|
||||
import android.webkit.WebResourceResponse;
|
||||
|
||||
/**
|
||||
* PluginManager is exposed to JavaScript in the Cordova WebView.
|
||||
*
|
||||
@@ -118,7 +121,7 @@ public class PluginManager {
|
||||
// System.out.println("Plugin: "+name+" => "+value);
|
||||
onload = "true".equals(xml.getAttributeValue(null, "onload"));
|
||||
entry = new PluginEntry(service, pluginClass, onload);
|
||||
this.addService(entry);
|
||||
this.addService(entry);
|
||||
}
|
||||
//What is this?
|
||||
else if (strNode.equals("url-filter")) {
|
||||
@@ -211,6 +214,7 @@ public class PluginManager {
|
||||
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 true;
|
||||
@@ -366,6 +370,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.
|
||||
*/
|
||||
|
||||
@@ -71,11 +71,15 @@ public class PluginResult {
|
||||
}
|
||||
|
||||
public PluginResult(Status status, byte[] data) {
|
||||
this.status = status.ordinal();
|
||||
this.messageType = MESSAGE_TYPE_ARRAYBUFFER;
|
||||
this.encodedMessage = Base64.encodeToString(data, Base64.NO_WRAP);
|
||||
this(status, data, false);
|
||||
}
|
||||
|
||||
public PluginResult(Status status, byte[] data, boolean binaryString) {
|
||||
this.status = status.ordinal();
|
||||
this.messageType = binaryString ? MESSAGE_TYPE_BINARYSTRING : MESSAGE_TYPE_ARRAYBUFFER;
|
||||
this.encodedMessage = Base64.encodeToString(data, Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
public void setKeepCallback(boolean b) {
|
||||
this.keepCallback = b;
|
||||
}
|
||||
@@ -143,6 +147,9 @@ public class PluginResult {
|
||||
public static final int MESSAGE_TYPE_BOOLEAN = 4;
|
||||
public static final int MESSAGE_TYPE_NULL = 5;
|
||||
public static final int MESSAGE_TYPE_ARRAYBUFFER = 6;
|
||||
// Use BINARYSTRING when your string may contain null characters.
|
||||
// This is required to work around a bug in the platform :(.
|
||||
public static final int MESSAGE_TYPE_BINARYSTRING = 7;
|
||||
|
||||
public static String[] StatusMessages = new String[] {
|
||||
"No result",
|
||||
|
||||
Reference in New Issue
Block a user