Deleting a gallery image after camera intent photo taken

AndroidImageAndroid Camera-Intent

Android Problem Overview


I know this has been asked in many different ways but I still can not seem to delete the gallery image from the default folder. I'm saving the file to the SD card correctly and I can delete that file fine, but the default gallery image file that shows under the folder Camera will not delete.

I would like the image to delete once the activity is returned since the file is already stored on the SD card under /Coupon2.

Any suggestions?

public void startCamera() {
    Log.d("ANDRO_CAMERA", "Starting camera on the phone...");

    mManufacturerText = (EditText) findViewById(R.id.manufacturer);
	String ManufacturerText = mManufacturerText.getText().toString();
	String currentDateTimeString = new Date().toString();
	
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    File filedir = new File(Environment.getExternalStorageDirectory()+"/Coupon2");
    filedir.mkdirs();
    
    File file = new File(Environment.getExternalStorageDirectory()+"/Coupon2", ManufacturerText+"-test.png");
    outputFileUri = Uri.fromFile(file);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);

    startActivityForResult(intent, CAMERA_PIC_REQUEST);
}

protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
	super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == CAMERA_PIC_REQUEST && resultCode == -1) {  
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.putExtra("crop", "true");
        intent.putExtra("scale", "true");
        
        intent.putExtra("return-data", false);
        intent.setDataAndType(outputFileUri, "image/*");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
        startActivityForResult(intent, CAMERA_CROP_REQUEST);
    }else { 
       	SetImage();
       	saveState();
    }
}

Android Solutions


Solution 1 - Android

My application requires me to call an intent to take a photo. The photo cannot be in the gallery but instead must be in a specific directory on the SD card.

Originally I just used the EXTRA_OUTPUT, but I soon discovered the following:

  • Some devices use it completely and skip the gallery.
  • Some devices ignore it completely and ONLY use the gallery.
  • Some devices really suck and save a full sized image to the gallery, and save a thumbnail only to the location I wanted. (HTC you know who you are...)

So, I can't blindly delete a gallery file when I'm done. The last added photo may or may not be the one I want to remove. Also, I may have to copy that file replacing my own afterwards. Because my activity is 2000 lines, and my company wouldn't want all of our code being posted, I'm posting only the methods involved in doing this. Hopefully this helps.

Also, I'll state, this is my first Android application. I wouldn't be surprised if there was a better way to do this that I just don't know about, but this is what's working for me!

So, here is my solution:

First, in my application context I define a variable as follows:

public ArrayList<String> GalleryList = new ArrayList<String>();

Next, in my activity, I define a method to get a list of all photos in the gallery:

private void FillPhotoList()
{
   // initialize the list!
   app.GalleryList.clear();
   String[] projection = { MediaStore.Images.ImageColumns.DISPLAY_NAME };
   // intialize the Uri and the Cursor, and the current expected size.
   Cursor c = null; 
   Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
   //
   // Query the Uri to get the data path.  Only if the Uri is valid.
   if (u != null)
   {
      c = managedQuery(u, projection, null, null, null);
   }
    
   // If we found the cursor and found a record in it (we also have the id).
   if ((c != null) && (c.moveToFirst())) 
   {
      do 
      {
        // Loop each and add to the list.
        app.GalleryList.add(c.getString(0));
      }     
      while (c.moveToNext());
   }
}

Here's a method to return a unique file name for my new image:

private String getTempFileString()
{
   // Only one time will we grab this location.
   final File path = new File(Environment.getExternalStorageDirectory(), 
         getString(getApplicationInfo().labelRes));
   //
   // If this does not exist, we can create it here.
   if (!path.exists())
   {
      path.mkdir();
   }
   //
   return new File(path, String.valueOf(System.currentTimeMillis()) + ".jpg").getPath();
}

I have three variables in my Activity that store information for me about a current file. A string (path), a File variable, and a URI to that file:

public static String sFilePath = ""; 
public static File CurrentFile = null;
public static Uri CurrentUri = null;

I never set these directly, I only call a setter on the file path:

