diff --git a/VERSION b/VERSION index 8862dbae..1bb19905 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0rc2 +1.0.0rc3 diff --git a/example/index.html b/example/index.html index 10a69d06..b5106057 100644 --- a/example/index.html +++ b/example/index.html @@ -5,7 +5,7 @@ PhoneGap - + diff --git a/example/main.js b/example/main.js index 110d3089..1001aabc 100644 --- a/example/main.js +++ b/example/main.js @@ -99,7 +99,7 @@ function get_contacts() { var obj = new ContactFindOptions(); obj.filter = ""; obj.multiple = true; - navigator.service.contacts.find( + navigator.contacts.find( [ "displayName", "name" ], contacts_success, fail, obj); } diff --git a/framework/assets/js/camera.js b/framework/assets/js/camera.js index 1c4be96d..3971e1f1 100755 --- a/framework/assets/js/camera.js +++ b/framework/assets/js/camera.js @@ -34,6 +34,21 @@ Camera.DestinationType = { }; Camera.prototype.DestinationType = Camera.DestinationType; +/** + * Encoding of image returned from getPicture. + * + * Example: navigator.camera.getPicture(success, fail, + * { quality: 80, + * destinationType: Camera.DestinationType.DATA_URL, + * sourceType: Camera.PictureSourceType.CAMERA, + * encodingType: Camera.EncodingType.PNG}) +*/ +Camera.EncodingType = { + JPEG: 0, // Return JPEG encoded image + PNG: 1 // Return PNG encoded image +}; +Camera.prototype.EncodingType = Camera.EncodingType; + /** * Source to getPicture from. * @@ -78,6 +93,12 @@ Camera.prototype.getPicture = function(successCallback, errorCallback, options) if (options.quality) { quality = this.options.quality; } + + var maxResolution = 0; + if (options.maxResolution) { + maxResolution = this.options.maxResolution; + } + var destinationType = Camera.DestinationType.DATA_URL; if (this.options.destinationType) { destinationType = this.options.destinationType; @@ -86,7 +107,32 @@ Camera.prototype.getPicture = function(successCallback, errorCallback, options) if (typeof this.options.sourceType === "number") { sourceType = this.options.sourceType; } - PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType]); + var encodingType = Camera.EncodingType.JPEG; + if (typeof options.encodingType == "number") { + encodingType = this.options.encodingType; + } + + var targetWidth = -1; + if (typeof options.targetWidth == "number") { + targetWidth = options.targetWidth; + } else if (typeof options.targetWidth == "string") { + var width = new Number(options.targetWidth); + if (isNaN(width) === false) { + targetWidth = width.valueOf(); + } + } + + var targetHeight = -1; + if (typeof options.targetHeight == "number") { + targetHeight = options.targetHeight; + } else if (typeof options.targetHeight == "string") { + var height = new Number(options.targetHeight); + if (isNaN(height) === false) { + targetHeight = height.valueOf(); + } + } + + PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType]); }; PhoneGap.addConstructor(function() { diff --git a/framework/assets/js/media.js b/framework/assets/js/media.js index 8c492271..de955c66 100755 --- a/framework/assets/js/media.js +++ b/framework/assets/js/media.js @@ -63,6 +63,7 @@ var Media = function(src, successCallback, errorCallback, statusCallback, positi // Media messages Media.MEDIA_STATE = 1; Media.MEDIA_DURATION = 2; +Media.MEDIA_POSITION = 3; Media.MEDIA_ERROR = 9; // Media states @@ -187,7 +188,6 @@ PhoneGap.Media.getMediaObject = function(id) { */ PhoneGap.Media.onStatus = function(id, msg, value) { var media = PhoneGap.mediaObjects[id]; - // If state update if (msg === Media.MEDIA_STATE) { if (value === Media.MEDIA_STOPPED) { @@ -207,5 +207,8 @@ PhoneGap.Media.onStatus = function(id, msg, value) { media.errorCallback(value); } } + else if (msg == Media.MEDIA_POSITION) { + media._position = value; + } }; } diff --git a/framework/assets/www/index.html b/framework/assets/www/index.html index e742b835..701402aa 100644 --- a/framework/assets/www/index.html +++ b/framework/assets/www/index.html @@ -1,7 +1,7 @@ - + diff --git a/framework/build.xml b/framework/build.xml index d48e8a1f..05da712a 100755 --- a/framework/build.xml +++ b/framework/build.xml @@ -1,5 +1,5 @@ - + diff --git a/framework/res/xml/plugins.xml b/framework/res/xml/plugins.xml old mode 100755 new mode 100644 index e62c959f..3d8d48d8 --- a/framework/res/xml/plugins.xml +++ b/framework/res/xml/plugins.xml @@ -10,7 +10,6 @@ - diff --git a/framework/src/com/phonegap/AudioPlayer.java b/framework/src/com/phonegap/AudioPlayer.java index 8f84a90e..2d99fdbc 100755 --- a/framework/src/com/phonegap/AudioPlayer.java +++ b/framework/src/com/phonegap/AudioPlayer.java @@ -11,11 +11,12 @@ import java.io.File; import java.io.IOException; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaPlayer.OnErrorListener; -import android.media.MediaRecorder; import android.media.MediaPlayer.OnCompletionListener; +import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; +import android.media.MediaRecorder; import android.os.Environment; +import android.util.Log; /** * This class implements the audio playback and recording capabilities used by PhoneGap. @@ -28,7 +29,9 @@ import android.os.Environment; */ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, OnErrorListener { - // AudioPlayer states + private static final String LOG_TAG = "AudioPlayer"; + + // AudioPlayer states private static int MEDIA_NONE = 0; private static int MEDIA_STARTING = 1; private static int MEDIA_RUNNING = 2; @@ -38,6 +41,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On // AudioPlayer message ids private static int MEDIA_STATE = 1; private static int MEDIA_DURATION = 2; + private static int MEDIA_POSITION = 3; private static int MEDIA_ERROR = 9; // AudioPlayer error codes @@ -99,7 +103,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On */ public void startRecording(String file) { if (this.mPlayer != null) { - System.out.println("AudioPlayer Error: Can't record in play mode."); + Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode."); this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_PLAY_MODE_SET+");"); } @@ -124,7 +128,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_STARTING_RECORDING+");"); } else { - System.out.println("AudioPlayer Error: Already recording."); + Log.d(LOG_TAG, "AudioPlayer Error: Already recording."); this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_ALREADY_RECORDING+");"); } } @@ -166,7 +170,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On */ public void startPlaying(String file) { if (this.recorder != null) { - System.out.println("AudioPlayer Error: Can't play in record mode."); + Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode."); this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_RECORD_MODE_SET+");"); } @@ -225,7 +229,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On this.setState(MEDIA_RUNNING); } else { - System.out.println("AudioPlayer Error: startPlaying() called during invalid state: "+this.state); + Log.d(LOG_TAG, "AudioPlayer Error: startPlaying() called during invalid state: "+this.state); this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_RESUME_STATE+");"); } } @@ -237,6 +241,8 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On public void seekToPlaying(int milliseconds) { if (this.mPlayer != null) { this.mPlayer.seekTo(milliseconds); + Log.d(LOG_TAG, "Send a onStatus update for the new seek"); + this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_POSITION+", "+milliseconds/1000.0f+");"); } } @@ -251,7 +257,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On this.setState(MEDIA_PAUSED); } else { - System.out.println("AudioPlayer Error: pausePlaying() called during invalid state: "+this.state); + Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: "+this.state); this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_PAUSE_STATE+");"); } } @@ -265,7 +271,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On this.setState(MEDIA_STOPPED); } else { - System.out.println("AudioPlayer Error: stopPlaying() called during invalid state: "+this.state); + Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: "+this.state); this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_STOP_STATE+");"); } } @@ -286,7 +292,9 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On */ public long getCurrentPosition() { if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) { - return this.mPlayer.getCurrentPosition(); + int curPos = this.mPlayer.getCurrentPosition(); + this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_POSITION+", "+curPos/1000.0f+");"); + return curPos; } else { return -1; @@ -386,7 +394,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On * @param arg2 an extra code, specific to the error. */ public boolean onError(MediaPlayer mPlayer, int arg1, int arg2) { - System.out.println("AudioPlayer.onError(" + arg1 + ", " + arg2+")"); + Log.d(LOG_TAG, "AudioPlayer.onError(" + arg1 + ", " + arg2+")"); // TODO: Not sure if this needs to be sent? this.mPlayer.stop(); @@ -409,5 +417,4 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On this.state = state; } - } diff --git a/framework/src/com/phonegap/CameraLauncher.java b/framework/src/com/phonegap/CameraLauncher.java index 1ad4bac0..6ca85c43 100755 --- a/framework/src/com/phonegap/CameraLauncher.java +++ b/framework/src/com/phonegap/CameraLauncher.java @@ -26,8 +26,6 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.net.Uri; -import android.os.Environment; -import android.provider.MediaStore; import android.util.Log; /** @@ -44,7 +42,12 @@ public class CameraLauncher extends Plugin { private static final int CAMERA = 1; // Take picture from camera private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture library (same as PHOTOLIBRARY for Android) + private static final int JPEG = 0; // Take a picture of type JPEG + private static final int PNG = 1; // Take a picture of type PNG + private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) + private int targetWidth; // desired width of the image + private int targetHeight; // desired height of the image private Uri imageUri; // Uri of captured image public String callbackId; @@ -77,8 +80,18 @@ public class CameraLauncher extends Plugin { if (args.length() > 2) { srcType = args.getInt(2); } + if (args.length() > 3) { + this.targetWidth = args.getInt(3); + } + if (args.length() > 4) { + this.targetHeight = args.getInt(4); + } + int encodingType = JPEG; + if (args.length() > 5) { + encodingType = args.getInt(5); + } if (srcType == CAMERA) { - this.takePicture(args.getInt(0), destType); + this.takePicture(args.getInt(0), destType, encodingType); } else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { this.getImage(args.getInt(0), srcType, destType); @@ -112,7 +125,7 @@ public class CameraLauncher extends Plugin { * @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) * @param returnType Set the type of image to return. */ - public void takePicture(int quality, int returnType) { + public void takePicture(int quality, int returnType, int encodingType) { this.mQuality = quality; // Display camera @@ -120,13 +133,29 @@ public class CameraLauncher extends Plugin { // Specify file so that large image is captured and returned // TODO: What if there isn't any external storage? - File photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Pic.jpg"); + File photo = createCaptureFile(encodingType); intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo)); this.imageUri = Uri.fromFile(photo); this.ctx.startActivityForResult((Plugin) this, intent, (CAMERA+1)*16 + returnType+1); } + /** + * Create a file in the applications temporary directory based upon the supplied encoding. + * + * @param encodingType of the image to be taken + * @return a File object pointing to the temporary picture + */ + private File createCaptureFile(int encodingType) { + File photo = null; + if (encodingType == JPEG) { + photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Pic.jpg"); + } else { + photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Pic.png"); + } + return photo; + } + /** * Get image from photo library. * @@ -146,6 +175,50 @@ public class CameraLauncher extends Plugin { new String("Get Picture")), (srcType+1)*16 + returnType + 1); } + /** + * Scales the bitmap according to the requested size. + * + * @param bitmap The bitmap to scale. + * @return Bitmap A new Bitmap object of the same bitmap after scaling. + */ + public Bitmap scaleBitmap(Bitmap bitmap) { + int newWidth = this.targetWidth; + int newHeight = this.targetHeight; + int origWidth = bitmap.getWidth(); + int origHeight = bitmap.getHeight(); + + // If no new width or height were specified return the original bitmap + if (newWidth <= 0 && newHeight <= 0) { + return bitmap; + } + // Only the width was specified + else if (newWidth > 0 && newHeight <= 0) { + newHeight = (newWidth * origHeight) / origWidth; + } + // only the height was specified + else if (newWidth <= 0 && newHeight > 0) { + newWidth = (newHeight * origWidth) / origHeight; + } + // If the user specified both a positive width and height + // (potentially different aspect ratio) then the width or height is + // scaled so that the image fits while maintaining aspect ratio. + // Alternatively, the specified width and height could have been + // kept and Bitmap.SCALE_TO_FIT specified when scaling, but this + // would result in whitespace in the new image. + else { + double newRatio = newWidth / (double)newHeight; + double origRatio = origWidth / (double)origHeight; + + if (origRatio > newRatio) { + newHeight = (newWidth * origHeight) / origWidth; + } else if (origRatio < newRatio) { + newWidth = (newHeight * origWidth) / origHeight; + } + } + + return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true); + } + /** * Called when the camera view exits. * @@ -175,6 +248,8 @@ public class CameraLauncher extends Plugin { bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri)); } + bitmap = scaleBitmap(bitmap); + // If sending base64 image back if (destType == DATA_URL) { this.processPicture(bitmap); @@ -237,6 +312,7 @@ public class CameraLauncher extends Plugin { if (destType == DATA_URL) { try { Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri)); + bitmap = scaleBitmap(bitmap); this.processPicture(bitmap); bitmap.recycle(); bitmap = null; diff --git a/framework/src/com/phonegap/ContactAccessorSdk5.java b/framework/src/com/phonegap/ContactAccessorSdk5.java index bcb25227..7e856049 100644 --- a/framework/src/com/phonegap/ContactAccessorSdk5.java +++ b/framework/src/com/phonegap/ContactAccessorSdk5.java @@ -425,13 +425,28 @@ public class ContactAccessorSdk5 extends ContactAccessor { JSONArray addresses, JSONArray phones, JSONArray emails, JSONArray ims, JSONArray websites, JSONArray photos) { try { - contact.put("organizations", organizations); - contact.put("addresses", addresses); - contact.put("phoneNumbers", phones); - contact.put("emails", emails); - contact.put("ims", ims); - contact.put("websites", websites); - contact.put("photos", photos); + // Only return the array if it has at least one entry + if (organizations.length() > 0) { + contact.put("organizations", organizations); + } + if (addresses.length() > 0) { + contact.put("addresses", addresses); + } + if (phones.length() > 0) { + contact.put("phoneNumbers", phones); + } + if (emails.length() > 0) { + contact.put("emails", emails); + } + if (ims.length() > 0) { + contact.put("ims", ims); + } + if (websites.length() > 0) { + contact.put("websites", websites); + } + if (photos.length() > 0) { + contact.put("photos", photos); + } } catch (JSONException e) { Log.e(LOG_TAG,e.getMessage(),e); diff --git a/framework/src/com/phonegap/Device.java b/framework/src/com/phonegap/Device.java index 60858bc9..aa8c52dd 100644 --- a/framework/src/com/phonegap/Device.java +++ b/framework/src/com/phonegap/Device.java @@ -18,7 +18,7 @@ import android.provider.Settings; public class Device extends Plugin { - public static String phonegapVersion = "1.0.0rc2"; // PhoneGap version + public static String phonegapVersion = "1.0.0rc3"; // PhoneGap version public static String platform = "Android"; // Device OS public static String uuid; // Device UUID diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java old mode 100755 new mode 100644 index a22622ba..d9ecccd9 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -1123,7 +1123,7 @@ public class DroidGap extends PhonegapActivity { try { // Init parameters to new DroidGap activity and propagate existing parameters HashMap params = new HashMap(); - params.put("loadingDialog", ""); + params.put("loadingDialog", null); if (this.ctx.loadInWebView) { params.put("loadInWebView", true); }