Fix StrictMode errors
This commit is contained in:
@@ -25,51 +25,50 @@ class KotatsuApp : BaseApp() {
|
||||
null
|
||||
}
|
||||
StrictMode.setThreadPolicy(
|
||||
StrictMode.ThreadPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.run {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && notifier != null) {
|
||||
penaltyListener(notifier.executor, notifier)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}.build(),
|
||||
StrictMode.ThreadPolicy.Builder().apply {
|
||||
detectNetwork()
|
||||
detectDiskWrites()
|
||||
detectCustomSlowCalls()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) detectUnbufferedIo()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) detectResourceMismatches()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) detectExplicitGc()
|
||||
penaltyLog()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && notifier != null) {
|
||||
penaltyListener(notifier.executor, notifier)
|
||||
}
|
||||
}.build(),
|
||||
)
|
||||
StrictMode.setVmPolicy(
|
||||
StrictMode.VmPolicy.Builder()
|
||||
.detectActivityLeaks()
|
||||
.detectLeakedSqlLiteObjects()
|
||||
.detectLeakedClosableObjects()
|
||||
.detectLeakedRegistrationObjects()
|
||||
.setClassInstanceLimit(LocalMangaRepository::class.java, 1)
|
||||
.setClassInstanceLimit(PagesCache::class.java, 1)
|
||||
.setClassInstanceLimit(MangaLoaderContext::class.java, 1)
|
||||
.setClassInstanceLimit(PageLoader::class.java, 1)
|
||||
.setClassInstanceLimit(ReaderViewModel::class.java, 1)
|
||||
.penaltyLog()
|
||||
.run {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && notifier != null) {
|
||||
penaltyListener(notifier.executor, notifier)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}.build(),
|
||||
)
|
||||
FragmentStrictMode.defaultPolicy = FragmentStrictMode.Policy.Builder()
|
||||
.penaltyDeath()
|
||||
.detectFragmentReuse()
|
||||
.detectWrongFragmentContainer()
|
||||
.detectRetainInstanceUsage()
|
||||
.detectSetUserVisibleHint()
|
||||
.detectFragmentTagUsage()
|
||||
.penaltyLog()
|
||||
.run {
|
||||
if (notifier != null) {
|
||||
penaltyListener(notifier)
|
||||
} else {
|
||||
this
|
||||
StrictMode.VmPolicy.Builder().apply {
|
||||
detectActivityLeaks()
|
||||
detectLeakedSqlLiteObjects()
|
||||
detectLeakedClosableObjects()
|
||||
detectLeakedRegistrationObjects()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) detectContentUriWithoutPermission()
|
||||
detectFileUriExposure()
|
||||
setClassInstanceLimit(LocalMangaRepository::class.java, 1)
|
||||
setClassInstanceLimit(PagesCache::class.java, 1)
|
||||
setClassInstanceLimit(MangaLoaderContext::class.java, 1)
|
||||
setClassInstanceLimit(PageLoader::class.java, 1)
|
||||
setClassInstanceLimit(ReaderViewModel::class.java, 1)
|
||||
penaltyLog()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && notifier != null) {
|
||||
penaltyListener(notifier.executor, notifier)
|
||||
}
|
||||
}.build()
|
||||
)
|
||||
FragmentStrictMode.defaultPolicy = FragmentStrictMode.Policy.Builder().apply {
|
||||
detectWrongFragmentContainer()
|
||||
detectFragmentTagUsage()
|
||||
detectRetainInstanceUsage()
|
||||
detectSetUserVisibleHint()
|
||||
detectWrongNestedHierarchy()
|
||||
detectTargetFragmentUsage()
|
||||
detectFragmentReuse()
|
||||
penaltyLog()
|
||||
if (notifier != null) {
|
||||
penaltyListener(notifier)
|
||||
}
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
package org.koitharu.kotatsu.core.fs
|
||||
|
||||
import android.os.Build
|
||||
import org.koitharu.kotatsu.core.util.iterator.CloseableIterator
|
||||
import androidx.annotation.RequiresApi
|
||||
import org.koitharu.kotatsu.core.util.CloseableSequence
|
||||
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> {
|
||||
sealed interface FileSequence : CloseableSequence<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()
|
||||
}
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
class StreamImpl(dir: File) : FileSequence {
|
||||
|
||||
private val stream = Files.newDirectoryStream(dir.toPath())
|
||||
|
||||
override fun iterator(): Iterator<File> = MappingIterator(stream.iterator(), Path::toFile)
|
||||
|
||||
override fun close() = stream.close()
|
||||
}
|
||||
|
||||
class ListImpl(dir: File) : FileSequence {
|
||||
|
||||
private val list = dir.listFiles().orEmpty()
|
||||
|
||||
override fun iterator(): Iterator<File> = list.iterator()
|
||||
|
||||
override fun close() = Unit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package org.koitharu.kotatsu.core.util
|
||||
|
||||
interface CloseableSequence<T> : Sequence<T>, AutoCloseable
|
||||
@@ -15,7 +15,6 @@ import org.jetbrains.annotations.Blocking
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.fs.FileSequence
|
||||
import java.io.File
|
||||
import java.io.FileFilter
|
||||
import java.io.InputStream
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
import java.util.zip.ZipEntry
|
||||
@@ -87,9 +86,13 @@ suspend fun File.computeSize(): Long = runInterruptible(Dispatchers.IO) {
|
||||
walkCompat(includeDirectories = false).sumOf { it.length() }
|
||||
}
|
||||
|
||||
fun File.children() = FileSequence(this)
|
||||
inline fun <R> File.withChildren(block: (children: Sequence<File>) -> R): R = FileSequence(this).use(block)
|
||||
|
||||
fun Sequence<File>.filterWith(filter: FileFilter): Sequence<File> = filter { f -> filter.accept(f) }
|
||||
fun FileSequence(dir: File): FileSequence = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
FileSequence.StreamImpl(dir)
|
||||
} else {
|
||||
FileSequence.ListImpl(dir)
|
||||
}
|
||||
|
||||
val File.creationTime
|
||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +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 org.koitharu.kotatsu.core.util.ext.withChildren
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.util.zip.Deflater
|
||||
@@ -91,8 +91,10 @@ class ZipOutput(
|
||||
}
|
||||
putNextEntry(entry)
|
||||
closeEntry()
|
||||
fileToZip.children().forEach { childFile ->
|
||||
appendFile(childFile, "$name/${childFile.name}")
|
||||
fileToZip.withChildren { children ->
|
||||
children.forEach { childFile ->
|
||||
appendFile(childFile, "$name/${childFile.name}")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FileInputStream(fileToZip).use { fis ->
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.koitharu.kotatsu.local.data.isZipUri
|
||||
import org.koitharu.kotatsu.local.data.util.withExtraCloseable
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.parsers.util.mimeType
|
||||
import org.koitharu.kotatsu.parsers.util.requireBody
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import java.util.zip.ZipFile
|
||||
import javax.inject.Inject
|
||||
@@ -98,9 +99,7 @@ class MangaPageFetcher(
|
||||
if (!response.isSuccessful) {
|
||||
throw HttpException(response)
|
||||
}
|
||||
val body = checkNotNull(response.body) {
|
||||
"Null response"
|
||||
}
|
||||
val body = response.requireBody()
|
||||
val mimeType = response.mimeType
|
||||
val file = body.use {
|
||||
pagesCache.put(pageUrl, it.source())
|
||||
|
||||
@@ -81,6 +81,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import org.koitharu.kotatsu.parsers.util.requireBody
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import java.io.File
|
||||
@@ -359,7 +360,7 @@ class DownloadWorker @AssistedInject constructor(
|
||||
.use { response ->
|
||||
val file = File(destination, UUID.randomUUID().toString() + ".tmp")
|
||||
try {
|
||||
checkNotNull(response.body).use { body ->
|
||||
response.requireBody().use { body ->
|
||||
file.sink(append = false).buffer().use {
|
||||
it.writeAllCancellable(body.source())
|
||||
}
|
||||
|
||||
@@ -15,10 +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.AlphanumComparator
|
||||
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.core.util.ext.withChildren
|
||||
import org.koitharu.kotatsu.local.data.index.LocalMangaIndex
|
||||
import org.koitharu.kotatsu.local.data.input.LocalMangaInput
|
||||
import org.koitharu.kotatsu.local.data.output.LocalMangaOutput
|
||||
@@ -216,10 +215,15 @@ class LocalMangaRepository @Inject constructor(
|
||||
}
|
||||
val dirs = storageManager.getWriteableDirs()
|
||||
runInterruptible(Dispatchers.IO) {
|
||||
dirs.flatMap { dir ->
|
||||
dir.children().filterWith(TempFileFilter())
|
||||
}.forEach { file ->
|
||||
file.deleteRecursively()
|
||||
val filter = TempFileFilter()
|
||||
dirs.forEach { dir ->
|
||||
dir.withChildren { children ->
|
||||
children.forEach { child ->
|
||||
if (filter.accept(child)) {
|
||||
child.deleteRecursively()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -246,7 +250,7 @@ class LocalMangaRepository @Inject constructor(
|
||||
private suspend fun getAllFiles() = storageManager.getReadableDirs()
|
||||
.asSequence()
|
||||
.flatMap { dir ->
|
||||
dir.children().filterNot { it.isHidden }
|
||||
dir.withChildren { children -> children.filterNot { it.isHidden }.toList() }
|
||||
}
|
||||
|
||||
private fun Collection<LocalManga>.unwrap(): List<Manga> = map { it.manga }
|
||||
|
||||
@@ -6,11 +6,11 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import org.koitharu.kotatsu.core.model.LocalMangaSource
|
||||
import org.koitharu.kotatsu.core.util.AlphanumComparator
|
||||
import org.koitharu.kotatsu.core.util.ext.children
|
||||
import org.koitharu.kotatsu.core.util.ext.creationTime
|
||||
import org.koitharu.kotatsu.core.util.ext.longHashCode
|
||||
import org.koitharu.kotatsu.core.util.ext.toListSorted
|
||||
import org.koitharu.kotatsu.core.util.ext.walkCompat
|
||||
import org.koitharu.kotatsu.core.util.ext.withChildren
|
||||
import org.koitharu.kotatsu.local.data.MangaIndex
|
||||
import org.koitharu.kotatsu.local.data.hasCbzExtension
|
||||
import org.koitharu.kotatsu.local.data.hasImageExtension
|
||||
@@ -101,13 +101,14 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) {
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> = runInterruptible(Dispatchers.IO) {
|
||||
val file = chapter.url.toUri().toFile()
|
||||
if (file.isDirectory) {
|
||||
file.children()
|
||||
.filter { it.isFile && hasImageExtension(it) }
|
||||
.toListSorted(compareBy(AlphanumComparator()) { x -> x.name })
|
||||
.map {
|
||||
val pageUri = it.toUri().toString()
|
||||
MangaPage(pageUri.longHashCode(), pageUri, null, LocalMangaSource)
|
||||
}
|
||||
file.withChildren { children ->
|
||||
children
|
||||
.filter { it.isFile && hasImageExtension(it) }
|
||||
.toListSorted(compareBy(AlphanumComparator()) { x -> x.name })
|
||||
}.map {
|
||||
val pageUri = it.toUri().toString()
|
||||
MangaPage(pageUri.longHashCode(), pageUri, null, LocalMangaSource)
|
||||
}
|
||||
} else {
|
||||
ZipFile(file).use { zip ->
|
||||
zip.entries()
|
||||
@@ -153,6 +154,6 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) {
|
||||
}
|
||||
|
||||
private fun File.isChapterDirectory(): Boolean {
|
||||
return isDirectory && children().any { hasImageExtension(it) }
|
||||
return isDirectory && withChildren { children -> children.any { hasImageExtension(it) } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ class LocalMangaDirOutput(
|
||||
index.getChapterFileName(chapter.value.id)?.let {
|
||||
return it
|
||||
}
|
||||
val baseName = "${chapter.index}_${chapter.value.name.toFileNameSafe()}".take(18)
|
||||
val baseName = "${chapter.index}_${chapter.value.name.toFileNameSafe()}".take(32)
|
||||
var i = 0
|
||||
while (true) {
|
||||
val name = (if (i == 0) baseName else baseName + "_$i") + ".cbz"
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.koitharu.kotatsu.local.data.isFileUri
|
||||
import org.koitharu.kotatsu.local.data.isZipUri
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.requireBody
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import java.util.LinkedList
|
||||
@@ -233,8 +234,7 @@ class PageLoader @Inject constructor(
|
||||
else -> {
|
||||
val request = createPageRequest(pageUrl, page.source)
|
||||
imageProxyInterceptor.interceptPageRequest(request, okHttp).ensureSuccess().use { response ->
|
||||
val body = checkNotNull(response.body) { "Null response body" }
|
||||
body.withProgress(progress).use {
|
||||
response.requireBody().withProgress(progress).use {
|
||||
cache.put(pageUrl, it.source())
|
||||
}
|
||||
}.toUri()
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.koitharu.kotatsu.core.network.BaseHttpClient
|
||||
import org.koitharu.kotatsu.core.util.ext.toRequestBody
|
||||
import org.koitharu.kotatsu.parsers.util.await
|
||||
import org.koitharu.kotatsu.parsers.util.parseJson
|
||||
import org.koitharu.kotatsu.parsers.util.parseRaw
|
||||
import org.koitharu.kotatsu.parsers.util.removeSurrounding
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -30,7 +31,7 @@ class SyncAuthApi @Inject constructor(
|
||||
return response.parseJson().getString("token")
|
||||
} else {
|
||||
val code = response.code
|
||||
val message = response.use { checkNotNull(it.body).string() }.removeSurrounding('"')
|
||||
val message = response.parseRaw().removeSurrounding('"')
|
||||
throw SyncApiException(message, code)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user