Saving Image to Android device’s Gallery – Phonegap Android

Update
Save HTML5 Canvas Image to Gallery – Phonegap Android plugin
I have a Phonegap Android plugin that helps to save HTML5 Canvas Image to the Gallery. See it here.

Now, back to the actual post,

Quick snippet of code that will help a Phonegap Android app to save an image to the Android device’s SD Card and make it available to the Gallery app to show. 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.

Moreover 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 image data from JavaScript interface to the Java side and let the Java class save the image on the SD Card. Well I am not going to talk on the entire plugin, but I will share the Java code,

The methods below should be included inside your plugin’s Java class. The idea is simple, whatever be your image/ image data (eg. Base64 data), you take that image data and save it on the SD Card using the FileOutputStream. You get the file path by querying the external storage public directory which is the first line inside the savePhoto() method below. Now, after the image is saved you broadcast it through the System’s Media Scanner so that it becomes available to the Android Gallery app and to other apps. Hope this is clear, time for action now.

private String savePhoto(Bitmap bmp)
{
  //File imageFileFolder = new File(Environment.getExternalStorageDirectory(),"MyFolder"); //when you need to save the image inside your own folder in the SD Card
  File path = Environment.getExternalStoragePublicDirectory(
     Environment.DIRECTORY_PICTURES
  ); //this is the default location inside SD Card - Pictures folder
  //imageFileFolder.mkdir(); //when you create your own folder, you use this line.
  FileOutputStream out = null;
  Calendar c = Calendar.getInstance();
  String date = fromInt(c.get(Calendar.MONTH))
  + fromInt(c.get(Calendar.DAY_OF_MONTH))
  + fromInt(c.get(Calendar.YEAR))
  + fromInt(c.get(Calendar.HOUR_OF_DAY))
  + fromInt(c.get(Calendar.MINUTE))
  + fromInt(c.get(Calendar.SECOND));
  File imageFileName = new File(path, date.toString() + ".jpg"); //imageFileFolder
  try
  {
    out = new FileOutputStream(imageFileName);
    bmp.compress(Bitmap.CompressFormat.JPEG, 100, out);
    out.flush();
    out.close();
    scanPhoto(imageFileName.toString());
    out = null;
  } catch (Exception e)
  {
    e.printStackTrace();
  }
  return imageFileName.toString();
}
private String fromInt(int val)
{
  return String.valueOf(val);
}

/* invoke the system's media scanner to add your photo to the Media Provider's database,
* making it available in the Android Gallery application and to other apps. */
private void scanPhoto(String imageFileName)
{
  Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
  File f = new File(imageFileName);
  Uri contentUri = Uri.fromFile(f);
  mediaScanIntent.setData(contentUri);
  //this.cordova.getContext().sendBroadcast(mediaScanIntent); //this is deprecated
  this.cordova.getActivity().sendBroadcast(mediaScanIntent); 
}

Note that I am not a Java developer, and you may need to modify this code as per your needs. But it should work. First save your image (create a Phonegap Android app and test it) and after that you can see it being displayed in the gallery app. I did collect this code by browsing the net and made it work 100%. Otherwise it was difficult for me, since I did not find any concrete answer on this. Leave a suggestion if there is a better solution.

