Use nio for File.listFiles() #449
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package org.koitharu.kotatsu.core.fs
|
||||
|
||||
import android.os.Build
|
||||
import org.koitharu.kotatsu.core.util.iterator.CloseableIterator
|
||||
import org.koitharu.kotatsu.core.util.iterator.MappingIterator
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
class FileSequence(private val dir: File) : Sequence<File> {
|
||||
|
||||
override fun iterator(): Iterator<File> {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val stream = Files.newDirectoryStream(dir.toPath())
|
||||
CloseableIterator(MappingIterator(stream.iterator(), Path::toFile), stream)
|
||||
} else {
|
||||
dir.listFiles().orEmpty().iterator()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.fs.FileSequence
|
||||
import java.io.File
|
||||
import java.io.FileFilter
|
||||
import java.util.zip.ZipEntry
|
||||
@@ -73,11 +74,10 @@ suspend fun File.computeSize(): Long = runInterruptible(Dispatchers.IO) {
|
||||
|
||||
@WorkerThread
|
||||
private fun computeSizeInternal(file: File): Long {
|
||||
if (file.isDirectory) {
|
||||
val files = file.listFiles() ?: return 0L
|
||||
return files.sumOf { computeSizeInternal(it) }
|
||||
return if (file.isDirectory) {
|
||||
file.children().sumOf { computeSizeInternal(it) }
|
||||
} else {
|
||||
return file.length()
|
||||
file.length()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +86,8 @@ fun File.listFilesRecursive(filter: FileFilter? = null): Sequence<File> = sequen
|
||||
}
|
||||
|
||||
private suspend fun SequenceScope<File>.listFilesRecursiveImpl(root: File, filter: FileFilter?) {
|
||||
val ss = root.list() ?: return
|
||||
for (s in ss) {
|
||||
val f = File(root, s)
|
||||
val ss = root.children()
|
||||
for (f in ss) {
|
||||
if (f.isDirectory) {
|
||||
listFilesRecursiveImpl(f, filter)
|
||||
} else if (filter == null || filter.accept(f)) {
|
||||
@@ -96,3 +95,7 @@ private suspend fun SequenceScope<File>.listFilesRecursiveImpl(root: File, filte
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun File.children() = FileSequence(this)
|
||||
|
||||
fun Sequence<File>.filterWith(filter: FileFilter): Sequence<File> = filter { f -> filter.accept(f) }
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.koitharu.kotatsu.core.util.iterator
|
||||
|
||||
import okhttp3.internal.closeQuietly
|
||||
import okio.Closeable
|
||||
|
||||
class CloseableIterator<T>(
|
||||
private val upstream: Iterator<T>,
|
||||
private val closeable: Closeable,
|
||||
) : Iterator<T>, Closeable {
|
||||
|
||||
private var isClosed = false
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
val result = upstream.hasNext()
|
||||
if (!result) {
|
||||
close()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun next(): T {
|
||||
try {
|
||||
return upstream.next()
|
||||
} catch (e: NoSuchElementException) {
|
||||
close()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (!isClosed) {
|
||||
closeable.closeQuietly()
|
||||
isClosed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.koitharu.kotatsu.core.util.iterator
|
||||
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
class MappingIterator<T, R>(
|
||||
private val upstream: Iterator<T>,
|
||||
private val mapper: (T) -> R,
|
||||
) : Iterator<R> {
|
||||
|
||||
override fun hasNext(): Boolean = upstream.hasNext()
|
||||
|
||||
override fun next(): R = mapper(upstream.next())
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.core.zip
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.collection.ArraySet
|
||||
import okio.Closeable
|
||||
import org.koitharu.kotatsu.core.util.ext.children
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.util.zip.Deflater
|
||||
@@ -90,7 +91,7 @@ class ZipOutput(
|
||||
}
|
||||
putNextEntry(entry)
|
||||
closeEntry()
|
||||
fileToZip.listFiles()?.forEach { childFile ->
|
||||
fileToZip.children().forEach { childFile ->
|
||||
appendFile(childFile, "$name/${childFile.name}")
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -15,7 +15,9 @@ import org.koitharu.kotatsu.core.model.isLocal
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.util.CompositeMutex
|
||||
import org.koitharu.kotatsu.core.util.ext.children
|
||||
import org.koitharu.kotatsu.core.util.ext.deleteAwait
|
||||
import org.koitharu.kotatsu.core.util.ext.filterWith
|
||||
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.local.data.input.LocalMangaInput
|
||||
import org.koitharu.kotatsu.local.data.output.LocalMangaOutput
|
||||
@@ -128,9 +130,6 @@ class LocalMangaRepository @Inject constructor(
|
||||
|
||||
suspend fun findSavedManga(remoteManga: Manga): LocalManga? {
|
||||
val files = getAllFiles()
|
||||
if (files.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
return channelFlow {
|
||||
for (file in files) {
|
||||
launch {
|
||||
@@ -172,7 +171,7 @@ class LocalMangaRepository @Inject constructor(
|
||||
val dirs = storageManager.getWriteableDirs()
|
||||
runInterruptible(Dispatchers.IO) {
|
||||
dirs.flatMap { dir ->
|
||||
dir.listFiles(TempFileFilter())?.toList().orEmpty()
|
||||
dir.children().filterWith(TempFileFilter())
|
||||
}.forEach { file ->
|
||||
file.deleteRecursively()
|
||||
}
|
||||
@@ -189,7 +188,7 @@ class LocalMangaRepository @Inject constructor(
|
||||
}
|
||||
|
||||
private suspend fun getRawList(): ArrayList<LocalManga> {
|
||||
val files = getAllFiles()
|
||||
val files = getAllFiles().toList() // TODO remove toList()
|
||||
return coroutineScope {
|
||||
val dispatcher = Dispatchers.IO.limitedParallelism(MAX_PARALLELISM)
|
||||
files.map { file ->
|
||||
@@ -200,8 +199,8 @@ class LocalMangaRepository @Inject constructor(
|
||||
}.filterNotNullTo(ArrayList(files.size))
|
||||
}
|
||||
|
||||
private suspend fun getAllFiles() = storageManager.getReadableDirs().flatMap { dir ->
|
||||
dir.listFiles()?.toList().orEmpty()
|
||||
private suspend fun getAllFiles() = storageManager.getReadableDirs().asSequence().flatMap { dir ->
|
||||
dir.children()
|
||||
}
|
||||
|
||||
private fun Collection<LocalManga>.unwrap(): List<Manga> = map { it.manga }
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package org.koitharu.kotatsu.local.data
|
||||
|
||||
import java.io.File
|
||||
import java.io.FileFilter
|
||||
import java.io.FilenameFilter
|
||||
|
||||
class TempFileFilter : FilenameFilter {
|
||||
class TempFileFilter : FilenameFilter, FileFilter {
|
||||
|
||||
override fun accept(dir: File, name: String): Boolean {
|
||||
return name.endsWith(".tmp", ignoreCase = true)
|
||||
}
|
||||
|
||||
override fun accept(file: File): Boolean {
|
||||
return file.name.endsWith(".tmp", ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import org.koitharu.kotatsu.core.util.AlphanumComparator
|
||||
import org.koitharu.kotatsu.core.util.ext.listFilesRecursive
|
||||
import org.koitharu.kotatsu.core.util.ext.longHashCode
|
||||
import org.koitharu.kotatsu.core.util.ext.toListSorted
|
||||
@@ -88,7 +89,7 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) {
|
||||
val file = chapter.url.toUri().toFile()
|
||||
if (file.isDirectory) {
|
||||
file.listFilesRecursive(ImageFileFilter())
|
||||
.toListSorted(compareBy(org.koitharu.kotatsu.core.util.AlphanumComparator()) { x -> x.name })
|
||||
.toListSorted(compareBy(AlphanumComparator()) { x -> x.name })
|
||||
.map {
|
||||
val pageUri = it.toUri().toString()
|
||||
MangaPage(
|
||||
@@ -104,7 +105,7 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) {
|
||||
.asSequence()
|
||||
.filter { x -> !x.isDirectory }
|
||||
.map { it.name }
|
||||
.toListSorted(org.koitharu.kotatsu.core.util.AlphanumComparator())
|
||||
.toListSorted(AlphanumComparator())
|
||||
.map {
|
||||
val pageUri = zipUri(file, it)
|
||||
MangaPage(
|
||||
@@ -121,7 +122,7 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) {
|
||||
private fun String.toHumanReadable() = replace("_", " ").toCamelCase()
|
||||
|
||||
private fun getChaptersFiles(): List<File> = root.listFilesRecursive(CbzFilter())
|
||||
.toListSorted(compareBy(org.koitharu.kotatsu.core.util.AlphanumComparator()) { x -> x.name })
|
||||
.toListSorted(compareBy(AlphanumComparator()) { x -> x.name })
|
||||
|
||||
private fun findFirstImageEntry(): String? {
|
||||
val filter = ImageFileFilter()
|
||||
|
||||
Reference in New Issue
Block a user