How to save an image to the media provider in Android? - android

I used this code snippet:
ContentValues image = new ContentValues();
image.put(Images.Media.TITLE, "ImageTitle");
image.put(Images.Media.DISPLAY_NAME, "Heart");
image.put(Images.Media.DESCRIPTION, "Heart");
image.put(Images.Media.DATE_ADDED, dateTaken);
image.put(Images.Media.DATE_TAKEN, dateTaken);
image.put(Images.Media.DATE_MODIFIED, dateTaken);
image.put(Images.Media.MIME_TYPE, "image/png");
image.put(Images.Media.ORIENTATION, 0);
File parent = imageFile.getParentFile();
String path = parent.toString().toLowerCase();
String name = parent.getName().toLowerCase();
image.put(Images.ImageColumns.BUCKET_ID, path.hashCode());
image.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, name);
image.put(Images.Media.SIZE, imageFile.length());
image.put("_data", imageFile.getAbsolutePath());
getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, image);
I create the image inside the cache directory of my application. I assume that the image is not copied into the media folder by the media provider and therefore not accessible from the media gallery application. Is it possible to add an image to the media gallery without writing the file to the mass storage of the phone?

I think that the answer you are looking for is described here. The second part of that section describes saving images. The gist of it appears to be that you first insert the ContentValues object into the content resolver, which returns a Uri object that the ContentResolver is willing to turn into an OutputStream for you that you can write the image to. The link provided includes the actual code you would need to do that.

Related

Display apps folder in dallery and images/video inside it like other popular apps

Hi I am new to android development and have been trying to accomplish the above said functionality.
I am testing app on Android 9, API 28. I am able to save captured image to folder but not been able to display it in gallery (Like WhatsApp).
I have tried:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
OutputStream os;
String[] split = imagePathNew.split("\\.");
ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, split[0] + ".jpg");
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + "Test");
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
os = (OutputStream) resolver.openOutputStream(Objects.requireNonNull(imageUri)); // imageLocalUri is the uri of captured image in folder
Bitmap bitmap = MediaStore.Images.Media.getBitmap(resolver, imageLocalUri);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
Objects.requireNonNull(os);
} else {
Intent updateInGallery = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
updateInGallery.setData(imageLocalUri); // imageLocalUri is the uri of captured image in folder
context.sendBroadcast(updateInGallery);
}
Can someone please help me with what am I doing wrong here?
Nothing is wrong with the posted code.
In the addition use following class
https://developer.android.com/reference/android/media/MediaScannerConnection#scanFile(java.lang.String,%20java.lang.String)
From docs
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.

Android save MediaStore image with mime type GIF

While this code successfully creates an image that is also present in the phone's gallery, the extension is '.jpg' instead of '.gif'.
File gifFile; // gif file stored in Context.getFilesDir()
final ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "Image" + System.currentTimeMillis());
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/gif");
// Create a new gif image using MediaStore
final Uri gifContentUri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
// Open a writable stream pointing to the new file created by MediaStore
OutputStream outputStream = context.getContentResolver().openOutputStream(gifContentUri, "w");
// Copy the original file from the app private data folder to the file created by MediaStore
IOUtils.copyFile(new FileInputStream(gifFile), outputStream);
Output file is created inside Pictures folder by MediaStore. If I manually change the output file's extension to gif, the gif animation is playing inside Android gallery.
I feel I'm missing a small detail for this to work
Removed the DISPLAY_NAME line.
Add contentValues.put(MediaStore.MediaColumns.DATA, "/storage/emulated/0/Pictures/Image." + System.currentTimeMillis() + ".gif");
It goes to a subdir of the Pictures directory if the subdir exists contentValues.put(MediaStore.MediaColumns.DATA, "/storage/emulated/0/Pictures/Mine/Image." + System.currentTimeMillis() + ".gif");.
For Android Q the DATA column is useless.
String displayName = "Image." + System.currentTimeMillis() + ".gif";
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName);
will do it there.

Set ringtone / notification tone via contentprovider (from assets)

