forked from github/cordova-android
Compare commits
119 Commits
2.9.0
...
CheckIn_no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7be1f018aa | ||
|
|
437daa368a | ||
|
|
3df09eacf2 | ||
|
|
483dd3435a | ||
|
|
2b0aa03380 | ||
|
|
fc7261b199 | ||
|
|
3200c50b9f | ||
|
|
862a8eab8d | ||
|
|
1bfcc92677 | ||
|
|
80a09b8f36 | ||
|
|
5f7ce4b868 | ||
|
|
412d97fa0c | ||
|
|
57bed98cf1 | ||
|
|
b1d8788506 | ||
|
|
1bd4900981 | ||
|
|
e6812f18a0 | ||
|
|
f604988181 | ||
|
|
150cdfd3ac | ||
|
|
f42b34d6b4 | ||
|
|
166b35bc6c | ||
|
|
121b74fa0c | ||
|
|
5451320350 | ||
|
|
fe45b29ef6 | ||
|
|
4e1aa8aa59 | ||
|
|
fa366eb7a8 | ||
|
|
e086a706ba | ||
|
|
53b8da8198 | ||
|
|
c2c5f71018 | ||
|
|
2bdc849c2b | ||
|
|
7cbe8f5843 | ||
|
|
810df61049 | ||
|
|
5c38101a9e | ||
|
|
b4236b9783 | ||
|
|
980c4699b0 | ||
|
|
463c7b5027 | ||
|
|
7c7230dd35 | ||
|
|
b915aafb5b | ||
|
|
8202ab83fa | ||
|
|
2fe6d14e56 | ||
|
|
7ee285342f | ||
|
|
6fe18ae0ab | ||
|
|
5c4fa213d1 | ||
|
|
1895d8ac9e | ||
|
|
7f5f87ac72 | ||
|
|
0244341ac5 | ||
|
|
ae96d5678b | ||
|
|
a9ebf50b86 | ||
|
|
8d8cbf6e28 | ||
|
|
77e9092108 | ||
|
|
210d7c76e6 | ||
|
|
01ad3f447a | ||
|
|
546948cb8c | ||
|
|
37c20c3f67 | ||
|
|
22daea11e1 | ||
|
|
792aa78aec | ||
|
|
5cf95cfa01 | ||
|
|
fff956304f | ||
|
|
3ae28b3085 | ||
|
|
b5df9dd88e | ||
|
|
b6a01a203a | ||
|
|
990d91360d | ||
|
|
b5c3ac605a | ||
|
|
55865a4f1d | ||
|
|
ff27ad332b | ||
|
|
85bad87c1c | ||
|
|
f7014f14df | ||
|
|
99341bce29 | ||
|
|
bf6291020a | ||
|
|
79829f6209 | ||
|
|
a97487bcec | ||
|
|
892ffc8ce4 | ||
|
|
fbf7f1c3f9 | ||
|
|
3b7e0504e8 | ||
|
|
68bc57ae85 | ||
|
|
f42e5f66fd | ||
|
|
10d31ea0a3 | ||
|
|
e64ebdfaee | ||
|
|
7e15ff930d | ||
|
|
b83610a653 | ||
|
|
16e08384c0 | ||
|
|
9cb14838e8 | ||
|
|
6fe66ad79c | ||
|
|
f78b444ed1 | ||
|
|
9946d15f23 | ||
|
|
b032dcca16 | ||
|
|
01d6ae55a4 | ||
|
|
f1b377bf07 | ||
|
|
03c5208d47 | ||
|
|
3ace9348f6 | ||
|
|
3a14017729 | ||
|
|
e726c0d60b | ||
|
|
98b971b4de | ||
|
|
4ec3d6d064 | ||
|
|
c531d97ed2 | ||
|
|
8c9e416175 | ||
|
|
63ab701685 | ||
|
|
c8140bad19 | ||
|
|
2e5b6bce55 | ||
|
|
5ee178789f | ||
|
|
d845736948 | ||
|
|
dd837f7130 | ||
|
|
12ebadbb0f | ||
|
|
e30bc6b6e4 | ||
|
|
9288158226 | ||
|
|
3a55991480 | ||
|
|
9e44596db7 | ||
|
|
9aaa152b9a | ||
|
|
a2b8ebf57e | ||
|
|
b3fe47985a | ||
|
|
98d9901693 | ||
|
|
75f358d01e | ||
|
|
f7c97cb1d0 | ||
|
|
7c22bc74bc | ||
|
|
dfd668d145 | ||
|
|
dbc6dd73f3 | ||
|
|
4765c6fcc5 | ||
|
|
78dd084303 | ||
|
|
cd9fb9b709 | ||
|
|
adcbd879c8 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,7 +20,6 @@ example
|
||||
test/bin
|
||||
test/assets/www/.tmp*
|
||||
tmp/**
|
||||
bin/node_modules
|
||||
.metadata
|
||||
tmp/**/*
|
||||
Thumbs.db
|
||||
|
||||
44
README.md
44
README.md
@@ -41,49 +41,21 @@ Cordova Android Developer Tools
|
||||
|
||||
The Cordova developer tooling is split between general tooling and project level tooling.
|
||||
|
||||
To enable the command-line tools available in the ./bin directory, make
|
||||
sure you have all of the dependencies installed. You will need
|
||||
[NodeJS](http://nodejs.org) (which should come with `npm`). To install
|
||||
the dependencies:
|
||||
|
||||
$ cd bin
|
||||
$ npm install
|
||||
|
||||
General Commands
|
||||
|
||||
./bin/create [path package activity] ... create the ./example app or a cordova android project
|
||||
./bin/autotest ......................... test the cli tools
|
||||
./bin/test ............................. run mobile-spec
|
||||
./bin/check_reqs ....................... checks that your environment is set up for cordova-android development
|
||||
./bin/update [path] .................... updates an existing cordova-android project to the version of the framework
|
||||
|
||||
Project Commands
|
||||
|
||||
These commands live in a generated Cordova Android project.
|
||||
These commands live in a generated Cordova Android project. Any interactions with the emulator require you to have an AVD defined.
|
||||
|
||||
./cordova/debug [path] ..................... install to first device
|
||||
./cordova/emulate .......................... start avd (emulator) named default
|
||||
./cordova/log .............................. starts logcat
|
||||
|
||||
Running the Example Project
|
||||
---
|
||||
|
||||
Start avd (emulator) named `default`:
|
||||
|
||||
./bin/emulate
|
||||
|
||||
Create the example project and build it to the first device:
|
||||
|
||||
./bin/create
|
||||
cd example
|
||||
./cordova/debug
|
||||
|
||||
Start adb logcat (console.log calls output here):
|
||||
|
||||
./cordova/log
|
||||
|
||||
Creating a new Cordova Android Project
|
||||
---
|
||||
|
||||
./bin/create ~/Desktop/myapp com.myapp.special MyApp
|
||||
./cordova/clean ........................ cleans the project
|
||||
./cordova/build ........................ calls `clean` then compiles the project
|
||||
./cordova/log ........................ stream device or emulate logs to stdout
|
||||
./cordova/run ........................ calls `build` then deploys to a connected Android device. If no Android device is detected, will launch an emulator and deploy to it.
|
||||
./cordova/version ...................... returns the cordova-android version of the current project
|
||||
|
||||
Importing a Cordova Android Project into Eclipse
|
||||
----
|
||||
|
||||
@@ -1,34 +1,27 @@
|
||||
#! /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.
|
||||
#
|
||||
ROOT="$( cd "$( dirname "$0" )/.." && pwd )"
|
||||
cmd=`android list target`
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "The command `android` failed. Make sure you have the latest Android SDK installed, and the `android` command (inside the tools/ folder) added to your path."
|
||||
exit 2
|
||||
elif [[ ! $cmd =~ "android-17" ]]; then
|
||||
echo "Please install Android target 17 (the Android 4.2 SDK). Make sure you have the latest Android tools installed as well. Run `android` from your command-line to install/update any missing SDKs or tools."
|
||||
exit 2
|
||||
else
|
||||
cmd="android update project -p $ROOT -t android-17 1> /dev/null 2>&1"
|
||||
eval $cmd
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "Error updating the Cordova library to work with your Android environment."
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var check_reqs = require('./lib/check_reqs');
|
||||
|
||||
if(!check_reqs.run()) {
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
SET full_path=%~dp0
|
||||
IF EXIST %full_path%check_reqs.js (
|
||||
cscript "%full_path%check_reqs.js" //nologo
|
||||
SET script_path="%~dp0check_reqs"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'check_reqs.js' in 'bin' folder, aborting...>&2
|
||||
ECHO ERROR: Could not find 'check_reqs' script in 'bin' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
var ROOT = WScript.ScriptFullName.split('\\bin\\check_reqs.js').join(''),
|
||||
shell = WScript.CreateObject("WScript.Shell"),
|
||||
fso = WScript.CreateObject('Scripting.FileSystemObject');
|
||||
|
||||
|
||||
// executes a command in the shell, returns stdout or stderr if error
|
||||
function exec_out(command) {
|
||||
var oExec=shell.Exec(command);
|
||||
var output = new String();
|
||||
while (oExec.Status == 0) {
|
||||
if (!oExec.StdOut.AtEndOfStream) {
|
||||
var line = oExec.StdOut.ReadAll();
|
||||
// XXX: Change to verbose mode
|
||||
// WScript.StdOut.WriteLine(line);
|
||||
output += line;
|
||||
}
|
||||
WScript.sleep(100);
|
||||
}
|
||||
//Check to make sure our scripts did not encounter an error
|
||||
if (!oExec.StdErr.AtEndOfStream) {
|
||||
var line = oExec.StdErr.ReadAll();
|
||||
return {'error' : true, 'output' : line};
|
||||
} else if (!oExec.StdOut.AtEndOfStream) {
|
||||
var line = oExec.StdOut.ReadAll();
|
||||
// XXX: Change to verbose mode
|
||||
// WScript.StdOut.WriteLine(line);
|
||||
output += line;
|
||||
}
|
||||
return {'error' : false, 'output' : output};
|
||||
}
|
||||
|
||||
// log to stdout or stderr
|
||||
function Log(msg, error) {
|
||||
if (error) {
|
||||
WScript.StdErr.WriteLine(msg);
|
||||
}
|
||||
else {
|
||||
WScript.StdOut.WriteLine(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// checks that android requirements are met
|
||||
function check_requirements() {
|
||||
var result = exec_out('%comspec% /c android list target');
|
||||
if(result.error) {
|
||||
Log('The command `android` failed. Make sure you have the latest Android SDK installed, and the `android` command (inside the tools/ folder) added to your path. Output: ' + result.output, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else if(!result.output.match(/android[-]17/)) {
|
||||
Log('Please install Android target 17 (the Android 4.2 SDK). Make sure you have the latest Android tools installed as well. Run `android` from your command-line to install/update any missing SDKs or tools.', true);
|
||||
Log('Output : ' + result.output);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else {
|
||||
var cmd = '%comspec% /c android update project -p ' + ROOT + '\\framework -t android-17';
|
||||
result = exec_out(cmd);
|
||||
if(result.error) {
|
||||
Log('Error updating the Cordova library to work with your Android environment. Command run: "' + cmd + '", output: ' + result.output, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check_requirements();
|
||||
173
bin/create
173
bin/create
@@ -1,153 +1,32 @@
|
||||
#! /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.
|
||||
#
|
||||
# create a cordova/android project
|
||||
#
|
||||
# USAGE
|
||||
# ./create [path package activity]
|
||||
#
|
||||
set -e
|
||||
#!/usr/bin/env node
|
||||
|
||||
if [ -z "$1" ] || [ "$1" == "-h" ]
|
||||
then
|
||||
echo "Usage: $0 <path_to_new_project> <package_name> <project_name>"
|
||||
echo "Make sure the Android SDK tools folder is in your PATH!"
|
||||
echo " <path_to_new_project>: Path to your new Cordova iOS project"
|
||||
echo " <package_name>: Package name, following reverse-domain style convention"
|
||||
echo " <project_name>: Project name"
|
||||
exit 0
|
||||
fi
|
||||
/*
|
||||
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
|
||||
|
||||
BUILD_PATH="$( cd "$( dirname "$0" )/.." && pwd )"
|
||||
VERSION=$(cat "$BUILD_PATH"/VERSION)
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
PROJECT_PATH="${1:-'./example'}"
|
||||
PACKAGE=${2:-"org.apache.cordova.example"}
|
||||
ACTIVITY=${3:-"cordovaExample"}
|
||||
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.
|
||||
*/
|
||||
var path = require('path');
|
||||
var args = process.argv;
|
||||
var create = require('./lib/create');
|
||||
|
||||
# clobber any existing example
|
||||
if [ -d "$PROJECT_PATH" ]
|
||||
then
|
||||
echo "Project already exists! Delete and recreate"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function createAppInfoJar {
|
||||
pushd "$BUILD_PATH"/bin/templates/cordova/ApplicationInfo > /dev/null
|
||||
javac ApplicationInfo.java
|
||||
jar -cfe ../appinfo.jar ApplicationInfo ApplicationInfo.class
|
||||
popd > /dev/null
|
||||
// Support basic help commands
|
||||
if(args.length < 3 || (args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
|
||||
args[2] == 'help' || args[2] == '-help' || args[2] == '/help')) {
|
||||
create.help();
|
||||
} else {
|
||||
create.run(args[2], args[3], args[4], args[5]);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
ANDROID_BIN="${ANDROID_BIN:=$( which android )}"
|
||||
PACKAGE_AS_PATH=$(echo $PACKAGE | sed 's/\./\//g')
|
||||
ACTIVITY_PATH="$PROJECT_PATH"/src/$PACKAGE_AS_PATH/$ACTIVITY.java
|
||||
MANIFEST_PATH="$PROJECT_PATH"/AndroidManifest.xml
|
||||
|
||||
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
|
||||
|
||||
# compile cordova.js and cordova.jar
|
||||
pushd "$BUILD_PATH"/framework > /dev/null
|
||||
ant jar > /dev/null
|
||||
popd > /dev/null
|
||||
fi
|
||||
|
||||
# create new android project
|
||||
"$ANDROID_BIN" create project --target $TARGET --path "$PROJECT_PATH" --package $PACKAGE --activity $ACTIVITY &> /dev/null
|
||||
|
||||
# copy project template
|
||||
cp -r "$BUILD_PATH"/bin/templates/project/assets "$PROJECT_PATH"
|
||||
cp -r "$BUILD_PATH"/bin/templates/project/res "$PROJECT_PATH"
|
||||
|
||||
# copy cordova.js, cordova.jar and res/xml
|
||||
if [ -d "$BUILD_PATH"/framework ]
|
||||
then
|
||||
cp -r "$BUILD_PATH"/framework/res/xml "$PROJECT_PATH"/res
|
||||
cp "$BUILD_PATH"/framework/assets/www/cordova.js "$PROJECT_PATH"/assets/www/cordova.js
|
||||
cp "$BUILD_PATH"/framework/cordova-$VERSION.jar "$PROJECT_PATH"/libs/cordova-$VERSION.jar
|
||||
else
|
||||
cp -r "$BUILD_PATH"/xml "$PROJECT_PATH"/res/xml
|
||||
cp "$BUILD_PATH"/cordova.js "$PROJECT_PATH"/assets/www/cordova.js
|
||||
cp "$BUILD_PATH"/cordova-$VERSION.jar "$PROJECT_PATH"/libs/cordova-$VERSION.jar
|
||||
fi
|
||||
|
||||
# interpolate the activity name and package
|
||||
cp "$BUILD_PATH"/bin/templates/project/Activity.java "$ACTIVITY_PATH"
|
||||
replace "s/__ACTIVITY__/${ACTIVITY}/g" "$ACTIVITY_PATH"
|
||||
replace "s/__ID__/${PACKAGE}/g" "$ACTIVITY_PATH"
|
||||
|
||||
cp "$BUILD_PATH"/bin/templates/project/AndroidManifest.xml "$MANIFEST_PATH"
|
||||
replace "s/__ACTIVITY__/${ACTIVITY}/g" "$MANIFEST_PATH"
|
||||
replace "s/__PACKAGE__/${PACKAGE}/g" "$MANIFEST_PATH"
|
||||
replace "s/__APILEVEL__/${API_LEVEL}/g" "$MANIFEST_PATH"
|
||||
|
||||
# creating cordova folder and copying run/build/log/launch scripts
|
||||
mkdir "$PROJECT_PATH"/cordova
|
||||
mkdir "$PROJECT_PATH"/cordova/lib
|
||||
createAppInfoJar
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/appinfo.jar "$PROJECT_PATH"/cordova/appinfo.jar
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/build "$PROJECT_PATH"/cordova/build
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/clean "$PROJECT_PATH"/cordova/clean
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/log "$PROJECT_PATH"/cordova/log
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/run "$PROJECT_PATH"/cordova/run
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/version "$PROJECT_PATH"/cordova/version
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/install-device "$PROJECT_PATH"/cordova/lib/install-device
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/install-emulator "$PROJECT_PATH"/cordova/lib/install-emulator
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/list-devices "$PROJECT_PATH"/cordova/lib/list-devices
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/list-emulator-images "$PROJECT_PATH"/cordova/lib/list-emulator-images
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/list-started-emulators "$PROJECT_PATH"/cordova/lib/list-started-emulators
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/start-emulator "$PROJECT_PATH"/cordova/lib/start-emulator
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@ECHO OFF
|
||||
GOTO BEGIN
|
||||
:: 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
|
||||
@@ -17,38 +15,12 @@ GOTO BEGIN
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
:BEGIN
|
||||
IF NOT DEFINED JAVA_HOME GOTO MISSING_JAVA_HOME
|
||||
|
||||
FOR %%X in (java.exe javac.exe ant.bat android.bat) do (
|
||||
IF [%%~$PATH:X]==[] (
|
||||
ECHO Cannot locate %%X using the PATH environment variable.
|
||||
ECHO Retry after adding directory containing %%X to the PATH variable.
|
||||
ECHO Remember to open a new command window after updating the PATH variable.
|
||||
IF "%%X"=="java.exe" GOTO GET_JAVA
|
||||
IF "%%X"=="javac.exe" GOTO GET_JAVA
|
||||
IF "%%X"=="ant.bat" GOTO GET_ANT
|
||||
IF "%%X"=="android.bat" GOTO GET_ANDROID
|
||||
GOTO ERROR
|
||||
)
|
||||
)
|
||||
cscript "%~dp0\create.js" %* //nologo
|
||||
GOTO END
|
||||
:MISSING_JAVA_HOME
|
||||
ECHO The JAVA_HOME environment variable is not set.
|
||||
ECHO Set JAVA_HOME to an existing JRE directory.
|
||||
ECHO Remember to also add JAVA_HOME to the PATH variable.
|
||||
ECHO After updating system variables, open a new command window and retry.
|
||||
GOTO ERROR
|
||||
:GET_JAVA
|
||||
ECHO Visit http://java.oracle.com if you need to install Java (JDK).
|
||||
GOTO ERROR
|
||||
:GET_ANT
|
||||
ECHO Visit http://ant.apache.org if you need to install Apache Ant.
|
||||
GOTO ERROR
|
||||
:GET_ANDROID
|
||||
ECHO Visit http://developer.android.com if you need to install the Android SDK.
|
||||
GOTO ERROR
|
||||
:ERROR
|
||||
EXIT /B 1
|
||||
:END
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0create"
|
||||
IF EXIST %script_path% (
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'create' script in 'bin' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
225
bin/create.js
225
bin/create.js
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* create a cordova/android project
|
||||
*
|
||||
* USAGE
|
||||
* ./create [path package activity]
|
||||
*/
|
||||
|
||||
var args = WScript.Arguments, PROJECT_PATH="example",
|
||||
PACKAGE="org.apache.cordova.example", ACTIVITY="cordovaExample",
|
||||
shell=WScript.CreateObject("WScript.Shell"),
|
||||
fso = WScript.CreateObject('Scripting.FileSystemObject');
|
||||
|
||||
function Usage() {
|
||||
Log("Usage: create PathTONewProject [ PackageName AppName ]");
|
||||
Log(" PathTONewProject : The path to where you wish to create the project");
|
||||
Log(" PackageName : The package for the project (default is org.apache.cordova.example)")
|
||||
Log(" AppName : The name of the application/activity (default is cordovaExample)");
|
||||
Log("examples:");
|
||||
Log(" create C:\\Users\\anonymous\\Desktop\\MyProject");
|
||||
Log(" create C:\\Users\\anonymous\\Desktop\\MyProject io.Cordova.Example AnApp");
|
||||
}
|
||||
|
||||
// logs messaged to stdout and stderr
|
||||
function Log(msg, error) {
|
||||
if (error) {
|
||||
WScript.StdErr.WriteLine(msg);
|
||||
}
|
||||
else {
|
||||
WScript.StdOut.WriteLine(msg);
|
||||
}
|
||||
}
|
||||
|
||||
function read(filename) {
|
||||
var fso=WScript.CreateObject("Scripting.FileSystemObject");
|
||||
var f=fso.OpenTextFile(filename, 1);
|
||||
var s=f.ReadAll();
|
||||
f.Close();
|
||||
return s;
|
||||
}
|
||||
|
||||
function checkTargets(targets) {
|
||||
if(!targets) {
|
||||
Log("You do not have any android targets setup. Please create at least one target with the `android` command", true);
|
||||
WScript.Quit(69);
|
||||
}
|
||||
}
|
||||
|
||||
function setTarget() {
|
||||
var targets = shell.Exec('android.bat list targets').StdOut.ReadAll().match(/id:\s\d+/g);
|
||||
checkTargets(targets);
|
||||
return targets[targets.length - 1].replace(/id: /, ""); // TODO: give users the option to set their target
|
||||
}
|
||||
function setApiLevel() {
|
||||
var targets = shell.Exec('android.bat list targets').StdOut.ReadAll().match(/API level:\s\d+/g);
|
||||
checkTargets(targets);
|
||||
return targets[targets.length - 1].replace(/API level: /, "");
|
||||
}
|
||||
function write(filename, contents) {
|
||||
var fso=WScript.CreateObject("Scripting.FileSystemObject");
|
||||
var f=fso.OpenTextFile(filename, 2, true);
|
||||
f.Write(contents);
|
||||
f.Close();
|
||||
}
|
||||
function replaceInFile(filename, regexp, replacement) {
|
||||
write(filename, read(filename).replace(regexp, replacement));
|
||||
}
|
||||
function exec(command) {
|
||||
var oShell=shell.Exec(command);
|
||||
while (oShell.Status == 0) {
|
||||
if(!oShell.StdOut.AtEndOfStream) {
|
||||
var line = oShell.StdOut.ReadLine();
|
||||
// XXX: Change to verbose mode
|
||||
// WScript.StdOut.WriteLine(line);
|
||||
}
|
||||
WScript.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
function createAppInfoJar() {
|
||||
if(!fso.FileExists(ROOT+"\\bin\\templates\\cordova\\appinfo.jar")) {
|
||||
Log("Creating appinfo.jar...");
|
||||
var cur = shell.CurrentDirectory;
|
||||
shell.CurrentDirectory = ROOT+"\\bin\\templates\\cordova\\ApplicationInfo";
|
||||
exec("javac ApplicationInfo.java");
|
||||
exec("jar -cfe ..\\appinfo.jar ApplicationInfo ApplicationInfo.class");
|
||||
shell.CurrentDirectory = cur;
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
// Cleanup
|
||||
// if(fso.FileExists(ROOT + '\\framework\\libs\\commons-codec-1.6.jar')) {
|
||||
// fso.DeleteFile(ROOT + '\\framework\\libs\\commons-codec-1.6.jar');
|
||||
// fso.DeleteFolder(ROOT + '\\framework\\libs', true);
|
||||
// }
|
||||
if(fso.FileExists(ROOT + '\\framework\\cordova-'+VERSION+'.jar')) {
|
||||
fso.DeleteFile(ROOT + '\\framework\\cordova-'+VERSION+'.jar');
|
||||
}
|
||||
if(fso.FileExists(ROOT + '\\framework\\assets\\www\\cordova-'+VERSION+'.js')) {
|
||||
fso.DeleteFile(ROOT + '\\framework\\assets\\www\\cordova-'+VERSION+'.js');
|
||||
}
|
||||
}
|
||||
|
||||
// working dir
|
||||
var ROOT = WScript.ScriptFullName.split('\\bin\\create.js').join('');
|
||||
if (args.Count() > 0) {
|
||||
// support help flags
|
||||
if (args(0) == "--help" || args(0) == "/?" ||
|
||||
args(0) == "help" || args(0) == "-help" || args(0) == "/help" || args(0) == "-h") {
|
||||
Usage();
|
||||
WScript.Quit(2);
|
||||
}
|
||||
|
||||
PROJECT_PATH=args(0);
|
||||
if (args.Count() > 1) {
|
||||
PACKAGE = args(1);
|
||||
}
|
||||
if (args.Count() > 2) {
|
||||
ACTIVITY = args(2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log("Error : No project path provided.");
|
||||
Usage();
|
||||
WScript.Quit(2);
|
||||
}
|
||||
|
||||
if(fso.FolderExists(PROJECT_PATH)) {
|
||||
Log("Project path already exists!", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
|
||||
var PACKAGE_AS_PATH=PACKAGE.replace(/\./g, '\\');
|
||||
var ACTIVITY_DIR=PROJECT_PATH + '\\src\\' + PACKAGE_AS_PATH;
|
||||
var ACTIVITY_PATH=ACTIVITY_DIR+'\\'+ACTIVITY+'.java';
|
||||
var MANIFEST_PATH=PROJECT_PATH+'\\AndroidManifest.xml';
|
||||
var TARGET=setTarget();
|
||||
var API_LEVEL=setApiLevel();
|
||||
var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
|
||||
// create the project
|
||||
Log("Creating new android project...");
|
||||
exec('android.bat create project --target "'+TARGET+'" --path "'+PROJECT_PATH+'" --package "'+PACKAGE+'" --activity "'+ACTIVITY+'"');
|
||||
|
||||
// build from source. distro should have these files
|
||||
if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
|
||||
!fso.FileExists(ROOT+'\\cordova.js')) {
|
||||
Log("Building jar and js files...");
|
||||
// update the cordova framework project to a target that exists on this machine
|
||||
exec('android.bat update project --target "'+TARGET+'" --path "'+ROOT+'\\framework"');
|
||||
exec('ant.bat -f "'+ ROOT +'\\framework\\build.xml" jar');
|
||||
}
|
||||
|
||||
// copy in the project template
|
||||
Log("Copying template files...");
|
||||
exec('%comspec% /c xcopy "'+ ROOT + '\\bin\\templates\\project\\res" "'+PROJECT_PATH+'\\res\\" /E /Y');
|
||||
exec('%comspec% /c xcopy "'+ ROOT + '\\bin\\templates\\project\\assets" "'+PROJECT_PATH+'\\assets\\" /E /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\project\\AndroidManifest.xml" "' + PROJECT_PATH + '\\AndroidManifest.xml" /Y');
|
||||
exec('%comspec% /c mkdir "' + ACTIVITY_DIR + '"');
|
||||
exec('%comspec% /c copy "' + ROOT + '"\\bin\\templates\\project\\Activity.java "' + ACTIVITY_PATH + '" /Y');
|
||||
|
||||
// check if we have the source or the distro files
|
||||
Log("Copying js, jar & config.xml files...");
|
||||
if(fso.FolderExists(ROOT + '\\framework')) {
|
||||
exec('%comspec% /c copy "'+ROOT+'\\framework\\assets\\www\\cordova.js" "'+PROJECT_PATH+'\\assets\\www\\cordova.js" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\framework\\cordova-'+VERSION+'.jar" "'+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar" /Y');
|
||||
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\framework\\res\\xml\\config.xml" "' + PROJECT_PATH + '\\res\\xml\\config.xml" /Y');
|
||||
} else {
|
||||
// copy in cordova.js
|
||||
exec('%comspec% /c copy "'+ROOT+'\\cordova.js" "'+PROJECT_PATH+'\\assets\\www\\cordova.js" /Y');
|
||||
// copy in cordova.jar
|
||||
exec('%comspec% /c copy "'+ROOT+'\\cordova-'+VERSION+'.jar" "'+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar" /Y');
|
||||
// copy in xml
|
||||
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\xml\\config.xml" "' + PROJECT_PATH + '\\res\\xml\\config.xml" /Y');
|
||||
}
|
||||
|
||||
// copy cordova scripts
|
||||
fso.CreateFolder(PROJECT_PATH + '\\cordova');
|
||||
fso.CreateFolder(PROJECT_PATH + '\\cordova\\lib');
|
||||
createAppInfoJar();
|
||||
Log("Copying cordova command tools...");
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\appinfo.jar" "' + PROJECT_PATH + '\\cordova\\appinfo.jar" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\cordova.js" "' + PROJECT_PATH + '\\cordova\\lib\\cordova.js" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\install-device.bat" "' + PROJECT_PATH + '\\cordova\\lib\\install-device.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\install-emulator.bat" "' + PROJECT_PATH + '\\cordova\\lib\\install-emulator.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-emulator-images.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-emulator-images.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-devices.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-devices.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-started-emulators.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-started-emulators.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\start-emulator.bat" "' + PROJECT_PATH + '\\cordova\\lib\\start-emulator.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\cordova.bat" "' + PROJECT_PATH + '\\cordova\\cordova.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\clean.bat" "' + PROJECT_PATH + '\\cordova\\clean.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\build.bat" "' + PROJECT_PATH + '\\cordova\\build.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\log.bat" "' + PROJECT_PATH + '\\cordova\\log.bat" /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\run.bat" "' + PROJECT_PATH + '\\cordova\\run.bat" /Y');
|
||||
|
||||
// interpolate the activity name and package
|
||||
Log("Updating AndroidManifest.xml and Main Activity...");
|
||||
replaceInFile(ACTIVITY_PATH, /__ACTIVITY__/, ACTIVITY);
|
||||
replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
|
||||
|
||||
replaceInFile(MANIFEST_PATH, /__ACTIVITY__/, ACTIVITY);
|
||||
replaceInFile(MANIFEST_PATH, /__PACKAGE__/, PACKAGE);
|
||||
replaceInFile(MANIFEST_PATH, /__APILEVEL__/, API_LEVEL);
|
||||
|
||||
cleanup();
|
||||
85
bin/lib/check_reqs.js
Normal file
85
bin/lib/check_reqs.js
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var shell = require('shelljs'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
// Get valid target from framework/project.properties
|
||||
module.exports.get_target = function() {
|
||||
if(fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) {
|
||||
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
|
||||
return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
|
||||
} else if (fs.existsSync(path.join(ROOT, 'project.properties'))) {
|
||||
// if no target found, we're probably in a project and project.properties is in ROOT.
|
||||
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties'));
|
||||
return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.check_ant = function() {
|
||||
var test = shell.exec('ant -version', {silent:true, async:false});
|
||||
if(test.code > 0) {
|
||||
console.error('ERROR : executing command \'ant\', make sure you have ant installed and added to your path.');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports.check_java = function() {
|
||||
if(process.env.JAVA_HOME) {
|
||||
var test = shell.exec('java', {silent:true, async:false});
|
||||
if(test.code > 0) {
|
||||
console.error('ERROR : executing command \'java\', make sure you java environment is set up. Including your JDK and JRE.');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
console.error('ERROR : Make sure JAVA_HOME is set, as well as paths to your JDK and JRE for java.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.check_android = function() {
|
||||
var valid_target = this.get_target();
|
||||
var targets = shell.exec('android list targets', {silent:true, async:false});
|
||||
|
||||
if(targets.code > 0 && targets.output.match(/command\snot\sfound/)) {
|
||||
console.error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.');
|
||||
return false;
|
||||
} else if(!targets.output.match(valid_target)) {
|
||||
console.error('Please install Android target ' + valid_target.split('-')[1] + ' (the Android newest SDK). Make sure you have the latest Android tools installed as well. Run \"android\" from your command-line to install/update any missing SDKs or tools.');
|
||||
return false;
|
||||
} else {
|
||||
var cmd = 'android update project -p ' + ROOT + ' -t ' + valid_target + ' 1> /dev/null 2>&1';
|
||||
var result = shell.exec(cmd, {silent:false, async:true});
|
||||
if(result.code > 0) {
|
||||
console.error('Error updating the Cordova library to work with your Android environment.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports.run = function() {
|
||||
return this.check_ant() && this.check_java && this.check_android();
|
||||
}
|
||||
250
bin/lib/create.js
Executable file
250
bin/lib/create.js
Executable file
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
var shell = require('shelljs'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
check_reqs = require('./check_reqs'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
|
||||
/**
|
||||
* $ create [options]
|
||||
*
|
||||
* Creates an android application with the given options.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `project_path` {String} Path to the new Cordova android project.
|
||||
* - `package_name`{String} Package name, following reverse-domain style convention.
|
||||
* - `project_name` {String} Project name.
|
||||
* - 'project_template_dir' {String} Path to project template (override).
|
||||
*/
|
||||
|
||||
module.exports.run = function(project_path, package_name, project_name, project_template_dir) {
|
||||
|
||||
var VERSION = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8');
|
||||
|
||||
// Set default values for path, package and name
|
||||
project_path = typeof project_path !== 'undefined' ? project_path : "CordovaExample";
|
||||
package_name = typeof package_name !== 'undefined' ? package_name : 'my.cordova.project';
|
||||
project_name = typeof project_name !== 'undefined' ? project_name : 'CordovaExample';
|
||||
project_template_dir = typeof project_template_dir !== 'undefined' ?
|
||||
project_template_dir :
|
||||
path.join(ROOT, 'bin', 'templates', 'project');
|
||||
|
||||
var safe_activity_name = project_name.replace(/\W/, '');
|
||||
var package_as_path = package_name.replace(/\./g, path.sep);
|
||||
var activity_dir = path.join(project_path, 'src', package_as_path);
|
||||
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
|
||||
var target_api = check_reqs.get_target();
|
||||
var strings_path = path.join(project_path, 'res', 'values', 'strings.xml');
|
||||
var manifest_path = path.join(project_path, 'AndroidManifest.xml');
|
||||
|
||||
// Check if project already exists
|
||||
if(fs.existsSync(project_path)) {
|
||||
console.error('Project already exists! Delete and recreate');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
// Check that requirements are met and proper targets are installed
|
||||
if(!check_reqs.run()) {
|
||||
console.error('Please make sure you meeet the software requirements in order to build an android cordova project');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
// Log the given values for the project
|
||||
console.log('Creating Cordova project for the Android platform :');
|
||||
console.log('\tPath : ' + path.relative(process.cwd(), project_path));
|
||||
console.log('\tPackage : ' + package_name);
|
||||
console.log('\tName : ' + project_name);
|
||||
console.log('\tAndroid target : ' + target_api);
|
||||
|
||||
// build from source. distro should have these files
|
||||
if(!fs.existsSync(path.join(ROOT, 'cordova-' + VERSION + '.jar')) && fs.existsSync(path.join(ROOT, 'framework'))) {
|
||||
console.log('Building jar and js files...');
|
||||
// update the cordova-android framework for the desired target
|
||||
exec('android update project --target ' + target_api + ' --path ' + path.join(ROOT, 'framework'));
|
||||
|
||||
// compile cordova.js and cordova.jar
|
||||
var cwd = process.cwd();
|
||||
process.chdir(path.join(ROOT, 'framework'));
|
||||
exec('ant jar');
|
||||
process.chdir(cwd);
|
||||
}
|
||||
|
||||
// create new android project
|
||||
var create_cmd = 'android create project --target "'+target_api+'" --path "'+path.relative(process.cwd(), project_path)+'" --package "'+package_name+'" --activity "'+safe_activity_name+'"';
|
||||
exec(create_cmd);
|
||||
|
||||
console.log('Copying template files...');
|
||||
|
||||
// copy project template
|
||||
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
|
||||
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
|
||||
|
||||
// copy cordova.js, cordova.jar and res/xml
|
||||
if(fs.existsSync(path.join(ROOT, 'framework'))) {
|
||||
shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
|
||||
shell.cp(path.join(ROOT, 'framework', 'assets', 'www', 'cordova.js'), path.join(project_path, 'assets', 'www', 'cordova.js'));
|
||||
shell.cp(path.join(ROOT, 'framework', 'cordova-' + VERSION + '.jar'), path.join(project_path, 'libs', 'cordova-' + VERSION + '.jar'));
|
||||
} else {
|
||||
shell.cp('-r', path.join(ROOT, 'xml'), path.join(project_path, 'res'));
|
||||
shell.cp(path.join(ROOT, 'cordova.js'), path.join(project_path, 'assets', 'www', 'cordova.js'));
|
||||
shell.cp(path.join(ROOT, 'cordova-' + VERSION + '.jar'), path.join(project_path, 'libs', 'cordova-' + VERSION + '.jar'));
|
||||
}
|
||||
|
||||
// interpolate the activity name and package
|
||||
shell.mkdir('-p', activity_dir);
|
||||
shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path);
|
||||
replaceInFile(activity_path, /__ACTIVITY__/, safe_activity_name);
|
||||
replaceInFile(activity_path, /__ID__/, package_name);
|
||||
|
||||
// interpolate the app name into strings.xml
|
||||
replaceInFile(strings_path, />Cordova</, '>' + project_name + '<');
|
||||
|
||||
shell.cp('-f', path.join(project_template_dir, 'AndroidManifest.xml'), manifest_path);
|
||||
replaceInFile(manifest_path, /__ACTIVITY__/, safe_activity_name);
|
||||
replaceInFile(manifest_path, /__PACKAGE__/, package_name);
|
||||
replaceInFile(manifest_path, /__APILEVEL__/, target_api.split('-')[1]);
|
||||
|
||||
var cordova_path = path.join(ROOT, 'bin', 'templates', 'cordova');
|
||||
// creating cordova folder and copying run/build/log/launch/check_reqs scripts
|
||||
var lib_path = path.join(cordova_path, 'lib');
|
||||
shell.mkdir(path.join(project_path, 'cordova'));
|
||||
shell.mkdir(path.join(project_path, 'cordova', 'lib'));
|
||||
|
||||
shell.cp(path.join(cordova_path, 'build'), path.join(project_path, 'cordova', 'build'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'build'));
|
||||
shell.cp(path.join(cordova_path, 'clean'), path.join(project_path, 'cordova', 'clean'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'clean'));
|
||||
shell.cp(path.join(cordova_path, 'log'), path.join(project_path, 'cordova', 'log'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'log'));
|
||||
shell.cp(path.join(cordova_path, 'run'), path.join(project_path, 'cordova', 'run'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'run'));
|
||||
shell.cp(path.join(cordova_path, 'version'), path.join(project_path, 'cordova', 'version'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'version'));
|
||||
shell.cp(path.join(ROOT, 'bin', 'check_reqs'), path.join(project_path, 'cordova', 'check_reqs'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'check_reqs'));
|
||||
|
||||
shell.cp(path.join(lib_path, 'appinfo.js'), path.join(project_path, 'cordova', 'lib', 'appinfo.js'));
|
||||
shell.cp(path.join(lib_path, 'build.js'), path.join(project_path, 'cordova', 'lib', 'build.js'));
|
||||
shell.cp(path.join(ROOT, 'bin', 'lib', 'check_reqs.js'), path.join(project_path, 'cordova', 'lib', 'check_reqs.js'));
|
||||
shell.cp(path.join(lib_path, 'clean.js'), path.join(project_path, 'cordova', 'lib', 'clean.js'));
|
||||
shell.cp(path.join(lib_path, 'device.js'), path.join(project_path, 'cordova', 'lib', 'device.js'));
|
||||
shell.cp(path.join(lib_path, 'emulator.js'), path.join(project_path, 'cordova', 'lib', 'emulator.js'));
|
||||
shell.cp(path.join(lib_path, 'log.js'), path.join(project_path, 'cordova', 'lib', 'log.js'));
|
||||
shell.cp(path.join(lib_path, 'run.js'), path.join(project_path, 'cordova', 'lib', 'run.js'));
|
||||
shell.cp(path.join(lib_path, 'install-device'), path.join(project_path, 'cordova', 'lib', 'install-device'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'install-device'));
|
||||
shell.cp(path.join(lib_path, 'install-emulator'), path.join(project_path, 'cordova', 'lib', 'install-emulator'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'install-emulator'));
|
||||
shell.cp(path.join(lib_path, 'list-devices'), path.join(project_path, 'cordova', 'lib', 'list-devices'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'list-devices'));
|
||||
shell.cp(path.join(lib_path, 'list-emulator-images'), path.join(project_path, 'cordova', 'lib', 'list-emulator-images'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'list-emulator-images'));
|
||||
shell.cp(path.join(lib_path, 'list-started-emulators'), path.join(project_path, 'cordova', 'lib', 'list-started-emulators'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'list-started-emulators'));
|
||||
shell.cp(path.join(lib_path, 'start-emulator'), path.join(project_path, 'cordova', 'lib', 'start-emulator'));
|
||||
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'start-emulator'));
|
||||
|
||||
// if on windows, copy .bat scripts
|
||||
// TODO : make these not nessesary, they clutter the scripting folder.
|
||||
if(process.platform == 'win32' || process.platform == 'win64') {
|
||||
shell.cp(path.join(cordova_path, 'build.bat'), path.join(project_path, 'cordova', 'build.bat'));
|
||||
shell.cp(path.join(cordova_path, 'clean.bat'), path.join(project_path, 'cordova', 'clean.bat'));
|
||||
shell.cp(path.join(cordova_path, 'log.bat'), path.join(project_path, 'cordova', 'log.bat'));
|
||||
shell.cp(path.join(cordova_path, 'run.bat'), path.join(project_path, 'cordova', 'run.bat'));
|
||||
shell.cp(path.join(cordova_path, 'version.bat'), path.join(project_path, 'cordova', 'version.bat'));
|
||||
shell.cp(path.join(ROOT, 'bin', 'check_reqs.bat'), path.join(project_path, 'cordova', 'check_reqs.bat'));
|
||||
|
||||
// lib scripts
|
||||
shell.cp(path.join(lib_path, 'install-device.bat'), path.join(project_path, 'cordova', 'lib', 'install-device.bat'));
|
||||
shell.cp(path.join(lib_path, 'install-emulator.bat'), path.join(project_path, 'cordova', 'lib', 'install-emulator.bat'));
|
||||
shell.cp(path.join(lib_path, 'list-devices.bat'), path.join(project_path, 'cordova', 'lib', 'list-devices.bat'));
|
||||
shell.cp(path.join(lib_path, 'list-emulator-images.bat'), path.join(project_path, 'cordova', 'lib', 'list-emulator-images.bat'));
|
||||
shell.cp(path.join(lib_path, 'list-started-emulators.bat'), path.join(project_path, 'cordova', 'lib', 'list-started-emulators.bat'));
|
||||
shell.cp(path.join(lib_path, 'start-emulator.bat'), path.join(project_path, 'cordova', 'lib', 'start-emulator.bat'));
|
||||
}
|
||||
|
||||
// copy node related files
|
||||
shell.cp(path.join(ROOT, 'bin', 'package.json'), path.join(project_path, 'cordova', 'package.json'));
|
||||
shell.cp('-r', path.join(ROOT, 'bin', 'node_modules'), path.join(project_path, 'cordova'));
|
||||
|
||||
/*
|
||||
* HELPER FUNCTIONS
|
||||
*/
|
||||
|
||||
function exec(command) {
|
||||
var result;
|
||||
try {
|
||||
result = shell.exec(command, {silent:false, async:false});
|
||||
} catch(e) {
|
||||
console.error('Command error on execuation : ' + command);
|
||||
console.error(e);
|
||||
process.exit(2);
|
||||
}
|
||||
if(result && result.code > 0) {
|
||||
console.error('Command failed to execute : ' + command);
|
||||
console.error(result.output);
|
||||
process.exit(2);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function replaceInFile(filename, regex, replacement) {
|
||||
write(filename, read(filename).replace(regex, replacement));
|
||||
}
|
||||
|
||||
function read(filename) {
|
||||
if(fs.existsSync(filename)) {
|
||||
if(fs.lstatSync(filename).isFile()) {
|
||||
return fs.readFileSync(filename, 'utf-8');
|
||||
} else {
|
||||
console.error('Uanble to read directory : ' + filename);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.error('Uanble to read file, not found : ' + filename);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function write(filename, content) {
|
||||
fs.writeFileSync(filename, content, 'utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage information.
|
||||
**/
|
||||
|
||||
module.exports.help = function() {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'bin', 'create')) + ' <path_to_new_project> <package_name> <project_name>');
|
||||
console.log('Make sure the Android SDK tools folder is in your PATH!');
|
||||
console.log(' <path_to_new_project>: Path to your new Cordova Android project');
|
||||
console.log(' <package_name>: Package name, following reverse-domain style convention');
|
||||
console.log(' <project_name>: Project name');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
1
bin/node_modules/.bin/shjs
generated
vendored
Symbolic link
1
bin/node_modules/.bin/shjs
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../shelljs/bin/shjs
|
||||
6
bin/node_modules/shelljs/.documentup.json
generated
vendored
Normal file
6
bin/node_modules/shelljs/.documentup.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "ShellJS",
|
||||
"twitter": [
|
||||
"r2r"
|
||||
]
|
||||
}
|
||||
1
bin/node_modules/shelljs/.npmignore
generated
vendored
Normal file
1
bin/node_modules/shelljs/.npmignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
5
bin/node_modules/shelljs/.travis.yml
generated
vendored
Normal file
5
bin/node_modules/shelljs/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.6
|
||||
- 0.8
|
||||
|
||||
26
bin/node_modules/shelljs/LICENSE
generated
vendored
Normal file
26
bin/node_modules/shelljs/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
Copyright (c) 2012, Artur Adib <aadib@mozilla.com>
|
||||
All rights reserved.
|
||||
|
||||
You may use this project under the terms of the New BSD license as follows:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Artur Adib nor the
|
||||
names of the contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
513
bin/node_modules/shelljs/README.md
generated
vendored
Normal file
513
bin/node_modules/shelljs/README.md
generated
vendored
Normal file
@@ -0,0 +1,513 @@
|
||||
# ShellJS - Unix shell commands for Node.js [](http://travis-ci.org/arturadib/shelljs)
|
||||
|
||||
ShellJS is a portable **(Windows/Linux/OS X)** implementation of Unix shell commands on top of the Node.js API. You can use it to eliminate your shell script's dependency on Unix while still keeping its familiar and powerful commands. You can also install it globally so you can run it from outside Node projects - say goodbye to those gnarly Bash scripts!
|
||||
|
||||
The project is [unit-tested](http://travis-ci.org/arturadib/shelljs) and battled-tested in projects like:
|
||||
|
||||
+ [PDF.js](http://github.com/mozilla/pdf.js) - Firefox's next-gen PDF reader
|
||||
+ [Firebug](http://getfirebug.com/) - Firefox's infamous debugger
|
||||
+ [JSHint](http://jshint.com) - Most popular JavaScript linter
|
||||
+ [Zepto](http://zeptojs.com) - jQuery-compatible JavaScript library for modern browsers
|
||||
+ [Yeoman](http://yeoman.io/) - Web application stack and development tool
|
||||
+ [Deployd.com](http://deployd.com) - Open source PaaS for quick API backend generation
|
||||
|
||||
and [many more](https://npmjs.org/browse/depended/shelljs).
|
||||
|
||||
## Installing
|
||||
|
||||
Via npm:
|
||||
|
||||
```bash
|
||||
$ npm install [-g] shelljs
|
||||
```
|
||||
|
||||
If the global option `-g` is specified, the binary `shjs` will be installed. This makes it possible to
|
||||
run ShellJS scripts much like any shell script from the command line, i.e. without requiring a `node_modules` folder:
|
||||
|
||||
```bash
|
||||
$ shjs my_script
|
||||
```
|
||||
|
||||
You can also just copy `shell.js` into your project's directory, and `require()` accordingly.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### JavaScript
|
||||
|
||||
```javascript
|
||||
require('shelljs/global');
|
||||
|
||||
if (!which('git')) {
|
||||
echo('Sorry, this script requires git');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Copy files to release dir
|
||||
mkdir('-p', 'out/Release');
|
||||
cp('-R', 'stuff/*', 'out/Release');
|
||||
|
||||
// Replace macros in each .js file
|
||||
cd('lib');
|
||||
ls('*.js').forEach(function(file) {
|
||||
sed('-i', 'BUILD_VERSION', 'v0.1.2', file);
|
||||
sed('-i', /.*REMOVE_THIS_LINE.*\n/, '', file);
|
||||
sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, cat('macro.js'), file);
|
||||
});
|
||||
cd('..');
|
||||
|
||||
// Run external tool synchronously
|
||||
if (exec('git commit -am "Auto-commit"').code !== 0) {
|
||||
echo('Error: Git commit failed');
|
||||
exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
### CoffeeScript
|
||||
|
||||
```coffeescript
|
||||
require 'shelljs/global'
|
||||
|
||||
if not which 'git'
|
||||
echo 'Sorry, this script requires git'
|
||||
exit 1
|
||||
|
||||
# Copy files to release dir
|
||||
mkdir '-p', 'out/Release'
|
||||
cp '-R', 'stuff/*', 'out/Release'
|
||||
|
||||
# Replace macros in each .js file
|
||||
cd 'lib'
|
||||
for file in ls '*.js'
|
||||
sed '-i', 'BUILD_VERSION', 'v0.1.2', file
|
||||
sed '-i', /.*REMOVE_THIS_LINE.*\n/, '', file
|
||||
sed '-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, cat 'macro.js', file
|
||||
cd '..'
|
||||
|
||||
# Run external tool synchronously
|
||||
if (exec 'git commit -am "Auto-commit"').code != 0
|
||||
echo 'Error: Git commit failed'
|
||||
exit 1
|
||||
```
|
||||
|
||||
## Global vs. Local
|
||||
|
||||
The example above uses the convenience script `shelljs/global` to reduce verbosity. If polluting your global namespace is not desirable, simply require `shelljs`.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
var shell = require('shelljs');
|
||||
shell.echo('hello world');
|
||||
```
|
||||
|
||||
## Make tool
|
||||
|
||||
A convenience script `shelljs/make` is also provided to mimic the behavior of a Unix Makefile. In this case all shell objects are global, and command line arguments will cause the script to execute only the corresponding function in the global `target` object. To avoid redundant calls, target functions are executed only once per script.
|
||||
|
||||
Example (CoffeeScript):
|
||||
|
||||
```coffeescript
|
||||
require 'shelljs/make'
|
||||
|
||||
target.all = ->
|
||||
target.bundle()
|
||||
target.docs()
|
||||
|
||||
target.bundle = ->
|
||||
cd __dirname
|
||||
mkdir 'build'
|
||||
cd 'lib'
|
||||
(cat '*.js').to '../build/output.js'
|
||||
|
||||
target.docs = ->
|
||||
cd __dirname
|
||||
mkdir 'docs'
|
||||
cd 'lib'
|
||||
for file in ls '*.js'
|
||||
text = grep '//@', file # extract special comments
|
||||
text.replace '//@', '' # remove comment tags
|
||||
text.to 'docs/my_docs.md'
|
||||
```
|
||||
|
||||
To run the target `all`, call the above script without arguments: `$ node make`. To run the target `docs`: `$ node make docs`, and so on.
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
DO NOT MODIFY BEYOND THIS POINT - IT'S AUTOMATICALLY GENERATED
|
||||
|
||||
-->
|
||||
|
||||
|
||||
## Command reference
|
||||
|
||||
|
||||
All commands run synchronously, unless otherwise stated.
|
||||
|
||||
|
||||
### cd('dir')
|
||||
Changes to directory `dir` for the duration of the script
|
||||
|
||||
### pwd()
|
||||
Returns the current directory.
|
||||
|
||||
### ls([options ,] path [,path ...])
|
||||
### ls([options ,] path_array)
|
||||
Available options:
|
||||
|
||||
+ `-R`: recursive
|
||||
+ `-A`: all files (include files beginning with `.`, except for `.` and `..`)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
ls('projs/*.js');
|
||||
ls('-R', '/users/me', '/tmp');
|
||||
ls('-R', ['/users/me', '/tmp']); // same as above
|
||||
```
|
||||
|
||||
Returns array of files in the given path, or in current directory if no path provided.
|
||||
|
||||
### find(path [,path ...])
|
||||
### find(path_array)
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
find('src', 'lib');
|
||||
find(['src', 'lib']); // same as above
|
||||
find('.').filter(function(file) { return file.match(/\.js$/); });
|
||||
```
|
||||
|
||||
Returns array of all files (however deep) in the given paths.
|
||||
|
||||
The main difference from `ls('-R', path)` is that the resulting file names
|
||||
include the base directories, e.g. `lib/resources/file1` instead of just `file1`.
|
||||
|
||||
### cp([options ,] source [,source ...], dest)
|
||||
### cp([options ,] source_array, dest)
|
||||
Available options:
|
||||
|
||||
+ `-f`: force
|
||||
+ `-r, -R`: recursive
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
cp('file1', 'dir1');
|
||||
cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp');
|
||||
cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above
|
||||
```
|
||||
|
||||
Copies files. The wildcard `*` is accepted.
|
||||
|
||||
### rm([options ,] file [, file ...])
|
||||
### rm([options ,] file_array)
|
||||
Available options:
|
||||
|
||||
+ `-f`: force
|
||||
+ `-r, -R`: recursive
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
rm('-rf', '/tmp/*');
|
||||
rm('some_file.txt', 'another_file.txt');
|
||||
rm(['some_file.txt', 'another_file.txt']); // same as above
|
||||
```
|
||||
|
||||
Removes files. The wildcard `*` is accepted.
|
||||
|
||||
### mv(source [, source ...], dest')
|
||||
### mv(source_array, dest')
|
||||
Available options:
|
||||
|
||||
+ `f`: force
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
mv('-f', 'file', 'dir/');
|
||||
mv('file1', 'file2', 'dir/');
|
||||
mv(['file1', 'file2'], 'dir/'); // same as above
|
||||
```
|
||||
|
||||
Moves files. The wildcard `*` is accepted.
|
||||
|
||||
### mkdir([options ,] dir [, dir ...])
|
||||
### mkdir([options ,] dir_array)
|
||||
Available options:
|
||||
|
||||
+ `p`: full path (will create intermediate dirs if necessary)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g');
|
||||
mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above
|
||||
```
|
||||
|
||||
Creates directories.
|
||||
|
||||
### test(expression)
|
||||
Available expression primaries:
|
||||
|
||||
+ `'-b', 'path'`: true if path is a block device
|
||||
+ `'-c', 'path'`: true if path is a character device
|
||||
+ `'-d', 'path'`: true if path is a directory
|
||||
+ `'-e', 'path'`: true if path exists
|
||||
+ `'-f', 'path'`: true if path is a regular file
|
||||
+ `'-L', 'path'`: true if path is a symboilc link
|
||||
+ `'-p', 'path'`: true if path is a pipe (FIFO)
|
||||
+ `'-S', 'path'`: true if path is a socket
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
if (test('-d', path)) { /* do something with dir */ };
|
||||
if (!test('-f', path)) continue; // skip if it's a regular file
|
||||
```
|
||||
|
||||
Evaluates expression using the available primaries and returns corresponding value.
|
||||
|
||||
### cat(file [, file ...])
|
||||
### cat(file_array)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
var str = cat('file*.txt');
|
||||
var str = cat('file1', 'file2');
|
||||
var str = cat(['file1', 'file2']); // same as above
|
||||
```
|
||||
|
||||
Returns a string containing the given file, or a concatenated string
|
||||
containing the files if more than one file is given (a new line character is
|
||||
introduced between each file). Wildcard `*` accepted.
|
||||
|
||||
### 'string'.to(file)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
cat('input.txt').to('output.txt');
|
||||
```
|
||||
|
||||
Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as
|
||||
those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_
|
||||
|
||||
### sed([options ,] search_regex, replace_str, file)
|
||||
Available options:
|
||||
|
||||
+ `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js');
|
||||
sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js');
|
||||
```
|
||||
|
||||
Reads an input string from `file` and performs a JavaScript `replace()` on the input
|
||||
using the given search regex and replacement string. Returns the new string after replacement.
|
||||
|
||||
### grep([options ,] regex_filter, file [, file ...])
|
||||
### grep([options ,] regex_filter, file_array)
|
||||
Available options:
|
||||
|
||||
+ `-v`: Inverse the sense of the regex and print the lines not matching the criteria.
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
grep('-v', 'GLOBAL_VARIABLE', '*.js');
|
||||
grep('GLOBAL_VARIABLE', '*.js');
|
||||
```
|
||||
|
||||
Reads input string from given files and returns a string containing all lines of the
|
||||
file that match the given `regex_filter`. Wildcard `*` accepted.
|
||||
|
||||
### which(command)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
var nodeExec = which('node');
|
||||
```
|
||||
|
||||
Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions.
|
||||
Returns string containing the absolute path to the command.
|
||||
|
||||
### echo(string [,string ...])
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
echo('hello world');
|
||||
var str = echo('hello world');
|
||||
```
|
||||
|
||||
Prints string to stdout, and returns string with additional utility methods
|
||||
like `.to()`.
|
||||
|
||||
### dirs([options | '+N' | '-N'])
|
||||
|
||||
Available options:
|
||||
|
||||
+ `-c`: Clears the directory stack by deleting all of the elements.
|
||||
|
||||
Arguments:
|
||||
|
||||
+ `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero.
|
||||
+ `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero.
|
||||
|
||||
Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified.
|
||||
|
||||
See also: pushd, popd
|
||||
|
||||
### pushd([options,] [dir | '-N' | '+N'])
|
||||
|
||||
Available options:
|
||||
|
||||
+ `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated.
|
||||
|
||||
Arguments:
|
||||
|
||||
+ `dir`: Makes the current working directory be the top of the stack, and then executes the equivalent of `cd dir`.
|
||||
+ `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack.
|
||||
+ `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack.
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
// process.cwd() === '/usr'
|
||||
pushd('/etc'); // Returns /etc /usr
|
||||
pushd('+1'); // Returns /usr /etc
|
||||
```
|
||||
|
||||
Save the current directory on the top of the directory stack and then cd to `dir`. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack.
|
||||
|
||||
### popd([options,] ['-N' | '+N'])
|
||||
|
||||
Available options:
|
||||
|
||||
+ `-n`: Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated.
|
||||
|
||||
Arguments:
|
||||
|
||||
+ `+N`: Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero.
|
||||
+ `-N`: Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero.
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
echo(process.cwd()); // '/usr'
|
||||
pushd('/etc'); // '/etc /usr'
|
||||
echo(process.cwd()); // '/etc'
|
||||
popd(); // '/usr'
|
||||
echo(process.cwd()); // '/usr'
|
||||
```
|
||||
|
||||
When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack.
|
||||
|
||||
### exit(code)
|
||||
Exits the current process with the given exit code.
|
||||
|
||||
### env['VAR_NAME']
|
||||
Object containing environment variables (both getter and setter). Shortcut to process.env.
|
||||
|
||||
### exec(command [, options] [, callback])
|
||||
Available options (all `false` by default):
|
||||
|
||||
+ `async`: Asynchronous execution. Defaults to true if a callback is provided.
|
||||
+ `silent`: Do not echo program output to console.
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
var version = exec('node --version', {silent:true}).output;
|
||||
|
||||
var child = exec('some_long_running_process', {async:true});
|
||||
child.stdout.on('data', function(data) {
|
||||
/* ... do something with data ... */
|
||||
});
|
||||
|
||||
exec('some_long_running_process', function(code, output) {
|
||||
console.log('Exit code:', code);
|
||||
console.log('Program output:', output);
|
||||
});
|
||||
```
|
||||
|
||||
Executes the given `command` _synchronously_, unless otherwise specified.
|
||||
When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's
|
||||
`output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and
|
||||
the `callback` gets the arguments `(code, output)`.
|
||||
|
||||
**Note:** For long-lived processes, it's best to run `exec()` asynchronously as
|
||||
the current synchronous implementation uses a lot of CPU. This should be getting
|
||||
fixed soon.
|
||||
|
||||
### chmod(octal_mode || octal_string, file)
|
||||
### chmod(symbolic_mode, file)
|
||||
|
||||
Available options:
|
||||
|
||||
+ `-v`: output a diagnostic for every file processed
|
||||
+ `-c`: like verbose but report only when a change is made
|
||||
+ `-R`: change files and directories recursively
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
chmod(755, '/Users/brandon');
|
||||
chmod('755', '/Users/brandon'); // same as above
|
||||
chmod('u+x', '/Users/brandon');
|
||||
```
|
||||
|
||||
Alters the permissions of a file or directory by either specifying the
|
||||
absolute permissions in octal form or expressing the changes in symbols.
|
||||
This command tries to mimic the POSIX behavior as much as possible.
|
||||
Notable exceptions:
|
||||
|
||||
+ In symbolic modes, 'a-r' and '-r' are identical. No consideration is
|
||||
given to the umask.
|
||||
+ There is no "quiet" option since default behavior is to run silent.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
### config.silent
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
var silentState = config.silent; // save old silent state
|
||||
config.silent = true;
|
||||
/* ... */
|
||||
config.silent = silentState; // restore old silent state
|
||||
```
|
||||
|
||||
Suppresses all command output if `true`, except for `echo()` calls.
|
||||
Default is `false`.
|
||||
|
||||
### config.fatal
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
config.fatal = true;
|
||||
cp('this_file_does_not_exist', '/dev/null'); // dies here
|
||||
/* more commands... */
|
||||
```
|
||||
|
||||
If `true` the script will die on errors. Default is `false`.
|
||||
|
||||
## Non-Unix commands
|
||||
|
||||
|
||||
### tempdir()
|
||||
Searches and returns string containing a writeable, platform-dependent temporary directory.
|
||||
Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir).
|
||||
|
||||
### error()
|
||||
Tests if error occurred in the last command. Returns `null` if no error occurred,
|
||||
otherwise returns string explaining the error
|
||||
51
bin/node_modules/shelljs/bin/shjs
generated
vendored
Executable file
51
bin/node_modules/shelljs/bin/shjs
generated
vendored
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env node
|
||||
require('../global');
|
||||
|
||||
if (process.argv.length < 3) {
|
||||
console.log('ShellJS: missing argument (script name)');
|
||||
console.log();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var args,
|
||||
scriptName = process.argv[2];
|
||||
env['NODE_PATH'] = __dirname + '/../..';
|
||||
|
||||
if (!scriptName.match(/\.js/) && !scriptName.match(/\.coffee/)) {
|
||||
if (test('-f', scriptName + '.js'))
|
||||
scriptName += '.js';
|
||||
if (test('-f', scriptName + '.coffee'))
|
||||
scriptName += '.coffee';
|
||||
}
|
||||
|
||||
if (!test('-f', scriptName)) {
|
||||
console.log('ShellJS: script not found ('+scriptName+')');
|
||||
console.log();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
args = process.argv.slice(3);
|
||||
|
||||
for (var i = 0, l = args.length; i < l; i++) {
|
||||
if (args[i][0] !== "-"){
|
||||
args[i] = '"' + args[i] + '"'; // fixes arguments with multiple words
|
||||
}
|
||||
}
|
||||
|
||||
if (scriptName.match(/\.coffee$/)) {
|
||||
//
|
||||
// CoffeeScript
|
||||
//
|
||||
if (which('coffee')) {
|
||||
exec('coffee ' + scriptName + ' ' + args.join(' '), { async: true });
|
||||
} else {
|
||||
console.log('ShellJS: CoffeeScript interpreter not found');
|
||||
console.log();
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// JavaScript
|
||||
//
|
||||
exec('node ' + scriptName + ' ' + args.join(' '), { async: true });
|
||||
}
|
||||
3
bin/node_modules/shelljs/global.js
generated
vendored
Normal file
3
bin/node_modules/shelljs/global.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
var shell = require('./shell.js');
|
||||
for (var cmd in shell)
|
||||
global[cmd] = shell[cmd];
|
||||
4
bin/node_modules/shelljs/jshint.json
generated
vendored
Normal file
4
bin/node_modules/shelljs/jshint.json
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"loopfunc": true,
|
||||
"sub": true
|
||||
}
|
||||
48
bin/node_modules/shelljs/make.js
generated
vendored
Normal file
48
bin/node_modules/shelljs/make.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
require('./global');
|
||||
config.fatal = true;
|
||||
|
||||
global.target = {};
|
||||
|
||||
// This ensures we only execute the script targets after the entire script has
|
||||
// been evaluated
|
||||
var args = process.argv.slice(2);
|
||||
setTimeout(function() {
|
||||
var t;
|
||||
|
||||
if (args.length === 1 && args[0] === '--help') {
|
||||
console.log('Available targets:');
|
||||
for (t in target)
|
||||
console.log(' ' + t);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrap targets to prevent duplicate execution
|
||||
for (t in target) {
|
||||
(function(t, oldTarget){
|
||||
|
||||
// Wrap it
|
||||
target[t] = function(force) {
|
||||
if (oldTarget.done && !force)
|
||||
return;
|
||||
oldTarget.done = true;
|
||||
return oldTarget.apply(oldTarget, arguments);
|
||||
};
|
||||
|
||||
})(t, target[t]);
|
||||
}
|
||||
|
||||
// Execute desired targets
|
||||
if (args.length > 0) {
|
||||
args.forEach(function(arg) {
|
||||
if (arg in target)
|
||||
target[arg]();
|
||||
else {
|
||||
console.log('no such target: ' + arg);
|
||||
exit(1);
|
||||
}
|
||||
});
|
||||
} else if ('all' in target) {
|
||||
target.all();
|
||||
}
|
||||
|
||||
}, 0);
|
||||
48
bin/node_modules/shelljs/package.json
generated
vendored
Normal file
48
bin/node_modules/shelljs/package.json
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
15
bin/node_modules/shelljs/scripts/docs.js
generated
vendored
Executable file
15
bin/node_modules/shelljs/scripts/docs.js
generated
vendored
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env node
|
||||
require('../global');
|
||||
|
||||
echo('Appending docs to README.md');
|
||||
|
||||
cd(__dirname + '/..');
|
||||
|
||||
// Extract docs from shell.js
|
||||
var docs = grep('//@', 'shell.js');
|
||||
// Remove '//@'
|
||||
docs = docs.replace(/\/\/\@ ?/g, '');
|
||||
// Append docs to README
|
||||
sed('-i', /## Command reference(.|\n)*/, '## Command reference\n\n' + docs, 'README.md');
|
||||
|
||||
echo('All done.');
|
||||
50
bin/node_modules/shelljs/scripts/run-tests.js
generated
vendored
Executable file
50
bin/node_modules/shelljs/scripts/run-tests.js
generated
vendored
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env node
|
||||
require('../global');
|
||||
|
||||
var path = require('path');
|
||||
|
||||
var failed = false;
|
||||
|
||||
//
|
||||
// Lint
|
||||
//
|
||||
JSHINT_BIN = './node_modules/jshint/bin/jshint';
|
||||
cd(__dirname + '/..');
|
||||
|
||||
if (!test('-f', JSHINT_BIN)) {
|
||||
echo('JSHint not found. Run `npm install` in the root dir first.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (exec(JSHINT_BIN + ' --config jshint.json *.js test/*.js').code !== 0) {
|
||||
failed = true;
|
||||
echo('*** JSHINT FAILED! (return code != 0)');
|
||||
echo();
|
||||
} else {
|
||||
echo('All JSHint tests passed');
|
||||
echo();
|
||||
}
|
||||
|
||||
//
|
||||
// Unit tests
|
||||
//
|
||||
cd(__dirname + '/../test');
|
||||
ls('*.js').forEach(function(file) {
|
||||
echo('Running test:', file);
|
||||
if (exec('node ' + file).code !== 123) { // 123 avoids false positives (e.g. premature exit)
|
||||
failed = true;
|
||||
echo('*** TEST FAILED! (missing exit code "123")');
|
||||
echo();
|
||||
}
|
||||
});
|
||||
|
||||
if (failed) {
|
||||
echo();
|
||||
echo('*******************************************************');
|
||||
echo('WARNING: Some tests did not pass!');
|
||||
echo('*******************************************************');
|
||||
exit(1);
|
||||
} else {
|
||||
echo();
|
||||
echo('All tests passed.');
|
||||
}
|
||||
1901
bin/node_modules/shelljs/shell.js
generated
vendored
Normal file
1901
bin/node_modules/shelljs/shell.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,32 @@
|
||||
{
|
||||
"name": "cordova-android-cli",
|
||||
"description": "CLI tooling for the cordova-android project",
|
||||
"version": "0.0.1",
|
||||
"licenses": [{
|
||||
"type": "APL 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0"
|
||||
}],
|
||||
"main" : "./create",
|
||||
"bin": {
|
||||
"create": "./create",
|
||||
"bench": "./bench",
|
||||
"autotest": "./autotest",
|
||||
"BOOM": "./BOOM",
|
||||
"test": "./test"
|
||||
},
|
||||
"homepage": "http://incubator.apache.org/cordova",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://git-wip-us.apache.org/repos/asf/incubator-cordova-android.git"
|
||||
}
|
||||
}
|
||||
"name": "cordova-android",
|
||||
"description": "Cordova tooling for the android platform.",
|
||||
"version": "0.0.0",
|
||||
"homepage": "http://github.com/apache/cordova-android",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git-wip-us.apache.org/repos/asf/cordova-andorid.git"
|
||||
},
|
||||
"keywords": [
|
||||
"cli",
|
||||
"cordova",
|
||||
"tooling"
|
||||
],
|
||||
"engineStrict": "true",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"shelljs" : "0.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
"optionalDependencies": {
|
||||
},
|
||||
"author": {
|
||||
"name": "Benn Mapes",
|
||||
"email": "bennmapes@gmail.com"
|
||||
},
|
||||
"contributors": [
|
||||
]
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.xml.sax.SAXException;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ApplicationInfo {
|
||||
private static void parseAndroidManifest(String path) {
|
||||
// System.out.println(path);
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
Document dom;
|
||||
try {
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
dom = db.parse(path);
|
||||
|
||||
// getting package information
|
||||
Element manifest = dom.getDocumentElement();
|
||||
String pakkage = manifest.getAttribute("package");
|
||||
|
||||
// getting activity name
|
||||
String activity = ((Element)dom.getElementsByTagName("activity").item(0)).getAttribute("android:name");
|
||||
System.out.println(String.format("%s/.%s", pakkage, activity.replace(".", "")));
|
||||
} catch(ParserConfigurationException pce) {
|
||||
pce.printStackTrace();
|
||||
} catch(SAXException se) {
|
||||
se.printStackTrace();
|
||||
} catch(IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String path;
|
||||
if(args.length > 0) {
|
||||
path = args[0];
|
||||
} else {
|
||||
path = System.getProperty("user.dir") + "/../AndroidManifest.xml";
|
||||
}
|
||||
parseAndroidManifest(path);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,35 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$DIR/.." && pwd )
|
||||
/*
|
||||
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
|
||||
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
if [[ $1 == "--debug" ]] ; then
|
||||
$DIR/clean
|
||||
ant debug -f "$PROJECT_PATH"/build.xml
|
||||
elif [[ $1 == "--release" ]] ; then
|
||||
$DIR/clean
|
||||
ant release -f "$PROJECT_PATH"/build.xml
|
||||
elif [[ $1 == "--nobuild" ]] ; then
|
||||
echo "Skipping build..."
|
||||
else
|
||||
echo "Error : Build command '$1' not recognized."
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
echo "Warning : [ --debug | --release | --nobuild ] not specified, defaulting to --debug"
|
||||
$DIR/clean
|
||||
ant debug -f "$PROJECT_PATH"/build.xml
|
||||
fi
|
||||
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.
|
||||
*/
|
||||
|
||||
var build = require('./lib/build'),
|
||||
reqs = require('./lib/check_reqs'),
|
||||
args = process.argv;
|
||||
|
||||
// Support basic help commands
|
||||
if(args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
|
||||
args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
|
||||
build.help();
|
||||
} else if(reqs.run()) {
|
||||
build.run(args[2]);
|
||||
process.exit(0);
|
||||
} else {
|
||||
process.exit(2);
|
||||
}
|
||||
@@ -14,5 +14,13 @@
|
||||
:: KIND, either express or implied. See the License for the
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
%~dp0\cordova.bat build %*
|
||||
SET script_path="%~dp0build"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'build' script in 'cordova' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
@@ -1,22 +1,34 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$DIR/.." && pwd )
|
||||
echo "Cleaning project..."
|
||||
ant -f "$PROJECT_PATH/build.xml" clean
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var clean = require('./lib/clean'),
|
||||
reqs = require('./lib/check_reqs'),
|
||||
args = process.argv;
|
||||
|
||||
// Usage support for when args are given
|
||||
if(args.length > 2) {
|
||||
clean.help();
|
||||
} else if(reqs.run()) {
|
||||
clean.run();
|
||||
process.exit(0);
|
||||
} else {
|
||||
process.exit(2);
|
||||
}
|
||||
@@ -14,5 +14,13 @@
|
||||
:: KIND, either express or implied. See the License for the
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
%~dp0\cordova.bat clean %*
|
||||
SET script_path="%~dp0clean"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'clean' script in 'cordova' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
41
bin/templates/cordova/lib/appinfo.js
vendored
Normal file
41
bin/templates/cordova/lib/appinfo.js
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var cachedAppInfo = null;
|
||||
|
||||
function readAppInfoFromManifest() {
|
||||
var manifestPath = path.join(__dirname, '..', '..', 'AndroidManifest.xml');
|
||||
var manifestData = fs.readFileSync(manifestPath, {encoding:'utf8'});
|
||||
var packageName = /\bpackage\s*=\s*"(.+?)"/.exec(manifestData);
|
||||
if (!packageName) throw new Error('Could not find package name within ' + manifestPath);
|
||||
var activityTag = /<activity\b[\s\S]*<\/activity>/.exec(manifestData);
|
||||
if (!activityTag) throw new Error('Could not find <activity> within ' + manifestPath);
|
||||
var activityName = /\bandroid:name\s*=\s*"(.+?)"/.exec(activityTag);
|
||||
if (!activityName) throw new Error('Could not find android:name within ' + manifestPath);
|
||||
|
||||
return packageName[1] + '/.' + activityName[1];
|
||||
}
|
||||
|
||||
exports.getActivityName = function() {
|
||||
return cachedAppInfo = cachedAppInfo || readAppInfoFromManifest();
|
||||
};
|
||||
89
bin/templates/cordova/lib/build.js
vendored
Normal file
89
bin/templates/cordova/lib/build.js
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var shell = require('shelljs'),
|
||||
clean = require('./clean'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
/*
|
||||
* Builds the project with ant.
|
||||
*/
|
||||
module.exports.run = function(build_type) {
|
||||
//default build type
|
||||
build_type = typeof build_type !== 'undefined' ? build_type : "--debug";
|
||||
var cmd;
|
||||
switch(build_type) {
|
||||
case '--debug' :
|
||||
clean.run();
|
||||
cmd = 'ant debug -f ' + path.join(ROOT, 'build.xml');
|
||||
break;
|
||||
case '--release' :
|
||||
clean.run();
|
||||
cmd = 'ant release -f ' + path.join(ROOT, 'build.xml');
|
||||
break;
|
||||
case '--nobuild' :
|
||||
console.log('Skipping build...');
|
||||
break;
|
||||
default :
|
||||
console.error('Build option \'' + build_type + '\' not recognized.');
|
||||
process.exit(2);
|
||||
break;
|
||||
}
|
||||
if(cmd) {
|
||||
var result = shell.exec(cmd, {silent:false, async:false});
|
||||
if(result.code > 0) {
|
||||
console.error('ERROR: Failed to build android project.');
|
||||
console.error(result.output);
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the path to the apk file, if not such file exists then
|
||||
* the script will error out. (should we error or just return undefined?)
|
||||
*/
|
||||
module.exports.get_apk = function() {
|
||||
if(fs.existsSync(path.join(ROOT, 'bin'))) {
|
||||
var bin_files = fs.readdirSync(path.join(ROOT, 'bin'));
|
||||
for (file in bin_files) {
|
||||
if(path.extname(bin_files[file]) == '.apk') {
|
||||
return path.join(ROOT, 'bin', bin_files[file]);
|
||||
}
|
||||
}
|
||||
console.error('ERROR : No .apk found in \'bin\' folder');
|
||||
process.exit(2);
|
||||
} else {
|
||||
console.error('ERROR : unable to find project bin folder, could not locate .apk');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.help = function() {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'corodva', 'build')) + ' [build_type]');
|
||||
console.log('Build Types : ');
|
||||
console.log(' \'--debug\': Default build, will build project in using ant debug');
|
||||
console.log(' \'--release\': will build project using ant release');
|
||||
console.log(' \'--nobuild\': will skip build process (can be used with run command)');
|
||||
process.exit(0);
|
||||
}
|
||||
29
framework/src/org/apache/cordova/NetworkListener.java → bin/templates/cordova/lib/clean.js
vendored
Executable file → Normal file
29
framework/src/org/apache/cordova/NetworkListener.java → bin/templates/cordova/lib/clean.js
vendored
Executable file → Normal file
@@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@@ -17,16 +19,25 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova;
|
||||
var shell = require('shelljs'),
|
||||
path = require('path'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
import android.location.LocationManager;
|
||||
|
||||
/**
|
||||
* This class handles requests for GPS location services.
|
||||
*
|
||||
/*
|
||||
* Cleans the project using ant
|
||||
*/
|
||||
public class NetworkListener extends CordovaLocationListener {
|
||||
public NetworkListener(LocationManager locationManager, GeoBroker m) {
|
||||
super(locationManager, m, "[Cordova NetworkListener]");
|
||||
module.exports.run = function() {
|
||||
var cmd = 'ant clean -f ' + path.join(ROOT, 'build.xml');
|
||||
var result = shell.exec(cmd, {silent:false, async:false});
|
||||
if (result.code > 0) {
|
||||
console.error('ERROR: Failed to clean android project.');
|
||||
console.error(result.output);
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.help = function() {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
|
||||
console.log('Cleans the project directory.');
|
||||
process.exit(0);
|
||||
}
|
||||
593
bin/templates/cordova/lib/cordova.js
vendored
593
bin/templates/cordova/lib/cordova.js
vendored
@@ -1,593 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\cordova.js').join(''),
|
||||
shell = WScript.CreateObject("WScript.Shell"),
|
||||
fso = WScript.CreateObject('Scripting.FileSystemObject');
|
||||
|
||||
|
||||
// log to stdout or stderr
|
||||
function Log(msg, error) {
|
||||
if (error) {
|
||||
WScript.StdErr.WriteLine(msg);
|
||||
}
|
||||
else {
|
||||
WScript.StdOut.WriteLine(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// executes a commmand in the shell, returning stdout
|
||||
function exec(command) {
|
||||
var oExec=shell.Exec(command);
|
||||
var output = new String();
|
||||
while (oExec.Status == 0) {
|
||||
if (!oExec.StdOut.AtEndOfStream) {
|
||||
var line = oExec.StdOut.ReadLine();
|
||||
output += line;
|
||||
}
|
||||
WScript.sleep(100);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
// executes a command in the shell, returns stdout or stderr if error
|
||||
function exec_out(command) {
|
||||
var oExec=shell.Exec(command);
|
||||
var output = new String();
|
||||
while (oExec.Status == 0) {
|
||||
if (!oExec.StdOut.AtEndOfStream) {
|
||||
var line = oExec.StdOut.ReadLine();
|
||||
// XXX: Change to verbose mode
|
||||
// WScript.StdOut.WriteLine(line);
|
||||
output += line;
|
||||
}
|
||||
WScript.sleep(100);
|
||||
}
|
||||
//Check to make sure our scripts did not encounter an error
|
||||
if (!oExec.StdErr.AtEndOfStream) {
|
||||
var line = oExec.StdErr.ReadAll();
|
||||
return {'error' : true, 'output' : line};
|
||||
}
|
||||
return {'error' : false, 'output' : output};
|
||||
}
|
||||
|
||||
// executes a commmand in the shell and outputs stdout and fails on stderr
|
||||
function exec_verbose(command) {
|
||||
//Log("Command: " + command);
|
||||
var oShell=shell.Exec(command);
|
||||
while (oShell.Status == 0) {
|
||||
//Wait a little bit so we're not super looping
|
||||
WScript.sleep(100);
|
||||
//Print any stdout output from the script
|
||||
if (!oShell.StdOut.AtEndOfStream) {
|
||||
var line = oShell.StdOut.ReadLine();
|
||||
Log(line);
|
||||
}
|
||||
}
|
||||
//Check to make sure our scripts did not encounter an error
|
||||
if (!oShell.StdErr.AtEndOfStream) {
|
||||
var line = oShell.StdErr.ReadAll();
|
||||
Log(line, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
|
||||
function get_devices() {
|
||||
var device_list = []
|
||||
var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
|
||||
if (local_devices.match(/\w+\tdevice/)) {
|
||||
devices = local_devices.split('\r\n');
|
||||
//format (ID DESCRIPTION)
|
||||
for (i in devices) {
|
||||
if (devices[i].match(/\w+\tdevice/) && !devices[i].match(/emulator/)) {
|
||||
device_list.push(devices[i].replace(/\t/, ' '));
|
||||
}
|
||||
}
|
||||
}
|
||||
return device_list
|
||||
}
|
||||
|
||||
function list_devices() {
|
||||
var devices = get_devices();
|
||||
if (devices.length > 0) {
|
||||
for (i in devices) {
|
||||
Log(devices[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log('No devices found, if your device is connected and not showing,');
|
||||
Log(' then try and install the drivers for your device.');
|
||||
Log(' http://developer.android.com/tools/extras/oem-usb.html');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function get_emulator_images() {
|
||||
// discription contains all data recieved squashed onto one line
|
||||
var add_description = true;
|
||||
var avd_list = [];
|
||||
var local_emulators = shell.Exec("%comspec% /c android list avds").StdOut.ReadAll();
|
||||
if (local_emulators.match(/Name\:/)) {
|
||||
emulators = local_emulators.split('\n');
|
||||
//format (ID DESCRIPTION)
|
||||
var count = 0;
|
||||
var output = '';
|
||||
for (i in emulators) {
|
||||
if (emulators[i].match(/Name\:/)) {
|
||||
var emulator_name = emulators[i].replace(/\s*Name\:\s/, '') + ' ';
|
||||
if (add_description) {
|
||||
count = 1;
|
||||
output += emulator_name
|
||||
}
|
||||
else {
|
||||
avd_list.push(emulator_name);
|
||||
}
|
||||
}
|
||||
// add description if indicated (all data squeezed onto one line)
|
||||
if (count > 0) {
|
||||
var emulator_description = emulators[i].replace(/\s*/g, '');
|
||||
if (count > 4) {
|
||||
avd_list.push(output + emulator_description);
|
||||
count = 0;
|
||||
output = '';
|
||||
}
|
||||
else {
|
||||
count++;
|
||||
output += emulator_description + ' '
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return avd_list;
|
||||
}
|
||||
|
||||
function list_emulator_images() {
|
||||
var images = get_emulator_images();
|
||||
if (images.length > 0) {
|
||||
for(i in images) {
|
||||
Log(images[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log('No emulators found, if you would like to create an emulator follow the instructions');
|
||||
Log(' provided here : http://developer.android.com/tools/devices/index.html');
|
||||
Log(' Or run \'android create avd --name <name> --target <targetID>\' in on the command line.');
|
||||
}
|
||||
}
|
||||
|
||||
function get_started_emulators() {
|
||||
var started_emulators = [];
|
||||
var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
|
||||
if (local_devices.match(/emulator/)) {
|
||||
devices = local_devices.split('\r\n');
|
||||
//format (ID DESCRIPTION)
|
||||
for (i in devices) {
|
||||
if (devices[i].match(/\w+\tdevice/) && devices[i].match(/emulator/)) {
|
||||
started_emulators.push(devices[i].replace(/\t/, ' '));
|
||||
}
|
||||
}
|
||||
}
|
||||
return started_emulators
|
||||
}
|
||||
|
||||
function list_started_emulators() {
|
||||
var images = get_started_emulators();
|
||||
if (images.length > 0) {
|
||||
for(i in images) {
|
||||
Log(images[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log('No started emulators found, if you would like to start an emulator call \'list-emulator-images\'');
|
||||
Log(' to get the name of an emulator and then start the emulator with \'start-emulator <Name>\'');
|
||||
}
|
||||
}
|
||||
|
||||
function start_emulator(name) {
|
||||
var emulators = get_emulator_images();
|
||||
var started_emulators = get_started_emulators();
|
||||
var num_started = started_emulators.length;
|
||||
var emulator_name;
|
||||
var started = false;
|
||||
if (name) {
|
||||
for (i in emulators) {
|
||||
if (emulators[i].substr(0,name.length) == name) {
|
||||
Log("Starting emulator : " + name);
|
||||
shell.Run("%comspec% /c start cmd /c emulator -avd " + name);
|
||||
//shell.Run("%comspec% /c start cmd /c emulator -cpu-delay 0 -no-boot-anim -cache %Temp%\cache -avd " + name);
|
||||
started = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (emulators.length > 0 && started_emulators < 1) {
|
||||
emulator_name = emulators[0].split(' ', 1)[0];
|
||||
start_emulator(emulator_name);
|
||||
return;
|
||||
} else if (started_emulators.length > 0) {
|
||||
Log("Emulator already started : " + started_emulators[0].split(' ', 1));
|
||||
return;
|
||||
} else {
|
||||
Log("Error : unable to start emulator, ensure you have emulators availible by checking \'list-emulator-images\'", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
if (!started) {
|
||||
Log("Error : unable to start emulator, ensure you have emulators availible by checking \'list-emulator-images\'", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else { // wait for emulator to boot before returning
|
||||
WScript.Stdout.Write('Booting up emulator..');
|
||||
var boot_anim = null;
|
||||
var emulator_ID = null;
|
||||
var new_started = get_started_emulators();
|
||||
var i = 0;
|
||||
// use boot animation property to tell when boot is complete.
|
||||
while ((boot_anim == null || !boot_anim.output.match(/stopped/)) && i < 100) {
|
||||
if (new_started.length > started_emulators.length && emulator_ID == null) {
|
||||
// find new emulator that was just started to get it's ID
|
||||
for(var i = 0; i < new_started.length; i++) {
|
||||
if (new_started[i] != started_emulators[i]) {
|
||||
emulator_ID = new_started[i].split(' ', 1)[0];
|
||||
boot_anim = exec_out('%comspec% /c adb -s ' + emulator_ID + ' shell getprop init.svc.bootanim');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (boot_anim == null) {
|
||||
new_started = get_started_emulators();
|
||||
}
|
||||
else {
|
||||
boot_anim = exec_out('%comspec% /c adb -s ' + emulator_ID + ' shell getprop init.svc.bootanim');
|
||||
}
|
||||
i++;
|
||||
WScript.Stdout.Write('.');
|
||||
WScript.Sleep(2000);
|
||||
}
|
||||
if (i < 100) {
|
||||
Log('\nBoot Complete!');
|
||||
} else {
|
||||
Log('\nEmulator boot timed out. Failed to load emulator');
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function install_device(target) {
|
||||
var devices = get_devices();
|
||||
var use_target = false;
|
||||
if (devices.length < 1) {
|
||||
Log("Error : No devices found to install to, make sure there are devices", true);
|
||||
Log(" availible by checking \'<project_dir>\\cordova\\lib\\list-devices\'", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
if (target) {
|
||||
var exists = false;
|
||||
for (i in devices) {
|
||||
if (devices[i].substr(0,target.length) == target)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists) {
|
||||
Log("Error : Unable to find target " + target, true);
|
||||
Log("Please ensure the target exists by checking \'<project>\\cordova\\lib\\list-devices'");
|
||||
WScript.Quit(2);
|
||||
}
|
||||
use_target = true;
|
||||
}
|
||||
// check if file .apk has been created
|
||||
if (fso.FolderExists(ROOT + '\\bin')) {
|
||||
var path_to_apk;
|
||||
var out_folder = fso.GetFolder(ROOT + '\\bin');
|
||||
var out_files = new Enumerator(out_folder.Files);
|
||||
for (;!out_files.atEnd(); out_files.moveNext()) {
|
||||
var path = out_files.item() + '';
|
||||
if (fso.GetExtensionName(path) == 'apk' && !path.match(/unaligned/)) {
|
||||
path_to_apk = out_files.item();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (path_to_apk) {
|
||||
var launch_name = exec_out("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
|
||||
if (launch_name.error) {
|
||||
Log("Failed to get application name from appinfo.jar + AndroidManifest : ", true);
|
||||
Log("Output : " + launch_name.output, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
// install on device (-d)
|
||||
Log("Installing app on device...");
|
||||
var cmd;
|
||||
if (use_target) {
|
||||
cmd = '%comspec% /c adb -s ' + target + ' install -r ' + path_to_apk;
|
||||
} else {
|
||||
cmd = '%comspec% /c adb -s ' + devices[0].split(' ', 1)[0] + ' install -r ' + path_to_apk;
|
||||
}
|
||||
var install = exec_out(cmd);
|
||||
if ( install.error && install.output.match(/Failure/)) {
|
||||
Log("Error : Could not install apk to device : ", true);
|
||||
Log(install.output, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else {
|
||||
Log(install.output);
|
||||
}
|
||||
// run on device
|
||||
Log("Launching application...");
|
||||
cmd;
|
||||
if (use_target) {
|
||||
cmd = '%comspec% /c adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
|
||||
} else {
|
||||
cmd = '%comspec% /c adb -s ' + devices[0].split(' ', 1)[0] + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
|
||||
}
|
||||
exec_verbose(cmd);
|
||||
}
|
||||
else {
|
||||
Log('Failed to find apk, make sure you project is built and there is an ', true);
|
||||
Log(' apk in <project>\\bin\\. To build your project use \'<project>\\cordova\\build\'', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function install_emulator(target) {
|
||||
var emulators = get_started_emulators();
|
||||
var use_target = false;
|
||||
if (emulators.length < 1) {
|
||||
Log("Error : No emulators found to install to, make sure there are emulators", true);
|
||||
Log(" availible by checking \'<project_dir>\\cordova\\lib\\list-started-emulators\'", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
if (target) {
|
||||
var exists = false;
|
||||
for (i in emulators) {
|
||||
if (emulators[i].substr(0,target.length) == target)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists) {
|
||||
Log("Error : Unable to find target " + target, true);
|
||||
Log("Please ensure the target exists by checking \'<project>\\cordova\\lib\\list-started-emulators'")
|
||||
}
|
||||
use_target = true;
|
||||
} else {
|
||||
target = emulators[0].split(' ', 1)[0];
|
||||
Log("Deploying to emulator : " + target);
|
||||
}
|
||||
// check if file .apk has been created
|
||||
if (fso.FolderExists(ROOT + '\\bin')) {
|
||||
var path_to_apk;
|
||||
var out_folder = fso.GetFolder(ROOT + '\\bin');
|
||||
var out_files = new Enumerator(out_folder.Files);
|
||||
for (;!out_files.atEnd(); out_files.moveNext()) {
|
||||
var path = out_files.item() + '';
|
||||
if (fso.GetExtensionName(path) == 'apk' && !path.match(/unaligned/)) {
|
||||
path_to_apk = out_files.item();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (path_to_apk) {
|
||||
var launch_name = exec_out("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
|
||||
if (launch_name.error) {
|
||||
Log("Failed to get application name from appinfo.jar + AndroidManifest : ", true);
|
||||
Log("Output : " + launch_name.output, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
// install on emulator (-e)
|
||||
Log("Installing app on emulator...");
|
||||
var cmd = '%comspec% /c adb -s ' + target + ' install -r ' + path_to_apk;
|
||||
var install = exec_out(cmd);
|
||||
if ( install.error && install.output.match(/Failure/)) {
|
||||
Log("Error : Could not install apk to emulator : ", true);
|
||||
Log(install.output, true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else {
|
||||
Log(install.output);
|
||||
}
|
||||
// run on emulator
|
||||
Log("Launching application...");
|
||||
cmd;
|
||||
if (use_target) {
|
||||
cmd = '%comspec% /c adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
|
||||
} else {
|
||||
cmd = '%comspec% /c adb -s ' + emulators[0].split(' ', 1)[0] + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output
|
||||
}
|
||||
exec_verbose(cmd);
|
||||
}
|
||||
else {
|
||||
Log('Failed to find apk, make sure you project is built and there is an ', true);
|
||||
Log(' apk in <project>\\bin\\. To build your project use \'<project>\\cordova\\build\'', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log('Failed to find apk, make sure you project is built and there is an ', true);
|
||||
Log(' apk in <project>\\bin\\. To build your project use \'<project>\\cordova\\build\'', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
|
||||
function clean() {
|
||||
Log("Cleaning project...");
|
||||
exec("%comspec% /c ant.bat clean -f "+ROOT+"\\build.xml 2>&1");
|
||||
}
|
||||
|
||||
function build(build_type) {
|
||||
if (build_type) {
|
||||
switch (build_type) {
|
||||
case "--debug" :
|
||||
clean();
|
||||
Log("Building project...");
|
||||
exec_verbose("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
|
||||
break;
|
||||
case "--release" :
|
||||
clean();
|
||||
Log("Building project...");
|
||||
exec_verbose("%comspec% /c ant.bat release -f "+ROOT+"\\build.xml 2>&1");
|
||||
break;
|
||||
case "--nobuild" :
|
||||
Log("Skipping build process.");
|
||||
break;
|
||||
default :
|
||||
Log("Build option not recognized: " + build_type, true);
|
||||
WScript.Quit(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log("WARNING: [ --debug | --release | --nobuild ] not specified, defaulting to --debug.");
|
||||
exec_verbose("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
|
||||
}
|
||||
}
|
||||
|
||||
function log() {
|
||||
// filter out nativeGetEnabledTags spam from latest sdk bug.
|
||||
shell.Run("%comspec% /c adb logcat | grep -v nativeGetEnabledTags");
|
||||
}
|
||||
|
||||
function run(target, build_type) {
|
||||
var use_target = false;
|
||||
if (!target) {
|
||||
Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, using defaults");
|
||||
}
|
||||
// build application
|
||||
build(build_type);
|
||||
// attempt to deploy to connected device
|
||||
var devices = get_devices();
|
||||
if (devices.length > 0 || target == "--device") {
|
||||
if (target) {
|
||||
if (target.substr(0,9) == "--target=") {
|
||||
install_device(target.split('--target=').join(''))
|
||||
} else if (target == "--device") {
|
||||
install_device();
|
||||
} else {
|
||||
Log("Did not regognize " + target + " as a run option.", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, using defaults");
|
||||
install_device();
|
||||
}
|
||||
}
|
||||
else {
|
||||
var emulators = get_started_emulators();
|
||||
if (emulators.length > 0) {
|
||||
install_emulator();
|
||||
}
|
||||
else {
|
||||
var emulator_images = get_emulator_images();
|
||||
if (emulator_images.length < 1) {
|
||||
Log('No emulators found, if you would like to create an emulator follow the instructions', true);
|
||||
Log(' provided here : http://developer.android.com/tools/devices/index.html', true);
|
||||
Log(' Or run \'android create avd --name <name> --target <targetID>\' in on the command line.', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
start_emulator(emulator_images[0].split(' ')[0]);
|
||||
emulators = get_started_emulators();
|
||||
if (emulators.length > 0) {
|
||||
install_emulator();
|
||||
}
|
||||
else {
|
||||
Log("Error : emulator failed to start.", true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var args = WScript.Arguments;
|
||||
if (args.count() == 0) {
|
||||
Log("Error: no args provided.");
|
||||
WScript.Quit(2);
|
||||
}
|
||||
else {
|
||||
if (args(0) == "build") {
|
||||
if (args.Count() > 1) {
|
||||
build(args(1))
|
||||
} else {
|
||||
build();
|
||||
}
|
||||
} else if (args(0) == "clean") {
|
||||
clean();
|
||||
} else if (args(0) == "list-devices") {
|
||||
list_devices();
|
||||
} else if (args(0) == "list-emulator-images") {
|
||||
list_emulator_images();
|
||||
} else if (args(0) == "list-started-emulators") {
|
||||
list_started_emulators();
|
||||
} else if (args(0) == "start-emulator") {
|
||||
if (args.Count() > 1) {
|
||||
start_emulator(args(1))
|
||||
} else {
|
||||
start_emulator();
|
||||
}
|
||||
} else if (args(0) == "log") {
|
||||
log();
|
||||
} else if (args(0) == "install-emulator") {
|
||||
if (args.Count() == 2) {
|
||||
if (args(1).substr(0,9) == "--target=") {
|
||||
install_emulator(args(1).split('--target=').join(''));
|
||||
} else {
|
||||
Log('Error: \"' + args(1) + '\" is not recognized as an install option', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
} else {
|
||||
install_emulator();
|
||||
}
|
||||
} else if (args(0) == "install-device") {
|
||||
if (args.Count() == 2) {
|
||||
if (args(1).substr(0,9) == "--target=") {
|
||||
install_device(args(1).split('--target=').join(''));
|
||||
} else {
|
||||
Log('Error: \"' + args(1) + '\" is not recognized as an install option', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
} else {
|
||||
install_device();
|
||||
}
|
||||
} else if (args(0) == "run") {
|
||||
if (args.Count() == 3) {
|
||||
run(args(1), args(2));
|
||||
}
|
||||
else if (args.Count() == 2) {
|
||||
if (args(1).substr(0,9) == "--target=" ||
|
||||
args(1) == "--emulator" ||
|
||||
args(1) == "--device") {
|
||||
run(args(1));
|
||||
} else if (args(1) == "--debug" ||
|
||||
args(1) == "--release" ||
|
||||
args(1) == "--nobuild") {
|
||||
run(null, args(1))
|
||||
} else {
|
||||
Log('Error: \"' + args(1) + '\" is not recognized as a run option', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
run();
|
||||
}
|
||||
} else {
|
||||
Log('Error: \"' + args(0) + '\" is not recognized as a tooling command', true);
|
||||
WScript.Quit(2);
|
||||
}
|
||||
}
|
||||
|
||||
95
bin/templates/cordova/lib/device.js
vendored
Normal file
95
bin/templates/cordova/lib/device.js
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var shell = require('shelljs'),
|
||||
path = require('path'),
|
||||
build = require('./build'),
|
||||
appinfo = require('./appinfo'),
|
||||
exec = require('child_process').exec,
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
/**
|
||||
* Returns a list of the device ID's found
|
||||
*/
|
||||
module.exports.list = function() {
|
||||
var cmd = 'adb devices';
|
||||
var result = shell.exec(cmd, {silent:true, async:false});
|
||||
if (result.code > 0) {
|
||||
console.error('Failed to execute android command \'' + cmd + '\'.');
|
||||
process.exit(2);
|
||||
} else {
|
||||
var response = result.output.split('\n');
|
||||
var device_list = [];
|
||||
for (var i = 1; i < response.length; i++) {
|
||||
if (response[i].match(/\w+\tdevice/) && !response[i].match(/emulator/)) {
|
||||
device_list.push(response[i].replace(/\tdevice/, '').replace('\r', ''));
|
||||
}
|
||||
}
|
||||
return device_list;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Installs a previously built application on the device
|
||||
* and launches it.
|
||||
*/
|
||||
module.exports.install = function(target) {
|
||||
var device_list = this.list();
|
||||
if (device_list.length > 0) {
|
||||
// default device
|
||||
target = typeof target !== 'undefined' ? target : device_list[0];
|
||||
if (device_list.indexOf(target) > -1) {
|
||||
var apk_path = build.get_apk();
|
||||
var launchName = appinfo.getActivityName();
|
||||
console.log('Installing app on device...');
|
||||
cmd = 'adb -s ' + target + ' install -r ' + apk_path;
|
||||
var install = shell.exec(cmd, {silent:false, async:false});
|
||||
if (install.error || install.output.match(/Failure/)) {
|
||||
console.error('ERROR : Failed to install apk to device : ');
|
||||
console.error(install.output);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
//unlock screen
|
||||
cmd = 'adb -s ' + target + ' shell input keyevent 82';
|
||||
shell.exec(cmd, {silent:true, async:false});
|
||||
|
||||
// launch the application
|
||||
console.log('Launching application...');
|
||||
cmd = 'adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
|
||||
var launch = shell.exec(cmd, {silent:true, async:false});
|
||||
if(launch.code > 0) {
|
||||
console.error('ERROR : Failed to launch application on emulator : ' + launch.error);
|
||||
console.error(launch.output);
|
||||
process.exit(2);
|
||||
} else {
|
||||
console.log('LANCH SUCCESS');
|
||||
}
|
||||
} else {
|
||||
console.error('ERROR : Unable to find target \'' + target + '\'.');
|
||||
console.error('Failed to deploy to device.');
|
||||
process.exit(2);
|
||||
}
|
||||
} else {
|
||||
console.error('ERROR : Failed to deploy to device, no devices found.');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
337
bin/templates/cordova/lib/emulator.js
vendored
Normal file
337
bin/templates/cordova/lib/emulator.js
vendored
Normal file
@@ -0,0 +1,337 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var shell = require('shelljs'),
|
||||
path = require('path'),
|
||||
appinfo = require('./appinfo'),
|
||||
build = require('./build'),
|
||||
ROOT = path.join(__dirname, '..', '..'),
|
||||
new_emulator = 'cordova_emulator';
|
||||
|
||||
/**
|
||||
* Returns a list of emulator images in the form of objects
|
||||
* {
|
||||
name : <emulator_name>,
|
||||
path : <path_to_emulator_image>,
|
||||
target : <api_target>,
|
||||
abi : <cpu>,
|
||||
skin : <skin>
|
||||
}
|
||||
*/
|
||||
module.exports.list_images = function() {
|
||||
var cmd = 'android list avds';
|
||||
var result = shell.exec(cmd, {silent:true, async:false});
|
||||
if (result.code > 0) {
|
||||
console.error('Failed to execute android command \'' + cmd + '\'.');
|
||||
process.exit(2);
|
||||
} else {
|
||||
var response = result.output.split('\n');
|
||||
var emulator_list = [];
|
||||
for (var i = 1; i < response.length; i++) {
|
||||
// To return more detailed information use img_obj
|
||||
var img_obj = {};
|
||||
if (response[i].match(/Name:\s/)) {
|
||||
img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
|
||||
if (response[i + 1].match(/Path:\s/)) {
|
||||
i++;
|
||||
img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
|
||||
}
|
||||
if (response[i + 1].match(/\(API\slevel\s/)) {
|
||||
i++;
|
||||
img_obj['target'] = response[i].replace('\r', '');
|
||||
}
|
||||
if (response[i + 1].match(/ABI:\s/)) {
|
||||
i++;
|
||||
img_obj['abi'] = response[i].split('ABI: ')[1].replace('\r', '');
|
||||
}
|
||||
if (response[i + 1].match(/Skin:\s/)) {
|
||||
i++;
|
||||
img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', '');
|
||||
}
|
||||
|
||||
emulator_list.push(img_obj);
|
||||
}
|
||||
/* To just return a list of names use this
|
||||
if (response[i].match(/Name:\s/)) {
|
||||
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
|
||||
}*/
|
||||
|
||||
}
|
||||
return emulator_list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return the closest avd to the projects target
|
||||
* or undefined if no avds exist.
|
||||
*/
|
||||
module.exports.best_image = function() {
|
||||
var project_target = this.get_target().replace('android-', '');
|
||||
var images = this.list_images();
|
||||
var closest = 9999;
|
||||
var best = images[0];
|
||||
for (i in images) {
|
||||
var target = images[i].target;
|
||||
if(target) {
|
||||
var num = target.split('(API level ')[1].replace(')', '');
|
||||
if (num == project_target) {
|
||||
return images[i];
|
||||
} else if (project_target - num < closest && project_target > num) {
|
||||
var closest = project_target - num;
|
||||
best = images[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
module.exports.list_started = function() {
|
||||
var cmd = 'adb devices';
|
||||
var result = shell.exec(cmd, {silent:true, async:false});
|
||||
if (result.code > 0) {
|
||||
console.error('Failed to execute android command \'' + cmd + '\'.');
|
||||
process.exit(2);
|
||||
} else {
|
||||
var response = result.output.split('\n');
|
||||
var started_emulator_list = [];
|
||||
for (var i = 1; i < response.length; i++) {
|
||||
if (response[i].match(/device/) && response[i].match(/emulator/)) {
|
||||
started_emulator_list.push(response[i].replace(/\tdevice/, '').replace('\r', ''));
|
||||
}
|
||||
}
|
||||
return started_emulator_list;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.get_target = function() {
|
||||
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties'));
|
||||
return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
|
||||
}
|
||||
|
||||
module.exports.list_targets = function() {
|
||||
var target_out = shell.exec('android list targets', {silent:true, async:false}).output.split('\n');
|
||||
var targets = [];
|
||||
for (var i = target_out.length; i >= 0; i--) {
|
||||
if(target_out[i].match(/id:/)) {
|
||||
targets.push(targets[i].split(' ')[1]);
|
||||
}
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
/*
|
||||
* Starts an emulator with the given ID,
|
||||
* and returns the started ID of that emulator.
|
||||
* If no ID is given it will used the first image availible,
|
||||
* if no image is availible it will error out (maybe create one?).
|
||||
*/
|
||||
module.exports.start = function(emulator_ID) {
|
||||
var started_emulators = this.list_started();
|
||||
var num_started = started_emulators.length;
|
||||
if (typeof emulator_ID === 'undefined') {
|
||||
var emulator_list = this.list_images();
|
||||
if (emulator_list.length > 0) {
|
||||
emulator_ID = this.best_image().name;
|
||||
console.log('WARNING : no emulator specified, defaulting to ' + emulator_ID);
|
||||
} else {
|
||||
console.error('ERROR : No emulator images (avds) found, if you would like to create an');
|
||||
console.error(' avd follow the instructions provided here : ');
|
||||
console.error(' http://developer.android.com/tools/devices/index.html')
|
||||
console.error(' Or run \'android create avd --name <name> --target <targetID>\' ');
|
||||
console.error(' in on the command line.');
|
||||
process.exit(2);
|
||||
/*console.log('WARNING : no emulators availible, creating \'' + new_emulator + '\'.');
|
||||
this.create_image(new_emulator, this.get_target());
|
||||
emulator_ID = new_emulator;*/
|
||||
}
|
||||
}
|
||||
|
||||
var pipe_null = (process.platform == 'win32' || process.platform == 'win64'? '> NUL' : '> /dev/null');
|
||||
var cmd = 'emulator -avd ' + emulator_ID + ' ' + pipe_null + ' &';
|
||||
if(process.platform == 'win32' || process.platform == 'win64') {
|
||||
cmd = '%comspec% /c start cmd /c ' + cmd;
|
||||
}
|
||||
var result = shell.exec(cmd, {silent:true, async:false}, function(code, output) {
|
||||
if (code > 0) {
|
||||
console.error('Failed to execute android command \'' + cmd + '\'.');
|
||||
console.error(output);
|
||||
process.exit(2);
|
||||
}
|
||||
});
|
||||
|
||||
// wait for emulator to start
|
||||
console.log('Waiting for emulator...');
|
||||
var new_started = this.wait_for_emulator(num_started);
|
||||
var emulator_id;
|
||||
if (new_started.length > 1) {
|
||||
for (i in new_started) {
|
||||
console.log(new_started[i]);
|
||||
console.log(started_emulators.indexOf(new_started[i]));
|
||||
if (started_emulators.indexOf(new_started[i]) < 0) {
|
||||
emulator_id = new_started[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
emulator_id = new_started[0];
|
||||
}
|
||||
if (!emulator_id) {
|
||||
console.error('ERROR : Failed to start emulator, could not find new emulator');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
//wait for emulator to boot up
|
||||
process.stdout.write('Booting up emulator (this may take a while)...');
|
||||
this.wait_for_boot(emulator_id);
|
||||
console.log('BOOT COMPLETE');
|
||||
|
||||
//unlock screen
|
||||
cmd = 'adb -s ' + emulator_id + ' shell input keyevent 82';
|
||||
shell.exec(cmd, {silent:false, async:false});
|
||||
|
||||
//return the new emulator id for the started emulators
|
||||
return emulator_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Waits for the new emulator to apear on the started-emulator list.
|
||||
*/
|
||||
module.exports.wait_for_emulator = function(num_running) {
|
||||
var new_started = this.list_started();
|
||||
if (new_started.length > num_running) {
|
||||
return new_started;
|
||||
} else {
|
||||
this.sleep(1);
|
||||
return this.wait_for_emulator(num_running);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Waits for the boot animation property of the emulator to switch to 'stopped'
|
||||
*/
|
||||
module.exports.wait_for_boot = function(emulator_id) {
|
||||
var cmd;
|
||||
// ShellJS opens a lot of file handles, and the default on OS X is too small.
|
||||
// TODO : This is not working, need to find a better way to increese the ulimit.
|
||||
if(process.platform == 'win32' || process.platform == 'win64') {
|
||||
cmd = 'adb -s ' + emulator_id + ' shell getprop init.svc.bootanim';
|
||||
} else {
|
||||
cmd = 'ulimit -S -n 4096; adb -s ' + emulator_id + ' shell getprop init.svc.bootanim';
|
||||
}
|
||||
var boot_anim = shell.exec(cmd, {silent:true, async:false});
|
||||
if (boot_anim.output.match(/stopped/)) {
|
||||
return;
|
||||
} else {
|
||||
process.stdout.write('.');
|
||||
this.sleep(3);
|
||||
return this.wait_for_boot(emulator_id);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO : find a better way to wait for the emulator (maybe using async methods?)
|
||||
*/
|
||||
module.exports.sleep = function(time_sec) {
|
||||
if (process.platform == 'win32' || process.platform == 'win64') {
|
||||
shell.exec('ping 127.0.0.1 -n ' + time_sec, {silent:true, async:false});
|
||||
} else {
|
||||
shell.exec('sleep ' + time_sec, {silent:true, async:false});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create avd
|
||||
* TODO : Enter the stdin input required to complete the creation of an avd.
|
||||
*/
|
||||
module.exports.create_image = function(name, target) {
|
||||
console.log('Creating avd named ' + name);
|
||||
if (target) {
|
||||
var cmd = 'android create avd --name ' + name + ' --target ' + target;
|
||||
var create = shell.exec(cmd, {sient:false, async:false});
|
||||
if (create.error) {
|
||||
console.error('ERROR : Failed to create emulator image : ');
|
||||
console.error(' Do you have the latest android targets including ' + target + '?');
|
||||
console.error(create.output);
|
||||
process.exit(2);
|
||||
}
|
||||
} else {
|
||||
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
|
||||
var cmd = 'android create avd --name ' + name + ' --target ' + this.list_targets()[0];
|
||||
var create = shell.exec(cmd, {sient:false, async:false});
|
||||
if (create.error) {
|
||||
console.error('ERROR : Failed to create emulator image : ');
|
||||
console.error(create.output);
|
||||
process.exit(2);
|
||||
}
|
||||
console.error('ERROR : Unable to create an avd emulator, no targets found.');
|
||||
console.error('Please insure you have targets availible by runing the "android" command').
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Installs a previously built application on the emulator and launches it.
|
||||
* If no target is specified, then it picks one.
|
||||
* If no started emulators are found, error out.
|
||||
*/
|
||||
module.exports.install = function(target) {
|
||||
var emulator_list = this.list_started();
|
||||
if (emulator_list.length < 1) {
|
||||
console.error('ERROR : No started emulators found, please start an emultor before deploying your project.');
|
||||
process.exit(2);
|
||||
/*console.log('WARNING : No started emulators found, attemting to start an avd...');
|
||||
this.start(this.best_image().name);*/
|
||||
}
|
||||
// default emulator
|
||||
target = typeof target !== 'undefined' ? target : emulator_list[0];
|
||||
if (emulator_list.indexOf(target) > -1) {
|
||||
console.log('Installing app on emulator...');
|
||||
var apk_path = build.get_apk();
|
||||
var cmd = 'adb -s ' + target + ' install -r ' + apk_path;
|
||||
var install = shell.exec(cmd, {sient:false, async:false});
|
||||
if (install.error || install.output.match(/Failure/)) {
|
||||
console.error('ERROR : Failed to install apk to emulator : ');
|
||||
console.error(install.output);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
//unlock screen
|
||||
cmd = 'adb -s ' + target + ' shell input keyevent 82';
|
||||
shell.exec(cmd, {silent:true, async:false});
|
||||
|
||||
// launch the application
|
||||
console.log('Launching application...');
|
||||
var launchName = appinfo.getActivityName();
|
||||
cmd = 'adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
|
||||
console.log(cmd);
|
||||
var launch = shell.exec(cmd, {silent:false, async:false});
|
||||
if(launch.code > 0) {
|
||||
console.error('ERROR : Failed to launch application on emulator : ' + launch.error);
|
||||
console.error(launch.output);
|
||||
process.exit(2);
|
||||
} else {
|
||||
console.log('LANCH SUCCESS');
|
||||
}
|
||||
} else {
|
||||
console.error('ERROR : Unable to find target \'' + target + '\'.');
|
||||
console.error('Failed to deploy to emulator.');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,38 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$DIR/../.." && pwd )
|
||||
device_list=$("$DIR/list-devices")
|
||||
if [ $? != 0 ]; then
|
||||
echo "No devices found to deploy to. Please make sure your device is connected"
|
||||
echo " and you can view it using the 'cordova/lib/list-devices' command."
|
||||
exit 2
|
||||
fi
|
||||
/*
|
||||
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
|
||||
|
||||
apks=`find $PROJECT_PATH/bin -type f -maxdepth 1 | egrep '\.apk$'`
|
||||
apk_list=($apks)
|
||||
if [[ ${#apk_list[@]} > 0 ]] ; then
|
||||
# handle target
|
||||
read -ra device_array <<< "$device_list"
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
# deploy to given target
|
||||
target=${1/--target=/}
|
||||
else
|
||||
# delete trailing space and 'device' after device ID
|
||||
target=${device_array[0]}
|
||||
fi
|
||||
echo "Installing ${apk_list[0]} onto device $target..."
|
||||
adb -s $target install -r ${apk_list[0]};
|
||||
echo "Launching application..."
|
||||
launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
|
||||
adb -s $target shell am start -W -a android.intent.action.MAIN -n $launch_str
|
||||
else
|
||||
echo "Application package not found, could not install to device"
|
||||
echo " make sure your application is built before deploying."
|
||||
exit 2
|
||||
fi
|
||||
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.
|
||||
*/
|
||||
|
||||
var device = require('./device'),
|
||||
args = process.argv;
|
||||
|
||||
if(args.length > 2) {
|
||||
var install_target;
|
||||
if (args[2].substring(0, 9) == '--target=') {
|
||||
install_target = args[2].substring(9, args[2].length);
|
||||
device.install(install_target);
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
|
||||
process.exit(2);
|
||||
}
|
||||
} else {
|
||||
device.install();
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -14,12 +14,13 @@
|
||||
:: KIND, either express or implied. See the License for the
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
SET full_path=%~dp0
|
||||
IF EXIST %full_path%cordova.js (
|
||||
cscript "%full_path%cordova.js" install-device %* //nologo
|
||||
SET script_path="%~dp0install-device"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'install-device' script in 'cordova\lib' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
)
|
||||
@@ -1,50 +1,38 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$DIR/../.." && pwd )
|
||||
emulator_list=$("$DIR/list-started-emulators")
|
||||
if [ $? != 0 ]; then
|
||||
echo "No emulators found to deploy to. Please make sure your emulator is started"
|
||||
echo " You can view it using the 'cordova/lib/list-started-emulators' command."
|
||||
echo " You can view created emulator images using the 'cordova/lib/list-emulator-images' command."
|
||||
echo " You can start an emulator image using the 'cordova/lib/start-emulator' command."
|
||||
exit 2
|
||||
fi
|
||||
/*
|
||||
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
|
||||
|
||||
apks=`find $PROJECT_PATH/bin -type f -maxdepth 1 | egrep '\.apk$'`
|
||||
apk_list=($apks)
|
||||
if [[ ${#apk_list[@]} > 0 ]] ; then
|
||||
# handle target emulator
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
# deploy to given target
|
||||
target=${1/--target=/}
|
||||
else
|
||||
# delete trailing space and 'device' after emulator ID
|
||||
target=${emulator_list[0]}
|
||||
fi
|
||||
echo "Installing ${apk_list[0]} onto emulator $target..."
|
||||
adb -s $target install -r ${apk_list[0]};
|
||||
echo "Launching application..."
|
||||
launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
|
||||
adb -s $target shell am start -W -a android.intent.action.MAIN -n $launch_str
|
||||
else
|
||||
echo "Application package not found, could not install to device"
|
||||
echo " make sure your application is built before deploying."
|
||||
exit 2
|
||||
fi
|
||||
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.
|
||||
*/
|
||||
|
||||
var emulator = require('./emulator'),
|
||||
args = process.argv;
|
||||
|
||||
if(args.length > 2) {
|
||||
var install_target;
|
||||
if (args[2].substring(0, 9) == '--target=') {
|
||||
install_target = args[2].substring(9, args[2].length);
|
||||
emulator.install(install_target);
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
|
||||
process.exit(2);
|
||||
}
|
||||
} else {
|
||||
emulator.install();
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -14,12 +14,13 @@
|
||||
:: KIND, either express or implied. See the License for the
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
SET full_path=%~dp0
|
||||
IF EXIST %full_path%cordova.js (
|
||||
cscript "%full_path%cordova.js" install-emulator %* //nologo
|
||||
SET script_path="%~dp0install-emulator"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'install-emulator' script in 'cordova\lib' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
)
|
||||
@@ -1,30 +1,28 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator' | awk '{ print $1; }'`
|
||||
device_list=($devices)
|
||||
if [[ ${#device_list[@]} > 0 ]] ; then
|
||||
for i in ${devices[@]}
|
||||
do
|
||||
echo $i
|
||||
done
|
||||
exit 0
|
||||
else
|
||||
echo "No devices found."
|
||||
exit 2
|
||||
fi
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var devices = require('./device');
|
||||
|
||||
// Usage support for when args are given
|
||||
var device_list = devices.list();
|
||||
for(device in device_list) {
|
||||
console.log(device_list[device]);
|
||||
}
|
||||
@@ -14,12 +14,13 @@
|
||||
:: KIND, either express or implied. See the License for the
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
SET full_path=%~dp0
|
||||
IF EXIST %full_path%cordova.js (
|
||||
cscript "%full_path%cordova.js" list-devices //nologo
|
||||
SET script_path="%~dp0list-devices"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'list-devices' script in 'cordova\lib' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
)
|
||||
@@ -1,32 +1,29 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
|
||||
emulator_list=($emulator_images)
|
||||
if [[ ${#emulator_list[@]} > 0 ]] ; then
|
||||
for i in ${emulator_list[@]}
|
||||
do
|
||||
echo $i
|
||||
done
|
||||
exit 0
|
||||
else
|
||||
echo "No emulators found, if you would like to create an emulator follow the instructions"
|
||||
echo " provided here : http://developer.android.com/tools/devices/index.html"
|
||||
echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
|
||||
exit 2
|
||||
fi
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var emulators = require('./emulator');
|
||||
|
||||
// Usage support for when args are given
|
||||
var emulator_list = emulators.list_images();
|
||||
for(emulator in emulator_list) {
|
||||
console.log(emulator_list[emulator].name);
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -14,12 +14,13 @@
|
||||
:: KIND, either express or implied. See the License for the
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
SET full_path=%~dp0
|
||||
IF EXIST %full_path%cordova.js (
|
||||
cscript "%full_path%cordova.js" list-emulator-images //nologo
|
||||
SET script_path="%~dp0list-emulator-images"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
|
||||
ECHO ERROR: Could not find 'list-emulator-images' script in 'cordova\lib' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print $1;}}' | grep 'emulator' | grep -v 'offline'`
|
||||
read -ra emulator_list <<< "$devices"
|
||||
if [[ ${#emulator_list[@]} > 0 ]] ; then
|
||||
for i in ${emulator_list[@]}
|
||||
do
|
||||
# remove space and 'device'
|
||||
echo $i
|
||||
done
|
||||
exit 0
|
||||
else
|
||||
echo "No started emulators found (it may still be booting up), you can start an emulator by using the command"
|
||||
echo " 'cordova/lib/start-emulator'"
|
||||
exit 2
|
||||
fi
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var emulators = require('./emulator');
|
||||
|
||||
// Usage support for when args are given
|
||||
var emulator_list = emulators.list_started();
|
||||
for(emulator in emulator_list) {
|
||||
console.log(emulator_list[emulator]);
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -14,12 +14,13 @@
|
||||
:: KIND, either express or implied. See the License for the
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
SET full_path=%~dp0
|
||||
IF EXIST %full_path%cordova.js (
|
||||
cscript "%full_path%cordova.js" list-started-emulators //nologo
|
||||
SET script_path="%~dp0list-started-emulators"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'list-started-emulators' script in 'cordova\lib' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
)
|
||||
@@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@@ -17,27 +19,25 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova;
|
||||
var shell = require('shelljs'),
|
||||
path = require('path'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.json.JSONArray;
|
||||
|
||||
public class SplashScreen extends CordovaPlugin {
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
if (action.equals("hide")) {
|
||||
this.webView.postMessage("splashscreen", "hide");
|
||||
} else if (action.equals("show")){
|
||||
this.webView.postMessage("splashscreen", "show");
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
callbackContext.success();
|
||||
return true;
|
||||
/*
|
||||
* Starts running logcat in the shell.
|
||||
*/
|
||||
module.exports.run = function() {
|
||||
var cmd = 'adb logcat | grep -v nativeGetEnabledTags';
|
||||
var result = shell.exec(cmd, {silent:false, async:false});
|
||||
if (result.code > 0) {
|
||||
console.error('ERROR: Failed to run logcat command.');
|
||||
console.error(result.output);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports.help = function() {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'corodva', 'log')));
|
||||
console.log('Gives the logcat output on the command line.');
|
||||
process.exit(0);
|
||||
}
|
||||
123
bin/templates/cordova/lib/run.js
vendored
Normal file
123
bin/templates/cordova/lib/run.js
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var path = require('path'),
|
||||
build = require('./build'),
|
||||
emulator = require('./emulator'),
|
||||
device = require('./device');
|
||||
|
||||
/*
|
||||
* Runs the application on a device if availible.
|
||||
* If not device is found, it will use a started emulator.
|
||||
* If no started emulators are found it will attempt to start an avd.
|
||||
* If no avds are found it will error out.
|
||||
*/
|
||||
module.exports.run = function(args) {
|
||||
var build_type;
|
||||
var install_target;
|
||||
|
||||
for (var i=2; i<args.length; i++) {
|
||||
if (args[i] == '--debug') {
|
||||
build_type = '--debug';
|
||||
} else if (args[i] == '--release') {
|
||||
build_type = '--release';
|
||||
} else if (args[i] == '--nobuild') {
|
||||
build_type = '--nobuild';
|
||||
} else if (args[i] == '--device') {
|
||||
install_target = '--device';
|
||||
} else if (args[i] == '--emulator') {
|
||||
install_target = '--emulator';
|
||||
} else if (args[i].substring(0, 9) == '--target=') {
|
||||
install_target = args[i].substring(9, args[i].length);
|
||||
} else {
|
||||
console.error('ERROR : Run option \'' + args[i] + '\' not recognized.');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
build.run(build_type);
|
||||
if (install_target == '--device') {
|
||||
device.install();
|
||||
} else if (install_target == '--emulator') {
|
||||
if (emulator.list_started() == 0) {
|
||||
emulator.start();
|
||||
}
|
||||
emulator.install();
|
||||
} else if (install_target) {
|
||||
var devices = device.list();
|
||||
var started_emulators = emulator.list_started();
|
||||
var avds = emulator.list_images();
|
||||
if (devices.indexOf(install_target) > -1) {
|
||||
device.install(install_target);
|
||||
} else if (started_emulators.indexOf(install_target) > -1) {
|
||||
emulator.install(install_target);
|
||||
} else {
|
||||
// if target emulator isn't started, then start it.
|
||||
var emulator_ID;
|
||||
for(avd in avds) {
|
||||
if(avds[avd].name == install_target) {
|
||||
emulator_ID = emulator.start(install_target);
|
||||
emulator.install(emulator_ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!emulator_ID) {
|
||||
console.error('ERROR : Target \'' + install_target + '\' not found, unalbe to run project');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no target given, deploy to device if availible, otherwise use the emulator.
|
||||
var device_list = device.list();
|
||||
if (device_list.length > 0) {
|
||||
console.log('WARNING : No target specified, deploying to device \'' + device_list[0] + '\'.');
|
||||
device.install(device_list[0])
|
||||
} else {
|
||||
var emulator_list = emulator.list_started();
|
||||
if (emulator_list.length > 0) {
|
||||
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_list[0] + '\'.');
|
||||
emulator.install(emulator_list[0]);
|
||||
} else {
|
||||
console.log('WARNING : No started emulators found, starting an emulator.');
|
||||
var best_avd = emulator.best_image();
|
||||
if(best_avd) {
|
||||
var emulator_ID = emulator.start(best_avd.name);
|
||||
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_ID + '\'.');
|
||||
emulator.install(emulator_ID);
|
||||
} else {
|
||||
emulator.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.help = function() {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), args[0]) + ' [options]');
|
||||
console.log('Build options :');
|
||||
console.log(' --debug : Builds project in debug mode');
|
||||
console.log(' --release : Builds project in release mode');
|
||||
console.log(' --nobuild : Runs the currently built project without recompiling');
|
||||
console.log('Deploy options :');
|
||||
console.log(' --device : Will deploy the built project to a device');
|
||||
console.log(' --emulator : Will deploy the built project to an emulator if one exists');
|
||||
console.log(' --target=<target_id> : Installs to the target with the specified id.');
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -1,91 +1,38 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$( dirname "$0" )/../.." && pwd )
|
||||
/*
|
||||
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
|
||||
|
||||
function dot {
|
||||
sleep 1
|
||||
echo -n "."
|
||||
}
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
function wait_for_emulator {
|
||||
local i="0"
|
||||
echo -n "Waiting for emulator"
|
||||
emulator_string=$($DIR/list-started-emulators)
|
||||
while [ $? != 0 ]
|
||||
do
|
||||
dot
|
||||
i=$[i+1]
|
||||
emulator_string=$($DIR/list-started-emulators)
|
||||
done
|
||||
read -ra target <<< "$emulator_string"
|
||||
echo ""
|
||||
echo -n "Waiting for it to boot up (this can take a while)"
|
||||
while [ $i -lt 300 ]
|
||||
do
|
||||
boot_anim=$(adb -s $target shell getprop init.svc.bootanim 2>&1)
|
||||
if [[ "$boot_anim" =~ "stopped" ]] ; then
|
||||
break
|
||||
else
|
||||
i=$[i+1]
|
||||
dot
|
||||
fi
|
||||
done
|
||||
# Device timeout: emulator has not started in time
|
||||
if [ $i -eq 300 ]
|
||||
then
|
||||
echo ""
|
||||
echo "Emulator timeout!"
|
||||
exit 69
|
||||
else
|
||||
echo ""
|
||||
echo "Connected!"
|
||||
fi
|
||||
# Unlock the device
|
||||
adb -s $target shell input keyevent 82
|
||||
exit 0
|
||||
}
|
||||
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.
|
||||
*/
|
||||
|
||||
emulator_images=$("$DIR/list-emulator-images")
|
||||
if [ $? != 0 ]; then
|
||||
echo "No emulators found, if you would like to create an emulator follow the instructions"
|
||||
echo " provided here : http://developer.android.com/tools/devices/index.html"
|
||||
echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
|
||||
exit 2
|
||||
fi
|
||||
var emulator = require('./emulator'),
|
||||
args = process.argv;
|
||||
|
||||
# if target emulator is provided
|
||||
if [[ "$#" -eq 1 ]] ; then
|
||||
# check that it exists
|
||||
if [[ $emulator_images =~ $1 ]] ; then
|
||||
#xterm -e emulator -avd $1 &
|
||||
emulator -avd $1 1> /dev/null 2>&1 &
|
||||
else
|
||||
echo "Could not find the provided emulator '$1', make sure the emulator exists"
|
||||
echo " by checking 'cordova/lib/list-emulator-images'"
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
# start first emulator
|
||||
read -ra emulator_list <<< "$emulator_images"
|
||||
#xterm -e emulator -avd ${emulator_list[0]} &
|
||||
emulator -avd ${emulator_list[0]} 1> /dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
wait_for_emulator
|
||||
if(args.length > 2) {
|
||||
var install_target;
|
||||
if (args[2].substring(0, 9) == '--target=') {
|
||||
install_target = args[2].substring(9, args[2].length);
|
||||
emulator.start(install_target);
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
|
||||
process.exit(2);
|
||||
}
|
||||
} else {
|
||||
emulator.start();
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -14,12 +14,13 @@
|
||||
:: KIND, either express or implied. See the License for the
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
SET full_path=%~dp0
|
||||
IF EXIST %full_path%cordova.js (
|
||||
cscript "%full_path%cordova.js" start-emulator %* //nologo
|
||||
SET script_path="%~dp0start-emulator"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'start-emulator' script in 'cordova\lib' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
)
|
||||
@@ -1,20 +1,33 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
# filter out nativeGetEnabledTags spam from latest sdk bug.
|
||||
adb logcat | grep -v nativeGetEnabledTags
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
var log = require('./lib/log'),
|
||||
reqs = require('./lib/check_reqs'),
|
||||
args = process.argv;
|
||||
|
||||
// Usage support for when args are given
|
||||
if(args.length > 2) {
|
||||
log.help();
|
||||
} else if(reqs.run()) {
|
||||
log.run();
|
||||
} else {
|
||||
process.exit(2);
|
||||
}
|
||||
@@ -14,5 +14,13 @@
|
||||
:: KIND, either express or implied. See the License for the
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
%~dp0\cordova.bat log %*
|
||||
SET script_path="%~dp0log"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'log' script in 'cordova' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
@@ -1,81 +1,35 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_PATH=$( cd "$DIR/.." && pwd )
|
||||
/*
|
||||
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
|
||||
|
||||
function run_on_device_or_emulator {
|
||||
devices=`$DIR/lib/list-devices`
|
||||
if [ $? = 0 ]; then
|
||||
$DIR/lib/install-device
|
||||
else
|
||||
run_on_emulator
|
||||
fi
|
||||
}
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
function run_on_emulator {
|
||||
emulators=`$DIR/lib/list-started-emulators`
|
||||
if [ $? = 0 ] ; then
|
||||
$DIR/lib/install-emulator
|
||||
else
|
||||
images=`$DIR/lib/list-emulator-images`
|
||||
if [ $? = 0 ] ; then
|
||||
$DIR/lib/start-emulator
|
||||
$DIR/lib/install-emulator
|
||||
else
|
||||
echo "No devices/emulators started nor images available to start. How are we supposed to do this, then?"
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
}
|
||||
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.
|
||||
*/
|
||||
|
||||
var run = require('./lib/run'),
|
||||
reqs = require('./lib/check_reqs'),
|
||||
args = process.argv;
|
||||
|
||||
|
||||
if [[ "$#" -eq 2 ]] ; then
|
||||
# TODO: the order of arguments here may be reversed from the assumption below
|
||||
$DIR/build $2
|
||||
if [[ $1 == "--device" ]] ; then
|
||||
$DIR/lib/install-device
|
||||
elif [[ $1 == "--emulator" ]] ; then
|
||||
run_on_emulator
|
||||
elif [[ $1 =~ "--target=" ]]; then
|
||||
$DIR/lib/install-device $1
|
||||
else
|
||||
echo "Error : '$1' is not recognized as an install option"
|
||||
fi
|
||||
elif [[ "$#" -eq 1 ]] ; then
|
||||
if [[ $1 == "--debug" || $1 == "--release" || $1 == "--nobuild" ]] ; then
|
||||
$DIR/build $1
|
||||
run_on_device_or_emulator
|
||||
elif [[ $1 == "--device" ]] ; then
|
||||
$DIR/build
|
||||
$DIR/lib/install-device
|
||||
elif [[ $1 == "--emulator" ]] ; then
|
||||
#$DIR/build
|
||||
run_on_emulator
|
||||
elif [[ $1 =~ "--target=" ]]; then
|
||||
$DIR/build
|
||||
$DIR/lib/install-device $1
|
||||
else
|
||||
echo "Error : '$1' is not recognized as an install option"
|
||||
fi
|
||||
else
|
||||
echo "Warning : [ --device | --emulate | --target=<targetID> ] not specified, using defaults."
|
||||
$DIR/build
|
||||
run_on_device_or_emulator
|
||||
fi
|
||||
// Support basic help commands
|
||||
if (args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
|
||||
args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
|
||||
run.help();
|
||||
} else if(reqs.run()) {
|
||||
run.run(args);
|
||||
process.exit(0);
|
||||
} else {
|
||||
process.exit(2);
|
||||
}
|
||||
@@ -14,5 +14,13 @@
|
||||
:: KIND, either express or implied. See the License for the
|
||||
:: specific language governing permissions and limitations
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
%~dp0\cordova.bat run %*
|
||||
SET script_path="%~dp0run"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'run' script in 'cordova' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
@@ -1,32 +1,25 @@
|
||||
#!/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.
|
||||
#!/usr/bin/env node
|
||||
|
||||
set -e
|
||||
/*
|
||||
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
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd -P)
|
||||
PROJECT_PATH="$(dirname "$CORDOVA_PATH")"
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
VERSION_FILE_PATH="$PROJECT_PATH/assets/www/cordova.js"
|
||||
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.
|
||||
*/
|
||||
|
||||
if [ -f "$VERSION_FILE_PATH" ]; then
|
||||
JSVersion=$(sed -n '2,2p' $VERSION_FILE_PATH)
|
||||
echo $JSVersion | sed -e 's/\/\/ //'| cut -f 1 -d '-'
|
||||
else
|
||||
echo "The file \"$VERSION_FILE_PATH\" does not exist."
|
||||
exit 1
|
||||
fi
|
||||
// Coho updates this line:
|
||||
var VERSION = '3.1.0-dev';
|
||||
|
||||
console.log(VERSION);
|
||||
|
||||
@@ -14,18 +14,13 @@
|
||||
:: 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 ant.bat android.bat) do (
|
||||
SET FOUND=%%~$PATH:X
|
||||
IF NOT DEFINED FOUND GOTO MISSING
|
||||
SET script_path="%~dp0version"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'version' script in 'cordova' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
cscript %~dp0\lib\cordova.js %* //nologo
|
||||
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
|
||||
@@ -28,23 +28,7 @@
|
||||
android:anyDensity="true"
|
||||
/>
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.RECORD_VIDEO"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
|
||||
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||
android:hardwareAccelerated="true"
|
||||
@@ -59,5 +43,5 @@
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="__APILEVEL__"/>
|
||||
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="__APILEVEL__"/>
|
||||
</manifest>
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
var build_path = __dirname + '/../..',
|
||||
project_path = '/tmp/example',
|
||||
package_name = 'org.apache.cordova.example',
|
||||
package_as_path = 'org/apache/cordova/example',
|
||||
project_name = 'cordovaExample';
|
||||
|
||||
var path = require('path'),
|
||||
fs = require('fs'),
|
||||
util = require('util'),
|
||||
assert = require('assert'),
|
||||
spawn = require('child_process').spawn;
|
||||
|
||||
var version = fs.readFileSync(build_path + '/VERSION').toString().replace('\n', '');
|
||||
|
||||
assert(version !== undefined);
|
||||
assert(version !== '');
|
||||
|
||||
var create_project = spawn(build_path + '/bin/create',
|
||||
[project_path,
|
||||
package_name,
|
||||
project_name]);
|
||||
|
||||
process.on('uncaughtException', function (err) {
|
||||
console.log('Caught exception: ' + err);
|
||||
spawn('rm', ['-rf', project_path], function(code) {
|
||||
if(code != 0) {
|
||||
console.log("Could not delete project directory");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
create_project.on('exit', function(code) {
|
||||
|
||||
assert.equal(code, 0, 'Project did not get created');
|
||||
|
||||
// make sure the project was created
|
||||
path.exists(project_path, function(exists) {
|
||||
assert(exists, 'Project path does not exist');
|
||||
});
|
||||
|
||||
// make sure the build directory was cleaned up
|
||||
// path.exists(build_path + '/framework/libs', function(exists) {
|
||||
// assert(!exists, 'libs directory did not get cleaned up');
|
||||
// });
|
||||
path.exists(build_path + util.format('/framework/assets/cordova-%s.js', version), function(exists) {
|
||||
assert(!exists, 'javascript file did not get cleaned up');
|
||||
});
|
||||
path.exists(build_path + util.format('/framework/cordova-%s.jar', version), function(exists) {
|
||||
assert(!exists, 'jar file did not get cleaned up');
|
||||
});
|
||||
|
||||
// make sure AndroidManifest.xml was added
|
||||
path.exists(util.format('%s/AndroidManifest.xml', project_path), function(exists) {
|
||||
assert(exists, 'AndroidManifest.xml did not get created');
|
||||
// TODO check that the activity name was properly substituted
|
||||
});
|
||||
|
||||
// make sure main Activity was added
|
||||
path.exists(util.format('%s/src/%s/%s.java', project_path, package_as_path, project_name), function(exists) {
|
||||
assert(exists, 'Activity did not get created');
|
||||
// TODO check that package name and activity name were substituted properly
|
||||
});
|
||||
|
||||
// make sure plugins.xml was added
|
||||
path.exists(util.format('%s/res/xml/plugins.xml', project_path), function(exists) {
|
||||
assert(exists, 'plugins.xml did not get created');
|
||||
});
|
||||
|
||||
// make sure cordova.xml was added
|
||||
path.exists(util.format('%s/res/xml/cordova.xml', project_path), function(exists) {
|
||||
assert(exists, 'plugins.xml did not get created');
|
||||
});
|
||||
|
||||
// make sure cordova.jar was added
|
||||
path.exists(util.format('%s/libs/cordova-%s.jar', project_path, version), function(exists) {
|
||||
assert(exists, 'cordova.jar did not get added');
|
||||
});
|
||||
|
||||
// make sure cordova.js was added
|
||||
path.exists(util.format('%s/assets/www/cordova-%s.js', project_path, version), function(exists) {
|
||||
assert(exists, 'cordova.js did not get added');
|
||||
});
|
||||
|
||||
// make sure cordova master script was added
|
||||
path.exists(util.format('%s/cordova/cordova', project_path), function(exists) {
|
||||
assert(exists, 'cordova script did not get added');
|
||||
});
|
||||
|
||||
// make sure debug script was added
|
||||
path.exists(util.format('%s/cordova/debug', project_path), function(exists) {
|
||||
assert(exists, 'debug script did not get added');
|
||||
});
|
||||
|
||||
// make sure BOOM script was added
|
||||
path.exists(util.format('%s/cordova/BOOM', project_path), function(exists) {
|
||||
assert(exists, 'BOOM script did not get added');
|
||||
});
|
||||
|
||||
// make sure log script was added
|
||||
path.exists(util.format('%s/cordova/log', project_path), function(exists) {
|
||||
assert(exists, 'log script did not get added');
|
||||
});
|
||||
|
||||
// make sure clean script was added
|
||||
path.exists(util.format('%s/cordova/clean', project_path), function(exists) {
|
||||
assert(exists, 'clean script did not get added');
|
||||
});
|
||||
|
||||
// make sure emulate script was added
|
||||
path.exists(util.format('%s/cordova/emulate', project_path), function(exists) {
|
||||
assert(exists, 'emulate script did not get added');
|
||||
});
|
||||
|
||||
// make sure appinfo.jar script was added
|
||||
path.exists(util.format('%s/cordova/appinfo.jar', project_path), function(exists) {
|
||||
assert(exists, 'appinfo.jar script did not get added');
|
||||
});
|
||||
|
||||
// check that project compiles && creates a cordovaExample-debug.apk
|
||||
var compile_project = spawn('ant', ['debug'], {cwd: project_path});
|
||||
|
||||
compile_project.on('exit', function(code) {
|
||||
assert.equal(code, 0, 'Cordova Android Project does not compile');
|
||||
// make sure cordovaExample-debug.apk was created
|
||||
path.exists(util.format('%s/bin/%s-debug.apk', project_path, project_name), function(exists) {
|
||||
assert(exists, 'Package did not get created');
|
||||
|
||||
// if project compiles properly just AXE it
|
||||
spawn('rm', ['-rf', project_path], function(code) {
|
||||
assert.equal(code, 0, 'Could not remove project directory');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,155 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
var build_path = __dirname + '/../..'
|
||||
project_path = process.env.Temp + '\\example',
|
||||
package_name = 'org.apache.cordova.example',
|
||||
package_as_path = 'org/apache/cordova/example',
|
||||
project_name = 'cordovaExample';
|
||||
|
||||
var path = require('path'),
|
||||
fs = require('fs'),
|
||||
util = require('util'),
|
||||
assert = require('assert'),
|
||||
exec = require('child_process').exec,
|
||||
spawn = require('child_process').spawn;
|
||||
|
||||
var version = fs.readFileSync(build_path + '/VERSION').toString().replace('\r\n', '');
|
||||
|
||||
assert(version !== undefined);
|
||||
assert(version !== '');
|
||||
|
||||
process.on('uncaughtException', function (err) {
|
||||
console.log('Caught exception: ' + err);
|
||||
exec('rd /s /q ' + project_path);
|
||||
});
|
||||
|
||||
var create_project = spawn('cscript',
|
||||
[build_path + '/bin/create.js',
|
||||
project_path,
|
||||
package_name,
|
||||
project_name]
|
||||
);
|
||||
|
||||
create_project.stderr.on('data', function (data) {
|
||||
console.log('ps stderr: ' + data);
|
||||
});
|
||||
|
||||
create_project.stderr.on('data', function(data) {
|
||||
console.log(data.toString());
|
||||
});
|
||||
|
||||
create_project.stdout.on('data', function(data) {
|
||||
console.log(data.toString());
|
||||
});
|
||||
|
||||
create_project.on('exit', function(code) {
|
||||
assert.equal(code, 0, 'Project did not get created');
|
||||
|
||||
// make sure the project was created
|
||||
path.exists(project_path, function(exists) {
|
||||
assert(exists, 'Project path does not exist');
|
||||
});
|
||||
|
||||
// make sure the build directory was cleaned up
|
||||
// path.exists(build_path + '/framework/libs', function(exists) {
|
||||
// assert(!exists, 'libs directory did not get cleaned up');
|
||||
// });
|
||||
path.exists(build_path + util.format('/framework/assets/cordova-%s.js', version), function(exists) {
|
||||
assert(!exists, 'javascript file did not get cleaned up');
|
||||
});
|
||||
path.exists(build_path + util.format('/framework/cordova-%s.jar', version), function(exists) {
|
||||
assert(!exists, 'jar file did not get cleaned up');
|
||||
});
|
||||
|
||||
// make sure AndroidManifest.xml was added
|
||||
path.exists(util.format('%s/AndroidManifest.xml', project_path), function(exists) {
|
||||
assert(exists, 'AndroidManifest.xml did not get created');
|
||||
// TODO check that the activity name was properly substituted
|
||||
});
|
||||
|
||||
// make sure main Activity was added
|
||||
path.exists(util.format('%s/src/%s/%s.java', project_path, package_as_path, project_name), function(exists) {
|
||||
assert(exists, 'Activity did not get created');
|
||||
// TODO check that package name and activity name were substituted properly
|
||||
});
|
||||
|
||||
// make sure config.xml was added
|
||||
path.exists(util.format('%s/res/xml/config.xml', project_path), function(exists) {
|
||||
assert(exists, 'config.xml did not get created');
|
||||
});
|
||||
|
||||
// make sure cordova.jar was added
|
||||
path.exists(util.format('%s/libs/cordova-%s.jar', project_path, version), function(exists) {
|
||||
assert(exists, 'cordova.jar did not get added');
|
||||
});
|
||||
|
||||
// make sure cordova.js was added
|
||||
path.exists(util.format('%s/assets/www/cordova-%s.js', project_path, version), function(exists) {
|
||||
assert(exists, 'cordova.js did not get added');
|
||||
});
|
||||
|
||||
// make sure cordova master script was added
|
||||
path.exists(util.format('%s/cordova/cordova.bat', project_path), function(exists) {
|
||||
assert(exists, 'cordova script did not get added');
|
||||
});
|
||||
|
||||
// make sure debug script was added
|
||||
path.exists(util.format('%s/cordova/debug.bat', project_path), function(exists) {
|
||||
assert(exists, 'debug script did not get added');
|
||||
});
|
||||
|
||||
// make sure BOOM script was added
|
||||
path.exists(util.format('%s/cordova/BOOM.bat', project_path), function(exists) {
|
||||
assert(exists, 'BOOM script did not get added');
|
||||
});
|
||||
|
||||
// make sure log script was added
|
||||
path.exists(util.format('%s/cordova/log.bat', project_path), function(exists) {
|
||||
assert(exists, 'log script did not get added');
|
||||
});
|
||||
|
||||
// make sure clean script was added
|
||||
path.exists(util.format('%s/cordova/clean.bat', project_path), function(exists) {
|
||||
assert(exists, 'clean script did not get added');
|
||||
});
|
||||
|
||||
// make sure emulate script was added
|
||||
path.exists(util.format('%s/cordova/emulate.bat', project_path), function(exists) {
|
||||
assert(exists, 'emulate script did not get added');
|
||||
});
|
||||
|
||||
// make sure appinfo.jar script was added
|
||||
path.exists(util.format('%s/cordova/appinfo.jar', project_path), function(exists) {
|
||||
assert(exists, 'appinfo.jar script did not get added');
|
||||
});
|
||||
|
||||
// check that project compiles && creates a cordovaExample-debug.apk
|
||||
// XXX: !@##!@# WINDOWS
|
||||
exec('ant debug -f ' + project_path + "\\build.xml", function(error, stdout, stderr) {
|
||||
assert(error == null, "Cordova Android Project does not compile");
|
||||
path.exists(util.format('%s/bin/%s-debug.apk', project_path, project_name),
|
||||
function(exists) {
|
||||
assert(exists, 'Package did not get created');
|
||||
// if project compiles properly just AXE it
|
||||
exec('rd /s /q ' + project_path);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
31
bin/update
31
bin/update
@@ -45,23 +45,12 @@ fi
|
||||
|
||||
# cleanup after exit and/or on error
|
||||
function on_exit {
|
||||
if [ -f "$BUILD_PATH"/framework/assets/www/cordova.js ]
|
||||
then
|
||||
rm "$BUILD_PATH"/framework/assets/www/cordova.js
|
||||
fi
|
||||
if [ -f "$BUILD_PATH"/framework/cordova-$VERSION.jar ]
|
||||
then
|
||||
rm "$BUILD_PATH"/framework/cordova-$VERSION.jar
|
||||
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 $?"
|
||||
exit 1
|
||||
@@ -103,16 +92,6 @@ 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
|
||||
@@ -128,14 +107,16 @@ else
|
||||
fi
|
||||
|
||||
# creating cordova folder and copying run/build/log/launch scripts
|
||||
mkdir "$PROJECT_PATH"/cordova
|
||||
mkdir "$PROJECT_PATH"/cordova/lib
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/appinfo.jar "$PROJECT_PATH"/cordova/appinfo.jar
|
||||
if [ ! -e "$PROJECT_PATH/cordova" ]
|
||||
then
|
||||
mkdir "$PROJECT_PATH"/cordova
|
||||
mkdir "$PROJECT_PATH"/cordova/lib
|
||||
fi
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/build "$PROJECT_PATH"/cordova/build
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/clean "$PROJECT_PATH"/cordova/clean
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/log "$PROJECT_PATH"/cordova/log
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/run "$PROJECT_PATH"/cordova/run
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/cordova "$PROJECT_PATH"/cordova/lib/cordova
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/cordova.js "$PROJECT_PATH"/cordova/lib/cordova.js
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/install-device "$PROJECT_PATH"/cordova/lib/install-device
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/install-emulator "$PROJECT_PATH"/cordova/lib/install-emulator
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/lib/list-devices "$PROJECT_PATH"/cordova/lib/list-devices
|
||||
|
||||
@@ -76,17 +76,6 @@ function exec(command) {
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
@@ -138,9 +127,7 @@ if(fso.FolderExists(ROOT + '\\framework')) {
|
||||
}
|
||||
|
||||
// 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.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');
|
||||
|
||||
@@ -28,9 +28,6 @@
|
||||
/>
|
||||
<!-- android:xlargeScreens="true" screen supported only after Android-9 -->
|
||||
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
@@ -40,7 +37,6 @@
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
@@ -59,5 +55,5 @@
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="7" />
|
||||
<uses-sdk android:minSdkVersion="8" />
|
||||
</manifest>
|
||||
|
||||
5128
framework/assets/www/cordova.js
vendored
5128
framework/assets/www/cordova.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=Google Inc.:Google APIs:17
|
||||
target=android-18
|
||||
apk-configurations=
|
||||
renderscript.opt.level=O0
|
||||
android.library=true
|
||||
|
||||
@@ -43,65 +43,8 @@
|
||||
<preference name="InAppBrowserStorageEnabled" value="true" />
|
||||
<preference name="disallowOverscroll" value="true" />
|
||||
-->
|
||||
|
||||
<!-- This is required for native Android hooks -->
|
||||
<feature name="App">
|
||||
<param name="android-package" value="org.apache.cordova.App"/>
|
||||
<param name="android-package" value="org.apache.cordova.App" />
|
||||
</feature>
|
||||
<feature name="Geolocation">
|
||||
<param name="android-package" value="org.apache.cordova.GeoBroker"/>
|
||||
</feature>
|
||||
<feature name="Device">
|
||||
<param name="android-package" value="org.apache.cordova.Device"/>
|
||||
</feature>
|
||||
<feature name="Accelerometer">
|
||||
<param name="android-package" value="org.apache.cordova.AccelListener"/>
|
||||
</feature>
|
||||
<feature name="Compass">
|
||||
<param name="android-package" value="org.apache.cordova.CompassListener"/>
|
||||
</feature>
|
||||
<feature name="Media">
|
||||
<param name="android-package" value="org.apache.cordova.AudioHandler"/>
|
||||
</feature>
|
||||
<feature name="Camera">
|
||||
<param name="android-package" value="org.apache.cordova.CameraLauncher"/>
|
||||
</feature>
|
||||
<feature name="Contacts">
|
||||
<param name="android-package" value="org.apache.cordova.ContactManager"/>
|
||||
</feature>
|
||||
<feature name="File">
|
||||
<param name="android-package" value="org.apache.cordova.FileUtils"/>
|
||||
</feature>
|
||||
<feature name="NetworkStatus">
|
||||
<param name="android-package" value="org.apache.cordova.NetworkManager"/>
|
||||
</feature>
|
||||
<feature name="Notification">
|
||||
<param name="android-package" value="org.apache.cordova.Notification"/>
|
||||
</feature>
|
||||
<feature name="Storage">
|
||||
<param name="android-package" value="org.apache.cordova.Storage"/>
|
||||
</feature>
|
||||
<feature name="FileTransfer">
|
||||
<param name="android-package" value="org.apache.cordova.FileTransfer"/>
|
||||
</feature>
|
||||
<feature name="Capture">
|
||||
<param name="android-package" value="org.apache.cordova.Capture"/>
|
||||
</feature>
|
||||
<feature name="Battery">
|
||||
<param name="android-package" value="org.apache.cordova.BatteryListener"/>
|
||||
</feature>
|
||||
<feature name="SplashScreen">
|
||||
<param name="android-package" value="org.apache.cordova.SplashScreen"/>
|
||||
</feature>
|
||||
<feature name="Echo">
|
||||
<param name="android-package" value="org.apache.cordova.Echo"/>
|
||||
</feature>
|
||||
<feature name="Globalization">
|
||||
<param name="android-package" value="org.apache.cordova.Globalization"/>
|
||||
</feature>
|
||||
<feature name="InAppBrowser">
|
||||
<param name="android-package" value="org.apache.cordova.InAppBrowser"/>
|
||||
</feature>
|
||||
<!-- Deprecated plugins element. Remove in 3.0 -->
|
||||
<plugins>
|
||||
</plugins>
|
||||
</widget>
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
/**
|
||||
* This class listens to the accelerometer sensor and stores the latest
|
||||
* acceleration values x,y,z.
|
||||
*/
|
||||
public class AccelListener extends CordovaPlugin implements SensorEventListener {
|
||||
|
||||
public static int STOPPED = 0;
|
||||
public static int STARTING = 1;
|
||||
public static int RUNNING = 2;
|
||||
public static int ERROR_FAILED_TO_START = 3;
|
||||
|
||||
private float x,y,z; // most recent acceleration values
|
||||
private long timestamp; // time of most recent value
|
||||
private int status; // status of listener
|
||||
private int accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE;
|
||||
|
||||
private SensorManager sensorManager; // Sensor manager
|
||||
private Sensor mSensor; // Acceleration sensor returned by sensor manager
|
||||
|
||||
private CallbackContext callbackContext; // Keeps track of the JS callback context.
|
||||
|
||||
/**
|
||||
* Create an accelerometer listener.
|
||||
*/
|
||||
public AccelListener() {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.z = 0;
|
||||
this.timestamp = 0;
|
||||
this.setStatus(AccelListener.STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The associated CordovaWebView.
|
||||
*/
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
this.sensorManager = (SensorManager) cordova.getActivity().getSystemService(Context.SENSOR_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args The exec() arguments.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @return Whether the action was valid.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
if (action.equals("start")) {
|
||||
this.callbackContext = callbackContext;
|
||||
if (this.status != AccelListener.RUNNING) {
|
||||
// If not running, then this is an async call, so don't worry about waiting
|
||||
// We drop the callback onto our stack, call start, and let start and the sensor callback fire off the callback down the road
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
else if (action.equals("stop")) {
|
||||
if (this.status == AccelListener.RUNNING) {
|
||||
this.stop();
|
||||
}
|
||||
} else {
|
||||
// Unsupported action
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT, "");
|
||||
result.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by AccelBroker when listener is to be shut down.
|
||||
* Stop listener.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/**
|
||||
* Start listening for acceleration sensor.
|
||||
*
|
||||
* @return status of listener
|
||||
*/
|
||||
private int start() {
|
||||
// If already starting or running, then just return
|
||||
if ((this.status == AccelListener.RUNNING) || (this.status == AccelListener.STARTING)) {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
this.setStatus(AccelListener.STARTING);
|
||||
|
||||
// Get accelerometer from sensor manager
|
||||
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
|
||||
|
||||
// If found, then register as listener
|
||||
if ((list != null) && (list.size() > 0)) {
|
||||
this.mSensor = list.get(0);
|
||||
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_UI);
|
||||
this.setStatus(AccelListener.STARTING);
|
||||
} else {
|
||||
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
|
||||
this.fail(AccelListener.ERROR_FAILED_TO_START, "No sensors found to register accelerometer listening to.");
|
||||
return this.status;
|
||||
}
|
||||
|
||||
// Set a timeout callback on the main thread.
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
AccelListener.this.timeout();
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
return this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening to acceleration sensor.
|
||||
*/
|
||||
private void stop() {
|
||||
if (this.status != AccelListener.STOPPED) {
|
||||
this.sensorManager.unregisterListener(this);
|
||||
}
|
||||
this.setStatus(AccelListener.STOPPED);
|
||||
this.accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an error if the sensor hasn't started.
|
||||
*
|
||||
* Called two seconds after starting the listener.
|
||||
*/
|
||||
private void timeout() {
|
||||
if (this.status == AccelListener.STARTING) {
|
||||
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
|
||||
this.fail(AccelListener.ERROR_FAILED_TO_START, "Accelerometer could not be started.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the accuracy of the sensor has changed.
|
||||
*
|
||||
* @param sensor
|
||||
* @param accuracy
|
||||
*/
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// Only look at accelerometer events
|
||||
if (sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not running, then just return
|
||||
if (this.status == AccelListener.STOPPED) {
|
||||
return;
|
||||
}
|
||||
this.accuracy = accuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sensor listener event.
|
||||
*
|
||||
* @param SensorEvent event
|
||||
*/
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
// Only look at accelerometer events
|
||||
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not running, then just return
|
||||
if (this.status == AccelListener.STOPPED) {
|
||||
return;
|
||||
}
|
||||
this.setStatus(AccelListener.RUNNING);
|
||||
|
||||
if (this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) {
|
||||
|
||||
// Save time that event was received
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
this.x = event.values[0];
|
||||
this.y = event.values[1];
|
||||
this.z = event.values[2];
|
||||
|
||||
this.win();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the view navigates.
|
||||
*/
|
||||
@Override
|
||||
public void onReset() {
|
||||
if (this.status == AccelListener.RUNNING) {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
// Sends an error back to JS
|
||||
private void fail(int code, String message) {
|
||||
// Error object
|
||||
JSONObject errorObj = new JSONObject();
|
||||
try {
|
||||
errorObj.put("code", code);
|
||||
errorObj.put("message", message);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj);
|
||||
err.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(err);
|
||||
}
|
||||
|
||||
private void win() {
|
||||
// Success return object
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
|
||||
result.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
private void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
private JSONObject getAccelerationJSON() {
|
||||
JSONObject r = new JSONObject();
|
||||
try {
|
||||
r.put("x", this.x);
|
||||
r.put("y", this.y);
|
||||
r.put("z", this.z);
|
||||
r.put("timestamp", this.timestamp);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,10 @@
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -97,7 +97,11 @@ public class App extends CordovaPlugin {
|
||||
* Clear the resource cache.
|
||||
*/
|
||||
public void clearCache() {
|
||||
this.webView.clearCache(true);
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
webView.clearCache(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,357 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This class called by CordovaActivity to play and record audio.
|
||||
* The file can be local or over a network using http.
|
||||
*
|
||||
* Audio formats supported (tested):
|
||||
* .mp3, .wav
|
||||
*
|
||||
* Local audio files must reside in one of two places:
|
||||
* android_asset: file name must start with /android_asset/sound.mp3
|
||||
* sdcard: file name is just sound.mp3
|
||||
*/
|
||||
public class AudioHandler extends CordovaPlugin {
|
||||
|
||||
public static String TAG = "AudioHandler";
|
||||
HashMap<String, AudioPlayer> players; // Audio player object
|
||||
ArrayList<AudioPlayer> pausedForPhone; // Audio players that were paused when phone call came in
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public AudioHandler() {
|
||||
this.players = new HashMap<String, AudioPlayer>();
|
||||
this.pausedForPhone = new ArrayList<AudioPlayer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
if (action.equals("startRecordingAudio")) {
|
||||
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), FileHelper.stripFileProtocol(args.getString(1)));
|
||||
}
|
||||
else if (action.equals("seekToAudio")) {
|
||||
this.seekToAudio(args.getString(0), args.getInt(1));
|
||||
}
|
||||
else if (action.equals("pausePlayingAudio")) {
|
||||
this.pausePlayingAudio(args.getString(0));
|
||||
}
|
||||
else if (action.equals("stopPlayingAudio")) {
|
||||
this.stopPlayingAudio(args.getString(0));
|
||||
} else if (action.equals("setVolume")) {
|
||||
try {
|
||||
this.setVolume(args.getString(0), Float.parseFloat(args.getString(1)));
|
||||
} catch (NumberFormatException nfe) {
|
||||
//no-op
|
||||
}
|
||||
} else if (action.equals("getCurrentPositionAudio")) {
|
||||
float f = this.getCurrentPositionAudio(args.getString(0));
|
||||
callbackContext.sendPluginResult(new PluginResult(status, f));
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("getDurationAudio")) {
|
||||
float f = this.getDurationAudio(args.getString(0), args.getString(1));
|
||||
callbackContext.sendPluginResult(new PluginResult(status, f));
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("create")) {
|
||||
String id = args.getString(0);
|
||||
String src = FileHelper.stripFileProtocol(args.getString(1));
|
||||
AudioPlayer audio = new AudioPlayer(this, id, src);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
else if (action.equals("release")) {
|
||||
boolean b = this.release(args.getString(0));
|
||||
callbackContext.sendPluginResult(new PluginResult(status, b));
|
||||
return true;
|
||||
}
|
||||
else { // Unrecognized action.
|
||||
return false;
|
||||
}
|
||||
|
||||
callbackContext.sendPluginResult(new PluginResult(status, result));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all audio players and recorders.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
for (AudioPlayer audio : this.players.values()) {
|
||||
audio.destroy();
|
||||
}
|
||||
this.players.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all audio players and recorders on navigate.
|
||||
*/
|
||||
@Override
|
||||
public void onReset() {
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a message is sent to plugin.
|
||||
*
|
||||
* @param id The message id
|
||||
* @param data The message data
|
||||
* @return Object to stop propagation or null
|
||||
*/
|
||||
public Object onMessage(String id, Object data) {
|
||||
|
||||
// If phone message
|
||||
if (id.equals("telephone")) {
|
||||
|
||||
// If phone ringing, then pause playing
|
||||
if ("ringing".equals(data) || "offhook".equals(data)) {
|
||||
|
||||
// Get all audio players and pause them
|
||||
for (AudioPlayer audio : this.players.values()) {
|
||||
if (audio.getState() == AudioPlayer.STATE.MEDIA_RUNNING.ordinal()) {
|
||||
this.pausedForPhone.add(audio);
|
||||
audio.pausePlaying();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If phone idle, then resume playing those players we paused
|
||||
else if ("idle".equals(data)) {
|
||||
for (AudioPlayer audio : this.pausedForPhone) {
|
||||
audio.startPlaying(null);
|
||||
}
|
||||
this.pausedForPhone.clear();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Release the audio player instance to save memory.
|
||||
* @param id The id of the audio player
|
||||
*/
|
||||
private boolean release(String id) {
|
||||
if (!this.players.containsKey(id)) {
|
||||
return false;
|
||||
}
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
this.players.remove(id);
|
||||
audio.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recording and save the specified file.
|
||||
* @param id The id of the audio player
|
||||
* @param file The name of the file
|
||||
*/
|
||||
public void startRecordingAudio(String id, String file) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if ( audio == null) {
|
||||
audio = new AudioPlayer(this, id, file);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
audio.startRecording(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop recording and save to the file specified when recording started.
|
||||
* @param id The id of the audio player
|
||||
*/
|
||||
public void stopRecordingAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.stopRecording();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start or resume playing audio file.
|
||||
* @param id The id of the audio player
|
||||
* @param file The name of the audio file.
|
||||
*/
|
||||
public void startPlayingAudio(String id, String file) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio == null) {
|
||||
audio = new AudioPlayer(this, id, file);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
audio.startPlaying(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to a location.
|
||||
* @param id The id of the audio player
|
||||
* @param milliseconds int: number of milliseconds to skip 1000 = 1 second
|
||||
*/
|
||||
public void seekToAudio(String id, int milliseconds) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.seekToPlaying(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause playing.
|
||||
* @param id The id of the audio player
|
||||
*/
|
||||
public void pausePlayingAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.pausePlaying();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop playing the audio file.
|
||||
* @param id The id of the audio player
|
||||
*/
|
||||
public void stopPlayingAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.stopPlaying();
|
||||
//audio.destroy();
|
||||
//this.players.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position of playback.
|
||||
* @param id The id of the audio player
|
||||
* @return position in msec
|
||||
*/
|
||||
public float getCurrentPositionAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
return (audio.getCurrentPosition() / 1000.0f);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the duration of the audio file.
|
||||
* @param id The id of the audio player
|
||||
* @param file The name of the audio file.
|
||||
* @return The duration in msec.
|
||||
*/
|
||||
public float getDurationAudio(String id, String file) {
|
||||
|
||||
// Get audio file
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
return (audio.getDuration(file));
|
||||
}
|
||||
|
||||
// If not already open, then open the file
|
||||
else {
|
||||
audio = new AudioPlayer(this, id, file);
|
||||
this.players.put(id, audio);
|
||||
return (audio.getDuration(file));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the audio device to be used for playback.
|
||||
*
|
||||
* @param output 1=earpiece, 2=speaker
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setAudioOutputDevice(int output) {
|
||||
AudioManager audiMgr = (AudioManager) this.cordova.getActivity().getSystemService(Context.AUDIO_SERVICE);
|
||||
if (output == 2) {
|
||||
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL);
|
||||
}
|
||||
else if (output == 1) {
|
||||
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL);
|
||||
}
|
||||
else {
|
||||
System.out.println("AudioHandler.setAudioOutputDevice() Error: Unknown output device.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the audio device to be used for playback.
|
||||
*
|
||||
* @return 1=earpiece, 2=speaker
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public int getAudioOutputDevice() {
|
||||
AudioManager audiMgr = (AudioManager) this.cordova.getActivity().getSystemService(Context.AUDIO_SERVICE);
|
||||
if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE) {
|
||||
return 1;
|
||||
}
|
||||
else if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_SPEAKER) {
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the volume for an audio device
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
* @param volume Volume to adjust to 0.0f - 1.0f
|
||||
*/
|
||||
public void setVolume(String id, float volume) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.setVolume(volume);
|
||||
} else {
|
||||
System.out.println("AudioHandler.setVolume() Error: Unknown Audio Player " + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,553 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.MediaPlayer.OnCompletionListener;
|
||||
import android.media.MediaPlayer.OnErrorListener;
|
||||
import android.media.MediaPlayer.OnPreparedListener;
|
||||
import android.media.MediaRecorder;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class implements the audio playback and recording capabilities used by Cordova.
|
||||
* It is called by the AudioHandler Cordova class.
|
||||
* Only one file can be played or recorded per class instance.
|
||||
*
|
||||
* Local audio files must reside in one of two places:
|
||||
* android_asset: file name must start with /android_asset/sound.mp3
|
||||
* sdcard: file name is just sound.mp3
|
||||
*/
|
||||
public class AudioPlayer implements OnCompletionListener, OnPreparedListener, OnErrorListener {
|
||||
|
||||
// AudioPlayer modes
|
||||
public enum MODE { NONE, PLAY, RECORD };
|
||||
|
||||
// AudioPlayer states
|
||||
public enum STATE { MEDIA_NONE,
|
||||
MEDIA_STARTING,
|
||||
MEDIA_RUNNING,
|
||||
MEDIA_PAUSED,
|
||||
MEDIA_STOPPED,
|
||||
MEDIA_LOADING
|
||||
};
|
||||
|
||||
private static final String LOG_TAG = "AudioPlayer";
|
||||
|
||||
// AudioPlayer message ids
|
||||
private static int MEDIA_STATE = 1;
|
||||
private static int MEDIA_DURATION = 2;
|
||||
private static int MEDIA_POSITION = 3;
|
||||
private static int MEDIA_ERROR = 9;
|
||||
|
||||
// Media error codes
|
||||
private static int MEDIA_ERR_NONE_ACTIVE = 0;
|
||||
private static int MEDIA_ERR_ABORTED = 1;
|
||||
private static int MEDIA_ERR_NETWORK = 2;
|
||||
private static int MEDIA_ERR_DECODE = 3;
|
||||
private static int MEDIA_ERR_NONE_SUPPORTED = 4;
|
||||
|
||||
private AudioHandler handler; // The AudioHandler object
|
||||
private String id; // The id of this player (used to identify Media object in JavaScript)
|
||||
private MODE mode = MODE.NONE; // Playback or Recording mode
|
||||
private STATE state = STATE.MEDIA_NONE; // State of recording or playback
|
||||
|
||||
private String audioFile = null; // File name to play or record to
|
||||
private float duration = -1; // Duration of audio
|
||||
|
||||
private MediaRecorder recorder = null; // Audio recording object
|
||||
private String tempFile = null; // Temporary recording file name
|
||||
|
||||
private MediaPlayer player = null; // Audio player object
|
||||
private boolean prepareOnly = true; // playback after file prepare flag
|
||||
private int seekOnPrepared = 0; // seek to this location once media is prepared
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param handler The audio handler object
|
||||
* @param id The id of this audio player
|
||||
*/
|
||||
public AudioPlayer(AudioHandler handler, String id, String file) {
|
||||
this.handler = handler;
|
||||
this.id = id;
|
||||
this.audioFile = file;
|
||||
this.recorder = new MediaRecorder();
|
||||
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.3gp";
|
||||
} else {
|
||||
this.tempFile = "/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/tmprecording.3gp";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy player and stop audio playing or recording.
|
||||
*/
|
||||
public void destroy() {
|
||||
// Stop any play or record
|
||||
if (this.player != null) {
|
||||
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||
this.player.stop();
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
this.player.release();
|
||||
this.player = null;
|
||||
}
|
||||
if (this.recorder != null) {
|
||||
this.stopRecording();
|
||||
this.recorder.release();
|
||||
this.recorder = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recording the specified file.
|
||||
*
|
||||
* @param file The name of the file
|
||||
*/
|
||||
public void startRecording(String file) {
|
||||
switch (this.mode) {
|
||||
case PLAY:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode.");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
break;
|
||||
case NONE:
|
||||
this.audioFile = file;
|
||||
this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
||||
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP);
|
||||
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB);
|
||||
this.recorder.setOutputFile(this.tempFile);
|
||||
try {
|
||||
this.recorder.prepare();
|
||||
this.recorder.start();
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
return;
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
break;
|
||||
case RECORD:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Already recording.");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save temporary recorded file to specified name
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
public void moveFile(String file) {
|
||||
/* this is a hack to save the file as the specified name */
|
||||
File f = new File(this.tempFile);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop recording and save to the file specified when recording started.
|
||||
*/
|
||||
public void stopRecording() {
|
||||
if (this.recorder != null) {
|
||||
try{
|
||||
if (this.state == STATE.MEDIA_RUNNING) {
|
||||
this.recorder.stop();
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
this.recorder.reset();
|
||||
this.moveFile(this.audioFile);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
// Playback
|
||||
//==========================================================================
|
||||
|
||||
/**
|
||||
* Start or resume playing audio file.
|
||||
*
|
||||
* @param file The name of the audio file.
|
||||
*/
|
||||
public void startPlaying(String file) {
|
||||
if (this.readyPlayer(file) && this.player != null) {
|
||||
this.player.start();
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
this.seekOnPrepared = 0; //insures this is always reset
|
||||
} else {
|
||||
this.prepareOnly = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek or jump to a new time in the track.
|
||||
*/
|
||||
public void seekToPlaying(int milliseconds) {
|
||||
if (this.readyPlayer(this.audioFile)) {
|
||||
this.player.seekTo(milliseconds);
|
||||
Log.d(LOG_TAG, "Send a onStatus update for the new seek");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + milliseconds / 1000.0f + ");");
|
||||
}
|
||||
else {
|
||||
this.seekOnPrepared = milliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause playing.
|
||||
*/
|
||||
public void pausePlaying() {
|
||||
|
||||
// If playing, then pause
|
||||
if (this.state == STATE.MEDIA_RUNNING && this.player != null) {
|
||||
this.player.pause();
|
||||
this.setState(STATE.MEDIA_PAUSED);
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: " + this.state.ordinal());
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop playing the audio file.
|
||||
*/
|
||||
public void stopPlaying() {
|
||||
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||
this.player.pause();
|
||||
this.player.seekTo(0);
|
||||
Log.d(LOG_TAG, "stopPlaying is calling stopped");
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state.ordinal());
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when playback of a media source has completed.
|
||||
*
|
||||
* @param player The MediaPlayer that reached the end of the file
|
||||
*/
|
||||
public void onCompletion(MediaPlayer player) {
|
||||
Log.d(LOG_TAG, "on completion is calling stopped");
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position of playback.
|
||||
*
|
||||
* @return position in msec or -1 if not playing
|
||||
*/
|
||||
public long getCurrentPosition() {
|
||||
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||
int curPos = this.player.getCurrentPosition();
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + curPos / 1000.0f + ");");
|
||||
return curPos;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if playback file is streaming or local.
|
||||
* It is streaming if file name starts with "http://"
|
||||
*
|
||||
* @param file The file name
|
||||
* @return T=streaming, F=local
|
||||
*/
|
||||
public boolean isStreaming(String file) {
|
||||
if (file.contains("http://") || file.contains("https://")) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the duration of the audio file.
|
||||
*
|
||||
* @param file The name of the audio file.
|
||||
* @return The duration in msec.
|
||||
* -1=can't be determined
|
||||
* -2=not allowed
|
||||
*/
|
||||
public float getDuration(String file) {
|
||||
|
||||
// Can't get duration of recording
|
||||
if (this.recorder != null) {
|
||||
return (-2); // not allowed
|
||||
}
|
||||
|
||||
// If audio file already loaded and started, then return duration
|
||||
if (this.player != null) {
|
||||
return this.duration;
|
||||
}
|
||||
|
||||
// If no player yet, then create one
|
||||
else {
|
||||
this.prepareOnly = true;
|
||||
this.startPlaying(file);
|
||||
|
||||
// This will only return value for local, since streaming
|
||||
// file hasn't been read yet.
|
||||
return this.duration;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the media source is ready for playback.
|
||||
*
|
||||
* @param player The MediaPlayer that is ready for playback
|
||||
*/
|
||||
public void onPrepared(MediaPlayer player) {
|
||||
// Listen for playback completion
|
||||
this.player.setOnCompletionListener(this);
|
||||
// seek to any location received while not prepared
|
||||
this.seekToPlaying(this.seekOnPrepared);
|
||||
// If start playing after prepared
|
||||
if (!this.prepareOnly) {
|
||||
this.player.start();
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
this.seekOnPrepared = 0; //reset only when played
|
||||
} else {
|
||||
this.setState(STATE.MEDIA_STARTING);
|
||||
}
|
||||
// Save off duration
|
||||
this.duration = getDurationInSeconds();
|
||||
// reset prepare only flag
|
||||
this.prepareOnly = true;
|
||||
|
||||
// Send status notification to JavaScript
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_DURATION + "," + this.duration + ");");
|
||||
}
|
||||
|
||||
/**
|
||||
* By default Android returns the length of audio in mills but we want seconds
|
||||
*
|
||||
* @return length of clip in seconds
|
||||
*/
|
||||
private float getDurationInSeconds() {
|
||||
return (this.player.getDuration() / 1000.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when there has been an error during an asynchronous operation
|
||||
* (other errors will throw exceptions at method call time).
|
||||
*
|
||||
* @param player the MediaPlayer the error pertains to
|
||||
* @param arg1 the type of error that has occurred: (MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_SERVER_DIED)
|
||||
* @param arg2 an extra code, specific to the error.
|
||||
*/
|
||||
public boolean onError(MediaPlayer player, int arg1, int arg2) {
|
||||
Log.d(LOG_TAG, "AudioPlayer.onError(" + arg1 + ", " + arg2 + ")");
|
||||
|
||||
// TODO: Not sure if this needs to be sent?
|
||||
this.player.stop();
|
||||
this.player.release();
|
||||
|
||||
// Send error notification to JavaScript
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', { \"code\":" + arg1 + "});");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state and send it to JavaScript.
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
private void setState(STATE state) {
|
||||
if (this.state != state) {
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + state.ordinal() + ");");
|
||||
}
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode and send it to JavaScript.
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
private void setMode(MODE mode) {
|
||||
if (this.mode != mode) {
|
||||
//mode is not part of the expected behavior, so no notification
|
||||
//this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + mode + ");");
|
||||
}
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the audio state.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getState() {
|
||||
return this.state.ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the volume for audio player
|
||||
*
|
||||
* @param volume
|
||||
*/
|
||||
public void setVolume(float volume) {
|
||||
this.player.setVolume(volume, volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* attempts to put the player in play mode
|
||||
* @return true if in playmode, false otherwise
|
||||
*/
|
||||
private boolean playMode() {
|
||||
switch(this.mode) {
|
||||
case NONE:
|
||||
this.setMode(MODE.PLAY);
|
||||
break;
|
||||
case PLAY:
|
||||
break;
|
||||
case RECORD:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode.");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
return false; //player is not ready
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* attempts to initialize the media player for playback
|
||||
* @param file the file to play
|
||||
* @return false if player not ready, reports if in wrong mode or state
|
||||
*/
|
||||
private boolean readyPlayer(String file) {
|
||||
if (playMode()) {
|
||||
switch (this.state) {
|
||||
case MEDIA_NONE:
|
||||
if (this.player == null) {
|
||||
this.player = new MediaPlayer();
|
||||
}
|
||||
try {
|
||||
this.loadAudioFile(file);
|
||||
} catch (Exception e) {
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
}
|
||||
return false;
|
||||
case MEDIA_LOADING:
|
||||
//cordova js is not aware of MEDIA_LOADING, so we send MEDIA_STARTING instead
|
||||
Log.d(LOG_TAG, "AudioPlayer Loading: startPlaying() called during media preparation: " + STATE.MEDIA_STARTING.ordinal());
|
||||
this.prepareOnly = false;
|
||||
return false;
|
||||
case MEDIA_STARTING:
|
||||
case MEDIA_RUNNING:
|
||||
case MEDIA_PAUSED:
|
||||
return true;
|
||||
case MEDIA_STOPPED:
|
||||
//if we are readying the same file
|
||||
if (this.audioFile.compareTo(file) == 0) {
|
||||
//reset the audio file
|
||||
player.seekTo(0);
|
||||
player.pause();
|
||||
return true;
|
||||
} else {
|
||||
//reset the player
|
||||
this.player.reset();
|
||||
try {
|
||||
this.loadAudioFile(file);
|
||||
} catch (Exception e) {
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
}
|
||||
//if we had to prepare= the file, we won't be in the correct state for playback
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: startPlaying() called during invalid state: " + this.state);
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* load audio file
|
||||
* @throws IOException
|
||||
* @throws IllegalStateException
|
||||
* @throws SecurityException
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private void loadAudioFile(String file) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {
|
||||
if (this.isStreaming(file)) {
|
||||
this.player.setDataSource(file);
|
||||
this.player.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
//if it's a streaming file, play mode is implied
|
||||
this.setMode(MODE.PLAY);
|
||||
this.setState(STATE.MEDIA_STARTING);
|
||||
this.player.setOnPreparedListener(this);
|
||||
this.player.prepareAsync();
|
||||
}
|
||||
else {
|
||||
if (file.startsWith("/android_asset/")) {
|
||||
String f = file.substring(15);
|
||||
android.content.res.AssetFileDescriptor fd = this.handler.cordova.getActivity().getAssets().openFd(f);
|
||||
this.player.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
|
||||
}
|
||||
else {
|
||||
File fp = new File(file);
|
||||
if (fp.exists()) {
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
this.player.setDataSource(fileInputStream.getFD());
|
||||
}
|
||||
else {
|
||||
this.player.setDataSource("/sdcard/" + file);
|
||||
}
|
||||
}
|
||||
this.setState(STATE.MEDIA_STARTING);
|
||||
this.player.setOnPreparedListener(this);
|
||||
this.player.prepare();
|
||||
|
||||
// Get duration
|
||||
this.duration = getDurationInSeconds();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.util.Log;
|
||||
|
||||
public class BatteryListener extends CordovaPlugin {
|
||||
|
||||
private static final String LOG_TAG = "BatteryManager";
|
||||
|
||||
BroadcastReceiver receiver;
|
||||
|
||||
private CallbackContext batteryCallbackContext = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public BatteryListener() {
|
||||
this.receiver = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false if not.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
if (action.equals("start")) {
|
||||
if (this.batteryCallbackContext != null) {
|
||||
callbackContext.error( "Battery listener already running.");
|
||||
return true;
|
||||
}
|
||||
this.batteryCallbackContext = callbackContext;
|
||||
|
||||
// We need to listen to power events to update battery status
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
|
||||
if (this.receiver == null) {
|
||||
this.receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updateBatteryInfo(intent);
|
||||
}
|
||||
};
|
||||
cordova.getActivity().registerReceiver(this.receiver, intentFilter);
|
||||
}
|
||||
|
||||
// Don't return any result now, since status results will be sent when events come in from broadcast receiver
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
pluginResult.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
else if (action.equals("stop")) {
|
||||
removeBatteryListener();
|
||||
this.sendUpdate(new JSONObject(), false); // release status callback in JS side
|
||||
this.batteryCallbackContext = null;
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop battery receiver.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
removeBatteryListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop battery receiver.
|
||||
*/
|
||||
public void onReset() {
|
||||
removeBatteryListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the battery receiver and set it to null.
|
||||
*/
|
||||
private void removeBatteryListener() {
|
||||
if (this.receiver != null) {
|
||||
try {
|
||||
this.cordova.getActivity().unregisterReceiver(this.receiver);
|
||||
this.receiver = null;
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Error unregistering battery receiver: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSONObject with the current battery information
|
||||
*
|
||||
* @param batteryIntent the current battery information
|
||||
* @return a JSONObject containing the battery status information
|
||||
*/
|
||||
private JSONObject getBatteryInfo(Intent batteryIntent) {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("level", batteryIntent.getIntExtra(android.os.BatteryManager.EXTRA_LEVEL, 0));
|
||||
obj.put("isPlugged", batteryIntent.getIntExtra(android.os.BatteryManager.EXTRA_PLUGGED, -1) > 0 ? true : false);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the JavaScript side whenever the battery changes
|
||||
*
|
||||
* @param batteryIntent the current battery information
|
||||
* @return
|
||||
*/
|
||||
private void updateBatteryInfo(Intent batteryIntent) {
|
||||
sendUpdate(this.getBatteryInfo(batteryIntent), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new plugin result and send it back to JavaScript
|
||||
*
|
||||
* @param connection the network info to set as navigator.connection
|
||||
*/
|
||||
private void sendUpdate(JSONObject info, boolean keepCallback) {
|
||||
if (this.batteryCallbackContext != null) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, info);
|
||||
result.setKeepCallback(keepCallback);
|
||||
this.batteryCallbackContext.sendPluginResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,14 @@
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova.api;
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class CallbackContext {
|
||||
@@ -109,8 +110,6 @@ public class CallbackContext {
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.OK));
|
||||
@@ -129,7 +128,6 @@ public class CallbackContext {
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(String message) {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message));
|
||||
@@ -139,7 +137,6 @@ public class CallbackContext {
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(int message) {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message));
|
||||
@@ -1,807 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class launches the camera view, allows the user to take a picture, closes the camera view,
|
||||
* and returns the captured image. When the camera view is closed, the screen displayed before
|
||||
* the camera view was shown is redisplayed.
|
||||
*/
|
||||
public class CameraLauncher extends CordovaPlugin implements MediaScannerConnectionClient {
|
||||
|
||||
private static final int DATA_URL = 0; // Return base64 encoded string
|
||||
private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android)
|
||||
private static final int NATIVE_URI = 2; // On Android, this is the same as FILE_URI
|
||||
|
||||
private static final int PHOTOLIBRARY = 0; // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
|
||||
private static final int CAMERA = 1; // Take picture from camera
|
||||
private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture library (same as PHOTOLIBRARY for Android)
|
||||
|
||||
private static final int PICTURE = 0; // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
|
||||
private static final int VIDEO = 1; // allow selection of video only, ONLY RETURNS URL
|
||||
private static final int ALLMEDIA = 2; // allow selection from all media types
|
||||
|
||||
private static final int JPEG = 0; // Take a picture of type JPEG
|
||||
private static final int PNG = 1; // Take a picture of type PNG
|
||||
private static final String GET_PICTURE = "Get Picture";
|
||||
private static final String GET_VIDEO = "Get Video";
|
||||
private static final String GET_All = "Get All";
|
||||
|
||||
private static final String LOG_TAG = "CameraLauncher";
|
||||
|
||||
private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
private int targetWidth; // desired width of the image
|
||||
private int targetHeight; // desired height of the image
|
||||
private Uri imageUri; // Uri of captured image
|
||||
private int encodingType; // Type of encoding to use
|
||||
private int mediaType; // What type of media to retrieve
|
||||
private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album
|
||||
private boolean correctOrientation; // Should the pictures orientation be corrected
|
||||
//private boolean allowEdit; // Should we allow the user to crop the image. UNUSED.
|
||||
|
||||
public CallbackContext callbackContext;
|
||||
private int numPics;
|
||||
|
||||
private MediaScannerConnection conn; // Used to update gallery app with newly-written files
|
||||
private Uri scanMe; // Uri of image to be added to content store
|
||||
|
||||
//This should never be null!
|
||||
//private CordovaInterface cordova;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CameraLauncher() {
|
||||
}
|
||||
|
||||
// public void setContext(CordovaInterface mCtx) {
|
||||
// super.setContext(mCtx);
|
||||
// if (CordovaInterface.class.isInstance(mCtx))
|
||||
// cordova = (CordovaInterface) mCtx;
|
||||
// else
|
||||
// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
|
||||
// }
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
this.callbackContext = callbackContext;
|
||||
|
||||
if (action.equals("takePicture")) {
|
||||
int srcType = CAMERA;
|
||||
int destType = FILE_URI;
|
||||
this.saveToPhotoAlbum = false;
|
||||
this.targetHeight = 0;
|
||||
this.targetWidth = 0;
|
||||
this.encodingType = JPEG;
|
||||
this.mediaType = PICTURE;
|
||||
this.mQuality = 80;
|
||||
|
||||
this.mQuality = args.getInt(0);
|
||||
destType = args.getInt(1);
|
||||
srcType = args.getInt(2);
|
||||
this.targetWidth = args.getInt(3);
|
||||
this.targetHeight = args.getInt(4);
|
||||
this.encodingType = args.getInt(5);
|
||||
this.mediaType = args.getInt(6);
|
||||
//this.allowEdit = args.getBoolean(7); // This field is unused.
|
||||
this.correctOrientation = args.getBoolean(8);
|
||||
this.saveToPhotoAlbum = args.getBoolean(9);
|
||||
|
||||
// If the user specifies a 0 or smaller width/height
|
||||
// make it -1 so later comparisons succeed
|
||||
if (this.targetWidth < 1) {
|
||||
this.targetWidth = -1;
|
||||
}
|
||||
if (this.targetHeight < 1) {
|
||||
this.targetHeight = -1;
|
||||
}
|
||||
|
||||
if (srcType == CAMERA) {
|
||||
this.takePicture(destType, encodingType);
|
||||
}
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
this.getImage(srcType, destType);
|
||||
}
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(r);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Take a picture with the camera.
|
||||
* When an image is captured or the camera view is cancelled, the result is returned
|
||||
* in CordovaActivity.onActivityResult, which forwards the result to this.onActivityResult.
|
||||
*
|
||||
* The image can either be returned as a base64 string or a URI that points to the file.
|
||||
* To display base64 string in an img tag, set the source to:
|
||||
* img.src="data:image/jpeg;base64,"+result;
|
||||
* or to display URI in an img tag
|
||||
* img.src=result;
|
||||
*
|
||||
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
* @param returnType Set the type of image to return.
|
||||
*/
|
||||
public void takePicture(int returnType, int encodingType) {
|
||||
// Save the number of images currently on disk for later
|
||||
this.numPics = queryImgDB(whichContentStore()).getCount();
|
||||
|
||||
// Display camera
|
||||
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
|
||||
|
||||
// Specify file so that large image is captured and returned
|
||||
File photo = createCaptureFile(encodingType);
|
||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
||||
this.imageUri = Uri.fromFile(photo);
|
||||
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, (CAMERA + 1) * 16 + returnType + 1);
|
||||
}
|
||||
// else
|
||||
// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file in the applications temporary directory based upon the supplied encoding.
|
||||
*
|
||||
* @param encodingType of the image to be taken
|
||||
* @return a File object pointing to the temporary picture
|
||||
*/
|
||||
private File createCaptureFile(int encodingType) {
|
||||
File photo = null;
|
||||
if (encodingType == JPEG) {
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.jpg");
|
||||
} else if (encodingType == PNG) {
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.png");
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType);
|
||||
}
|
||||
return photo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image from photo library.
|
||||
*
|
||||
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
* @param srcType The album to get image from.
|
||||
* @param returnType Set the type of image to return.
|
||||
*/
|
||||
// TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do!
|
||||
public void getImage(int srcType, int returnType) {
|
||||
Intent intent = new Intent();
|
||||
String title = GET_PICTURE;
|
||||
if (this.mediaType == PICTURE) {
|
||||
intent.setType("image/*");
|
||||
}
|
||||
else if (this.mediaType == VIDEO) {
|
||||
intent.setType("video/*");
|
||||
title = GET_VIDEO;
|
||||
}
|
||||
else if (this.mediaType == ALLMEDIA) {
|
||||
// I wanted to make the type 'image/*, video/*' but this does not work on all versions
|
||||
// of android so I had to go with the wildcard search.
|
||||
intent.setType("*/*");
|
||||
title = GET_All;
|
||||
}
|
||||
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
|
||||
new String(title)), (srcType + 1) * 16 + returnType + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the camera view exits.
|
||||
*
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
|
||||
// Get src and dest types from request code
|
||||
int srcType = (requestCode / 16) - 1;
|
||||
int destType = (requestCode % 16) - 1;
|
||||
int rotate = 0;
|
||||
|
||||
// If CAMERA
|
||||
if (srcType == CAMERA) {
|
||||
// If image available
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
try {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg");
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Bitmap bitmap = null;
|
||||
Uri uri = null;
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
|
||||
if (bitmap == null) {
|
||||
// Try to get the bitmap from intent.
|
||||
bitmap = (Bitmap)intent.getExtras().get("data");
|
||||
}
|
||||
|
||||
// Double-check the bitmap.
|
||||
if (bitmap == null) {
|
||||
Log.d(LOG_TAG, "I either have a null image path or bitmap");
|
||||
this.failPicture("Unable to create bitmap!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (rotate != 0 && this.correctOrientation) {
|
||||
bitmap = getRotatedBitmap(rotate, bitmap, exif);
|
||||
}
|
||||
|
||||
this.processPicture(bitmap);
|
||||
checkForDuplicateImage(DATA_URL);
|
||||
}
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI || destType == NATIVE_URI) {
|
||||
if (this.saveToPhotoAlbum) {
|
||||
Uri inputUri = getUriFromMediaStore();
|
||||
//Just because we have a media URI doesn't mean we have a real file, we need to make it
|
||||
uri = Uri.fromFile(new File(FileHelper.getRealPath(inputUri, this.cordova)));
|
||||
} else {
|
||||
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
this.failPicture("Error capturing image - no media storage found.");
|
||||
}
|
||||
|
||||
// If all this is true we shouldn't compress the image.
|
||||
if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 &&
|
||||
!this.correctOrientation) {
|
||||
writeUncompressedImage(uri);
|
||||
|
||||
this.callbackContext.success(uri.toString());
|
||||
} else {
|
||||
bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
|
||||
|
||||
if (rotate != 0 && this.correctOrientation) {
|
||||
bitmap = getRotatedBitmap(rotate, bitmap, exif);
|
||||
}
|
||||
|
||||
// Add compressed version of captured image to returned media store Uri
|
||||
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
|
||||
os.close();
|
||||
|
||||
// Restore exif data to file
|
||||
if (this.encodingType == JPEG) {
|
||||
String exifPath;
|
||||
if (this.saveToPhotoAlbum) {
|
||||
exifPath = FileHelper.getRealPath(uri, this.cordova);
|
||||
} else {
|
||||
exifPath = uri.getPath();
|
||||
}
|
||||
exif.createOutFile(exifPath);
|
||||
exif.writeExifData();
|
||||
}
|
||||
|
||||
}
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.callbackContext.success(uri.toString());
|
||||
}
|
||||
|
||||
this.cleanup(FILE_URI, this.imageUri, uri, bitmap);
|
||||
bitmap = null;
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error capturing image.");
|
||||
}
|
||||
}
|
||||
|
||||
// If cancelled
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
this.failPicture("Camera cancelled.");
|
||||
}
|
||||
|
||||
// If something else
|
||||
else {
|
||||
this.failPicture("Did not complete!");
|
||||
}
|
||||
}
|
||||
|
||||
// If retrieving photo from library
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
Uri uri = intent.getData();
|
||||
|
||||
// If you ask for video or all media type you will automatically get back a file URI
|
||||
// and there will be no attempt to resize any returned data
|
||||
if (this.mediaType != PICTURE) {
|
||||
this.callbackContext.success(uri.toString());
|
||||
}
|
||||
else {
|
||||
// This is a special case to just return the path as no scaling,
|
||||
// rotating, nor compressing needs to be done
|
||||
if (this.targetHeight == -1 && this.targetWidth == -1 &&
|
||||
(destType == FILE_URI || destType == NATIVE_URI) && !this.correctOrientation) {
|
||||
this.callbackContext.success(uri.toString());
|
||||
} else {
|
||||
String uriString = uri.toString();
|
||||
// Get the path to the image. Makes loading so much easier.
|
||||
String mimeType = FileHelper.getMimeType(uriString, this.cordova);
|
||||
// If we don't have a valid image so quit.
|
||||
if (!("image/jpeg".equalsIgnoreCase(mimeType) || "image/png".equalsIgnoreCase(mimeType))) {
|
||||
Log.d(LOG_TAG, "I either have a null image path or bitmap");
|
||||
this.failPicture("Unable to retrieve path to picture!");
|
||||
return;
|
||||
}
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
bitmap = getScaledBitmap(uriString);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (bitmap == null) {
|
||||
Log.d(LOG_TAG, "I either have a null image path or bitmap");
|
||||
this.failPicture("Unable to create bitmap!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.correctOrientation) {
|
||||
rotate = getImageOrientation(uri);
|
||||
if (rotate != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(rotate);
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
}
|
||||
}
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
this.processPicture(bitmap);
|
||||
}
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI || destType == NATIVE_URI) {
|
||||
// Do we need to scale the returned file
|
||||
if (this.targetHeight > 0 && this.targetWidth > 0) {
|
||||
try {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
|
||||
// Some content: URIs do not map to file paths (e.g. picasa).
|
||||
String realPath = FileHelper.getRealPath(uri, this.cordova);
|
||||
ExifHelper exif = new ExifHelper();
|
||||
if (realPath != null && this.encodingType == JPEG) {
|
||||
try {
|
||||
exif.createInFile(realPath);
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream os = new FileOutputStream(resizePath);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
|
||||
os.close();
|
||||
|
||||
// Restore exif data to file
|
||||
if (realPath != null && this.encodingType == JPEG) {
|
||||
exif.createOutFile(resizePath);
|
||||
exif.writeExifData();
|
||||
}
|
||||
|
||||
// The resized image is cached by the app in order to get around this and not have to delete you
|
||||
// application cache I'm adding the current system time to the end of the file url.
|
||||
this.callbackContext.success("file://" + resizePath + "?" + System.currentTimeMillis());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error retrieving image.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.callbackContext.success(uri.toString());
|
||||
}
|
||||
}
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
}
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
this.failPicture("Selection cancelled.");
|
||||
}
|
||||
else {
|
||||
this.failPicture("Selection did not complete!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getImageOrientation(Uri uri) {
|
||||
String[] cols = { MediaStore.Images.Media.ORIENTATION };
|
||||
Cursor cursor = cordova.getActivity().getContentResolver().query(uri,
|
||||
cols, null, null, null);
|
||||
int rotate = 0;
|
||||
if (cursor != null) {
|
||||
cursor.moveToPosition(0);
|
||||
rotate = cursor.getInt(0);
|
||||
cursor.close();
|
||||
}
|
||||
return rotate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out if the bitmap should be rotated. For instance if the picture was taken in
|
||||
* portrait mode
|
||||
*
|
||||
* @param rotate
|
||||
* @param bitmap
|
||||
* @return rotated bitmap
|
||||
*/
|
||||
private Bitmap getRotatedBitmap(int rotate, Bitmap bitmap, ExifHelper exif) {
|
||||
Matrix matrix = new Matrix();
|
||||
if (rotate == 180) {
|
||||
matrix.setRotate(rotate);
|
||||
} else {
|
||||
matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
|
||||
}
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
exif.resetOrientation();
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* In the special case where the default width, height and quality are unchanged
|
||||
* we just write the file out to disk saving the expensive Bitmap.compress function.
|
||||
*
|
||||
* @param uri
|
||||
* @throws FileNotFoundException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeUncompressedImage(Uri uri) throws FileNotFoundException,
|
||||
IOException {
|
||||
FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString()));
|
||||
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||
byte[] buffer = new byte[4096];
|
||||
int len;
|
||||
while ((len = fis.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, len);
|
||||
}
|
||||
os.flush();
|
||||
os.close();
|
||||
fis.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create entry in media store for image
|
||||
*
|
||||
* @return uri
|
||||
*/
|
||||
private Uri getUriFromMediaStore() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
|
||||
Uri uri;
|
||||
try {
|
||||
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
LOG.d(LOG_TAG, "Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
LOG.d(LOG_TAG, "Can't write to internal media storage.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a scaled bitmap based on the target width and height
|
||||
*
|
||||
* @param imagePath
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private Bitmap getScaledBitmap(String imageUrl) throws IOException {
|
||||
// If no new width or height were specified return the original bitmap
|
||||
if (this.targetWidth <= 0 && this.targetHeight <= 0) {
|
||||
return BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova));
|
||||
}
|
||||
|
||||
// figure out the original width and height of the image
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
|
||||
|
||||
//CB-2292: WTF? Why is the width null?
|
||||
if(options.outWidth == 0 || options.outHeight == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// determine the correct aspect ratio
|
||||
int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight);
|
||||
|
||||
// Load in the smallest bitmap possible that is closest to the size we want
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
|
||||
Bitmap unscaledBitmap = BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
|
||||
if (unscaledBitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintain the aspect ratio so the resulting image does not look smooshed
|
||||
*
|
||||
* @param origWidth
|
||||
* @param origHeight
|
||||
* @return
|
||||
*/
|
||||
public int[] calculateAspectRatio(int origWidth, int origHeight) {
|
||||
int newWidth = this.targetWidth;
|
||||
int newHeight = this.targetHeight;
|
||||
|
||||
// If no new width or height were specified return the original bitmap
|
||||
if (newWidth <= 0 && newHeight <= 0) {
|
||||
newWidth = origWidth;
|
||||
newHeight = origHeight;
|
||||
}
|
||||
// Only the width was specified
|
||||
else if (newWidth > 0 && newHeight <= 0) {
|
||||
newHeight = (newWidth * origHeight) / origWidth;
|
||||
}
|
||||
// only the height was specified
|
||||
else if (newWidth <= 0 && newHeight > 0) {
|
||||
newWidth = (newHeight * origWidth) / origHeight;
|
||||
}
|
||||
// If the user specified both a positive width and height
|
||||
// (potentially different aspect ratio) then the width or height is
|
||||
// scaled so that the image fits while maintaining aspect ratio.
|
||||
// Alternatively, the specified width and height could have been
|
||||
// kept and Bitmap.SCALE_TO_FIT specified when scaling, but this
|
||||
// would result in whitespace in the new image.
|
||||
else {
|
||||
double newRatio = newWidth / (double) newHeight;
|
||||
double origRatio = origWidth / (double) origHeight;
|
||||
|
||||
if (origRatio > newRatio) {
|
||||
newHeight = (newWidth * origHeight) / origWidth;
|
||||
} else if (origRatio < newRatio) {
|
||||
newWidth = (newHeight * origWidth) / origHeight;
|
||||
}
|
||||
}
|
||||
|
||||
int[] retval = new int[2];
|
||||
retval[0] = newWidth;
|
||||
retval[1] = newHeight;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out what ratio we can load our image into memory at while still being bigger than
|
||||
* our desired width and height
|
||||
*
|
||||
* @param srcWidth
|
||||
* @param srcHeight
|
||||
* @param dstWidth
|
||||
* @param dstHeight
|
||||
* @return
|
||||
*/
|
||||
public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
|
||||
final float srcAspect = (float)srcWidth / (float)srcHeight;
|
||||
final float dstAspect = (float)dstWidth / (float)dstHeight;
|
||||
|
||||
if (srcAspect > dstAspect) {
|
||||
return srcWidth / dstWidth;
|
||||
} else {
|
||||
return srcHeight / dstHeight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cursor that can be used to determine how many images we have.
|
||||
*
|
||||
* @return a cursor
|
||||
*/
|
||||
private Cursor queryImgDB(Uri contentStore) {
|
||||
return this.cordova.getActivity().getContentResolver().query(
|
||||
contentStore,
|
||||
new String[] { MediaStore.Images.Media._ID },
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up after picture taking. Checking for duplicates and that kind of stuff.
|
||||
* @param newImage
|
||||
*/
|
||||
private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
|
||||
// Clean up initial camera-written image file.
|
||||
(new File(FileHelper.stripFileProtocol(oldImage.toString()))).delete();
|
||||
|
||||
checkForDuplicateImage(imageType);
|
||||
// Scan for the gallery to update pic refs in gallery
|
||||
if (this.saveToPhotoAlbum && newImage != null) {
|
||||
this.scanForGallery(newImage);
|
||||
}
|
||||
|
||||
System.gc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to find out if we are in a situation where the Camera Intent adds to images
|
||||
* to the content store. If we are using a FILE_URI and the number of images in the DB
|
||||
* increases by 2 we have a duplicate, when using a DATA_URL the number is 1.
|
||||
*
|
||||
* @param type FILE_URI or DATA_URL
|
||||
*/
|
||||
private void checkForDuplicateImage(int type) {
|
||||
int diff = 1;
|
||||
Uri contentStore = whichContentStore();
|
||||
Cursor cursor = queryImgDB(contentStore);
|
||||
int currentNumOfImages = cursor.getCount();
|
||||
|
||||
if (type == FILE_URI && this.saveToPhotoAlbum) {
|
||||
diff = 2;
|
||||
}
|
||||
|
||||
// delete the duplicate file if the difference is 2 for file URI or 1 for Data URL
|
||||
if ((currentNumOfImages - numPics) == diff) {
|
||||
cursor.moveToLast();
|
||||
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID)));
|
||||
if (diff == 2) {
|
||||
id--;
|
||||
}
|
||||
Uri uri = Uri.parse(contentStore + "/" + id);
|
||||
this.cordova.getActivity().getContentResolver().delete(uri, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are storing the images in internal or external storage
|
||||
* @return Uri
|
||||
*/
|
||||
private Uri whichContentStore() {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else {
|
||||
return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress bitmap using jpeg, convert to Base64 encoded string, and return to JavaScript.
|
||||
*
|
||||
* @param bitmap
|
||||
*/
|
||||
public void processPicture(Bitmap bitmap) {
|
||||
ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream();
|
||||
try {
|
||||
if (bitmap.compress(CompressFormat.JPEG, mQuality, jpeg_data)) {
|
||||
byte[] code = jpeg_data.toByteArray();
|
||||
byte[] output = Base64.encode(code, Base64.DEFAULT);
|
||||
String js_out = new String(output);
|
||||
this.callbackContext.success(js_out);
|
||||
js_out = null;
|
||||
output = null;
|
||||
code = null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.failPicture("Error compressing image.");
|
||||
}
|
||||
jpeg_data = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send error message to JavaScript.
|
||||
*
|
||||
* @param err
|
||||
*/
|
||||
public void failPicture(String err) {
|
||||
this.callbackContext.error(err);
|
||||
}
|
||||
|
||||
private void scanForGallery(Uri newImage) {
|
||||
this.scanMe = newImage;
|
||||
if(this.conn != null) {
|
||||
this.conn.disconnect();
|
||||
}
|
||||
this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this);
|
||||
conn.connect();
|
||||
}
|
||||
|
||||
public void onMediaScannerConnected() {
|
||||
try{
|
||||
this.conn.scanFile(this.scanMe.toString(), "image/*");
|
||||
} catch (java.lang.IllegalStateException e){
|
||||
LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onScanCompleted(String path, Uri uri) {
|
||||
this.conn.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -1,445 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.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;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
|
||||
public class Capture extends CordovaPlugin {
|
||||
|
||||
private static final String VIDEO_3GPP = "video/3gpp";
|
||||
private static final String VIDEO_MP4 = "video/mp4";
|
||||
private static final String AUDIO_3GPP = "audio/3gpp";
|
||||
private static final String IMAGE_JPEG = "image/jpeg";
|
||||
|
||||
private static final int CAPTURE_AUDIO = 0; // Constant for capture audio
|
||||
private static final int CAPTURE_IMAGE = 1; // Constant for capture image
|
||||
private static final int CAPTURE_VIDEO = 2; // Constant for capture video
|
||||
private static final String LOG_TAG = "Capture";
|
||||
|
||||
private static final int CAPTURE_INTERNAL_ERR = 0;
|
||||
// private static final int CAPTURE_APPLICATION_BUSY = 1;
|
||||
// private static final int CAPTURE_INVALID_ARGUMENT = 2;
|
||||
private static final int CAPTURE_NO_MEDIA_FILES = 3;
|
||||
|
||||
private CallbackContext callbackContext; // The callback context from which we were invoked.
|
||||
private long limit; // the number of pics/vids/clips to take
|
||||
private double duration; // optional duration parameter for video recording
|
||||
private JSONArray results; // The array of results to be returned to the user
|
||||
private int numPics; // Number of pictures before capture activity
|
||||
|
||||
//private CordovaInterface cordova;
|
||||
|
||||
// public void setContext(Context mCtx)
|
||||
// {
|
||||
// if (CordovaInterface.class.isInstance(mCtx))
|
||||
// cordova = (CordovaInterface) mCtx;
|
||||
// else
|
||||
// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
this.callbackContext = callbackContext;
|
||||
this.limit = 1;
|
||||
this.duration = 0.0f;
|
||||
this.results = new JSONArray();
|
||||
|
||||
JSONObject options = args.optJSONObject(0);
|
||||
if (options != null) {
|
||||
limit = options.optLong("limit", 1);
|
||||
duration = options.optDouble("duration", 0.0f);
|
||||
}
|
||||
|
||||
if (action.equals("getFormatData")) {
|
||||
JSONObject obj = getFormatData(args.getString(0), args.getString(1));
|
||||
callbackContext.success(obj);
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("captureAudio")) {
|
||||
this.captureAudio();
|
||||
}
|
||||
else if (action.equals("captureImage")) {
|
||||
this.captureImage();
|
||||
}
|
||||
else if (action.equals("captureVideo")) {
|
||||
this.captureVideo(duration);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the media data file data depending on it's mime type
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param mimeType of the file
|
||||
* @return a MediaFileData object
|
||||
*/
|
||||
private JSONObject getFormatData(String filePath, String mimeType) throws JSONException {
|
||||
JSONObject obj = new JSONObject();
|
||||
// setup defaults
|
||||
obj.put("height", 0);
|
||||
obj.put("width", 0);
|
||||
obj.put("bitrate", 0);
|
||||
obj.put("duration", 0);
|
||||
obj.put("codecs", "");
|
||||
|
||||
// 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 = FileHelper.getMimeType(filePath, cordova);
|
||||
}
|
||||
Log.d(LOG_TAG, "Mime type = " + mimeType);
|
||||
|
||||
if (mimeType.equals(IMAGE_JPEG) || filePath.endsWith(".jpg")) {
|
||||
obj = getImageData(filePath, obj);
|
||||
}
|
||||
else if (mimeType.endsWith(AUDIO_3GPP)) {
|
||||
obj = getAudioVideoData(filePath, obj, false);
|
||||
}
|
||||
else if (mimeType.equals(VIDEO_3GPP) || mimeType.equals(VIDEO_MP4)) {
|
||||
obj = getAudioVideoData(filePath, obj, true);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Image specific attributes
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param obj represents the Media File Data
|
||||
* @return a JSONObject that represents the Media File Data
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getImageData(String filePath, JSONObject obj) throws JSONException {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(FileHelper.stripFileProtocol(filePath), options);
|
||||
obj.put("height", options.outHeight);
|
||||
obj.put("width", options.outWidth);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Image specific attributes
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param obj represents the Media File Data
|
||||
* @param video if true get video attributes as well
|
||||
* @return a JSONObject that represents the Media File Data
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getAudioVideoData(String filePath, JSONObject obj, boolean video) throws JSONException {
|
||||
MediaPlayer player = new MediaPlayer();
|
||||
try {
|
||||
player.setDataSource(filePath);
|
||||
player.prepare();
|
||||
obj.put("duration", player.getDuration() / 1000);
|
||||
if (video) {
|
||||
obj.put("height", player.getVideoHeight());
|
||||
obj.put("width", player.getVideoWidth());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d(LOG_TAG, "Error: loading video file");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture audio. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureAudio() {
|
||||
Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
||||
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_AUDIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture images. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureImage() {
|
||||
// Save the number of images currently on disk for later
|
||||
this.numPics = queryImgDB(whichContentStore()).getCount();
|
||||
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
|
||||
// Specify file so that large image is captured and returned
|
||||
File photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), "Capture.jpg");
|
||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
||||
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_IMAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture video. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureVideo(double duration) {
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
|
||||
|
||||
if(Build.VERSION.SDK_INT > 8){
|
||||
intent.putExtra("android.intent.extra.durationLimit", duration);
|
||||
}
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_VIDEO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the video view exits.
|
||||
*
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
|
||||
// Result received okay
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
// An audio clip was requested
|
||||
if (requestCode == CAPTURE_AUDIO) {
|
||||
// Get the uri of the audio clip
|
||||
Uri data = intent.getData();
|
||||
// create a file object from the uri
|
||||
results.put(createMediaFile(data));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for listening to audio
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
} else {
|
||||
// still need to capture more audio clips
|
||||
captureAudio();
|
||||
}
|
||||
} else if (requestCode == CAPTURE_IMAGE) {
|
||||
// For some reason if I try to do:
|
||||
// Uri data = intent.getData();
|
||||
// It crashes in the emulator and on my phone with a null pointer exception
|
||||
// To work around it I had to grab the code from CameraLauncher.java
|
||||
try {
|
||||
// Create entry in media store for image
|
||||
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, IMAGE_JPEG);
|
||||
Uri uri = null;
|
||||
try {
|
||||
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
LOG.d(LOG_TAG, "Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
LOG.d(LOG_TAG, "Can't write to internal media storage.");
|
||||
this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image - no media storage found."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
FileInputStream fis = new FileInputStream(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/Capture.jpg");
|
||||
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||
byte[] buffer = new byte[4096];
|
||||
int len;
|
||||
while ((len = fis.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, len);
|
||||
}
|
||||
os.flush();
|
||||
os.close();
|
||||
fis.close();
|
||||
|
||||
// Add image to results
|
||||
results.put(createMediaFile(uri));
|
||||
|
||||
checkForDuplicateImage();
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
} else {
|
||||
// still need to capture more images
|
||||
captureImage();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image."));
|
||||
}
|
||||
} else if (requestCode == CAPTURE_VIDEO) {
|
||||
// Get the uri of the video clip
|
||||
Uri data = intent.getData();
|
||||
// create a file object from the uri
|
||||
results.put(createMediaFile(data));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing video
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
} else {
|
||||
// still need to capture more video clips
|
||||
captureVideo(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If canceled
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
// If we have partial results send them back to the user
|
||||
if (results.length() > 0) {
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
}
|
||||
// user canceled the action
|
||||
else {
|
||||
this.fail(createErrorObject(CAPTURE_NO_MEDIA_FILES, "Canceled."));
|
||||
}
|
||||
}
|
||||
// If something else
|
||||
else {
|
||||
// If we have partial results send them back to the user
|
||||
if (results.length() > 0) {
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
}
|
||||
// something bad happened
|
||||
else {
|
||||
this.fail(createErrorObject(CAPTURE_NO_MEDIA_FILES, "Did not complete!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSONObject that represents a File from the Uri
|
||||
*
|
||||
* @param data the Uri of the audio/image/video
|
||||
* @return a JSONObject that represents a File
|
||||
* @throws IOException
|
||||
*/
|
||||
private JSONObject createMediaFile(Uri data) {
|
||||
File fp = new File(FileHelper.getRealPath(data, this.cordova));
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try {
|
||||
// File properties
|
||||
obj.put("name", fp.getName());
|
||||
obj.put("fullPath", "file://" + fp.getAbsolutePath());
|
||||
// Because of an issue with MimeTypeMap.getMimeTypeFromExtension() all .3gpp files
|
||||
// are reported as video/3gpp. I'm doing this hacky check of the URI to see if it
|
||||
// is stored in the audio or video content store.
|
||||
if (fp.getAbsoluteFile().toString().endsWith(".3gp") || fp.getAbsoluteFile().toString().endsWith(".3gpp")) {
|
||||
if (data.toString().contains("/audio/")) {
|
||||
obj.put("type", AUDIO_3GPP);
|
||||
} else {
|
||||
obj.put("type", VIDEO_3GPP);
|
||||
}
|
||||
} else {
|
||||
obj.put("type", FileHelper.getMimeType(fp.getAbsolutePath(), cordova));
|
||||
}
|
||||
|
||||
obj.put("lastModifiedDate", fp.lastModified());
|
||||
obj.put("size", fp.length());
|
||||
} catch (JSONException e) {
|
||||
// this will never happen
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
private JSONObject createErrorObject(int code, String message) {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("code", code);
|
||||
obj.put("message", message);
|
||||
} catch (JSONException e) {
|
||||
// This will never happen
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send error message to JavaScript.
|
||||
*
|
||||
* @param err
|
||||
*/
|
||||
public void fail(JSONObject err) {
|
||||
this.callbackContext.error(err);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a cursor that can be used to determine how many images we have.
|
||||
*
|
||||
* @return a cursor
|
||||
*/
|
||||
private Cursor queryImgDB(Uri contentStore) {
|
||||
return this.cordova.getActivity().getContentResolver().query(
|
||||
contentStore,
|
||||
new String[] { MediaStore.Images.Media._ID },
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to find out if we are in a situation where the Camera Intent adds to images
|
||||
* to the content store.
|
||||
*/
|
||||
private void checkForDuplicateImage() {
|
||||
Uri contentStore = whichContentStore();
|
||||
Cursor cursor = queryImgDB(contentStore);
|
||||
int currentNumOfImages = cursor.getCount();
|
||||
|
||||
// delete the duplicate file if the difference is 2
|
||||
if ((currentNumOfImages - numPics) == 2) {
|
||||
cursor.moveToLast();
|
||||
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1;
|
||||
Uri uri = Uri.parse(contentStore + "/" + id);
|
||||
this.cordova.getActivity().getContentResolver().delete(uri, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are storing the images in internal or external storage
|
||||
* @return Uri
|
||||
*/
|
||||
private Uri whichContentStore() {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else {
|
||||
return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.content.Context;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
/**
|
||||
* This class listens to the compass sensor and stores the latest heading value.
|
||||
*/
|
||||
public class CompassListener extends CordovaPlugin implements SensorEventListener {
|
||||
|
||||
public static int STOPPED = 0;
|
||||
public static int STARTING = 1;
|
||||
public static int RUNNING = 2;
|
||||
public static int ERROR_FAILED_TO_START = 3;
|
||||
|
||||
public long TIMEOUT = 30000; // Timeout in msec to shut off listener
|
||||
|
||||
int status; // status of listener
|
||||
float heading; // most recent heading value
|
||||
long timeStamp; // time of most recent value
|
||||
long lastAccessTime; // time the value was last retrieved
|
||||
int accuracy; // accuracy of the sensor
|
||||
|
||||
private SensorManager sensorManager;// Sensor manager
|
||||
Sensor mSensor; // Compass sensor returned by sensor manager
|
||||
|
||||
private CallbackContext callbackContext;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CompassListener() {
|
||||
this.heading = 0;
|
||||
this.timeStamp = 0;
|
||||
this.setStatus(CompassListener.STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The CordovaWebView Cordova is running in.
|
||||
*/
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
this.sensorManager = (SensorManager) cordova.getActivity().getSystemService(Context.SENSOR_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackS=Context The callback id used when calling back into JavaScript.
|
||||
* @return True if the action was valid.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("start")) {
|
||||
this.start();
|
||||
}
|
||||
else if (action.equals("stop")) {
|
||||
this.stop();
|
||||
}
|
||||
else if (action.equals("getStatus")) {
|
||||
int i = this.getStatus();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, i));
|
||||
}
|
||||
else if (action.equals("getHeading")) {
|
||||
// If not running, then this is an async call, so don't worry about waiting
|
||||
if (this.status != CompassListener.RUNNING) {
|
||||
int r = this.start();
|
||||
if (r == CompassListener.ERROR_FAILED_TO_START) {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, CompassListener.ERROR_FAILED_TO_START));
|
||||
return true;
|
||||
}
|
||||
// Set a timeout callback on the main thread.
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
CompassListener.this.timeout();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, getCompassHeading()));
|
||||
}
|
||||
else if (action.equals("setTimeout")) {
|
||||
this.setTimeout(args.getLong(0));
|
||||
}
|
||||
else if (action.equals("getTimeout")) {
|
||||
long l = this.getTimeout();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, l));
|
||||
} else {
|
||||
// Unsupported action
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when listener is to be shut down and object is being destroyed.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when app has navigated and JS listeners have been destroyed.
|
||||
*/
|
||||
public void onReset() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Start listening for compass sensor.
|
||||
*
|
||||
* @return status of listener
|
||||
*/
|
||||
public int start() {
|
||||
|
||||
// If already starting or running, then just return
|
||||
if ((this.status == CompassListener.RUNNING) || (this.status == CompassListener.STARTING)) {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
// Get compass sensor from sensor manager
|
||||
@SuppressWarnings("deprecation")
|
||||
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
|
||||
|
||||
// If found, then register as listener
|
||||
if (list != null && list.size() > 0) {
|
||||
this.mSensor = list.get(0);
|
||||
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL);
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
this.setStatus(CompassListener.STARTING);
|
||||
}
|
||||
|
||||
// If error, then set status to error
|
||||
else {
|
||||
this.setStatus(CompassListener.ERROR_FAILED_TO_START);
|
||||
}
|
||||
|
||||
return this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening to compass sensor.
|
||||
*/
|
||||
public void stop() {
|
||||
if (this.status != CompassListener.STOPPED) {
|
||||
this.sensorManager.unregisterListener(this);
|
||||
}
|
||||
this.setStatus(CompassListener.STOPPED);
|
||||
}
|
||||
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a delay to time out if the listener has not attached fast enough.
|
||||
*/
|
||||
private void timeout() {
|
||||
if (this.status == CompassListener.STARTING) {
|
||||
this.setStatus(CompassListener.ERROR_FAILED_TO_START);
|
||||
if (this.callbackContext != null) {
|
||||
this.callbackContext.error("Compass listener failed to start.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sensor listener event.
|
||||
*
|
||||
* @param SensorEvent event
|
||||
*/
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
|
||||
// We only care about the orientation as far as it refers to Magnetic North
|
||||
float heading = event.values[0];
|
||||
|
||||
// Save heading
|
||||
this.timeStamp = System.currentTimeMillis();
|
||||
this.heading = heading;
|
||||
this.setStatus(CompassListener.RUNNING);
|
||||
|
||||
// If heading hasn't been read for TIMEOUT time, then turn off compass sensor to save power
|
||||
if ((this.timeStamp - this.lastAccessTime) > this.TIMEOUT) {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status of compass sensor.
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
public int getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recent compass heading.
|
||||
*
|
||||
* @return heading
|
||||
*/
|
||||
public float getHeading() {
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
return this.heading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeout to turn off compass sensor if getHeading() hasn't been called.
|
||||
*
|
||||
* @param timeout Timeout in msec.
|
||||
*/
|
||||
public void setTimeout(long timeout) {
|
||||
this.TIMEOUT = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timeout to turn off compass sensor if getHeading() hasn't been called.
|
||||
*
|
||||
* @return timeout in msec
|
||||
*/
|
||||
public long getTimeout() {
|
||||
return this.TIMEOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status and send it to JavaScript.
|
||||
* @param status
|
||||
*/
|
||||
private void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the CompassHeading JSON object to be returned to JavaScript
|
||||
*
|
||||
* @return a compass heading
|
||||
*/
|
||||
private JSONObject getCompassHeading() throws JSONException {
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
obj.put("magneticHeading", this.getHeading());
|
||||
obj.put("trueHeading", this.getHeading());
|
||||
// Since the magnetic and true heading are always the same our and accuracy
|
||||
// is defined as the difference between true and magnetic always return zero
|
||||
obj.put("headingAccuracy", 0);
|
||||
obj.put("timestamp", this.timeStamp);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,14 +21,12 @@ package org.apache.cordova;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.LOG;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
@@ -43,8 +41,7 @@ public class Config {
|
||||
|
||||
public static final String TAG = "Config";
|
||||
|
||||
private ArrayList<Pattern> whiteList = new ArrayList<Pattern>();
|
||||
private HashMap<String, Boolean> whiteListCache = new HashMap<String, Boolean>();
|
||||
private Whitelist whitelist = new Whitelist();
|
||||
private String startUrl;
|
||||
|
||||
private static Config self = null;
|
||||
@@ -81,6 +78,11 @@ public class Config {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add implicitly allowed URLs
|
||||
whitelist.addWhiteListEntry("file:///*", false);
|
||||
whitelist.addWhiteListEntry("content:///*", false);
|
||||
whitelist.addWhiteListEntry("data:*", false);
|
||||
|
||||
XmlResourceParser xml = action.getResources().getXml(id);
|
||||
int eventType = -1;
|
||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||
@@ -91,7 +93,7 @@ public class Config {
|
||||
String origin = xml.getAttributeValue(null, "origin");
|
||||
String subdomains = xml.getAttributeValue(null, "subdomains");
|
||||
if (origin != null) {
|
||||
this._addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
||||
whitelist.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
||||
}
|
||||
}
|
||||
else if (strNode.equals("log")) {
|
||||
@@ -102,7 +104,7 @@ public class Config {
|
||||
}
|
||||
}
|
||||
else if (strNode.equals("preference")) {
|
||||
String name = xml.getAttributeValue(null, "name");
|
||||
String name = xml.getAttributeValue(null, "name").toLowerCase(Locale.getDefault());
|
||||
/* Java 1.6 does not support switch-based strings
|
||||
Java 7 does, but we're using Dalvik, which is apparently not Java.
|
||||
Since we're reading XML, this has to be an ugly if/else.
|
||||
@@ -112,10 +114,10 @@ public class Config {
|
||||
|
||||
Note: We should probably pass in the classname for the variable splash on splashscreen!
|
||||
*/
|
||||
if (name.equals("loglevel")) {
|
||||
if (name.equalsIgnoreCase("LogLevel")) {
|
||||
String level = xml.getAttributeValue(null, "value");
|
||||
LOG.setLogLevel(level);
|
||||
} else if (name.equals("splashscreen")) {
|
||||
} else if (name.equalsIgnoreCase("SplashScreen")) {
|
||||
String value = xml.getAttributeValue(null, "value");
|
||||
int resource = 0;
|
||||
if (value == null)
|
||||
@@ -126,25 +128,29 @@ public class Config {
|
||||
|
||||
action.getIntent().putExtra(name, resource);
|
||||
}
|
||||
else if(name.equals("backgroundColor")) {
|
||||
else if(name.equalsIgnoreCase("BackgroundColor")) {
|
||||
int value = xml.getAttributeIntValue(null, "value", Color.BLACK);
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equals("loadUrlTimeoutValue")) {
|
||||
else if(name.equalsIgnoreCase("LoadUrlTimeoutValue")) {
|
||||
int value = xml.getAttributeIntValue(null, "value", 20000);
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equals("keepRunning"))
|
||||
else if(name.equalsIgnoreCase("SplashScreenDelay")) {
|
||||
int value = xml.getAttributeIntValue(null, "value", 3000);
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equalsIgnoreCase("KeepRunning"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equals("InAppBrowserStorageEnabled"))
|
||||
else if(name.equalsIgnoreCase("InAppBrowserStorageEnabled"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
}
|
||||
else if(name.equals("disallowOverscroll"))
|
||||
else if(name.equalsIgnoreCase("DisallowOverscroll"))
|
||||
{
|
||||
boolean value = xml.getAttributeValue(null, "value").equals("true");
|
||||
action.getIntent().putExtra(name, value);
|
||||
@@ -199,55 +205,7 @@ public class Config {
|
||||
if (self == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
self._addWhiteListEntry(origin, subdomains);
|
||||
}
|
||||
|
||||
|
||||
private void _addWhiteListEntry(String origin, boolean subdomains) {
|
||||
try {
|
||||
// Unlimited access to network resources
|
||||
if (origin.compareTo("*") == 0) {
|
||||
LOG.d(TAG, "Unlimited access to network resources");
|
||||
this.whiteList.add(Pattern.compile(".*"));
|
||||
} else { // specific access
|
||||
// check if subdomains should be included
|
||||
// TODO: we should not add more domains if * has already been added
|
||||
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
|
||||
Matcher matcher = schemeRegex.matcher(origin);
|
||||
if (subdomains) {
|
||||
// Check for http or https protocols
|
||||
if (origin.startsWith("http")) {
|
||||
this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?")));
|
||||
}
|
||||
// Check for other protocols
|
||||
else if(matcher.find()){
|
||||
this.whiteList.add(Pattern.compile("^" + origin.replaceFirst("//", "//(.*\\.)?")));
|
||||
}
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
else {
|
||||
this.whiteList.add(Pattern.compile("^https?://(.*\\.)?" + origin));
|
||||
}
|
||||
LOG.d(TAG, "Origin to allow with subdomains: %s", origin);
|
||||
} else {
|
||||
// Check for http or https protocols
|
||||
if (origin.startsWith("http")) {
|
||||
this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://")));
|
||||
}
|
||||
// Check for other protocols
|
||||
else if(matcher.find()){
|
||||
this.whiteList.add(Pattern.compile("^" + origin));
|
||||
}
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
else {
|
||||
this.whiteList.add(Pattern.compile("^https?://" + origin));
|
||||
}
|
||||
LOG.d(TAG, "Origin to allow: %s", origin);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.d(TAG, "Failed to add origin %s", origin);
|
||||
}
|
||||
self.whitelist.addWhiteListEntry(origin, subdomains);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,25 +218,7 @@ public class Config {
|
||||
if (self == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check to see if we have matched url previously
|
||||
if (self.whiteListCache.get(url) != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Look for match in white list
|
||||
Iterator<Pattern> pit = self.whiteList.iterator();
|
||||
while (pit.hasNext()) {
|
||||
Pattern p = pit.next();
|
||||
Matcher m = p.matcher(url);
|
||||
|
||||
// If match found, then cache it to speed up subsequent comparisons
|
||||
if (m.find()) {
|
||||
self.whiteListCache.put(url, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return self.whitelist.isUrlWhiteListed(url);
|
||||
}
|
||||
|
||||
public static String getStartUrl() {
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* This abstract class defines SDK-independent API for communication with
|
||||
* Contacts Provider. The actual implementation used by the application depends
|
||||
* on the level of API available on the device. If the API level is Cupcake or
|
||||
* Donut, we want to use the {@link ContactAccessorSdk3_4} class. If it is
|
||||
* Eclair or higher, we want to use {@link ContactAccessorSdk5}.
|
||||
*/
|
||||
public abstract class ContactAccessor {
|
||||
|
||||
protected final String LOG_TAG = "ContactsAccessor";
|
||||
protected CordovaInterface mApp;
|
||||
protected WebView mView;
|
||||
|
||||
/**
|
||||
* Check to see if the data associated with the key is required to
|
||||
* be populated in the Contact object.
|
||||
* @param key
|
||||
* @param map created by running buildPopulationSet.
|
||||
* @return true if the key data is required
|
||||
*/
|
||||
protected boolean isRequired(String key, HashMap<String,Boolean> map) {
|
||||
Boolean retVal = map.get(key);
|
||||
return (retVal == null) ? false : retVal.booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hash map of what data needs to be populated in the Contact object
|
||||
* @param fields the list of fields to populate
|
||||
* @return the hash map of required data
|
||||
*/
|
||||
protected HashMap<String,Boolean> buildPopulationSet(JSONArray fields) {
|
||||
HashMap<String,Boolean> map = new HashMap<String,Boolean>();
|
||||
|
||||
String key;
|
||||
try {
|
||||
if (fields.length() == 1 && fields.getString(0).equals("*")) {
|
||||
map.put("displayName", true);
|
||||
map.put("name", true);
|
||||
map.put("nickname", true);
|
||||
map.put("phoneNumbers", true);
|
||||
map.put("emails", true);
|
||||
map.put("addresses", true);
|
||||
map.put("ims", true);
|
||||
map.put("organizations", true);
|
||||
map.put("birthday", true);
|
||||
map.put("note", true);
|
||||
map.put("urls", true);
|
||||
map.put("photos", true);
|
||||
map.put("categories", true);
|
||||
}
|
||||
else {
|
||||
for (int i=0; i<fields.length(); i++) {
|
||||
key = fields.getString(i);
|
||||
if (key.startsWith("displayName")) {
|
||||
map.put("displayName", true);
|
||||
}
|
||||
else if (key.startsWith("name")) {
|
||||
map.put("displayName", true);
|
||||
map.put("name", true);
|
||||
}
|
||||
else if (key.startsWith("nickname")) {
|
||||
map.put("nickname", true);
|
||||
}
|
||||
else if (key.startsWith("phoneNumbers")) {
|
||||
map.put("phoneNumbers", true);
|
||||
}
|
||||
else if (key.startsWith("emails")) {
|
||||
map.put("emails", true);
|
||||
}
|
||||
else if (key.startsWith("addresses")) {
|
||||
map.put("addresses", true);
|
||||
}
|
||||
else if (key.startsWith("ims")) {
|
||||
map.put("ims", true);
|
||||
}
|
||||
else if (key.startsWith("organizations")) {
|
||||
map.put("organizations", true);
|
||||
}
|
||||
else if (key.startsWith("birthday")) {
|
||||
map.put("birthday", true);
|
||||
}
|
||||
else if (key.startsWith("note")) {
|
||||
map.put("note", true);
|
||||
}
|
||||
else if (key.startsWith("urls")) {
|
||||
map.put("urls", true);
|
||||
}
|
||||
else if (key.startsWith("photos")) {
|
||||
map.put("photos", true);
|
||||
}
|
||||
else if (key.startsWith("categories")) {
|
||||
map.put("categories", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to get a string from a JSON object. Saves a
|
||||
* lot of try/catch writing.
|
||||
* If the property is not found in the object null will be returned.
|
||||
*
|
||||
* @param obj contact object to search
|
||||
* @param property to be looked up
|
||||
* @return The value of the property
|
||||
*/
|
||||
protected String getJsonString(JSONObject obj, String property) {
|
||||
String value = null;
|
||||
try {
|
||||
if (obj != null) {
|
||||
value = obj.getString(property);
|
||||
if (value.equals("null")) {
|
||||
Log.d(LOG_TAG, property + " is string called 'null'");
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Could not get = " + e.getMessage());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adding a JSON Contact object into the database.
|
||||
* @return TODO
|
||||
*/
|
||||
public abstract String save(JSONObject contact);
|
||||
|
||||
/**
|
||||
* Handles searching through SDK-specific contacts API.
|
||||
*/
|
||||
public abstract JSONArray search(JSONArray filter, JSONObject options);
|
||||
|
||||
/**
|
||||
* Handles searching through SDK-specific contacts API.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public abstract JSONObject getContactById(String id) throws JSONException;
|
||||
|
||||
/**
|
||||
* Handles removing a contact from the database.
|
||||
*/
|
||||
public abstract boolean remove(String id);
|
||||
|
||||
/**
|
||||
* A class that represents the where clause to be used in the database query
|
||||
*/
|
||||
class WhereOptions {
|
||||
private String where;
|
||||
private String[] whereArgs;
|
||||
public void setWhere(String where) {
|
||||
this.where = where;
|
||||
}
|
||||
public String getWhere() {
|
||||
return where;
|
||||
}
|
||||
public void setWhereArgs(String[] whereArgs) {
|
||||
this.whereArgs = whereArgs;
|
||||
}
|
||||
public String[] getWhereArgs() {
|
||||
return whereArgs;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import android.util.Log;
|
||||
|
||||
public class ContactManager extends CordovaPlugin {
|
||||
|
||||
private ContactAccessor contactAccessor;
|
||||
private static final String LOG_TAG = "Contact Query";
|
||||
|
||||
public static final int UNKNOWN_ERROR = 0;
|
||||
public static final int INVALID_ARGUMENT_ERROR = 1;
|
||||
public static final int TIMEOUT_ERROR = 2;
|
||||
public static final int PENDING_OPERATION_ERROR = 3;
|
||||
public static final int IO_ERROR = 4;
|
||||
public static final int NOT_SUPPORTED_ERROR = 5;
|
||||
public static final int PERMISSION_DENIED_ERROR = 20;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public ContactManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArray of arguments for the plugin.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false otherwise.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
/**
|
||||
* Check to see if we are on an Android 1.X device. If we are return an error as we
|
||||
* do not support this as of Cordova 1.0.
|
||||
*/
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, ContactManager.NOT_SUPPORTED_ERROR));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only create the contactAccessor after we check the Android version or the program will crash
|
||||
* older phones.
|
||||
*/
|
||||
if (this.contactAccessor == null) {
|
||||
this.contactAccessor = new ContactAccessorSdk5(this.webView, this.cordova);
|
||||
}
|
||||
|
||||
if (action.equals("search")) {
|
||||
final JSONArray filter = args.getJSONArray(0);
|
||||
final JSONObject options = args.getJSONObject(1);
|
||||
this.cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
JSONArray res = contactAccessor.search(filter, options);
|
||||
callbackContext.success(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (action.equals("save")) {
|
||||
final JSONObject contact = args.getJSONObject(0);
|
||||
this.cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
JSONObject res = null;
|
||||
String id = contactAccessor.save(contact);
|
||||
if (id != null) {
|
||||
try {
|
||||
res = contactAccessor.getContactById(id);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, "JSON fail.", e);
|
||||
}
|
||||
}
|
||||
if (res != null) {
|
||||
callbackContext.success(res);
|
||||
} else {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (action.equals("remove")) {
|
||||
final String contactId = args.getString(0);
|
||||
this.cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
if (contactAccessor.remove(contactId)) {
|
||||
callbackContext.success();
|
||||
} else {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,13 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -122,16 +123,6 @@ import android.widget.LinearLayout;
|
||||
* Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
|
||||
* <log level="DEBUG" />
|
||||
*
|
||||
* Cordova plugins:
|
||||
* Cordova uses a file at res/xml/plugins.xml to list all plugins that are installed.
|
||||
* Before using a new plugin, a new element must be added to the file.
|
||||
* name attribute is the service name passed to Cordova.exec() in JavaScript
|
||||
* value attribute is the Java class name to call.
|
||||
*
|
||||
* <plugins>
|
||||
* <plugin name="App" value="org.apache.cordova.App"/>
|
||||
* ...
|
||||
* </plugins>
|
||||
*/
|
||||
public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
public static String TAG = "CordovaActivity";
|
||||
@@ -267,12 +258,12 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
initCallbackClass = savedInstanceState.getString("callbackClass");
|
||||
}
|
||||
|
||||
if(!this.getBooleanProperty("showTitle", false))
|
||||
if(!this.getBooleanProperty("ShowTitle", false))
|
||||
{
|
||||
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
}
|
||||
|
||||
if(this.getBooleanProperty("setFullscreen", false))
|
||||
if(this.getBooleanProperty("SetFullscreen", false))
|
||||
{
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
@@ -306,21 +297,50 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the default web view object.
|
||||
*
|
||||
* This is intended to be overridable by subclasses of CordovaIntent which
|
||||
* require a more specialized web view.
|
||||
*/
|
||||
protected CordovaWebView makeWebView() {
|
||||
return new CordovaWebView(CordovaActivity.this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the client for the default web view object.
|
||||
*
|
||||
* This is intended to be overridable by subclasses of CordovaIntent which
|
||||
* require a more specialized web view.
|
||||
*
|
||||
* @param webView the default constructed web view object
|
||||
*/
|
||||
protected CordovaWebViewClient makeWebViewClient(CordovaWebView webView) {
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
|
||||
return new CordovaWebViewClient(this, webView);
|
||||
} else {
|
||||
return new IceCreamCordovaWebViewClient(this, webView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the chrome client for the default web view object.
|
||||
*
|
||||
* This is intended to be overridable by subclasses of CordovaIntent which
|
||||
* require a more specialized web view.
|
||||
*
|
||||
* @param webView the default constructed web view object
|
||||
*/
|
||||
protected CordovaChromeClient makeChromeClient(CordovaWebView webView) {
|
||||
return new CordovaChromeClient(this, webView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and initialize web container with default web view objects.
|
||||
*/
|
||||
public void init() {
|
||||
CordovaWebView webView = new CordovaWebView(CordovaActivity.this);
|
||||
CordovaWebViewClient webViewClient;
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
{
|
||||
webViewClient = new CordovaWebViewClient(this, webView);
|
||||
}
|
||||
else
|
||||
{
|
||||
webViewClient = new IceCreamCordovaWebViewClient(this, webView);
|
||||
}
|
||||
this.init(webView, webViewClient, new CordovaChromeClient(this, webView));
|
||||
CordovaWebView webView = makeWebView();
|
||||
this.init(webView, makeWebViewClient(webView), makeChromeClient(webView));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,7 +368,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
1.0F));
|
||||
|
||||
if (this.getBooleanProperty("disallowOverscroll", false)) {
|
||||
if (this.getBooleanProperty("DisallowOverscroll", false)) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
|
||||
this.appView.setOverScrollMode(CordovaWebView.OVER_SCROLL_NEVER);
|
||||
}
|
||||
@@ -376,19 +396,63 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
this.init();
|
||||
}
|
||||
|
||||
this.splashscreenTime = this.getIntegerProperty("SplashScreenDelay", this.splashscreenTime);
|
||||
if(this.splashscreenTime > 0)
|
||||
{
|
||||
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
||||
if(this.splashscreen != 0)
|
||||
{
|
||||
this.showSplashScreen(this.splashscreenTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Set backgroundColor
|
||||
this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK);
|
||||
this.backgroundColor = this.getIntegerProperty("BackgroundColor", Color.BLACK);
|
||||
this.root.setBackgroundColor(this.backgroundColor);
|
||||
|
||||
// If keepRunning
|
||||
this.keepRunning = this.getBooleanProperty("keepRunning", true);
|
||||
this.keepRunning = this.getBooleanProperty("KeepRunning", true);
|
||||
|
||||
// Then load the spinner
|
||||
this.loadSpinner();
|
||||
|
||||
this.appView.loadUrl(url);
|
||||
//Load the correct splashscreen
|
||||
|
||||
if(this.splashscreen != 0)
|
||||
{
|
||||
this.appView.loadUrl(url, this.splashscreenTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.appView.loadUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the url into the webview after waiting for period of time.
|
||||
* This is used to display the splashscreen for certain amount of time.
|
||||
*
|
||||
* @param url
|
||||
* @param time The number of ms to wait before loading webview
|
||||
*/
|
||||
public void loadUrl(final String url, int time) {
|
||||
|
||||
this.splashscreenTime = time;
|
||||
this.loadUrl(url);
|
||||
|
||||
/*
|
||||
// Init web view if not already done
|
||||
if (this.appView == null) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
this.splashscreenTime = time;
|
||||
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
||||
this.showSplashScreen(this.splashscreenTime);
|
||||
this.appView.loadUrl(url, time);
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the spinner
|
||||
*/
|
||||
@@ -397,10 +461,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
// If loadingDialog property, then show the App loading dialog for first page of app
|
||||
String loading = null;
|
||||
if ((this.appView == null) || !this.appView.canGoBack()) {
|
||||
loading = this.getStringProperty("loadingDialog", null);
|
||||
loading = this.getStringProperty("LoadingDialog", null);
|
||||
}
|
||||
else {
|
||||
loading = this.getStringProperty("loadingPageDialog", null);
|
||||
loading = this.getStringProperty("LoadingPageDialog", null);
|
||||
}
|
||||
if (loading != null) {
|
||||
|
||||
@@ -422,25 +486,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the url into the webview after waiting for period of time.
|
||||
* This is used to display the splashscreen for certain amount of time.
|
||||
*
|
||||
* @param url
|
||||
* @param time The number of ms to wait before loading webview
|
||||
*/
|
||||
public void loadUrl(final String url, int time) {
|
||||
|
||||
// Init web view if not already done
|
||||
if (this.appView == null) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
this.splashscreenTime = time;
|
||||
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||
this.showSplashScreen(this.splashscreenTime);
|
||||
this.appView.loadUrl(url, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel loadUrl before it has been loaded.
|
||||
@@ -503,6 +548,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
Boolean p;
|
||||
try {
|
||||
p = (Boolean) bundle.get(name);
|
||||
@@ -533,6 +579,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
Integer p;
|
||||
try {
|
||||
p = (Integer) bundle.get(name);
|
||||
@@ -557,6 +604,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
String p = bundle.getString(name);
|
||||
if (p == null) {
|
||||
return defaultValue;
|
||||
@@ -576,6 +624,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
Double p;
|
||||
try {
|
||||
p = (Double) bundle.get(name);
|
||||
@@ -596,7 +645,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
*/
|
||||
public void setBooleanProperty(String name, boolean value) {
|
||||
Log.d(TAG, "Setting boolean properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name, value);
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -607,7 +656,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
*/
|
||||
public void setIntegerProperty(String name, int value) {
|
||||
Log.d(TAG, "Setting integer properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name, value);
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -618,7 +667,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
*/
|
||||
public void setStringProperty(String name, String value) {
|
||||
Log.d(TAG, "Setting string properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name, value);
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -629,7 +678,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
*/
|
||||
public void setDoubleProperty(String name, double value) {
|
||||
Log.d(TAG, "Setting double properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
|
||||
this.getIntent().putExtra(name, value);
|
||||
this.getIntent().putExtra(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -682,7 +731,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
|
||||
|
||||
//Code to test CB-3064
|
||||
String errorUrl = this.getStringProperty("errorUrl", null);
|
||||
String errorUrl = this.getStringProperty("ErrorUrl", null);
|
||||
LOG.d(TAG, "CB-3064: The errorUrl is " + errorUrl);
|
||||
|
||||
if (this.activityState == ACTIVITY_STARTING) {
|
||||
@@ -757,7 +806,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
* Send JavaScript statement back to JavaScript.
|
||||
* (This is a convenience method)
|
||||
*
|
||||
* @param message
|
||||
* @param statement
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
if (this.appView != null) {
|
||||
@@ -839,8 +888,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
LOG.d(TAG, "Incoming Result");
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
Log.d(TAG, "Request code = " + requestCode);
|
||||
ValueCallback<Uri> mUploadMessage = this.appView.getWebChromeClient().getValueCallback();
|
||||
if (requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
|
||||
if (appView != null && requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
|
||||
ValueCallback<Uri> mUploadMessage = this.appView.getWebChromeClient().getValueCallback();
|
||||
Log.d(TAG, "did we get here?");
|
||||
if (null == mUploadMessage)
|
||||
return;
|
||||
@@ -1029,8 +1078,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
root.setMinimumWidth(display.getWidth());
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setBackgroundColor(that.getIntegerProperty("backgroundColor", Color.BLACK));
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 0.0F));
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
|
||||
root.setBackgroundResource(that.splashscreen);
|
||||
|
||||
// Create and show the dialog
|
||||
@@ -1060,9 +1109,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event)
|
||||
{
|
||||
//Get whatever has focus!
|
||||
View childView = appView.getFocusedChild();
|
||||
if ((appView.isCustomViewShowing() || childView != null ) &&
|
||||
if (appView != null && (appView.isCustomViewShowing() || appView.getFocusedChild() != null ) &&
|
||||
(keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
return appView.onKeyUp(keyCode, event);
|
||||
} else {
|
||||
@@ -1080,10 +1127,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||
{
|
||||
//Get whatever has focus!
|
||||
View childView = appView.getFocusedChild();
|
||||
//Determine if the focus is on the current view or not
|
||||
if (childView != null && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
if (appView != null && appView.getFocusedChild() != null && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
return appView.onKeyDown(keyCode, event);
|
||||
}
|
||||
else
|
||||
@@ -1107,7 +1152,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
else {
|
||||
// If the splash dialog is showing don't try to show it again
|
||||
if (this.splashDialog == null || !this.splashDialog.isShowing()) {
|
||||
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||
this.splashscreen = this.getIntegerProperty("SplashScreen", 0);
|
||||
this.showSplashScreen(this.splashscreenTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
@@ -54,8 +54,8 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
private static final String LOG_TAG = "CordovaChromeClient";
|
||||
private String TAG = "CordovaLog";
|
||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||
private CordovaInterface cordova;
|
||||
private CordovaWebView appView;
|
||||
protected CordovaInterface cordova;
|
||||
protected CordovaWebView appView;
|
||||
|
||||
// the video progress view
|
||||
private View mVideoProgressView;
|
||||
@@ -228,13 +228,18 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
|
||||
// Sets the native->JS bridge mode.
|
||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_bridge_mode:")) {
|
||||
this.appView.exposedJsApi.setNativeToJsBridgeMode(Integer.parseInt(message));
|
||||
result.confirm("");
|
||||
try {
|
||||
this.appView.exposedJsApi.setNativeToJsBridgeMode(Integer.parseInt(message));
|
||||
result.confirm("");
|
||||
} catch (NumberFormatException e){
|
||||
result.confirm("");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Polling for JavaScript messages
|
||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) {
|
||||
String r = this.appView.exposedJsApi.retrieveJsMessages();
|
||||
String r = this.appView.exposedJsApi.retrieveJsMessages("1".equals(message));
|
||||
result.confirm(r == null ? "" : r);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova.api;
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
public class CordovaLocationListener implements LocationListener {
|
||||
public static int PERMISSION_DENIED = 1;
|
||||
public static int POSITION_UNAVAILABLE = 2;
|
||||
public static int TIMEOUT = 3;
|
||||
|
||||
protected LocationManager locationManager;
|
||||
private GeoBroker owner;
|
||||
protected boolean running = false;
|
||||
|
||||
public HashMap<String, CallbackContext> watches = new HashMap<String, CallbackContext>();
|
||||
private List<CallbackContext> callbacks = new ArrayList<CallbackContext>();
|
||||
|
||||
private Timer timer = null;
|
||||
|
||||
private String TAG = "[Cordova Location Listener]";
|
||||
|
||||
public CordovaLocationListener(LocationManager manager, GeoBroker broker, String tag) {
|
||||
this.locationManager = manager;
|
||||
this.owner = broker;
|
||||
this.TAG = tag;
|
||||
}
|
||||
|
||||
protected void fail(int code, String message) {
|
||||
this.cancelTimer();
|
||||
for (CallbackContext callbackContext: this.callbacks)
|
||||
{
|
||||
this.owner.fail(code, message, callbackContext, false);
|
||||
}
|
||||
if(this.owner.isGlobalListener(this) && this.watches.size() == 0)
|
||||
{
|
||||
Log.d(TAG, "Stopping global listener");
|
||||
this.stop();
|
||||
}
|
||||
this.callbacks.clear();
|
||||
|
||||
Iterator<CallbackContext> it = this.watches.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
this.owner.fail(code, message, it.next(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void win(Location loc) {
|
||||
this.cancelTimer();
|
||||
for (CallbackContext callbackContext: this.callbacks)
|
||||
{
|
||||
this.owner.win(loc, callbackContext, false);
|
||||
}
|
||||
if(this.owner.isGlobalListener(this) && this.watches.size() == 0)
|
||||
{
|
||||
Log.d(TAG, "Stopping global listener");
|
||||
this.stop();
|
||||
}
|
||||
this.callbacks.clear();
|
||||
|
||||
Iterator<CallbackContext> it = this.watches.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
this.owner.win(loc, it.next(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Location Listener Methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Called when the provider is disabled by the user.
|
||||
*
|
||||
* @param provider
|
||||
*/
|
||||
public void onProviderDisabled(String provider) {
|
||||
Log.d(TAG, "Location provider '" + provider + "' disabled.");
|
||||
this.fail(POSITION_UNAVAILABLE, "GPS provider disabled.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the provider is enabled by the user.
|
||||
*
|
||||
* @param provider
|
||||
*/
|
||||
public void onProviderEnabled(String provider) {
|
||||
Log.d(TAG, "Location provider "+ provider + " has been enabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the provider status changes. This method is called when a
|
||||
* provider is unable to fetch a location or if the provider has recently
|
||||
* become available after a period of unavailability.
|
||||
*
|
||||
* @param provider
|
||||
* @param status
|
||||
* @param extras
|
||||
*/
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
Log.d(TAG, "The status of the provider " + provider + " has changed");
|
||||
if (status == 0) {
|
||||
Log.d(TAG, provider + " is OUT OF SERVICE");
|
||||
this.fail(CordovaLocationListener.POSITION_UNAVAILABLE, "Provider " + provider + " is out of service.");
|
||||
}
|
||||
else if (status == 1) {
|
||||
Log.d(TAG, provider + " is TEMPORARILY_UNAVAILABLE");
|
||||
}
|
||||
else {
|
||||
Log.d(TAG, provider + " is AVAILABLE");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the location has changed.
|
||||
*
|
||||
* @param location
|
||||
*/
|
||||
public void onLocationChanged(Location location) {
|
||||
Log.d(TAG, "The location has been updated!");
|
||||
this.win(location);
|
||||
}
|
||||
|
||||
// PUBLIC
|
||||
|
||||
public int size() {
|
||||
return this.watches.size() + this.callbacks.size();
|
||||
}
|
||||
|
||||
public void addWatch(String timerId, CallbackContext callbackContext) {
|
||||
this.watches.put(timerId, callbackContext);
|
||||
if (this.size() == 1) {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
public void addCallback(CallbackContext callbackContext, int timeout) {
|
||||
if(this.timer == null) {
|
||||
this.timer = new Timer();
|
||||
}
|
||||
this.timer.schedule(new LocationTimeoutTask(callbackContext, this), timeout);
|
||||
this.callbacks.add(callbackContext);
|
||||
if (this.size() == 1) {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
public void clearWatch(String timerId) {
|
||||
if (this.watches.containsKey(timerId)) {
|
||||
this.watches.remove(timerId);
|
||||
}
|
||||
if (this.size() == 0) {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy listener.
|
||||
*/
|
||||
public void destroy() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
// LOCAL
|
||||
|
||||
/**
|
||||
* Start requesting location updates.
|
||||
*
|
||||
* @param interval
|
||||
*/
|
||||
protected void start() {
|
||||
if (!this.running) {
|
||||
if (this.locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
|
||||
this.running = true;
|
||||
this.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 60000, 10, this);
|
||||
} else {
|
||||
this.fail(CordovaLocationListener.POSITION_UNAVAILABLE, "Network provider is not available.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop receiving location updates.
|
||||
*/
|
||||
private void stop() {
|
||||
this.cancelTimer();
|
||||
if (this.running) {
|
||||
this.locationManager.removeUpdates(this);
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelTimer() {
|
||||
if(this.timer != null) {
|
||||
this.timer.cancel();
|
||||
this.timer.purge();
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private class LocationTimeoutTask extends TimerTask {
|
||||
|
||||
private CallbackContext callbackContext = null;
|
||||
private CordovaLocationListener listener = null;
|
||||
|
||||
public LocationTimeoutTask(CallbackContext callbackContext, CordovaLocationListener listener) {
|
||||
this.callbackContext = callbackContext;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (CallbackContext callbackContext: listener.callbacks) {
|
||||
if(this.callbackContext == callbackContext) {
|
||||
listener.callbacks.remove(callbackContext);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(listener.size() == 0) {
|
||||
listener.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,17 @@
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova.api;
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
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;
|
||||
import android.net.Uri;
|
||||
|
||||
/**
|
||||
* Plugins must extend this class and override one of the execute methods.
|
||||
@@ -149,7 +148,7 @@ public class CordovaPlugin {
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
}
|
||||
@@ -165,16 +164,12 @@ public class CordovaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Hook for redirecting requests. Applies to WebView requests as well as requests made by plugins.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public WebResourceResponse shouldInterceptRequest(String url) {
|
||||
public Uri remapUri(Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the WebView does a top-level navigation or refreshes.
|
||||
*
|
||||
416
framework/src/org/apache/cordova/CordovaResourceApi.java
Normal file
416
framework/src/org/apache/cordova/CordovaResourceApi.java
Normal file
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.AssetManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Looper;
|
||||
import android.util.Base64;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
|
||||
import org.apache.http.util.EncodingUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.Locale;
|
||||
|
||||
public class CordovaResourceApi {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String LOG_TAG = "CordovaResourceApi";
|
||||
|
||||
public static final int URI_TYPE_FILE = 0;
|
||||
public static final int URI_TYPE_ASSET = 1;
|
||||
public static final int URI_TYPE_CONTENT = 2;
|
||||
public static final int URI_TYPE_RESOURCE = 3;
|
||||
public static final int URI_TYPE_DATA = 4;
|
||||
public static final int URI_TYPE_HTTP = 5;
|
||||
public static final int URI_TYPE_HTTPS = 6;
|
||||
public static final int URI_TYPE_UNKNOWN = -1;
|
||||
|
||||
private static final String[] LOCAL_FILE_PROJECTION = { "_data" };
|
||||
|
||||
// Creating this is light-weight.
|
||||
private static OkHttpClient httpClient = new OkHttpClient();
|
||||
|
||||
static Thread jsThread;
|
||||
|
||||
private final AssetManager assetManager;
|
||||
private final ContentResolver contentResolver;
|
||||
private final PluginManager pluginManager;
|
||||
private boolean threadCheckingEnabled = true;
|
||||
|
||||
|
||||
public CordovaResourceApi(Context context, PluginManager pluginManager) {
|
||||
this.contentResolver = context.getContentResolver();
|
||||
this.assetManager = context.getAssets();
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
public void setThreadCheckingEnabled(boolean value) {
|
||||
threadCheckingEnabled = value;
|
||||
}
|
||||
|
||||
public boolean isThreadCheckingEnabled() {
|
||||
return threadCheckingEnabled;
|
||||
}
|
||||
|
||||
public static int getUriType(Uri uri) {
|
||||
assertNonRelative(uri);
|
||||
String scheme = uri.getScheme();
|
||||
if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
|
||||
return URI_TYPE_CONTENT;
|
||||
}
|
||||
if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
|
||||
return URI_TYPE_RESOURCE;
|
||||
}
|
||||
if (ContentResolver.SCHEME_FILE.equals(scheme)) {
|
||||
if (uri.getPath().startsWith("/android_asset/")) {
|
||||
return URI_TYPE_ASSET;
|
||||
}
|
||||
return URI_TYPE_FILE;
|
||||
}
|
||||
if ("data".equals(scheme)) {
|
||||
return URI_TYPE_DATA;
|
||||
}
|
||||
if ("http".equals(scheme)) {
|
||||
return URI_TYPE_HTTP;
|
||||
}
|
||||
if ("https".equals(scheme)) {
|
||||
return URI_TYPE_HTTPS;
|
||||
}
|
||||
return URI_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
public Uri remapUri(Uri uri) {
|
||||
assertNonRelative(uri);
|
||||
Uri pluginUri = pluginManager.remapUri(uri);
|
||||
return pluginUri != null ? pluginUri : uri;
|
||||
}
|
||||
|
||||
public String remapPath(String path) {
|
||||
return remapUri(Uri.fromFile(new File(path))).getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a File that points to the resource, or null if the resource
|
||||
* is not on the local filesystem.
|
||||
*/
|
||||
public File mapUriToFile(Uri uri) {
|
||||
assertBackgroundThread();
|
||||
switch (getUriType(uri)) {
|
||||
case URI_TYPE_FILE:
|
||||
return new File(uri.getPath());
|
||||
case URI_TYPE_CONTENT: {
|
||||
Cursor cursor = contentResolver.query(uri, LOCAL_FILE_PROJECTION, null, null, null);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
int columnIndex = cursor.getColumnIndex(LOCAL_FILE_PROJECTION[0]);
|
||||
if (columnIndex != -1 && cursor.getCount() > 0) {
|
||||
cursor.moveToFirst();
|
||||
String realPath = cursor.getString(columnIndex);
|
||||
if (realPath != null) {
|
||||
return new File(realPath);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMimeType(Uri uri) {
|
||||
switch (getUriType(uri)) {
|
||||
case URI_TYPE_FILE:
|
||||
case URI_TYPE_ASSET:
|
||||
return getMimeTypeFromPath(uri.getPath());
|
||||
case URI_TYPE_CONTENT:
|
||||
case URI_TYPE_RESOURCE:
|
||||
return contentResolver.getType(uri);
|
||||
case URI_TYPE_DATA: {
|
||||
return getDataUriMimeType(uri);
|
||||
}
|
||||
case URI_TYPE_HTTP:
|
||||
case URI_TYPE_HTTPS: {
|
||||
try {
|
||||
HttpURLConnection conn = httpClient.open(new URL(uri.toString()));
|
||||
conn.setDoInput(false);
|
||||
conn.setRequestMethod("HEAD");
|
||||
return conn.getHeaderField("Content-Type");
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getMimeTypeFromPath(String path) {
|
||||
String extension = path;
|
||||
int lastDot = extension.lastIndexOf('.');
|
||||
if (lastDot != -1) {
|
||||
extension = extension.substring(lastDot + 1);
|
||||
}
|
||||
// Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185).
|
||||
extension = extension.toLowerCase(Locale.getDefault());
|
||||
if (extension.equals("3ga")) {
|
||||
return "audio/3gpp";
|
||||
}
|
||||
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a stream to the givne URI, also providing the MIME type & length.
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
* @throws Throws an IllegalStateException if called on a foreground thread.
|
||||
*/
|
||||
public OpenForReadResult openForRead(Uri uri) throws IOException {
|
||||
return openForRead(uri, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a stream to the givne URI, also providing the MIME type & length.
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
* @throws Throws an IllegalStateException if called on a foreground thread and skipThreadCheck is false.
|
||||
*/
|
||||
public OpenForReadResult openForRead(Uri uri, boolean skipThreadCheck) throws IOException {
|
||||
if (!skipThreadCheck) {
|
||||
assertBackgroundThread();
|
||||
}
|
||||
switch (getUriType(uri)) {
|
||||
case URI_TYPE_FILE: {
|
||||
FileInputStream inputStream = new FileInputStream(uri.getPath());
|
||||
String mimeType = getMimeTypeFromPath(uri.getPath());
|
||||
long length = inputStream.getChannel().size();
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, null);
|
||||
}
|
||||
case URI_TYPE_ASSET: {
|
||||
String assetPath = uri.getPath().substring(15);
|
||||
AssetFileDescriptor assetFd = null;
|
||||
InputStream inputStream;
|
||||
long length = -1;
|
||||
try {
|
||||
assetFd = assetManager.openFd(assetPath);
|
||||
inputStream = assetFd.createInputStream();
|
||||
length = assetFd.getLength();
|
||||
} catch (FileNotFoundException e) {
|
||||
// Will occur if the file is compressed.
|
||||
inputStream = assetManager.open(assetPath);
|
||||
}
|
||||
String mimeType = getMimeTypeFromPath(assetPath);
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, assetFd);
|
||||
}
|
||||
case URI_TYPE_CONTENT:
|
||||
case URI_TYPE_RESOURCE: {
|
||||
String mimeType = contentResolver.getType(uri);
|
||||
AssetFileDescriptor assetFd = contentResolver.openAssetFileDescriptor(uri, "r");
|
||||
InputStream inputStream = assetFd.createInputStream();
|
||||
long length = assetFd.getLength();
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, assetFd);
|
||||
}
|
||||
case URI_TYPE_DATA: {
|
||||
OpenForReadResult ret = readDataUri(uri);
|
||||
if (ret == null) {
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case URI_TYPE_HTTP:
|
||||
case URI_TYPE_HTTPS: {
|
||||
HttpURLConnection conn = httpClient.open(new URL(uri.toString()));
|
||||
conn.setDoInput(true);
|
||||
String mimeType = conn.getHeaderField("Content-Type");
|
||||
int length = conn.getContentLength();
|
||||
InputStream inputStream = conn.getInputStream();
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, null);
|
||||
}
|
||||
}
|
||||
throw new FileNotFoundException("URI not supported by CordovaResourceApi: " + uri);
|
||||
}
|
||||
|
||||
public OutputStream openOutputStream(Uri uri) throws IOException {
|
||||
return openOutputStream(uri, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a stream to the given URI.
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
*/
|
||||
public OutputStream openOutputStream(Uri uri, boolean append) throws IOException {
|
||||
assertBackgroundThread();
|
||||
switch (getUriType(uri)) {
|
||||
case URI_TYPE_FILE: {
|
||||
File localFile = new File(uri.getPath());
|
||||
File parent = localFile.getParentFile();
|
||||
if (parent != null) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
return new FileOutputStream(localFile, append);
|
||||
}
|
||||
case URI_TYPE_CONTENT:
|
||||
case URI_TYPE_RESOURCE: {
|
||||
AssetFileDescriptor assetFd = contentResolver.openAssetFileDescriptor(uri, append ? "wa" : "w");
|
||||
return assetFd.createOutputStream();
|
||||
}
|
||||
}
|
||||
throw new FileNotFoundException("URI not supported by CordovaResourceApi: " + uri);
|
||||
}
|
||||
|
||||
public HttpURLConnection createHttpConnection(Uri uri) throws IOException {
|
||||
assertBackgroundThread();
|
||||
return httpClient.open(new URL(uri.toString()));
|
||||
}
|
||||
|
||||
// Copies the input to the output in the most efficient manner possible.
|
||||
// Closes both streams.
|
||||
public void copyResource(OpenForReadResult input, OutputStream outputStream) throws IOException {
|
||||
assertBackgroundThread();
|
||||
try {
|
||||
InputStream inputStream = input.inputStream;
|
||||
if (inputStream instanceof FileInputStream && outputStream instanceof FileOutputStream) {
|
||||
FileChannel inChannel = ((FileInputStream)input.inputStream).getChannel();
|
||||
FileChannel outChannel = ((FileOutputStream)outputStream).getChannel();
|
||||
long offset = 0;
|
||||
long length = input.length;
|
||||
if (input.assetFd != null) {
|
||||
offset = input.assetFd.getStartOffset();
|
||||
}
|
||||
outChannel.transferFrom(inChannel, offset, length);
|
||||
} else {
|
||||
final int BUFFER_SIZE = 8192;
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
for (;;) {
|
||||
int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE);
|
||||
|
||||
if (bytesRead <= 0) {
|
||||
break;
|
||||
}
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
input.inputStream.close();
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void copyResource(Uri sourceUri, OutputStream outputStream) throws IOException {
|
||||
copyResource(openForRead(sourceUri), outputStream);
|
||||
}
|
||||
|
||||
|
||||
private void assertBackgroundThread() {
|
||||
if (threadCheckingEnabled) {
|
||||
Thread curThread = Thread.currentThread();
|
||||
if (curThread == Looper.getMainLooper().getThread()) {
|
||||
throw new IllegalStateException("Do not perform IO operations on the UI thread. Use CordovaInterface.getThreadPool() instead.");
|
||||
}
|
||||
if (curThread == jsThread) {
|
||||
throw new IllegalStateException("Tried to perform an IO operation on the WebCore thread. Use CordovaInterface.getThreadPool() instead.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getDataUriMimeType(Uri uri) {
|
||||
String uriAsString = uri.getSchemeSpecificPart();
|
||||
int commaPos = uriAsString.indexOf(',');
|
||||
if (commaPos == -1) {
|
||||
return null;
|
||||
}
|
||||
String[] mimeParts = uriAsString.substring(0, commaPos).split(";");
|
||||
if (mimeParts.length > 0) {
|
||||
return mimeParts[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private OpenForReadResult readDataUri(Uri uri) {
|
||||
String uriAsString = uri.getSchemeSpecificPart();
|
||||
int commaPos = uriAsString.indexOf(',');
|
||||
if (commaPos == -1) {
|
||||
return null;
|
||||
}
|
||||
String[] mimeParts = uriAsString.substring(0, commaPos).split(";");
|
||||
String contentType = null;
|
||||
boolean base64 = false;
|
||||
if (mimeParts.length > 0) {
|
||||
contentType = mimeParts[0];
|
||||
}
|
||||
for (int i = 1; i < mimeParts.length; ++i) {
|
||||
if ("base64".equalsIgnoreCase(mimeParts[i])) {
|
||||
base64 = true;
|
||||
}
|
||||
}
|
||||
String dataPartAsString = uriAsString.substring(commaPos + 1);
|
||||
byte[] data = base64 ? Base64.decode(dataPartAsString, Base64.DEFAULT) : EncodingUtils.getBytes(dataPartAsString, "UTF-8");
|
||||
InputStream inputStream = new ByteArrayInputStream(data);
|
||||
return new OpenForReadResult(uri, inputStream, contentType, data.length, null);
|
||||
}
|
||||
|
||||
private static void assertNonRelative(Uri uri) {
|
||||
if (!uri.isAbsolute()) {
|
||||
throw new IllegalArgumentException("Relative URIs are not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public static final class OpenForReadResult {
|
||||
public final Uri uri;
|
||||
public final InputStream inputStream;
|
||||
public final String mimeType;
|
||||
public final long length;
|
||||
public final AssetFileDescriptor assetFd;
|
||||
|
||||
OpenForReadResult(Uri uri, InputStream inputStream, String mimeType, long length, AssetFileDescriptor assetFd) {
|
||||
this.uri = uri;
|
||||
this.inputStream = inputStream;
|
||||
this.mimeType = mimeType;
|
||||
this.length = length;
|
||||
this.assetFd = assetFd;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,14 +23,13 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.cordova.Config;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.PluginManager;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.apache.cordova.PluginManager;
|
||||
import org.apache.cordova.PluginResult;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
@@ -59,7 +58,10 @@ import android.widget.FrameLayout;
|
||||
|
||||
public class CordovaWebView extends WebView {
|
||||
|
||||
|
||||
public static final String TAG = "CordovaWebView";
|
||||
|
||||
public static final String CORDOVA_VERSION = "3.1.0-dev";
|
||||
|
||||
private ArrayList<Integer> keyDownCodes = new ArrayList<Integer>();
|
||||
private ArrayList<Integer> keyUpCodes = new ArrayList<Integer>();
|
||||
@@ -96,6 +98,8 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
private ActivityResult mResult = null;
|
||||
|
||||
private CordovaResourceApi resourceApi;
|
||||
|
||||
class ActivityResult {
|
||||
|
||||
int request;
|
||||
@@ -227,9 +231,10 @@ public class CordovaWebView extends WebView {
|
||||
private void setup() {
|
||||
this.setInitialScale(0);
|
||||
this.setVerticalScrollBarEnabled(false);
|
||||
this.requestFocusFromTouch();
|
||||
|
||||
// Enable JavaScript
|
||||
if (shouldRequestFocusOnInit()) {
|
||||
this.requestFocusFromTouch();
|
||||
}
|
||||
// Enable JavaScript
|
||||
WebSettings settings = this.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
@@ -305,10 +310,21 @@ public class CordovaWebView extends WebView {
|
||||
pluginManager = new PluginManager(this, this.cordova);
|
||||
jsMessageQueue = new NativeToJsMessageQueue(this, cordova);
|
||||
exposedJsApi = new ExposedJsApi(pluginManager, jsMessageQueue);
|
||||
resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
|
||||
exposeJsInterface();
|
||||
}
|
||||
|
||||
private void updateUserAgentString() {
|
||||
|
||||
/**
|
||||
* Override this method to decide wether or not you need to request the
|
||||
* focus when your application start
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean shouldRequestFocusOnInit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateUserAgentString() {
|
||||
this.getSettings().getUserAgentString();
|
||||
}
|
||||
|
||||
@@ -413,7 +429,7 @@ public class CordovaWebView extends WebView {
|
||||
// Create a timeout timer for loadUrl
|
||||
final CordovaWebView me = this;
|
||||
final int currentLoadUrlTimeout = me.loadUrlTimeout;
|
||||
final int loadUrlTimeoutValue = Integer.parseInt(this.getProperty("loadUrlTimeoutValue", "20000"));
|
||||
final int loadUrlTimeoutValue = Integer.parseInt(this.getProperty("LoadUrlTimeoutValue", "20000"));
|
||||
|
||||
// Timeout error method
|
||||
final Runnable loadError = new Runnable() {
|
||||
@@ -499,7 +515,7 @@ public class CordovaWebView extends WebView {
|
||||
* Send JavaScript statement back to JavaScript.
|
||||
* (This is a convenience method)
|
||||
*
|
||||
* @param message
|
||||
* @param statement
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
this.jsMessageQueue.addJavaScript(statement);
|
||||
@@ -509,7 +525,8 @@ public class CordovaWebView extends WebView {
|
||||
* Send a plugin result back to JavaScript.
|
||||
* (This is a convenience method)
|
||||
*
|
||||
* @param message
|
||||
* @param result
|
||||
* @param callbackId
|
||||
*/
|
||||
public void sendPluginResult(PluginResult result, String callbackId) {
|
||||
this.jsMessageQueue.addPluginResult(result, callbackId);
|
||||
@@ -608,7 +625,7 @@ public class CordovaWebView extends WebView {
|
||||
*/
|
||||
private void loadConfiguration() {
|
||||
|
||||
if ("true".equals(this.getProperty("fullscreen", "false"))) {
|
||||
if ("true".equals(this.getProperty("Fullscreen", "false"))) {
|
||||
this.cordova.getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
this.cordova.getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
@@ -626,6 +643,7 @@ public class CordovaWebView extends WebView {
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
Object p = bundle.get(name);
|
||||
if (p == null) {
|
||||
return defaultValue;
|
||||
@@ -805,7 +823,6 @@ public class CordovaWebView extends WebView {
|
||||
public void handleDestroy()
|
||||
{
|
||||
// Send destroy event to JavaScript
|
||||
// Since baseUrl is set in loadUrlIntoView, if user hit Back button before loadUrl was called, we'll get an NPE on baseUrl (CB-2458)
|
||||
this.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
|
||||
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
@@ -944,4 +961,8 @@ public class CordovaWebView extends WebView {
|
||||
public void storeResult(int requestCode, int resultCode, Intent intent) {
|
||||
mResult = new ActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
|
||||
public CordovaResourceApi getResourceApi() {
|
||||
return resourceApi;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ package org.apache.cordova;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -48,7 +48,7 @@ import android.webkit.WebViewClient;
|
||||
*/
|
||||
public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
private static final String TAG = "Cordova";
|
||||
private static final String TAG = "CordovaWebViewClient";
|
||||
private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/";
|
||||
CordovaInterface cordova;
|
||||
CordovaWebView appView;
|
||||
@@ -188,6 +188,17 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
LOG.e(TAG, "Error sending sms " + url + ":" + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
//Android Market
|
||||
else if(url.startsWith("market:")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error loading Google Play Store: " + url, e);
|
||||
}
|
||||
}
|
||||
|
||||
// All else
|
||||
else {
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
public class Device extends CordovaPlugin {
|
||||
public static final String TAG = "Device";
|
||||
|
||||
public static String cordovaVersion = "dev"; // Cordova version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
BroadcastReceiver telephonyReceiver = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public Device() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The CordovaWebView Cordova is running in.
|
||||
*/
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
Device.uuid = getUuid();
|
||||
this.initTelephonyReceiver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false if not.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("getDeviceInfo")) {
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("uuid", Device.uuid);
|
||||
r.put("version", this.getOSVersion());
|
||||
r.put("platform", Device.platform);
|
||||
r.put("cordova", Device.cordovaVersion);
|
||||
r.put("model", this.getModel());
|
||||
callbackContext.success(r);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister receiver.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
this.cordova.getActivity().unregisterReceiver(this.telephonyReceiver);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Listen for telephony events: RINGING, OFFHOOK and IDLE
|
||||
* Send these events to all plugins using
|
||||
* CordovaActivity.onMessage("telephone", "ringing" | "offhook" | "idle")
|
||||
*/
|
||||
private void initTelephonyReceiver() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
|
||||
//final CordovaInterface mycordova = this.cordova;
|
||||
this.telephonyReceiver = new BroadcastReceiver() {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
// If state has changed
|
||||
if ((intent != null) && intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
|
||||
if (intent.hasExtra(TelephonyManager.EXTRA_STATE)) {
|
||||
String extraData = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
|
||||
if (extraData.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
|
||||
LOG.i(TAG, "Telephone RINGING");
|
||||
webView.postMessage("telephone", "ringing");
|
||||
}
|
||||
else if (extraData.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
|
||||
LOG.i(TAG, "Telephone OFFHOOK");
|
||||
webView.postMessage("telephone", "offhook");
|
||||
}
|
||||
else if (extraData.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
|
||||
LOG.i(TAG, "Telephone IDLE");
|
||||
webView.postMessage("telephone", "idle");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Register the receiver
|
||||
this.cordova.getActivity().registerReceiver(this.telephonyReceiver, intentFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OS name.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPlatform() {
|
||||
return Device.platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the device's Universally Unique Identifier (UUID).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getUuid() {
|
||||
String uuid = Settings.Secure.getString(this.cordova.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Cordova version.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getCordovaVersion() {
|
||||
return Device.cordovaVersion;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
String model = android.os.Build.MODEL;
|
||||
return model;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
String productname = android.os.Build.PRODUCT;
|
||||
return productname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OS version.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getOSVersion() {
|
||||
String osversion = android.os.Build.VERSION.RELEASE;
|
||||
return osversion;
|
||||
}
|
||||
|
||||
public String getSDKVersion() {
|
||||
@SuppressWarnings("deprecation")
|
||||
String sdkversion = android.os.Build.VERSION.SDK;
|
||||
return sdkversion;
|
||||
}
|
||||
|
||||
public String getTimeZoneID() {
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
return (tz.getID());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
|
||||
/**
|
||||
* This class provides file directory utilities.
|
||||
* All file operations are performed on the SD card.
|
||||
*
|
||||
* It is used by the FileUtils class.
|
||||
*/
|
||||
public class DirectoryManager {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String LOG_TAG = "DirectoryManager";
|
||||
|
||||
/**
|
||||
* Determine if a file or directory exists.
|
||||
* @param name The name of the file to check.
|
||||
* @return T=exists, F=not found
|
||||
*/
|
||||
protected static boolean testFileExists(String name) {
|
||||
boolean status;
|
||||
|
||||
// If SD card exists
|
||||
if ((testSaveLocationExists()) && (!name.equals(""))) {
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
File newPath = constructFilePaths(path.toString(), name);
|
||||
status = newPath.exists();
|
||||
}
|
||||
// If no SD card
|
||||
else {
|
||||
status = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the free disk space
|
||||
*
|
||||
* @return Size in KB or -1 if not available
|
||||
*/
|
||||
protected static long getFreeDiskSpace(boolean checkInternal) {
|
||||
String status = Environment.getExternalStorageState();
|
||||
long freeSpace = 0;
|
||||
|
||||
// If SD card exists
|
||||
if (status.equals(Environment.MEDIA_MOUNTED)) {
|
||||
freeSpace = freeSpaceCalculation(Environment.getExternalStorageDirectory().getPath());
|
||||
}
|
||||
else if (checkInternal) {
|
||||
freeSpace = freeSpaceCalculation("/");
|
||||
}
|
||||
// If no SD card and we haven't been asked to check the internal directory then return -1
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return freeSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a path return the number of free KB
|
||||
*
|
||||
* @param path to the file system
|
||||
* @return free space in KB
|
||||
*/
|
||||
private static long freeSpaceCalculation(String path) {
|
||||
StatFs stat = new StatFs(path);
|
||||
long blockSize = stat.getBlockSize();
|
||||
long availableBlocks = stat.getAvailableBlocks();
|
||||
return availableBlocks * blockSize / 1024;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if SD card exists.
|
||||
*
|
||||
* @return T=exists, F=not found
|
||||
*/
|
||||
protected static boolean testSaveLocationExists() {
|
||||
String sDCardStatus = Environment.getExternalStorageState();
|
||||
boolean status;
|
||||
|
||||
// If SD card is mounted
|
||||
if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)) {
|
||||
status = true;
|
||||
}
|
||||
|
||||
// If no SD card
|
||||
else {
|
||||
status = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new file object from two file paths.
|
||||
*
|
||||
* @param file1 Base file path
|
||||
* @param file2 Remaining file path
|
||||
* @return File object
|
||||
*/
|
||||
private static File constructFilePaths (String file1, String file2) {
|
||||
File newPath;
|
||||
if (file2.startsWith(file1)) {
|
||||
newPath = new File(file2);
|
||||
}
|
||||
else {
|
||||
newPath = new File(file1 + "/" + file2);
|
||||
}
|
||||
return newPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we can use the SD Card to store the temporary file. If not then use
|
||||
* the internal cache directory.
|
||||
*
|
||||
* @return the absolute path of where to store the file
|
||||
*/
|
||||
protected static String getTempDirectoryPath(Context ctx) {
|
||||
File cache = null;
|
||||
|
||||
// SD Card Mounted
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
||||
"/Android/data/" + ctx.getPackageName() + "/cache/");
|
||||
}
|
||||
// Use internal storage
|
||||
else {
|
||||
cache = ctx.getCacheDir();
|
||||
}
|
||||
|
||||
// Create the cache directory if it doesn't exist
|
||||
if (!cache.exists()) {
|
||||
cache.mkdirs();
|
||||
}
|
||||
|
||||
return cache.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class Echo extends CordovaPlugin {
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
|
||||
if ("echo".equals(action)) {
|
||||
final String result = args.isNull(0) ? null : args.getString(0);
|
||||
callbackContext.success(result);
|
||||
return true;
|
||||
} else if ("echoAsync".equals(action)) {
|
||||
final String result = args.isNull(0) ? null : args.getString(0);
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
callbackContext.success(result);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else if ("echoArrayBuffer".equals(action)) {
|
||||
final byte[] result = args.getArrayBuffer(0);
|
||||
callbackContext.success(result);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import android.media.ExifInterface;
|
||||
|
||||
public class ExifHelper {
|
||||
private String aperture = null;
|
||||
private String datetime = null;
|
||||
private String exposureTime = null;
|
||||
private String flash = null;
|
||||
private String focalLength = null;
|
||||
private String gpsAltitude = null;
|
||||
private String gpsAltitudeRef = null;
|
||||
private String gpsDateStamp = null;
|
||||
private String gpsLatitude = null;
|
||||
private String gpsLatitudeRef = null;
|
||||
private String gpsLongitude = null;
|
||||
private String gpsLongitudeRef = null;
|
||||
private String gpsProcessingMethod = null;
|
||||
private String gpsTimestamp = null;
|
||||
private String iso = null;
|
||||
private String make = null;
|
||||
private String model = null;
|
||||
private String orientation = null;
|
||||
private String whiteBalance = null;
|
||||
|
||||
private ExifInterface inFile = null;
|
||||
private ExifInterface outFile = null;
|
||||
|
||||
/**
|
||||
* The file before it is compressed
|
||||
*
|
||||
* @param filePath
|
||||
* @throws IOException
|
||||
*/
|
||||
public void createInFile(String filePath) throws IOException {
|
||||
this.inFile = new ExifInterface(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* The file after it has been compressed
|
||||
*
|
||||
* @param filePath
|
||||
* @throws IOException
|
||||
*/
|
||||
public void createOutFile(String filePath) throws IOException {
|
||||
this.outFile = new ExifInterface(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all the EXIF data from the input file.
|
||||
*/
|
||||
public void readExifData() {
|
||||
this.aperture = inFile.getAttribute(ExifInterface.TAG_APERTURE);
|
||||
this.datetime = inFile.getAttribute(ExifInterface.TAG_DATETIME);
|
||||
this.exposureTime = inFile.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
|
||||
this.flash = inFile.getAttribute(ExifInterface.TAG_FLASH);
|
||||
this.focalLength = inFile.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
|
||||
this.gpsAltitude = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE);
|
||||
this.gpsAltitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF);
|
||||
this.gpsDateStamp = inFile.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
|
||||
this.gpsLatitude = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
|
||||
this.gpsLatitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
|
||||
this.gpsLongitude = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
|
||||
this.gpsLongitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
|
||||
this.gpsProcessingMethod = inFile.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD);
|
||||
this.gpsTimestamp = inFile.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
|
||||
this.iso = inFile.getAttribute(ExifInterface.TAG_ISO);
|
||||
this.make = inFile.getAttribute(ExifInterface.TAG_MAKE);
|
||||
this.model = inFile.getAttribute(ExifInterface.TAG_MODEL);
|
||||
this.orientation = inFile.getAttribute(ExifInterface.TAG_ORIENTATION);
|
||||
this.whiteBalance = inFile.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the previously stored EXIF data to the output file.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writeExifData() throws IOException {
|
||||
// Don't try to write to a null file
|
||||
if (this.outFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.aperture != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperture);
|
||||
}
|
||||
if (this.datetime != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_DATETIME, this.datetime);
|
||||
}
|
||||
if (this.exposureTime != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_EXPOSURE_TIME, this.exposureTime);
|
||||
}
|
||||
if (this.flash != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_FLASH, this.flash);
|
||||
}
|
||||
if (this.focalLength != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_FOCAL_LENGTH, this.focalLength);
|
||||
}
|
||||
if (this.gpsAltitude != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, this.gpsAltitude);
|
||||
}
|
||||
if (this.gpsAltitudeRef != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, this.gpsAltitudeRef);
|
||||
}
|
||||
if (this.gpsDateStamp != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_GPS_DATESTAMP, this.gpsDateStamp);
|
||||
}
|
||||
if (this.gpsLatitude != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE, this.gpsLatitude);
|
||||
}
|
||||
if (this.gpsLatitudeRef != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, this.gpsLatitudeRef);
|
||||
}
|
||||
if (this.gpsLongitude != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, this.gpsLongitude);
|
||||
}
|
||||
if (this.gpsLongitudeRef != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, this.gpsLongitudeRef);
|
||||
}
|
||||
if (this.gpsProcessingMethod != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD, this.gpsProcessingMethod);
|
||||
}
|
||||
if (this.gpsTimestamp != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, this.gpsTimestamp);
|
||||
}
|
||||
if (this.iso != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_ISO, this.iso);
|
||||
}
|
||||
if (this.make != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_MAKE, this.make);
|
||||
}
|
||||
if (this.model != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_MODEL, this.model);
|
||||
}
|
||||
if (this.orientation != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_ORIENTATION, this.orientation);
|
||||
}
|
||||
if (this.whiteBalance != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_WHITE_BALANCE, this.whiteBalance);
|
||||
}
|
||||
|
||||
this.outFile.saveAttributes();
|
||||
}
|
||||
|
||||
public int getOrientation() {
|
||||
int o = Integer.parseInt(this.orientation);
|
||||
|
||||
if (o == ExifInterface.ORIENTATION_NORMAL) {
|
||||
return 0;
|
||||
} else if (o == ExifInterface.ORIENTATION_ROTATE_90) {
|
||||
return 90;
|
||||
} else if (o == ExifInterface.ORIENTATION_ROTATE_180) {
|
||||
return 180;
|
||||
} else if (o == ExifInterface.ORIENTATION_ROTATE_270) {
|
||||
return 270;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void resetOrientation() {
|
||||
this.orientation = "" + ExifInterface.ORIENTATION_NORMAL;
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,7 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.webkit.JavascriptInterface;
|
||||
import org.apache.cordova.api.PluginManager;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.apache.cordova.PluginManager;
|
||||
import org.json.JSONException;
|
||||
|
||||
/**
|
||||
@@ -48,12 +47,18 @@ import org.json.JSONException;
|
||||
|
||||
jsMessageQueue.setPaused(true);
|
||||
try {
|
||||
boolean wasSync = pluginManager.exec(service, action, callbackId, arguments);
|
||||
// Tell the resourceApi what thread the JS is running on.
|
||||
CordovaResourceApi.jsThread = Thread.currentThread();
|
||||
|
||||
pluginManager.exec(service, action, callbackId, arguments);
|
||||
String ret = "";
|
||||
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING || wasSync) {
|
||||
ret = jsMessageQueue.popAndEncode();
|
||||
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) {
|
||||
ret = jsMessageQueue.popAndEncode(false);
|
||||
}
|
||||
return ret;
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
} finally {
|
||||
jsMessageQueue.setPaused(false);
|
||||
}
|
||||
@@ -65,7 +70,7 @@ import org.json.JSONException;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String retrieveJsMessages() {
|
||||
return jsMessageQueue.popAndEncode();
|
||||
public String retrieveJsMessages(boolean fromOnlineEvent) {
|
||||
return jsMessageQueue.popAndEncode(fromOnlineEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.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;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Locale;
|
||||
|
||||
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://")) {
|
||||
int question = uriString.indexOf("?");
|
||||
if (question > -1) {
|
||||
uriString = uriString.substring(0,question);
|
||||
}
|
||||
if (uriString.startsWith("file:///android_asset/")) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
String relativePath = uri.getPath().substring(15);
|
||||
return cordova.getActivity().getAssets().open(relativePath);
|
||||
} else {
|
||||
return new FileInputStream(getRealPath(uriString, cordova));
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
|
||||
Uri uri = Uri.parse(uriString);
|
||||
if (uriString.startsWith("content://")) {
|
||||
mimeType = cordova.getActivity().getContentResolver().getType(uri);
|
||||
} else {
|
||||
// MimeTypeMap.getFileExtensionFromUrl() fails when there are query parameters.
|
||||
String extension = uri.getPath();
|
||||
int lastDot = extension.lastIndexOf('.');
|
||||
if (lastDot != -1) {
|
||||
extension = extension.substring(lastDot + 1);
|
||||
}
|
||||
// Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185).
|
||||
extension = extension.toLowerCase();
|
||||
if (extension.equals("3ga")) {
|
||||
mimeType = "audio/3gpp";
|
||||
} else {
|
||||
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
}
|
||||
}
|
||||
|
||||
return mimeType;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Encapsulates in-progress status of uploading or downloading a file to a remote server.
|
||||
*/
|
||||
public class FileProgressResult {
|
||||
|
||||
private boolean lengthComputable = false; // declares whether total is known
|
||||
private long loaded = 0; // bytes sent so far
|
||||
private long total = 0; // bytes total, if known
|
||||
|
||||
public boolean getLengthComputable() {
|
||||
return lengthComputable;
|
||||
}
|
||||
|
||||
public void setLengthComputable(boolean computable) {
|
||||
this.lengthComputable = computable;
|
||||
}
|
||||
|
||||
public long getLoaded() {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public void setLoaded(long bytes) {
|
||||
this.loaded = bytes;
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(long bytes) {
|
||||
this.total = bytes;
|
||||
}
|
||||
|
||||
public JSONObject toJSONObject() throws JSONException {
|
||||
return new JSONObject(
|
||||
"{loaded:" + loaded +
|
||||
",total:" + total +
|
||||
",lengthComputable:" + (lengthComputable ? "true" : "false") + "}");
|
||||
}
|
||||
}
|
||||
@@ -1,923 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLDecoder;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.webkit.CookieManager;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
|
||||
public class FileTransfer extends CordovaPlugin {
|
||||
|
||||
private static final String LOG_TAG = "FileTransfer";
|
||||
private static final String LINE_START = "--";
|
||||
private static final String LINE_END = "\r\n";
|
||||
private static final String BOUNDARY = "+++++";
|
||||
|
||||
public static int FILE_NOT_FOUND_ERR = 1;
|
||||
public static int INVALID_URL_ERR = 2;
|
||||
public static int CONNECTION_ERR = 3;
|
||||
public static int ABORTED_ERR = 4;
|
||||
|
||||
private static HashMap<String, RequestContext> activeRequests = new HashMap<String, RequestContext>();
|
||||
private static final int MAX_BUFFER_SIZE = 16 * 1024;
|
||||
|
||||
private static OkHttpClient httpClient = new OkHttpClient();
|
||||
|
||||
private static final class RequestContext {
|
||||
String source;
|
||||
String target;
|
||||
File targetFile;
|
||||
CallbackContext callbackContext;
|
||||
InputStream currentInputStream;
|
||||
OutputStream currentOutputStream;
|
||||
boolean aborted;
|
||||
RequestContext(String source, String target, CallbackContext callbackContext) {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
void sendPluginResult(PluginResult pluginResult) {
|
||||
synchronized (this) {
|
||||
if (!aborted) {
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an interface method to an InputStream to return the number of bytes
|
||||
* read from the raw stream. This is used to track total progress against
|
||||
* the HTTP Content-Length header value from the server.
|
||||
*/
|
||||
private static abstract class TrackingInputStream extends FilterInputStream {
|
||||
public TrackingInputStream(final InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
public abstract long getTotalRawBytesRead();
|
||||
}
|
||||
|
||||
private static class ExposedGZIPInputStream extends GZIPInputStream {
|
||||
public ExposedGZIPInputStream(final InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
public Inflater getInflater() {
|
||||
return inf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides raw bytes-read tracking for a GZIP input stream. Reports the
|
||||
* total number of compressed bytes read from the input, rather than the
|
||||
* number of uncompressed bytes.
|
||||
*/
|
||||
private static class TrackingGZIPInputStream extends TrackingInputStream {
|
||||
private ExposedGZIPInputStream gzin;
|
||||
public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException {
|
||||
super(gzin);
|
||||
this.gzin = gzin;
|
||||
}
|
||||
public long getTotalRawBytesRead() {
|
||||
return gzin.getInflater().getBytesRead();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides simple total-bytes-read tracking for an existing InputStream
|
||||
*/
|
||||
private static class TrackingHTTPInputStream extends TrackingInputStream {
|
||||
private long bytesRead = 0;
|
||||
public TrackingHTTPInputStream(InputStream stream) {
|
||||
super(stream);
|
||||
}
|
||||
|
||||
private int updateBytesRead(int newBytesRead) {
|
||||
if (newBytesRead != -1) {
|
||||
bytesRead += newBytesRead;
|
||||
}
|
||||
return newBytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return updateBytesRead(super.read());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer) throws IOException {
|
||||
return updateBytesRead(super.read(buffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] bytes, int offset, int count) throws IOException {
|
||||
return updateBytesRead(super.read(bytes, offset, count));
|
||||
}
|
||||
|
||||
public long getTotalRawBytesRead() {
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("upload") || action.equals("download")) {
|
||||
String source = args.getString(0);
|
||||
String target = args.getString(1);
|
||||
|
||||
if (action.equals("upload")) {
|
||||
try {
|
||||
source = URLDecoder.decode(source, "UTF-8");
|
||||
upload(source, target, args, callbackContext);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.MALFORMED_URL_EXCEPTION, "UTF-8 error."));
|
||||
}
|
||||
} else {
|
||||
download(source, target, args, callbackContext);
|
||||
}
|
||||
return true;
|
||||
} else if (action.equals("abort")) {
|
||||
String objectId = args.getString(0);
|
||||
abort(objectId);
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
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
|
||||
* @param target URL of the server to receive the file
|
||||
* @param args JSON Array of args
|
||||
* @param callbackContext callback id for optional progress reports
|
||||
*
|
||||
* args[2] fileKey Name of file request parameter
|
||||
* args[3] fileName File name to be used on server
|
||||
* args[4] mimeType Describes file content type
|
||||
* args[5] params key:value pairs of user-defined parameters
|
||||
* @return FileUploadResult containing result of upload request
|
||||
*/
|
||||
private void upload(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
Log.d(LOG_TAG, "upload " + source + " to " + target);
|
||||
|
||||
// Setup the options
|
||||
final String fileKey = getArgument(args, 2, "file");
|
||||
final String fileName = getArgument(args, 3, "image.jpg");
|
||||
final String mimeType = getArgument(args, 4, "image/jpeg");
|
||||
final JSONObject params = args.optJSONObject(5) == null ? new JSONObject() : args.optJSONObject(5);
|
||||
final boolean trustEveryone = args.optBoolean(6);
|
||||
// Always use chunked mode unless set to false as per API
|
||||
final boolean chunkedMode = args.optBoolean(7) || args.isNull(7);
|
||||
// Look for headers on the params map for backwards compatibility with older Cordova versions.
|
||||
final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers") : args.optJSONObject(8);
|
||||
final String objectId = args.getString(9);
|
||||
final String httpMethod = getArgument(args, 10, "POST");
|
||||
|
||||
Log.d(LOG_TAG, "fileKey: " + fileKey);
|
||||
Log.d(LOG_TAG, "fileName: " + fileName);
|
||||
Log.d(LOG_TAG, "mimeType: " + mimeType);
|
||||
Log.d(LOG_TAG, "params: " + params);
|
||||
Log.d(LOG_TAG, "trustEveryone: " + trustEveryone);
|
||||
Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
|
||||
Log.d(LOG_TAG, "headers: " + headers);
|
||||
Log.d(LOG_TAG, "objectId: " + objectId);
|
||||
Log.d(LOG_TAG, "httpMethod: " + httpMethod);
|
||||
|
||||
final URL url;
|
||||
try {
|
||||
url = new URL(target);
|
||||
} catch (MalformedURLException e) {
|
||||
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
return;
|
||||
}
|
||||
final boolean useHttps = url.getProtocol().equals("https");
|
||||
|
||||
final RequestContext context = new RequestContext(source, target, callbackContext);
|
||||
synchronized (activeRequests) {
|
||||
activeRequests.put(objectId, context);
|
||||
}
|
||||
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
if (context.aborted) {
|
||||
return;
|
||||
}
|
||||
HttpURLConnection conn = null;
|
||||
HostnameVerifier oldHostnameVerifier = null;
|
||||
SSLSocketFactory oldSocketFactory = null;
|
||||
int totalBytes = 0;
|
||||
int fixedLength = -1;
|
||||
try {
|
||||
// Create return object
|
||||
FileUploadResult result = new FileUploadResult();
|
||||
FileProgressResult progress = new FileProgressResult();
|
||||
|
||||
//------------------ CLIENT REQUEST
|
||||
// Open a HTTP connection to the URL based on protocol
|
||||
if (useHttps) {
|
||||
// Using standard HTTPS connection. Will not allow self signed certificate
|
||||
if (!trustEveryone) {
|
||||
conn = (HttpsURLConnection) httpClient.open(url);
|
||||
}
|
||||
// Use our HTTPS connection that blindly trusts everyone.
|
||||
// This should only be used in debug environments
|
||||
else {
|
||||
// Setup the HTTPS connection class to trust everyone
|
||||
HttpsURLConnection https = (HttpsURLConnection) httpClient.open(url);
|
||||
oldSocketFactory = trustAllHosts(https);
|
||||
// Save the current hostnameVerifier
|
||||
oldHostnameVerifier = https.getHostnameVerifier();
|
||||
// Setup the connection not to verify hostnames
|
||||
https.setHostnameVerifier(DO_NOT_VERIFY);
|
||||
conn = https;
|
||||
}
|
||||
}
|
||||
// Return a standard HTTP connection
|
||||
else {
|
||||
conn = httpClient.open(url);
|
||||
}
|
||||
|
||||
// Allow Inputs
|
||||
conn.setDoInput(true);
|
||||
|
||||
// Allow Outputs
|
||||
conn.setDoOutput(true);
|
||||
|
||||
// Don't use a cached copy.
|
||||
conn.setUseCaches(false);
|
||||
|
||||
// Use a post method.
|
||||
conn.setRequestMethod(httpMethod);
|
||||
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDARY);
|
||||
|
||||
// Set the cookies on the response
|
||||
String cookie = CookieManager.getInstance().getCookie(target);
|
||||
if (cookie != null) {
|
||||
conn.setRequestProperty("Cookie", cookie);
|
||||
}
|
||||
|
||||
// Handle the other headers
|
||||
if (headers != null) {
|
||||
addHeadersToRequest(conn, headers);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the non-file portions of the multipart data as a string, so that we can add it
|
||||
* to the contentSize, since it is part of the body of the HTTP request.
|
||||
*/
|
||||
StringBuilder beforeData = new StringBuilder();
|
||||
try {
|
||||
for (Iterator<?> iter = params.keys(); iter.hasNext();) {
|
||||
Object key = iter.next();
|
||||
if(!String.valueOf(key).equals("headers"))
|
||||
{
|
||||
beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);
|
||||
beforeData.append("Content-Disposition: form-data; name=\"").append(key.toString()).append('"');
|
||||
beforeData.append(LINE_END).append(LINE_END);
|
||||
beforeData.append(params.getString(key.toString()));
|
||||
beforeData.append(LINE_END);
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);
|
||||
beforeData.append("Content-Disposition: form-data; name=\"").append(fileKey).append("\";");
|
||||
beforeData.append(" filename=\"").append(fileName).append('"').append(LINE_END);
|
||||
beforeData.append("Content-Type: ").append(mimeType).append(LINE_END).append(LINE_END);
|
||||
byte[] beforeDataBytes = beforeData.toString().getBytes("UTF-8");
|
||||
byte[] tailParamsBytes = (LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END).getBytes("UTF-8");
|
||||
|
||||
|
||||
// Get a input stream of the file on the phone
|
||||
InputStream sourceInputStream = getPathFromUri(source);
|
||||
|
||||
int stringLength = beforeDataBytes.length + tailParamsBytes.length;
|
||||
if (sourceInputStream instanceof FileInputStream) {
|
||||
fixedLength = (int) ((FileInputStream)sourceInputStream).getChannel().size() + stringLength;
|
||||
progress.setLengthComputable(true);
|
||||
progress.setTotal(fixedLength);
|
||||
}
|
||||
Log.d(LOG_TAG, "Content Length: " + fixedLength);
|
||||
// setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices.
|
||||
// http://code.google.com/p/android/issues/detail?id=3164
|
||||
// It also causes OOM if HTTPS is used, even on newer devices.
|
||||
boolean useChunkedMode = chunkedMode && (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps);
|
||||
useChunkedMode = useChunkedMode || (fixedLength == -1);
|
||||
|
||||
if (useChunkedMode) {
|
||||
conn.setChunkedStreamingMode(MAX_BUFFER_SIZE);
|
||||
// Although setChunkedStreamingMode sets this header, setting it explicitly here works
|
||||
// around an OutOfMemoryException when using https.
|
||||
conn.setRequestProperty("Transfer-Encoding", "chunked");
|
||||
} else {
|
||||
conn.setFixedLengthStreamingMode(fixedLength);
|
||||
}
|
||||
|
||||
conn.connect();
|
||||
|
||||
OutputStream sendStream = null;
|
||||
try {
|
||||
sendStream = conn.getOutputStream();
|
||||
synchronized (context) {
|
||||
if (context.aborted) {
|
||||
return;
|
||||
}
|
||||
context.currentOutputStream = sendStream;
|
||||
}
|
||||
//We don't want to change encoding, we just want this to write for all Unicode.
|
||||
sendStream.write(beforeDataBytes);
|
||||
totalBytes += beforeDataBytes.length;
|
||||
|
||||
// create a buffer of maximum size
|
||||
int bytesAvailable = sourceInputStream.available();
|
||||
int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
|
||||
// read file and write it into form...
|
||||
int bytesRead = sourceInputStream.read(buffer, 0, bufferSize);
|
||||
|
||||
long prevBytesRead = 0;
|
||||
while (bytesRead > 0) {
|
||||
result.setBytesSent(totalBytes);
|
||||
sendStream.write(buffer, 0, bytesRead);
|
||||
totalBytes += bytesRead;
|
||||
if (totalBytes > prevBytesRead + 102400) {
|
||||
prevBytesRead = totalBytes;
|
||||
Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes");
|
||||
}
|
||||
bytesAvailable = sourceInputStream.available();
|
||||
bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
|
||||
bytesRead = sourceInputStream.read(buffer, 0, bufferSize);
|
||||
|
||||
// Send a progress event.
|
||||
progress.setLoaded(totalBytes);
|
||||
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
|
||||
progressResult.setKeepCallback(true);
|
||||
context.sendPluginResult(progressResult);
|
||||
}
|
||||
|
||||
// send multipart form data necessary after file data...
|
||||
sendStream.write(tailParamsBytes);
|
||||
totalBytes += tailParamsBytes.length;
|
||||
sendStream.flush();
|
||||
} finally {
|
||||
safeClose(sourceInputStream);
|
||||
safeClose(sendStream);
|
||||
}
|
||||
context.currentOutputStream = null;
|
||||
Log.d(LOG_TAG, "Sent " + totalBytes + " of " + fixedLength);
|
||||
|
||||
//------------------ read the SERVER RESPONSE
|
||||
String responseString;
|
||||
int responseCode = conn.getResponseCode();
|
||||
Log.d(LOG_TAG, "response code: " + responseCode);
|
||||
Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields());
|
||||
TrackingInputStream inStream = null;
|
||||
try {
|
||||
inStream = getInputStream(conn);
|
||||
synchronized (context) {
|
||||
if (context.aborted) {
|
||||
return;
|
||||
}
|
||||
context.currentInputStream = inStream;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(1024, conn.getContentLength()));
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead = 0;
|
||||
// write bytes to file
|
||||
while ((bytesRead = inStream.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
responseString = out.toString("UTF-8");
|
||||
} finally {
|
||||
context.currentInputStream = null;
|
||||
safeClose(inStream);
|
||||
}
|
||||
|
||||
Log.d(LOG_TAG, "got response from server");
|
||||
Log.d(LOG_TAG, responseString.substring(0, Math.min(256, responseString.length())));
|
||||
|
||||
// send request and retrieve response
|
||||
result.setResponseCode(responseCode);
|
||||
result.setResponse(responseString);
|
||||
|
||||
context.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject()));
|
||||
} catch (FileNotFoundException e) {
|
||||
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, conn);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
} catch (IOException e) {
|
||||
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
Log.e(LOG_TAG, "Failed after uploading " + totalBytes + " of " + fixedLength + " bytes.");
|
||||
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
context.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
|
||||
} catch (Throwable t) {
|
||||
// Shouldn't happen, but will
|
||||
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
|
||||
Log.e(LOG_TAG, error.toString(), t);
|
||||
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
} finally {
|
||||
synchronized (activeRequests) {
|
||||
activeRequests.remove(objectId);
|
||||
}
|
||||
|
||||
if (conn != null) {
|
||||
// Revert back to the proper verifier and socket factories
|
||||
// Revert back to the proper verifier and socket factories
|
||||
if (trustEveryone && useHttps) {
|
||||
HttpsURLConnection https = (HttpsURLConnection) conn;
|
||||
https.setHostnameVerifier(oldHostnameVerifier);
|
||||
https.setSSLSocketFactory(oldSocketFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void safeClose(Closeable stream) {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static TrackingInputStream getInputStream(URLConnection conn) throws IOException {
|
||||
String encoding = conn.getContentEncoding();
|
||||
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
|
||||
return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
|
||||
}
|
||||
return new TrackingHTTPInputStream(conn.getInputStream());
|
||||
}
|
||||
|
||||
// always verify the host - don't check for certificate
|
||||
private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
private static final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
|
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
return new java.security.cert.X509Certificate[] {};
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
} };
|
||||
|
||||
/**
|
||||
* This function will install a trust manager that will blindly trust all SSL
|
||||
* certificates. The reason this code is being added is to enable developers
|
||||
* to do development using self signed SSL certificates on their web server.
|
||||
*
|
||||
* The standard HttpsURLConnection class will throw an exception on self
|
||||
* signed certificates if this code is not run.
|
||||
*/
|
||||
private static SSLSocketFactory trustAllHosts(HttpsURLConnection connection) {
|
||||
// Install the all-trusting trust manager
|
||||
SSLSocketFactory oldFactory = connection.getSSLSocketFactory();
|
||||
try {
|
||||
// Install our all trusting manager
|
||||
SSLContext sc = SSLContext.getInstance("TLS");
|
||||
sc.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||
SSLSocketFactory newFactory = sc.getSocketFactory();
|
||||
connection.setSSLSocketFactory(newFactory);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return oldFactory;
|
||||
}
|
||||
|
||||
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, body, httpStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an error object based on the passed in errorCode
|
||||
* @param errorCode the error
|
||||
* @return JSONObject containing the error
|
||||
*/
|
||||
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);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to read a parameter from the list of JSON args.
|
||||
* @param args the args passed to the Plugin
|
||||
* @param position the position to retrieve the arg from
|
||||
* @param defaultString the default to be used if the arg does not exist
|
||||
* @return String with the retrieved value
|
||||
*/
|
||||
private static String getArgument(JSONArray args, int position, String defaultString) {
|
||||
String arg = defaultString;
|
||||
if (args.length() > position) {
|
||||
arg = args.optString(position);
|
||||
if (arg == null || "null".equals(arg)) {
|
||||
arg = defaultString;
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a file form a given URL and saves it to the specified directory.
|
||||
*
|
||||
* @param source URL of the server to receive the file
|
||||
* @param target Full path of the file on the file system
|
||||
*/
|
||||
private void download(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
Log.d(LOG_TAG, "download " + source + " to " + target);
|
||||
|
||||
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, null, 0);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
return;
|
||||
}
|
||||
final boolean useHttps = url.getProtocol().equals("https");
|
||||
|
||||
if (!Config.isUrlWhiteListed(source)) {
|
||||
Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'");
|
||||
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, null, 401);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final RequestContext context = new RequestContext(source, target, callbackContext);
|
||||
synchronized (activeRequests) {
|
||||
activeRequests.put(objectId, context);
|
||||
}
|
||||
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
if (context.aborted) {
|
||||
return;
|
||||
}
|
||||
URLConnection connection = null;
|
||||
HostnameVerifier oldHostnameVerifier = null;
|
||||
SSLSocketFactory oldSocketFactory = null;
|
||||
File file = null;
|
||||
PluginResult result = null;
|
||||
|
||||
try {
|
||||
file = getFileFromPath(target);
|
||||
context.targetFile = file;
|
||||
// create needed directories
|
||||
file.getParentFile().mkdirs();
|
||||
|
||||
// connect to server
|
||||
// Open a HTTP connection to the URL based on protocol
|
||||
if (useHttps) {
|
||||
// Using standard HTTPS connection. Will not allow self signed certificate
|
||||
if (!trustEveryone) {
|
||||
connection = (HttpsURLConnection) httpClient.open(url);
|
||||
}
|
||||
// Use our HTTPS connection that blindly trusts everyone.
|
||||
// This should only be used in debug environments
|
||||
else {
|
||||
// Setup the HTTPS connection class to trust everyone
|
||||
HttpsURLConnection https = (HttpsURLConnection) httpClient.open(url);
|
||||
oldSocketFactory = trustAllHosts(https);
|
||||
// Save the current hostnameVerifier
|
||||
oldHostnameVerifier = https.getHostnameVerifier();
|
||||
// Setup the connection not to verify hostnames
|
||||
https.setHostnameVerifier(DO_NOT_VERIFY);
|
||||
connection = https;
|
||||
}
|
||||
}
|
||||
// Return a standard HTTP connection
|
||||
else {
|
||||
connection = httpClient.open(url);
|
||||
|
||||
}
|
||||
|
||||
if (connection instanceof HttpURLConnection) {
|
||||
((HttpURLConnection)connection).setRequestMethod("GET");
|
||||
}
|
||||
|
||||
//Add cookie support
|
||||
String cookie = CookieManager.getInstance().getCookie(source);
|
||||
if(cookie != null)
|
||||
{
|
||||
connection.setRequestProperty("cookie", cookie);
|
||||
}
|
||||
|
||||
// This must be explicitly set for gzip progress tracking to work.
|
||||
connection.setRequestProperty("Accept-Encoding", "gzip");
|
||||
|
||||
// Handle the other headers
|
||||
if (headers != null) {
|
||||
addHeadersToRequest(connection, headers);
|
||||
}
|
||||
|
||||
connection.connect();
|
||||
|
||||
Log.d(LOG_TAG, "Download file:" + url);
|
||||
|
||||
FileProgressResult progress = new FileProgressResult();
|
||||
if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
|
||||
// Only trust content-length header if we understand
|
||||
// the encoding -- identity or gzip
|
||||
progress.setLengthComputable(true);
|
||||
progress.setTotal(connection.getContentLength());
|
||||
}
|
||||
|
||||
FileOutputStream outputStream = null;
|
||||
TrackingInputStream inputStream = null;
|
||||
|
||||
try {
|
||||
inputStream = getInputStream(connection);
|
||||
outputStream = new FileOutputStream(file);
|
||||
synchronized (context) {
|
||||
if (context.aborted) {
|
||||
return;
|
||||
}
|
||||
context.currentInputStream = inputStream;
|
||||
}
|
||||
|
||||
// write bytes to file
|
||||
byte[] buffer = new byte[MAX_BUFFER_SIZE];
|
||||
int bytesRead = 0;
|
||||
while ((bytesRead = inputStream.read(buffer)) > 0) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
// Send a progress event.
|
||||
progress.setLoaded(inputStream.getTotalRawBytesRead());
|
||||
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
|
||||
progressResult.setKeepCallback(true);
|
||||
context.sendPluginResult(progressResult);
|
||||
}
|
||||
} finally {
|
||||
context.currentInputStream = null;
|
||||
safeClose(inputStream);
|
||||
safeClose(outputStream);
|
||||
}
|
||||
|
||||
Log.d(LOG_TAG, "Saved file: " + target);
|
||||
|
||||
// create FileEntry object
|
||||
JSONObject fileEntry = FileUtils.getEntry(file);
|
||||
|
||||
result = new PluginResult(PluginResult.Status.OK, fileEntry);
|
||||
} catch (FileNotFoundException e) {
|
||||
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (IOException e) {
|
||||
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
result = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
} catch (Throwable e) {
|
||||
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} finally {
|
||||
synchronized (activeRequests) {
|
||||
activeRequests.remove(objectId);
|
||||
}
|
||||
|
||||
if (connection != null) {
|
||||
// Revert back to the proper verifier and socket factories
|
||||
if (trustEveryone && useHttps) {
|
||||
HttpsURLConnection https = (HttpsURLConnection) connection;
|
||||
https.setHostnameVerifier(oldHostnameVerifier);
|
||||
https.setSSLSocketFactory(oldSocketFactory);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
result = new PluginResult(PluginResult.Status.ERROR, createFileTransferError(CONNECTION_ERR, source, target, connection));
|
||||
}
|
||||
// Remove incomplete download.
|
||||
if (result.getStatus() != PluginResult.Status.OK.ordinal() && file != null) {
|
||||
file.delete();
|
||||
}
|
||||
context.sendPluginResult(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an input stream based on file path or content:// uri
|
||||
*
|
||||
* @param path foo
|
||||
* @return an input stream
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
private InputStream getPathFromUri(String path) throws FileNotFoundException {
|
||||
try {
|
||||
InputStream stream = FileHelper.getInputStreamFromUriString(path, cordova);
|
||||
if (stream == null) {
|
||||
return new FileInputStream(path);
|
||||
} else {
|
||||
return stream;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a File object from the passed in path
|
||||
*
|
||||
* @param path file path
|
||||
* @return file object
|
||||
*/
|
||||
private File getFileFromPath(String path) throws FileNotFoundException {
|
||||
File file;
|
||||
String prefix = "file://";
|
||||
|
||||
if (path.startsWith(prefix)) {
|
||||
file = new File(path.substring(prefix.length()));
|
||||
} else {
|
||||
file = new File(path);
|
||||
}
|
||||
|
||||
if (file.getParent() == null) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort an ongoing upload or download.
|
||||
*/
|
||||
private void abort(String objectId) {
|
||||
final RequestContext context;
|
||||
synchronized (activeRequests) {
|
||||
context = activeRequests.remove(objectId);
|
||||
}
|
||||
if (context != null) {
|
||||
File file = context.targetFile;
|
||||
if (file != null) {
|
||||
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, null, -1);
|
||||
synchronized (context) {
|
||||
context.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
|
||||
context.aborted = true;
|
||||
}
|
||||
// Closing the streams can block, so execute on a background thread.
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
synchronized (context) {
|
||||
safeClose(context.currentInputStream);
|
||||
safeClose(context.currentOutputStream);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the result and/or status of uploading a file to a remote server.
|
||||
*/
|
||||
public class FileUploadResult {
|
||||
|
||||
private long bytesSent = 0; // bytes sent
|
||||
private int responseCode = -1; // HTTP response code
|
||||
private String response = null; // HTTP response
|
||||
private String objectId = null; // FileTransfer object id
|
||||
|
||||
public long getBytesSent() {
|
||||
return bytesSent;
|
||||
}
|
||||
|
||||
public void setBytesSent(long bytes) {
|
||||
this.bytesSent = bytes;
|
||||
}
|
||||
|
||||
public int getResponseCode() {
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
public void setResponseCode(int responseCode) {
|
||||
this.responseCode = responseCode;
|
||||
}
|
||||
|
||||
public String getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(String response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public String getObjectId() {
|
||||
return objectId;
|
||||
}
|
||||
|
||||
public void setObjectId(String objectId) {
|
||||
this.objectId = objectId;
|
||||
}
|
||||
|
||||
public JSONObject toJSONObject() throws JSONException {
|
||||
return new JSONObject(
|
||||
"{bytesSent:" + bytesSent +
|
||||
",responseCode:" + responseCode +
|
||||
",response:" + JSONObject.quote(response) +
|
||||
",objectId:" + JSONObject.quote(objectId) + "}");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.location.LocationManager;
|
||||
|
||||
/**
|
||||
* This class handles requests for GPS location services.
|
||||
*
|
||||
*/
|
||||
public class GPSListener extends CordovaLocationListener {
|
||||
public GPSListener(LocationManager locationManager, GeoBroker m) {
|
||||
super(locationManager, m, "[Cordova GPSListener]");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start requesting location updates.
|
||||
*
|
||||
* @param interval
|
||||
*/
|
||||
@Override
|
||||
protected void start() {
|
||||
if (!this.running) {
|
||||
if (this.locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
|
||||
this.running = true;
|
||||
this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000, 0, this);
|
||||
} else {
|
||||
this.fail(CordovaLocationListener.POSITION_UNAVAILABLE, "GPS provider is not available.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
|
||||
/*
|
||||
* This class is the interface to the Geolocation. It's bound to the geo object.
|
||||
*
|
||||
* This class only starts and stops various GeoListeners, which consist of a GPS and a Network Listener
|
||||
*/
|
||||
|
||||
public class GeoBroker extends CordovaPlugin {
|
||||
private GPSListener gpsListener;
|
||||
private NetworkListener networkListener;
|
||||
private LocationManager locationManager;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public GeoBroker() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||
* @return True if the action was valid, or false if not.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (this.locationManager == null) {
|
||||
this.locationManager = (LocationManager) this.cordova.getActivity().getSystemService(Context.LOCATION_SERVICE);
|
||||
this.networkListener = new NetworkListener(this.locationManager, this);
|
||||
this.gpsListener = new GPSListener(this.locationManager, this);
|
||||
}
|
||||
|
||||
if ( locationManager.isProviderEnabled( LocationManager.GPS_PROVIDER ) ||
|
||||
locationManager.isProviderEnabled( LocationManager.NETWORK_PROVIDER )) {
|
||||
|
||||
if (action.equals("getLocation")) {
|
||||
boolean enableHighAccuracy = args.getBoolean(0);
|
||||
int maximumAge = args.getInt(1);
|
||||
Location last = this.locationManager.getLastKnownLocation((enableHighAccuracy ? LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER));
|
||||
// Check if we can use lastKnownLocation to get a quick reading and use less battery
|
||||
if (last != null && (System.currentTimeMillis() - last.getTime()) <= maximumAge) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last));
|
||||
callbackContext.sendPluginResult(result);
|
||||
} else {
|
||||
this.getCurrentLocation(callbackContext, enableHighAccuracy, args.optInt(2, 60000));
|
||||
}
|
||||
}
|
||||
else if (action.equals("addWatch")) {
|
||||
String id = args.getString(0);
|
||||
boolean enableHighAccuracy = args.getBoolean(1);
|
||||
this.addWatch(id, callbackContext, enableHighAccuracy);
|
||||
}
|
||||
else if (action.equals("clearWatch")) {
|
||||
String id = args.getString(0);
|
||||
this.clearWatch(id);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
PluginResult.Status status = PluginResult.Status.NO_RESULT;
|
||||
String message = "Location API is not available for this device.";
|
||||
PluginResult result = new PluginResult(status, message);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void clearWatch(String id) {
|
||||
this.gpsListener.clearWatch(id);
|
||||
this.networkListener.clearWatch(id);
|
||||
}
|
||||
|
||||
private void getCurrentLocation(CallbackContext callbackContext, boolean enableHighAccuracy, int timeout) {
|
||||
if (enableHighAccuracy) {
|
||||
this.gpsListener.addCallback(callbackContext, timeout);
|
||||
} else {
|
||||
this.networkListener.addCallback(callbackContext, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
private void addWatch(String timerId, CallbackContext callbackContext, boolean enableHighAccuracy) {
|
||||
if (enableHighAccuracy) {
|
||||
this.gpsListener.addWatch(timerId, callbackContext);
|
||||
} else {
|
||||
this.networkListener.addWatch(timerId, callbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity is to be shut down.
|
||||
* Stop listener.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
if (this.networkListener != null) {
|
||||
this.networkListener.destroy();
|
||||
this.networkListener = null;
|
||||
}
|
||||
if (this.gpsListener != null) {
|
||||
this.gpsListener.destroy();
|
||||
this.gpsListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the view navigates.
|
||||
* Stop the listeners.
|
||||
*/
|
||||
public void onReset() {
|
||||
this.onDestroy();
|
||||
}
|
||||
|
||||
public JSONObject returnLocationJSON(Location loc) {
|
||||
JSONObject o = new JSONObject();
|
||||
|
||||
try {
|
||||
o.put("latitude", loc.getLatitude());
|
||||
o.put("longitude", loc.getLongitude());
|
||||
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("velocity", loc.getSpeed());
|
||||
o.put("timestamp", loc.getTime());
|
||||
} catch (JSONException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
public void win(Location loc, CallbackContext callbackContext, boolean keepCallback) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(loc));
|
||||
result.setKeepCallback(keepCallback);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Location failed. Send error back to JavaScript.
|
||||
*
|
||||
* @param code The error code
|
||||
* @param msg The error message
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void fail(int code, String msg, CallbackContext callbackContext, boolean keepCallback) {
|
||||
JSONObject obj = new JSONObject();
|
||||
String backup = null;
|
||||
try {
|
||||
obj.put("code", code);
|
||||
obj.put("message", msg);
|
||||
} catch (JSONException e) {
|
||||
obj = null;
|
||||
backup = "{'code':" + code + ",'message':'" + msg.replaceAll("'", "\'") + "'}";
|
||||
}
|
||||
PluginResult result;
|
||||
if (obj != null) {
|
||||
result = new PluginResult(PluginResult.Status.ERROR, obj);
|
||||
} else {
|
||||
result = new PluginResult(PluginResult.Status.ERROR, backup);
|
||||
}
|
||||
|
||||
result.setKeepCallback(keepCallback);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
public boolean isGlobalListener(CordovaLocationListener listener)
|
||||
{
|
||||
if (gpsListener != null && networkListener != null)
|
||||
{
|
||||
return gpsListener.equals(listener) || networkListener.equals(listener);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,578 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.text.format.Time;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Globalization extends CordovaPlugin {
|
||||
//GlobalizationCommand Plugin Actions
|
||||
public static final String GETLOCALENAME = "getLocaleName";
|
||||
public static final String DATETOSTRING = "dateToString";
|
||||
public static final String STRINGTODATE = "stringToDate";
|
||||
public static final String GETDATEPATTERN = "getDatePattern";
|
||||
public static final String GETDATENAMES = "getDateNames";
|
||||
public static final String ISDAYLIGHTSAVINGSTIME = "isDayLightSavingsTime";
|
||||
public static final String GETFIRSTDAYOFWEEK = "getFirstDayOfWeek";
|
||||
public static final String NUMBERTOSTRING = "numberToString";
|
||||
public static final String STRINGTONUMBER = "stringToNumber";
|
||||
public static final String GETNUMBERPATTERN = "getNumberPattern";
|
||||
public static final String GETCURRENCYPATTERN = "getCurrencyPattern";
|
||||
public static final String GETPREFERREDLANGUAGE = "getPreferredLanguage";
|
||||
|
||||
//GlobalizationCommand Option Parameters
|
||||
public static final String OPTIONS = "options";
|
||||
public static final String FORMATLENGTH = "formatLength";
|
||||
//public static final String SHORT = "short"; //default for dateToString format
|
||||
public static final String MEDIUM = "medium";
|
||||
public static final String LONG = "long";
|
||||
public static final String FULL = "full";
|
||||
public static final String SELECTOR = "selector";
|
||||
//public static final String DATEANDTIME = "date and time"; //default for dateToString
|
||||
public static final String DATE = "date";
|
||||
public static final String TIME = "time";
|
||||
public static final String DATESTRING = "dateString";
|
||||
public static final String TYPE = "type";
|
||||
public static final String ITEM = "item";
|
||||
public static final String NARROW = "narrow";
|
||||
public static final String WIDE = "wide";
|
||||
public static final String MONTHS = "months";
|
||||
public static final String DAYS = "days";
|
||||
//public static final String DECMIAL = "wide"; //default for numberToString
|
||||
public static final String NUMBER = "number";
|
||||
public static final String NUMBERSTRING = "numberString";
|
||||
public static final String PERCENT = "percent";
|
||||
public static final String CURRENCY = "currency";
|
||||
public static final String CURRENCYCODE = "currencyCode";
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) {
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try{
|
||||
if (action.equals(GETLOCALENAME)){
|
||||
obj = getLocaleName();
|
||||
}else if (action.equals(GETPREFERREDLANGUAGE)){
|
||||
obj = getPreferredLanguage();
|
||||
} else if (action.equalsIgnoreCase(DATETOSTRING)) {
|
||||
obj = getDateToString(data);
|
||||
}else if(action.equalsIgnoreCase(STRINGTODATE)){
|
||||
obj = getStringtoDate(data);
|
||||
}else if(action.equalsIgnoreCase(GETDATEPATTERN)){
|
||||
obj = getDatePattern(data);
|
||||
}else if(action.equalsIgnoreCase(GETDATENAMES)){
|
||||
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.GINGERBREAD) {
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
} else {
|
||||
obj = getDateNames(data);
|
||||
}
|
||||
}else if(action.equalsIgnoreCase(ISDAYLIGHTSAVINGSTIME)){
|
||||
obj = getIsDayLightSavingsTime(data);
|
||||
}else if(action.equalsIgnoreCase(GETFIRSTDAYOFWEEK)){
|
||||
obj = getFirstDayOfWeek(data);
|
||||
}else if(action.equalsIgnoreCase(NUMBERTOSTRING)){
|
||||
obj = getNumberToString(data);
|
||||
}else if(action.equalsIgnoreCase(STRINGTONUMBER)){
|
||||
obj = getStringToNumber(data);
|
||||
}else if(action.equalsIgnoreCase(GETNUMBERPATTERN)){
|
||||
obj = getNumberPattern(data);
|
||||
}else if(action.equalsIgnoreCase(GETCURRENCYPATTERN)){
|
||||
obj = getCurrencyPattern(data);
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
|
||||
callbackContext.success(obj);
|
||||
}catch (GlobalizationError ge){
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, ge.toJson()));
|
||||
}catch (Exception e){
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* @Description: Returns the string identifier for the client's current locale setting
|
||||
*
|
||||
* @Return: JSONObject
|
||||
* Object.value {String}: The locale identifier
|
||||
*
|
||||
* @throws: GlobalizationError.UNKNOWN_ERROR
|
||||
*/
|
||||
private JSONObject getLocaleName() throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
try{
|
||||
obj.put("value",Locale.getDefault().toString());//get the locale from the Android Device
|
||||
return obj;
|
||||
}catch(Exception e){
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* @Description: Returns the string identifier for the client's current language
|
||||
*
|
||||
* @Return: JSONObject
|
||||
* Object.value {String}: The language identifier
|
||||
*
|
||||
* @throws: GlobalizationError.UNKNOWN_ERROR
|
||||
*/
|
||||
private JSONObject getPreferredLanguage() throws GlobalizationError {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("value", Locale.getDefault().getDisplayLanguage().toString());
|
||||
return obj;
|
||||
} catch (Exception e) {
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* @Description: Returns a date formatted as a string according to the client's user preferences and
|
||||
* calendar using the time zone of the client.
|
||||
*
|
||||
* @Return: JSONObject
|
||||
* Object.value {String}: The localized date string
|
||||
*
|
||||
* @throws: GlobalizationError.FORMATTING_ERROR
|
||||
*/
|
||||
private JSONObject getDateToString(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
try{
|
||||
Date date = new Date((Long)options.getJSONObject(0).get(DATE));
|
||||
|
||||
//get formatting pattern from android device (Will only have device specific formatting for short form of date) or options supplied
|
||||
JSONObject datePattern = getDatePattern(options);
|
||||
SimpleDateFormat fmt = new SimpleDateFormat(datePattern.getString("pattern"));
|
||||
|
||||
//return formatted date
|
||||
return obj.put("value",fmt.format(date));
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Parses a date formatted as a string according to the client's user
|
||||
* preferences and calendar using the time zone of the client and returns
|
||||
* the corresponding date object
|
||||
* @Return: JSONObject
|
||||
* Object.year {Number}: The four digit year
|
||||
* Object.month {Number}: The month from (0 - 11)
|
||||
* Object.day {Number}: The day from (1 - 31)
|
||||
* Object.hour {Number}: The hour from (0 - 23)
|
||||
* Object.minute {Number}: The minute from (0 - 59)
|
||||
* Object.second {Number}: The second from (0 - 59)
|
||||
* Object.millisecond {Number}: The milliseconds (from 0 - 999), not available on all platforms
|
||||
*
|
||||
* @throws: GlobalizationError.PARSING_ERROR
|
||||
*/
|
||||
private JSONObject getStringtoDate(JSONArray options)throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
Date date;
|
||||
try{
|
||||
//get format pattern from android device (Will only have device specific formatting for short form of date) or options supplied
|
||||
DateFormat fmt = new SimpleDateFormat(getDatePattern(options).getString("pattern"));
|
||||
|
||||
//attempt parsing string based on user preferences
|
||||
date = fmt.parse(options.getJSONObject(0).get(DATESTRING).toString());
|
||||
|
||||
//set Android Time object
|
||||
Time time = new Time();
|
||||
time.set(date.getTime());
|
||||
|
||||
//return properties;
|
||||
obj.put("year", time.year);
|
||||
obj.put("month", time.month);
|
||||
obj.put("day", time.monthDay);
|
||||
obj.put("hour", time.hour);
|
||||
obj.put("minute", time.minute);
|
||||
obj.put("second", time.second);
|
||||
obj.put("millisecond", new Long(0));
|
||||
return obj;
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.PARSING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns a pattern string for formatting and parsing dates according to the client's
|
||||
* user preferences.
|
||||
* @Return: JSONObject
|
||||
*
|
||||
* Object.pattern {String}: The date and time pattern for formatting and parsing dates.
|
||||
* The patterns follow Unicode Technical Standard #35
|
||||
* http://unicode.org/reports/tr35/tr35-4.html
|
||||
* Object.timezone {String}: The abbreviated name of the time zone on the client
|
||||
* Object.utc_offset {Number}: The current difference in seconds between the client's
|
||||
* time zone and coordinated universal time.
|
||||
* Object.dst_offset {Number}: The current daylight saving time offset in seconds
|
||||
* between the client's non-daylight saving's time zone
|
||||
* and the client's daylight saving's time zone.
|
||||
*
|
||||
* @throws: GlobalizationError.PATTERN_ERROR
|
||||
*/
|
||||
private JSONObject getDatePattern(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try{
|
||||
SimpleDateFormat fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getDateFormat(this.cordova.getActivity()); //default user preference for date
|
||||
SimpleDateFormat fmtTime = (SimpleDateFormat)android.text.format.DateFormat.getTimeFormat(this.cordova.getActivity()); //default user preference for time
|
||||
|
||||
String fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern(); //default SHORT date/time format. ex. dd/MM/yyyy h:mm a
|
||||
|
||||
//get Date value + options (if available)
|
||||
boolean test = options.getJSONObject(0).has(OPTIONS);
|
||||
if (options.getJSONObject(0).has(OPTIONS)){
|
||||
//options were included
|
||||
JSONObject innerOptions = options.getJSONObject(0).getJSONObject(OPTIONS);
|
||||
//get formatLength option
|
||||
if (!innerOptions.isNull(FORMATLENGTH)){
|
||||
String fmtOpt = innerOptions.getString(FORMATLENGTH);
|
||||
if (fmtOpt.equalsIgnoreCase(MEDIUM)){//medium
|
||||
fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getMediumDateFormat(this.cordova.getActivity());
|
||||
}else if (fmtOpt.equalsIgnoreCase(LONG) || fmtOpt.equalsIgnoreCase(FULL)){ //long/full
|
||||
fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getLongDateFormat(this.cordova.getActivity());
|
||||
}
|
||||
}
|
||||
|
||||
//return pattern type
|
||||
fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern();
|
||||
if (!innerOptions.isNull(SELECTOR)){
|
||||
String selOpt = innerOptions.getString(SELECTOR);
|
||||
if (selOpt.equalsIgnoreCase(DATE)){
|
||||
fmt = fmtDate.toLocalizedPattern();
|
||||
}else if (selOpt.equalsIgnoreCase(TIME)){
|
||||
fmt = fmtTime.toLocalizedPattern();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TimeZone from users device
|
||||
//TimeZone tz = Calendar.getInstance(Locale.getDefault()).getTimeZone(); //substitute method
|
||||
TimeZone tz = TimeZone.getTimeZone(Time.getCurrentTimezone());
|
||||
|
||||
obj.put("pattern", fmt);
|
||||
obj.put("timezone", tz.getDisplayName(tz.inDaylightTime(Calendar.getInstance().getTime()),TimeZone.SHORT));
|
||||
obj.put("utc_offset", tz.getRawOffset()/1000);
|
||||
obj.put("dst_offset", tz.getDSTSavings()/1000);
|
||||
return obj;
|
||||
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.PATTERN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns an array of either the names of the months or days of the week
|
||||
* according to the client's user preferences and calendar
|
||||
* @Return: JSONObject
|
||||
* Object.value {Array{String}}: The array of names starting from either
|
||||
* the first month in the year or the
|
||||
* first day of the week.
|
||||
*
|
||||
* @throws: GlobalizationError.UNKNOWN_ERROR
|
||||
*/
|
||||
@TargetApi(9)
|
||||
private JSONObject getDateNames(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
//String[] value;
|
||||
JSONArray value = new JSONArray();
|
||||
List<String> namesList = new ArrayList<String>();
|
||||
final Map<String,Integer> namesMap; // final needed for sorting with anonymous comparator
|
||||
try{
|
||||
int type = 0; //default wide
|
||||
int item = 0; //default months
|
||||
|
||||
//get options if available
|
||||
if (options.getJSONObject(0).length() > 0){
|
||||
//get type if available
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
|
||||
String t = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
|
||||
if (t.equalsIgnoreCase(NARROW)){type++;} //DateUtils.LENGTH_MEDIUM
|
||||
}
|
||||
//get item if available
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(ITEM)){
|
||||
String t = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(ITEM);
|
||||
if (t.equalsIgnoreCase(DAYS)){item += 10;} //Days of week start at 1
|
||||
}
|
||||
}
|
||||
//determine return value
|
||||
int method = item + type;
|
||||
if (method == 1) { //months and narrow
|
||||
namesMap = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.SHORT, Locale.getDefault());
|
||||
} else if (method == 10) { //days and wide
|
||||
namesMap = Calendar.getInstance().getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault());
|
||||
} else if (method == 11) { //days and narrow
|
||||
namesMap = Calendar.getInstance().getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.getDefault());
|
||||
} else { //default: months and wide
|
||||
namesMap = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
|
||||
}
|
||||
|
||||
// save names as a list
|
||||
for(String name : namesMap.keySet()) {
|
||||
namesList.add(name);
|
||||
}
|
||||
|
||||
// sort the list according to values in namesMap
|
||||
Collections.sort(namesList, new Comparator<String>() {
|
||||
public int compare(String arg0, String arg1) {
|
||||
return namesMap.get(arg0).compareTo(namesMap.get(arg1));
|
||||
}
|
||||
});
|
||||
|
||||
// convert nameList into JSONArray of String objects
|
||||
for (int i = 0; i < namesList.size(); i ++){
|
||||
value.put(namesList.get(i));
|
||||
}
|
||||
|
||||
//return array of names
|
||||
return obj.put("value", value);
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns whether daylight savings time is in effect for a given date using the client's
|
||||
* time zone and calendar.
|
||||
* @Return: JSONObject
|
||||
* Object.dst {Boolean}: The value "true" indicates that daylight savings time is
|
||||
* in effect for the given date and "false" indicate that it is not. *
|
||||
*
|
||||
* @throws: GlobalizationError.UNKNOWN_ERROR
|
||||
*/
|
||||
private JSONObject getIsDayLightSavingsTime(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
boolean dst = false;
|
||||
try{
|
||||
Date date = new Date((Long)options.getJSONObject(0).get(DATE));
|
||||
//TimeZone tz = Calendar.getInstance(Locale.getDefault()).getTimeZone();
|
||||
TimeZone tz = TimeZone.getTimeZone(Time.getCurrentTimezone());
|
||||
dst = tz.inDaylightTime(date); //get daylight savings data from date object and user timezone settings
|
||||
|
||||
return obj.put("dst",dst);
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns the first day of the week according to the client's user preferences and calendar.
|
||||
* The days of the week are numbered starting from 1 where 1 is considered to be Sunday.
|
||||
* @Return: JSONObject
|
||||
* Object.value {Number}: The number of the first day of the week.
|
||||
*
|
||||
* @throws: GlobalizationError.UNKNOWN_ERROR
|
||||
*/
|
||||
private JSONObject getFirstDayOfWeek(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
try{
|
||||
int value = Calendar.getInstance(Locale.getDefault()).getFirstDayOfWeek(); //get first day of week based on user locale settings
|
||||
return obj.put("value", value);
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns a number formatted as a string according to the client's user preferences.
|
||||
* @Return: JSONObject
|
||||
* Object.value {String}: The formatted number string.
|
||||
*
|
||||
* @throws: GlobalizationError.FORMATTING_ERROR
|
||||
*/
|
||||
private JSONObject getNumberToString(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
String value = "";
|
||||
try{
|
||||
DecimalFormat fmt = getNumberFormatInstance(options);//returns Decimal/Currency/Percent instance
|
||||
value = fmt.format(options.getJSONObject(0).get(NUMBER));
|
||||
return obj.put("value", value);
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Parses a number formatted as a string according to the client's user preferences and
|
||||
* returns the corresponding number.
|
||||
* @Return: JSONObject
|
||||
* Object.value {Number}: The parsed number.
|
||||
*
|
||||
* @throws: GlobalizationError.PARSING_ERROR
|
||||
*/
|
||||
private JSONObject getStringToNumber(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
Number value;
|
||||
try{
|
||||
DecimalFormat fmt = getNumberFormatInstance(options); //returns Decimal/Currency/Percent instance
|
||||
value = fmt.parse((String)options.getJSONObject(0).get(NUMBERSTRING));
|
||||
return obj.put("value", value);
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.PARSING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns a pattern string for formatting and parsing numbers according to the client's user
|
||||
* preferences.
|
||||
* @Return: JSONObject
|
||||
* Object.pattern {String}: The number pattern for formatting and parsing numbers.
|
||||
* The patterns follow Unicode Technical Standard #35.
|
||||
* http://unicode.org/reports/tr35/tr35-4.html
|
||||
* Object.symbol {String}: The symbol to be used when formatting and parsing
|
||||
* e.g., percent or currency symbol.
|
||||
* Object.fraction {Number}: The number of fractional digits to use when parsing and
|
||||
* formatting numbers.
|
||||
* Object.rounding {Number}: The rounding increment to use when parsing and formatting.
|
||||
* Object.positive {String}: The symbol to use for positive numbers when parsing and formatting.
|
||||
* Object.negative: {String}: The symbol to use for negative numbers when parsing and formatting.
|
||||
* Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
|
||||
* Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
|
||||
*
|
||||
* @throws: GlobalizationError.PATTERN_ERROR
|
||||
*/
|
||||
private JSONObject getNumberPattern(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
try{
|
||||
//uses java.text.DecimalFormat to format value
|
||||
DecimalFormat fmt = (DecimalFormat) DecimalFormat.getInstance(Locale.getDefault()); //default format
|
||||
String symbol = String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator());
|
||||
//get Date value + options (if available)
|
||||
if (options.getJSONObject(0).length() > 0){
|
||||
//options were included
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
|
||||
String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
|
||||
if (fmtOpt.equalsIgnoreCase(CURRENCY)){
|
||||
fmt = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault());
|
||||
symbol = fmt.getDecimalFormatSymbols().getCurrencySymbol();
|
||||
}else if(fmtOpt.equalsIgnoreCase(PERCENT)){
|
||||
fmt = (DecimalFormat) DecimalFormat.getPercentInstance(Locale.getDefault());
|
||||
symbol = String.valueOf(fmt.getDecimalFormatSymbols().getPercent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//return properties
|
||||
obj.put("pattern", fmt.toPattern());
|
||||
obj.put("symbol", symbol);
|
||||
obj.put("fraction", fmt.getMinimumFractionDigits());
|
||||
obj.put("rounding", new Integer(0));
|
||||
obj.put("positive", fmt.getPositivePrefix());
|
||||
obj.put("negative", fmt.getNegativePrefix());
|
||||
obj.put("decimal", String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator()));
|
||||
obj.put("grouping", String.valueOf(fmt.getDecimalFormatSymbols().getGroupingSeparator()));
|
||||
|
||||
return obj;
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.PATTERN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns a pattern string for formatting and parsing currency values according to the client's
|
||||
* user preferences and ISO 4217 currency code.
|
||||
* @Return: JSONObject
|
||||
* Object.pattern {String}: The currency pattern for formatting and parsing currency values.
|
||||
* The patterns follow Unicode Technical Standard #35
|
||||
* http://unicode.org/reports/tr35/tr35-4.html
|
||||
* Object.code {String}: The ISO 4217 currency code for the pattern.
|
||||
* Object.fraction {Number}: The number of fractional digits to use when parsing and
|
||||
* formatting currency.
|
||||
* Object.rounding {Number}: The rounding increment to use when parsing and formatting.
|
||||
* Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
|
||||
* Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
|
||||
*
|
||||
* @throws: GlobalizationError.FORMATTING_ERROR
|
||||
*/
|
||||
private JSONObject getCurrencyPattern(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
try{
|
||||
//get ISO 4217 currency code
|
||||
String code = options.getJSONObject(0).getString(CURRENCYCODE);
|
||||
|
||||
//uses java.text.DecimalFormat to format value
|
||||
DecimalFormat fmt = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault());
|
||||
|
||||
//set currency format
|
||||
Currency currency = Currency.getInstance(code);
|
||||
fmt.setCurrency(currency);
|
||||
|
||||
//return properties
|
||||
obj.put("pattern", fmt.toPattern());
|
||||
obj.put("code", currency.getCurrencyCode());
|
||||
obj.put("fraction", fmt.getMinimumFractionDigits());
|
||||
obj.put("rounding", new Integer(0));
|
||||
obj.put("decimal", String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator()));
|
||||
obj.put("grouping", String.valueOf(fmt.getDecimalFormatSymbols().getGroupingSeparator()));
|
||||
|
||||
return obj;
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Parses a JSONArray from user options and returns the correct Instance of Decimal/Percent/Currency.
|
||||
* @Return: DecimalFormat : The Instance to use.
|
||||
*
|
||||
* @throws: JSONException
|
||||
*/
|
||||
private DecimalFormat getNumberFormatInstance(JSONArray options) throws JSONException{
|
||||
DecimalFormat fmt = (DecimalFormat)DecimalFormat.getInstance(Locale.getDefault()); //default format
|
||||
try{
|
||||
if (options.getJSONObject(0).length() > 1){
|
||||
//options were included
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
|
||||
String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
|
||||
if (fmtOpt.equalsIgnoreCase(CURRENCY)){
|
||||
fmt = (DecimalFormat)DecimalFormat.getCurrencyInstance(Locale.getDefault());
|
||||
}else if(fmtOpt.equalsIgnoreCase(PERCENT)){
|
||||
fmt = (DecimalFormat)DecimalFormat.getPercentInstance(Locale.getDefault());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}catch (JSONException je){}
|
||||
return fmt;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user