How to Improve Content resolver query speed in Android? - android

I would like to display the list of folders that contains images/videos. Also, I would like to get the number of images/videos in that folder, recently image/video. and also I would like to display the Camera, All Media, and All Videos folder on top of the list like WhatsApp.
fun getImageBuckets(mContext: Context) {
viewModelScope.launch(Dispatchers.IO) {
imageFolderList.clear()
val uri = MediaStore.Files.getContentUri("external")
val projection = arrayOf(
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.MIME_TYPE
)
// Return only video and image metadata.
val selection = (MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
+ " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO)
val order = MediaStore.Files.FileColumns.DATE_ADDED
val cursor: Cursor? =
mContext.contentResolver.query(uri, projection, selection, null, order)
var path = ""
var id: Long
var bucketPath: String? = null
var bucketCompletePath: String
var bucketCameraPath = ""
var mimeType: String
var imageDetail: ImageDetail
val cameraPathName =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
.toString() + "/Camera"
val folderMap: MutableMap<String, FolderDetail> = mutableMapOf()
val allMedias: MutableList<ImageDetail> = mutableListOf()
val allVideos: MutableList<ImageDetail> = mutableListOf()
if (cursor != null) {
val allMedia = FolderDetail("All media", path, 0, MediaFolderType.ALL_MEDIA)
val allVideo = FolderDetail("All videos", path, 0, MediaFolderType.ALL_VIDEO)
if (cursor.moveToLast()) {
do {
id = cursor.getLong(0)
bucketPath = cursor.getString(1)
path = cursor.getString(2)
mimeType = cursor.getString(3)
// val file = ImagePickerUtils.makeSafeFile(path) ?: continue
if (bucketPath == null) {
val parent = File(path).parentFile
bucketPath = if (parent != null) {
parent.name
} else {
PickerConstants.EMPTY_STRING
}
}
imageDetail = ImageDetail(id, path)
allMedias.add(imageDetail)
if (bucketPath != null) {
bucketCompletePath = path.substringBeforeLast("/")
var folder = folderMap[bucketCompletePath]
if (folder == null) {
if (path.contains(cameraPathName, true)) {
bucketCameraPath = bucketCompletePath
folder =
FolderDetail(bucketPath, path, 0, MediaFolderType.CAMERA)
folderMap[bucketCompletePath] = folder
} else {
folder =
FolderDetail(bucketPath, path, 0, MediaFolderType.FOLDER)
folderMap[bucketCompletePath] = folder
}
}
folder.images.add(imageDetail)
if (mimeType.contains("video", true)) {
allVideos.add(imageDetail)
}
}
} while (cursor.moveToPrevious())
cursor.close()
if (allMedias.isNotEmpty()) { // Check and Add top 3 folders has data then add at top
allMedia.images.addAll(allMedias)
allMedia.firstImagePath = allMedias[0].path
imageFolderList.add(allMedia)
if (allVideos.isNotEmpty()) {
allVideo.images.addAll(allVideos)
allVideo.firstImagePath = allVideos[0].path
imageFolderList.add(allVideo)
}
if (bucketCameraPath.isNotBlank()) {
val cameraMedia = folderMap[bucketCameraPath]
folderMap.remove(bucketCameraPath)
imageFolderList.add(0, cameraMedia!!)
}
}
imageFolderList.addAll(folderMap.values)
}
}
getFolderListDiffResult()
}
}
This is code is used to achieve this, It takes 200ms to read 9000 media files. I would like to further improve the logical performance. Any performance inputs for the above code?

Related

Access Images In External Storage Created By My App

I need a method to access the images stored in external storage by my app.
I want to access those images without requesting READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGES from the user
I'm using ACTION_GET_CONTENT to get the image from the user.
val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()){
val uri : String = it.data?.data?.toString()?:"null"
if (uri != "null"){
val mimeType = context.contentResolver.getType(uri.toUri()).toString()
it.data?.data?.let {
returnUri ->
context.contentResolver.query(returnUri, null, null, null, null)
}?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
cursor.moveToFirst()
val name = cursor.getString(nameIndex)
val size = cursor.getLong(sizeIndex)
image = image.copy(
path = uri, mimeType = mimeType.replace("image/",""), size = size,
name = name, uri = it.data?.data
)
}
}else{
image = image.copy(path = uri)
}
}
Calling the launcher for result
launcher.launch(Intent(Intent.ACTION_GET_CONTENT).setType("image/*"))
After performing the required actions on the image, I save the image using the following method.
fun saveFile(context : Context, file: File) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
try {
val values = ContentValues()
val path = Environment.DIRECTORY_PICTURES + "/FolderName"
values.put(MediaStore.MediaColumns.DISPLAY_NAME, file.name)
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/*")
values.put(MediaStore.MediaColumns.RELATIVE_PATH, path)
val savedFile = context.contentResolver.insert(Media.EXTERNAL_CONTENT_URI, values)
val outputStream = savedFile?.let {
context.contentResolver.openOutputStream(it)
}
val fis = FileInputStream(file)
var length : Int
val buffer = ByteArray(8192)
while (fis.read(buffer).also { length = it } > 0)
outputStream?.write(buffer, 0, length)
Toast.makeText(context, "Picture saved to $path", Toast.LENGTH_SHORT).show()
println("Picture : $path / $savedFile")
}catch (e : IOException){
Toast.makeText(
context,
"An error occured while saving the file",
Toast.LENGTH_SHORT
).show()
e.printStackTrace()
}catch (e : Exception) { e.printStackTrace() }
}else{
try {
val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).absolutePath + "/FolderName"
val image = File(dir, file.name)
val fis = FileInputStream(file)
val fos = FileOutputStream(image)
var length : Int
val buffer = ByteArray(8192)
while (fis.read(buffer).also { length = it } > 0) {
fos.write(buffer, 0, length)
}
}catch (e : IOException){
Toast.makeText(
context,
"An Error occurred while saving the file",
Toast.LENGTH_SHORT
).show()
e.printStackTrace()
}catch (e : Exception) { e.printStackTrace() }
}
}
psst ~ All of these actions are performed without requesting any permissions.
When I'm trying to access images from contentResolver, it always returns 0.
private fun loadImages() : List<Image> {
val photos = mutableListOf<Image>()
val collection = sdk29andUp {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} ?: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_ADDED
)
contentResolver.query(
collection, projection, null, null, null
)?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val displayNameColumn =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
val dateAddedColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_ADDED)
cursor.moveToFirst()
while (cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val name = cursor.getString(displayNameColumn)
val date = cursor.getLong(dateAddedColumn)
val contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
photos.add(
Image(
id = id, name = name, entryDate = date, contentUri = contentUri
))
}
}
return photos.toList()
}
If someone can help me with this, I would really appreciate that.
Edit : The app crashes on API 28 and Before, so I'll have to request permissions for those API levels, but it means there is a solution for api's after 28
I was able to fix it with a very straight forward implementation. Posting the code in hopes that it will help someone.
This implementation covers all API levels from 21 to 33
Saving the image
/** import MediaStore.Images.Media to remove Redundant typing of MediaStore.Images.Media **/
private fun saveImage(file: File) {
sdk29andUp {
val collection = Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val path = Environment.DIRECTORY_PICTURES + "/" + getString(R.string.app_name)
val values = ContentValues().apply {
put(Media.DISPLAY_NAME, file.name)
put(Media.SIZE, file.length())
put(Media.RELATIVE_PATH, path)
}
contentResolver.insert(collection, values)?.also { uri ->
contentResolver.openOutputStream(uri)?.use { outputStream ->
val fis = FileInputStream(file)
var length : Int
val buffer = ByteArray(8192)
while (fis.read(buffer).also { length = it } > 0)
outputStream.write(buffer, 0, length)
}
println(uri)
} ?: throw IOException("Error creating entry in mediaStore")
} ?: saveImageBefore29(file)
}
private fun saveImageBefore29(file: File) {
val resolver = contentResolver
val collection = Media.EXTERNAL_CONTENT_URI
val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).absolutePath + "/${getString(R.string.app_name)}"
val directory = File(dir)
if (!directory.exists())
directory.mkdirs()
val image = File(dir, file.name).also { println(it) }
val values = ContentValues().apply {
put(Media.DISPLAY_NAME, image.name)
put(Media.SIZE, image.length())
put(Media.MIME_TYPE, "image/png")
put(Media.DATA, image.path)
}
resolver.insert(collection, values)?.also { uri ->
contentResolver.openOutputStream(uri)?.use { outputStream ->
val fis = FileInputStream(file)
var length : Int
val buffer = ByteArray(8192)
while (fis.read(buffer).also { length = it } > 0)
outputStream.write(buffer, 0, length)
}
}
}
sdk29andUp function ->
inline fun <T> sdk29andUp(onSdk29: () -> T) : T? =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
onSdk29()
else null
Some things I want to point out are:
API 29 and above:
Creating the folder you want to save the file in before this function would result in IllegalArgumentException as your app doesn't own that folder. Just insert the folder path in RELATIVE_PATH and resolver will create the folder in which your app can write freely.
API 28 and below:
You will need to create the folder yourself in these API levels while also requesting the READ_EXTERNAL_STORAGE & WRITE_EXTERNAL_STORAGE from the user.
Loading The Images
private fun loadImages() : List<Image> {
return sdk29andUp {
val imagesList = mutableListOf<Image>()
val collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_MODIFIED
)
contentResolver.query(
collection,
projection,
null, null,
null
)?.use { cursor ->
val idColumn = cursor.getColumnIndex(MediaStore.Images.Media._ID)
val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
val dateColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATE_MODIFIED)
while (cursor.moveToNext()){
val id = cursor.getLong(idColumn)
val name = cursor.getString(nameColumn)
val date = cursor.getLong(dateColumn)
val contentUri = ContentUris.withAppendedId(collection, id).also { println("URI $it") }
imagesList += Image(
id = id, name = name, path = contentUri.path.toString(),
contentUri = contentUri, entryDate = date
)
}
imagesList
}
} ?: loadImagesBefore29()
}
private fun loadImagesBefore29() : List<Image> {
val images = mutableListOf<Image>()
val collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media.DATA
)
val selection = "${MediaStore.Images.Media.DATA} like ? "
val selectionArgs = arrayOf("%${getString(R.string.app_name)}%")
contentResolver.query(
collection,
projection,
selection,
selectionArgs,
null
)?.use { cursor ->
val idColumn = cursor.getColumnIndex(MediaStore.Images.Media._ID)
val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
val dateColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN)
val dataColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
while (cursor.moveToNext()){
val id = cursor.getLong(idColumn)
val name = cursor.getString(nameColumn)
val date = cursor.getLong(dateColumn)
val data = cursor.getString(dataColumn)
val contentUri = ContentUris.withAppendedId(
collection, id
).also { println("Uri $it") }
images += Image(
id = id, name = name, path = data,
contentUri = contentUri, entryDate = date
)
}
}
return images
}
When loading the images on API level 29 and above, if your app doesn't have READ_EXTERNAL_STORAGE or READ_MEDIA_AUDIO permission granted, ContentResolver will only query the MediaStore for images/files your app created.
On API level 28 and below, I'm querying a specific folder as the app holds READ_EXTERNAL_STORAGE so contentResolver will return all images in MediaStore if no selectionArgs are specified.
Hope I was able to explain this and help someone with this.

