Android custom image gallery – part 2 – Add capture button and update gallery


This is the squeal of my previous post to create custom image gallery.

In this post, I added a button to capture new image, and update default gallery.

main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent" android:layout_height="fill_parent">
	<LinearLayout android:id="@+id/customGalleryFooter"
		android:layout_width="fill_parent" android:layout_height="wrap_content"
		android:orientation="horizontal" android:gravity="center"
		android:layout_alignParentBottom="true">
		<Button android:id="@+id/captureBtn" android:layout_width="wrap_content"
			android:layout_height="wrap_content" android:text="Capture"
			android:minWidth="200px" />
		<Button android:id="@+id/selectBtn" android:layout_width="wrap_content"
			android:layout_height="wrap_content" android:text="Select"
			android:minWidth="200px" />
	</LinearLayout>
	<GridView android:id="@+id/PhoneImageGrid"
		android:layout_width="fill_parent" android:layout_height="fill_parent"
		android:numColumns="auto_fit" android:verticalSpacing="10dp"
		android:horizontalSpacing="10dp" android:columnWidth="90dp"
		android:stretchMode="columnWidth" android:gravity="center"
		android:layout_above="@id/customGalleryFooter" />
</RelativeLayout>

AndroidCustomGalleryActivity.java

package com.isummation.customgallery;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;

public class AndroidCustomGalleryActivity extends Activity {
	public ImageAdapter imageAdapter;
	private final static int TAKE_IMAGE = 1;
	private Uri imageUri;
	private MediaScannerConnection mScanner;
	public GridView imagegrid;
	private long lastId;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		imageAdapter = new ImageAdapter();
		imageAdapter.initialize();
		imagegrid = (GridView) findViewById(R.id.PhoneImageGrid);
		imagegrid.setAdapter(imageAdapter);

