Monday, October 3, 2011

Changes in PhoneGap Android 1.1.0

As of Friday PhoneGap 1.1.0 was released upon the world. I figured I would use this post to talk about some of the additions/changes to the API in our latest point release.

Chunked Streaming Mode

One of the biggest complaints about the FileTransfer code on Android is that it would throw an Out of Memory exception if the file you were trying to upload was too big. This happened because the HttpURLConnection class likes to buffer all the data to upload in memory before it even sends the first byte. You can see how that would be a problem when you are uploading a 10 mb file.

Now the default is to send the data in chunked streaming mode. Most web servers support chunked mode but if the one you are communicating with doesn't you'll end up getting a FileTransferError.CONNECTION_ERR (Error code 3). To make this error go away you just need to set the right option:

var options = new FileUploadOptions();
options.chunkedMode = false;

Clean Up the Content Store

Previously when you were using FileEntry.remove() to delete a file, like an image, that had a corresponding entry in the content store the file would be removed from the file system but the database entry remained. This would cause odd behaviour in the Gallery app where you would see a black image. Now when you delete a file we do a query to see if there is a corresponding entry in the content store and remove it.

Double Image Bug

On some phones, Samsung for instance, when you used Camera.getPicture() you would end up getting two pictures in the gallery. One would be the raw image the camera took and the second would be the image with the options you specified like quality, targetWidth and targetHeight. Now we are doing a check to see if two images have been created and we'll remove the raw image and only store the one with the options you've specified.

Use Camera.getPicture() to Retrieve a Video

Typically you can get an image out of the gallery by doing:

var options = {
    quality: 100,
    destinationType: navigator.camera.DestinationType.FILE_URI,
    sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY
}       
navigator.camera.getPicture(win, fail, options);

but that only lists all the images in the gallery. What if you wanted the user to be able to select a Video? Well you couldn't until now. We've introduced a new mediaType option that will allow you to get an image, a video or both.

This will bring up the video gallery:

var options = {
    quality: 100,
    destinationType: navigator.camera.DestinationType.FILE_URI,
    sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY,
    mediaType: navigator.camera.MediaType.VIDEO
}       
navigator.camera.getPicture(win, fail, options);

This will bring up the gallery for both images and video:

var options = {
    quality: 100,
    destinationType: navigator.camera.DestinationType.FILE_URI,
    sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY,
    mediaType: navigator.camera.MediaType.ALLMEDIA
}       
navigator.camera.getPicture(win, fail, options);

The default will continue to be to get a picture but if you want to explicitly state it do this:

var options = {
    quality: 100,
    destinationType: navigator.camera.DestinationType.FILE_URI,
    sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY,
    mediaType: navigator.camera.MediaType.PICTURE
}       
navigator.camera.getPicture(win, fail, options);

Compass Return Value Has Changed

Previously on Android the getHeading() and watchHeading() methods would call their success call backs with a heading parameter which was express in degrees. In 1.1 to line up with iOS the return value is now a CompassHeading object which has the following properties:

magneticHeading: The heading in degrees from 0 - 359.99 at a single moment in time.
trueHeading: It is supposed to be the heading relative to the geographic North Pole in degrees 0 - 359.99 at a single moment in time. Unfortunately, on Android this will have the same value as magneticHeading.
headingAccuracy: Is supposed to report the degrees difference between the magnetic and true heading but since they are the same on Android this will always be 0.
timestamp: The time at which this heading was determined.

Multi-Page Apps

Now when you are dealing with multiple page apps there is a change to navigator.app.exitApp(). This command now exits the app completely it does not return to the previous page. If you want to go back a page you should use navigator.app.backHistory().

10 comments:

LucyJp said...

Hi Simon, thank you for the nice post! Very happy to see that now we can get the video files via getPicture () method!

Question, do you know there is way that we can get the file extension and mime type of the video file? When calling getPicture() and receiving FILE_URI , the uri does not contain the file extension (i.g: content://media/external/video/media/360 ).

Need the file extension for our server side process (need either mime type or file extension so that file will have correct content type)

For the photo, I am appending the extension (jpg) and mime type (options.mimeType="image/jpeg";) before calling FileTransfer.upload() but video file extension can be different on different devices.

Could you please suggest me something?

Thank you

Simon Mac Donald said...

@LucyJp

Yup, you just need to do a:

window.resolveLocalFileSystemURI("content://media/external/video/media/360", onSuccess, onError);

and you will get back a FileEntry object which you can then get the fullPath property which is perfect for the FileTransfer.upload() method.

LucyJp said...

Hi Simon, thank you for your response, it worked very well!! Thank you!

David S.Y. Wong said...

Hi Simon,

Thanks for the excellent help you provided me along with the fixes for FileEntry.remove() and the double image bug. Nice write-up on this page.

Cheers,
David

Greg said...

Hi Simon,

By default - does taking a picture on the Android iOS version save it to your Album?

Thanks,
Greg

Simon Mac Donald said...

@Greg

It does on my Samsung device. Can't promise that it will work that way on all Android devices. Gotta love fragmentation.

James Henderson said...

Hi Simon

Thank you for all your excellent work with PhoneGap and this post is very helpful. I am working on iOS and Android with PhoneGap 1.1.0 and have found that there is a huge difference between phonegap-1.1.0.js file for the two operating systems. For example, when I create a new xCode app, it creates a phonegap-1.1.0.js file of 125,723 bytes, but the Android sample uses phonegap-1.1.0.js which is 140,170 bytes. Does this mean I need to use two different phonegap.js files for the two operating systems?

Simon Mac Donald said...

@James

Yes, the .js files are platform dependent. you need a different one for each platform. we are working on it.

Salman FF said...

Thx for all your help Simon.
I also find there are big differences between Android & iOS implementations. With Android, I can use your clarifications to give users access to all their photos in the photo library. (thx @LucyJP for the question :) )
Do you know of any way to do that in iOS?
Browsing around a little, it seems the way to do it is via the ALAssetsLibrary library. Do you know of any phone gap plugins giving access to it? (There is a bar code scanner plugin using the library but no camera roll library!)
Thanks!

Simon Mac Donald said...

@Salman FF

I think you just want to set the Camera.PictureSourceType to PHOTOLIBRARY or SAVEDPHOTOALBUM to get the data you want.