From 11aa25ad45f4f466251a53c86e6e1eb0402091d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Fri, 21 Jul 2023 11:16:24 +0200 Subject: [PATCH] Make subfolder grouping follow file structure This changes subfolder grouping to follow file hierarchy to prevent unreliable behavior with middle folders missing media. This fixes #1886 --- .../gallery/pro/extensions/Context.kt | 140 ++++++++---------- 1 file changed, 64 insertions(+), 76 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt index fe01f1229..96e83c074 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt @@ -195,90 +195,83 @@ fun Context.getDirsToShow(dirs: ArrayList, allDirs: ArrayList, currentPathPrefix: String): ArrayList { val folders = dirs.map { it.path }.sorted().toMutableSet() as HashSet - val currentPaths = LinkedHashSet() - val foldersWithoutMediaFiles = ArrayList() + // Sort by path length, to ensure that parents get processed first + val foldersByPathLength = dirs.filter { currentPathPrefix.isEmpty() || (it.path.startsWith(currentPathPrefix, true) && it.path != currentPathPrefix) }.sortedBy { it.path.length } var newDirId = 1000L + val groups = mutableMapOf>() - for (path in folders) { - if (path == RECYCLE_BIN || path == FAVORITES) { - continue - } + for (folder in foldersByPathLength) { + val parent = groups.keys.firstOrNull { folder.path.startsWith(it, true) } + if (parent != null) { + // If we have parent in top level groups + // Add this folder to that group, + // but also add all folders in between which may not have media files + groups.getOrPut(parent, ::mutableListOf).apply { + var midParent = File(folder.path).parent + while (midParent != null && none { it.path.equals(midParent, true) }) { + val isSortingAscending = config.sorting.isSortingAscending() + val subDirs = dirs.filter { File(it.path).parent.equals(midParent, true) } as ArrayList + if (subDirs.isNotEmpty()) { + val lastModified = if (isSortingAscending) { + subDirs.minByOrNull { it.modified }?.modified + } else { + subDirs.maxByOrNull { it.modified }?.modified + } ?: 0 - if (currentPathPrefix.isNotEmpty()) { - if (!path.startsWith(currentPathPrefix, true)) { - continue - } + val dateTaken = if (isSortingAscending) { + subDirs.minByOrNull { it.taken }?.taken + } else { + subDirs.maxByOrNull { it.taken }?.taken + } ?: 0 - if (!File(path).parent.equals(currentPathPrefix, true)) { - continue - } - } + var mediaTypes = 0 + subDirs.forEach { + mediaTypes = mediaTypes or it.types + } - if (currentPathPrefix.isNotEmpty() && path.equals(currentPathPrefix, true) || File(path).parent.equals(currentPathPrefix, true)) { - currentPaths.add(path) - } else if (folders.any { !it.equals(path, true) && (File(path).parent.equals(it, true) || File(it).parent.equals(File(path).parent, true)) }) { - // if we have folders like - // /storage/emulated/0/Pictures/Images and - // /storage/emulated/0/Pictures/Screenshots, - // but /storage/emulated/0/Pictures is empty, still Pictures with the first folders thumbnails and proper other info - val parent = File(path).parent - if (parent != null && !folders.contains(parent) && dirs.none { it.path.equals(parent, true) }) { - currentPaths.add(parent) - val isSortingAscending = config.sorting.isSortingAscending() - val subDirs = dirs.filter { File(it.path).parent.equals(File(path).parent, true) } as ArrayList - if (subDirs.isNotEmpty()) { - val lastModified = if (isSortingAscending) { - subDirs.minByOrNull { it.modified }?.modified - } else { - subDirs.maxByOrNull { it.modified }?.modified - } ?: 0 + val directory = Directory( + newDirId++, + midParent, + subDirs.first().tmb, + getFolderNameFromPath(midParent), + subDirs.sumBy { it.mediaCnt }, + lastModified, + dateTaken, + subDirs.sumByLong { it.size }, + getPathLocation(midParent), + mediaTypes, + "" + ) - val dateTaken = if (isSortingAscending) { - subDirs.minByOrNull { it.taken }?.taken - } else { - subDirs.maxByOrNull { it.taken }?.taken - } ?: 0 - - var mediaTypes = 0 - subDirs.forEach { - mediaTypes = mediaTypes or it.types + directory.containsMediaFilesDirectly = false + dirs.add(directory) + add(directory) } - - val directory = Directory( - newDirId++, - parent, - subDirs.first().tmb, - getFolderNameFromPath(parent), - subDirs.sumBy { it.mediaCnt }, - lastModified, - dateTaken, - subDirs.sumByLong { it.size }, - getPathLocation(parent), - mediaTypes, - "" - ) - - directory.containsMediaFilesDirectly = false - dirs.add(directory) - currentPaths.add(parent) - foldersWithoutMediaFiles.add(parent) + midParent = File(midParent).parent } + add(folder) } } else { - currentPaths.add(path) - } - } - - var areDirectSubfoldersAvailable = false - currentPaths.forEach { - val path = it - currentPaths.forEach { - if (!foldersWithoutMediaFiles.contains(it) && !it.equals(path, true) && File(it).parent?.equals(path, true) == true) { - areDirectSubfoldersAvailable = true + // If we have don't have parent in top level groups + // Set this folder as top level group if it is direct child + if (currentPathPrefix.isEmpty() || File(folder.path).parent.equals(currentPathPrefix, true)) { + groups.getOrPut(folder.path, ::mutableListOf).add(folder) + } else { + // Otherwise find its parent which is a direct child of current path prefix + // And create a group for it + var firstVisibleParent = File(folder.path).parent + while (firstVisibleParent != null && !File(firstVisibleParent).parent.equals(currentPathPrefix, true)) { + firstVisibleParent = File(firstVisibleParent).parent + } + if (firstVisibleParent != null) { + groups.getOrPut(firstVisibleParent, ::mutableListOf).add(folder) + } } } } + val currentPaths = groups.keys.toMutableList() + if (currentPathPrefix.isEmpty() && folders.contains(RECYCLE_BIN)) { currentPaths.add(RECYCLE_BIN) } @@ -294,12 +287,7 @@ fun Context.getDirectParentSubfolders(dirs: ArrayList, currentPathPre folders.clear() folders.addAll(currentPaths) - val dirsToShow = dirs.filter { folders.contains(it.path) } as ArrayList - return if (areDirectSubfoldersAvailable) { - getDirectParentSubfolders(dirsToShow, currentPathPrefix) - } else { - dirsToShow - } + return dirs.filter { folders.contains(it.path) } as ArrayList } fun Context.updateSubfolderCounts(children: ArrayList, parentDirs: ArrayList) {