如果有人感兴趣,我是通过将文件暂时保存在公共存储中,然后从Mediastore返回Uri来解决这个问题的。
fun createTemporaryCopyInPublicStorage(file: File, context: Context): Uri? {
val fileName = "tmp"
return if(Build.VERSION.SDK_INT >= 29) {
val uri = findCreatedTemporaryUri(context, fileName, TEMPORARY_DIR_Q)
copyVideoQAndAbove(context, file, uri, fileName, TEMPORARY_DIR_Q)
} else {
val uri = findCreatedTemporaryUri(context, fileName, TEMPORARY_DIR_BELOWQ)
copyVideoBelowQ(context, file, uri, fileName, TEMPORARY_DIR_BELOWQ)
}
}
private fun findCreatedTemporaryUri(context: Context, fileName: String, path: String): Uri? {
val collection = if(Build.VERSION.SDK_INT >= 29) {
MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
}
val selection = if (Build.VERSION.SDK_INT >= 29) {
"${MediaStore.Video.Media.TITLE} = ? " +
"AND " +
"${MediaStore.Video.Media.RELATIVE_PATH} = ? "
} else {
"${MediaStore.Video.Media.TITLE} = ? " +
"AND " +
"${MediaStore.Video.Media.DATA} = ? "
}
val args = if (Build.VERSION.SDK_INT >= 29) {
arrayOf(fileName, path)
} else {
arrayOf(fileName, File(path, fileName).absolutePath)
}
context.contentResolver.query(
collection,
arrayOf(MediaStore.Video.Media._ID
),
selection,
args,
null
).use { cursor ->
return if (cursor != null && cursor.moveToFirst()) {
val columnIndexID = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)
val id = cursor.getLong(columnIndexID)
Log.i(TAG, "findCreatedTemporaryUri: " +
"contentUri was already added $id $path $fileName")
Uri.withAppendedPath(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "$id")
} else {
null
}
}
}
@RequiresApi(Build.VERSION_CODES.Q)
private fun copyVideoQAndAbove(context: Context,
fileToCopy: File,
uri: Uri?,
fileName: String,
relPath: String): Uri? {
val contentDetails = ContentValues().apply {
put(MediaStore.Video.Media.IS_PENDING, 1)
}
val contentUri = if (uri != null) {
context.contentResolver.update(uri, contentDetails, null, null)
uri
} else {
Log.i(TAG, "saveVideoQAndAbove: contentUri insert")
contentDetails.apply {
put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
put(MediaStore.Video.Media.RELATIVE_PATH, relPath)
}
val collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
context.contentResolver.insert(collection, contentDetails)
}
return contentUri?.let { createdUri ->
try {
context.contentResolver.openFileDescriptor(createdUri, "w").use { pfd ->
ParcelFileDescriptor.AutoCloseOutputStream(pfd).write(fileToCopy.readBytes())
}
} catch (e: IOException) {
e.printStackTrace()
}
contentDetails.clear()
contentDetails.put(MediaStore.Video.Media.IS_PENDING, 0)
context.contentResolver.update(createdUri, contentDetails, null, null)
createdUri
}
}
private fun copyVideoBelowQ(context: Context,
fileToCopy: File,
uri: Uri?,
fileName: String,
dstParentPath: String): Uri? {
val dstDir = File(dstParentPath)
if (!dstDir.exists())
dstDir.mkdirs()
val fileToCreate = File(dstDir, fileName)
fileToCreate.delete()
fileToCreate.createNewFile()
Log.i(TAG, "saveVideo: created ${fileToCreate.name}")
try {
fileToCopy.inputStream().use { input ->
fileToCreate.outputStream().use { output ->
input.copyTo(output, 2 * 1024)
Log.i(TAG, "saveVideo: finished ${fileToCreate.path}")
}
}
return uri ?: let {
val values = ContentValues().apply {
put(MediaStore.Video.Media.TITLE, fileToCreate.name)
put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
put(MediaStore.Video.Media.DATA, fileToCreate.path)
}
context.contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values)
}
} catch (e: IOException) {
e.printStackTrace()
}
return null
}
private val TEMPORARY_DIR_Q = Environment.DIRECTORY_MOVIES + File.separator +
"APPNAME" + File.separator +
"temporary" + File.separator
private val TEMPORARY_DIR_BELOWQ = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES).absolutePath + File.separator +
"APPNAME" + File.separator +
"temporary" + File.separator