Android Studio Kotlin File Read

Hey i have this working FileWrite routine which i can simply call with
writeFile("Filename as String","Filetext as String")
but i cant read back the File i tried many solutions. I just want to call the read routine like this:
readFile("Filename as String") and file data should go to Sting: "txtfromfile"
`fun writeFile(name : String, data : String) {
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS + "/Sunny/")
}
val extVolumeUri: Uri = MediaStore.Files.getContentUri("external")
// query for the file
val cursor: Cursor? = contentResolver.query(
extVolumeUri,
null,
MediaStore.MediaColumns.DISPLAY_NAME + " = ? AND " + MediaStore.MediaColumns.MIME_TYPE + " = ?",
arrayOf(name, "text/plain"),
null
)
var fileUri: Uri? = null
// if file found
if (cursor != null && cursor.count > 0) {
// get URI
while (cursor.moveToNext()) {
val nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)
if (nameIndex > -1) {
val displayName = cursor.getString(nameIndex)
if (displayName == name) {
val idIndex = cursor.getColumnIndex(MediaStore.MediaColumns._ID)
if (idIndex > -1) {
val id = cursor.getLong(idIndex)
fileUri = ContentUris.withAppendedId(extVolumeUri, id)
}
}
}
}
cursor.close()
} else {
// insert new file otherwise
fileUri = contentResolver.insert(extVolumeUri, contentValues)
}
if (fileUri != null) {
val os = contentResolver.openOutputStream(fileUri, "wt")
if (os != null) {
os.write(data.toByteArray())
os.close()
}
}
}`
i tryed numerous read Routines
But I can't find any that I can call like my write routine
i tried this but it gives me "java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)"
fun readFile(name : String) {
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS + "/Sunny/")
}
val extVolumeUri: Uri = MediaStore.Files.getContentUri("external")
var fileUri: Uri? = null
fileUri = contentResolver.insert(extVolumeUri, contentValues)
val os = fileUri?.let { contentResolver.openInputStream(it) }
if (os != null) {
txtfromfile = os.read().toString()
os.close()
}
}

