diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 00000000..59037bbf
--- /dev/null
+++ b/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 4222e57a..9a15bce4 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -6,6 +6,7 @@
+
diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt
index 895a947e..4c701164 100644
--- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt
+++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt
@@ -84,7 +84,6 @@ lateinit var webView: WebView
val _webViewFlow = MutableSharedFlow>()
val webViewFlow = _webViewFlow.asSharedFlow()
var webViewReady = false
-var webViewFailed = false
var oldWebView = false
private var reloadJob: Job? = null
@@ -93,7 +92,6 @@ fun reloadWebView() {
reloadJob = CoroutineScope(Dispatchers.IO).launch {
webViewReady = false
- webViewFailed = false
oldWebView = false
evaluationContext.cancelChildren(CancellationException("reload"))
@@ -105,8 +103,6 @@ fun reloadWebView() {
else
"https://tom5079.github.io/PupilSources/hitomi.html"
).readText()
- }.onFailure {
- webViewFailed = true
}.getOrNull()?.let { html ->
launch(Dispatchers.Main) {
webView.loadDataWithBaseURL(
@@ -125,7 +121,7 @@ private var htmlVersion: String = ""
fun reloadWhenFailedOrUpdate() = CoroutineScope(Dispatchers.Default).launch {
while (true) {
if (
- (webViewFailed && !oldWebView) ||
+ (!webViewReady && !oldWebView) ||
runCatching {
URL(
if (BuildConfig.DEBUG)
@@ -142,7 +138,7 @@ fun reloadWhenFailedOrUpdate() = CoroutineScope(Dispatchers.Default).launch {
reloadWebView()
}
- delay(if (webViewReady && !webViewFailed) 10000 else 1000)
+ delay(if (webViewReady) 10000 else 1000)
}
}
@@ -160,12 +156,11 @@ fun initWebView(context: Context) {
webViewClient = object: WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
- webView.evaluateJavascript("self_test()") {
+ webView.evaluateJavascript("try { self_test() } catch (err) { 'err' }") {
val result: String = Json.decodeFromString(it)
oldWebView = result == "es2020_unsupported";
webViewReady = result == "OK";
- webViewFailed = result != "OK";
}
}
diff --git a/app/src/main/java/xyz/quaver/pupil/hitomi/common.kt b/app/src/main/java/xyz/quaver/pupil/hitomi/common.kt
index f78007bb..0c1e79f9 100644
--- a/app/src/main/java/xyz/quaver/pupil/hitomi/common.kt
+++ b/app/src/main/java/xyz/quaver/pupil/hitomi/common.kt
@@ -48,12 +48,12 @@ val json = Json {
useArrayPolymorphism = true
}
-suspend inline fun WebView.evaluate(script: String): T = coroutineScope {
+suspend inline fun WebView.evaluate(script: String): T = coroutineScope { withTimeout(60000) {
var result: String? = null
while (result == null) {
try {
- while (!oldWebView && !(webViewReady && !webViewFailed)) delay(100)
+ while (!oldWebView && !webViewReady) delay(1000)
result = if (oldWebView)
"null"
@@ -71,18 +71,18 @@ suspend inline fun WebView.evaluate(script: String): T = coroutineSc
}
json.decodeFromString(result)
-}
+} }
@OptIn(ExperimentalCoroutinesApi::class)
suspend inline fun WebView.evaluatePromise(
script: String,
then: String = ".then(result => Callback.onResult(%uid, JSON.stringify(result))).catch(err => Callback.onError(%uid, String.raw`$script`, err.message, err.stack))"
-): T = coroutineScope {
+): T = coroutineScope { withTimeout(60000) {
var result: String? = null
while (result == null) {
try {
- while (!oldWebView && !(webViewReady && !webViewFailed)) delay(100)
+ while (!oldWebView && !webViewReady) delay(1000)
result = if (oldWebView)
"null"
@@ -108,7 +108,7 @@ suspend inline fun WebView.evaluatePromise(
}
json.decodeFromString(result)
-}
+} }
@Suppress("EXPERIMENTAL_API_USAGE")
suspend fun getGalleryInfo(galleryID: Int): GalleryInfo =
diff --git a/app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt b/app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt
index c0cad248..1263b54b 100644
--- a/app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt
+++ b/app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt
@@ -329,9 +329,8 @@ class DownloadService : Service() {
}
if (isCompleted(galleryID)) {
- if (DownloadManager.getInstance(this@DownloadService)
- .getDownloadFolder(galleryID) != null )
- Cache.getInstance(this@DownloadService, galleryID).moveToDownload()
+ DownloadManager.getInstance(this@DownloadService).addDownloadFolder(galleryID)
+ Cache.getInstance(this@DownloadService, galleryID).moveToDownload()
notificationManager.cancel(galleryID)
startId?.let { stopSelf(it) }
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageStorageFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageStorageFragment.kt
index fbaca241..d1a69da9 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageStorageFragment.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageStorageFragment.kt
@@ -18,22 +18,37 @@
package xyz.quaver.pupil.ui.fragment
+import android.graphics.ColorFilter
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
import android.os.Bundle
+import android.widget.Toast
import androidx.appcompat.app.AlertDialog
+import androidx.core.content.ContextCompat
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
+import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
import xyz.quaver.io.FileX
+import xyz.quaver.io.SAFileX
import xyz.quaver.io.util.deleteRecursively
+import xyz.quaver.io.util.getChild
+import xyz.quaver.io.util.readText
+import xyz.quaver.io.util.writeText
import xyz.quaver.pupil.R
import xyz.quaver.pupil.histories
import xyz.quaver.pupil.util.byteToString
import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.downloader.DownloadManager
+import xyz.quaver.pupil.util.downloader.Metadata
import java.io.File
+import kotlin.math.roundToInt
class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener {
@@ -80,6 +95,46 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show()
}
+ "recover_downloads" -> {
+ val density = context.resources.displayMetrics.density
+ this.icon = object: CircularProgressDrawable(context) {
+ override fun getIntrinsicHeight() = (24*density).roundToInt()
+ override fun getIntrinsicWidth() = (24*density).roundToInt()
+ }.apply {
+ setStyle(CircularProgressDrawable.DEFAULT)
+ colorFilter = PorterDuffColorFilter(ContextCompat.getColor(context, R.color.colorAccent), PorterDuff.Mode.SRC_IN)
+ start()
+ }
+
+ val downloadManager = DownloadManager.getInstance(context)
+
+ val downloadFolderMap = downloadManager.downloadFolderMap
+
+ downloadFolderMap.clear()
+
+ downloadManager.downloadFolder.listFiles { file -> file.isDirectory }?.forEach { folder ->
+ val metadataFile = FileX(context, folder, ".metadata")
+
+ if (!metadataFile.exists()) return@forEach
+
+ val metadata = metadataFile.readText()?.let {
+ Json.decodeFromString(it)
+ } ?: return@forEach
+
+ val galleryID = metadata.galleryBlock?.id ?: metadata.galleryInfo?.id ?: return@forEach
+
+ downloadFolderMap[galleryID] = folder.name
+ }
+
+ downloadManager.downloadFolderMap.putAll(downloadFolderMap)
+ val downloads = FileX(context, downloadManager.downloadFolder, ".download")
+
+ if (!downloads.exists()) downloads.createNewFile()
+ downloads.writeText(Json.encodeToString(downloadFolderMap))
+
+ this.icon = null
+ Toast.makeText(context, android.R.string.ok, Toast.LENGTH_SHORT).show()
+ }
"delete_downloads" -> {
val dir = DownloadManager.getInstance(context).downloadFolder
@@ -191,6 +246,12 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
onPreferenceClickListener = this@ManageStorageFragment
}
+
+ with(findPreference("recover_downloads")) {
+ this ?: return@with
+
+ onPreferenceClickListener = this@ManageStorageFragment
+ }
}
override fun onDestroy() {
diff --git a/app/src/main/java/xyz/quaver/pupil/util/downloader/Cache.kt b/app/src/main/java/xyz/quaver/pupil/util/downloader/Cache.kt
index 72ff0ee8..6dac5f15 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/downloader/Cache.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/downloader/Cache.kt
@@ -21,7 +21,6 @@ package xyz.quaver.pupil.util.downloader
import android.content.Context
import android.content.ContextWrapper
import android.net.Uri
-import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -40,7 +39,6 @@ import xyz.quaver.pupil.hitomi.GalleryBlock
import xyz.quaver.pupil.hitomi.GalleryInfo
import xyz.quaver.pupil.hitomi.getGalleryBlock
import xyz.quaver.pupil.hitomi.getGalleryInfo
-import xyz.quaver.pupil.userAgent
import java.io.File
import java.io.IOException
import java.io.InputStream
@@ -231,6 +229,9 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
return@launch
(lock[galleryID] ?: Mutex().also { lock[galleryID] = it }).withLock {
+ if (downloadFolder.exists()) downloadFolder.deleteRecursively()
+ downloadFolder.mkdir()
+
val cacheMetadata = cacheFolder.getChild(".metadata")
val downloadMetadata = downloadFolder.getChild(".metadata")
diff --git a/app/src/main/java/xyz/quaver/pupil/util/downloader/DownloadManager.kt b/app/src/main/java/xyz/quaver/pupil/util/downloader/DownloadManager.kt
index df559d66..ed8f088f 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/downloader/DownloadManager.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/downloader/DownloadManager.kt
@@ -48,14 +48,12 @@ class DownloadManager private constructor(context: Context) : ContextWrapper(con
val defaultDownloadFolder = FileX(this, getExternalFilesDir(null)!!)
val downloadFolder: FileX
- get() = {
- kotlin.runCatching {
- FileX(this, Preferences.get("download_folder"))
- }.getOrElse {
- Preferences["download_folder"] = defaultDownloadFolder.uri.toString()
- defaultDownloadFolder
- }
- }.invoke()
+ get() = kotlin.runCatching {
+ FileX(this, Preferences.get("download_folder"))
+ }.getOrElse {
+ Preferences["download_folder"] = defaultDownloadFolder.uri.toString()
+ defaultDownloadFolder
+ }
private var prevDownloadFolder: FileX? = null
private var downloadFolderMapInstance: MutableMap? = null
@@ -64,21 +62,19 @@ class DownloadManager private constructor(context: Context) : ContextWrapper(con
get() {
if (prevDownloadFolder != downloadFolder) {
prevDownloadFolder = downloadFolder
- downloadFolderMapInstance = {
+ downloadFolderMapInstance = run {
val file = downloadFolder.getChild(".download")
-
val data = if (file.exists())
kotlin.runCatching {
- file.readText()?.let { Json.decodeFromString>(it) }
+ file.readText()?.let{ Json.decodeFromString>(it) }
}.onFailure { file.delete() }.getOrNull()
else
null
-
- data ?: {
+ data ?: run {
file.createNewFile()
- mutableMapOf()
- }.invoke()
- }.invoke()
+ mutableMapOf()
+ }
+ }
}
return downloadFolderMapInstance ?: mutableMapOf()
@@ -103,14 +99,13 @@ class DownloadManager private constructor(context: Context) : ContextWrapper(con
val folder = downloadFolder.getChild(name)
- if (folder.exists()) return@launch
-
- folder.mkdir()
-
- downloadFolderMap[galleryID] = folder.name
+ downloadFolderMap[galleryID] = name
downloadFolder.getChild(".download").let { if (!it.exists()) it.createNewFile() }
downloadFolder.getChild(".download").writeText(Json.encodeToString(downloadFolderMap))
+
+ if (folder.exists()) return@launch
+ folder.mkdir()
}
@Synchronized
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index db2ebac9..8008573f 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -158,4 +158,5 @@
アンドロイド11以上では外部からのアプリ内部空間接近が不可能です。ダウンロードフォルダを変更しますか?
ネットワーク
WebViewのアップデートが必要です
+ ダウンロードデータベースを再構築
\ No newline at end of file
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 3a00fd9a..9d4ebb0c 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -158,4 +158,5 @@
안드로이드 11 이상에서는 외부에서 현재 다운로드 폴더에 접근할 수 없습니다. 변경하시겠습니까?
네트워크
WebView 업데이트가 필요합니다
+ 다운로드 데이터베이스 복구
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index af69a1db..8fa0a73d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -152,6 +152,7 @@
Calculating storage usage…
Clear cache
Deleting cache can affect image loading speed. Do you want to continue?
+ Reconstruct download database
Clear downloads
Delete all downloaded galleries.\nDo you want to continue?
Clear history
diff --git a/app/src/main/res/xml/manage_storage_preferences.xml b/app/src/main/res/xml/manage_storage_preferences.xml
index f6413aaa..45c2feda 100644
--- a/app/src/main/res/xml/manage_storage_preferences.xml
+++ b/app/src/main/res/xml/manage_storage_preferences.xml
@@ -27,6 +27,11 @@
app:key="delete_downloads"
app:title="@string/settings_clear_downloads"/>
+
+