getExternalStoragePublicDirectory deprecated warning persists below Android Q - android

Since Android 10, getExternalStoragePublicDirectory was deprecated in favor of other alternatives such as MediaStore or getExternalFilesDir.
I am saving videos to the "DCIM->MyFolder" directory.
For Android 10 and above, I use MediaStore to get the FileDescriptor:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
FileDescriptor fileDescriptor;
Uri videoUri;
ContentResolver resolver = this.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/" + "MyFolder");
videoUri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues);
ParcelFileDescriptor descriptor = this.getContentResolver().openFileDescriptor(videoUri, "w");
fileDescriptor = descriptor.getFileDescriptor();
return fileDescriptor;
}
Then open an OputputStream to be able to write the file:
outputStream = new FileOutputStream(fileDescriptor);
For devices below Android 10, I use Environment.getExternalStoragePublicDirectory:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
String outputPath = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "MyFolder") + "/";
}
Then open the OutputStream: outputStream = new FileOutputStream(outputPath + fileName);
The problem: Despite targeting the right Android versions, Android Studio still gives me a warning about the deprecated getExternalStoragePublicDirectory method.
Question: What is the correct way to get the /storage/emulated/0/DCIM path without using deprecated methods?
Note:
MediaStore cannot be used below API 29 because of this MediaStore.MediaColumns.RELATIVE_PATH
I cannot use getExternalFilesDir because it returns a path to the application internal storage and my saved videos wouldn't be visible in gallery.

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.

How to download image/video file in Android R with help of MediaStore?

As getExternalStoragePublicDirectory has been deprecated in Android Q and recommendation is to use MediaStore API
Here is my Image URL https://cdn.pixabay.com/photo/2020/04/21/06/41/bulldog-5071407_1280.jpg
I want to download this image file and want to save the shared folder i.e. Downloads or Pictures Folder as per MediaStore API
Below is the sample code
ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
Note: compileSdkVersion 30
I want an example of How to download an Image/Video file in Android R and save it into the download folder location with MediaStore.
In order to save it on android 10. Then after calling the insert method you will get the URI then call
os = getContentResolver().openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
os.close();
In Android 10 calling insert method is not enough
Your code will look like this
ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
OutputStream os = getContentResolver().openOutputStream(uri);
Bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
os.close();
Hi Kanaiya I answered an improvement on MediaStore answer MediaStore.Images.Media.insertImage deprecated
I did use workManager and viewModel not sure if that would apply to your use-case, I did test my answer on API 26 and on API 30.
My working sample is just a 2018 workmanager codelab for which I made minor changes to remove a depreciated api call, thanks for the original idea commonsware

Android 11 Media store - cannot record new videos on relative path. It works with Environment.DIRECTORY_MOVIES path but not not with sub folder

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;
}

How do insert file with content resolver in Android?

I'm trying to insert audio file to shared storage in Android. I'm getting error on api 29(emulator).
Error :
java.lang.IllegalArgumentException: Primary directory (invalid) not allowed for content://media/external_primary/audio/media; allowed directories are [Alarms, Music, Notifications, Podcasts, Ringtones]
My Code is:
...
Uri collection = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
? MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
: MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
values = new ContentValues();
values.put(MediaStore.Audio.Media.DISPLAY_NAME, targetFileName);
values.put(MediaStore.Audio.Media.RELATIVE_PATH, targetFileDirPath);
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mpeg");
values.put(MediaStore.Audio.Media.IS_PENDING, 1);
resolver = getContentResolver();
uri = resolver.insert(collection, values); // error throws from here
outputStream = uri != null ? resolver.openOutputStream(uri) : null;
...
What is the cause of this error and how can I solve this problem?
Apparently, MediaStore.Audio.Media.getContentUri() does not return a directly-usable Uri, at least on Android 10+. It points to an abstract location for "audio", but you cannot write content directly to that Uri. Instead, you need to use RELATIVE_PATH to specify one of the supported collections (Alarms, Music, Notifications, Podcasts, Ringtones), and then any path that you want inside of there.
Note, though, that RELATIVE_PATH itself is new to Android 10. For Android 9 and older devices, I recommend just writing to the filesystem directly.
{ Confirm usage of #CommnWare
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/" + FILE_DIR);
values.put(MediaStore.MediaColumns.IS_PENDING, 1);
values.put(MediaStore.MediaColumns.DISPLAY_NAME, FILE_NAME);
values.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain");enter code here
ContentResolver resolver = _context.getContentResolver();
resolver.insert(MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY), values);
}

Android Q, how to save audio recording with MediaStore with the correct file name?

I want to store my mp3 files recorded with my app to the Music folder with MediaStore for the new "Scoped Storage" of Android 10.
This method works good, but the files are named with timestamp (e.g. 1576253519449), without .mp3 extension.
If I add the extension manually with file manager, the file is recorded correctly.
How i can use fileName + ".mp3" to name the files?
String fileName; //Obtained by intent
MediaRecorder audioRecorder;
Uri audiouri;
ParcelFileDescriptor file;
private void startRecording() throws IOException {
ContentValues values = new ContentValues(4);
values.put(MediaStore.Audio.Media.TITLE, fileName);
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.RELATIVE_PATH, "Music/Recordings/");
audiouri = getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
file = getContentResolver().openFileDescriptor(audiouri, "w");
if (file != null) {
audioRecorder = new MediaRecorder();
audioRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
audioRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
audioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
audioRecorder.setOutputFile(file.getFileDescriptor());
audioRecorder.setAudioChannels(1);
audioRecorder.prepare();
audioRecorder.start();
}
}
And another question: MediaStore.Audio.Media.RELATIVE_PATH is only for Android 10.
How to maintain retrocompatibility with older releases?
EDIT:
For retrocompatibility, you can just remove values.put(MediaStore.Audio.Media.RELATIVE_PATH, "Music/Recordings/");: the files will be saved directly in Music directory.
Or use if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) to add the subdirectory only on Android 10.
As #blackapps suggested, can be used the DISPLAY_NAME property.

Categories

Resources