Store video sample in specific folder

I am trying to save video, created from custom application, to the specific folder inside DCIM folder, side by side to original camera folder.
private fun recordVideo() {
val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
takeVideo.launch(intent)
}
private val takeVideo = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
Log.i(
"VIDEO_RECORD_TAG ", "Video is available at ${
it.data?.data?.let { it1 -> getRealPathFromURI(it1) }
}"
)
saveMediaFile(
it.data?.data?.let { it1 -> getRealPathFromURI(it1) }, "MyVideoName"
)
}
}
private fun saveMediaFile(filePath: String?, fileName: String) {
filePath?.let {
val values = ContentValues().apply {
put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
val mExtension = MimeTypeMap.getFileExtensionFromUrl(filePath)
put(MediaStore.Video.Media.MIME_TYPE, MimeTypeMap.getSingleton().getMimeTypeFromExtension(mExtension))
put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + "/${getString(R.string.app_name)}/")
put(MediaStore.Video.Media.IS_PENDING, 1)
}
val fileUri = contentResolver.insert(
MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY),
values
)
fileUri?.let {
contentResolver.openFileDescriptor(fileUri, "w").use { descriptor ->
descriptor?.let {
FileOutputStream(descriptor.fileDescriptor).use { out ->
val videoFile = File(filePath)
FileInputStream(videoFile).use { inputStream ->
val buf = ByteArray(8192)
while (true) {
val sz = inputStream.read(buf)
if (sz <= 0) break
out.write(buf, 0, sz)
}
}
}
}
}
values.clear()
values.put(MediaStore.Video.Media.IS_PENDING, 0)
contentResolver.update(fileUri, values, null, null)
}
}
}
private fun getRealPathFromURI(contentUri: Uri): String? {
var cursor: Cursor? = null
return try {
val proj = arrayOf(MediaStore.Images.Media.DATA)
cursor = contentResolver.query(contentUri, proj, null, null, null)
val columnIndex = cursor?.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
cursor?.moveToFirst()
columnIndex?.let { cursor?.getString(it) }
} catch (e: Exception) {
Log.e("TAG", "getRealPathFromURI Exception : $e")
""
} finally {
cursor?.close()
}
}
Using this code, folder is created, video is stored as well, but in camera folder I have the same video with original name.
I would like to have only custom created folder with stored videos.
Any suggestion ?
Thanks

