How to copy Images/Videos from DocumentFile Object to Gallery Folder using MediaStore Api
Thanks to @blackapps!
All I had to do was to request a writable URI from the MediaStore
using the insert()
method and then open a InputStream
for source URI and OutputStream
for destination URI.
Code for above action :-
public void saveFile(Uri sourceUri, String fileName, String mimeType) throws IOException{
ContentValues values = new ContentValues();
Uri destinationUri;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P){
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
if (fileName.endsWith(".mp4")){
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + "/MyFolder");
destinationUri = context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
} else {
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyFolder");
destinationUri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
InputStream inputStream = context.getContentResolver().openInputStream(sourceUri);
OutputStream outputStream = context.getContentResolver().openOutputStream(destinationUri);
IOUtils.copy(inputStream, outputStream);
Toast.makeText(context, "The File has been saved!", Toast.LENGTH_SHORT).show();
}
Note :- You have to add the Commons-io
dependency in your build.gradle file to access IOUtils.copy()
function
If you find any better way to do this, Do share it!
How to move/copy any type of file from asset file to scoped storage ANDROID Q in JAVA?
I finally find a solution, I pretty sure it's not the best way but it work.
I give me access to all files acccess by this way :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
try {
Intent intentFiles = new Intent();
intentFiles.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
Uri uriFiles = Uri.fromParts("package", myContext.getPackageName(), null);
intentFiles.setData(uriFiles);
myContext.startActivity(intentFiles);
} catch (Exception e)
{
Intent intentFiles = new Intent();
intentFiles.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
myContext.startActivity(intentFiles);
}
add this line to your manifest:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
after that, this code below work :
AssetManager assetManager = Objects.requireNonNull(requireContext()).getAssets();
Context myContext = requireContext();
//Essential for creating the external storage directory for the first launch
myContext.getExternalFilesDir(null);
File databasesFolder = new File(myContext.getExternalFilesDir(null).getParent(), "com.mydb.orca/databases");
databasesFolder.mkdirs();
if (files!= null) {
for (String filename : files) {
InputStream in;
OutputStream out;
try {
in = assetManager.open("database/test/" + filename);
File outFile = new File(databasesFolder, filename);
out = new FileOutputStream(outFile);
copyFile(in, out);
in.close();
out.flush();
out.close();
} catch (IOException e) {
Log.e("tag", "Failed to copy asset file: " + filename, e);
}
}
} else {
Log.e("Error NPE", "files is null");
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
if someone have a best solution it should be nice
I tested on android 11 and it work
How to copy file from app-specific folder (file:// scheme) to MediaStore Images collection (content:// scheme) in Android Q?
length = inputStream.read(buffer)
Remove that statement.
You are reading a lot of bytes but not writing them to the new file.
So the new file misses the 'header'.
The new file is shorter. But you did not compare file sizes. –
How to save an image in Android Q using MediaStore?
Try the next method. Android Q (and above) already takes care of creating the folders if they don’t exist. The example is hard-coded to output into the DCIM folder. If you need a sub-folder then append the sub-folder name as next:
final String relativeLocation = Environment.DIRECTORY_DCIM + File.separator + “YourSubforderName”;
Consider that the compress format should be related to the mime-type parameter. For example, with a JPEG compress format the mime-type would be "image/jpeg", and so on. Probably you may also want to pass the compress quality as a parameter, in this example is hardcoded to 95.
Java:
@NonNull
public Uri saveBitmap(@NonNull final Context context, @NonNull final Bitmap bitmap,
@NonNull final Bitmap.CompressFormat format,
@NonNull final String mimeType,
@NonNull final String displayName) throws IOException {
final ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName);
values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
final ContentResolver resolver = context.getContentResolver();
Uri uri = null;
try {
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
uri = resolver.insert(contentUri, values);
if (uri == null)
throw new IOException("Failed to create new MediaStore record.");
try (final OutputStream stream = resolver.openOutputStream(uri)) {
if (stream == null)
throw new IOException("Failed to open output stream.");
if (!bitmap.compress(format, 95, stream))
throw new IOException("Failed to save bitmap.");
}
return uri;
}
catch (IOException e) {
if (uri != null) {
// Don't leave an orphan entry in the MediaStore
resolver.delete(uri, null, null);
}
throw e;
}
}
Kotlin:
@Throws(IOException::class)
fun saveBitmap(
context: Context, bitmap: Bitmap, format: Bitmap.CompressFormat,
mimeType: String, displayName: String
): Uri {
val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
}
val resolver = context.contentResolver
var uri: Uri? = null
try {
uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
?: throw IOException("Failed to create new MediaStore record.")
resolver.openOutputStream(uri)?.use {
if (!bitmap.compress(format, 95, it))
throw IOException("Failed to save bitmap.")
} ?: throw IOException("Failed to open output stream.")
return uri
} catch (e: IOException) {
uri?.let { orphanUri ->
// Don't leave an orphan entry in the MediaStore
resolver.delete(orphanUri, null, null)
}
throw e
}
}
Kotlin variant, with a more functional style:
@Throws(IOException::class)
fun saveBitmap(
context: Context, bitmap: Bitmap, format: Bitmap.CompressFormat,
mimeType: String, displayName: String
): Uri {
val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
}
var uri: Uri? = null
return runCatching {
with(context.contentResolver) {
insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)?.also {
uri = it // Keep uri reference so it can be removed on failure
openOutputStream(it)?.use { stream ->
if (!bitmap.compress(format, 95, stream))
throw IOException("Failed to save bitmap.")
} ?: throw IOException("Failed to open output stream.")
} ?: throw IOException("Failed to create new MediaStore record.")
}
}.getOrElse {
uri?.let { orphanUri ->
// Don't leave an orphan entry in the MediaStore
context.contentResolver.delete(orphanUri, null, null)
}
throw it
}
}
Related Topics
How to Check If an App Is a Non-System App in Android
Filtering Accelerometer Data Noise
Asynctask: Where Does the Return Value of Doinbackground() Go
Multiple Selection in Custom Listview with Cab
Programmatically Collapse a Group in Expandablelistview
How to Implement Export SQLite to Excel/CSV File in Android
How to Use Android Emulator for Testing Bluetooth Application
Android - Play Sound on Button Click - Null Pointer Exception
How to Build a Native (Command Line) Executable to Run on Android
Fileprovider Crash - Npe Attempting to Invoke Xmlresourceparser on a Null String
Filter Output in Logcat by Tagname
Android Retrofit - Onprogressupdate for Showing Progress Notification