public void setsFilePath(String value)
{
   // We just updated this value. Set the property first.
   sFilePath = value;
   //
   // initialize these two
   CurrentFile = null;
   CurrentUri = null;
   //
   // If we have something real, setup the file and the Uri.
   if (!sFilePath.equalsIgnoreCase(""))
   {
      CurrentFile = new File(sFilePath);
      CurrentUri = Uri.fromFile(CurrentFile);
   }
}

Now I call an intent to take a photo.

public void startCamera()
{
   Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
   // Specify the output. This will be unique.
   setsFilePath(getTempFileString());
   //
   intent.putExtra(MediaStore.EXTRA_OUTPUT, CurrentUri);
   //
   // Keep a list for afterwards
   FillPhotoList();
   //
   // finally start the intent and wait for a result.
   startActivityForResult(intent, IMAGE_CAPTURE);
}

Once this is done, and the activity comes back, here is my code:

protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
   if (requestCode == IMAGE_CAPTURE)
   {
      // based on the result we either set the preview or show a quick toast splash.
      if (resultCode == RESULT_OK)
      {
         // This is ##### ridiculous.  Some versions of Android save
         // to the MediaStore as well.  Not sure why!  We don't know what
         // name Android will give either, so we get to search for this
         // manually and remove it.  
         String[] projection = { MediaStore.Images.ImageColumns.SIZE,
                                 MediaStore.Images.ImageColumns.DISPLAY_NAME,
                                 MediaStore.Images.ImageColumns.DATA,
                                 BaseColumns._ID,};
         //    
         // intialize the Uri and the Cursor, and the current expected size.
         Cursor c = null; 
         Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
         //
         if (CurrentFile != null)
         {               
            // Query the Uri to get the data path.  Only if the Uri is valid,
            // and we had a valid size to be searching for.
            if ((u != null) && (CurrentFile.length() > 0))
            {
               c = managedQuery(u, projection, null, null, null);
            }
            //   
            // If we found the cursor and found a record in it (we also have the size).
            if ((c != null) && (c.moveToFirst())) 
            {
               do 
               {
                  // Check each area in the gallary we built before.
                  boolean bFound = false;
                  for (String sGallery : app.GalleryList)
                  {
                     if (sGallery.equalsIgnoreCase(c.getString(1)))
                     {
                        bFound = true;
                        break;
                     }
                  }
                  //       
                  // To here we looped the full gallery.
                  if (!bFound)
                  {
                     // This is the NEW image.  If the size is bigger, copy it.
                     // Then delete it!
                     File f = new File(c.getString(2));
                     
                     // Ensure it's there, check size, and delete!
                     if ((f.exists()) && (CurrentFile.length() < c.getLong(0)) && (CurrentFile.delete()))
                     {
                        // Finally we can stop the copy.
                        try
                        {
                           CurrentFile.createNewFile();
                           FileChannel source = null;
                           FileChannel destination = null;
                           try 
                           {
                              source = new FileInputStream(f).getChannel();
                              destination = new FileOutputStream(CurrentFile).getChannel();
                              destination.transferFrom(source, 0, source.size());
                           }
                           finally 
                           {
                              if (source != null) 
                              {
                                 source.close();
                              }
                              if (destination != null) 
                              {
                                 destination.close();
                              }
                           }
                        }
                        catch (IOException e)
                        {
                           // Could not copy the file over.
                           app.CallToast(PhotosActivity.this, getString(R.string.ErrorOccured), 0);
                        }
                     }
                     //       
                     ContentResolver cr = getContentResolver();
                     cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
                        BaseColumns._ID + "=" + c.getString(3), null);
                     break;                        
                  }
               } 
               while (c.moveToNext());
            }
         }
      }
   }      
}

Solution 2 - Android

This will delete the file from gallery:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == CAMERA_PIC_REQUEST && resultCode == RESULT_OK) { 

        /* Copy the file to other directory or whatever you want */

        // mContext is the context of the activity
        mContext.getContentResolver().delete(data.getData(), null, null);
    }
 }

About the EXTRA_OUTPUT not standard behaviour. I think this pseudo-algorithm should work in every case: > 1) Do not use EXTRA_OUTPUT. The image/photo always will go to the gallery location.

> 2) Copy the file from the gallery location to the desired location.

> 3) Remove (with the upper code) the file from gallery.

