如何使用 MediaStore 在 Android Q 中保存图像?

这里有一个新的 Android Q 范围仓库的链接。

根据 这个 Android 开发者最佳实践博客storing shared media files(这是我的情况)应该使用 MediaStore API 完成。

深入研究文档,我找不到相关的功能。

以下是我在 Kotlin 受到的审判:

val bitmap = getImageBitmap() // I have a bitmap from a function or callback or whatever
val name = "example.png" // I have a name


val picturesDirectory = getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!


// Make sure the directory "Android/data/com.mypackage.etc/files/Pictures" exists
if (!picturesDirectory.exists()) {
picturesDirectory.mkdirs()
}


try {
val out = FileOutputStream(File(picturesDirectory, name))
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)


out.flush()
out.close()


} catch(e: Exception) {
// handle the error
}

结果就是我的图像在这里保存为 Android/data/com.mypackage.etc/files/Pictures/example.png,如最佳实践博客中所描述的 Storing app-internal files


我的问题是:

如何使用 MediaStore API 保存图像?


用 Java 回答也是可以接受的。

预先感谢!


剪辑

多亏了 PerracoLabs

真的很有帮助!

但还有三点。

这是我的代码:

val name = "Myimage"
val relativeLocation = Environment.DIRECTORY_PICTURES + File.pathSeparator + "AppName"


val contentValues  = ContentValues().apply {
put(MediaStore.Images.ImageColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/png")


// without this part causes "Failed to create new MediaStore record" exception to be invoked (uri is null below)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativeLocation)
}
}


val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
var stream: OutputStream? = null
var uri: Uri? = null


try {
uri = contentResolver.insert(contentUri, contentValues)
if (uri == null)
{
throw IOException("Failed to create new MediaStore record.")
}


stream = contentResolver.openOutputStream(uri)


if (stream == null)
{
throw IOException("Failed to get output stream.")
}


if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream))
{
throw IOException("Failed to save bitmap.")
}




Snackbar.make(mCoordinator, R.string.image_saved_success, Snackbar.LENGTH_INDEFINITE).setAction("Open") {
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_VIEW
intent.data = contentUri
startActivity(Intent.createChooser(intent, "Select Gallery App"))
}.show()


} catch(e: IOException) {
if (uri != null)
{
contentResolver.delete(uri, null, null)
}


throw IOException(e)


}
finally {
stream?.close()
}

1-保存的图像没有得到正确的名称“ Myimage. png”

我试过用“ Myimage”和“ Myimage. PNG”,但都不管用。

图像的名字总是由以下数字组成:

1563468625314.jpg

这就引出了第二个问题:

图像保存为 jpg,即使我压缩位图的格式为 png

没什么大不了的,只是好奇为什么。

3-relativeLocation 位在比 Android Q 小的设备上导致异常。 在使用“ Android 版本检查”if 语句包围之后,图像将直接保存在 Pictures文件夹的根目录中。

再来一杯,谢谢。


编辑2

改为:

uri = contentResolver.insert(contentUri, contentValues)
if (uri == null)
{
throw IOException("Failed to create new MediaStore record.")
}


val cursor = contentResolver.query(uri, null, null, null, null)
DatabaseUtils.dumpCursor(cursor)
cursor!!.close()


stream = contentResolver.openOutputStream(uri)

这是记录

I/System.out: >>>>> Dumping cursor android.content.ContentResolver$CursorWrapperInner@76da9d1
I/System.out: 0 {
I/System.out:    _id=25417
I/System.out:    _data=/storage/emulated/0/Pictures/1563640732667.jpg
I/System.out:    _size=null
I/System.out:    _display_name=Myimage
I/System.out:    mime_type=image/png
I/System.out:    title=1563640732667
I/System.out:    date_added=1563640732
I/System.out:    is_hdr=null
I/System.out:    date_modified=null
I/System.out:    description=null
I/System.out:    picasa_id=null
I/System.out:    isprivate=null
I/System.out:    latitude=null
I/System.out:    longitude=null
I/System.out:    datetaken=null
I/System.out:    orientation=null
I/System.out:    mini_thumb_magic=null
I/System.out:    bucket_id=-1617409521
I/System.out:    bucket_display_name=Pictures
I/System.out:    width=null
I/System.out:    height=null
I/System.out:    is_hw_privacy=null
I/System.out:    hw_voice_offset=null
I/System.out:    is_hw_favorite=null
I/System.out:    hw_image_refocus=null
I/System.out:    album_sort_index=null
I/System.out:    bucket_display_name_alias=null
I/System.out:    is_hw_burst=0
I/System.out:    hw_rectify_offset=null
I/System.out:    special_file_type=0
I/System.out:    special_file_offset=null
I/System.out:    cam_perception=null
I/System.out:    cam_exif_flag=null
I/System.out: }
I/System.out: <<<<<

我注意到 title与名称相匹配,所以我尝试添加:

put(MediaStore.Images.ImageColumns.TITLE, name)

它仍然没有工作,这里是新的日志:

I/System.out: >>>>> Dumping cursor android.content.ContentResolver$CursorWrapperInner@51021a5
I/System.out: 0 {
I/System.out:    _id=25418
I/System.out:    _data=/storage/emulated/0/Pictures/1563640934803.jpg
I/System.out:    _size=null
I/System.out:    _display_name=Myimage
I/System.out:    mime_type=image/png
I/System.out:    title=Myimage
I/System.out:    date_added=1563640934
I/System.out:    is_hdr=null
I/System.out:    date_modified=null
I/System.out:    description=null
I/System.out:    picasa_id=null
I/System.out:    isprivate=null
I/System.out:    latitude=null
I/System.out:    longitude=null
I/System.out:    datetaken=null
I/System.out:    orientation=null
I/System.out:    mini_thumb_magic=null
I/System.out:    bucket_id=-1617409521
I/System.out:    bucket_display_name=Pictures
I/System.out:    width=null
I/System.out:    height=null
I/System.out:    is_hw_privacy=null
I/System.out:    hw_voice_offset=null
I/System.out:    is_hw_favorite=null
I/System.out:    hw_image_refocus=null
I/System.out:    album_sort_index=null
I/System.out:    bucket_display_name_alias=null
I/System.out:    is_hw_burst=0
I/System.out:    hw_rectify_offset=null
I/System.out:    special_file_type=0
I/System.out:    special_file_offset=null
I/System.out:    cam_perception=null
I/System.out:    cam_exif_flag=null
I/System.out: }
I/System.out: <<<<<

我不能把 date_added改成一个名字。

MediaStore.MediaColumns.DATA已废弃。

预先感谢!

69429 次浏览

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
}
}
private void saveImage(Bitmap bitmap, @NonNull String name) throws IOException {
OutputStream fos;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentResolver resolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name + ".jpg");
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
fos = resolver.openOutputStream(Objects.requireNonNull(imageUri));
} else {
String imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
File image = new File(imagesDir, name + ".jpg");
fos = new FileOutputStream(image);
}
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
Objects.requireNonNull(fos).close();
}

Image will store in Pictures Folder @ root level

see in live https://youtu.be/695HqaiwzQ0 i created tutorial

This is what i always use. You can try it.

 private void saveImageToStorage() throws IOException {


OutputStream imageOutStream;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {


ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "image_screenshot.jpg");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);


imageOutStream = getContentResolver().openOutputStream(uri);
} else {
String imagePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
File image = new File(imagePath, "image_screenshotjpg");
imageOutStream = new FileOutputStream(image);
}


try {
bitmapObject.compress(Bitmap.CompressFormat.JPEG, 100, imageOutStream);
} finally {
imageOutStream.close();
}


}

If anyone is looking how to save a photo into the DCIM folder, in a way that will appear in Google Photos later: (based on: https://github.com/yasirkula/UnityNativeGallery/blob/670d9e2b8328f7796dd95d29dd80fadd8935b804/JAR%20Source/NativeGallery.java#L73-L96)

ContentValue values = new ContentValues();
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
values.put(MediaStore.MediaColumns.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.MediaColumns.IS_PENDING, true);


Uri uri = context.getContentResolver().insert(externalContentUri, values);


if (uri != null) {
try {
if (WriteFileToStream(originalFile, context.getContentResolver().openOutputStream(uri))) {
values.put(MediaStore.MediaColumns.IS_PENDING, false);
context.getContentResolver().update(uri, values, null, null);
}
} catch (Exception e) {
context.getContentResolver().delete( uri, null, null );
}
}

Where WriteFileToStream is a standard method copying from file to stream.

Here is my version for 2022, this version was tested in Emulator SDK 27 and 30 also on Samsung S22 Phone.

TL:DR

For SDK < 29 you need following the code here, and need little add code after successfully take picture. You can see at my savePictureQ(...) function below

Otherwise, if you are SDK >= 29 just pass the URI at MediaStore.EXTRA_OUTPUT extras from contentResolver.insert(...) function


Since startActivityForResult(Intent) already deprecated my version using registerForActivityResult(...)

private val cameraLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
val name: String = viewModel.savePictureQ()
if (name != "") requireActivity().applicationContext.deleteFile(name)
val cr = requireContext().contentResolver
val uri = viewModel.getTargetUri()
if (uri != null) {
val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val source = ImageDecoder.createSource(cr, uri)
ImageDecoder.decodeBitmap(source)
} else MediaStore.Images.Media.getBitmap(cr, uri)
val resized = Bitmap.createScaledBitmap(bitmap, 512, 512, true)
}
}
}

I call the Intent in another file named Repository.kt, I also using fake viewModel to call Repository code. Here is how I call my viewModel code

private lateinit var viewModel: MenuViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
viewModel = MenuViewModel(Injection.provideRepository(requireContext()))
...
}


private fun permissionCheck() {
val granted = PackageManager.PERMISSION_GRANTED
val permissions = arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
if (ActivityCompat.checkSelfPermission(
requireContext(),
permissions[0]
) != granted && ActivityCompat.checkSelfPermission(
requireContext(),
permissions[1]
) != granted && ActivityCompat.checkSelfPermission(
requireContext(),
permissions[2]
) != granted
) ActivityCompat.requestPermissions(
requireActivity(), permissions, MainActivity.REQUEST_CODE_PERMISSION
) else MainActivity.accepted = true


} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (ActivityCompat.checkSelfPermission(
requireContext(),
permissions[2]
) != granted && ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_MEDIA_LOCATION
) != granted
) ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(permissions[2], Manifest.permission.ACCESS_MEDIA_LOCATION),
MainActivity.REQUEST_CODE_PERMISSION
) else MainActivity.accepted = true
}
}


override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
bind.fromCamera.setOnClickListener {
permissionCheck()
if (`permission granted check`) {
viewModel.getCameraIntent(cameraLauncher)
}
}
...
}

in my fake viewModel:

class MenuViewModel(private val repository: IRepository) {
fun getTargetUri() = repository.getTargetUri()
fun getCameraIntent(launcher: ActivityResultLauncher<Intent>) =
repository.createTakePictureIntent(launcher)
fun savePictureQ(): String = repository.savePictureQ()
}

in my repository code:

class Repository private constructor(private val context: Context) : IRepository {


companion object {
@Volatile
private var INSTANCE: IRepository? = null


fun getInstance(context: Context) = INSTANCE ?: synchronized(this) {
INSTANCE ?: Repository(context).apply { INSTANCE = this }
}
}


private var currentPath = ""
private var targetUri: Uri? = null


private fun createImageFile(): File {  // create temporary file for SDK < 29
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(timestamp, ".jpg", storageDir)
.apply { currentPath = absolutePath }
}


override fun savePictureQ() : String {  // Saving picture and added to Gallery for SDK < 29
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
val f = File(currentPath)
val cr = context.contentResolver
val bitmap = BitmapFactory.decodeFile(currentPath)
val path = "${Environment.DIRECTORY_PICTURES}${File.separator}PoCkDetection"
val values = createContentValues(f.name, path)
var uri: Uri? = null
try {
uri = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)!!
val os = cr.openOutputStream(uri)
try {
val result = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os)
if (!result) throw Exception()
} catch (e: Exception) {
e.printStackTrace()
throw e
} finally {
os?.close()
targetUri = uri
}
f.delete()
if (f.exists()) {
f.canonicalFile.delete()
if (f.exists()) return f.name
}
} catch (e: Exception) {
e.printStackTrace()
uri?.let {
cr.delete(it, null, null)
}
}
}
return ""
}


