Use DownloadManager for pages saving
parent
2ce5cb524f
commit
f22963b315
@ -0,0 +1,87 @@
|
|||||||
|
package org.koitharu.kotatsu.utils
|
||||||
|
|
||||||
|
import android.app.DownloadManager
|
||||||
|
import android.app.DownloadManager.Request.*
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import okhttp3.Cookie
|
||||||
|
import okhttp3.CookieJar
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaPage
|
||||||
|
import org.koitharu.kotatsu.core.network.CommonHeaders
|
||||||
|
import org.koitharu.kotatsu.utils.ext.toFileNameSafe
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
|
class DownloadManagerHelper(
|
||||||
|
private val context: Context,
|
||||||
|
private val cookieJar: CookieJar,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val manager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||||
|
private val subDir = context.getString(R.string.app_name).toFileNameSafe()
|
||||||
|
|
||||||
|
fun downloadPage(page: MangaPage, fullUrl: String): Long {
|
||||||
|
val uri = fullUrl.toUri()
|
||||||
|
val cookies = cookieJar.loadForRequest(fullUrl.toHttpUrl())
|
||||||
|
val dest = subDir + File.separator + uri.lastPathSegment
|
||||||
|
val request = DownloadManager.Request(uri)
|
||||||
|
.addRequestHeader(CommonHeaders.REFERER, page.referer)
|
||||||
|
.addRequestHeader(CommonHeaders.COOKIE, cookieHeader(cookies))
|
||||||
|
.setAllowedOverMetered(true)
|
||||||
|
.setAllowedNetworkTypes(NETWORK_WIFI or NETWORK_MOBILE)
|
||||||
|
.setNotificationVisibility(VISIBILITY_VISIBLE)
|
||||||
|
.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, dest)
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
request.allowScanningByMediaScanner()
|
||||||
|
}
|
||||||
|
return manager.enqueue(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun awaitDownload(id: Long): Uri {
|
||||||
|
getUriForDownloadedFile(id)?.let { return it } // fast path
|
||||||
|
suspendCancellableCoroutine<Unit> { cont ->
|
||||||
|
val receiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
|
if (
|
||||||
|
intent?.action == DownloadManager.ACTION_DOWNLOAD_COMPLETE &&
|
||||||
|
intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0L) == id
|
||||||
|
) {
|
||||||
|
context.unregisterReceiver(this)
|
||||||
|
cont.resume(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.registerReceiver(
|
||||||
|
receiver,
|
||||||
|
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
||||||
|
)
|
||||||
|
cont.invokeOnCancellation {
|
||||||
|
context.unregisterReceiver(receiver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkNotNull(getUriForDownloadedFile(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getUriForDownloadedFile(id: Long) = withContext(Dispatchers.IO) {
|
||||||
|
manager.getUriForDownloadedFile(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cookieHeader(cookies: List<Cookie>): String = buildString {
|
||||||
|
cookies.forEachIndexed { index, cookie ->
|
||||||
|
if (index > 0) append("; ")
|
||||||
|
append(cookie.name).append('=').append(cookie.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,66 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.utils
|
|
||||||
|
|
||||||
import android.content.ContentResolver
|
|
||||||
import android.content.ContentValues
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
|
||||||
import android.provider.MediaStore
|
|
||||||
import android.provider.OpenableColumns
|
|
||||||
import android.webkit.MimeTypeMap
|
|
||||||
import androidx.core.database.getStringOrNull
|
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
|
||||||
import java.io.OutputStream
|
|
||||||
|
|
||||||
class MediaStoreCompat(private val contentResolver: ContentResolver) {
|
|
||||||
|
|
||||||
fun insertImage(
|
|
||||||
fileName: String,
|
|
||||||
block: (OutputStream) -> Unit
|
|
||||||
): Uri? {
|
|
||||||
val name = fileName.substringBeforeLast('.')
|
|
||||||
val cv = ContentValues(7)
|
|
||||||
cv.put(MediaStore.Images.Media.DISPLAY_NAME, name)
|
|
||||||
cv.put(MediaStore.Images.Media.TITLE, name)
|
|
||||||
cv.put(
|
|
||||||
MediaStore.Images.Media.MIME_TYPE,
|
|
||||||
MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileName.substringAfterLast('.'))
|
|
||||||
)
|
|
||||||
cv.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1_000)
|
|
||||||
cv.put(MediaStore.Images.Media.DATE_MODIFIED, System.currentTimeMillis())
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
cv.put(MediaStore.Images.Media.IS_PENDING, 1)
|
|
||||||
}
|
|
||||||
var uri: Uri? = null
|
|
||||||
try {
|
|
||||||
uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cv)
|
|
||||||
contentResolver.openOutputStream(uri!!)?.use(block)
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
cv.clear()
|
|
||||||
cv.put(MediaStore.Images.Media.IS_PENDING, 0)
|
|
||||||
contentResolver.update(uri, cv, null, null)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
uri?.let {
|
|
||||||
contentResolver.delete(it, null, null)
|
|
||||||
}
|
|
||||||
uri = null
|
|
||||||
}
|
|
||||||
return uri
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getName(uri: Uri): String? =
|
|
||||||
(if (uri.scheme == "content") {
|
|
||||||
contentResolver.query(uri, null, null, null, null)?.use {
|
|
||||||
if (it.moveToFirst()) {
|
|
||||||
it.getStringOrNull(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}) ?: uri.path?.substringAfterLast('/')
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue