Save bookmark images

This commit is contained in:
Koitharu
2025-05-11 15:16:49 +03:00
parent 1bbe1204e6
commit 4ec9a91644
8 changed files with 80 additions and 6 deletions

View File

@@ -39,6 +39,7 @@ import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
import org.koitharu.kotatsu.list.ui.model.ListHeader import org.koitharu.kotatsu.list.ui.model.ListHeader
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.reader.ui.PageSaveHelper
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@@ -52,10 +53,19 @@ class AllBookmarksFragment :
@Inject @Inject
lateinit var settings: AppSettings lateinit var settings: AppSettings
@Inject
lateinit var pageSaveHelperFactory: PageSaveHelper.Factory
private lateinit var pageSaveHelper: PageSaveHelper
private val viewModel by viewModels<AllBookmarksViewModel>() private val viewModel by viewModels<AllBookmarksViewModel>()
private var bookmarksAdapter: BookmarksAdapter? = null private var bookmarksAdapter: BookmarksAdapter? = null
private var selectionController: ListSelectionController? = null private var selectionController: ListSelectionController? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
pageSaveHelper = pageSaveHelperFactory.create(this)
}
override fun onCreateViewBinding( override fun onCreateViewBinding(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@@ -179,6 +189,12 @@ class AllBookmarksFragment :
true true
} }
R.id.action_save -> {
viewModel.savePages(pageSaveHelper, selectionController?.snapshot() ?: return false)
mode?.finish()
true
}
else -> false else -> false
} }
} }

View File

@@ -22,6 +22,7 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorState import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.reader.ui.PageSaveHelper
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@@ -56,6 +57,23 @@ class AllBookmarksViewModel @Inject constructor(
} }
} }
fun savePages(pageSaveHelper: PageSaveHelper, ids: Set<Long>) {
launchLoadingJob(Dispatchers.Default) {
val tasks = content.value.mapNotNull {
if (it !is Bookmark || it.pageId !in ids) return@mapNotNull null
PageSaveHelper.Task(
manga = it.manga,
chapterId = it.chapterId,
pageNumber = it.page + 1,
page = it.toMangaPage(),
)
}
val dest = pageSaveHelper.save(tasks)
val msg = if (dest.size == 1) R.string.page_saved else R.string.pages_saved
onActionDone.call(ReversibleAction(msg, null))
}
}
private fun mapList(data: Map<Manga, List<Bookmark>>): List<ListModel> { private fun mapList(data: Map<Manga, List<Bookmark>>): List<ListModel> {
val result = ArrayList<ListModel>(data.values.sumOf { it.size + 1 }) val result = ArrayList<ListModel>(data.values.sumOf { it.size + 1 })
for ((manga, bookmarks) in data) { for ((manga, bookmarks) in data) {

View File

@@ -39,6 +39,7 @@ import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesViewModel
import org.koitharu.kotatsu.list.ui.GridSpanResolver import org.koitharu.kotatsu.list.ui.GridSpanResolver
import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.ListItemType
import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
import org.koitharu.kotatsu.reader.ui.PageSaveHelper
import org.koitharu.kotatsu.reader.ui.ReaderNavigationCallback import org.koitharu.kotatsu.reader.ui.ReaderNavigationCallback
import javax.inject.Inject import javax.inject.Inject
@@ -54,9 +55,13 @@ class BookmarksFragment : BaseFragment<FragmentMangaBookmarksBinding>(),
@Inject @Inject
lateinit var settings: AppSettings lateinit var settings: AppSettings
@Inject
lateinit var pageSaveHelperFactory: PageSaveHelper.Factory
override val recyclerView: RecyclerView? override val recyclerView: RecyclerView?
get() = viewBinding?.recyclerView get() = viewBinding?.recyclerView
private lateinit var pageSaveHelper: PageSaveHelper
private var bookmarksAdapter: BookmarksAdapter? = null private var bookmarksAdapter: BookmarksAdapter? = null
private var spanResolver: GridSpanResolver? = null private var spanResolver: GridSpanResolver? = null
private var selectionController: ListSelectionController? = null private var selectionController: ListSelectionController? = null
@@ -68,6 +73,7 @@ class BookmarksFragment : BaseFragment<FragmentMangaBookmarksBinding>(),
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
pageSaveHelper = pageSaveHelperFactory.create(this)
activityViewModel.mangaDetails.observe(this, viewModel) activityViewModel.mangaDetails.observe(this, viewModel)
} }
@@ -180,6 +186,12 @@ class BookmarksFragment : BaseFragment<FragmentMangaBookmarksBinding>(),
true true
} }
R.id.action_save -> {
viewModel.savePages(pageSaveHelper, selectionController?.snapshot() ?: return false)
mode?.finish()
true
}
else -> false else -> false
} }
} }