I'm trying to set the android default ringtone or notification tone via content provider from my assets folder.
Surprisingly, it works like this, but is it a legitimate way?
Uri audiouri = Uri.parse("content://"+BuildConfig.APPLICATION_ID+"/"+soundname+".mp3");
RingtoneManager.setActualDefaultRingtoneUri(a, TYPE_NOTIFICATION, audiouri );
Unfortunately, the sound name isn't shown in Android settings.
Strangely the sound name is actually shown when I go to 'Other sounds'
I also tried this:
Uri audiouri = Uri.parse("content://"+BuildConfig.APPLICATION_ID+"/"+soundname+".mp3");
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.TITLE, soundname);
Uri ringtoneuri = a.getContentResolver().insert(audiouri, contentValues);
RingtoneManager.setActualDefaultRingtoneUri(a, TYPE_NOTIFICATION, ringtoneuri);
resulting in a null sound (no sound is set)
third option I tried is:
Uri audiouri = MediaStore.Audio.Media.getContentUriForPath("content://"+BuildConfig.APPLICATION_ID+"/"+soundname+".mp3");
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DATA, "content://"+BuildConfig.APPLICATION_ID+"/"+soundname+".mp3");
contentValues.put(MediaStore.MediaColumns.TITLE, soundname);
Uri ringtoneuri = a.getContentResolver().insert(audiouri, contentValues);
RingtoneManager.setActualDefaultRingtoneUri(a, TYPE_NOTIFICATION, ringtoneuri);
Now the sound name is shown correctly, but no sound is actually played.
I get error on logcat:
java.io.FileNotFoundException: Can't access /content:/com.mydomain.myapp/test.mp3
So it seems it's taking the value from MediaColumns.DATA which does not support Content provider paths but only real paths. Right?
Final question: How to set tone AND name in android settings? Preferably without copying the file to external storage.
So, unfortunately I did not find out how to set asset as ringtone directly,
but this is a nice workaround:
When copying asset to internal app storage or cache dir (no permissions needed for that!) I was able to set the ringtone without WRITE_EXTERNAL_STORAGE permisson.
static void settone(int type, Sound sound, Activity a)
{
lastsound = sound; //global remember sound and type (alarm/ringtone/notification)
lasttype = type; // if we have to get permissions first, then call this from onActivityResult
if (canwritesystem(a))
{
RingtoneManager.setActualDefaultRingtoneUri(a, type, getringtoneuri(sound, a));
Toast.makeText(a, a.getString(R.string.settonesuccess), Toast.LENGTH_LONG).show();
}
else a.startActivityForResult(new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS).setData(Uri.parse("package:" + a.getPackageName())),CONTEXT_SET_TONE);
}
static Uri getringtoneuri(Sound sound, Activity a)
{
File tonefile = new File(sound.getpath); // path could be like: /Android/data/com.company.yourapp
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DATA, tonefile.getAbsolutePath());
contentValues.put(MediaStore.MediaColumns.TITLE, sound.getDisplayName());
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mp3");
contentValues.put(MediaStore.MediaColumns.SIZE, tonefile.length());
contentValues.put(MediaStore.Audio.Media.IS_RINGTONE, true);
contentValues.put(MediaStore.Audio.Media.IS_NOTIFICATION, true);
contentValues.put(MediaStore.Audio.Media.IS_ALARM, true);
contentValues.put(MediaStore.Audio.Media.IS_MUSIC, false);
Uri generalaudiouri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
a.getContentResolver().delete(generalaudiouri, MediaStore.MediaColumns.DATA + "='" + tonefile.getAbsolutePath() + "'", null);
return a.getContentResolver().insert(generalaudiouri, contentValues);
}

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.

store images with MediaStore to specific folder

I was searching a bit on how to add images using the mediastore since I'm currently doing it manual with
final String myBitmap = "MyImage_"+ mFileDate + ".png";
File fileNumber = new File(APP_FILE_PATH, myBitmap);
out = new FileOutputStream( fileNumber );
final Bitmap mBitmap =getBitmap();
mBitmap.compress( Bitmap.CompressFormat.PNG, 85, out );
out.flush();
however I need the IDs that media manager uses to store since its easier to manage id rather than paths and such.
I tried adding it this way:
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, mFileName.getName());
values.put(Images.Media.DESCRIPTION, getString(R.string.bitdraw_description));
values.put(Images.Media.MIME_TYPE, "image/png");
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
out = getContentResolver().openOutputStream(uri);
mBitmap.compress( Bitmap.CompressFormat.PNG, 85, out );
out.flush();
however this adds the image to the folder where the photos are saved and not to the specific folder I want to.
Also tried this after the manual (first part):
Media.insertImage(getContentResolver(), mFileName.getPath(), mFileName.getName(), getString(R.string.description));
but this only generates duplicates since the image was saved first by the outputStream
A workaround I found on a thread here is to call a broadcast like this:
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse ("file://"+ Environment.getExternalStorageDirectory())));
but this is rather inneficient and takes long, and I want it to be the fastest, and I'm quite unsure if this actually generates the IDs, and if it does, how could I make this to scan only a specific folder in the SD ??
Any help would be appreciated.

Categories

Resources