Problem in storing image in MediaStore in Android - android

I have written a block of code to insert new image to Android device Image gallery through java program, please find the code below,
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, "title");
values.put(Images.Media.BUCKET_ID, "test");
values.put(Images.Media.DESCRIPTION, "test Image taken");
values.put(Images.Media.MIME_TYPE, "image/jpeg");
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
OutputStream outstream;
try {
outstream = getContentResolver().openOutputStream(uri);
receivedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, outstream);
outstream.close();
alertDialog.showMessage("Image Stored Successfully", "Media");
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
} catch (Exception e) {
Image is stored perfectly, but the problem is i could not view the image immediately. I need to switch off and turn it on the device to view the image. Can anyone plese help me to solve this problem?
Edit: Hi Aleadam, Thanks for the replay, pls check my code below
m_pScanner = new MediaScannerConnection(this,
new MediaScannerConnectionClient() {
public void onMediaScannerConnected() {
m_pScanner.scanFile(returnUrl, null /*mimeType*/);
}
public void onScanCompleted(String path, Uri uri) {
if (path.equals(returnUrl)) {
ImageViewActivity.this.runOnUiThread(new Runnable() {
public void run() {
}
});
m_pScanner.disconnect();
}
}
});
m_pScanner.connect();
It not working for me, it not even connected with the MediaScanner. whether i missed out something.
Thanks
Rajapandian

Media Scanner needs to rescan the storage in order to show the image in the Gallery.
Look for the MediaScannerConnection API to make it happen.
MediaScannerConnection provides a way for applications to pass a newly created or downloaded media file to the media scanner service. The media scanner service will read metadata from the file and add the file to the media content provider. The MediaScannerConnectionClient provides an interface for the media scanner service to return the Uri for a newly scanned file to the client of the MediaScannerConnection class.

you can add the image to mediadb through code without invoking the mediascanner but without the absolute path of the new image the image cannot be displayed.
You have to add
values.put(MediaStore.Images.Media.DATA, "/absolute/path/to/image.jpg");
Invoking the media scanner is the more complete way because the media scanner also sets the oter fields (date, with, height, .....)
warning: If you are using android before version 4.4 invoking the media scanner always starts scanning the complete device which may take some time (45 minutes on my android-4.2 tablet with 16000 photos)

Related

Save to Gallery - Android

I've a strange (and oddly specific) issue with saving to the gallery on Android.
A bit of background: The app I'm developing needs to be able to save images to the gallery, which has been well discussed on here before. However, there's a specific requirement for this project which is I need to be able to tag it with a specific date/time.
I've tried several methods to get this to work correctly and so far the best I have is a workaround.
What I'm doing at the moment is generating the image, saving it to a file and setting the created date in the EXIF data. I then open the Google Photos app and it shows up in the gallery, showing the correct date and time and is in the correct place within the gallery.
The issue with this however, is that it doesn't automatically show in any other gallery software (for example, the OEM gallery apps that may be shipped with a given device), nor does it show if the Google Photos app is open at the time of saving; It must be closed and relaunched in order for it to show.
Now, if I run a media scan it ignores the EXIF data and the image shows up as the last image created.
Here's the code I'm currently using:
static class InsertImageObj{
public String url;
public long id;
}
public static InsertImageObj insertImage(Bitmap source,
String title, long time) {
String path = createDirectoryAndSaveFile(source, title, time);
String stringUrl = path;
InsertImageObj retVal = new InsertImageObj();
retVal.url = stringUrl;
return retVal;
}
private static String createDirectoryAndSaveFile(Bitmap imageToSave, String fileName, long dateTime) {
File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); //DCIM = Digital Camera Image. This is where camera photos go!
if (!directory.exists()) {
directory.mkdirs();
}
File file = new File(directory, fileName);
if (file.exists()) {
file.delete();
}
try {
FileOutputStream out = new FileOutputStream(file);
imageToSave.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
ExifInterface ei = new ExifInterface(file.getAbsolutePath());
ei.setAttribute(ExifInterface.TAG_DATETIME, convertToExifDateTime(dateTime));
ei.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL, convertToExifDateTime(dateTime));
ei.setAttribute(ExifInterface.TAG_DATETIME_DIGITIZED, convertToExifDateTime(dateTime));
ei.saveAttributes();
} catch (Exception e) {
e.printStackTrace();
}
return file.getAbsolutePath();
}
private static String convertToExifDateTime(long timestamp) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.getDefault());
return sdf.format(new Date(timestamp));
}
I've also tried running setLastModified on the file (which doesn't work, OS permissions or something or other) and using a MediaScannerConnection instance to scan the individual file once it has been saved. The latter, however causes the system to ignore the date/time tags in the Exif data.
I also tried inserting the image into the gallery via a ContentResolver instance and setting the DATE_ADDED and DATE_TAKEN fields, again to no avail.
Is there something really, really obvious I've missed here?
You need to save your image in the media store provider
Use this function
public static void imageToGallery(Context context, String fileName) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.DATA, fileName);
context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
So, after saving your image, call imageToGallery.
Images are only visible in gallery apps if they are added to the android-media-db.
You can ask MediaScannerConnection.scanFile(...) to add the new photo-file to the android-media-db.
On some androiddevices (but not on all) the mediascanner starts automatically.
Also note: you should save the new photo as ".../DCIM/myApp/myPhotoName.jpg" instead of ".../DCIM/myPhotoName.jpg"
Use this function. Its working for me!
private void galleryAddPic() {
File f = new File(imageFilePath);
try {
MediaStore.Images.Media.insertImage(getActivity().getContentResolver(),
f.getAbsolutePath(), f.getName(), null);
getActivity().sendBroadcast(new Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(f)));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

How to refresh whole SD card?

I am writing a media player application. I am showing all videos of my sd card in a grid view. It's working fine. If any one making changes to those videos(like rename, delete) using some other app like file expert or file manager are not not effecting my grid view. My grid still showing old names and deleted files. To resolve this i have added refresh button. In this i am using
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory())));
But this is ot supported in KITKAT version. Then after google, i got this solution
Android How to use MediaScannerConnection scanFile
But i don't know how to scan my sdcard with MediaScannerConnection on my refresh buttton click.
You can use the scanFile methof from the MediaScannerConnection
For example, to refresh files in a specific directory, you can call:
for (File child : fileFolder.listFiles()) {
if (child.isFile()) {
fName = child.getName();
MediaScannerConnection.scanFile( MyActivity.this,
new String[] { "folderPath" + fName },
null, new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
//file scanned
}
});
}
}
View source here.
Hope this helped!

