I am developing an android application in which there is a scenario that we need to convert a file's absolute path (/storage/emulated/0/..../1584362593949.pdf) to content URI(content://)
I have tried converting for the file types such as Audio and video with the help of content resolver and there was no issue I faced but stuck at converting for PDF documents.
Check the code snippet below
public Uri convertFileUriToContentUri(String filePath, String title) {
String fileType = title.substring(title.lastIndexOf(".") + 1);
File file = new File(filePath);
Uri base = null;
//creating content values of size 4
ContentValues values = new ContentValues(4);
long current = System.currentTimeMillis();
switch (fileType) {
case "mp3":
values.put(MediaStore.Audio.Media.TITLE, "audio" + file.getName());
values.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mp3");
values.put(MediaStore.Audio.Media.DATA, file.getAbsolutePath());
base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
break;
case "pdf":
// I am Stuck here as MediaStore does not provide support to documents
break;
case "mp4":
values.put(MediaStore.Video.Media.TITLE, "video" + file.getName());
values.put(MediaStore.Video.Media.DATE_ADDED, (int) (current / 1000));
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
values.put(MediaStore.Video.Media.DATA, file.getAbsolutePath());
base = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
break;
}
//creating content resolver and storing it in the external content uri
ContentResolver contentResolver = context.getContentResolver();
Uri newUri = contentResolver.insert(base, values);
//sending broadcast message to scan the media file so that it can be available
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));
return newUri;
}
Please help me find out the solution.
Related
My code runs perfect on any Android lower then Android 11.
On Android 11 (specifically on Pixel2 & Pixel3 emulator and real devices) the file descriptor fail to find the file after inserting it to the media store.
If I change the relative path on the second line to be only the Environment.DIRECTORY_MOVIES path - it works
String relativeLocation = Environment.DIRECTORY_MOVIES;
This is my code:
String mimeType = "video/mp4";
String relativeLocation = Environment.DIRECTORY_MOVIES + File.separator + SUB_DIRECTORY_NAME ;
shortFileName = getDateStamp() + selectedExtension;
videoContentValues = new ContentValues();
videoContentValues.put(MediaStore.Video.Media.TITLE, shortFileName);
videoContentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, shortFileName);
videoContentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
videoContentValues.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
videoContentValues.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
videoContentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
videoContentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
videResolver = mContext.getContentResolver();
Uri contentUri = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
videoUri = videResolver.insert(contentUri, videoContentValues);
if (videoUri == null) {
throw new IOException("Failed to create new MediaStore record.");
}
pfd = mContext.getContentResolver().openFileDescriptor(videoUri, "w");
mMediaRecorder.setOutputFile(pfd.getFileDescriptor());
The code runs OK until it gets to the openFileDescriptor which return the following error:
java.io.FileNotFoundException: java.io.FileNotFoundException: No item at content://media/external_primary/video/media/47
What am I doing wrong?
Update 1:
More info:
I am still requesting WRITE_EXTERNAL_STORAGE and making sure it is allowed before start recording
The following code works in the same camera app on Android 11 Pixel 2 emulator to save pics:
String mimeType = "image/jpeg";
final String relativeLocation = Environment.DIRECTORY_PICTURES + File.separator + SUB_DIRECTORY_NAME;
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, createImageFileNameQ());
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
ContentResolver resolver = mContext.getContentResolver();
OutputStream stream = null;
Uri uri = null;
try {
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
uri = resolver.insert(contentUri, contentValues);
try {
OutputStream fos = resolver.openOutputStream(uri);
fos.write(data);
fos.flush();
fos.close();
}
catch (Exception ex) {
Log.e(TAG, ex.toString());
}
Update 2:
If I write on the second line "String relativeLocation = Environment.DIRECTORY_PICTURES + File.separator + SUB_DIRECTORY_NAME;" and not DIRECTORY_MOVIES :
It works!#!!
Video files are saved on Videos\Sub_Directory
Update 3:
Now on the emulators:
no trick helps to make relative path.
And also the pics cannot be saved to relative path
String mimeType = "image/jpeg"; String relativeLocation = Environment.DIRECTORY_PICTURES + File.separator + SUB_DIRECTORY_NAME;
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, createImageFileNameQ());
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
ContentResolver resolver = mContext.getContentResolver();
OutputStream stream = null;
Uri uri = null;
try {
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
uri = resolver.insert(contentUri, contentValues);
try {
OutputStream fos = resolver.openOutputStream(uri);
fos.write(data);
fos.flush();
fos.close();
} catch (Exception ex) {
Log.e(TAG, ex.toString());
}
} catch (Exception e) {}
I have opened a ticket to Google/Android with full recording of the emulator debug messages like they asked. they said: "interesting, they will look into it". they did not answer - but they have fixed it for the latest Android 11 release. Security patch Nov. 2020. So - problem was solved.
I have left a bug fix in my code in case somebody did not get the last android 11 update. In case of this error - I set IsAndroid11SaveOnSubDirectoryEnabled variable to false
String relativeLocation = Environment.DIRECTORY_MOVIES + File.separator + SUB_DIRECTORY_NAME;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q && IsAndroid11SaveOnSubDirectoryEnabled == false)
{
relativeLocation = Environment.DIRECTORY_MOVIES + File.separator + "$" + SUB_DIRECTORY_NAME;
Log.e(TAG, "Recording on $SubDirectory");
}
Edit II:
I got an answer from Google/Android that maybe I have a ".nomedia" file in the directory, and so - the Media store cannot insert/index new files. (.nomedia is a file that tells the gallery not to read the medias on this folder, it applies until Android 10, on Android 11 - this file cannot be created or deleted in the movies folder because it is not media). But it was not correct - the log files and recording that I have sent to Google/Android were from an Android 11 Emulator which did not have this file.
Edit III - Since Dilijeet asked, IsAndroid11SaveOnSubDirectoryEnabled is a boolean that I save to the SharedPreference. here is the code - whenever the recording crash on IOExcpetion & Android 11 - I assume it is the subdirectory problem, and set this boolean for further recordings.
catch (IOException ex)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && IsAndroid11SaveOnSubDirectoryEnabled == true) {
mSharedPreferences.edit().putBoolean("chkSaveOnSubDirectory", false).apply();
IsAndroid11SaveOnSubDirectoryEnabled = false;
}
I try to save a downloaded file with the ContentResolver to maintain metadata like artist, album and title.
First question:
Do i need a file with ID3 Tags or is it enough to specify the metadata via the MediaStore?
The MP3 file which gets downloaded doesn't have ID3 Tags.
Second question:
I use the following code to save the mp3 file to the MediaStore:
ContentValues values = new ContentValues(7);
values.put(MediaStore.Audio.Media.TITLE, audiodata.getTitle());
values.put(MediaStore.Audio.Media.ARTIST, audiodata.getArtist());
values.put(MediaStore.Audio.Media.ALBUM, audiodata.getAlbum());
values.put(MediaStore.Audio.Media.DATE_ADDED, (int) (System.currentTimeMillis() / 1000));
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mp3");
values.put(MediaStore.Audio.Media.DISPLAY_NAME, audiodata.getTitle() + ".mp3");
values.put(MediaStore.Audio.Media.RELATIVE_PATH, "Music/AudioConverter/");
Uri audiouri = getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
ParcelFileDescriptor file = null;
try {
file = getContentResolver().openFileDescriptor(audiouri, "w");
boolean writtenToDisk = writeResponseBodyToDisk(response.body(), file);
btnSelectFile.setText("Datei wählen...");
pbMain.setVisibility(View.GONE);
} catch (FileNotFoundException e) {
e.printStackTrace();
pbMain.setVisibility(View.GONE);
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
If I remove the extension ".mp3" from the display name, the Musicplayer shows me the right artist, album and so on. But if I leave the extension as shown above then no metadata gets saved except the display name.
Is there anything wrong? Do i miss something?
I created a simple android app for adding frames and cropping images, now after the editing is done the pictures are saved in android root>app folder but non of these pictures show up in the gallery, here is My code
/**
* Create a File for saving an image or video
*/
private File getOutputMediaFile() {
// To be safe, you should check that the SDCard is mounted
// File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
// + "/Android/data"
// + getApplicationContext().getPackageName()
// + "/Files" + Global.AppFolder);
File root = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "/" + Global.AppFolder);
// Create the storage directory if it does not exist
if (!root.exists()) {
if (!root.mkdirs()) {
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
File mediaFile;
String mImageName = "MI_" + timeStamp + ".jpg";
mediaFile = new File(root.getAbsolutePath(), mImageName);
return mediaFile;
}
private String getRealPathFromURI(Uri uri) {
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor == null) {
return uri.getPath();
} else {
cursor.moveToFirst();
String s = cursor.getString(cursor.getColumnIndex("_data"));
cursor.close();
return s;
}
}
By the way I am a newbie and don't have a lot of experience with android coding so detailed answers and references(for further learning) shall be highly appreciated.
try this add below code after saving image
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, "title");
values.put(Images.Media.DESCRIPTION, "description");
values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(Images.ImageColumns.BUCKET_ID, root.toString().toLowerCase(Locale.US).hashCode());
values.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, root.getName().toLowerCase(Locale.US));
values.put("_data", root.getAbsolutePath());
ContentResolver cr = getContentResolver();
cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
Add these code after you save the image
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
The Media Scanning is only done when the system Bootup. So you need to tell the media scanner to re-scan the files.
This is how i am saving voice recorded file to SDcard,Here want to give rename option for user before saving to gallery,not getting any idea ,Suggest me any solution,Thanks.
protected void addRecordingToMediaLibrary() {
ContentValues values = new ContentValues(4);
long current = System.currentTimeMillis();
values.put(MediaStore.Audio.Media.TITLE, "audio" + audiofile.getName());
values.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/3gp");
values.put(MediaStore.Audio.Media.DATA, audiofile.getAbsolutePath());
ContentResolver contentResolver = getContentResolver();
Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Uri newUri = contentResolver.insert(base, values);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));
Toast.makeText(this, "Added File " + newUri, Toast.LENGTH_LONG).show();
}
Guessing that the above method you posted will get called once the recording is done, What I suggest you do is, show the user a Dialog requesting for the name of the file he/she wishes to enter and have this method called on the click of positive button of this Dialog also passing this method a String parameter denoting the filename; something like this protected void addRecordingToMediaLibrary(String fileName) {. Set this filename values.put(MediaStore.Audio.Media.TITLE, fileName); like this.
I am creating one application which record voice and when i click on StopRecording button,
it store in SD card/Internal memory. But while storing it take random name like sound1356543. But i need to set name on user choice or sequence wise like
MyAudio1
MyAudio2
MyAudio3
MyAudion
How can i do this? Here is my code for reference.
File sampleDir = Environment.getExternalStorageDirectory();
audiofile = File.createTempFile("Recording", ".3gp", sampleDir);
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
//recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(audiofile.getAbsolutePath());
protected void addRecordingToMediaLibrary() {
ContentValues values = new ContentValues(4);
long current = System.currentTimeMillis();
values.put(MediaStore.Audio.Media.TITLE, "audio" + audiofile.getName());
values.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/3gpp");
values.put(MediaStore.Audio.Media.DATA, audiofile.getAbsolutePath());
ContentResolver contentResolver = getContentResolver();
Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Uri newUri = contentResolver.insert(base, values);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));
Toast.makeText(this, "Added File " + newUri, Toast.LENGTH_LONG).show();
}
}
How to set name to audio file.
Thanks in Advance.
remove this line and then check
values.put(MediaStore.Audio.Media.TITLE, "audio" + audiofile.getName());
I had the same issue like this.