Save HTML5 Canvas Image to Gallery – Phonegap Android plugin

Update

Readers of this post have raised issues with the plugin. I have to personally check for the issues but while using the plugin if you find any issues, you might want to debug it in ADT. I will come up with a working plugin soon. Thanks.

Sometime back I worked on a Phonegap Android plugin that helps to save an HTML5 Canvas Image to the device’s gallery. Well, the title of this post may be misleading but note that when you save an image, you actually have to save it on the SD Card or the device’s memory. The Gallery is just an app that shows the collection of images from various locations on the SD Card. So, there’s nothing like saving an image directly to the Gallery.
I had this working with Phonegap 2.2.0 (the version that I used). Newer versions of Phonegap/Cordova are available and things might have changed a bit, specially in the way custom plugins are written. Hence, if you are using newer versions of Phonegap you should have a look at the official documentation before proceeding.
Alright, here is what I did. I tried to save an image using the FileWriter – Phonegap API, but as it turned out, it can save only text data on the device’s memory. So, only way to do it was write a Phonegap plugin, pass the Canvas details from JavaScript interface to the Java side and let the Java class save the Canvas image on the SD Card.
Note that I am not a Java developer, and there may be better ways of writing this plugin.

How to install the plugin?
You need to get hold of two files :
1) The Java class – SavePhotoPlugin.java
2) The JavaScript interface – SavePhotoPlugin.js

Both files and project details are available on Github. So go to the repo : https://github.com/jsphkhan/rialabphonegap/tree/master/android and grab the files under the folder - Save HTML5 Canvas Image to Gallery

With both the files ready, let’s see how to use it and get it working. Firstly you have to add these two files to your Phonegap Android project. Add the SavePhotoPlugin.js file inside assets/www/ folder and provide a reference to it in your index.html file, like this

<script type="text/javascript" charset="utf-8" src="SavePhotoPlugin.js"></script>

Then create a directory inside your project’s src folder that matches the package name of SavePhotoPlugin.java class. For our case make a directory – /org/apache/cordova/plugin inside src and then paste SavePhotoPlugin.java inside it. If you change the package name, make sure to change the directory structure as well. The package name can be found at the top of SavePhotoPlugin.java file.

Next thing to do is to register the plugin in the config file – open res/xml/config.xml and then add the plugin details given below to the <plugins></plugins> section of the XML file. The name attribute is the Java class name and the value is the path of the class which should match the package name.

<plugin name="SavePhotoPlugin" value="org.apache.cordova.plugin.SavePhotoPlugin"/>

And then call the plugin inside your javascript (your script.js file or app.js, whatever you have named it) code like this. (You can call this inside a button click handler or so),

var canvas = document.getElementById("your_canvas_id");
window.savephotoplugin(canvas,"image/png",device.version,function(val){ 
  //returns you the saved path in val	
		alert("Photo Saved: " + val);	
});

If everything goes fine, you will see an alert box with the path of the saved image. For Android version greater than 2.3.3, the Canvas image will be saved at /mnt/sdcard/Pictures folder. And for Android lesser than or equals to 2.3.3 the image will be saved at /mnt/sdcard. For both cases the saved image will appear in the device Gallery. For this, I invoke the device’s media scanner to add the saved image to the Media Provider’s database, thereby making it available in the device Gallery and to other apps.

Right now, I have hard coded the mime-type of the saved image to be a .png image (this was my requirement). You can easily change it as per your need to image/png or image/jpeg in the call to window.savephotoplugin() and the corresponding type of image will be saved in your device.

Check out the source code of both the plugin’s file and you would understand better.

Update:

HTML5 Canvas – toDataURL() support for Android devices – working Phonegap 2.2.0 Plugin
For Android 2.3 (in general Android < 4.0) devices the native HTML5 Canvas- toDataURL() function does not work, which would otherwise give a base64 encoded string  as image data. So I have a plugin which provides native support for low end Android versions and gives you a base64 encoded string of your HTML5 Canvas image. Check it out here.

About these ads