Android ContentResolver.insert crashes with "unable to create new file"

I have a code inside some function of my activity:
ContentValues cv = new ContentValues();
cv.put(MediaStore.Images.Media.TITLE, "1354213408296.jpg");
ContentResolver contentResolver = getContentResolver();
Uri imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cv);
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
cameraIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
startActivityForResult(cameraIntent, 712984419/*Some request code*/);
It crashes with:
java.lang.IllegalStateException: Unable to create new file:
/mnt/sdcard/DCIM/Camera/1354213408296.jpg at
android.os.Parcel.readException(Parcel.java:1335) at
android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:182) at
android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:136) at
android.content.ContentProviderProxy.insert(ContentProviderNative.java:415) at
android.content.ContentResolver.insert(ContentResolver.java:730)
crashes on:
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cv);
The "1354213408296.jpg" is just System.currentTimeInMillis() + ".jpg", so it is always unique
The android.permission.WRITE_EXTERNAL_STORAGE is provided in manifest
Here is some phone's environment specifications (I am using ACRA to get it):
getDataDirectory=/data
getDownloadCacheDirectory=/cache
getExternalStorageAndroidDataDir=/mnt/sdcard/Android/data
getExternalStorageDirectory=/mnt/sdcard
getExternalStorageState=removed
getRootDirectory=/system
getSecureDataDirectory=/data
getSystemSecureDirectory=/data/system
is4GConfig=true
is8GConfig=false
isEncryptedFilesystemEnabled=false
isExternalStorageEmulated=false
isExternalStorageRemovable=true
What can I do to prevent this crashes?
I'm not sure what you're trying to do. All you seem to be doing is trying to create a new row in MediaStore.Images.Media, with only a TITLE column. Putting in a title without the data to go with it doesn't make much sense.
This seems to be just another exception you will get when no sdcard is present (I was able to reproduce it only on very weird emulators, but who knows?). Cases of missing sdcard should be handled for sure. My current solution is as follows:
public static Uri getImageFileUri(Context context) throws IOException{
String fullFileName = generateImageFileName(imageName); // a method i have defined
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, fullFileName);
return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
return Uri.fromFile(new File(fullFileName));
}
}
This is the method I use to generate the Uri I will start the camera intent with. Afterwards I use it exactly like you do. The thing is that the value I return in the no sdcard case will not work properly, but on the other hand Android devices do not allow taking pictures if no sdcard is present. Using this solution you will succeed in taking picture if there is a sdcard and will launch the native camera that will show message "Insert sdcard in order to take picture" in the other cases.

Trigger mediascanner on specific path (folder), how to?

