|
|
|
@ -3,15 +3,21 @@ package org.koitharu.kotatsu.local.data
|
|
|
|
import android.content.Context
|
|
|
|
import android.content.Context
|
|
|
|
import com.tomclaw.cache.DiskLruCache
|
|
|
|
import com.tomclaw.cache.DiskLruCache
|
|
|
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
|
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
|
|
import java.io.File
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
import java.io.InputStream
|
|
|
|
import kotlinx.coroutines.Job
|
|
|
|
import javax.inject.Inject
|
|
|
|
import kotlinx.coroutines.currentCoroutineContext
|
|
|
|
import javax.inject.Singleton
|
|
|
|
import kotlinx.coroutines.ensureActive
|
|
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
|
|
|
|
|
|
import kotlinx.coroutines.withContext
|
|
|
|
import org.koitharu.kotatsu.utils.FileSize
|
|
|
|
import org.koitharu.kotatsu.utils.FileSize
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.utils.ext.copyToSuspending
|
|
|
|
import org.koitharu.kotatsu.utils.ext.longHashCode
|
|
|
|
import org.koitharu.kotatsu.utils.ext.longHashCode
|
|
|
|
import org.koitharu.kotatsu.utils.ext.subdir
|
|
|
|
import org.koitharu.kotatsu.utils.ext.subdir
|
|
|
|
import org.koitharu.kotatsu.utils.ext.takeIfReadable
|
|
|
|
import org.koitharu.kotatsu.utils.ext.takeIfReadable
|
|
|
|
|
|
|
|
import java.io.File
|
|
|
|
|
|
|
|
import java.io.InputStream
|
|
|
|
|
|
|
|
import javax.inject.Inject
|
|
|
|
|
|
|
|
import javax.inject.Singleton
|
|
|
|
|
|
|
|
|
|
|
|
@Singleton
|
|
|
|
@Singleton
|
|
|
|
class PagesCache @Inject constructor(@ApplicationContext context: Context) {
|
|
|
|
class PagesCache @Inject constructor(@ApplicationContext context: Context) {
|
|
|
|
@ -26,37 +32,44 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) {
|
|
|
|
return lruCache.get(url)?.takeIfReadable()
|
|
|
|
return lruCache.get(url)?.takeIfReadable()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fun put(url: String, inputStream: InputStream): File {
|
|
|
|
suspend fun put(url: String, inputStream: InputStream): File = withContext(Dispatchers.IO) {
|
|
|
|
val file = File(cacheDir, url.longHashCode().toString())
|
|
|
|
val file = File(cacheDir, url.longHashCode().toString())
|
|
|
|
file.outputStream().use { out ->
|
|
|
|
try {
|
|
|
|
inputStream.copyTo(out)
|
|
|
|
file.outputStream().use { out ->
|
|
|
|
|
|
|
|
inputStream.copyToSuspending(out)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
lruCache.put(url, file)
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
file.delete()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val res = lruCache.put(url, file)
|
|
|
|
|
|
|
|
file.delete()
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fun put(
|
|
|
|
suspend fun put(
|
|
|
|
url: String,
|
|
|
|
url: String,
|
|
|
|
inputStream: InputStream,
|
|
|
|
inputStream: InputStream,
|
|
|
|
contentLength: Long,
|
|
|
|
contentLength: Long,
|
|
|
|
progress: MutableStateFlow<Float>,
|
|
|
|
progress: MutableStateFlow<Float>,
|
|
|
|
): File {
|
|
|
|
): File = withContext(Dispatchers.IO) {
|
|
|
|
|
|
|
|
val job = currentCoroutineContext()[Job]
|
|
|
|
val file = File(cacheDir, url.longHashCode().toString())
|
|
|
|
val file = File(cacheDir, url.longHashCode().toString())
|
|
|
|
file.outputStream().use { out ->
|
|
|
|
try {
|
|
|
|
var bytesCopied: Long = 0
|
|
|
|
file.outputStream().use { out ->
|
|
|
|
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
|
|
|
var bytesCopied: Long = 0
|
|
|
|
var bytes = inputStream.read(buffer)
|
|
|
|
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
|
|
|
while (bytes >= 0) {
|
|
|
|
var bytes = inputStream.read(buffer)
|
|
|
|
out.write(buffer, 0, bytes)
|
|
|
|
while (bytes >= 0) {
|
|
|
|
bytesCopied += bytes
|
|
|
|
out.write(buffer, 0, bytes)
|
|
|
|
publishProgress(contentLength, bytesCopied, progress)
|
|
|
|
bytesCopied += bytes
|
|
|
|
bytes = inputStream.read(buffer)
|
|
|
|
job?.ensureActive()
|
|
|
|
|
|
|
|
publishProgress(contentLength, bytesCopied, progress)
|
|
|
|
|
|
|
|
bytes = inputStream.read(buffer)
|
|
|
|
|
|
|
|
job?.ensureActive()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lruCache.put(url, file)
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
file.delete()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val res = lruCache.put(url, file)
|
|
|
|
|
|
|
|
file.delete()
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun publishProgress(contentLength: Long, bytesCopied: Long, progress: MutableStateFlow<Float>) {
|
|
|
|
private fun publishProgress(contentLength: Long, bytesCopied: Long, progress: MutableStateFlow<Float>) {
|
|
|
|
|