10 thoughts on “Save HTML5 Canvas Image to Gallery – Phonegap Android plugin

  1. Pingback: HTML5 Canvas – toDataURL() support for Android devices – working Phonegap 2.2.0 Plugin | RIA Lab

  2. Pingback: Saving Image to Android device’s Gallery – Phonegap Android | RIA Lab

  3. This is a great plugin and works fine in Android 4.0.3. To get it working in Cordova 3.0 you just have to add the line in the config.xml like this:

    The tag has been deprecated in Cordova 3.0.

    The bad part is that in Android 4.2.1 you can find two problems:
    1. in my device you can not write in the Pictures Directory. I get an error. I had to change it to the DCIM directory. That was in the line 143 of the .java:
    // Environment.DIRECTORY_PICTURES
    Environment.DIRECTORY_DCIM
    2. I don’t know why but if I have a canvas with an image as background and I paint it with beginPath() and stroke() and then I save that canvas with this plugin, the image that I get is just the canvas with the background image without the paints. That didn’t happened in 4.0.3 :(

    Any idea?

    Thanks in advance

  4. It seems to be something about the toCanvasURL method. It only saves the background while ignores the paths…

    I’ll have to continue researching.

  5. Hello again Joseph

    In first place, I saw that the tag to put into the config.xml disappeared. I supose ti didn’t like to the comment editor. The code must be something like this (I replaced the > with “greater than”, the < with "lesser than" and the 7 with "slash")

    "less than" feature name="SavePhotoPlugin" "greater than"
    "less than" param name="android-package" value="org.apache.cordova.plugin.SavePhotoPlugin" "slash" "greater than"
    "less than" "slash" feature "greater than"

    In second place, I have found a solution digging in the internet. In fact, I found a workaround. I thought "maybe I can convert the canvas to a base64 image and the save it directly". So I did something like this:

    1. To insert the background intro de canvas you can use a code like this:

    //Create an intermediate canvas called cmix and get its context, ctxmix
    // then, fill it with a white rectangle, just to erase all.
    ctxmix.fillStyle='rgba(255,255,255,1)';
    ctxmix.fillRect(0,0,width,height);
    // then, draw the background_image into the new canvas
    ctxmix.drawImage(background_image,0,0);
    // and draw the content of the original canvas into de intermediate canvas
    ctxmix.drawImage(originalcanvas,0,0);
    // at last, convert it to an image
    var image_data = cmix.toDataURL('image/png');

    2. Save directly the image_data into a file. To do it, I modified your code (I'll send it to you so you can use it if you want to).
    a. The new way to call the function is this. For some reason, it only worked into de setTimeout function.
    setTimeout(function(){
    window.savephotoplugin(cmix,"image/png",window.device.version, image_data,function(val){
    alert('Image saved' );
    })
    },1);

    b. I Modified the SavePhotoPlugin.js so the call finished like this:
    window.savephotoplugin = function(canvasEl,mimeType,appVersion,dataurl,callback){

    [your code]

    if(dataurl == null){
    // if the parameter dataurl is empty, your original code is executed.
    //call the Plugin execute method()
    cordova.exec(callback,function(err){
    callback("Error: " + err);
    },"SavePhotoPlugin","savePhoto",[canvasProps.mimeType,
    canvasProps.xpos,
    canvasProps.ypos,
    canvasProps.width,
    canvasProps.height,
    canvasProps.screenWidth,
    appVersion,
    dataurl]);
    } else {
    // else, I call mine
    //call the Plugin execute method()
    cordova.exec(callback,function(err){
    callback("Error: " + err);
    },"SavePhotoPlugin","saveImage",[canvasProps.mimeType,
    canvasProps.xpos,
    canvasProps.ypos,
    canvasProps.width,
    canvasProps.height,
    canvasProps.screenWidth,
    appVersion,
    dataurl]);
    }

    c. convert the base64 image into a bitmap, so you can save it into the gallery:

    private String image;

    //after your code "action.equals" I put this one.
    } else {
    if (action.equals("saveImage")){
    try {
    image = data.getString(7);
    Bitmap b = this.ConvertToImage(image);
    String path = this.savePhoto(b);

    callbackContext.success(path);
    } catch (JSONException jsonEx) {
    callbackContext.error("Could not save the image");
    }
    }

    // and this is the magic. I found in here: http://stackoverflow.com/questions/17506428/convert-base64-string-to-image-in-java
    private Bitmap ConvertToImage(String image){
    try{

    String imageDataBytes = image.substring(image.indexOf(",")+1);
    InputStream stream = new ByteArrayInputStream(Base64.decode(imageDataBytes.getBytes(), Base64.DEFAULT));

    Bitmap bitmap = BitmapFactory.decodeStream(stream);
    Log.v("Ben", "Image Converted");
    return bitmap;
    }
    catch (Exception e) {
    return null;
    }
    }

    I hope this can help other people with similar problems.

    bye!

  6. i have used your plugin to one app in phonegap.While running in device i got “class not found” error as alert value :( …pls help….Thanx in advance.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s