diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/data/BookmarksDao.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/data/BookmarksDao.kt index 076b19a3c..0f7533857 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/data/BookmarksDao.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/data/BookmarksDao.kt @@ -1,6 +1,10 @@ package org.koitharu.kotatsu.bookmarks.data -import androidx.room.* +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Transaction import kotlinx.coroutines.flow.Flow import org.koitharu.kotatsu.core.db.entity.MangaWithTags @@ -18,7 +22,7 @@ abstract class BookmarksDao { @Transaction @Query( - "SELECT * FROM manga JOIN bookmarks ON bookmarks.manga_id = manga.manga_id ORDER BY bookmarks.created_at" + "SELECT * FROM manga JOIN bookmarks ON bookmarks.manga_id = manga.manga_id ORDER BY bookmarks.created_at", ) abstract fun observe(): Flow>> @@ -29,5 +33,8 @@ abstract class BookmarksDao { abstract suspend fun delete(entity: BookmarkEntity) @Query("DELETE FROM bookmarks WHERE manga_id = :mangaId AND page_id = :pageId") - abstract suspend fun delete(mangaId: Long, pageId: Long) -} \ No newline at end of file + abstract suspend fun delete(mangaId: Long, pageId: Long): Int + + @Query("DELETE FROM bookmarks WHERE manga_id = :mangaId AND chapter_id = :chapterId AND page = :page") + abstract suspend fun delete(mangaId: Long, chapterId: Long, page: Int): Int +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/Bookmark.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/Bookmark.kt index 5b6ff3bf0..421105155 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/Bookmark.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/Bookmark.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.bookmarks.domain import org.koitharu.kotatsu.parsers.model.Manga -import java.util.* +import java.util.Date class Bookmark( val manga: Manga, @@ -27,9 +27,7 @@ class Bookmark( if (scroll != other.scroll) return false if (imageUrl != other.imageUrl) return false if (createdAt != other.createdAt) return false - if (percent != other.percent) return false - - return true + return percent == other.percent } override fun hashCode(): Int { @@ -43,4 +41,4 @@ class Bookmark( result = 31 * result + percent.hashCode() return result } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/BookmarksRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/BookmarksRepository.kt index 0c439a6bc..598113eb8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/BookmarksRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/BookmarksRepository.kt @@ -52,8 +52,14 @@ class BookmarksRepository @Inject constructor( } } - suspend fun removeBookmark(mangaId: Long, pageId: Long) { - db.bookmarksDao.delete(mangaId, pageId) + suspend fun removeBookmark(mangaId: Long, chapterId: Long, page: Int) { + check(db.bookmarksDao.delete(mangaId, chapterId, page) != 0) { + "Bookmark not found" + } + } + + suspend fun removeBookmark(bookmark: Bookmark) { + removeBookmark(bookmark.manga.id, bookmark.chapterId, bookmark.page) } suspend fun removeBookmarks(ids: Map>): ReversibleHandle { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt index d47bbb785..8d15f00ab 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt @@ -19,7 +19,9 @@ class BookmarksAdapter( private class DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Bookmark, newItem: Bookmark): Boolean { - return oldItem.manga.id == newItem.manga.id && oldItem.pageId == newItem.pageId + return oldItem.manga.id == newItem.manga.id && + oldItem.chapterId == newItem.chapterId && + oldItem.page == newItem.page } override fun areContentsTheSame(oldItem: Bookmark, newItem: Bookmark): Boolean { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/BottomSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/BottomSheet.kt new file mode 100644 index 000000000..faba08d87 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/BottomSheet.kt @@ -0,0 +1,23 @@ +package org.koitharu.kotatsu.core.util.ext + +import android.view.View +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback + +fun BottomSheetBehavior<*>.doOnExpansionsChanged(callback: (isExpanded: Boolean) -> Unit) { + var isExpended = state == BottomSheetBehavior.STATE_EXPANDED + callback(isExpended) + addBottomSheetCallback( + object : BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + val expanded = newState == BottomSheetBehavior.STATE_EXPANDED + if (expanded != isExpended) { + isExpended = expanded + callback(expanded) + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit + }, + ) +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersBottomSheetMediator.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersBottomSheetMediator.kt index 2e0929ae9..727ff31f3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersBottomSheetMediator.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersBottomSheetMediator.kt @@ -6,18 +6,25 @@ import androidx.activity.OnBackPressedCallback import androidx.appcompat.view.ActionMode import com.google.android.material.bottomsheet.BottomSheetBehavior import org.koitharu.kotatsu.core.ui.util.ActionModeListener -import org.koitharu.kotatsu.core.ui.widgets.BottomSheetHeaderBar +import org.koitharu.kotatsu.core.util.ext.doOnExpansionsChanged class ChaptersBottomSheetMediator( - bottomSheet: View, + private val behavior: BottomSheetBehavior<*>, ) : OnBackPressedCallback(false), ActionModeListener, - BottomSheetHeaderBar.OnExpansionChangeListener, OnLayoutChangeListener { - private val behavior = BottomSheetBehavior.from(bottomSheet) private var lockCounter = 0 + init { + behavior.doOnExpansionsChanged { isExpanded -> + isEnabled = isExpanded + if (!isExpanded) { + unlock() + } + } + } + override fun handleOnBackPressed() { behavior.state = BottomSheetBehavior.STATE_COLLAPSED } @@ -30,13 +37,6 @@ class ChaptersBottomSheetMediator( unlock() } - override fun onExpansionStateChanged(headerBar: BottomSheetHeaderBar, isExpanded: Boolean) { - isEnabled = isExpanded - if (!isExpanded) { - unlock() - } - } - override fun onLayoutChange( v: View?, left: Int, @@ -61,6 +61,9 @@ class ChaptersBottomSheetMediator( fun unlock() { lockCounter-- + if (lockCounter < 0) { + lockCounter = 0 + } behavior.isDraggable = lockCounter <= 0 } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt index 07a6f8902..576aa88c9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.details.ui +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.Menu @@ -56,6 +57,9 @@ class ChaptersFragment : checkNotNull(selectionController).attachToRecyclerView(this) setHasFixedSize(true) adapter = chaptersAdapter + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + scrollIndicators = if (resources.getBoolean(R.bool.is_tablet)) 0 else View.SCROLL_INDICATOR_TOP + } } viewModel.isLoading.observe(viewLifecycleOwner, this::onLoadingStateChanged) viewModel.chapters.observe(viewLifecycleOwner, this::onChaptersChanged) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMapper.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMapper.kt index 27a937c0c..257d4bef5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMapper.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMapper.kt @@ -1,9 +1,11 @@ package org.koitharu.kotatsu.details.ui +import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.core.model.MangaHistory import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.toListItem import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.util.mapToSet fun mapChapters( remoteManga: Manga?, @@ -11,12 +13,14 @@ fun mapChapters( history: MangaHistory?, newCount: Int, branch: String?, + bookmarks: List, ): List { val remoteChapters = remoteManga?.getChapters(branch).orEmpty() val localChapters = localManga?.getChapters(branch).orEmpty() if (remoteChapters.isEmpty() && localChapters.isEmpty()) { return emptyList() } + val bookmarked = bookmarks.mapToSet { it.chapterId } val currentId = history?.chapterId ?: 0L val newFrom = if (newCount == 0 || remoteChapters.isEmpty()) Int.MAX_VALUE else remoteChapters.size - newCount val chaptersSize = maxOf(remoteChapters.size, localChapters.size) @@ -41,6 +45,7 @@ fun mapChapters( isUnread = isUnread, isNew = isUnread && result.size >= newFrom, isDownloaded = local != null, + isBookmarked = chapter.id in bookmarked, ) } if (!localMap.isNullOrEmpty()) { @@ -53,6 +58,7 @@ fun mapChapters( isUnread = isUnread, isNew = false, isDownloaded = remoteManga != null, + isBookmarked = chapter.id in bookmarked, ) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMenuProvider.kt index c6c3fc64b..e42fa2ca8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMenuProvider.kt @@ -3,14 +3,18 @@ package org.koitharu.kotatsu.details.ui import android.view.Menu import android.view.MenuInflater import android.view.MenuItem +import androidx.activity.OnBackPressedCallback import androidx.appcompat.widget.SearchView import androidx.core.view.MenuProvider import org.koitharu.kotatsu.R +import java.lang.ref.WeakReference class ChaptersMenuProvider( private val viewModel: DetailsViewModel, private val bottomSheetMediator: ChaptersBottomSheetMediator?, -) : MenuProvider, SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener { +) : OnBackPressedCallback(false), MenuProvider, SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener { + + private var searchItemRef: WeakReference? = null override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { menuInflater.inflate(R.menu.opt_chapters, menu) @@ -20,6 +24,7 @@ class ChaptersMenuProvider( searchView.setOnQueryTextListener(this) searchView.setIconifiedByDefault(false) searchView.queryHint = searchMenuItem.title + searchItemRef = WeakReference(searchMenuItem) } override fun onPrepareMenu(menu: Menu) { @@ -32,15 +37,22 @@ class ChaptersMenuProvider( viewModel.setChaptersReversed(!menuItem.isChecked) true } + else -> false } + override fun handleOnBackPressed() { + searchItemRef?.get()?.collapseActionView() + } + override fun onMenuItemActionExpand(item: MenuItem): Boolean { bottomSheetMediator?.lock() + isEnabled = true return true } override fun onMenuItemActionCollapse(item: MenuItem): Boolean { + isEnabled = false (item.actionView as? SearchView)?.setQuery("", false) viewModel.performChapterSearch(null) bottomSheetMediator?.unlock() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt index 64abbc9f8..723fc99e3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.DialogInterface import android.content.Intent import android.os.Bundle +import android.transition.AutoTransition import android.transition.Slide import android.transition.TransitionManager import android.view.Gravity @@ -18,6 +19,7 @@ import androidx.core.graphics.Insets import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.view.updatePadding +import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint @@ -31,8 +33,11 @@ import org.koitharu.kotatsu.core.parser.MangaIntent import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.dialog.RecyclerViewAlertDialog import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener -import org.koitharu.kotatsu.core.ui.widgets.BottomSheetHeaderBar import org.koitharu.kotatsu.core.util.ViewBadge +import org.koitharu.kotatsu.core.util.ext.doOnExpansionsChanged +import org.koitharu.kotatsu.core.util.ext.getAnimationDuration +import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled +import org.koitharu.kotatsu.core.util.ext.measureHeight import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.setNavigationBarTransparentCompat @@ -49,19 +54,16 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.thumbnails.PagesThumbnailsSheet import javax.inject.Inject +import com.google.android.material.R as materialR @AndroidEntryPoint class DetailsActivity : BaseActivity(), View.OnClickListener, - BottomSheetHeaderBar.OnExpansionChangeListener, NoModalBottomSheetOwner, View.OnLongClickListener, PopupMenu.OnMenuItemClickListener { - override val bsHeader: BottomSheetHeaderBar? - get() = viewBinding.headerChapters - @Inject lateinit var shortcutsUpdater: ShortcutsUpdater @@ -82,16 +84,22 @@ class DetailsActivity : viewBinding.buttonDropdown.setOnClickListener(this) viewBadge = ViewBadge(viewBinding.buttonRead, this) - chaptersMenuProvider = if (viewBinding.layoutBottom != null) { - val bsMediator = ChaptersBottomSheetMediator(checkNotNull(viewBinding.layoutBottom)) + if (viewBinding.layoutBottom != null) { + val behavior = BottomSheetBehavior.from(checkNotNull(viewBinding.layoutBottom)) + val bsMediator = ChaptersBottomSheetMediator(behavior) actionModeDelegate.addListener(bsMediator) - checkNotNull(viewBinding.headerChapters).addOnExpansionChangeListener(bsMediator) - checkNotNull(viewBinding.headerChapters).addOnLayoutChangeListener(bsMediator) + checkNotNull(viewBinding.layoutBsHeader).addOnLayoutChangeListener(bsMediator) onBackPressedDispatcher.addCallback(bsMediator) - ChaptersMenuProvider(viewModel, bsMediator) + chaptersMenuProvider = ChaptersMenuProvider(viewModel, bsMediator) + behavior.doOnExpansionsChanged(::onChaptersSheetStateChanged) + viewBinding.toolbarChapters?.setNavigationOnClickListener { + behavior.state = BottomSheetBehavior.STATE_COLLAPSED + } } else { - ChaptersMenuProvider(viewModel, null) + chaptersMenuProvider = ChaptersMenuProvider(viewModel, null) + addMenuProvider(chaptersMenuProvider) } + onBackPressedDispatcher.addCallback(chaptersMenuProvider) viewModel.manga.filterNotNull().observe(this, ::onMangaUpdated) viewModel.newChaptersCount.observe(this, ::onNewChaptersChanged) @@ -114,11 +122,11 @@ class DetailsActivity : } viewModel.historyInfo.observe(this, ::onHistoryChanged) viewModel.selectedBranch.observe(this) { - viewBinding.headerChapters?.subtitle = it + viewBinding.toolbarChapters?.subtitle = it viewBinding.textViewSubtitle?.textAndVisible = it } viewModel.isChaptersReversed.observe(this) { - viewBinding.headerChapters?.invalidateMenu() ?: invalidateOptionsMenu() + viewBinding.toolbarChapters?.invalidateMenu() ?: invalidateOptionsMenu() } viewModel.favouriteCategories.observe(this) { invalidateOptionsMenu() @@ -137,7 +145,10 @@ class DetailsActivity : shortcutsUpdater = shortcutsUpdater, ), ) - viewBinding.headerChapters?.addOnExpansionChangeListener(this) ?: addMenuProvider(chaptersMenuProvider) + } + + override fun getBottomSheetCollapsedHeight(): Int { + return viewBinding.layoutBsHeader?.measureHeight() ?: 0 } override fun onClick(v: View) { @@ -184,11 +195,19 @@ class DetailsActivity : } } - override fun onExpansionStateChanged(headerBar: BottomSheetHeaderBar, isExpanded: Boolean) { + private fun onChaptersSheetStateChanged(isExpanded: Boolean) { + val toolbar = viewBinding.toolbarChapters ?: return + if (isAnimationsEnabled) { + val transition = AutoTransition() + transition.duration = getAnimationDuration(R.integer.config_tinyAnimTime) + TransitionManager.beginDelayedTransition(toolbar, transition) + } if (isExpanded) { - headerBar.addMenuProvider(chaptersMenuProvider) + toolbar.addMenuProvider(chaptersMenuProvider) + toolbar.setNavigationIcon(materialR.drawable.abc_ic_clear_material) } else { - headerBar.removeMenuProvider(chaptersMenuProvider) + toolbar.removeMenuProvider(chaptersMenuProvider) + toolbar.navigationIcon = null } viewBinding.buttonRead.isGone = isExpanded } @@ -237,7 +256,7 @@ class DetailsActivity : info.totalChapters == 0 -> getString(R.string.no_chapters) else -> resources.getQuantityString(R.plurals.chapters, info.totalChapters, info.totalChapters) } - viewBinding.headerChapters?.title = text + viewBinding.toolbarChapters?.title = text viewBinding.textViewTitle?.text = text } @@ -282,8 +301,6 @@ class DetailsActivity : } } - private fun isTabletLayout() = viewBinding.layoutBottom == null - private fun showBottomSheet(isVisible: Boolean) { val view = viewBinding.layoutBottom ?: return if (view.isVisible == isVisible) return @@ -297,7 +314,7 @@ class DetailsActivity : private fun makeSnackbar(text: CharSequence, @BaseTransientBottomBar.Duration duration: Int): Snackbar { val sb = Snackbar.make(viewBinding.containerDetails, text, duration) if (viewBinding.layoutBottom?.isVisible == true) { - sb.anchorView = viewBinding.headerChapters + sb.anchorView = viewBinding.toolbarChapters } return sb } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index 14b964316..28c79fc75 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -34,7 +34,6 @@ import org.koitharu.kotatsu.core.util.ext.crossfade import org.koitharu.kotatsu.core.util.ext.drawableTop import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty -import org.koitharu.kotatsu.core.util.ext.measureHeight import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.resolveDp import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf @@ -271,7 +270,7 @@ class DetailsFragment : override fun onWindowInsetsChanged(insets: Insets) { requireViewBinding().root.updatePadding( bottom = ( - (activity as? NoModalBottomSheetOwner)?.bsHeader?.measureHeight() + (activity as? NoModalBottomSheetOwner)?.getBottomSheetCollapsedHeight() ?.plus(insets.bottom)?.plus(resources.resolveDp(16)) ) ?: insets.bottom, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt index 7579b3301..0c5c13484 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt @@ -171,8 +171,9 @@ class DetailsViewModel @Inject constructor( history, selectedBranch, newChaptersCount, - ) { manga, history, branch, news -> - mapChapters(manga?.remote, manga?.local, history, news, branch) + bookmarks, + ) { manga, history, branch, news, bookmarks -> + mapChapters(manga?.remote, manga?.local, history, news, branch, bookmarks) }, isChaptersReversed, chaptersQuery, @@ -209,8 +210,8 @@ class DetailsViewModel @Inject constructor( } fun removeBookmark(bookmark: Bookmark) { - launchJob { - bookmarksRepository.removeBookmark(bookmark.manga.id, bookmark.pageId) + launchJob(Dispatchers.Default) { + bookmarksRepository.removeBookmark(bookmark) onShowToast.call(R.string.bookmark_removed) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt index 6ff818ccf..9865b59cb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt @@ -1,10 +1,12 @@ package org.koitharu.kotatsu.details.ui.adapter +import androidx.core.content.ContextCompat import androidx.core.view.isVisible import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener +import org.koitharu.kotatsu.core.util.ext.drawableStart import org.koitharu.kotatsu.core.util.ext.getThemeColor import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.ItemChapterBinding @@ -43,7 +45,13 @@ fun chapterListItemAD( binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorTertiary)) } } + binding.imageViewBookmarked.isVisible = item.isBookmarked binding.imageViewDownloaded.isVisible = item.isDownloaded - binding.imageViewNew.isVisible = item.isNew + // binding.imageViewNew.isVisible = item.isNew + binding.textViewTitle.drawableStart = if (item.isNew) { + ContextCompat.getDrawable(context, R.drawable.ic_new) + } else { + null + } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt index 2a62acb1e..5c3cfd46e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ChapterListItem.kt @@ -31,6 +31,9 @@ class ChapterListItem( val isDownloaded: Boolean get() = hasFlag(FLAG_DOWNLOADED) + val isBookmarked: Boolean + get() = hasFlag(FLAG_BOOKMARKED) + val isNew: Boolean get() = hasFlag(FLAG_NEW) @@ -70,6 +73,7 @@ class ChapterListItem( const val FLAG_UNREAD = 2 const val FLAG_CURRENT = 4 const val FLAG_NEW = 8 + const val FLAG_BOOKMARKED = 16 const val FLAG_DOWNLOADED = 32 } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt index 73d70d3df..95c4cae15 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/model/ListModelConversionExt.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.details.ui.model +import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_BOOKMARKED import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_CURRENT import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_DOWNLOADED import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_NEW @@ -11,11 +12,13 @@ fun MangaChapter.toListItem( isUnread: Boolean, isNew: Boolean, isDownloaded: Boolean, + isBookmarked: Boolean, ): ChapterListItem { var flags = 0 if (isCurrent) flags = flags or FLAG_CURRENT if (isUnread) flags = flags or FLAG_UNREAD if (isNew) flags = flags or FLAG_NEW + if (isBookmarked) flags = flags or FLAG_BOOKMARKED if (isDownloaded) flags = flags or FLAG_DOWNLOADED return ChapterListItem( chapter = this, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/owners/NoModalBottomSheetOwner.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/owners/NoModalBottomSheetOwner.kt index e03f90ad2..3920088d4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/owners/NoModalBottomSheetOwner.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/owners/NoModalBottomSheetOwner.kt @@ -1,8 +1,6 @@ package org.koitharu.kotatsu.main.ui.owners -import org.koitharu.kotatsu.core.ui.widgets.BottomSheetHeaderBar - interface NoModalBottomSheetOwner { - val bsHeader: BottomSheetHeaderBar? + fun getBottomSheetCollapsedHeight(): Int } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ChaptersSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ChaptersSheet.kt index 0c74a0d80..4dcfce2e8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ChaptersSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ChaptersSheet.kt @@ -47,6 +47,7 @@ class ChaptersSheet : BaseAdaptiveSheet(), OnListItemClick isUnread = index > currentPosition, isNew = false, isDownloaded = false, + isBookmarked = false, ) } binding.recyclerView.adapter = ChaptersAdapter(this).also { adapter -> diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt index 0ccbac877..17b52edd0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt @@ -41,6 +41,7 @@ import org.koitharu.kotatsu.core.prefs.observeAsStateFlow import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call +import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty import org.koitharu.kotatsu.core.util.ext.requireValue import org.koitharu.kotatsu.details.domain.DoubleMangaLoadUseCase import org.koitharu.kotatsu.details.domain.model.DoubleManga @@ -140,7 +141,9 @@ class ReaderViewModel @Inject constructor( flowOf(false) } else { bookmarksRepository.observeBookmark(manga, state.chapterId, state.page) - .map { it != null } + .map { + it != null && it.chapterId == state.chapterId && it.page == state.page + } } }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, false) @@ -285,7 +288,7 @@ class ReaderViewModel @Inject constructor( chapterId = state.chapterId, page = state.page, scroll = state.scroll, - imageUrl = page.preview ?: pageLoader.getPageUrl(page), + imageUrl = page.preview.ifNullOrEmpty { pageLoader.getPageUrl(page) }, createdAt = Date(), percent = computePercent(state.chapterId, state.page), ) @@ -301,8 +304,8 @@ class ReaderViewModel @Inject constructor( bookmarkJob = launchJob { loadingJob?.join() val manga = checkNotNull(mangaData.value?.any) - val page = checkNotNull(getCurrentPage()) { "Page not found" } - bookmarksRepository.removeBookmark(manga.id, page.id) + val state = checkNotNull(getCurrentState()) + bookmarksRepository.removeBookmark(manga.id, state.chapterId, state.page) onShowToast.call(R.string.bookmark_removed) } } diff --git a/app/src/main/res/drawable/ic_new.xml b/app/src/main/res/drawable/ic_new.xml index f51c265cf..f0e81e12d 100644 --- a/app/src/main/res/drawable/ic_new.xml +++ b/app/src/main/res/drawable/ic_new.xml @@ -2,7 +2,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" - android:tint="?colorControlNormal" + android:tint="?colorError" android:viewportWidth="24" android:viewportHeight="24"> - + android:orientation="vertical"> - + - + android:theme="@style/ThemeOverlay.Kotatsu.MainToolbar" + tools:menu="@menu/opt_chapters"> + + + + + + - + diff --git a/app/src/main/res/layout/item_chapter.xml b/app/src/main/res/layout/item_chapter.xml index 0a44ea419..3244de9c9 100644 --- a/app/src/main/res/layout/item_chapter.xml +++ b/app/src/main/res/layout/item_chapter.xml @@ -38,6 +38,7 @@ android:id="@+id/textView_title" android:layout_width="match_parent" android:layout_height="wrap_content" + android:drawablePadding="4dp" android:ellipsize="end" android:singleLine="true" android:textAppearance="?attr/textAppearanceBodyLarge" @@ -55,18 +56,19 @@ + android:contentDescription="@string/bookmarks" + app:srcCompat="@drawable/ic_bookmark" /> + android:contentDescription="@string/downloaded" + app:srcCompat="@drawable/ic_save_ok" /> diff --git a/app/src/main/res/layout/sheet_filter.xml b/app/src/main/res/layout/sheet_filter.xml index 48ff214d7..56f00ccbf 100644 --- a/app/src/main/res/layout/sheet_filter.xml +++ b/app/src/main/res/layout/sheet_filter.xml @@ -26,8 +26,6 @@ android:scrollIndicators="top" app:bubbleSize="normal" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" - app:scrollerOffset="6dp" - app:trackColor="?attr/colorOutline" tools:ignore="UnusedAttribute" tools:listitem="@layout/item_checkable_new" /> diff --git a/app/src/main/res/layout/sheet_pages.xml b/app/src/main/res/layout/sheet_pages.xml index 1cc9d1b56..986ea651b 100644 --- a/app/src/main/res/layout/sheet_pages.xml +++ b/app/src/main/res/layout/sheet_pages.xml @@ -26,7 +26,6 @@ app:bubbleSize="small" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" app:spanCount="3" - app:trackColor="?attr/colorOutline" tools:listitem="@layout/item_page_thumb" tools:targetApi="m" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2c3977217..e063c5d10 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -425,4 +425,5 @@ Proxy Invalid value %1$s (%2$s) + Downloaded diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 452574345..a4652f0cb 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -133,6 +133,7 @@ ?colorOnTertiary ?colorOutline normal + 6dp