I got this class:
import android.content.Context;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.util.Log;
public class MediaScannerWrapper implements
MediaScannerConnection.MediaScannerConnectionClient {
private MediaScannerConnection mConnection;
private String mPath;
private String mMimeType;
// filePath - where to scan;
// mime type of media to scan i.e. "image/jpeg".
// use "*/*" for any media
public MediaScannerWrapper(Context ctx, String filePath, String mime){
mPath = "/sdcard/DCIM/Camera";
mMimeType = "jpg";
mConnection = new MediaScannerConnection(ctx, this);
}
// do the scanning
public void scan() {
mConnection.connect();
}
// start the scan when scanner is ready
public void onMediaScannerConnected() {
mConnection.scanFile(mPath, mMimeType);
Log.w("MediaScannerWrapper", "media file scanned: " + mPath);
}
public void onScanCompleted(String path, Uri uri) {
// when scan is completes, update media file tags
}
}
How to use it in the other class?
I don't know how to properly use classes, I tried but nothing is working.
I do something wrong, but I don't know what, can someone help me with this.
The Story
Before Android 4.4, we could just send a broadcast to trigger the media scanner on any particular file, or folder or even on the root of the storage. But from 4.4 KitKat, this have been fixed by the Android Developers.
Why do I say fixed? The reason is simple. Sending a broadcast using MEDIA_MOUNTED on the root directory is very expensive. Running the Media Scanner is an expensive operation and the situation gets even worse when the user has got a lot of files in the storage and deep folder structures.
Before Android 4.4
Keep it straight and simple. If you are targeting your app before Android 4.4. But keep in mind not to use it on the root directory unless absolutely necessary.
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
From Android 4.4
There are two ways for you.
i) The first one is very similar to the previous example, but may not work efficiently and is not recommended too.
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
ii) Now, let us move on to the most recommended and efficient solution to this problem.
Add the file paths of the files which have been updated, like this, in a String type ArrayList
ArrayList<String> toBeScanned = new ArrayList<String>();
toBeScanned.add(item.getFilePath());
Now you need to run scanFile() static method of the MediaScannerConnection class and pass the String array containing the list of all the files which have been updated and needs to be media scanned.
You can also put a listener to respond when the scanning has been finished for individual files.
String[] toBeScannedStr = new String[toBeScanned.size()];
toBeScannedStr = toBeScanned.toArray(toBeScannedStr);
MediaScannerConnection.scanFile(getActivity(), toBeScannedStr, null, new OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
System.out.println("SCAN COMPLETED: " + path);
}
});
Hey I found out how to do it with a very simple code.
Just call this line of code:
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
This should trigger mediascanner.
In Android, there is a content database which is used by the media scanner to keep track of all the media content present on the device.
When Android boots up, the mediascanner service is launched and runs through the entire external storage to find if there is any new media content if it finds one then,
It adds an entry of that media content into the content database
Each entry in the content database contains metadata of the media content like Name, date, file size, type of file, etc..
So when you make a modification to a media content, you will need to update the content database also.
If the content database is not update then other applications also will not be able to access that particular media content.
Running the media scanner just updates the content database
Instead of running the media scanner, you can update the content database yourself and it should resolve the problem.
Here is an explanation on how to insert, delete, update using the content resolver. (Search for the section "Inserting, Updating, and Deleting Data")
Edit:
There is a sample code in this answer. Check for the answer by Janusz.
File file = new File(absolutePath);
Uri uri = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
sendBroadcast(intent);
private void galleryAddPic() {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
Reference: http://developer.android.com/training/camera/photobasics.html#TaskGallery
The Add the Photo to a Gallery Section
As #Aritra Roy's answer, i decide to make an experiment about this issue.
What i got here are:
Intent.ACTION_MEDIA_MOUNTED and Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
can accept individual file path, so sendBroadcast(new
Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse(filePath)));
or sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse(filePath))); will be valid.
If you use individual file path with Intent.ACTION_MEDIA_MOUNTED on Kitkat or above, your application will still crash
If you use Intent.ACTION_MEDIA_SCANNER_SCAN_FILE or MediaScannerConnection on device lower than Kitkat, your application will not force close, but the method will just simply not working as you want.
From that experiment, i think the best method to handle is
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
MediaScannerConnection.scanFile(context, new String[]{imagePath}, null, new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
//something that you want to do
}
});
} else {
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + imagePath)));
}
Let me know if i missed something

dynamically add pictures to gallery widget

Is there a good way to add new image resources(from SD card) to a gallery widget at runtime?
"new image resources"?
Image resources are a part of /res/drawable folder inside your .apk application package. You can not add "new" image resources during runtime.
Is there some other use case you had in mind?
Edited after posters explanation:
You have to add media files to Media Store in order to be seen by gallery widget. Use MediaScanner. I use this convenient wrapper in my code:
public class MediaScannerWrapper implements
MediaScannerConnection.MediaScannerConnectionClient {
private MediaScannerConnection mConnection;
private String mPath;
private String mMimeType;
// filePath - where to scan;
// mime type of media to scan i.e. "image/jpeg".
// use "*/*" for any media
public MediaScannerWrapper(Context ctx, String filePath, String mime){
mPath = filePath;
mMimeType = mime;
mConnection = new MediaScannerConnection(ctx, this);
}
// do the scanning
public void scan() {
mConnection.connect();
}
// start the scan when scanner is ready
public void onMediaScannerConnected() {
mConnection.scanFile(mPath, mMimeType);
Log.w("MediaScannerWrapper", "media file scanned: " + mPath);
}
public void onScanCompleted(String path, Uri uri) {
// when scan is completes, update media file tags
}
}
Then instantiate MediaScannerWrapper and start it with scan(). You could tweak it to handle more than one file at the time. Hint: pass List of File paths, and then loop around mConnection.scanFile.
Send broadcast to MediaStore Content Provider when you add a file
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(imageAdded)));
Working for devices before KitKat
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
Also have a look at this
Working in Lolipop and should also solve kitkat issues.
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA,"file path");
values.put(MediaStore.Images.Media.MIME_TYPE,"image/jpeg");
mContext.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values);
Add Permission.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Categories

Resources