		final Button selectBtn = (Button) findViewById(R.id.selectBtn);
		selectBtn.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				final int len = imageAdapter.images.size();
				int cnt = 0;
				String selectImages = "";
				for (int i = 0; i < len; i++) {
					if (imageAdapter.images.get(i).selection) {
						cnt++;
						selectImages = selectImages
								+ imageAdapter.images.get(i).id + ",";
					}
				}
				if (cnt == 0) {
					Toast.makeText(getApplicationContext(),
							"Please select at least one image",
							Toast.LENGTH_LONG).show();
				} else {
					selectImages = selectImages + "-1";
					Intent intent = new Intent(getApplicationContext(),
							UploadQueue.class);
					intent.putExtra("Ids", selectImages);
					startActivityForResult(intent, 2);
				}
				
			}
		});
		final Button captureBtn = (Button) findViewById(R.id.captureBtn);
		captureBtn.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
				String fileName = "IMG_" + sdf.format(new Date()) + ".jpg";
				File myDirectory = new File(Environment
						.getExternalStorageDirectory() + "/REOAllegiance/");
				myDirectory.mkdirs();
				File file = new File(myDirectory, fileName);
				imageUri = Uri.fromFile(file);
				Intent intent = new Intent(
						android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
				intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
				startActivityForResult(intent, TAKE_IMAGE);
			}
		});
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		switch (resultCode) {
		case RESULT_OK:
			try {
				// we need to update the gallery by starting MediaSanner service.
				mScanner = new MediaScannerConnection(
						AndroidCustomGalleryActivity.this,
						new MediaScannerConnection.MediaScannerConnectionClient() {
							public void onMediaScannerConnected() {
								mScanner.scanFile(imageUri.getPath(), null /* mimeType */);
							}

							public void onScanCompleted(String path, Uri uri) {
								//we can use the uri, to get the newly added image, but it will return path to full sized image
								//e.g. content://media/external/images/media/7
								//we can also update this path by replacing media by thumbnail to get the thumbnail
								//because thumbnail path would be like content://media/external/images/thumbnail/7
								//But the thumbnail is created after some delay by Android OS
								//So you may not get the thumbnail. This is why I started new UI thread
								//and it'll only run after the current thread completed.
								if (path.equals(imageUri.getPath())) {
									mScanner.disconnect();
									//we need to create new UI thread because, we can't update our mail thread from here
									//Both the thread will run one by one, see documentation of android  
									AndroidCustomGalleryActivity.this
											.runOnUiThread(new Runnable() {
												public void run() {
													updateUI();
												}
											});
								}
							}
						});
				mScanner.connect();

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public void updateUI() {
		imageAdapter.checkForNewImages();
	}

	public class ImageAdapter extends BaseAdapter {
		private LayoutInflater mInflater;
		public ArrayList<ImageItem> images = new ArrayList<ImageItem>();

		public ImageAdapter() {
			mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		}

		public void initialize() {
			images.clear();
			final String[] columns = { MediaStore.Images.Thumbnails._ID };
			final String orderBy = MediaStore.Images.Media._ID;
			Cursor imagecursor = managedQuery(
					MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns,
					null, null, orderBy);
			int image_column_index = imagecursor
					.getColumnIndex(MediaStore.Images.Media._ID);
			int count = imagecursor.getCount();
			for (int i = 0; i < count; i++) {
				imagecursor.moveToPosition(i);
				int id = imagecursor.getInt(image_column_index);
				ImageItem imageItem = new ImageItem();
				imageItem.id = id;
				lastId = id;
				Log.d("l_id", ""+ id);
				imageItem.img = MediaStore.Images.Thumbnails.getThumbnail(
						getApplicationContext().getContentResolver(), id,
						MediaStore.Images.Thumbnails.MICRO_KIND, null);
				images.add(imageItem);
			}
			imagecursor.close();
			notifyDataSetChanged();
		}
		
		public void checkForNewImages(){
			//Here we'll only check for newer images
			final String[] columns = { MediaStore.Images.Thumbnails._ID };
			final String orderBy = MediaStore.Images.Media._ID;
			Cursor imagecursor = managedQuery(
					MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns,
					MediaStore.Images.Media._ID + " > " + lastId , null, orderBy);
			int image_column_index = imagecursor
					.getColumnIndex(MediaStore.Images.Media._ID);
			int count = imagecursor.getCount();
			for (int i = 0; i < count; i++) {
				imagecursor.moveToPosition(i);
				int id = imagecursor.getInt(image_column_index);
				ImageItem imageItem = new ImageItem();
				imageItem.id = id;
				lastId = id;
				imageItem.img = MediaStore.Images.Thumbnails.getThumbnail(
						getApplicationContext().getContentResolver(), id,
						MediaStore.Images.Thumbnails.MICRO_KIND, null);
				imageItem.selection = true; //newly added item will be selected by default
				images.add(imageItem);
			}
			imagecursor.close();
			notifyDataSetChanged();
		}

		public int getCount() {
			return images.size();
		}

		public Object getItem(int position) {
			return position;
		}

		public long getItemId(int position) {
			return position;
		}

		public View getView(int position, View convertView, ViewGroup parent) {
			ViewHolder holder;
			if (convertView == null) {
				holder = new ViewHolder();
				convertView = mInflater.inflate(R.layout.galleryitem, null);
				holder.imageview = (ImageView) convertView
						.findViewById(R.id.thumbImage);
				holder.checkbox = (CheckBox) convertView
						.findViewById(R.id.itemCheckBox);

				convertView.setTag(holder);
			} else {
				holder = (ViewHolder) convertView.getTag();
			}
			ImageItem item = images.get(position);
			holder.checkbox.setId(item.id);
			holder.imageview.setId(item.id);
			holder.checkbox.setOnClickListener(new OnClickListener() {

				public void onClick(View v) {
					// TODO Auto-generated method stub
					CheckBox cb = (CheckBox) v;
					int id = cb.getId();
					if (images.get(id).selection) {
						cb.setChecked(false);
						images.get(id).selection = false;
					} else {
						cb.setChecked(true);
						images.get(id).selection = true;
					}
				}
			});
			holder.imageview.setOnClickListener(new OnClickListener() {

				public void onClick(View v) {
					// TODO Auto-generated method stub
					int id = v.getId();
					Intent intent = new Intent();
					intent.setAction(Intent.ACTION_VIEW);
					final String[] columns = { MediaStore.Images.Media.DATA };
					Cursor imagecursor = managedQuery(
							MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns,
							MediaStore.Images.Media._ID + " = " + id, null, MediaStore.Images.Media._ID);
					if (imagecursor != null && imagecursor.getCount() > 0){
						imagecursor.moveToPosition(0);
						String path = imagecursor.getString(imagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
						imagecursor.close();
						intent.setDataAndType(
								Uri.parse("file://" + path),
								"image/*");
						startActivityForResult(intent, 3);
					}
				}
			});
			holder.imageview.setImageBitmap(item.img);
			holder.checkbox.setChecked(item.selection);
			return convertView;
		}
	}

	class ViewHolder {
		ImageView imageview;
		CheckBox checkbox;
	}

	class ImageItem {
		boolean selection;
		int id;
		Bitmap img;
	}
	
	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
	}
}

Update:
I highly recommend to do not use this code, as it is old and with some bugs. Please visit my open source project page, Where I keep updating code.

Advertisements

18 thoughts on “Android custom image gallery – part 2 – Add capture button and update gallery

  1. Anne

    I love your example project……if only the part where you capture a new photo and the GridView is refreshed worked on Droid X. For whatever reason, the MediaStore is not being refreshed until I USB connect the phone (which unmounts the sd card) and then disconnect it (which causes the phone to remount the sd card). Then the new photo is shown in the GridView. Have you found this to be a bug and fixed it in a later version then the one you currently have in Github? I would love to get this working.

    Reply
    1. Vikas Post author

      You can connect your phone to computer with 2 mode, one is for debugging (if phone is supported) and one to use SD card as USB drive. Our app will not work if you’ve attached phone with USB function, as it took read/write lock. So, we’ll not consider this case. But in I see your new comment that you broadcast an action, but this will not work all the time, because, it is asynchronous task with other service. So you may not get the new image sometime.

      Reply
      1. Anne

        My problem was that MediaScanner was not working at all on my phone. The new image was never being displayed, even if I let the phone sit there for a long time. It was only when I would connect the phone USB and then disconnect it that the photo was showing up. Got any ideas why MediaScanner was not doing its job?

      2. Vikas Post author

        I need to check what is happening via logcat, when you take new image, mediascanner does not start in your case. There could be many reason, like, check code in onActivityResult, mediascanner errors, UI updater function not properly called or written etc… With android, this is the biggest issue that you can’t be sure even if your code is working in emulator. You must need to test it on real device. anyway, glad that it worked but in different way for you…

  2. Anne

    As a follow up, I was able to get it to work by replacing all the MediaScanner code with this:
    sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse(“file://”+ Environment.getExternalStorageDirectory())));

    Reply
    1. sachin

      another way i used is
      on capture button click
      public void onClick(View v)
      {
      Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      startActivityForResult(intent, TAKE_IMAGE);
      }

      and get back from captured image from camera
      case TAKE_IMAGE:
      try {
      if (resultCode == RESULT_OK) {
      imageAdapter.initialize();
      }
      } catch (Exception e) {
      e.printStackTrace();
      }
      break;

      Reply
  3. Dori

    wen i am loading all images and log out of the application and login again i am getting out of memory exception as all images are loaded (i used your code as is) do you have a solution ? android default gallery application is working great and i want to be able to have my customize applicaton where i can select images and use them later in my application (like what you did)
    ithought maybe to use lazyAdapter and to load only images which are currently visiable to the user but i amnot sure that it will solve the issue as the query return from the DB all images and load the memory (i have around 1200 images) , any idea how to solve better this memory issue ?

    Reply
    1. Vikas Post author

      You forget to tell in which screen you get memory exception? In thumbnail selection screen? or upload queue screen?
      In upload screen, we are using listview and it reuse the same list items currently visible in phone’s screen. So there should not be any memory issue in that screen. In the first screen we are also showing some of the images at a time, we use sql lite database of default gallery. So I don’t think it should make any difference. Anyway I haven’t tested with 1200 images!! for memory issue what can help you is to recycle bitmap image, and run System.gc() then after. That I did in uploading screen. Did you take a look into my open source project? Checkout this https://vikaskanani.wordpress.com/android-custom-gallery-and-instant-upload-project/ it is more efficient than this.

      Reply
  4. manoj kumar

    hi vikaskanani
    i am new to andorid
    my problem is that i have no of images with check box on it,when we select check box and click hide button then instantly images are not hide.when we close the apps and restarts app,my checked image not shown at this time .how to selected images instantly disappear

    Reply
  5. Rashmi

    hii,
    m making app which use camera surfaceview.
    using that i didnot set all captuerd images in galleryview.
    I have problem with all captured images stored in sd card as path given folder but not set in galleryview.
    Means all captured image not seen in galleryview.
    pls reply asap.

    with this single point i couldnt complete my app.

    pls help me out.

    Thanks in adv

    Reply
  6. Min Uswachoke

    hi, i am developing my own app, and it seem that the picture display is just a thumbmail(which is not the full size image, it left-out many part of the image).How could i display the full image instead of just a thumbnail

    Reply

Leave a Reply

Please log in using one of these methods to post your comment:

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