diff --git a/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt b/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt index 872c47e68..aa555f6e9 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt @@ -25,6 +25,8 @@ import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.runtime.toMutableStateList import com.google.android.material.math.MathUtils import com.hippo.ehviewer.EhDB import com.hippo.ehviewer.Settings @@ -46,12 +48,9 @@ import com.hippo.ehviewer.util.runAssertingNotMainThread import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.withIOContext -import java.util.LinkedList import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.transform import kotlinx.coroutines.launch import splitties.init.appCtx @@ -59,24 +58,22 @@ import splitties.preferences.edit object DownloadManager : OnSpiderListener { // All download info list - val allInfoList: LinkedList = runAssertingNotMainThread { LinkedList(EhDB.getAllDownloadInfo()) } + val allInfoList = runAssertingNotMainThread { EhDB.getAllDownloadInfo() }.toMutableStateList() // All download info map private val mAllInfoMap = allInfoList.associateBy { it.gid } as MutableMap // label and info list map, without default label info list - private val map: MutableMap> + private val map: MutableMap> // All labels without default label - val labelList = mutableStateListOf().apply { - addAll(runAssertingNotMainThread { EhDB.getAllDownloadLabelList() }) - } + val labelList = runAssertingNotMainThread { EhDB.getAllDownloadLabelList() }.toMutableStateList() // Store download info with default label - val defaultInfoList: LinkedList + private val defaultInfoList: SnapshotStateList // Store download info wait to start - private val mWaitList = LinkedList() + private val mWaitList = ArrayDeque() private val mSpeedReminder = SpeedReminder() private val mNotifyTaskPool = ConcurrentPool(5) private var mDownloadListener: DownloadListener? = null @@ -96,16 +93,16 @@ object DownloadManager : OnSpiderListener { labelList.add(EhDB.addDownloadLabel(DownloadLabel(label, labelList.size))) } } - it.key to LinkedList(it.value) + it.key to it.value.toMutableStateList() } - } as MutableMap> - defaultInfoList = map.remove(null) ?: LinkedList() + } as MutableMap> + defaultInfoList = map.remove(null) ?: mutableStateListOf() labelList.forEach { (label) -> - map[label] ?: LinkedList().also { map[label] = it } + map[label] ?: mutableStateListOf().also { map[label] = it } } } - private fun getInfoListForLabel(label: String?) = if (label == null) { + fun getInfoListForLabel(label: String?) = if (label == null) { defaultInfoList } else { map[label] @@ -125,8 +122,6 @@ object DownloadManager : OnSpiderListener { fun containDownloadInfo(gid: Long) = mAllInfoMap.containsKey(gid) - fun getLabelDownloadInfoList(label: String?) = map[label] - fun getDownloadInfo(gid: Long) = mAllInfoMap[gid] fun getDownloadState(gid: Long): Int { @@ -205,10 +200,10 @@ object DownloadManager : OnSpiderListener { Log.e(TAG, "Can't find download info list with label: $label") return } - list.addFirst(info) + list.add(0, info) // Add to all download list and map - allInfoList.addFirst(info) + allInfoList.add(0, info) mAllInfoMap[galleryInfo.gid] = info // Add to wait list @@ -311,7 +306,7 @@ object DownloadManager : OnSpiderListener { suspend fun addDownloadLabel(downloadLabelList: List) { val offset = downloadLabelList.size downloadLabelList.filterNot { containLabel(it.label) }.forEachIndexed { index, label -> - map[label.label] = LinkedList() + map[label.label] = mutableStateListOf() label.position = index + offset labelList.add(EhDB.addDownloadLabel(label)) } @@ -324,10 +319,10 @@ object DownloadManager : OnSpiderListener { // Add to default label download list val list = defaultInfoList - list.addFirst(info) + list.add(0, info) // Add to all download list and map - allInfoList.addFirst(info) + allInfoList.add(0, info) mAllInfoMap[galleryInfo.gid] = info // Save to @@ -568,7 +563,7 @@ object DownloadManager : OnSpiderListener { Log.e(TAG, "Not exits label: $label") return } - val dstList: MutableList? = getInfoListForLabel(label) + val dstList = getInfoListForLabel(label) if (dstList == null) { Log.e(TAG, "Can't find label with label: $label") return @@ -577,7 +572,7 @@ object DownloadManager : OnSpiderListener { if (info.label == label) { continue } - val srcList: MutableList? = getInfoListForLabel(info.label) + val srcList = getInfoListForLabel(info.label) if (srcList == null) { Log.e(TAG, "Can't find label with label: " + info.label) continue @@ -597,7 +592,7 @@ object DownloadManager : OnSpiderListener { return } labelList.add(EhDB.addDownloadLabel(DownloadLabel(label, labelList.size))) - map[label] = LinkedList() + map[label] = mutableStateListOf() } suspend fun moveLabel(fromPosition: Int, toPosition: Int) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/CommonOperations.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/CommonOperations.kt index fc3c8b433..026c9a198 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/CommonOperations.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/CommonOperations.kt @@ -414,12 +414,13 @@ suspend fun DialogState.showMoveDownloadLabel(info: GalleryInfo) { withUIContext { DownloadManager.changeLabel(listOf(downloadInfo), label) } } -suspend fun DialogState.showMoveDownloadLabelList(list: Collection) { +suspend fun DialogState.showMoveDownloadLabelList(list: Collection): String? { val defaultLabel = appCtx.getString(R.string.default_download_label_name) val labels = DownloadManager.labelList.map { it.label }.toTypedArray() val selected = showSelectItem(defaultLabel, *labels, title = R.string.download_move_dialog_title) val label = if (selected == 0) null else labels[selected - 1] withUIContext { DownloadManager.changeLabel(list, label) } + return label } suspend fun DialogState.showDatePicker(): String? { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt index e3a3cdcc7..c8ec0d503 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt @@ -46,6 +46,7 @@ import androidx.compose.material3.rememberDismissState import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -90,6 +91,7 @@ import com.hippo.ehviewer.ui.tools.FastScrollLazyColumn import com.hippo.ehviewer.ui.tools.LocalDialogState import com.hippo.ehviewer.ui.tools.SwipeToDismissBox2 import com.hippo.ehviewer.ui.tools.draggingHapticFeedback +import com.hippo.ehviewer.ui.tools.observed import com.hippo.ehviewer.util.containsIgnoreCase import com.hippo.ehviewer.util.findActivity import com.hippo.ehviewer.util.mapToLongArray @@ -107,7 +109,7 @@ import sh.calvin.reorderable.rememberReorderableLazyColumnState @Destination @Composable fun DownloadsScreen(navigator: DestinationsNavigator) { - var label by rememberSaveable { mutableStateOf(null) } + var label by Settings::recentDownloadLabel.observed var keyword by rememberSaveable { mutableStateOf(null) } var filterType by rememberSaveable { mutableStateOf(-1) } var searchBarOffsetY by remember { mutableStateOf(0) } @@ -125,17 +127,16 @@ fun DownloadsScreen(navigator: DestinationsNavigator) { val density = LocalDensity.current val dialogState = LocalDialogState.current val view = LocalView.current - val title = stringResource(R.string.scene_download_title, label ?: stringResource(R.string.download_all)) - val hint = stringResource(R.string.search_bar_hint, title) - val defaultName = stringResource(R.string.default_download_label_name) val allName = stringResource(R.string.download_all) + val defaultName = stringResource(R.string.default_download_label_name) + val title = stringResource(R.string.scene_download_title, if (label == "") allName else label ?: defaultName) + val hint = stringResource(R.string.search_bar_hint, title) val list = remember(label, filterType, keyword) { - when (label) { - null -> DownloadManager.allInfoList - defaultName -> DownloadManager.defaultInfoList - else -> DownloadManager.getLabelDownloadInfoList(label) ?: DownloadManager.allInfoList.also { label = null } - }.filter { info -> - (filterType == -1 || info.state == filterType) && keyword?.let { info.title.containsIgnoreCase(it) || info.titleJpn.containsIgnoreCase(it) || info.uploader.containsIgnoreCase(it) } ?: true + val raw = DownloadManager.getInfoListForLabel(label) ?: DownloadManager.allInfoList + raw.filterTo(mutableStateListOf()) { info -> + (filterType == -1 || info.state == filterType) && keyword?.let { + info.title.containsIgnoreCase(it) || info.titleJpn.containsIgnoreCase(it) || info.uploader.containsIgnoreCase(it) + } ?: true } } val labelsList = DownloadManager.labelList @@ -179,7 +180,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) { } onSelect(defaultName) { Settings.hasDefaultDownloadLabel = true - Settings.defaultDownloadLabel = defaultName + Settings.defaultDownloadLabel = null } DownloadManager.labelList.forEach { (label) -> onSelect(label) { @@ -213,7 +214,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) { val all = DownloadManager.allInfoList ListItem( modifier = Modifier.clickable { - label = null + label = "" closeSheet() }, tonalElevation = 1.dp, @@ -224,10 +225,10 @@ fun DownloadsScreen(navigator: DestinationsNavigator) { ) } stickyHeader { - val default = DownloadManager.defaultInfoList + val default = DownloadManager.getInfoListForLabel(null)!! ListItem( modifier = Modifier.clickable { - label = defaultName + label = null closeSheet() }, tonalElevation = 1.dp, @@ -243,6 +244,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) { if (it == DismissValue.DismissedToStart) { coroutineScope.launch { DownloadManager.deleteLabel(item) + label = "" } } true @@ -262,7 +264,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) { }, label = "elevation", ) - val thatList = DownloadManager.getLabelDownloadInfoList(item) + val thatList = DownloadManager.getInfoListForLabel(item) val text = if (thatList != null) "$item [${thatList.size}]" else item ListItem( modifier = Modifier.clickable { @@ -288,6 +290,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) { } } DownloadManager.renameLabel(item, new) + label = new } }, ) { @@ -402,6 +405,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) { coroutineScope.launchIO { EhDB.updateDownloadInfo(newList) } + list.add(toIndex, list.removeAt(fromIndex)) view.performHapticFeedback(draggingHapticFeedback) } val searchBarConnection = remember { @@ -513,10 +517,14 @@ fun DownloadsScreen(navigator: DestinationsNavigator) { onClick(Icons.Default.Delete) { val infoList = checkedInfoMap.run { toMap().values.also { clear() } } dialogState.confirmRemoveDownloadRange(infoList) + list.removeAll(infoList) } onClick(Icons.AutoMirrored.Default.DriveFileMove) { val infoList = checkedInfoMap.run { toMap().values.also { clear() } } - dialogState.showMoveDownloadLabelList(infoList) + val toLabel = dialogState.showMoveDownloadLabelList(infoList) + if (label != "" && label != toLabel) { + list.removeAll(infoList) + } } } }