13 thoughts on “Saving Image to Android device’s Gallery – Phonegap Android

  1. Well, this clarified a lot. Everything is clear now, except this:

    quote: The methods below should be included inside your plugin’s Java class

    If only I knew where to put the java code, and how to be able to access it from inside my javascript, it could be working. I’ve been searching for this all day.

    I hope you can clarify this for me🙂

    • Joucke,
      You can see the official documentation for creating a Phonegap Android plugin. Its straight forward. You will have a java script file and a Java class. Then you communicate between the two. So the methods that I described in my post will be inside the Java class. See this – http://docs.phonegap.com/en/2.2.0/guide_plugin-development_index.md.html#Plugin%20Development%20Guide
      and
      http://docs.phonegap.com/en/2.2.0/guide_plugin-development_android_index.md.html#Developing%20a%20Plugin%20on%20Android
      Thanks.

      • Thanks, that helped a lot🙂

        A sidenote (though I know you wrote this for phonegap), on cordova the java methods should be void, and call a callbackContext. Also see http://cordova.apache.org/docs/en/2.4.0/guide_plugin-development_index.md.html

        Also, the cordova camera.getPicture methods already save the photo (if called with Camera.DestinationType.FILE_URI), so you don’t have to save the photo, just rename it🙂

        Hope this helps others who had the same problem as me!

      • Joucke,
        Thanks a lot. I am not a Java developer at all. So I missed out on all those. Thanks for pointing it out. This will help other visitors to this post.
        Joseph

      • Joseph,
        If I get this fully working, I’ll post my logic here if you want🙂
        Kind regards,
        Joucke

      • Sure you can do that, always. I will update the post. Or do you want to update it yourself, if you have the time though🙂

        Thanks.

      • I get my image from camera using Cordova 2.4

        navigator.camera.getPicture(gotPicture,function(){},{quality:100,destinationType:Camera.DestinationType.FILE_URI,sourceType:Camera.PictureSourceType.CAMERA});
        
        function gotPicture(fileName) {
          window.copyPhoto(fileName,function(newFilename) {
            console.log(newFilename); // this is the new (copied) filename.
          },function(error){console.log(error)});
        }
        
        // connect to plugin Filesystem.copyPhoto()
        window.copyPhoto = function(currentFilename,success,failure) {
          cordova.exec(success,failure,"Filesystem","copyPhoto",[currentFilename]);
        }
        

        The java plugin extends CordovaPlugin:

        public class Filesystem extends CordovaPlugin {
        	@Override
        	public boolean execute(String action, JSONArray args, CallbackContext callbackContext) 
        			throws JSONException {
        		if("copyPhoto".equals(action)) {
        			this.copyPhoto(args.getString(0), callbackContext);
        			callbackContext.success();
        			return true;
        		}
        		return false;
        	}
        	
        	private void copyPhoto(String filePath, CallbackContext callbackContext) {
        		String errorText = "";
        		String[] filePathParts = filePath.split("://");
        		filePath = filePathParts[1];
        		File ext =  Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        		try {
        			FileChannel inChannel = new FileInputStream(filePath).getChannel();
        			File outFile = new File(ext, filenameFromCurrentDate()+".jpg");
        			FileChannel outChannel = new FileOutputStream(outFile).getChannel();
        			try {
        				inChannel.transferTo(0,inChannel.size(),outChannel);
        			}
        			finally {
        				if(inChannel!=null) inChannel.close();
        				if(outChannel!=null) outChannel.close();
        			}
        			scanPhoto(outFile.getPath());
        			callbackContext.success(outFile.getPath());
        		}
        		catch(IOException e) {
        			callbackContext.error("error: "+errorText);
        			e.printStackTrace();
        		}
        	}
        	
        	private String filenameFromCurrentDate() {
        		Calendar c = Calendar.getInstance();
        		String date = fromInt(c.get(Calendar.YEAR)) + fromInt(c.get(Calendar.MONTH)) + 
        				fromInt(c.get(Calendar.DAY_OF_MONTH)) + fromInt(c.get(Calendar.HOUR_OF_DAY)) + 
        				fromInt(c.get(Calendar.MINUTE)) + fromInt(c.get(Calendar.SECOND));
        		return date;
        	}
        	
        	private String fromInt(int val) {
        		return String.valueOf(val);
        	}
        	
        	private void scanPhoto(String imageFileName) {
        		Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        		File f = new File(imageFileName);
        		Uri contentUri = Uri.fromFile(f);
        		mediaScanIntent.setData(contentUri);
        		this.cordova.getActivity().sendBroadcast(mediaScanIntent);
        	}
        }

        Hope this helps others!

  2. Hum.

    Re-read my code, and replaced the copyPhoto function:

    	private void copyPhoto(String filePath, CallbackContext callbackContext) {
    		String[] filePathParts = filePath.split("://");
    		filePath = filePathParts[filePathParts.length-1];
    		File oldFile = new File(filePath);
    		
    		File ext =  Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    		File newFile = new File(ext,filenameFromCurrentDate()+".jpg");
    		
    		if(oldFile.renameTo(newFile)) {
    			scanPhoto(newFile.getPath());
    			callbackContext.success(newFile.getPath());
    		}
    		else {
    			callbackContext.error("Could not move "+oldFile.getPath()+" to "+
    					newFile.getPath());
    		}
    	}
    • Joucke,

      Thanks a lot for sharing this. I will update this in the post and give your credits.
      Let me know if you would like to be an author for this blog. You can contribute (occasionally..) if you would like to.

      Joseph

  3. Joseph, is the java class a page of its own? (I’m even less of a Java person than you are!) I’m uncertain where the code is to be placed in the project. If this is a plugin what do you add to the config.xml page to initiate this? Thanks.

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