View File

@@ -21,12 +21,14 @@ import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.ui.util.ReversibleAction import org.koitharu.kotatsu.core.ui.util.ReversibleAction
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.ext.requireValue
import org.koitharu.kotatsu.details.data.MangaDetails import org.koitharu.kotatsu.details.data.MangaDetails
import org.koitharu.kotatsu.list.ui.model.EmptyState import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.ListHeader import org.koitharu.kotatsu.list.ui.model.ListHeader
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.reader.ui.PageSaveHelper
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@@ -62,6 +64,24 @@ class BookmarksViewModel @Inject constructor(
} }
} }
fun savePages(pageSaveHelper: PageSaveHelper, ids: Set<Long>) {
launchLoadingJob(Dispatchers.Default) {
val m = manga.requireValue()
val tasks = content.value.mapNotNull {
if (it !is Bookmark || it.pageId !in ids) return@mapNotNull null
PageSaveHelper.Task(
manga = m,
chapterId = it.chapterId,
pageNumber = it.page + 1,
page = it.toMangaPage(),
)
}
val dest = pageSaveHelper.save(tasks)
val msg = if (dest.size == 1) R.string.page_saved else R.string.pages_saved
onActionDone.call(ReversibleAction(msg, null))
}
}
private fun mapList(manga: Manga, bookmarks: List<Bookmark>): List<ListModel>? { private fun mapList(manga: Manga, bookmarks: List<Bookmark>): List<ListModel>? {
val chapters = manga.chapters ?: return null val chapters = manga.chapters ?: return null
val bookmarksMap = bookmarks.groupBy { it.chapterId } val bookmarksMap = bookmarks.groupBy { it.chapterId }

View File

@@ -89,7 +89,7 @@ class PagesViewModel @Inject constructor(
val tasks = pages.map { val tasks = pages.map {
PageSaveHelper.Task( PageSaveHelper.Task(
manga = manga, manga = manga,
chapter = manga.requireChapterById(it.chapterId), chapterId = it.chapterId,
pageNumber = it.index + 1, pageNumber = it.index + 1,
page = it.toMangaPage(), page = it.toMangaPage(),
) )

View File

@@ -178,15 +178,17 @@ class PageSaveHelper @AssistedInject constructor(
data class Task( data class Task(
val manga: Manga, val manga: Manga,
val chapter: MangaChapter, val chapterId: Long,
val pageNumber: Int, val pageNumber: Int,
val page: MangaPage, val page: MangaPage,
) { ) {
fun getFileBaseName() = buildString { fun getFileBaseName() = buildString {
append(manga.title.toFileNameSafe().take(MAX_BASENAME_LENGTH)) append(manga.title.toFileNameSafe().take(MAX_BASENAME_LENGTH))
append('-') manga.findChapterById(chapterId)?.let { chapter ->
append(chapter.number) append('-')
append(chapter.number)
}
append('-') append('-')
append(pageNumber) append(pageNumber)
append('_') append('_')

View File

@@ -257,7 +257,7 @@ class ReaderViewModel @Inject constructor(
val currentManga = manga.requireValue() val currentManga = manga.requireValue()
val task = PageSaveHelper.Task( val task = PageSaveHelper.Task(
manga = currentManga, manga = currentManga,
chapter = currentManga.requireChapterById(state.chapterId), chapterId = state.chapterId,
pageNumber = state.page + 1, pageNumber = state.page + 1,
page = checkNotNull(getCurrentPage()) { "Cannot find current page" }, page = checkNotNull(getCurrentPage()) { "Cannot find current page" },
) )

View File

@@ -9,4 +9,10 @@
android:title="@string/remove" android:title="@string/remove"
app:showAsAction="ifRoom|withText" /> app:showAsAction="ifRoom|withText" />
</menu> <item
android:id="@+id/action_save"
android:icon="@drawable/ic_save"
android:title="@string/save"
app:showAsAction="ifRoom|withText" />
</menu>