forked from github/cordova-android
merging conflict with create script
This commit is contained in:
commit
d406e2ed22
@ -15,8 +15,8 @@ indicate that the project has yet to be fully endorsed by the ASF.
|
|||||||
Requires
|
Requires
|
||||||
---
|
---
|
||||||
|
|
||||||
- Java JDK 1.5
|
- Java JDK 1.5 or greater
|
||||||
- Apache ANT
|
- Apache ANT 1.8.0 or greater
|
||||||
- Android SDK [http://developer.android.com](http://developer.android.com)
|
- Android SDK [http://developer.android.com](http://developer.android.com)
|
||||||
- Apache Commons Codec [http://commons.apache.org/codec/](http://commons.apache.org/codec/)
|
- Apache Commons Codec [http://commons.apache.org/codec/](http://commons.apache.org/codec/)
|
||||||
|
|
||||||
|
14
bin/create
14
bin/create
@ -30,10 +30,10 @@ then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BUILD_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
BUILD_PATH="$( cd "$( dirname "$0" )/.." && pwd )"
|
||||||
VERSION=$(cat "$BUILD_PATH"/VERSION)
|
VERSION=$(cat "$BUILD_PATH"/VERSION)
|
||||||
|
|
||||||
PROJECT_PATH=${1:-'./example'}
|
PROJECT_PATH="${1:-'./example'}"
|
||||||
PACKAGE=${2:-"org.apache.cordova.example"}
|
PACKAGE=${2:-"org.apache.cordova.example"}
|
||||||
ACTIVITY=${3:-"cordovaExample"}
|
ACTIVITY=${3:-"cordovaExample"}
|
||||||
|
|
||||||
@ -87,19 +87,19 @@ function replace {
|
|||||||
trap on_error ERR
|
trap on_error ERR
|
||||||
trap on_exit EXIT
|
trap on_exit EXIT
|
||||||
|
|
||||||
ANDROID_BIN=$( which android )
|
ANDROID_BIN="${ANDROID_BIN:=$( which android )}"
|
||||||
PACKAGE_AS_PATH=$(echo $PACKAGE | sed 's/\./\//g')
|
PACKAGE_AS_PATH=$(echo $PACKAGE | sed 's/\./\//g')
|
||||||
ACTIVITY_PATH="$PROJECT_PATH"/src/$PACKAGE_AS_PATH/$ACTIVITY.java
|
ACTIVITY_PATH="$PROJECT_PATH"/src/$PACKAGE_AS_PATH/$ACTIVITY.java
|
||||||
MANIFEST_PATH="$PROJECT_PATH"/AndroidManifest.xml
|
MANIFEST_PATH="$PROJECT_PATH"/AndroidManifest.xml
|
||||||
|
|
||||||
TARGET=$($ANDROID_BIN list targets | grep id: | tail -1 | cut -f 2 -d ' ' )
|
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 ' ')
|
API_LEVEL=$("$ANDROID_BIN" list target | grep "API level:" | tail -n 1 | cut -f 2 -d ':' | tr -d ' ')
|
||||||
|
|
||||||
# if this a distribution release no need to build a jar
|
# if this a distribution release no need to build a jar
|
||||||
if [ ! -e "$BUILD_PATH"/cordova-$VERSION.jar ] && [ -d "$BUILD_PATH"/framework ]
|
if [ ! -e "$BUILD_PATH"/cordova-$VERSION.jar ] && [ -d "$BUILD_PATH"/framework ]
|
||||||
then
|
then
|
||||||
# update the cordova-android framework for the desired target
|
# update the cordova-android framework for the desired target
|
||||||
$ANDROID_BIN update project --target $TARGET --path "$BUILD_PATH"/framework &> /dev/null
|
"$ANDROID_BIN" update project --target $TARGET --path "$BUILD_PATH"/framework &> /dev/null
|
||||||
|
|
||||||
if [ ! -e "$BUILD_PATH"/framework/libs/commons-codec-1.7.jar ]; then
|
if [ ! -e "$BUILD_PATH"/framework/libs/commons-codec-1.7.jar ]; then
|
||||||
# Use curl to get the jar (TODO: Support Apache Mirrors)
|
# Use curl to get the jar (TODO: Support Apache Mirrors)
|
||||||
@ -116,7 +116,7 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# create new android project
|
# create new android project
|
||||||
$ANDROID_BIN create project --target $TARGET --path "$PROJECT_PATH" --package $PACKAGE --activity $ACTIVITY &> /dev/null
|
"$ANDROID_BIN" create project --target $TARGET --path "$PROJECT_PATH" --package $PACKAGE --activity $ACTIVITY &> /dev/null
|
||||||
|
|
||||||
# copy project template
|
# copy project template
|
||||||
cp -r "$BUILD_PATH"/bin/templates/project/assets "$PROJECT_PATH"
|
cp -r "$BUILD_PATH"/bin/templates/project/assets "$PROJECT_PATH"
|
||||||
|
@ -163,40 +163,40 @@ if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
|
|||||||
|
|
||||||
// copy in the project template
|
// copy in the project template
|
||||||
WScript.Echo("Copying template files...");
|
WScript.Echo("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\\res '+PROJECT_PATH+'\\res\\ /E /Y');
|
||||||
exec('%comspec% /c xcopy '+ ROOT + '\\bin\\templates\\project\\assets '+PROJECT_PATH+'\\assets\\ /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 copy "'+ROOT+'"\\bin\\templates\\project\\AndroidManifest.xml ' + PROJECT_PATH + '\\AndroidManifest.xml /Y');
|
||||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\project\\Activity.java '+ ACTIVITY_PATH +' /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\project\\Activity.java '+ ACTIVITY_PATH +' /Y');
|
||||||
|
|
||||||
// check if we have the source or the distro files
|
// check if we have the source or the distro files
|
||||||
WScript.Echo("Copying js, jar & config.xml files...");
|
WScript.Echo("Copying js, jar & config.xml files...");
|
||||||
if(fso.FolderExists(ROOT + '\\framework')) {
|
if(fso.FolderExists(ROOT + '\\framework')) {
|
||||||
exec('%comspec% /c copy '+ROOT+'\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
|
||||||
exec('%comspec% /c copy '+ROOT+'\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
||||||
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
||||||
exec('%comspec% /c copy '+ROOT+'\\framework\\res\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\framework\\res\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
|
||||||
} else {
|
} else {
|
||||||
// copy in cordova.js
|
// copy in cordova.js
|
||||||
exec('%comspec% /c copy '+ROOT+'\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
|
||||||
// copy in cordova.jar
|
// copy in cordova.jar
|
||||||
exec('%comspec% /c copy '+ROOT+'\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
||||||
// copy in xml
|
// copy in xml
|
||||||
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
||||||
exec('%comspec% /c copy '+ROOT+'\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy cordova scripts
|
// copy cordova scripts
|
||||||
fso.CreateFolder(PROJECT_PATH + '\\cordova');
|
fso.CreateFolder(PROJECT_PATH + '\\cordova');
|
||||||
createAppInfoJar();
|
createAppInfoJar();
|
||||||
WScript.Echo("Copying cordova command tools...");
|
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\\appinfo.jar ' + PROJECT_PATH + '\\cordova\\appinfo.jar /Y');
|
||||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y');
|
||||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\cordova.bat ' + PROJECT_PATH + '\\cordova\\cordova.bat /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\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\\clean.bat ' + PROJECT_PATH + '\\cordova\\clean.bat /Y');
|
||||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\debug.bat ' + PROJECT_PATH + '\\cordova\\debug.bat /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\debug.bat ' + PROJECT_PATH + '\\cordova\\debug.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\\log.bat ' + PROJECT_PATH + '\\cordova\\log.bat /Y');
|
||||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\emulate.bat ' + PROJECT_PATH + '\\cordova\\emulate.bat /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\emulate.bat ' + PROJECT_PATH + '\\cordova\\emulate.bat /Y');
|
||||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\BOOM.bat ' + PROJECT_PATH + '\\cordova\\BOOM.bat /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\BOOM.bat ' + PROJECT_PATH + '\\cordova\\BOOM.bat /Y');
|
||||||
|
|
||||||
// interpolate the activity name and package
|
// interpolate the activity name and package
|
||||||
WScript.Echo("Updating AndroidManifest.xml and Main Activity...");
|
WScript.Echo("Updating AndroidManifest.xml and Main Activity...");
|
||||||
|
@ -88,14 +88,9 @@ create_project.on('exit', function(code) {
|
|||||||
// TODO check that package name and activity name were substituted properly
|
// TODO check that package name and activity name were substituted properly
|
||||||
});
|
});
|
||||||
|
|
||||||
// make sure plugins.xml was added
|
// make sure config.xml was added
|
||||||
path.exists(util.format('%s/res/xml/plugins.xml', project_path), function(exists) {
|
path.exists(util.format('%s/res/xml/config.xml', project_path), function(exists) {
|
||||||
assert(exists, 'plugins.xml did not get created');
|
assert(exists, 'config.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
|
// make sure cordova.jar was added
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="gen"/>
|
<classpathentry kind="src" path="gen"/>
|
||||||
<classpathentry kind="lib" path="libs/commons-codec-1.6.jar"/>
|
<classpathentry kind="lib" path="libs/commons-codec-1.7.jar"/>
|
||||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||||
<classpathentry kind="output" path="bin/classes"/>
|
<classpathentry kind="output" path="bin/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -26,9 +26,37 @@
|
|||||||
</filterchain>
|
</filterchain>
|
||||||
</loadfile>
|
</loadfile>
|
||||||
|
|
||||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
<!-- check that the version of ant is at least 1.8.0, as is needed
|
||||||
It contains the path to the SDK. It should *NOT* be checked into
|
for the dblQuote property -->
|
||||||
Version Control Systems. -->
|
<antversion property="thisantversion" atleast="1.8.0" />
|
||||||
|
<fail message="The required minimum version of ant is 1.8.0, you have ${ant.version}"
|
||||||
|
unless="thisantversion" />
|
||||||
|
|
||||||
|
<!-- check that commons codec is available. You should copy the codec jar to
|
||||||
|
framework/libs, as it is not included in the Cordova distribution.
|
||||||
|
The name of the jar file in framework/libs does not matter. -->
|
||||||
|
<available classname="org.apache.commons.codec.binary.Base64"
|
||||||
|
property="exists.base64"
|
||||||
|
ignoresystemclasses="true">
|
||||||
|
<classpath>
|
||||||
|
<pathelement path="${classpath}" />
|
||||||
|
<fileset dir="libs">
|
||||||
|
<include name="*.jar" />
|
||||||
|
</fileset>
|
||||||
|
</classpath>
|
||||||
|
</available>
|
||||||
|
<fail message="You need to put a copy of Apache Commons Codec jar in the framework/libs directory"
|
||||||
|
unless="exists.base64" />
|
||||||
|
|
||||||
|
<!-- The local.properties file is created and updated by the 'android'
|
||||||
|
tool. (For example "sdkdir/tools/android update project -p ." inside
|
||||||
|
of this directory where the AndroidManifest.xml file exists. This
|
||||||
|
properties file that gets built contains the path to the SDK. It
|
||||||
|
should *NOT* be checked into Version Control Systems since it holds
|
||||||
|
data about the local machine. -->
|
||||||
|
<available file="local.properties" property="exists.local.properties" />
|
||||||
|
<fail message="You need to create the file 'local.properties' by running 'android update project -p .' here."
|
||||||
|
unless="exists.local.properties" />
|
||||||
<loadproperties srcFile="local.properties" />
|
<loadproperties srcFile="local.properties" />
|
||||||
|
|
||||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||||
@ -66,13 +94,13 @@
|
|||||||
application and should be checked into Version Control Systems. -->
|
application and should be checked into Version Control Systems. -->
|
||||||
<loadproperties srcFile="project.properties" />
|
<loadproperties srcFile="project.properties" />
|
||||||
|
|
||||||
<!-- quick check on sdk.dir -->
|
<!-- quick check on sdk.dir -->
|
||||||
<fail
|
<fail
|
||||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
|
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
|
||||||
unless="sdk.dir"
|
unless="sdk.dir"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- version-tag: custom -->
|
<!-- version-tag: custom -->
|
||||||
<!-- extension targets. Uncomment the ones where you want to do custom work
|
<!-- extension targets. Uncomment the ones where you want to do custom work
|
||||||
in between standard targets -->
|
in between standard targets -->
|
||||||
<!--
|
<!--
|
||||||
@ -106,7 +134,7 @@
|
|||||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||||
in order to avoid having your file be overridden by tools such as "android update project"
|
in order to avoid having your file be overridden by tools such as "android update project"
|
||||||
-->
|
-->
|
||||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||||
|
|
||||||
<!-- Combine JavaScript files into one cordova-uncompressed.js file. -->
|
<!-- Combine JavaScript files into one cordova-uncompressed.js file. -->
|
||||||
<target name="build-javascript" depends="clean">
|
<target name="build-javascript" depends="clean">
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
<plugin name="Battery" value="org.apache.cordova.BatteryListener"/>
|
<plugin name="Battery" value="org.apache.cordova.BatteryListener"/>
|
||||||
<plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/>
|
<plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/>
|
||||||
<plugin name="Echo" value="org.apache.cordova.Echo" />
|
<plugin name="Echo" value="org.apache.cordova.Echo" />
|
||||||
|
<plugin name="Globalization" value="org.apache.cordova.Globalization"/>
|
||||||
</plugins>
|
</plugins>
|
||||||
</cordova>
|
</cordova>
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.cordova.api.CordovaInterface;
|
import org.apache.cordova.api.CordovaInterface;
|
||||||
import org.apache.cordova.api.Plugin;
|
import org.apache.cordova.api.Plugin;
|
||||||
import org.apache.cordova.api.PluginResult;
|
import org.apache.cordova.api.PluginResult;
|
||||||
@ -26,11 +27,11 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.hardware.Sensor;
|
import android.hardware.Sensor;
|
||||||
import android.hardware.SensorEvent;
|
import android.hardware.SensorEvent;
|
||||||
import android.hardware.SensorEventListener;
|
import android.hardware.SensorEventListener;
|
||||||
import android.hardware.SensorManager;
|
import android.hardware.SensorManager;
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class listens to the accelerometer sensor and stores the latest
|
* This class listens to the accelerometer sensor and stores the latest
|
||||||
@ -224,6 +225,16 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the view navigates.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onReset() {
|
||||||
|
if (this.status == AccelListener.RUNNING) {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sends an error back to JS
|
// Sends an error back to JS
|
||||||
private void fail(int code, String message) {
|
private void fail(int code, String message) {
|
||||||
// Error object
|
// Error object
|
||||||
|
@ -139,6 +139,14 @@ public class AudioHandler extends Plugin {
|
|||||||
this.players.clear();
|
this.players.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop all audio players and recorders on navigate.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onReset() {
|
||||||
|
onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a message is sent to plugin.
|
* Called when a message is sent to plugin.
|
||||||
*
|
*
|
||||||
|
@ -99,6 +99,13 @@ public class BatteryListener extends Plugin {
|
|||||||
removeBatteryListener();
|
removeBatteryListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop battery receiver.
|
||||||
|
*/
|
||||||
|
public void onReset() {
|
||||||
|
removeBatteryListener();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the battery receiver and set it to null.
|
* Stop the battery receiver and set it to null.
|
||||||
*/
|
*/
|
||||||
|
@ -1,380 +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.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class provides a way for Java to run JavaScript in the web page that has loaded Cordova.
|
|
||||||
* The CallbackServer class implements an XHR server and a polling server with a list of JavaScript
|
|
||||||
* statements that are to be executed on the web page.
|
|
||||||
*
|
|
||||||
* The process flow for XHR is:
|
|
||||||
* 1. JavaScript makes an async XHR call.
|
|
||||||
* 2. The server holds the connection open until data is available.
|
|
||||||
* 3. The server writes the data to the client and closes the connection.
|
|
||||||
* 4. The server immediately starts listening for the next XHR call.
|
|
||||||
* 5. The client receives this XHR response, processes it.
|
|
||||||
* 6. The client sends a new async XHR request.
|
|
||||||
*
|
|
||||||
* The CallbackServer class requires the following permission in Android manifest file
|
|
||||||
* <uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
*
|
|
||||||
* If the device has a proxy set, then XHR cannot be used, so polling must be used instead.
|
|
||||||
* This can be determined by the client by calling CallbackServer.usePolling().
|
|
||||||
*
|
|
||||||
* The process flow for polling is:
|
|
||||||
* 1. The client calls CallbackServer.getJavascript() to retrieve next statement.
|
|
||||||
* 2. If statement available, then client processes it.
|
|
||||||
* 3. The client repeats #1 in loop.
|
|
||||||
*/
|
|
||||||
public class CallbackServer implements Runnable {
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static final String LOG_TAG = "CallbackServer";
|
|
||||||
|
|
||||||
private ServerSocket waitSocket;
|
|
||||||
/**
|
|
||||||
* The list of JavaScript statements to be sent to JavaScript.
|
|
||||||
* This can be null when there are no messages available.
|
|
||||||
*/
|
|
||||||
private NativeToJsMessageQueue jsMessageQueue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The port to listen on.
|
|
||||||
*/
|
|
||||||
private int port;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The server thread.
|
|
||||||
*/
|
|
||||||
private Thread serverThread;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the server is running.
|
|
||||||
*/
|
|
||||||
private boolean active;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates that polling should be used instead of XHR.
|
|
||||||
*/
|
|
||||||
private boolean usePolling = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Security token to prevent other apps from accessing this callback server via XHR
|
|
||||||
*/
|
|
||||||
private String token;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*/
|
|
||||||
public CallbackServer() {
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer()");
|
|
||||||
this.active = false;
|
|
||||||
this.port = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init callback server and start XHR if running local app.
|
|
||||||
*
|
|
||||||
* If Cordova app is loaded from file://, then we can use XHR
|
|
||||||
* otherwise we have to use polling due to cross-domain security restrictions.
|
|
||||||
*
|
|
||||||
* @param url The URL of the Cordova app being loaded
|
|
||||||
*/
|
|
||||||
public void init(String url) {
|
|
||||||
//System.out.println("CallbackServer.start("+url+")");
|
|
||||||
this.stopServer();
|
|
||||||
this.port = 0;
|
|
||||||
|
|
||||||
// Determine if XHR or polling is to be used
|
|
||||||
if ((url != null) && !url.startsWith("file://")) {
|
|
||||||
this.usePolling = true;
|
|
||||||
this.stopServer();
|
|
||||||
}
|
|
||||||
else if (android.net.Proxy.getDefaultHost() != null) {
|
|
||||||
this.usePolling = true;
|
|
||||||
this.stopServer();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.usePolling = false;
|
|
||||||
this.startServer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return if polling is being used instead of XHR.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public boolean usePolling() {
|
|
||||||
return this.usePolling;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the port that this server is running on.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public int getPort() {
|
|
||||||
return this.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the security token that this server requires when calling getJavascript().
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String getToken() {
|
|
||||||
return this.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start the server on a new thread.
|
|
||||||
*/
|
|
||||||
public void startServer() {
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer.startServer()");
|
|
||||||
this.active = false;
|
|
||||||
|
|
||||||
// Start server on new thread
|
|
||||||
this.serverThread = new Thread(this);
|
|
||||||
this.serverThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restart the server on a new thread.
|
|
||||||
*/
|
|
||||||
public void restartServer() {
|
|
||||||
|
|
||||||
// Stop server
|
|
||||||
this.stopServer();
|
|
||||||
|
|
||||||
// Start server again
|
|
||||||
this.startServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start running the server.
|
|
||||||
* This is called automatically when the server thread is started.
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
// Start server
|
|
||||||
try {
|
|
||||||
this.active = true;
|
|
||||||
String request;
|
|
||||||
waitSocket = new ServerSocket(0);
|
|
||||||
this.port = waitSocket.getLocalPort();
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer -- using port " +this.port);
|
|
||||||
this.token = java.util.UUID.randomUUID().toString();
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer -- using token "+this.token);
|
|
||||||
|
|
||||||
while (this.active) {
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer: Waiting for data on socket");
|
|
||||||
Socket connection = waitSocket.accept();
|
|
||||||
BufferedReader xhrReader = new BufferedReader(new InputStreamReader(connection.getInputStream()), 40);
|
|
||||||
DataOutputStream output = new DataOutputStream(connection.getOutputStream());
|
|
||||||
request = xhrReader.readLine();
|
|
||||||
String response = "";
|
|
||||||
//Log.d(LOG_TAG, "CallbackServerRequest="+request);
|
|
||||||
if (this.active && (request != null)) {
|
|
||||||
if (request.contains("GET")) {
|
|
||||||
|
|
||||||
// Get requested file
|
|
||||||
String[] requestParts = request.split(" ");
|
|
||||||
|
|
||||||
// Must have security token
|
|
||||||
if ((requestParts.length == 3) && (requestParts[1].substring(1).equals(this.token))) {
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer -- Processing GET request");
|
|
||||||
String payload = null;
|
|
||||||
|
|
||||||
// Wait until there is some data to send, or send empty data every 10 sec
|
|
||||||
// to prevent XHR timeout on the client
|
|
||||||
while (this.active) {
|
|
||||||
if (jsMessageQueue != null) {
|
|
||||||
payload = jsMessageQueue.popAndEncode();
|
|
||||||
if (payload != null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
try {
|
|
||||||
this.wait(10000); // prevent timeout from happening
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer>>> break <<<");
|
|
||||||
break;
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If server is still running
|
|
||||||
if (this.active) {
|
|
||||||
|
|
||||||
// If no data, then send 404 back to client before it times out
|
|
||||||
if (payload == null) {
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer -- sending data 0");
|
|
||||||
response = "HTTP/1.1 404 NO DATA\r\n\r\n "; // need to send content otherwise some Android devices fail, so send space
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer -- sending item");
|
|
||||||
response = "HTTP/1.1 200 OK\r\n\r\n";
|
|
||||||
response += encode(payload, "UTF-8");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
response = "HTTP/1.1 503 Service Unavailable\r\n\r\n ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
response = "HTTP/1.1 403 Forbidden\r\n\r\n ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
response = "HTTP/1.1 400 Bad Request\r\n\r\n ";
|
|
||||||
}
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer: response="+response);
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer: closing output");
|
|
||||||
output.writeBytes(response);
|
|
||||||
output.flush();
|
|
||||||
}
|
|
||||||
output.close();
|
|
||||||
xhrReader.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
this.active = false;
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer.startServer() - EXIT");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop server.
|
|
||||||
* This stops the thread that the server is running on.
|
|
||||||
*/
|
|
||||||
public void stopServer() {
|
|
||||||
//Log.d(LOG_TAG, "CallbackServer.stopServer()");
|
|
||||||
if (this.active) {
|
|
||||||
this.active = false;
|
|
||||||
|
|
||||||
try { waitSocket.close(); } catch (IOException ignore) {}
|
|
||||||
|
|
||||||
// Break out of server wait
|
|
||||||
synchronized (this) {
|
|
||||||
this.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy
|
|
||||||
*/
|
|
||||||
public void destroy() {
|
|
||||||
this.stopServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue) {
|
|
||||||
synchronized (this) {
|
|
||||||
this.jsMessageQueue = queue;
|
|
||||||
this.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The Following code has been modified from original implementation of URLEncoder */
|
|
||||||
|
|
||||||
/* start */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
static final String digits = "0123456789ABCDEF";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This will encode the return value to JavaScript. We revert the encoding for
|
|
||||||
* common characters that don't require encoding to reduce the size of the string
|
|
||||||
* being passed to JavaScript.
|
|
||||||
*
|
|
||||||
* @param s to be encoded
|
|
||||||
* @param enc encoding type
|
|
||||||
* @return encoded string
|
|
||||||
*/
|
|
||||||
public static String encode(String s, String enc) throws UnsupportedEncodingException {
|
|
||||||
if (s == null || enc == null) {
|
|
||||||
throw new NullPointerException();
|
|
||||||
}
|
|
||||||
// check for UnsupportedEncodingException
|
|
||||||
"".getBytes(enc);
|
|
||||||
|
|
||||||
// Guess a bit bigger for encoded form
|
|
||||||
StringBuilder buf = new StringBuilder(s.length() + 16);
|
|
||||||
int start = -1;
|
|
||||||
for (int i = 0; i < s.length(); i++) {
|
|
||||||
char ch = s.charAt(i);
|
|
||||||
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|
|
||||||
|| (ch >= '0' && ch <= '9')
|
|
||||||
|| " .-*_'(),<>=?@[]{}:~\"\\/;!".indexOf(ch) > -1) {
|
|
||||||
if (start >= 0) {
|
|
||||||
convert(s.substring(start, i), buf, enc);
|
|
||||||
start = -1;
|
|
||||||
}
|
|
||||||
if (ch != ' ') {
|
|
||||||
buf.append(ch);
|
|
||||||
} else {
|
|
||||||
buf.append(' ');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (start < 0) {
|
|
||||||
start = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (start >= 0) {
|
|
||||||
convert(s.substring(start, s.length()), buf, enc);
|
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void convert(String s, StringBuilder buf, String enc) throws UnsupportedEncodingException {
|
|
||||||
byte[] bytes = s.getBytes(enc);
|
|
||||||
for (int j = 0; j < bytes.length; j++) {
|
|
||||||
buf.append('%');
|
|
||||||
buf.append(digits.charAt((bytes[j] & 0xf0) >> 4));
|
|
||||||
buf.append(digits.charAt(bytes[j] & 0xf));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* end */
|
|
||||||
}
|
|
@ -33,6 +33,8 @@ import android.hardware.SensorEventListener;
|
|||||||
import android.hardware.SensorManager;
|
import android.hardware.SensorManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class listens to the compass sensor and stores the latest heading value.
|
* This class listens to the compass sensor and stores the latest heading value.
|
||||||
*/
|
*/
|
||||||
@ -166,6 +168,13 @@ public class CompassListener extends Plugin implements SensorEventListener {
|
|||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when app has navigated and JS listeners have been destroyed.
|
||||||
|
*/
|
||||||
|
public void onReset() {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// LOCAL METHODS
|
// LOCAL METHODS
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
@ -226,24 +226,6 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
result.confirm("OK");
|
result.confirm("OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calling into CallbackServer
|
|
||||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_callbackServer:")) {
|
|
||||||
String r = "";
|
|
||||||
if (message.equals("usePolling")) {
|
|
||||||
r = "" + this.appView.callbackServer.usePolling();
|
|
||||||
}
|
|
||||||
else if (message.equals("restartServer")) {
|
|
||||||
this.appView.callbackServer.restartServer();
|
|
||||||
}
|
|
||||||
else if (message.equals("getPort")) {
|
|
||||||
r = Integer.toString(this.appView.callbackServer.getPort());
|
|
||||||
}
|
|
||||||
else if (message.equals("getToken")) {
|
|
||||||
r = this.appView.callbackServer.getToken();
|
|
||||||
}
|
|
||||||
result.confirm(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show dialog
|
// Show dialog
|
||||||
else {
|
else {
|
||||||
final JsPromptResult res = result;
|
final JsPromptResult res = result;
|
||||||
|
@ -50,6 +50,8 @@ import android.util.AttributeSet;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.webkit.WebBackForwardList;
|
||||||
|
import android.webkit.WebHistoryItem;
|
||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||||
@ -65,7 +67,6 @@ public class CordovaWebView extends WebView {
|
|||||||
private ArrayList<Integer> keyUpCodes = new ArrayList<Integer>();
|
private ArrayList<Integer> keyUpCodes = new ArrayList<Integer>();
|
||||||
|
|
||||||
public PluginManager pluginManager;
|
public PluginManager pluginManager;
|
||||||
public CallbackServer callbackServer;
|
|
||||||
private boolean paused;
|
private boolean paused;
|
||||||
|
|
||||||
private BroadcastReceiver receiver;
|
private BroadcastReceiver receiver;
|
||||||
@ -572,12 +573,13 @@ public class CordovaWebView extends WebView {
|
|||||||
// Check webview first to see if there is a history
|
// Check webview first to see if there is a history
|
||||||
// This is needed to support curPage#diffLink, since they are added to appView's history, but not our history url array (JQMobile behavior)
|
// This is needed to support curPage#diffLink, since they are added to appView's history, but not our history url array (JQMobile behavior)
|
||||||
if (super.canGoBack()) {
|
if (super.canGoBack()) {
|
||||||
|
printBackForwardList();
|
||||||
super.goBack();
|
super.goBack();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If our managed history has prev url
|
// If our managed history has prev url
|
||||||
if (this.urls.size() > 1) {
|
if (this.urls.size() > 1 && !this.useBrowserHistory) {
|
||||||
this.urls.pop(); // Pop current url
|
this.urls.pop(); // Pop current url
|
||||||
String url = this.urls.pop(); // Pop prev url that we want to load, since it will be added back by loadUrl()
|
String url = this.urls.pop(); // Pop prev url that we want to load, since it will be added back by loadUrl()
|
||||||
this.loadUrl(url);
|
this.loadUrl(url);
|
||||||
@ -937,4 +939,17 @@ public class CordovaWebView extends WebView {
|
|||||||
settings.setAllowUniversalAccessFromFileURLs(true);
|
settings.setAllowUniversalAccessFromFileURLs(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void printBackForwardList() {
|
||||||
|
WebBackForwardList currentList = this.copyBackForwardList();
|
||||||
|
int currentSize = currentList.getSize();
|
||||||
|
for(int i = 0; i < currentSize; ++i)
|
||||||
|
{
|
||||||
|
WebHistoryItem item = currentList.getItemAtIndex(i);
|
||||||
|
String url = item.getUrl();
|
||||||
|
LOG.d(TAG, "The URL at index: " + Integer.toString(i) + "is " + url );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,20 +23,15 @@ import java.util.Hashtable;
|
|||||||
import org.apache.cordova.api.CordovaInterface;
|
import org.apache.cordova.api.CordovaInterface;
|
||||||
import org.apache.cordova.api.PluginResult;
|
import org.apache.cordova.api.PluginResult;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import org.apache.cordova.api.LOG;
|
import org.apache.cordova.api.LOG;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.net.http.SslError;
|
import android.net.http.SslError;
|
||||||
@ -44,7 +39,6 @@ import android.util.Log;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.webkit.HttpAuthHandler;
|
import android.webkit.HttpAuthHandler;
|
||||||
import android.webkit.SslErrorHandler;
|
import android.webkit.SslErrorHandler;
|
||||||
import android.webkit.WebResourceResponse;
|
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebViewClient;
|
import android.webkit.WebViewClient;
|
||||||
|
|
||||||
@ -107,7 +101,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
String action = url.substring(idx2 + 1, idx3);
|
String action = url.substring(idx2 + 1, idx3);
|
||||||
String callbackId = url.substring(idx3 + 1, idx4);
|
String callbackId = url.substring(idx3 + 1, idx4);
|
||||||
String jsonArgs = url.substring(idx4 + 1);
|
String jsonArgs = url.substring(idx4 + 1);
|
||||||
appView.pluginManager.exec(service, action, callbackId, jsonArgs, true /* async */);
|
appView.pluginManager.exec(service, action, callbackId, jsonArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -261,14 +255,13 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
// Flush stale messages.
|
// Flush stale messages.
|
||||||
this.appView.jsMessageQueue.reset();
|
this.appView.jsMessageQueue.reset();
|
||||||
|
|
||||||
// Create callback server
|
|
||||||
if (this.appView.callbackServer == null) {
|
|
||||||
this.appView.callbackServer = new CallbackServer();
|
|
||||||
}
|
|
||||||
this.appView.callbackServer.init(url);
|
|
||||||
|
|
||||||
// Broadcast message that page has loaded
|
// Broadcast message that page has loaded
|
||||||
this.appView.postMessage("onPageStarted", url);
|
this.appView.postMessage("onPageStarted", url);
|
||||||
|
|
||||||
|
// Notify all plugins of the navigation, so they can clean up if necessary.
|
||||||
|
if (this.appView.pluginManager != null) {
|
||||||
|
this.appView.pluginManager.onReset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -329,9 +322,6 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
|
|
||||||
// Shutdown if blank loaded
|
// Shutdown if blank loaded
|
||||||
if (url.equals("about:blank")) {
|
if (url.equals("about:blank")) {
|
||||||
if (this.appView.callbackServer != null) {
|
|
||||||
this.appView.callbackServer.destroy();
|
|
||||||
}
|
|
||||||
appView.postMessage("exit", null);
|
appView.postMessage("exit", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import org.apache.cordova.api.IPlugin;
|
import org.apache.cordova.api.IPlugin;
|
||||||
import org.apache.cordova.api.LOG;
|
import org.apache.cordova.api.LOG;
|
||||||
@ -142,6 +144,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
protected LinearLayout root;
|
protected LinearLayout root;
|
||||||
protected boolean cancelLoadUrl = false;
|
protected boolean cancelLoadUrl = false;
|
||||||
protected ProgressDialog spinnerDialog = null;
|
protected ProgressDialog spinnerDialog = null;
|
||||||
|
private final ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
|
|
||||||
// The initial URL for our app
|
// The initial URL for our app
|
||||||
// ie http://server/path/index.html#abc?query
|
// ie http://server/path/index.html#abc?query
|
||||||
@ -1051,4 +1055,9 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecutorService getThreadPool() {
|
||||||
|
return threadPool;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ import org.json.JSONException;
|
|||||||
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
|
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
|
||||||
jsMessageQueue.setPaused(true);
|
jsMessageQueue.setPaused(true);
|
||||||
try {
|
try {
|
||||||
boolean wasSync = pluginManager.exec(service, action, callbackId, arguments, true /* async */);
|
boolean wasSync = pluginManager.exec(service, action, callbackId, arguments);
|
||||||
String ret = "";
|
String ret = "";
|
||||||
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING || wasSync) {
|
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING || wasSync) {
|
||||||
ret = jsMessageQueue.popAndEncode();
|
ret = jsMessageQueue.popAndEncode();
|
||||||
|
63
framework/src/org/apache/cordova/FileProgressResult.java
Normal file
63
framework/src/org/apache/cordova/FileProgressResult.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
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") + "}");
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FilterInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
@ -32,6 +33,7 @@ import java.net.URL;
|
|||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import javax.net.ssl.HostnameVerifier;
|
import javax.net.ssl.HostnameVerifier;
|
||||||
@ -58,34 +60,81 @@ public class FileTransfer extends Plugin {
|
|||||||
private static final String LOG_TAG = "FileTransfer";
|
private static final String LOG_TAG = "FileTransfer";
|
||||||
private static final String LINE_START = "--";
|
private static final String LINE_START = "--";
|
||||||
private static final String LINE_END = "\r\n";
|
private static final String LINE_END = "\r\n";
|
||||||
private static final String BOUNDARY = "*****";
|
private static final String BOUNDARY = "+++++";
|
||||||
|
|
||||||
public static int FILE_NOT_FOUND_ERR = 1;
|
public static int FILE_NOT_FOUND_ERR = 1;
|
||||||
public static int INVALID_URL_ERR = 2;
|
public static int INVALID_URL_ERR = 2;
|
||||||
public static int CONNECTION_ERR = 3;
|
public static int CONNECTION_ERR = 3;
|
||||||
|
public static int ABORTED_ERR = 4;
|
||||||
|
|
||||||
|
private static HashSet<String> abortTriggered = new HashSet<String>();
|
||||||
|
|
||||||
private SSLSocketFactory defaultSSLSocketFactory = null;
|
private SSLSocketFactory defaultSSLSocketFactory = null;
|
||||||
private HostnameVerifier defaultHostnameVerifier = null;
|
private HostnameVerifier defaultHostnameVerifier = null;
|
||||||
|
|
||||||
|
private static final class AbortException extends Exception {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
public AbortException(String str) {
|
||||||
|
super(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Works around a bug on Android 2.3.
|
||||||
|
* http://code.google.com/p/android/issues/detail?id=14562
|
||||||
|
*/
|
||||||
|
private static final class DoneHandlerInputStream extends FilterInputStream {
|
||||||
|
private boolean done;
|
||||||
|
|
||||||
|
public DoneHandlerInputStream(InputStream stream) {
|
||||||
|
super(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
int result = done ? -1 : super.read();
|
||||||
|
done = (result == -1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] buffer) throws IOException {
|
||||||
|
int result = done ? -1 : super.read(buffer);
|
||||||
|
done = (result == -1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] bytes, int offset, int count) throws IOException {
|
||||||
|
int result = done ? -1 : super.read(bytes, offset, count);
|
||||||
|
done = (result == -1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.apache.cordova.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String)
|
* @see org.apache.cordova.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||||
String source = null;
|
if (action.equals("upload") || action.equals("download")) {
|
||||||
String target = null;
|
String source = null;
|
||||||
try {
|
String target = null;
|
||||||
source = args.getString(0);
|
try {
|
||||||
target = args.getString(1);
|
source = args.getString(0);
|
||||||
} catch (JSONException e) {
|
target = args.getString(1);
|
||||||
Log.d(LOG_TAG, "Missing source or target");
|
} catch (JSONException e) {
|
||||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
|
Log.d(LOG_TAG, "Missing source or target");
|
||||||
}
|
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
|
||||||
|
}
|
||||||
|
|
||||||
if (action.equals("upload")) {
|
if (action.equals("upload")) {
|
||||||
return upload(URLDecoder.decode(source), target, args);
|
return upload(URLDecoder.decode(source), target, args, callbackId);
|
||||||
} else if (action.equals("download")) {
|
} else {
|
||||||
return download(source, target, args.optBoolean(2));
|
return download(source, target, args, callbackId);
|
||||||
|
}
|
||||||
|
} else if (action.equals("abort")) {
|
||||||
|
return abort(args);
|
||||||
} else {
|
} else {
|
||||||
return new PluginResult(PluginResult.Status.INVALID_ACTION);
|
return new PluginResult(PluginResult.Status.INVALID_ACTION);
|
||||||
}
|
}
|
||||||
@ -96,6 +145,7 @@ public class FileTransfer extends Plugin {
|
|||||||
* @param source Full path of the file on the file system
|
* @param source Full path of the file on the file system
|
||||||
* @param target URL of the server to receive the file
|
* @param target URL of the server to receive the file
|
||||||
* @param args JSON Array of args
|
* @param args JSON Array of args
|
||||||
|
* @param callbackId callback id for optional progress reports
|
||||||
*
|
*
|
||||||
* args[2] fileKey Name of file request parameter
|
* args[2] fileKey Name of file request parameter
|
||||||
* args[3] fileName File name to be used on server
|
* args[3] fileName File name to be used on server
|
||||||
@ -103,7 +153,7 @@ public class FileTransfer extends Plugin {
|
|||||||
* args[5] params key:value pairs of user-defined parameters
|
* args[5] params key:value pairs of user-defined parameters
|
||||||
* @return FileUploadResult containing result of upload request
|
* @return FileUploadResult containing result of upload request
|
||||||
*/
|
*/
|
||||||
private PluginResult upload(String source, String target, JSONArray args) {
|
private PluginResult upload(String source, String target, JSONArray args, String callbackId) {
|
||||||
Log.d(LOG_TAG, "upload " + source + " to " + target);
|
Log.d(LOG_TAG, "upload " + source + " to " + target);
|
||||||
|
|
||||||
HttpURLConnection conn = null;
|
HttpURLConnection conn = null;
|
||||||
@ -121,6 +171,7 @@ public class FileTransfer extends Plugin {
|
|||||||
if (headers == null && params != null) {
|
if (headers == null && params != null) {
|
||||||
headers = params.optJSONObject("headers");
|
headers = params.optJSONObject("headers");
|
||||||
}
|
}
|
||||||
|
String objectId = args.getString(9);
|
||||||
|
|
||||||
Log.d(LOG_TAG, "fileKey: " + fileKey);
|
Log.d(LOG_TAG, "fileKey: " + fileKey);
|
||||||
Log.d(LOG_TAG, "fileName: " + fileName);
|
Log.d(LOG_TAG, "fileName: " + fileName);
|
||||||
@ -129,12 +180,14 @@ public class FileTransfer extends Plugin {
|
|||||||
Log.d(LOG_TAG, "trustEveryone: " + trustEveryone);
|
Log.d(LOG_TAG, "trustEveryone: " + trustEveryone);
|
||||||
Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
|
Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
|
||||||
Log.d(LOG_TAG, "headers: " + headers);
|
Log.d(LOG_TAG, "headers: " + headers);
|
||||||
|
Log.d(LOG_TAG, "objectId: " + objectId);
|
||||||
|
|
||||||
// Create return object
|
// Create return object
|
||||||
FileUploadResult result = new FileUploadResult();
|
FileUploadResult result = new FileUploadResult();
|
||||||
|
FileProgressResult progress = new FileProgressResult();
|
||||||
|
|
||||||
// Get a input stream of the file on the phone
|
// Get a input stream of the file on the phone
|
||||||
FileInputStream fileInputStream = (FileInputStream) getPathFromUri(source);
|
InputStream inputStream = getPathFromUri(source);
|
||||||
|
|
||||||
DataOutputStream dos = null;
|
DataOutputStream dos = null;
|
||||||
|
|
||||||
@ -242,12 +295,18 @@ public class FileTransfer extends Plugin {
|
|||||||
|
|
||||||
int stringLength = extraBytes.length + midParams.length() + tailParams.length() + fileNameBytes.length;
|
int stringLength = extraBytes.length + midParams.length() + tailParams.length() + fileNameBytes.length;
|
||||||
Log.d(LOG_TAG, "String Length: " + stringLength);
|
Log.d(LOG_TAG, "String Length: " + stringLength);
|
||||||
int fixedLength = (int) fileInputStream.getChannel().size() + stringLength;
|
int fixedLength = -1;
|
||||||
|
if (inputStream instanceof FileInputStream) {
|
||||||
|
fixedLength = (int) ((FileInputStream)inputStream).getChannel().size() + stringLength;
|
||||||
|
progress.setLengthComputable(true);
|
||||||
|
progress.setTotal(fixedLength);
|
||||||
|
}
|
||||||
Log.d(LOG_TAG, "Content Length: " + fixedLength);
|
Log.d(LOG_TAG, "Content Length: " + fixedLength);
|
||||||
// setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices.
|
// setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices.
|
||||||
// http://code.google.com/p/android/issues/detail?id=3164
|
// http://code.google.com/p/android/issues/detail?id=3164
|
||||||
// It also causes OOM if HTTPS is used, even on newer devices.
|
// It also causes OOM if HTTPS is used, even on newer devices.
|
||||||
chunkedMode = chunkedMode && (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps);
|
chunkedMode = chunkedMode && (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps);
|
||||||
|
chunkedMode = chunkedMode || (fixedLength == -1);
|
||||||
|
|
||||||
if (chunkedMode) {
|
if (chunkedMode) {
|
||||||
conn.setChunkedStreamingMode(maxBufferSize);
|
conn.setChunkedStreamingMode(maxBufferSize);
|
||||||
@ -265,12 +324,12 @@ public class FileTransfer extends Plugin {
|
|||||||
dos.writeBytes(midParams);
|
dos.writeBytes(midParams);
|
||||||
|
|
||||||
// create a buffer of maximum size
|
// create a buffer of maximum size
|
||||||
bytesAvailable = fileInputStream.available();
|
bytesAvailable = inputStream.available();
|
||||||
bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
||||||
buffer = new byte[bufferSize];
|
buffer = new byte[bufferSize];
|
||||||
|
|
||||||
// read file and write it into form...
|
// read file and write it into form...
|
||||||
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
|
bytesRead = inputStream.read(buffer, 0, bufferSize);
|
||||||
totalBytes = 0;
|
totalBytes = 0;
|
||||||
|
|
||||||
long prevBytesRead = 0;
|
long prevBytesRead = 0;
|
||||||
@ -282,28 +341,36 @@ public class FileTransfer extends Plugin {
|
|||||||
prevBytesRead = totalBytes;
|
prevBytesRead = totalBytes;
|
||||||
Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes");
|
Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes");
|
||||||
}
|
}
|
||||||
bytesAvailable = fileInputStream.available();
|
bytesAvailable = inputStream.available();
|
||||||
bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
||||||
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
|
bytesRead = inputStream.read(buffer, 0, bufferSize);
|
||||||
|
if (objectId != null) {
|
||||||
|
// Only send progress callbacks if the JS code sent us an object ID,
|
||||||
|
// so we don't spam old versions with unrecognized callbacks.
|
||||||
|
progress.setLoaded(totalBytes);
|
||||||
|
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
|
||||||
|
progressResult.setKeepCallback(true);
|
||||||
|
success(progressResult, callbackId);
|
||||||
|
}
|
||||||
|
synchronized (abortTriggered) {
|
||||||
|
if (objectId != null && abortTriggered.contains(objectId)) {
|
||||||
|
abortTriggered.remove(objectId);
|
||||||
|
throw new AbortException("upload aborted");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// send multipart form data necessary after file data...
|
// send multipart form data necessary after file data...
|
||||||
dos.writeBytes(tailParams);
|
dos.writeBytes(tailParams);
|
||||||
|
|
||||||
// close streams
|
// close streams
|
||||||
fileInputStream.close();
|
inputStream.close();
|
||||||
dos.flush();
|
dos.flush();
|
||||||
dos.close();
|
dos.close();
|
||||||
|
|
||||||
//------------------ read the SERVER RESPONSE
|
//------------------ read the SERVER RESPONSE
|
||||||
StringBuffer responseString = new StringBuffer("");
|
StringBuffer responseString = new StringBuffer("");
|
||||||
DataInputStream inStream;
|
DataInputStream inStream = new DataInputStream(getInputStream(conn));
|
||||||
try {
|
|
||||||
inStream = new DataInputStream ( conn.getInputStream() );
|
|
||||||
} catch(FileNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, e.toString(), e);
|
|
||||||
throw new IOException("Received error from server");
|
|
||||||
}
|
|
||||||
|
|
||||||
String line;
|
String line;
|
||||||
while (( line = inStream.readLine()) != null) {
|
while (( line = inStream.readLine()) != null) {
|
||||||
@ -342,6 +409,9 @@ public class FileTransfer extends Plugin {
|
|||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.e(LOG_TAG, e.getMessage(), e);
|
Log.e(LOG_TAG, e.getMessage(), e);
|
||||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||||
|
} catch (AbortException e) {
|
||||||
|
JSONObject error = createFileTransferError(ABORTED_ERR, source, target, conn);
|
||||||
|
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
// Shouldn't happen, but will
|
// Shouldn't happen, but will
|
||||||
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
|
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
|
||||||
@ -354,6 +424,13 @@ public class FileTransfer extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private InputStream getInputStream(HttpURLConnection conn) throws IOException {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
return new DoneHandlerInputStream(conn.getInputStream());
|
||||||
|
}
|
||||||
|
return conn.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
// always verify the host - don't check for certificate
|
// always verify the host - don't check for certificate
|
||||||
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
|
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
@ -459,11 +536,13 @@ public class FileTransfer extends Plugin {
|
|||||||
* @param target Full path of the file on the file system
|
* @param target Full path of the file on the file system
|
||||||
* @return JSONObject the downloaded file
|
* @return JSONObject the downloaded file
|
||||||
*/
|
*/
|
||||||
private PluginResult download(String source, String target, boolean trustEveryone) {
|
private PluginResult download(String source, String target, JSONArray args, String callbackId) {
|
||||||
Log.d(LOG_TAG, "download " + source + " to " + target);
|
Log.d(LOG_TAG, "download " + source + " to " + target);
|
||||||
|
|
||||||
HttpURLConnection connection = null;
|
HttpURLConnection connection = null;
|
||||||
try {
|
try {
|
||||||
|
boolean trustEveryone = args.optBoolean(2);
|
||||||
|
String objectId = args.getString(3);
|
||||||
File file = getFileFromPath(target);
|
File file = getFileFromPath(target);
|
||||||
|
|
||||||
// create needed directories
|
// create needed directories
|
||||||
@ -513,22 +592,39 @@ public class FileTransfer extends Plugin {
|
|||||||
connection.connect();
|
connection.connect();
|
||||||
|
|
||||||
Log.d(LOG_TAG, "Download file:" + url);
|
Log.d(LOG_TAG, "Download file:" + url);
|
||||||
InputStream inputStream;
|
InputStream inputStream = getInputStream(connection);
|
||||||
try {
|
|
||||||
inputStream = connection.getInputStream();
|
|
||||||
} catch(FileNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, e.toString(), e);
|
|
||||||
throw new IOException("Received error from server");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] buffer = new byte[1024];
|
byte[] buffer = new byte[1024];
|
||||||
int bytesRead = 0;
|
int bytesRead = 0;
|
||||||
|
long totalBytes = 0;
|
||||||
|
FileProgressResult progress = new FileProgressResult();
|
||||||
|
|
||||||
|
if (connection.getContentEncoding() == null) {
|
||||||
|
// Only trust content-length header if no gzip etc
|
||||||
|
progress.setLengthComputable(true);
|
||||||
|
progress.setTotal(connection.getContentLength());
|
||||||
|
}
|
||||||
|
|
||||||
FileOutputStream outputStream = new FileOutputStream(file);
|
FileOutputStream outputStream = new FileOutputStream(file);
|
||||||
|
|
||||||
// write bytes to file
|
// write bytes to file
|
||||||
while ((bytesRead = inputStream.read(buffer)) > 0) {
|
while ((bytesRead = inputStream.read(buffer)) > 0) {
|
||||||
outputStream.write(buffer, 0, bytesRead);
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
totalBytes += bytesRead;
|
||||||
|
if (objectId != null) {
|
||||||
|
// Only send progress callbacks if the JS code sent us an object ID,
|
||||||
|
// so we don't spam old versions with unrecognized callbacks.
|
||||||
|
progress.setLoaded(totalBytes);
|
||||||
|
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
|
||||||
|
progressResult.setKeepCallback(true);
|
||||||
|
success(progressResult, callbackId);
|
||||||
|
}
|
||||||
|
synchronized (abortTriggered) {
|
||||||
|
if (objectId != null && abortTriggered.contains(objectId)) {
|
||||||
|
abortTriggered.remove(objectId);
|
||||||
|
throw new AbortException("download aborted");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
@ -554,6 +650,9 @@ public class FileTransfer extends Plugin {
|
|||||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (AbortException e) {
|
||||||
|
JSONObject error = createFileTransferError(ABORTED_ERR, source, target, connection);
|
||||||
|
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection);
|
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection);
|
||||||
Log.d(LOG_TAG, "I got a file not found exception");
|
Log.d(LOG_TAG, "I got a file not found exception");
|
||||||
@ -621,4 +720,23 @@ public class FileTransfer extends Plugin {
|
|||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abort an ongoing upload or download.
|
||||||
|
*
|
||||||
|
* @param args args
|
||||||
|
*/
|
||||||
|
private PluginResult abort(JSONArray args) {
|
||||||
|
String objectId;
|
||||||
|
try {
|
||||||
|
objectId = args.getString(0);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.d(LOG_TAG, "Missing objectId");
|
||||||
|
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing objectId");
|
||||||
|
}
|
||||||
|
synchronized (abortTriggered) {
|
||||||
|
abortTriggered.add(objectId);
|
||||||
|
}
|
||||||
|
return new PluginResult(PluginResult.Status.OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ public class FileUploadResult {
|
|||||||
private long bytesSent = 0; // bytes sent
|
private long bytesSent = 0; // bytes sent
|
||||||
private int responseCode = -1; // HTTP response code
|
private int responseCode = -1; // HTTP response code
|
||||||
private String response = null; // HTTP response
|
private String response = null; // HTTP response
|
||||||
|
private String objectId = null; // FileTransfer object id
|
||||||
|
|
||||||
public long getBytesSent() {
|
public long getBytesSent() {
|
||||||
return bytesSent;
|
return bytesSent;
|
||||||
@ -54,10 +55,19 @@ public class FileUploadResult {
|
|||||||
this.response = response;
|
this.response = response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getObjectId() {
|
||||||
|
return objectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setObjectId(String objectId) {
|
||||||
|
this.objectId = objectId;
|
||||||
|
}
|
||||||
|
|
||||||
public JSONObject toJSONObject() throws JSONException {
|
public JSONObject toJSONObject() throws JSONException {
|
||||||
return new JSONObject(
|
return new JSONObject(
|
||||||
"{bytesSent:" + bytesSent +
|
"{bytesSent:" + bytesSent +
|
||||||
",responseCode:" + responseCode +
|
",responseCode:" + responseCode +
|
||||||
",response:" + JSONObject.quote(response) + "}");
|
",response:" + JSONObject.quote(response) +
|
||||||
|
",objectId:" + JSONObject.quote(objectId) + "}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1048,10 +1048,16 @@ public class FileUtils extends Plugin {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
protected static String getRealPathFromURI(Uri contentUri, CordovaInterface cordova) {
|
protected static String getRealPathFromURI(Uri contentUri, CordovaInterface cordova) {
|
||||||
String[] proj = { _DATA };
|
String uri = contentUri.toString();
|
||||||
Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null);
|
if (uri.startsWith("content:")) {
|
||||||
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
String[] proj = { _DATA };
|
||||||
cursor.moveToFirst();
|
Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null);
|
||||||
return cursor.getString(column_index);
|
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
return cursor.getString(column_index);
|
||||||
|
} else {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,10 +135,22 @@ public class GeoBroker extends Plugin {
|
|||||||
* Stop listener.
|
* Stop listener.
|
||||||
*/
|
*/
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
this.networkListener.destroy();
|
if (this.networkListener != null) {
|
||||||
this.gpsListener.destroy();
|
this.networkListener.destroy();
|
||||||
this.networkListener = null;
|
this.networkListener = null;
|
||||||
this.gpsListener = 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) {
|
public JSONObject returnLocationJSON(Location loc) {
|
||||||
|
579
framework/src/org/apache/cordova/Globalization.java
Normal file
579
framework/src/org/apache/cordova/Globalization.java
Normal file
@ -0,0 +1,579 @@
|
|||||||
|
/*
|
||||||
|
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.Plugin;
|
||||||
|
import org.apache.cordova.api.PluginResult;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.text.format.Time;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Globalization extends Plugin {
|
||||||
|
//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 PluginResult execute(String action, JSONArray data, String callbackId) {
|
||||||
|
PluginResult.Status status = PluginResult.Status.OK;
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
|
||||||
|
try{
|
||||||
|
if (action.equals(GETLOCALENAME)){
|
||||||
|
obj = getLocaleName();
|
||||||
|
return new PluginResult(status, obj);
|
||||||
|
}else if (action.equals(GETPREFERREDLANGUAGE)){
|
||||||
|
obj = getPreferredLanguage();
|
||||||
|
return new PluginResult(status, obj);
|
||||||
|
} else if (action.equalsIgnoreCase(DATETOSTRING)) {
|
||||||
|
obj = getDateToString(data);
|
||||||
|
return new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
}else if(action.equalsIgnoreCase(STRINGTODATE)){
|
||||||
|
obj = getStringtoDate(data);
|
||||||
|
return new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
}else if(action.equalsIgnoreCase(GETDATEPATTERN)){
|
||||||
|
obj = getDatePattern(data);
|
||||||
|
return new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
}else if(action.equalsIgnoreCase(GETDATENAMES)){
|
||||||
|
obj = getDateNames(data);
|
||||||
|
return new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
}else if(action.equalsIgnoreCase(ISDAYLIGHTSAVINGSTIME)){
|
||||||
|
obj = getIsDayLightSavingsTime(data);
|
||||||
|
return new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
}else if(action.equalsIgnoreCase(GETFIRSTDAYOFWEEK)){
|
||||||
|
obj = getFirstDayOfWeek(data);
|
||||||
|
return new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
}else if(action.equalsIgnoreCase(NUMBERTOSTRING)){
|
||||||
|
obj = getNumberToString(data);
|
||||||
|
return new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
}else if(action.equalsIgnoreCase(STRINGTONUMBER)){
|
||||||
|
obj = getStringToNumber(data);
|
||||||
|
return new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
}else if(action.equalsIgnoreCase(GETNUMBERPATTERN)){
|
||||||
|
obj = getNumberPattern(data);
|
||||||
|
return new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
}else if(action.equalsIgnoreCase(GETCURRENCYPATTERN)){
|
||||||
|
obj = getCurrencyPattern(data);
|
||||||
|
return new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
}
|
||||||
|
}catch (GlobalizationError ge){
|
||||||
|
return new PluginResult(PluginResult.Status.ERROR, ge.toJson());
|
||||||
|
}catch (Exception e){
|
||||||
|
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||||
|
}
|
||||||
|
return new PluginResult(PluginResult.Status.INVALID_ACTION);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* @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)
|
||||||
|
if (options.getJSONObject(0).length() > 1){
|
||||||
|
//options were included
|
||||||
|
|
||||||
|
//get formatLength option
|
||||||
|
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(FORMATLENGTH)){
|
||||||
|
String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(FORMATLENGTH);
|
||||||
|
if (fmtOpt.equalsIgnoreCase(MEDIUM)){//medium
|
||||||
|
fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getMediumDateFormat(this.cordova.getActivity());
|
||||||
|
}else if (fmtOpt.equalsIgnoreCase(LONG) || fmtOpt.equalsIgnoreCase(FULL)){ //long/full
|
||||||
|
fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getLongDateFormat(this.cordova.getActivity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//return pattern type
|
||||||
|
fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern();
|
||||||
|
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(SELECTOR)){
|
||||||
|
String selOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(SELECTOR);
|
||||||
|
if (selOpt.equalsIgnoreCase(DATE)){
|
||||||
|
fmt = fmtDate.toLocalizedPattern();
|
||||||
|
}else if (selOpt.equalsIgnoreCase(TIME)){
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
108
framework/src/org/apache/cordova/GlobalizationError.java
Normal file
108
framework/src/org/apache/cordova/GlobalizationError.java
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Exception class representing defined Globalization error codes
|
||||||
|
* @Globalization error codes:
|
||||||
|
* GlobalizationError.UNKNOWN_ERROR = 0;
|
||||||
|
* GlobalizationError.FORMATTING_ERROR = 1;
|
||||||
|
* GlobalizationError.PARSING_ERROR = 2;
|
||||||
|
* GlobalizationError.PATTERN_ERROR = 3;
|
||||||
|
*/
|
||||||
|
public class GlobalizationError extends Exception{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
public static final String UNKNOWN_ERROR = "UNKNOWN_ERROR";
|
||||||
|
public static final String FORMATTING_ERROR = "FORMATTING_ERROR";
|
||||||
|
public static final String PARSING_ERROR = "PARSING_ERROR";
|
||||||
|
public static final String PATTERN_ERROR = "PATTERN_ERROR";
|
||||||
|
|
||||||
|
int error = 0; //default unknown error thrown
|
||||||
|
/**
|
||||||
|
* Default constructor
|
||||||
|
*/
|
||||||
|
public GlobalizationError() {}
|
||||||
|
/**
|
||||||
|
* Create an exception returning an error code
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
*/
|
||||||
|
public GlobalizationError(String s) {
|
||||||
|
if (s.equalsIgnoreCase(FORMATTING_ERROR)){
|
||||||
|
error = 1;
|
||||||
|
}else if (s.equalsIgnoreCase(PARSING_ERROR)){
|
||||||
|
error = 2;
|
||||||
|
}else if (s.equalsIgnoreCase(PATTERN_ERROR)){
|
||||||
|
error = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* get error string based on error code
|
||||||
|
*
|
||||||
|
* @param String msg
|
||||||
|
*/
|
||||||
|
public String getErrorString(){
|
||||||
|
String msg = "";
|
||||||
|
switch (error){
|
||||||
|
case 0:
|
||||||
|
msg = UNKNOWN_ERROR;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
msg = FORMATTING_ERROR;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
msg = PARSING_ERROR;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
msg = PATTERN_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* get error code
|
||||||
|
*
|
||||||
|
* @param String msg
|
||||||
|
*/
|
||||||
|
public int getErrorCode(){
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the json version of this object to return to javascript
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public JSONObject toJson() {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
try {
|
||||||
|
obj.put("code", getErrorCode());
|
||||||
|
obj.put("message", getErrorString());
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// never happens
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
@ -36,7 +36,7 @@ public class NativeToJsMessageQueue {
|
|||||||
private static final String LOG_TAG = "JsMessageQueue";
|
private static final String LOG_TAG = "JsMessageQueue";
|
||||||
|
|
||||||
// This must match the default value in incubator-cordova-js/lib/android/exec.js
|
// This must match the default value in incubator-cordova-js/lib/android/exec.js
|
||||||
private static final int DEFAULT_BRIDGE_MODE = 1;
|
private static final int DEFAULT_BRIDGE_MODE = 3;
|
||||||
|
|
||||||
// Set this to true to force plugin results to be encoding as
|
// Set this to true to force plugin results to be encoding as
|
||||||
// JS instead of the custom format (useful for benchmarking).
|
// JS instead of the custom format (useful for benchmarking).
|
||||||
@ -80,12 +80,11 @@ public class NativeToJsMessageQueue {
|
|||||||
public NativeToJsMessageQueue(CordovaWebView webView, CordovaInterface cordova) {
|
public NativeToJsMessageQueue(CordovaWebView webView, CordovaInterface cordova) {
|
||||||
this.cordova = cordova;
|
this.cordova = cordova;
|
||||||
this.webView = webView;
|
this.webView = webView;
|
||||||
registeredListeners = new BridgeMode[5];
|
registeredListeners = new BridgeMode[4];
|
||||||
registeredListeners[0] = null; // Polling. Requires no logic.
|
registeredListeners[0] = null; // Polling. Requires no logic.
|
||||||
registeredListeners[1] = new CallbackBridgeMode();
|
registeredListeners[1] = new LoadUrlBridgeMode();
|
||||||
registeredListeners[2] = new LoadUrlBridgeMode();
|
registeredListeners[2] = new OnlineEventsBridgeMode();
|
||||||
registeredListeners[3] = new OnlineEventsBridgeMode();
|
registeredListeners[3] = new PrivateApiBridgeMode();
|
||||||
registeredListeners[4] = new PrivateApiBridgeMode();
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,15 +269,6 @@ public class NativeToJsMessageQueue {
|
|||||||
void onNativeToJsMessageAvailable();
|
void onNativeToJsMessageAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Uses a local server to send messages to JS via an XHR */
|
|
||||||
private class CallbackBridgeMode implements BridgeMode {
|
|
||||||
public void onNativeToJsMessageAvailable() {
|
|
||||||
if (webView.callbackServer != null) {
|
|
||||||
webView.callbackServer.onNativeToJsMessageAvailable(NativeToJsMessageQueue.this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Uses webView.loadUrl("javascript:") to execute messages. */
|
/** Uses webView.loadUrl("javascript:") to execute messages. */
|
||||||
private class LoadUrlBridgeMode implements BridgeMode {
|
private class LoadUrlBridgeMode implements BridgeMode {
|
||||||
final Runnable runnable = new Runnable() {
|
final Runnable runnable = new Runnable() {
|
||||||
|
@ -69,6 +69,7 @@ public class NetworkManager extends Plugin {
|
|||||||
private static final String LOG_TAG = "NetworkManager";
|
private static final String LOG_TAG = "NetworkManager";
|
||||||
|
|
||||||
private String connectionCallbackId;
|
private String connectionCallbackId;
|
||||||
|
private boolean registered = false;
|
||||||
|
|
||||||
ConnectivityManager sockMan;
|
ConnectivityManager sockMan;
|
||||||
BroadcastReceiver receiver;
|
BroadcastReceiver receiver;
|
||||||
@ -99,10 +100,13 @@ public class NetworkManager extends Plugin {
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
|
// (The null check is for the ARM Emulator, please use Intel Emulator for better results)
|
||||||
|
if(webView != null)
|
||||||
|
updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cordova.getActivity().registerReceiver(this.receiver, intentFilter);
|
cordova.getActivity().registerReceiver(this.receiver, intentFilter);
|
||||||
|
this.registered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -144,15 +148,23 @@ public class NetworkManager extends Plugin {
|
|||||||
* Stop network receiver.
|
* Stop network receiver.
|
||||||
*/
|
*/
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
if (this.receiver != null) {
|
if (this.receiver != null && this.registered) {
|
||||||
try {
|
try {
|
||||||
this.cordova.getActivity().unregisterReceiver(this.receiver);
|
this.cordova.getActivity().unregisterReceiver(this.receiver);
|
||||||
|
this.registered = false;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
|
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the network receiver on navigation.
|
||||||
|
*/
|
||||||
|
public void onReset() {
|
||||||
|
this.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// LOCAL METHODS
|
// LOCAL METHODS
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@ -198,10 +210,9 @@ public class NetworkManager extends Plugin {
|
|||||||
result.setKeepCallback(true);
|
result.setKeepCallback(true);
|
||||||
this.success(result, this.connectionCallbackId);
|
this.success(result, this.connectionCallbackId);
|
||||||
|
|
||||||
// Send to all plugins
|
|
||||||
webView.postMessage("networkconnection", type);
|
webView.postMessage("networkconnection", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the type of connection
|
* Determine the type of connection
|
||||||
*
|
*
|
||||||
|
@ -115,6 +115,13 @@ public class Storage extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up on navigation/refresh.
|
||||||
|
*/
|
||||||
|
public void onReset() {
|
||||||
|
this.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// LOCAL METHODS
|
// LOCAL METHODS
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
@ -82,6 +82,13 @@ public class TempListener extends Plugin implements SensorEventListener {
|
|||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on navigation.
|
||||||
|
*/
|
||||||
|
public void onReset() {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// LOCAL METHODS
|
// LOCAL METHODS
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
@ -22,6 +22,8 @@ import android.app.Activity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Cordova activity abstract class that is extended by DroidGap.
|
* The Cordova activity abstract class that is extended by DroidGap.
|
||||||
* It is used to isolate plugin development, and remove dependency on entire Cordova library.
|
* It is used to isolate plugin development, and remove dependency on entire Cordova library.
|
||||||
@ -67,5 +69,9 @@ public interface CordovaInterface {
|
|||||||
* @return Object or null
|
* @return Object or null
|
||||||
*/
|
*/
|
||||||
public Object onMessage(String id, Object data);
|
public Object onMessage(String id, Object data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a shared thread pool that can be used for background tasks.
|
||||||
|
*/
|
||||||
|
public ExecutorService getThreadPool();
|
||||||
}
|
}
|
||||||
|
@ -116,4 +116,11 @@ public interface IPlugin {
|
|||||||
* @return Return true to prevent the URL from loading. Default is false.
|
* @return Return true to prevent the URL from loading. Default is false.
|
||||||
*/
|
*/
|
||||||
boolean onOverrideUrlLoading(String url);
|
boolean onOverrideUrlLoading(String url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the WebView does a top-level navigation or refreshes.
|
||||||
|
*
|
||||||
|
* Plugins should stop any long-running processes and clean up internal state.
|
||||||
|
*/
|
||||||
|
void onReset();
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ import android.content.res.AssetManager;
|
|||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public class LegacyContext implements CordovaInterface {
|
public class LegacyContext implements CordovaInterface {
|
||||||
private static final String LOG_TAG = "Deprecation Notice";
|
private static final String LOG_TAG = "Deprecation Notice";
|
||||||
@ -145,4 +147,10 @@ public class LegacyContext implements CordovaInterface {
|
|||||||
Log.i(LOG_TAG, "Replace ctx.unbindService() with cordova.getActivity().unbindService()");
|
Log.i(LOG_TAG, "Replace ctx.unbindService() with cordova.getActivity().unbindService()");
|
||||||
this.cordova.getActivity().unbindService(conn);
|
this.cordova.getActivity().unbindService(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecutorService getThreadPool() {
|
||||||
|
Log.i(LOG_TAG, "Replace ctx.getThreadPool() with cordova.getThreadPool()");
|
||||||
|
return this.cordova.getThreadPool();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin interface must be implemented by any plugin classes.
|
* Plugin interface must be implemented by any plugin classes.
|
||||||
*
|
*
|
||||||
@ -215,4 +217,14 @@ public abstract class Plugin implements IPlugin {
|
|||||||
public void error(String message, String callbackId) {
|
public void error(String message, String callbackId) {
|
||||||
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
|
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the WebView does a top-level navigation or refreshes.
|
||||||
|
*
|
||||||
|
* Plugins should stop any long-running processes and clean up internal state.
|
||||||
|
*
|
||||||
|
* Does nothing by default.
|
||||||
|
*/
|
||||||
|
public void onReset() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,6 @@ public class PluginManager {
|
|||||||
|
|
||||||
private final CordovaInterface ctx;
|
private final CordovaInterface ctx;
|
||||||
private final CordovaWebView app;
|
private final CordovaWebView app;
|
||||||
private final ExecutorService execThreadPool = Executors.newCachedThreadPool();
|
|
||||||
|
|
||||||
// Flag to track first time through
|
// Flag to track first time through
|
||||||
private boolean firstRun;
|
private boolean firstRun;
|
||||||
@ -210,23 +209,19 @@ public class PluginManager {
|
|||||||
* this is an async plugin call.
|
* this is an async plugin call.
|
||||||
* @param args An Array literal string containing any arguments needed in the
|
* @param args An Array literal string containing any arguments needed in the
|
||||||
* plugin execute method.
|
* plugin execute method.
|
||||||
* @param async Boolean indicating whether the calling JavaScript code is expecting an
|
|
||||||
* immediate return value. If true, either Cordova.callbackSuccess(...) or
|
|
||||||
* Cordova.callbackError(...) is called once the plugin code has executed.
|
|
||||||
* @return Whether the task completed synchronously.
|
* @return Whether the task completed synchronously.
|
||||||
*/
|
*/
|
||||||
public boolean exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async) {
|
public boolean exec(final String service, final String action, final String callbackId, final String jsonArgs) {
|
||||||
PluginResult cr = null;
|
PluginResult cr = null;
|
||||||
boolean runAsync = async;
|
final IPlugin plugin = this.getPlugin(service);
|
||||||
|
boolean runAsync = !plugin.isSynch(action);
|
||||||
try {
|
try {
|
||||||
final JSONArray args = new JSONArray(jsonArgs);
|
final JSONArray args = new JSONArray(jsonArgs);
|
||||||
final IPlugin plugin = this.getPlugin(service);
|
|
||||||
//final CordovaInterface ctx = this.ctx;
|
//final CordovaInterface ctx = this.ctx;
|
||||||
if (plugin != null) {
|
if (plugin != null) {
|
||||||
runAsync = async && !plugin.isSynch(action);
|
|
||||||
if (runAsync) {
|
if (runAsync) {
|
||||||
// Run this on a different thread so that this one can return back to JS
|
// Run this on a different thread so that this one can return back to JS
|
||||||
execThreadPool.execute(new Runnable() {
|
ctx.getThreadPool().execute(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
// Call execute on the plugin so that it can do it's thing
|
// Call execute on the plugin so that it can do it's thing
|
||||||
@ -267,6 +262,11 @@ public class PluginManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public boolean exec(String service, String action, String callbackId, String jsonArgs, boolean async) {
|
||||||
|
return exec(service, action, callbackId, jsonArgs);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the plugin object that implements the service.
|
* Get the plugin object that implements the service.
|
||||||
* If the plugin object does not already exist, then create it.
|
* If the plugin object does not already exist, then create it.
|
||||||
@ -397,6 +397,20 @@ public class PluginManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the app navigates or refreshes.
|
||||||
|
*/
|
||||||
|
public void onReset() {
|
||||||
|
Iterator<PluginEntry> it = this.entries.values().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
IPlugin plugin = it.next().plugin;
|
||||||
|
if (plugin != null) {
|
||||||
|
plugin.onReset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void pluginConfigurationMissing() {
|
private void pluginConfigurationMissing() {
|
||||||
LOG.e(TAG, "=====================================================================================");
|
LOG.e(TAG, "=====================================================================================");
|
||||||
LOG.e(TAG, "ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");
|
LOG.e(TAG, "ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");
|
||||||
|
Loading…
Reference in New Issue
Block a user