But of course it seems to be too perfect... in some devices (for example the original Galaxy Tab with android 2.3), you have to use EXTRA_OUTPUT with ACTION_IMAGE_CAPTURE, without this the intent does not work.

Solution 3 - Android

If someone is looking for a simpler work around to this problem, here is how I solved the issue.

I have a capture button and when it is pressed the intent is sent, what I added is that I also go and get the last id from image mediastore and store it:

/**
 * Gets the last image id from the media store
 * @return
 */
private int getLastImageId(){
	final String[] imageColumns = { MediaStore.Images.Media._ID };
	final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
	final String imageWhere = null;
	final String[] imageArguments = null;
	Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, imageWhere, imageArguments, imageOrderBy);
	if(imageCursor.moveToFirst()){
		int id = imageCursor.getInt(imageCursor.getColumnIndex(MediaStore.Images.Media._ID));
		imageCursor.close();
		return id;
	}else{
		return 0;
	}
}

Then when the activity returns I run this code to checks for the last image id before capture, then queries for images after capture have an id larger then recorded and if it is more then one deletes the record located at the location I specified for the camera to save in.

/*
 * Checking for duplicate images
 * This is necessary because some camera implementation not only save where you want them to save but also in their default location.
 */
final String[] imageColumns = { MediaStore.Images.Media.DATA, MediaStore.Images.Media.DATE_TAKEN, MediaStore.Images.Media.SIZE, MediaStore.Images.Media._ID };
final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
final String imageWhere = MediaStore.Images.Media._ID+">?";
final String[] imageArguments = { Integer.toString(MyActivity.this.captureLastId) };
Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, imageWhere, imageArguments, imageOrderBy);
if(imageCursor.getCount()>1){
	while(imageCursor.moveToNext()){
		int id = imageCursor.getInt(imageCursor.getColumnIndex(MediaStore.Images.Media._ID));
		String path = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
		Long takenTimeStamp = imageCursor.getLong(imageCursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN));
		Long size = imageCursor.getLong(imageCursor.getColumnIndex(MediaStore.Images.Media.SIZE));
		if(path.contentEquals(MyActivity.this.capturePath)){
			// Remove it
			ContentResolver cr = getContentResolver();
			cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media._ID + "=?", new String[]{ Long.toString(id) } );
			break;
		}
	}				
}
imageCursor.close();

For me this was a much simpler solution, and I tested on my HTC which was having this issue.

Another side note, I originally used DATE_TAKEN not _ID as the parameter but there seems to be some bug that on the emulator some of the images being captured through the intent had their millsecond DATE_TAKEN times multiplied by a 1000 so I switched to _ID which seems to be much more robust.

Solution 4 - Android

Well the whole problem I think is coming from the fact that you are expecting a certain result from another application which is not happening. Which is (just to be clear) -

  • Launch an activity that can take picture, since your application doesn't capture images.
  • Tell the other application where you want to save the picutre
  • Use the saved image at the location you specified.
  • But the problem is the other application has also saved the image at a different location also, which you don't want it to.

Now lets look at it from the other application perspective, which is capturing the image. Lets consider two scenarios (Which is possible) -

  • First, The application is launched with the option of the fileName or location where the image is to be saved.
  • Second, the application is launched without any extra information like the fileName or location where the image is to be saved.(This could happen if the capturing app is lauched directly from the menu)

Now ideally what should have happenend is if there is extra information like fileName or location then he should have used that location if not then he can use his own. But alas we are from an ideal world and since nothing is written in stone on what should happen when you launch a camera application the camera app developer will have his own interpretation.

Since different devices have different camera application set as the default(yes, the stock camera is usually replaced) the results obtained are/will be different.

So in some camera's it might be saved in only one place in other it might be saved in the Camera folder as well (since the camera application might always save the captured image in the camera folder and saving it in a different location is bonus given by the application)

If you just pass a fileName to the camera application should the application return after just taking one photo? I think not. So what should in such a scenario? It's all very ambiguous or an gray area.

Now if you don't want it to be saved in the Camera folder then find out if can just get the filename of the recently captured image and then delete it from your application. Or don't tell the camera application where it needs to be saved, just get the filename and move it location that you want.

If you don't get the filename then you are at the mercy of the other application developer. (Hey! it's not his fault either, he just designed it the way he wants!)

Solution 5 - Android

I think you can't do what you want. That's sad but I can't find another answer.

If you work with google Camera implementation it works fine. It just doesn't store photo to gallery in EXTRA_OUTPUT is specified.

But when you face some other devices they may do something completely different. That's because HTC, LG and maybe some others have custom Camera implementation and you can't do anything about that. You can leave it as it is or write your own Camera that will work exactly the way you need it.

Actually it's not related to this question, but one day you'll find out that CROP intent doesn't work on some devices (http://groups.google.com/group/android-developers/browse_frm/thread/2dd647523926192c/4b6d087073a39607?tvc=1&pli=1). So again if you need it you'll have to write it yourself.

Solution 6 - Android

This is not an easy task to solve but can approach a tricky method.If any one need a simple and working solution for this. Try the below code:

  1. Save the current time in milli second before calling the camera intent.
  2. OnActivityResult query the image uri whose taken date is greater than the millisecond in step1. And delete the file. Thats it.

String[] projection = {MediaStore.Images.ImageColumns.SIZE,
                MediaStore.Images.ImageColumns.DISPLAY_NAME,
                MediaStore.Images.ImageColumns.DATA,
                BaseColumns._ID,MediaStore.Images.ImageColumns.DATE_ADDED};
        final String imageOrderBy = MediaStore.Images.Media._ID + " DESC";
        final String selection = MediaStore.Images.Media.DATE_TAKEN+" > "+mImageTakenTime;
        //// intialize the Uri and the Cursor, and the current expected size.
        Cursor c = null;
        Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        c = getActivity().managedQuery(u, projection, selection, null, imageOrderBy);
        if(null != c && c.moveToFirst()){
            ContentResolver cr = getActivity().getContentResolver();
            cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    BaseColumns._ID + "=" + c.getString(3), null);
        }

Solution 7 - Android

Look here - that's a piece of code which saves picture to EXTRA_OUTPUT folder without saving it to gallery. In this way in my application I used to grab picture directly from camera and then delete taken picture.

Solution 8 - Android

This is the code I use which takes a pic and saves it at the location specified

Uri outputFileUri;

public void takePhoto() {
	File directory = new File(Environment.getExternalStorageDirectory()
			+ "/HI-Tech" + "/");

	if (!directory.exists()) {
		directory.mkdir();
	}

	int count = 0;
	if (directory != null) {
		File[] files = directory.listFiles();
		if (files != null) {
			count = files.length;
		}
	}
	count++;
	String imagePath = "IMAGE_" + count + ".jpg";
	File file = new File(directory, imagePath);
	outputFileUri = Uri.fromFile(file);

		Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
	intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
	startActivityForResult(intent, JobActivity.TAKE_PIC);
}

