Skip to content

Instantly share code, notes, and snippets.

@samkirton
Last active November 14, 2022 13:31
Show Gist options
  • Star 46 You must be signed in to star a gist
  • Fork 15 You must be signed in to fork a gist
  • Save samkirton/0242ba81d7ca00b475b9 to your computer and use it in GitHub Desktop.
Save samkirton/0242ba81d7ca00b475b9 to your computer and use it in GitHub Desktop.
Fixed a bug where images added to the MediaStore are not inserted at the front of the gallery
package com.memtrip;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.net.Uri;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
/**
* Android internals have been modified to store images in the media folder with
* the correct date meta data
* @author samuelkirton
*/
public class CapturePhotoUtils {
/**
* A copy of the Android internals insertImage method, this method populates the
* meta data with DATE_ADDED and DATE_TAKEN. This fixes a common problem where media
* that is inserted manually gets saved at the end of the gallery (because date is not populated).
* @see android.provider.MediaStore.Images.Media#insertImage(ContentResolver, Bitmap, String, String)
*/
public static final String insertImage(ContentResolver cr,
Bitmap source,
String title,
String description) {
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, title);
values.put(Images.Media.DISPLAY_NAME, title);
values.put(Images.Media.DESCRIPTION, description);
values.put(Images.Media.MIME_TYPE, "image/jpeg");
// Add the date meta data to ensure the image is added at the front of the gallery
values.put(Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());
Uri url = null;
String stringUrl = null; /* value to be returned */
try {
url = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
if (source != null) {
OutputStream imageOut = cr.openOutputStream(url);
try {
source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
} finally {
imageOut.close();
}
long id = ContentUris.parseId(url);
// Wait until MINI_KIND thumbnail is generated.
Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null);
// This is for backward compatibility.
storeThumbnail(cr, miniThumb, id, 50F, 50F,Images.Thumbnails.MICRO_KIND);
} else {
cr.delete(url, null, null);
url = null;
}
} catch (Exception e) {
if (url != null) {
cr.delete(url, null, null);
url = null;
}
}
if (url != null) {
stringUrl = url.toString();
}
return stringUrl;
}
/**
* A copy of the Android internals StoreThumbnail method, it used with the insertImage to
* populate the android.provider.MediaStore.Images.Media#insertImage with all the correct
* meta data. The StoreThumbnail method is private so it must be duplicated here.
* @see android.provider.MediaStore.Images.Media (StoreThumbnail private method)
*/
private static final Bitmap storeThumbnail(
ContentResolver cr,
Bitmap source,
long id,
float width,
float height,
int kind) {
// create the matrix to scale it
Matrix matrix = new Matrix();
float scaleX = width / source.getWidth();
float scaleY = height / source.getHeight();
matrix.setScale(scaleX, scaleY);
Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
source.getWidth(),
source.getHeight(), matrix,
true
);
ContentValues values = new ContentValues(4);
values.put(Images.Thumbnails.KIND,kind);
values.put(Images.Thumbnails.IMAGE_ID,(int)id);
values.put(Images.Thumbnails.HEIGHT,thumb.getHeight());
values.put(Images.Thumbnails.WIDTH,thumb.getWidth());
Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);
try {
OutputStream thumbOut = cr.openOutputStream(url);
thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
thumbOut.close();
return thumb;
} catch (FileNotFoundException ex) {
return null;
} catch (IOException ex) {
return null;
}
}
}
@ValuesFeng
Copy link

while url null?

@flyer88
Copy link

flyer88 commented May 27, 2015

it works!! Thank u!

@hendrawd
Copy link

the image date added become Jan 1, 1970 when opened with Intent(Intent.ACTION_OPEN_DOCUMENT) anyone has this problem too?

Edit = Hmm... looks like it's a bug from my phone model, OnePlus One

@xMonty
Copy link

xMonty commented Nov 27, 2015

For some reason the inserted image is very low quality, i tried replacing 50 with 100 in
source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
but no change the image inserted in Pictures folder is very low quality

@matghazaryan
Copy link

when I changing to PNG It's always saving as jpeg why ?

@burnix
Copy link

burnix commented Aug 7, 2016

How to save image in new folder?

@faywong
Copy link

faywong commented Dec 1, 2016

Per the api document of android, the timestamp should be refined as follow:

		values.put(Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000); // should be in unit of seconds
		values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis()); // should be in unit of ms

@GiulioCMSanto
Copy link

Hello! I am new at Android and I am trying to use this Class to saVe a Bitmap frame from a video screenshot. Basically, I want to save a specific frame of a video from my phone Library. I am not sure why your code is not working. Probably is because ContentResolver. What is the correct way to passa this parameter at the function InsertImage?

I am showing my code here:

public class MainActivity extends AppCompatActivity {

private String videopath;
VideoView gravacao;
Button captura;
int currentPosition;

//TAG PARA LOG DE ERRO
String TAG = "LogErro";

//VARIÁVEIS GLOBAIS DO MEDIAMETADATA -> UTILZIADO PARA SALVAR FRAME DA VIDEOVIEW
MediaMetadataRetriever mediaMetadataRetriever;
Uri urivideosource;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    gravacao = (VideoView) findViewById(R.id.videoID);
    captura = (Button) findViewById(R.id.capturaID);
    mediaMetadataRetriever = new MediaMetadataRetriever();

    Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    intent.setType("video/*");
    startActivityForResult(intent, 1);

    //mediaMetadataRetriever.setDataSource(videopath);

    captura.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            currentPosition = gravacao.getCurrentPosition(); //in millisecond
            Toast.makeText(MainActivity.this,
                    "Current Position: " + currentPosition + " (ms)",
                    Toast.LENGTH_LONG).show();


         mediaMetadataRetriever.setDataSource(getApplicationContext(),urivideosource);
         Bitmap bmFrame = mediaMetadataRetriever.getFrameAtTime(currentPosition * 1000); //unit in microsecond

//HERE IS WHERE I AM USING YOUR CLASS !!!!
CapturePhotoUtils capturePhotoUtils = new CapturePhotoUtils();
capturePhotoUtils.insertImage(getContentResolver(),bmFrame,"Meu Video","Descricao");

            if(bmFrame == null){
                Toast.makeText(MainActivity.this,
                        "bmFrame == null!",
                        Toast.LENGTH_LONG).show();
            }else{
                AlertDialog.Builder myCaptureDialog =
                        new AlertDialog.Builder(MainActivity.this);
                ImageView capturedImageView = new ImageView(MainActivity.this);
                capturedImageView.setImageBitmap(bmFrame);
                ViewGroup.LayoutParams capturedImageViewLayoutParams =
                        new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                                ViewGroup.LayoutParams.WRAP_CONTENT);
                capturedImageView.setLayoutParams(capturedImageViewLayoutParams);
                myCaptureDialog.setView(capturedImageView);
                myCaptureDialog.show();
            }

        }});


}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if(requestCode == 1 && resultCode == Activity.RESULT_OK)
    {
        try
        {
            videopath = data.getData().toString();

            //É NECESSÁRIO CRIAR URI PARA O MEDIAMETADATA FUNCIONAR. ELE NÃO VAI FUNCIONAR COM O PATHNAME!!
            urivideosource = Uri.parse(videopath);
            //URI CRIADO PARA O MEDIAMETADATA

            gravacao.setVideoURI(Uri.parse(videopath));
            MediaController mediaController = new MediaController(this);
            gravacao.setMediaController(mediaController);
            gravacao.start();
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }
}

}

@alirezazarghi
Copy link

hi
plz help
Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values); is Null
thx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment