1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-22 14:47:29 +00:00

use ContentResolver instead of findFile/listFiles

seems to have made the downloaded badge much smoother although it did not improve downloading en masse...
This commit is contained in:
Austin Huang 2021-07-01 16:16:09 -04:00
parent 8dc128563a
commit 6fba483bd0
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
2 changed files with 100 additions and 205 deletions

View File

@ -1,18 +1,14 @@
package awais.instagrabber.utils package awais.instagrabber.utils
import android.content.Context import android.content.Context
import android.content.DialogInterface
import android.content.UriPermission import android.content.UriPermission
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.util.Log import android.util.Log
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.util.Pair
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.work.* import androidx.work.*
import awais.instagrabber.R import awais.instagrabber.R
@ -40,6 +36,7 @@ object DownloadUtils {
private const val DIR_RECORDINGS = "Sent Recordings" private const val DIR_RECORDINGS = "Sent Recordings"
private const val DIR_TEMP = "Temp" private const val DIR_TEMP = "Temp"
private const val DIR_BACKUPS = "Backups" private const val DIR_BACKUPS = "Backups"
private const val MIME_DIR = DocumentsContract.Document.MIME_TYPE_DIR
private val dirMap: MutableMap<String, DocumentFile?> = mutableMapOf() private val dirMap: MutableMap<String, DocumentFile?> = mutableMapOf()
private var root: DocumentFile? = null private var root: DocumentFile? = null
@JvmStatic @JvmStatic
@ -71,8 +68,15 @@ object DownloadUtils {
} }
Utils.settingsHelper.putString(PreferenceKeys.PREF_BARINSTA_DIR_URI, uri.toString()) Utils.settingsHelper.putString(PreferenceKeys.PREF_BARINSTA_DIR_URI, uri.toString())
// set up directories // set up directories
val dirKeys = listOf(DIR_DOWNLOADS, DIR_CAMERA, DIR_EDIT, DIR_RECORDINGS, DIR_TEMP, DIR_BACKUPS) val dirKeys = mapOf(
dirMap.putAll(checkSubDirs(context, root, dirKeys, true)) DIR_DOWNLOADS to MIME_DIR,
DIR_CAMERA to MIME_DIR,
DIR_EDIT to MIME_DIR,
DIR_RECORDINGS to MIME_DIR,
DIR_TEMP to MIME_DIR,
DIR_BACKUPS to MIME_DIR
)
dirMap.putAll(checkFiles(context, root, dirKeys, true))
} }
fun destroy() { fun destroy() {
@ -80,16 +84,16 @@ object DownloadUtils {
dirMap.clear() dirMap.clear()
} }
fun checkSubDirs(context: Context, fun checkFiles(context: Context,
parent: DocumentFile?, parent: DocumentFile?,
queries: List<String>, queries: Map<String, String>, // <file name, mime type>
create: Boolean): Map<String, DocumentFile?> { create: Boolean
): Map<String, DocumentFile?> {
// first we'll find existing ones // first we'll find existing ones
val result: MutableMap<String, DocumentFile?> = mutableMapOf() val result: MutableMap<String, DocumentFile?> = mutableMapOf()
if (parent == null) return result.toMap() if (root == null || parent == null || !parent.isDirectory) return result.toMap()
val parentUri = parent.uri val docId = DocumentsContract.getDocumentId(parent.uri)
val docId = DocumentsContract.getTreeDocumentId(parentUri) val docUri = DocumentsContract.buildChildDocumentsUriUsingTree(root!!.uri, docId)
val docUri = DocumentsContract.buildChildDocumentsUriUsingTree(parentUri, docId)
val docCursor = context.contentResolver.query( val docCursor = context.contentResolver.query(
docUri, arrayOf( docUri, arrayOf(
DocumentsContract.Document.COLUMN_DISPLAY_NAME, DocumentsContract.Document.COLUMN_DISPLAY_NAME,
@ -99,162 +103,117 @@ object DownloadUtils {
) )
if (docCursor == null) return result.toMap() if (docCursor == null) return result.toMap()
while (docCursor.moveToNext()) { while (docCursor.moveToNext()) {
if (!DocumentsContract.Document.MIME_TYPE_DIR.equals(docCursor.getString(2)) || !queries.contains(docCursor.getString(0))) val q = queries.get(docCursor.getString(0))
continue if (q == null || !docCursor.getString(2).equals(q)) continue
val userFolderUri = DocumentsContract.buildDocumentUriUsingTree(parentUri, docCursor.getString(1)) val fileUri = DocumentsContract.buildDocumentUriUsingTree(parent.uri, docCursor.getString(1))
val dir = DocumentFile.fromTreeUri(context, userFolderUri) val dir = if (q.equals(MIME_DIR)) DocumentFile.fromTreeUri(context, fileUri)
Log.d("austin_debug", userFolderUri.toString() + " " + dir!!.uri.toString()) else DocumentFile.fromSingleUri(context, fileUri)
result.put(docCursor.getString(0), dir) result.put(docCursor.getString(0), dir)
if (result.size >= queries.size) break
} }
docCursor.close() docCursor.close()
// next we'll create inexistent ones // next we'll create inexistent ones, if necessary
if (create) { if (create) {
for (k in queries) { for (k in queries) {
if (result.get(k) == null) { if (result.get(k.key) == null) {
result.put(k, parent.createDirectory(k)) result.put(k.key, if (MIME_DIR.equals(k.value)) parent.createDirectory(k.key)
else parent.createFile(k.value, k.key))
} }
} }
} }
return result.toMap() return result.toMap()
} }
fun getDownloadDir(dir: String): DocumentFile? { fun getRootDir(dir: String): DocumentFile? {
if (root == null) { if (root == null) return null
return null
}
return dirMap.get(dir) return dirMap.get(dir)
} }
@JvmStatic @JvmStatic
val downloadDir: DocumentFile? val downloadDir: DocumentFile?
get() = getDownloadDir(DIR_DOWNLOADS) get() = getRootDir(DIR_DOWNLOADS)
@JvmStatic @JvmStatic
val cameraDir: DocumentFile? val cameraDir: DocumentFile?
get() = getDownloadDir(DIR_CAMERA) get() = getRootDir(DIR_CAMERA)
@JvmStatic @JvmStatic
fun getImageEditDir(sessionId: String?, context: Context): DocumentFile? { fun getImageEditDir(sessionId: String?, context: Context): DocumentFile? {
val editRoot = getDownloadDir(DIR_EDIT) val editRoot = getRootDir(DIR_EDIT)
if (sessionId == null) return editRoot if (sessionId == null) return editRoot
val result = checkSubDirs(context, editRoot, listOf(sessionId), true) return checkFiles(context,
return result.get(sessionId) editRoot,
mapOf(sessionId to MIME_DIR),
true).get(sessionId)
} }
@JvmStatic @JvmStatic
val recordingsDir: DocumentFile? val recordingsDir: DocumentFile?
get() = getDownloadDir(DIR_RECORDINGS) get() = getRootDir(DIR_RECORDINGS)
@JvmStatic @JvmStatic
val backupsDir: DocumentFile? val backupsDir: DocumentFile?
get() = getDownloadDir(DIR_BACKUPS) get() = getRootDir(DIR_BACKUPS)
// @Nullable
// private static DocumentFile getDownloadDir(@NonNull final Context context, @Nullable final String username) {
// return getDownloadDir(context, username, false);
// }
private fun getDownloadDir( private fun getDownloadDir(
context: Context?, context: Context,
username: String? username: String?,
shouldCreate: Boolean
): DocumentFile? { ): DocumentFile? {
val userFolderPaths: List<String> = getSubPathForUserFolder(username) if (!Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_USER_FOLDER) || username.isNullOrEmpty())
var dir = root return downloadDir
for (dirName in userFolderPaths) { return checkFiles(context,
val file = dir!!.findFile(dirName) downloadDir,
if (file != null) { mapOf(username to MIME_DIR),
dir = file shouldCreate).get(username)
continue
}
dir = dir.createDirectory(dirName)
if (dir == null) break
}
// final String joined = android.text.TextUtils.join("/", userFolderPaths);
// final Uri userFolderUri = DocumentsContract.buildDocumentUriUsingTree(root.getUri(), joined);
// final DocumentFile userFolder = DocumentFile.fromSingleUri(context, userFolderUri);
if (context != null && (dir == null || !dir.exists())) {
Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show()
return null
}
return dir
} }
private fun getSubPathForUserFolder(username: String?): MutableList<String> { private val tempDir: DocumentFile?
val list: MutableList<String> = ArrayList() get() = getRootDir(DIR_TEMP)
if (!Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_USER_FOLDER) ||
username.isNullOrEmpty()) {
list.add(DIR_DOWNLOADS)
return list
}
val finalUsername = if (username.startsWith("@")) username.substring(1) else username
list.add(DIR_DOWNLOADS)
list.add(finalUsername)
return list
}
private fun getTempDir(): DocumentFile? {
var file = root!!.findFile(DIR_TEMP)
if (file == null) {
file = root!!.createDirectory(DIR_TEMP)
}
return file
}
private fun getDownloadSavePaths( private fun getDownloadSavePaths(
paths: MutableList<String>,
postId: String?, postId: String?,
displayUrl: String? displayUrl: String?
): Pair<List<String>, String?>? { ): Pair<String, String> {
return getDownloadSavePaths(paths, postId, "", displayUrl, "") return getDownloadFileName(postId, "", displayUrl, "")
} }
private fun getDownloadSavePaths( private fun getDownloadSavePaths(
paths: MutableList<String>,
postId: String?, postId: String?,
displayUrl: String, displayUrl: String,
username: String username: String
): Pair<List<String>, String?>? { ): Pair<String, String> {
return getDownloadSavePaths(paths, postId, "", displayUrl, username) return getDownloadFileName(postId, "", displayUrl, username)
} }
private fun getDownloadChildSavePaths( private fun getDownloadChildSavePaths(
paths: MutableList<String>,
postId: String?, postId: String?,
childPosition: Int, childPosition: Int,
url: String?, url: String?,
username: String username: String
): Pair<List<String>, String?>? { ): Pair<String, String> {
val sliderPostfix = "_slide_$childPosition" val sliderPostfix = "_slide_$childPosition"
return getDownloadSavePaths(paths, postId, sliderPostfix, url, username) return getDownloadFileName(postId, sliderPostfix, url, username)
} }
private fun getDownloadSavePaths( private fun getDownloadFileName(
paths: MutableList<String>?,
postId: String?, postId: String?,
sliderPostfix: String, sliderPostfix: String,
displayUrl: String?, displayUrl: String?,
username: String username: String
): Pair<List<String>, String?>? { ): Pair<String, String> {
if (paths == null) return null
val extension = getFileExtensionFromUrl(displayUrl) val extension = getFileExtensionFromUrl(displayUrl)
val usernamePrepend = if (isEmpty(username)) "" else username + "_" val usernamePrepend = if (isEmpty(username)) "" else username + "_"
val fileName = usernamePrepend + postId + sliderPostfix + extension val fileName = usernamePrepend + postId + sliderPostfix + extension
// return new File(finalDir, fileName);
// DocumentFile file = finalDir.findFile(fileName);
// if (file == null) {
val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension( val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(
if (extension.startsWith(".")) extension.substring(1) else extension if (extension.startsWith(".")) extension.substring(1) else extension
) )
// file = finalDir.createFile(mimeType, fileName); return Pair(fileName, mimeType!!)
// }
paths.add(fileName)
return Pair(paths, mimeType)
} }
// public static DocumentFile getTempFile() { // can't convert to checkFiles() due to lack of Context
// return getTempFile(null, null);
// }
fun getTempFile(fileName: String?, extension: String): DocumentFile? { fun getTempFile(fileName: String?, extension: String): DocumentFile? {
val dir = getTempDir() val dir = tempDir
var name = fileName var name = fileName
if (isEmpty(name)) { if (isEmpty(name)) {
name = UUID.randomUUID().toString() name = UUID.randomUUID().toString()
@ -322,26 +281,23 @@ object DownloadUtils {
if (user != null) { if (user != null) {
username = user.username username = user.username
} }
val userFolderPaths: List<String> = getSubPathForUserFolder(username) val userFolder = getDownloadDir(context, username, false)
if (userFolder == null) return checkList
when (media.type) { when (media.type) {
MediaItemType.MEDIA_TYPE_IMAGE, MediaItemType.MEDIA_TYPE_VIDEO -> { MediaItemType.MEDIA_TYPE_IMAGE, MediaItemType.MEDIA_TYPE_VIDEO -> {
val url = val url =
if (media.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl( if (media.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(
media media
) else ResponseBodyUtils.getImageUrl(media) ) else ResponseBodyUtils.getImageUrl(media)
val file = getDownloadSavePaths(ArrayList(userFolderPaths), media.code, url, "") val fileName = getDownloadSavePaths(media.code, url)
val fileExists = file!!.first != null && checkPathExists(file.first, context) val fileNameWithUser = getDownloadSavePaths(media.code, url, username)
var usernameFileExists = false val files = checkFiles(context, userFolder, mapOf(fileName, fileNameWithUser), false)
if (!fileExists) { checkList.add(files.size > 0)
val usernameFile = getDownloadSavePaths(
ArrayList(userFolderPaths), media.code, url, username
)
usernameFileExists = usernameFile!!.first != null && checkPathExists(usernameFile.first, context)
}
checkList.add(fileExists || usernameFileExists)
} }
MediaItemType.MEDIA_TYPE_SLIDER -> { MediaItemType.MEDIA_TYPE_SLIDER -> {
val sliderItems = media.carouselMedia val sliderItems = media.carouselMedia
val fileNames: MutableMap<String, String> = mutableMapOf()
val filePairs: MutableMap<String, String> = mutableMapOf()
var i = 0 var i = 0
while (i < sliderItems!!.size) { while (i < sliderItems!!.size) {
val child = sliderItems[i] val child = sliderItems[i]
@ -349,20 +305,17 @@ object DownloadUtils {
if (child.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl( if (child.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(
child child
) else ResponseBodyUtils.getImageUrl(child) ) else ResponseBodyUtils.getImageUrl(child)
val file = getDownloadChildSavePaths( val fileName = getDownloadChildSavePaths(media.code, i+1, url, "")
ArrayList(userFolderPaths), media.code, i + 1, url, "" val fileNameWithUser = getDownloadChildSavePaths(media.code, i+1, url, username)
) fileNames.put(fileName.first, fileName.second)
val fileExists = file!!.first != null && checkPathExists(file.first, context) fileNames.put(fileNameWithUser.first, fileNameWithUser.second)
var usernameFileExists = false filePairs.put(fileName.first, fileNameWithUser.first)
if (!fileExists) {
val usernameFile = getDownloadChildSavePaths(
ArrayList(userFolderPaths), media.code, i + 1, url, username
)
usernameFileExists = usernameFile!!.first != null && checkPathExists(usernameFile.first, context)
}
checkList.add(fileExists || usernameFileExists)
i++ i++
} }
val files = checkFiles(context, userFolder, fileNames, false)
for (p in filePairs) {
checkList.add(files.get(p.key) != null || files.get(p.value) != null)
}
} }
else -> { else -> {
} }
@ -370,33 +323,6 @@ object DownloadUtils {
return checkList return checkList
} }
private fun checkPathExists(paths: List<String>, context: Context): Boolean {
if (root == null) return false
val uri = root!!.uri
var found = false
var docId = DocumentsContract.getTreeDocumentId(uri)
for (path in paths) {
val docUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, docId)
val docCursor = context.contentResolver.query(
docUri, arrayOf(
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_DOCUMENT_ID
), null, null, null
)
if (docCursor == null) return false
while (docCursor.moveToNext() && !found) {
if (path.equals(docCursor.getString(0))) {
docId = docCursor.getString(1)
found = true
}
}
docCursor.close()
if (!found) return false
found = false
}
return true
}
@JvmStatic @JvmStatic
fun showDownloadDialog( fun showDownloadDialog(
context: Context, context: Context,
@ -430,27 +356,19 @@ object DownloadUtils {
context: Context, context: Context,
storyModel: StoryMedia storyModel: StoryMedia
) { ) {
val downloadDir = getDownloadDir(context, storyModel.user?.username) ?: return val downloadDir = getDownloadDir(context, storyModel.user?.username, true) ?: return
val url = val url =
if (storyModel.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(storyModel) if (storyModel.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(storyModel)
else ResponseBodyUtils.getImageUrl(storyModel) else ResponseBodyUtils.getImageUrl(storyModel)
val extension = getFileExtensionFromUrl(url) val extension = getFileExtensionFromUrl(url)
val baseFileName = (storyModel.id + "_" val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(extension)
+ storyModel.takenAt + extension) val baseFileName = storyModel.id + "_" + storyModel.takenAt + extension
val usernamePrepend = val usernamePrepend =
if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME)
&& storyModel.user?.username != null && storyModel.user?.username != null
) storyModel.user.username + "_" else "" ) storyModel.user.username + "_" else ""
val fileName = usernamePrepend + baseFileName val fileName = usernamePrepend + baseFileName
var saveFile = downloadDir.findFile(fileName) var saveFile = checkFiles(context, downloadDir, mapOf(fileName to mimeType!!), true).get(fileName)
if (saveFile == null) {
val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(
if (extension.startsWith(".")) extension.substring(1) else extension
)
?: return
saveFile = downloadDir.createFile(mimeType, fileName)
}
// final File saveFile = new File(downloadDir, fileName);
download(context, url, saveFile) download(context, url, saveFile)
} }
@ -477,11 +395,12 @@ object DownloadUtils {
feedModels: List<Media>, feedModels: List<Media>,
childPositionIfSingle: Int childPositionIfSingle: Int
) { ) {
val map: MutableMap<String, DocumentFile> = HashMap() val map: MutableMap<String, Pair<String, String>> = HashMap()
val fileMap: MutableMap<String, DocumentFile?> = HashMap()
for (media in feedModels) { for (media in feedModels) {
val mediaUser = media.user val mediaUser = media.user
val username = mediaUser?.username ?: "" val username = mediaUser?.username ?: ""
val userFolderPaths = getSubPathForUserFolder(username) val dir = getDownloadDir(context, username, true)
when (media.type) { when (media.type) {
MediaItemType.MEDIA_TYPE_IMAGE, MediaItemType.MEDIA_TYPE_VIDEO -> { MediaItemType.MEDIA_TYPE_IMAGE, MediaItemType.MEDIA_TYPE_VIDEO -> {
val url = getUrlOfType(media) val url = getUrlOfType(media)
@ -495,9 +414,8 @@ object DownloadUtils {
fileName = mediaUser.username + "_" + fileName fileName = mediaUser.username + "_" + fileName
} }
} }
val pair = getDownloadSavePaths(userFolderPaths, fileName, url) val pair = getDownloadSavePaths(fileName, url)
val file = createFile(pair!!) ?: continue map[url!!] = pair
map[url!!] = file
} }
MediaItemType.MEDIA_TYPE_VOICE -> { MediaItemType.MEDIA_TYPE_VOICE -> {
val url = getUrlOfType(media) val url = getUrlOfType(media)
@ -505,9 +423,8 @@ object DownloadUtils {
if (mediaUser != null) { if (mediaUser != null) {
fileName = mediaUser.username + "_" + fileName fileName = mediaUser.username + "_" + fileName
} }
val pair = getDownloadSavePaths(userFolderPaths, fileName, url) val pair = getDownloadSavePaths(fileName, url)
val file = createFile(pair!!) ?: continue map[url!!] = pair
map[url!!] = file
} }
MediaItemType.MEDIA_TYPE_SLIDER -> { MediaItemType.MEDIA_TYPE_SLIDER -> {
val sliderItems = media.carouselMedia val sliderItems = media.carouselMedia
@ -522,42 +439,19 @@ object DownloadUtils {
val usernamePrepend = val usernamePrepend =
if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null) mediaUser.username else "" if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null) mediaUser.username else ""
val pair = getDownloadChildSavePaths( val pair = getDownloadChildSavePaths(
ArrayList(userFolderPaths), media.code, i + 1, url, usernamePrepend media.code, i + 1, url, usernamePrepend
) )
val file = createFile(pair!!) map[url!!] = pair
if (file == null) {
i++
continue
}
map[url!!] = file
i++ i++
} }
} }
} }
fileMap.putAll(checkFiles(context, dir, map.values.toMap(), true))
} }
if (map.isEmpty()) return if (map.isEmpty() || fileMap.isEmpty()) return
download(context, map) val resultMap: MutableMap<String, DocumentFile?> = mutableMapOf()
} map.mapValuesTo(resultMap) { fileMap.get(it.value.first) }
download(context, resultMap)
private fun createFile(pair: Pair<List<String>, String?>): DocumentFile? {
if (root == null) return null
if (pair.first == null || pair.second == null) return null
var dir = root
val first = pair.first
for (i in first.indices) {
val name = first[i]
val file = dir!!.findFile(name)
if (file != null) {
dir = file
continue
}
dir = if (i == first.size - 1) dir.createFile(
pair.second!!,
name
) else dir.createDirectory(name)
if (dir == null) break
}
return dir
} }
private fun getUrlOfType(media: Media): String? { private fun getUrlOfType(media: Media): String? {
@ -595,7 +489,7 @@ object DownloadUtils {
download(context, Collections.singletonMap(url!!, filePath)) download(context, Collections.singletonMap(url!!, filePath))
} }
private fun download(context: Context?, urlFilePathMap: Map<String, DocumentFile>) { private fun download(context: Context?, urlFilePathMap: Map<String, DocumentFile?>) {
if (context == null) return if (context == null) return
val constraints = Constraints.Builder() val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) .setRequiredNetworkType(NetworkType.CONNECTED)

View File

@ -393,9 +393,10 @@ class DownloadWorker(context: Context, workerParams: WorkerParameters) : Corouti
class Builder { class Builder {
private var urlToFilePathMap: MutableMap<String, String> = mutableMapOf() private var urlToFilePathMap: MutableMap<String, String> = mutableMapOf()
fun setUrlToFilePathMap(urlToFilePathMap: Map<String, DocumentFile>): Builder { fun setUrlToFilePathMap(urlToFilePathMap: Map<String, DocumentFile?>): Builder {
this.urlToFilePathMap = urlToFilePathMap this.urlToFilePathMap = urlToFilePathMap
.mapValues { it.value.uri.toString() } .filter{ it.value != null }
.mapValues { it.value!!.uri.toString() }
.toMutableMap() .toMutableMap()
return this return this
} }