I then handle the response.

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	try {
		if (resultCode == RESULT_OK) {
			if (requestCode == JobActivity.TAKE_PIC) {
				Bitmap bitmap = decodeFile(new File(outputFileUri.getPath()), 160, 120);
	} catch (Exception e) {
		Log.e("Error", "Unable to set thumbnail", e);
	}
}

I had to declare the outputFileUri as a global variable as I found no way of acquiring the saved path onActivityResult. By passing the outputFileUri you will notice that the image isn't saved in the Camera folde but at the location specified. I've tried this code on my Nexus1 and a cheap samsung thing.

Hope this helps

Solution 9 - Android

I also was struggling with this issue. By the way, this issue is marked as Obsolete in the Android bug tracker. For some reason Android team doesn't consider this as a bug anymore. Maybe because this is manufacturer-related.

It seems that all solutions (from this thread and other blog posts etc.) form the following algorithm:

  1. Save a unique identifier of the last taken photo;
  2. Launch camera activity using Intent with EXTRA_OUTPUT ;
  3. Take a photo and check whether an id of the last taken photo in Gallery has changed to decide whether it should be deleted.

Something similar is proposed in the accepted answer.

But I found that such approach cannot be used in a production code. For example, let's image a simple situation:

You store some id of the last taken photo in Gallery and launch camera activity. When the user takes a photo on some devices the camera activity shows a dialog with Cancel and OK buttons (or something like that). And the logic behind them is as follows:

  • if the user presses Cancel he returns to the camera activity and can continue to take photos.
  • if he/she (the user) presses OK button the camera activity returns the result to your app.

The important notes here: it turns out that on some devices (I tested on LG and HTC) the image can be already saved while this dialog being presented.

Now imagine what if the user for some reason decides to tap Home button and starts another app (e.g. another camera app where the user can take different photos!). And then returns to your app where that dialog is still presented and presses OK. Obviously in that case your app your will delete the wrong file... and disappoint the user :(

So after a lot of researching I decided that the best approach to overcome this problem is to write your own camera engine or just use some third-party libs like this: material camera.

Solution 10 - Android

Your best bet is probably to handle the Camera class directly, and then store the jpeg or raw returned in the callback where you want.

Alternatively, you could try and delete the taken image by _id from the content provider for media after it's been added. Just find it by query and delete it via the ContentResolver.delete, not sure about the implementation though.

Solution 11 - Android

After struggeling with this for some time, I bit the bullet and wrote my own camera caputere activity. I am convinced this is much more portable and safer than the MediaStore.ACTION_IMAGE_CAPTURE solutions. The image will be stored where you store it and nowhere else and you are not in danger to delete some unrelated file by accident. Also, it is possible to adapt the actual camera function exactly to the requirements.

For portability reasons, I used the Camera class, not camera2.

It is worth to note that the camera parameters should all be set, especially picture size, focus mode and flash mode, they all may be in an unexpected state when starting the camera.

Posting this as an answer, not as a comment, because in my opinion it is the correct answer resulting in minimal effort.

Solution 12 - Android

protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    if (requestCode == CAMERA_REQUEST && resultCode == RESULT_OK) {

	Bitmap photo = (Bitmap) data.getExtras().get("data");
	Uri tempUri = Utils.getUri(getActivity(), photo);
	File finalFile = new File(getRealPathFromURI(tempUri));
	finalFile.delete();
    }  
}


public String getRealPathFromURI (Uri contentUri) {
	String path = null;
	String[] proj = { MediaStore.MediaColumns.DATA };
	Cursor cursor = getActivity().getContentResolver().query(contentUri, proj, null, null, null);
	if (cursor.moveToFirst()) {
	    int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
	    path = cursor.getString(column_index);
	}
	cursor.close();
	return path;
}

Solution 13 - Android

Below code not only deletes the image file but also deletes the 0Byte file that gets left behind when filw is deleted. I observed this 0Byte ghost file behavior in Android 10.

    val fileToDelete = File(photoUri.path)
    if (fileToDelete.exists()) {
        if (fileToDelete.delete()) {
            if (fileToDelete.exists()) {
                fileToDelete.canonicalFile.delete()
                if (fileToDelete.exists()) {
                    getApplicationContext().deleteFile(fileToDelete.name)
                }
            }
            Log.e("File", "Deleted " + savedPhotoUri?.path)
        } else {
            Log.e("File", "not Deleted " + savedPhotoUri?.path)
        }
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionandroid-overflowView Question on Stackoverflow
Solution 1 - AndroidPaulView Answer on Stackoverflow
Solution 2 - AndroidElYeanteView Answer on Stackoverflow
Solution 3 - AndroidEmil DavtyanView Answer on Stackoverflow
Solution 4 - AndroidbluefalconView Answer on Stackoverflow
Solution 5 - AndroidFedorView Answer on Stackoverflow
Solution 6 - Androidms shaijuView Answer on Stackoverflow
Solution 7 - AndroidBarmaleyView Answer on Stackoverflow
Solution 8 - AndroidBearView Answer on Stackoverflow
Solution 9 - AndroidStan MotsView Answer on Stackoverflow
Solution 10 - AndroidHaMMeReDView Answer on Stackoverflow
Solution 11 - AndroidMoritz BothView Answer on Stackoverflow
Solution 12 - AndroidVignesView Answer on Stackoverflow
Solution 13 - AndroidRamakrishna JoshiView Answer on Stackoverflow