diff --git a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt index 0d1f733e9..827e54957 100644 --- a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt +++ b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt @@ -38,6 +38,7 @@ import com.hippo.ehviewer.yorozuya.ObjectUtils import com.hippo.ehviewer.yorozuya.SimpleHandler import com.hippo.ehviewer.yorozuya.collect.LongList import com.hippo.unifile.UniFile +import eu.kanade.tachiyomi.util.lang.withUIContext import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import splitties.init.appCtx @@ -395,7 +396,7 @@ object DownloadManager : OnSpiderListener { } } - fun addDownload(galleryInfo: GalleryInfo, label: String?) { + suspend fun addDownload(galleryInfo: GalleryInfo, label: String?) { if (containDownloadInfo(galleryInfo.gid)) { // Contain return @@ -423,8 +424,10 @@ object DownloadManager : OnSpiderListener { EhDB.putDownloadInfo(info) // Notify - for (l in mDownloadInfoListeners) { - l!!.onAdd(info, list, list.size - 1) + withUIContext { + for (l in mDownloadInfoListeners) { + l!!.onAdd(info, list, list.size - 1) + } } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt b/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt index 3f621cfb7..77cd23a3d 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt @@ -65,6 +65,7 @@ fun AdvancedScreen() { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() val snackbarHostState = remember { SnackbarHostState() } val coroutineScope = rememberCoroutineScope { Dispatchers.IO } + fun launchSnackBar(content: String) = coroutineScope.launch { snackbarHostState.showSnackbar(content) } Scaffold( topBar = { TopAppBar( @@ -111,10 +112,10 @@ fun AdvancedScreen() { zipOs.putNextEntry(logcatEntry) LogCat.save(zipOs) } - snackbarHostState.showSnackbar(getString(R.string.settings_advanced_dump_logcat_to, uri.toString())) + launchSnackBar(getString(R.string.settings_advanced_dump_logcat_to, uri.toString())) } }.onFailure { - snackbarHostState.showSnackbar(dumpLogError) + launchSnackBar(dumpLogError) it.printStackTrace() } } @@ -179,18 +180,16 @@ fun AdvancedScreen() { val exportLauncher = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/vnd.sqlite3")) { uri -> uri?.let { coroutineScope.launch { + val alertDialog = withUIContext { BaseDialogBuilder(context).setCancelable(false).setView(R.layout.preference_dialog_task).show() } context.runCatching { grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - val alertDialog = withUIContext { - BaseDialogBuilder(context).setCancelable(false).setView(R.layout.preference_dialog_task).show() - } EhDB.exportDB(context, uri) - if (alertDialog.isShowing) alertDialog.dismiss() - snackbarHostState.showSnackbar(getString(R.string.settings_advanced_export_data_to, uri.toString())) + launchSnackBar(getString(R.string.settings_advanced_export_data_to, uri.toString())) }.onFailure { it.printStackTrace() - snackbarHostState.showSnackbar(exportFailed) + launchSnackBar(exportFailed) } + if (alertDialog.isShowing) alertDialog.dismiss() } } } @@ -204,18 +203,16 @@ fun AdvancedScreen() { val importLauncher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri -> uri?.let { coroutineScope.launch { + val alertDialog = withUIContext { BaseDialogBuilder(context).setCancelable(false).setView(R.layout.preference_dialog_task).show() } context.runCatching { grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) - val alertDialog = withUIContext { - BaseDialogBuilder(context).setCancelable(false).setView(R.layout.preference_dialog_task).show() - } EhDB.importDB(context, uri) - if (alertDialog.isShowing) alertDialog.dismiss() - snackbarHostState.showSnackbar(importSucceed) + launchSnackBar(importSucceed) }.onFailure { it.printStackTrace() - snackbarHostState.showSnackbar(importFailed) + launchSnackBar(importFailed) } + if (alertDialog.isShowing) alertDialog.dismiss() } } } @@ -236,13 +233,13 @@ fun AdvancedScreen() { tailrec suspend fun doBackup() { val result = EhEngine.getFavorites(favListUrlBuilder.build()) if (result.galleryInfoList.isEmpty()) { - snackbarHostState.showSnackbar(backupNothing) + launchSnackBar(backupNothing) } else { if (favTotal == 0) favTotal = result.countArray.sum() favIndex += result.galleryInfoList.size val status = "($favIndex/$favTotal)" EhDB.putLocalFavorites(result.galleryInfoList) - snackbarHostState.showSnackbar(context.getString(R.string.settings_advanced_backup_favorite_start, status)) + launchSnackBar(context.getString(R.string.settings_advanced_backup_favorite_start, status)) if (result.next != null) { delay(Settings.downloadDelay.toLong()) favListUrlBuilder.setIndex(result.next, true) @@ -254,10 +251,10 @@ fun AdvancedScreen() { runSuspendCatching { doBackup() }.onSuccess { - snackbarHostState.showSnackbar(backupSucceed) + launchSnackBar(backupSucceed) }.onFailure { it.printStackTrace() - snackbarHostState.showSnackbar(backupFailed) + launchSnackBar(backupFailed) } } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/settings/DownloadScreen.kt b/app/src/main/java/com/hippo/ehviewer/ui/settings/DownloadScreen.kt index e8b164d02..2dc674d01 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/settings/DownloadScreen.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/settings/DownloadScreen.kt @@ -26,9 +26,16 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.hippo.ehviewer.AppConfig +import com.hippo.ehviewer.EhDB import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings +import com.hippo.ehviewer.client.EhEngine.fillGalleryListByApi +import com.hippo.ehviewer.client.EhUrl +import com.hippo.ehviewer.client.data.BaseGalleryInfo +import com.hippo.ehviewer.download.DownloadManager import com.hippo.ehviewer.download.downloadLocation +import com.hippo.ehviewer.spider.SpiderQueen +import com.hippo.ehviewer.spider.readCompatFromUniFile import com.hippo.ehviewer.ui.compose.observed import com.hippo.ehviewer.ui.compose.rememberedAccessor import com.hippo.ehviewer.ui.keepNoMediaFileStatus @@ -36,8 +43,11 @@ import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder import com.hippo.ehviewer.ui.login.LocalNavController import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.util.lang.launchNonCancellable +import eu.kanade.tachiyomi.util.lang.withUIContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import moe.tarsin.coroutines.runSuspendCatching +import splitties.init.appCtx @Composable fun DownloadScreen() { @@ -46,6 +56,7 @@ fun DownloadScreen() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope { Dispatchers.IO } val snackbarHostState = remember { SnackbarHostState() } + fun launchSnackBar(content: String) = coroutineScope.launch { snackbarHostState.showSnackbar(content) } Scaffold( topBar = { TopAppBar( @@ -59,8 +70,8 @@ fun DownloadScreen() { ) }, snackbarHost = { SnackbarHost(snackbarHostState) }, - ) { - Column(modifier = Modifier.padding(it).nestedScroll(scrollBehavior.nestedScrollConnection)) { + ) { padding -> + Column(modifier = Modifier.padding(padding).nestedScroll(scrollBehavior.nestedScrollConnection)) { var downloadLocationState by ::downloadLocation.observed val cannotGetDownloadLocation = stringResource(id = R.string.settings_download_cant_get_download_location) val selectDownloadDirLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocumentTree()) { treeUri -> @@ -75,7 +86,7 @@ fun DownloadScreen() { } } }.onFailure { - snackbarHostState.showSnackbar(cannotGetDownloadLocation) + launchSnackBar(cannotGetDownloadLocation) } } } @@ -96,7 +107,7 @@ fun DownloadScreen() { downloadLocationState = uniFile coroutineScope.launchNonCancellable { keepNoMediaFileStatus() } } else { - coroutineScope.launch { snackbarHostState.showSnackbar(cannotGetDownloadLocation) } + launchSnackBar(cannotGetDownloadLocation) } } .show() @@ -139,10 +150,63 @@ fun DownloadScreen() { summary = stringResource(id = R.string.settings_download_download_origin_image_summary), value = Settings::downloadOriginImage, ) + val restoreFailed = stringResource(id = R.string.settings_download_restore_failed) Preference( title = stringResource(id = R.string.settings_download_restore_download_items), summary = stringResource(id = R.string.settings_download_restore_download_items_summary), - ) + ) { + var restoreDirCount = 0 + fun getRestoreItem(file: UniFile): RestoreItem? { + if (!file.isDirectory) return null + val siFile = file.findFile(SpiderQueen.SPIDER_INFO_FILENAME) ?: return null + return runCatching { + val spiderInfo = readCompatFromUniFile(siFile) ?: return null + val gid = spiderInfo.gid + val dirname = file.name ?: return null + if (DownloadManager.containDownloadInfo(gid)) { + // Restore download dir to avoid redownload + val dbdirname = EhDB.getDownloadDirname(gid) + if (null == dbdirname || dirname != dbdirname) { + EhDB.putDownloadDirname(gid, dirname) + restoreDirCount++ + } + return null + } + RestoreItem(dirname).also { + it.gid = spiderInfo.gid + it.token = spiderInfo.token + } + }.onFailure { + it.printStackTrace() + }.getOrNull() + } + coroutineScope.launch { + val alertDialog = withUIContext { BaseDialogBuilder(context).setCancelable(false).setView(R.layout.preference_dialog_task).show() } + context.runCatching { + val result = downloadLocation.listFiles()?.mapNotNull { getRestoreItem(it) }?.apply { + runSuspendCatching { + fillGalleryListByApi(this, EhUrl.referer) + }.onFailure { + it.printStackTrace() + } + } + if (result == null) { + launchSnackBar(restoreFailed) + } else { + if (result.isEmpty()) { + launchSnackBar(RESTORE_COUNT_MSG(restoreDirCount)) + } else { + val count = result.filterNot { it.title.isNullOrBlank() }.map { + DownloadManager.addDownload(it, null) + EhDB.putDownloadDirname(it.gid, it.dirname) + }.size + launchSnackBar(RESTORE_COUNT_MSG(count + restoreDirCount)) + } + } + } + if (alertDialog.isShowing) alertDialog.dismiss() + } + } Preference( title = stringResource(id = R.string.settings_download_clean_redundancy), summary = stringResource(id = R.string.settings_download_clean_redundancy_summary), @@ -150,3 +214,7 @@ fun DownloadScreen() { } } } + +private class RestoreItem(val dirname: String) : BaseGalleryInfo() +private val RESTORE_NOT_FOUND = appCtx.getString(R.string.settings_download_restore_not_found) +private val RESTORE_COUNT_MSG = { cnt: Int -> if (cnt == 0) RESTORE_NOT_FOUND else appCtx.getString(R.string.settings_download_restore_successfully, cnt) }