mirror of
https://github.com/apache/cordova-android.git
synced 2026-01-30 00:05:28 +08:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
909e6645e7 | ||
|
|
a308100288 | ||
|
|
fea8c5d204 | ||
|
|
a890c8156e | ||
|
|
665c1bd8bb | ||
|
|
b22990ca78 | ||
|
|
832998b67f | ||
|
|
c798d131bb | ||
|
|
bf3e024648 | ||
|
|
191f31baa7 | ||
|
|
d3b7903af8 | ||
|
|
99e7d1e161 | ||
|
|
b13166f5d9 | ||
|
|
80fe4458c6 | ||
|
|
791574c26e | ||
|
|
ac61ebf2d5 | ||
|
|
cb99ed0a01 | ||
|
|
4864d52966 | ||
|
|
b2d61679fb | ||
|
|
383b3dadd5 | ||
|
|
c65c259123 | ||
|
|
e7e2730929 | ||
|
|
bb9615eed0 | ||
|
|
18877bf80e | ||
|
|
778b784eb6 | ||
|
|
5ff900f7ec | ||
|
|
ba31424604 | ||
|
|
1782111d45 | ||
|
|
1fa63300aa | ||
|
|
b42c918973 | ||
|
|
f12262ea96 | ||
|
|
334cf45d6d | ||
|
|
b7bb72294a | ||
|
|
64ff204371 | ||
|
|
282367c6d5 | ||
|
|
36c33a5889 | ||
|
|
5ee7e81ff9 | ||
|
|
f4859444dd | ||
|
|
31bc015cdd | ||
|
|
2bf6509e1d |
@@ -25,8 +25,11 @@ set -e
|
||||
|
||||
if [ -z "$1" ] || [ "$1" == "-h" ]
|
||||
then
|
||||
echo 'usage: create path package activity'
|
||||
echo "Usage: $0 <path_to_new_project> <package_name> <project_name>"
|
||||
echo "Make sure the Android SDK tools folder is in your PATH!"
|
||||
echo " <path_to_new_project>: Path to your new Cordova iOS project"
|
||||
echo " <package_name>: Package name, following reverse-domain style convention"
|
||||
echo " <project_name>: Project name"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
@@ -16,17 +16,37 @@
|
||||
:: under the License.
|
||||
|
||||
@ECHO OFF
|
||||
IF NOT DEFINED JAVA_HOME GOTO MISSING
|
||||
IF NOT DEFINED JAVA_HOME GOTO MISSING_JAVA_HOME
|
||||
|
||||
FOR %%X in (java.exe javac.exe ant.bat android.bat) do (
|
||||
SET FOUND=%%~$PATH:X
|
||||
IF NOT DEFINED FOUND GOTO MISSING
|
||||
IF [%%~$PATH:X]==[] (
|
||||
ECHO Cannot locate %%X using the PATH environment variable.
|
||||
ECHO Retry after adding directory containing %%X to the PATH variable.
|
||||
ECHO Remember to open a new command window after updating the PATH variable.
|
||||
IF "%%X"=="java.exe" GOTO GET_JAVA
|
||||
IF "%%X"=="javac.exe" GOTO GET_JAVA
|
||||
IF "%%X"=="ant.bat" GOTO GET_ANT
|
||||
IF "%%X"=="android.bat" GOTO GET_ANDROID
|
||||
GOTO ERROR
|
||||
)
|
||||
)
|
||||
cscript "%~dp0\create.js" %*
|
||||
GOTO END
|
||||
:MISSING
|
||||
ECHO Missing one of the following:
|
||||
ECHO JDK: http://java.oracle.com
|
||||
ECHO Android SDK: http://developer.android.com
|
||||
ECHO Apache ant: http://ant.apache.org
|
||||
:MISSING_JAVA_HOME
|
||||
ECHO The JAVA_HOME environment variable is not set.
|
||||
ECHO Set JAVA_HOME to an existing JRE directory.
|
||||
ECHO Remember to also add JAVA_HOME to the PATH variable.
|
||||
ECHO After updating system variables, open a new command window and retry.
|
||||
GOTO ERROR
|
||||
:GET_JAVA
|
||||
ECHO Visit http://java.oracle.com if you need to install Java (JDK).
|
||||
GOTO ERROR
|
||||
:GET_ANT
|
||||
ECHO Visit http://ant.apache.org if you need to install Apache Ant.
|
||||
GOTO ERROR
|
||||
:GET_ANDROID
|
||||
ECHO Visit http://developer.android.com if you need to install the Android SDK.
|
||||
GOTO ERROR
|
||||
:ERROR
|
||||
EXIT /B 1
|
||||
:END
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
|
||||
<link rel="stylesheet" type="text/css" href="css/index.css" />
|
||||
@@ -33,7 +33,7 @@
|
||||
<p class="event received">Device is Ready</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="cordova-2.5.0.js"></script>
|
||||
<script type="text/javascript" src="cordova-2.7.0.js"></script>
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
<script type="text/javascript">
|
||||
app.initialize();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="cordova-2.5.0.js"></script>
|
||||
<script src="cordova-2.7.0.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
||||
import android.net.Uri;
|
||||
@@ -312,10 +313,12 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI || destType == NATIVE_URI) {
|
||||
if (!this.saveToPhotoAlbum) {
|
||||
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
|
||||
if (this.saveToPhotoAlbum) {
|
||||
Uri inputUri = getUriFromMediaStore();
|
||||
//Just because we have a media URI doesn't mean we have a real file, we need to make it
|
||||
uri = Uri.fromFile(new File(FileHelper.getRealPath(inputUri, this.cordova)));
|
||||
} else {
|
||||
uri = getUriFromMediaStore();
|
||||
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
@@ -394,19 +397,21 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
(destType == FILE_URI || destType == NATIVE_URI) && !this.correctOrientation) {
|
||||
this.callbackContext.success(uri.toString());
|
||||
} else {
|
||||
String uriString = uri.toString();
|
||||
// Get the path to the image. Makes loading so much easier.
|
||||
String imagePath = FileHelper.getRealPath(uri, this.cordova);
|
||||
String mimeType = FileHelper.getMimeType(imagePath, this.cordova);
|
||||
// Log.d(LOG_TAG, "Real path = " + imagePath);
|
||||
// Log.d(LOG_TAG, "mime type = " + mimeType);
|
||||
String mimeType = FileHelper.getMimeType(uriString, this.cordova);
|
||||
// If we don't have a valid image so quit.
|
||||
if (imagePath == null || mimeType == null ||
|
||||
!(mimeType.equalsIgnoreCase("image/jpeg") || mimeType.equalsIgnoreCase("image/png"))) {
|
||||
if (!("image/jpeg".equalsIgnoreCase(mimeType) || "image/png".equalsIgnoreCase(mimeType))) {
|
||||
Log.d(LOG_TAG, "I either have a null image path or bitmap");
|
||||
this.failPicture("Unable to retrieve path to picture!");
|
||||
return;
|
||||
}
|
||||
Bitmap bitmap = getScaledBitmap(imagePath);
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
bitmap = getScaledBitmap(uriString);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (bitmap == null) {
|
||||
Log.d(LOG_TAG, "I either have a null image path or bitmap");
|
||||
this.failPicture("Unable to create bitmap!");
|
||||
@@ -414,14 +419,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
|
||||
if (this.correctOrientation) {
|
||||
String[] cols = { MediaStore.Images.Media.ORIENTATION };
|
||||
Cursor cursor = this.cordova.getActivity().getContentResolver().query(intent.getData(),
|
||||
cols, null, null, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToPosition(0);
|
||||
rotate = cursor.getInt(0);
|
||||
cursor.close();
|
||||
}
|
||||
rotate = getImageOrientation(uri);
|
||||
if (rotate != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(rotate);
|
||||
@@ -441,15 +439,17 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
try {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
|
||||
// Some content: URIs do not map to file paths (e.g. picasa).
|
||||
String realPath = FileHelper.getRealPath(uri, this.cordova);
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(resizePath);
|
||||
if (realPath != null && this.encodingType == JPEG) {
|
||||
try {
|
||||
exif.createInFile(realPath);
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
OutputStream os = new FileOutputStream(resizePath);
|
||||
@@ -457,8 +457,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
os.close();
|
||||
|
||||
// Restore exif data to file
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createOutFile(FileHelper.getRealPath(uri, this.cordova));
|
||||
if (realPath != null && this.encodingType == JPEG) {
|
||||
exif.createOutFile(resizePath);
|
||||
exif.writeExifData();
|
||||
}
|
||||
|
||||
@@ -491,6 +491,19 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
}
|
||||
|
||||
private int getImageOrientation(Uri uri) {
|
||||
String[] cols = { MediaStore.Images.Media.ORIENTATION };
|
||||
Cursor cursor = cordova.getActivity().getContentResolver().query(uri,
|
||||
cols, null, null, null);
|
||||
int rotate = 0;
|
||||
if (cursor != null) {
|
||||
cursor.moveToPosition(0);
|
||||
rotate = cursor.getInt(0);
|
||||
cursor.close();
|
||||
}
|
||||
return rotate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out if the bitmap should be rotated. For instance if the picture was taken in
|
||||
* portrait mode
|
||||
@@ -561,17 +574,18 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
*
|
||||
* @param imagePath
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private Bitmap getScaledBitmap(String imagePath) {
|
||||
private Bitmap getScaledBitmap(String imageUrl) throws IOException {
|
||||
// If no new width or height were specified return the original bitmap
|
||||
if (this.targetWidth <= 0 && this.targetHeight <= 0) {
|
||||
return BitmapFactory.decodeFile(imagePath);
|
||||
return BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova));
|
||||
}
|
||||
|
||||
// figure out the original width and height of the image
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(imagePath, options);
|
||||
BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
|
||||
|
||||
//CB-2292: WTF? Why is the width null?
|
||||
if(options.outWidth == 0 || options.outHeight == 0)
|
||||
@@ -585,7 +599,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// Load in the smallest bitmap possible that is closest to the size we want
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
|
||||
Bitmap unscaledBitmap = BitmapFactory.decodeFile(imagePath, options);
|
||||
Bitmap unscaledBitmap = BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
|
||||
if (unscaledBitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ public class Config {
|
||||
LOG.i("CordovaLog", "Found start page location: %s", src);
|
||||
|
||||
if (src != null) {
|
||||
Pattern schemeRegex = Pattern.compile("^[a-z]+://");
|
||||
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
|
||||
Matcher matcher = schemeRegex.matcher(src);
|
||||
if (matcher.find()) {
|
||||
startUrl = src;
|
||||
@@ -220,19 +220,33 @@ public class Config {
|
||||
} else { // specific access
|
||||
// check if subdomains should be included
|
||||
// TODO: we should not add more domains if * has already been added
|
||||
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
|
||||
Matcher matcher = schemeRegex.matcher(origin);
|
||||
if (subdomains) {
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
// Check for http or https protocols
|
||||
if (origin.startsWith("http")) {
|
||||
this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?")));
|
||||
} else {
|
||||
}
|
||||
// Check for other protocols
|
||||
else if(matcher.find()){
|
||||
this.whiteList.add(Pattern.compile("^" + origin.replaceFirst("//", "//(.*\\.)?")));
|
||||
}
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
else {
|
||||
this.whiteList.add(Pattern.compile("^https?://(.*\\.)?" + origin));
|
||||
}
|
||||
LOG.d(TAG, "Origin to allow with subdomains: %s", origin);
|
||||
} else {
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
// Check for http or https protocols
|
||||
if (origin.startsWith("http")) {
|
||||
this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://")));
|
||||
} else {
|
||||
}
|
||||
// Check for other protocols
|
||||
else if(matcher.find()){
|
||||
this.whiteList.add(Pattern.compile("^" + origin));
|
||||
}
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
else {
|
||||
this.whiteList.add(Pattern.compile("^https?://" + origin));
|
||||
}
|
||||
LOG.d(TAG, "Origin to allow: %s", origin);
|
||||
|
||||
62
framework/src/org/apache/cordova/CordovaLocationListener.java
Executable file → Normal file
62
framework/src/org/apache/cordova/CordovaLocationListener.java
Executable file → Normal file
@@ -22,6 +22,8 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
|
||||
@@ -42,6 +44,8 @@ public class CordovaLocationListener implements LocationListener {
|
||||
|
||||
public HashMap<String, CallbackContext> watches = new HashMap<String, CallbackContext>();
|
||||
private List<CallbackContext> callbacks = new ArrayList<CallbackContext>();
|
||||
|
||||
private Timer timer = null;
|
||||
|
||||
private String TAG = "[Cordova Location Listener]";
|
||||
|
||||
@@ -52,11 +56,12 @@ public class CordovaLocationListener implements LocationListener {
|
||||
}
|
||||
|
||||
protected void fail(int code, String message) {
|
||||
this.cancelTimer();
|
||||
for (CallbackContext callbackContext: this.callbacks)
|
||||
{
|
||||
this.owner.fail(code, message, callbackContext);
|
||||
this.owner.fail(code, message, callbackContext, false);
|
||||
}
|
||||
if(this.owner.isGlobalListener(this))
|
||||
if(this.owner.isGlobalListener(this) && this.watches.size() == 0)
|
||||
{
|
||||
Log.d(TAG, "Stopping global listener");
|
||||
this.stop();
|
||||
@@ -65,16 +70,17 @@ public class CordovaLocationListener implements LocationListener {
|
||||
|
||||
Iterator<CallbackContext> it = this.watches.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
this.owner.fail(code, message, it.next());
|
||||
this.owner.fail(code, message, it.next(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void win(Location loc) {
|
||||
this.cancelTimer();
|
||||
for (CallbackContext callbackContext: this.callbacks)
|
||||
{
|
||||
this.owner.win(loc, callbackContext);
|
||||
this.owner.win(loc, callbackContext, false);
|
||||
}
|
||||
if(this.owner.isGlobalListener(this))
|
||||
if(this.owner.isGlobalListener(this) && this.watches.size() == 0)
|
||||
{
|
||||
Log.d(TAG, "Stopping global listener");
|
||||
this.stop();
|
||||
@@ -83,7 +89,7 @@ public class CordovaLocationListener implements LocationListener {
|
||||
|
||||
Iterator<CallbackContext> it = this.watches.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
this.owner.win(loc, it.next());
|
||||
this.owner.win(loc, it.next(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,8 +161,12 @@ public class CordovaLocationListener implements LocationListener {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
public void addCallback(CallbackContext callbackContext) {
|
||||
this.callbacks.add(callbackContext);
|
||||
public void addCallback(CallbackContext callbackContext, int timeout) {
|
||||
if(this.timer == null) {
|
||||
this.timer = new Timer();
|
||||
}
|
||||
this.timer.schedule(new LocationTimeoutTask(callbackContext, this), timeout);
|
||||
this.callbacks.add(callbackContext);
|
||||
if (this.size() == 1) {
|
||||
this.start();
|
||||
}
|
||||
@@ -173,7 +183,7 @@ public class CordovaLocationListener implements LocationListener {
|
||||
/**
|
||||
* Destroy listener.
|
||||
*/
|
||||
public void destroy() {
|
||||
public void destroy() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
@@ -199,9 +209,43 @@ public class CordovaLocationListener implements LocationListener {
|
||||
* Stop receiving location updates.
|
||||
*/
|
||||
private void stop() {
|
||||
this.cancelTimer();
|
||||
if (this.running) {
|
||||
this.locationManager.removeUpdates(this);
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelTimer() {
|
||||
if(this.timer != null) {
|
||||
this.timer.cancel();
|
||||
this.timer.purge();
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private class LocationTimeoutTask extends TimerTask {
|
||||
|
||||
private CallbackContext callbackContext = null;
|
||||
private CordovaLocationListener listener = null;
|
||||
|
||||
public LocationTimeoutTask(CallbackContext callbackContext, CordovaLocationListener listener) {
|
||||
this.callbackContext = callbackContext;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (CallbackContext callbackContext: listener.callbacks) {
|
||||
if(this.callbackContext == callbackContext) {
|
||||
listener.callbacks.remove(callbackContext);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(listener.size() == 0) {
|
||||
listener.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.cordova.Config;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
@@ -237,7 +238,11 @@ public class CordovaWebView extends WebView {
|
||||
// Set the nav dump for HTC 2.x devices (disabling for ICS, deprecated entirely for Jellybean 4.2)
|
||||
try {
|
||||
Method gingerbread_getMethod = WebSettings.class.getMethod("setNavDump", new Class[] { boolean.class });
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
|
||||
String manufacturer = android.os.Build.MANUFACTURER;
|
||||
Log.d(TAG, "CordovaWebView is running on device made by: " + manufacturer);
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB &&
|
||||
android.os.Build.MANUFACTURER.contains("HTC"))
|
||||
{
|
||||
gingerbread_getMethod.invoke(settings, true);
|
||||
}
|
||||
@@ -262,11 +267,8 @@ public class CordovaWebView extends WebView {
|
||||
// Enable database
|
||||
// We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16
|
||||
String databasePath = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
{
|
||||
settings.setDatabaseEnabled(true);
|
||||
settings.setDatabasePath(databasePath);
|
||||
}
|
||||
settings.setDatabaseEnabled(true);
|
||||
settings.setDatabasePath(databasePath);
|
||||
|
||||
settings.setGeolocationDatabasePath(databasePath);
|
||||
|
||||
|
||||
@@ -314,15 +314,6 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
// Clear timeout flag
|
||||
this.appView.loadUrlTimeout++;
|
||||
|
||||
// Try firing the onNativeReady event in JS. If it fails because the JS is
|
||||
// not loaded yet then just set a flag so that the onNativeReady can be fired
|
||||
// from the JS side when the JS gets to that code.
|
||||
if (!url.equals("about:blank")) {
|
||||
LOG.d(TAG, "Trying to fire onNativeReady");
|
||||
this.appView.loadUrl("javascript:try{ cordova.require('cordova/channel').onNativeReady.fire();}catch(e){_nativeReady = true;}");
|
||||
this.appView.postMessage("onNativeReady", null);
|
||||
}
|
||||
|
||||
// Broadcast message that page has loaded
|
||||
this.appView.postMessage("onPageFinished", url);
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import android.telephony.TelephonyManager;
|
||||
public class Device extends CordovaPlugin {
|
||||
public static final String TAG = "Device";
|
||||
|
||||
public static String cordovaVersion = "2.5.0"; // Cordova version
|
||||
public static String cordovaVersion = "2.7.0"; // Cordova version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
@@ -77,7 +77,6 @@ public class Device extends CordovaPlugin {
|
||||
r.put("uuid", Device.uuid);
|
||||
r.put("version", this.getOSVersion());
|
||||
r.put("platform", Device.platform);
|
||||
r.put("name", this.getProductName());
|
||||
r.put("cordova", Device.cordovaVersion);
|
||||
r.put("model", this.getModel());
|
||||
callbackContext.success(r);
|
||||
|
||||
@@ -51,6 +51,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.webkit.ValueCallback;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.LinearLayout;
|
||||
@@ -717,7 +718,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
appView.handleDestroy();
|
||||
}
|
||||
else {
|
||||
this.endActivity();
|
||||
this.activityState = ACTIVITY_EXITING;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,7 +1032,13 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
root.setBackgroundColor(that.getIntegerProperty("backgroundColor", Color.BLACK));
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 0.0F));
|
||||
root.setBackgroundResource(that.splashscreen);
|
||||
// We want the splashscreen to keep its ratio,
|
||||
// for this we need to use an ImageView and not simply the background of the LinearLayout
|
||||
ImageView splashscreenView = new ImageView(that.getActivity());
|
||||
splashscreenView.setImageResource(that.splashscreen);
|
||||
splashscreenView.setScaleType(ImageView.ScaleType.CENTER_CROP); // similar to the background-size:cover CSS property
|
||||
splashscreenView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
|
||||
root.addView(splashscreenView);
|
||||
|
||||
// Create and show the dialog
|
||||
splashDialog = new Dialog(that, android.R.style.Theme_Translucent_NoTitleBar);
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.apache.cordova;
|
||||
|
||||
import android.webkit.JavascriptInterface;
|
||||
import org.apache.cordova.api.PluginManager;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONException;
|
||||
|
||||
/**
|
||||
@@ -39,6 +40,12 @@ import org.json.JSONException;
|
||||
|
||||
@JavascriptInterface
|
||||
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
|
||||
// If the arguments weren't received, send a message back to JS. It will switch bridge modes and try again. See CB-2666.
|
||||
// We send a message meant specifically for this case. It starts with "@" so no other message can be encoded into the same string.
|
||||
if (arguments == null) {
|
||||
return "@Null arguments.";
|
||||
}
|
||||
|
||||
jsMessageQueue.setPaused(true);
|
||||
try {
|
||||
boolean wasSync = pluginManager.exec(service, action, callbackId, arguments);
|
||||
|
||||
@@ -28,6 +28,8 @@ import org.apache.cordova.api.LOG;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Locale;
|
||||
|
||||
public class FileHelper {
|
||||
private static final String LOG_TAG = "FileUtils";
|
||||
@@ -92,7 +94,8 @@ public class FileHelper {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
return cordova.getActivity().getContentResolver().openInputStream(uri);
|
||||
} else if (uriString.startsWith("file:///android_asset/")) {
|
||||
String relativePath = uriString.substring(22);
|
||||
Uri uri = Uri.parse(uriString);
|
||||
String relativePath = uri.getPath().substring(15);
|
||||
return cordova.getActivity().getAssets().open(relativePath);
|
||||
} else {
|
||||
return new FileInputStream(getRealPath(uriString, cordova));
|
||||
@@ -122,14 +125,18 @@ public class FileHelper {
|
||||
public static String getMimeType(String uriString, CordovaInterface cordova) {
|
||||
String mimeType = null;
|
||||
|
||||
Uri uri = Uri.parse(uriString);
|
||||
if (uriString.startsWith("content://")) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
mimeType = cordova.getActivity().getContentResolver().getType(uri);
|
||||
} else {
|
||||
// MimeTypeMap.getFileExtensionFromUrl has a bug that occurs when the filename has a space, so we encode it.
|
||||
// We also convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185).
|
||||
String encodedUriString = uriString.replace(" ", "%20").toLowerCase();
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(encodedUriString);
|
||||
// MimeTypeMap.getFileExtensionFromUrl() fails when there are query parameters.
|
||||
String extension = uri.getPath();
|
||||
int lastDot = extension.lastIndexOf('.');
|
||||
if (lastDot != -1) {
|
||||
extension = extension.substring(lastDot + 1);
|
||||
}
|
||||
// Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185).
|
||||
extension = extension.toLowerCase();
|
||||
if (extension.equals("3ga")) {
|
||||
mimeType = "audio/3gpp";
|
||||
} else {
|
||||
|
||||
@@ -41,6 +41,8 @@ import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
@@ -99,11 +101,84 @@ public class FileTransfer extends CordovaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an interface method to an InputStream to return the number of bytes
|
||||
* read from the raw stream. This is used to track total progress against
|
||||
* the HTTP Content-Length header value from the server.
|
||||
*/
|
||||
private static abstract class TrackingInputStream extends FilterInputStream {
|
||||
public TrackingInputStream(final InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
public abstract long getTotalRawBytesRead();
|
||||
}
|
||||
|
||||
private static class ExposedGZIPInputStream extends GZIPInputStream {
|
||||
public ExposedGZIPInputStream(final InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
public Inflater getInflater() {
|
||||
return inf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides raw bytes-read tracking for a GZIP input stream. Reports the
|
||||
* total number of compressed bytes read from the input, rather than the
|
||||
* number of uncompressed bytes.
|
||||
*/
|
||||
private static class TrackingGZIPInputStream extends TrackingInputStream {
|
||||
private ExposedGZIPInputStream gzin;
|
||||
public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException {
|
||||
super(gzin);
|
||||
this.gzin = gzin;
|
||||
}
|
||||
public long getTotalRawBytesRead() {
|
||||
return gzin.getInflater().getBytesRead();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides simple total-bytes-read tracking for an existing InputStream
|
||||
*/
|
||||
private static class TrackingHTTPInputStream extends TrackingInputStream {
|
||||
private long bytesRead = 0;
|
||||
public TrackingHTTPInputStream(InputStream stream) {
|
||||
super(stream);
|
||||
}
|
||||
|
||||
private int updateBytesRead(int newBytesRead) {
|
||||
if (newBytesRead != -1) {
|
||||
bytesRead += newBytesRead;
|
||||
}
|
||||
return newBytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return updateBytesRead(super.read());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer) throws IOException {
|
||||
return updateBytesRead(super.read(buffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] bytes, int offset, int count) throws IOException {
|
||||
return updateBytesRead(super.read(bytes, offset, count));
|
||||
}
|
||||
|
||||
public long getTotalRawBytesRead() {
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 static final class DoneHandlerInputStream extends TrackingHTTPInputStream {
|
||||
private boolean done;
|
||||
|
||||
public DoneHandlerInputStream(InputStream stream) {
|
||||
@@ -204,6 +279,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
// Look for headers on the params map for backwards compatibility with older Cordova versions.
|
||||
final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers") : args.optJSONObject(8);
|
||||
final String objectId = args.getString(9);
|
||||
final String httpMethod = getArgument(args, 10, "POST");
|
||||
|
||||
Log.d(LOG_TAG, "fileKey: " + fileKey);
|
||||
Log.d(LOG_TAG, "fileName: " + fileName);
|
||||
@@ -213,6 +289,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
|
||||
Log.d(LOG_TAG, "headers: " + headers);
|
||||
Log.d(LOG_TAG, "objectId: " + objectId);
|
||||
Log.d(LOG_TAG, "httpMethod: " + httpMethod);
|
||||
|
||||
final URL url;
|
||||
try {
|
||||
@@ -280,7 +357,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
conn.setUseCaches(false);
|
||||
|
||||
// Use a post method.
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestMethod(httpMethod);
|
||||
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDARY);
|
||||
|
||||
// Set the cookies on the response
|
||||
@@ -407,7 +484,7 @@ public class FileTransfer extends CordovaPlugin {
|
||||
int responseCode = conn.getResponseCode();
|
||||
Log.d(LOG_TAG, "response code: " + responseCode);
|
||||
Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields());
|
||||
InputStream inStream = null;
|
||||
TrackingInputStream inStream = null;
|
||||
try {
|
||||
inStream = getInputStream(conn);
|
||||
synchronized (context) {
|
||||
@@ -483,11 +560,15 @@ public class FileTransfer extends CordovaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private static InputStream getInputStream(URLConnection conn) throws IOException {
|
||||
private static TrackingInputStream getInputStream(URLConnection conn) throws IOException {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||
return new DoneHandlerInputStream(conn.getInputStream());
|
||||
}
|
||||
return conn.getInputStream();
|
||||
String encoding = conn.getContentEncoding();
|
||||
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
|
||||
return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
|
||||
}
|
||||
return new TrackingHTTPInputStream(conn.getInputStream());
|
||||
}
|
||||
|
||||
// always verify the host - don't check for certificate
|
||||
@@ -698,6 +779,9 @@ public class FileTransfer extends CordovaPlugin {
|
||||
{
|
||||
connection.setRequestProperty("cookie", cookie);
|
||||
}
|
||||
|
||||
// This must be explicitly set for gzip progress tracking to work.
|
||||
connection.setRequestProperty("Accept-Encoding", "gzip");
|
||||
|
||||
// Handle the other headers
|
||||
if (headers != null) {
|
||||
@@ -709,14 +793,15 @@ public class FileTransfer extends CordovaPlugin {
|
||||
Log.d(LOG_TAG, "Download file:" + url);
|
||||
|
||||
FileProgressResult progress = new FileProgressResult();
|
||||
if (connection.getContentEncoding() == null) {
|
||||
// Only trust content-length header if no gzip etc
|
||||
if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
|
||||
// Only trust content-length header if we understand
|
||||
// the encoding -- identity or gzip
|
||||
progress.setLengthComputable(true);
|
||||
progress.setTotal(connection.getContentLength());
|
||||
}
|
||||
|
||||
FileOutputStream outputStream = null;
|
||||
InputStream inputStream = null;
|
||||
TrackingInputStream inputStream = null;
|
||||
|
||||
try {
|
||||
inputStream = getInputStream(connection);
|
||||
@@ -731,12 +816,10 @@ public class FileTransfer extends CordovaPlugin {
|
||||
// write bytes to file
|
||||
byte[] buffer = new byte[MAX_BUFFER_SIZE];
|
||||
int bytesRead = 0;
|
||||
long totalBytes = 0;
|
||||
while ((bytesRead = inputStream.read(buffer)) > 0) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
totalBytes += bytesRead;
|
||||
// Send a progress event.
|
||||
progress.setLoaded(totalBytes);
|
||||
progress.setLoaded(inputStream.getTotalRawBytesRead());
|
||||
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
|
||||
progressResult.setKeepCallback(true);
|
||||
context.sendPluginResult(progressResult);
|
||||
|
||||
18
framework/src/org/apache/cordova/GeoBroker.java
Executable file → Normal file
18
framework/src/org/apache/cordova/GeoBroker.java
Executable file → Normal file
@@ -38,7 +38,7 @@ import android.location.LocationManager;
|
||||
public class GeoBroker extends CordovaPlugin {
|
||||
private GPSListener gpsListener;
|
||||
private NetworkListener networkListener;
|
||||
private LocationManager locationManager;
|
||||
private LocationManager locationManager;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -73,7 +73,7 @@ public class GeoBroker extends CordovaPlugin {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last));
|
||||
callbackContext.sendPluginResult(result);
|
||||
} else {
|
||||
this.getCurrentLocation(callbackContext, enableHighAccuracy);
|
||||
this.getCurrentLocation(callbackContext, enableHighAccuracy, args.optInt(2, 60000));
|
||||
}
|
||||
}
|
||||
else if (action.equals("addWatch")) {
|
||||
@@ -102,11 +102,11 @@ public class GeoBroker extends CordovaPlugin {
|
||||
this.networkListener.clearWatch(id);
|
||||
}
|
||||
|
||||
private void getCurrentLocation(CallbackContext callbackContext, boolean enableHighAccuracy) {
|
||||
private void getCurrentLocation(CallbackContext callbackContext, boolean enableHighAccuracy, int timeout) {
|
||||
if (enableHighAccuracy) {
|
||||
this.gpsListener.addCallback(callbackContext);
|
||||
this.gpsListener.addCallback(callbackContext, timeout);
|
||||
} else {
|
||||
this.networkListener.addCallback(callbackContext);
|
||||
this.networkListener.addCallback(callbackContext, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,8 +160,9 @@ public class GeoBroker extends CordovaPlugin {
|
||||
return o;
|
||||
}
|
||||
|
||||
public void win(Location loc, CallbackContext callbackContext) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(loc));
|
||||
public void win(Location loc, CallbackContext callbackContext, boolean keepCallback) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(loc));
|
||||
result.setKeepCallback(keepCallback);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
@@ -172,7 +173,7 @@ public class GeoBroker extends CordovaPlugin {
|
||||
* @param msg The error message
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void fail(int code, String msg, CallbackContext callbackContext) {
|
||||
public void fail(int code, String msg, CallbackContext callbackContext, boolean keepCallback) {
|
||||
JSONObject obj = new JSONObject();
|
||||
String backup = null;
|
||||
try {
|
||||
@@ -189,6 +190,7 @@ public class GeoBroker extends CordovaPlugin {
|
||||
result = new PluginResult(PluginResult.Status.ERROR, backup);
|
||||
}
|
||||
|
||||
result.setKeepCallback(keepCallback);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,12 @@ import java.io.InputStream;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.LOG;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
|
||||
|
||||
@@ -42,35 +43,20 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||
if(url.contains("?") || url.contains("#") || needsIceCreamSpaceInAssetUrlFix(url)){
|
||||
return generateWebResourceResponse(url);
|
||||
} else {
|
||||
return super.shouldInterceptRequest(view, url);
|
||||
//Check if plugins intercept the request
|
||||
WebResourceResponse ret = super.shouldInterceptRequest(view, url);
|
||||
if(ret == null && (url.contains("?") || url.contains("#") || needsIceCreamSpaceInAssetUrlFix(url))){
|
||||
ret = generateWebResourceResponse(url);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private WebResourceResponse generateWebResourceResponse(String url) {
|
||||
final String ANDROID_ASSET = "file:///android_asset/";
|
||||
if (url.startsWith(ANDROID_ASSET)) {
|
||||
String niceUrl = url;
|
||||
niceUrl = url.replaceFirst(ANDROID_ASSET, "");
|
||||
if(niceUrl.contains("?")){
|
||||
niceUrl = niceUrl.split("\\?")[0];
|
||||
}
|
||||
else if(niceUrl.contains("#"))
|
||||
{
|
||||
niceUrl = niceUrl.split("#")[0];
|
||||
}
|
||||
|
||||
String mimetype = null;
|
||||
if(niceUrl.endsWith(".html")){
|
||||
mimetype = "text/html";
|
||||
}
|
||||
if (url.startsWith("file:///android_asset/")) {
|
||||
String mimetype = FileHelper.getMimeType(url, cordova);
|
||||
|
||||
try {
|
||||
AssetManager assets = cordova.getActivity().getAssets();
|
||||
Uri uri = Uri.parse(niceUrl);
|
||||
InputStream stream = assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING);
|
||||
InputStream stream = FileHelper.getInputStreamFromUriString(url, cordova);
|
||||
WebResourceResponse response = new WebResourceResponse(mimetype, "UTF-8", stream);
|
||||
return response;
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -51,6 +51,7 @@ import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
@@ -92,12 +93,9 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
this.callbackContext = callbackContext;
|
||||
|
||||
try {
|
||||
if (action.equals("open")) {
|
||||
this.callbackContext = callbackContext;
|
||||
String url = args.getString(0);
|
||||
String target = args.optString(1);
|
||||
if (target == null || target.equals("") || target.equals(NULL)) {
|
||||
@@ -108,6 +106,7 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
Log.d(LOG_TAG, "target = " + target);
|
||||
|
||||
url = updateUrl(url);
|
||||
String result = "";
|
||||
|
||||
// SELF
|
||||
if (SELF.equals(target)) {
|
||||
@@ -143,41 +142,89 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
Log.d(LOG_TAG, "in blank");
|
||||
result = this.showWebPage(url, features);
|
||||
}
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
|
||||
pluginResult.setKeepCallback(true);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
else if (action.equals("close")) {
|
||||
closeDialog();
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
|
||||
pluginResult.setKeepCallback(false);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
|
||||
}
|
||||
else if (action.equals("injectScriptCode")) {
|
||||
String source = args.getString(0);
|
||||
|
||||
org.json.JSONArray jsonEsc = new org.json.JSONArray();
|
||||
jsonEsc.put(source);
|
||||
String jsonRepr = jsonEsc.toString();
|
||||
String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
|
||||
String scriptEnclosure = "(function(d){var c=d.createElement('script');c.type='text/javascript';c.innerText="
|
||||
+ jsonSourceString
|
||||
+ ";d.getElementsByTagName('head')[0].appendChild(c);})(document)";
|
||||
this.inAppWebView.loadUrl("javascript:" + scriptEnclosure);
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
String jsWrapper = null;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId());
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("injectScriptFile")) {
|
||||
String jsWrapper;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId());
|
||||
} else {
|
||||
jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)";
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("injectStyleCode")) {
|
||||
String jsWrapper;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
|
||||
} else {
|
||||
jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)";
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else if (action.equals("injectStyleFile")) {
|
||||
String jsWrapper;
|
||||
if (args.getBoolean(1)) {
|
||||
jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
|
||||
} else {
|
||||
jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)";
|
||||
}
|
||||
injectDeferredObject(args.getString(0), jsWrapper);
|
||||
}
|
||||
else {
|
||||
status = PluginResult.Status.INVALID_ACTION;
|
||||
return false;
|
||||
}
|
||||
PluginResult pluginResult = new PluginResult(status, result);
|
||||
pluginResult.setKeepCallback(true);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
} catch (JSONException e) {
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject an object (script or style) into the InAppBrowser WebView.
|
||||
*
|
||||
* This is a helper method for the inject{Script|Style}{Code|File} API calls, which
|
||||
* provides a consistent method for injecting JavaScript code into the document.
|
||||
*
|
||||
* If a wrapper string is supplied, then the source string will be JSON-encoded (adding
|
||||
* quotes) and wrapped using string formatting. (The wrapper string should have a single
|
||||
* '%s' marker)
|
||||
*
|
||||
* @param source The source object (filename or script/style text) to inject into
|
||||
* the document.
|
||||
* @param jsWrapper A JavaScript string to wrap the source string in, so that the object
|
||||
* is properly injected, or null if the source string is JavaScript text
|
||||
* which should be executed directly.
|
||||
*/
|
||||
private void injectDeferredObject(String source, String jsWrapper) {
|
||||
String scriptToInject;
|
||||
if (jsWrapper != null) {
|
||||
org.json.JSONArray jsonEsc = new org.json.JSONArray();
|
||||
jsonEsc.put(source);
|
||||
String jsonRepr = jsonEsc.toString();
|
||||
String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
|
||||
scriptToInject = String.format(jsWrapper, jsonSourceString);
|
||||
} else {
|
||||
scriptToInject = source;
|
||||
}
|
||||
// This action will have the side-effect of blurring the currently focused element
|
||||
this.inAppWebView.loadUrl("javascript:" + scriptToInject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the list of features into a hash map
|
||||
*
|
||||
@@ -444,7 +491,7 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
// WebView
|
||||
inAppWebView = new WebView(cordova.getActivity());
|
||||
inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||
inAppWebView.setWebChromeClient(new InAppChromeClient());
|
||||
inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView));
|
||||
WebViewClient client = new InAppBrowserClient(thatWebView, edittext);
|
||||
inAppWebView.setWebViewClient(client);
|
||||
WebSettings settings = inAppWebView.getSettings();
|
||||
@@ -527,8 +574,15 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
result.setKeepCallback(keepCallback);
|
||||
this.callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
public class InAppChromeClient extends WebChromeClient {
|
||||
|
||||
private CordovaWebView webView;
|
||||
|
||||
public InAppChromeClient(CordovaWebView webView) {
|
||||
super();
|
||||
this.webView = webView;
|
||||
}
|
||||
/**
|
||||
* Handle database quota exceeded notification.
|
||||
*
|
||||
@@ -571,6 +625,57 @@ public class InAppBrowser extends CordovaPlugin {
|
||||
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
||||
callback.invoke(origin, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the client to display a prompt dialog to the user.
|
||||
* If the client returns true, WebView will assume that the client will
|
||||
* handle the prompt dialog and call the appropriate JsPromptResult method.
|
||||
*
|
||||
* The prompt bridge provided for the InAppBrowser is capable of executing any
|
||||
* oustanding callback belonging to the InAppBrowser plugin. Care has been
|
||||
* taken that other callbacks cannot be triggered, and that no other code
|
||||
* execution is possible.
|
||||
*
|
||||
* To trigger the bridge, the prompt default value should be of the form:
|
||||
*
|
||||
* gap-iab://<callbackId>
|
||||
*
|
||||
* where <callbackId> is the string id of the callback to trigger (something
|
||||
* like "InAppBrowser0123456789")
|
||||
*
|
||||
* If present, the prompt message is expected to be a JSON-encoded value to
|
||||
* pass to the callback. A JSON_EXCEPTION is returned if the JSON is invalid.
|
||||
*
|
||||
* @param view
|
||||
* @param url
|
||||
* @param message
|
||||
* @param defaultValue
|
||||
* @param result
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
|
||||
// See if the prompt string uses the 'gap-iab' protocol. If so, the remainder should be the id of a callback to execute.
|
||||
if (defaultValue != null && defaultValue.startsWith("gap-iab://")) {
|
||||
PluginResult scriptResult;
|
||||
String scriptCallbackId = defaultValue.substring(10);
|
||||
if (scriptCallbackId.startsWith("InAppBrowser")) {
|
||||
if(message == null || message.length() == 0) {
|
||||
scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray());
|
||||
} else {
|
||||
try {
|
||||
scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray(message));
|
||||
} catch(JSONException e) {
|
||||
scriptResult = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
|
||||
}
|
||||
}
|
||||
this.webView.sendPluginResult(scriptResult, scriptCallbackId);
|
||||
result.confirm("");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,13 +50,10 @@ public class NativeToJsMessageQueue {
|
||||
// exec() is asynchronous. Set this to true when running bridge benchmarks.
|
||||
static final boolean DISABLE_EXEC_CHAINING = false;
|
||||
|
||||
// Upper limit for how much data to send to JS in one shot.
|
||||
// TODO(agrieve): This is currently disable. It should be re-enabled once we
|
||||
// remove support for returning values from exec() calls. This was
|
||||
// deprecated in 2.2.0.
|
||||
// Also, this currently only chops up on message boundaries. It may be useful
|
||||
// Arbitrarily chosen upper limit for how much data to send to JS in one shot.
|
||||
// This currently only chops up on message boundaries. It may be useful
|
||||
// to allow it to break up messages.
|
||||
private static int MAX_PAYLOAD_SIZE = -1; //50 * 1024 * 10240;
|
||||
private static int MAX_PAYLOAD_SIZE = 50 * 1024 * 10240;
|
||||
|
||||
/**
|
||||
* The index into registeredListeners to treat as active.
|
||||
|
||||
@@ -1,177 +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.api;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Legacy Plugin class. This acts as a shim to support the old execute() signature.
|
||||
* New plugins should extend CordovaPlugin directly.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class Plugin extends CordovaPlugin {
|
||||
public LegacyContext ctx; // LegacyContext object
|
||||
|
||||
public abstract PluginResult execute(String action, JSONArray args, String callbackId);
|
||||
|
||||
public boolean isSynch(String action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
this.setContext(cordova);
|
||||
this.setView(webView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of the Plugin. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param ctx The context of the main Activity.
|
||||
*/
|
||||
public void setContext(CordovaInterface ctx) {
|
||||
this.cordova = ctx;
|
||||
this.ctx = new LegacyContext(cordova);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the main View of the application, this is the WebView within which
|
||||
* a Cordova app runs.
|
||||
*
|
||||
* @param webView The Cordova WebView
|
||||
*/
|
||||
public void setView(CordovaWebView webView) {
|
||||
this.webView = webView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final String callbackId = callbackContext.getCallbackId();
|
||||
boolean runAsync = !isSynch(action);
|
||||
if (runAsync) {
|
||||
// Run this on a different thread so that this one can return back to JS
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
PluginResult cr;
|
||||
try {
|
||||
cr = execute(action, args, callbackId);
|
||||
} catch (Throwable e) {
|
||||
cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
|
||||
}
|
||||
sendPluginResult(cr, callbackId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
PluginResult cr = execute(action, args, callbackId);
|
||||
|
||||
// Interpret a null response as NO_RESULT, which *does* clear the callbacks on the JS side.
|
||||
if (cr == null) {
|
||||
cr = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
}
|
||||
|
||||
callbackContext.sendPluginResult(cr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send generic JavaScript statement back to JavaScript.
|
||||
* sendPluginResult() should be used instead where possible.
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
this.webView.sendJavascript(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send generic JavaScript statement back to JavaScript.
|
||||
*/
|
||||
public void sendPluginResult(PluginResult pluginResult, String callbackId) {
|
||||
this.webView.sendPluginResult(pluginResult, callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the JavaScript success callback for this plugin.
|
||||
*
|
||||
* This can be used if the execute code for the plugin is asynchronous meaning
|
||||
* that execute should return null and the callback from the async operation can
|
||||
* call success(...) or error(...)
|
||||
*
|
||||
* @param pluginResult The result to return.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(PluginResult pluginResult, String callbackId) {
|
||||
this.webView.sendPluginResult(pluginResult, callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(JSONObject message, String callbackId) {
|
||||
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.OK, message), callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(String message, String callbackId) {
|
||||
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.OK, message), callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the JavaScript error callback for this plugin.
|
||||
*
|
||||
* @param pluginResult The result to return.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(PluginResult pluginResult, String callbackId) {
|
||||
this.webView.sendPluginResult(pluginResult, callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(JSONObject message, String callbackId) {
|
||||
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(String message, String callbackId) {
|
||||
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user