override fun getTargetUri(): Uri? = targetUri


private fun createContentValues(title: String, path: String): ContentValues =
ContentValues().apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.MediaColumns.TITLE, "$title.jpg")
put(MediaStore.MediaColumns.DISPLAY_NAME, "$title.jpg")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.MediaColumns.DATE_TAKEN, System.currentTimeMillis())
put(MediaStore.MediaColumns.RELATIVE_PATH, path)
} else {
put(MediaStore.Images.Media.TITLE, "$title.jpg")
put(MediaStore.Images.Media.DISPLAY_NAME, "$title.jpg")
put(MediaStore.Images.Media.MIME_TYPE, "image/jpg")
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
}
}


override fun createTakePictureIntent(launcher: ActivityResultLauncher<Intent>) {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
takePictureIntent.resolveActivity(context.packageManager).also {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
val photoFile: File? = try {
createImageFile()
} catch (e: IOException) {
e.printStackTrace()
null
}
photoFile?.also {
val photoURI =
FileProvider.getUriForFile(context, "com.your.package.name", it)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
launcher.launch(takePictureIntent)
}
} else {
val timestamp =
SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val path = "${Environment.DIRECTORY_PICTURES}${File.separator}PoCkDetection"
val values = createContentValues(timestamp, path)
val photoURI = context.contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values
)
targetUri = photoURI
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
launcher.launch(takePictureIntent)
}
}
}
}
}

For SDK < 29 I follow this code from Google Developer

this is how my manifest look after following the code:

<application ...>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.your.package.name"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/camera_paths" />
</provider>
</application>

make new res folder called xml, then make new xml file make sure the name same like you place on <meta-data> in <provider> and inside that file:

<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path
name="camera_take"
path="Pictures" />
</paths>
   **You can use this too**


private fun saveFileInternal(
sourceFile: File,
fileName: String?,
fileType: FolderType,
contentResolver: ContentResolver
): Boolean {
var filename: String? = fileName
return try {
var selectedType = fileType
val contentValues = ContentValues()
val extension: String? = getFileExtension(sourceFile)
var mimeType: String? = null
if (extension != null) {
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
}
var uriToInsert: Uri? = null
if ((fileType == FolderType.IMAGE || fileType == FolderType.VIDEO) && mimeType != null) {
if (mimeType.startsWith("image")) {
selectedType = FolderType.IMAGE
}
if (mimeType.startsWith("video")) {
selectedType = FolderType.VIDEO
}
}
when (selectedType) {
FolderType.IMAGE -> {
if (filename == null) {
filename = generateFileName(0, extension)
}
uriToInsert =
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val dirDest = File(Environment.DIRECTORY_PICTURES, "folder")
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "$dirDest${separator}")
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, filename)
contentValues.put(MediaStore.Images.Media.MIME_TYPE, mimeType)
}
FolderType.VIDEO -> {
if (filename == null) {
filename = generateFileName(1, extension)
}
val dirDest = File(Environment.DIRECTORY_MOVIES, "folder")
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "$dirDest${separator}")
uriToInsert =
MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, filename)
    

}
FolderType.DOWNLOAD -> {
if (filename == null) {
filename = sourceFile.name
}
val dirDest = File(Environment.DIRECTORY_DOWNLOADS, "folder")
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "$dirDest${separator}")
uriToInsert =
MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
contentValues.put(MediaStore.Downloads.DISPLAY_NAME, filename)
}
else -> {
if (filename == null) {
filename = sourceFile.name
}
val dirDest = File(Environment.DIRECTORY_MUSIC, "folder")
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "$dirDest${separator}")
uriToInsert =
MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
contentValues.put(MediaStore.Audio.Media.DISPLAY_NAME, filename)
}
}
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
val dstUri: Uri = contentResolver.insert(uriToInsert!!, contentValues)!!
val fileInputStream = FileInputStream(sourceFile)
val outputStream: OutputStream = contentResolver.openOutputStream(dstUri)!!
    

copyFile(fileInputStream, outputStream)
fileInputStream.close()
true
} catch (e: java.lang.Exception) {
Log.e("fileManager", e.message.toString())
false
}
}