Android - unable to get files like pdf on android 10 and above

By this code, I successfully got pdf files on below android 10 but on android 10 above pdf files did not shows up in the debugger.
private val fileInfo: ArrayList<PdfFileManagerMC> = ArrayList()
val rootDir = Environment.getExternalStorageDirectory().absolutePath
val androidDir = File("$rootDir/Android")
val dataDir = File("$rootDir/data")
val files: Array<File>? =
File(rootDir).listFiles()
if (files != null) {
for (file in files) {
if (file != androidDir && file != dataDir)
getFiles(file)
}
}
private fun getFiles(root: File) {
val listAllFiles = root.listFiles()
if (listAllFiles != null && listAllFiles.isNotEmpty()) {
for (currentFile in listAllFiles) {
if (currentFile.name.endsWith(".pdf")) {
val temp = PdfFileManagerMC()
Log.e("fileManager", currentFile.absolutePath)
Log.e("fileManager", currentFile.name)
temp.path = currentFile.absolutePath
temp.name = currentFile.name
fileInfo.add(temp)
}
}
Log.e("fileManager", "" + fileInfo.size)
}
}
Note: Already added runtime permissions and manifest permission also
android:requestLegacyExternalStorage="true"
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Code by Media Store:
private fun getPdfList() {
val projection = arrayOf(
MediaStore.Files.FileColumns.DISPLAY_NAME,
MediaStore.Files.FileColumns.DATE_MODIFIED,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.MIME_TYPE
)
//// val sortOrder = MediaStore.Files.FileColumns.DATE_ADDED + " DESC"
//// val selection = MediaStore.Files.FileColumns.MIME_TYPE + " = ?"
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf")
val selectionArgs = arrayOf(mimeType)
val collection: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else {
MediaStore.Files.getContentUri("external")
}
contentResolver.query(collection, projection, "", selectionArgs, "")
.use { cursor ->
assert(cursor != null)
if (cursor!!.moveToFirst()) {
do {
val columnData = cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)
val name =
cursor.getColumnIndex(MediaStore.Files.FileColumns.DISPLAY_NAME)
val dateModified =
cursor.getColumnIndex(MediaStore.Files.FileColumns.DATE_MODIFIED)
val temp = PdfFileManagerMC()
Log.e("fileManager", cursor.getString(columnData))
Log.e("fileManager", cursor.getString(name))
Log.e("fileManager", cursor.getString(dateModified))
temp.path = cursor.getString(columnData)
temp.name = name.toString()
temp.date = dateModified.toString()
fileInfo.add(temp)
} while (cursor.moveToNext())
}
}
}

Android: Send intent with playable video to telegram app

I want to send a video to the telegram app via Intent but when I send it, it sends as a file, (same happens with images). Is there any trick to specify how you want to send it? (Because from the gallery it sends the same video playable)
function for sending via Intent
private fun shareItem(fileName: String) {
Log.i(TAG, "shareItem: ")
val videoContentUri = FileProvider.getUriForFile(requireContext(),
"${activity?.applicationContext?.packageName}.provider",
File(LocalFilesRepository.getFullVideoPath(fileName, requireContext()))
)
Log.i("TAG", "shareItem: $videoContentUri")
val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
putExtra(Intent.EXTRA_STREAM, videoContentUri)
type = "video/mp4"
}
startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.send_to)))
}
...
fun getFullVideoPath(fileName: String, context: Context) =
context.getExternalFilesDir(Environment.DIRECTORY_MOVIES).toString() + File.separatorChar + fileName
videoContentUri looks like this content://com.dmytroa.appname.provider/external_files/emulated/0/Android/data/com.dmytroa.appname/files/Movies/1625360538459.mp4
If someone is interested I solved it by just saving a file temporarily at the public storage and then returning Uri from Mediastore.
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

Categories

Resources