Update chapters list ui, add bookmark indicator

This commit is contained in:
Koitharu
2023-06-01 17:55:57 +03:00
parent 84f41810c5
commit 2e3be00e26
26 changed files with 202 additions and 94 deletions

View File

@@ -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<Map<MangaWithTags, List<BookmarkEntity>>>
@@ -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)
}
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
}

View File

@@ -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
}
}
}

View File

@@ -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<Manga, Set<Long>>): ReversibleHandle {

View File

@@ -19,7 +19,9 @@ class BookmarksAdapter(
private class DiffCallback : DiffUtil.ItemCallback<Bookmark>() {
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 {

View File

@@ -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
},
)
}

View File

@@ -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
}
}

View File

@@ -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)

View File

@@ -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<Bookmark>,
): List<ChapterListItem> {
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,
)
}
}

View File

@@ -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<MenuItem>? = 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()

View File

@@ -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<ActivityDetailsBinding>(),
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
}

View File

@@ -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,

View File

@@ -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)
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}

View File

@@ -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,

View File

@@ -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
}

View File

@@ -47,6 +47,7 @@ class ChaptersSheet : BaseAdaptiveSheet<SheetChaptersBinding>(), OnListItemClick
isUnread = index > currentPosition,
isNew = false,
isDownloaded = false,
isBookmarked = false,
)
}
binding.recyclerView.adapter = ChaptersAdapter(this).also { adapter ->

View File

@@ -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)
}
}

View File

@@ -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">
<path

View File

@@ -48,38 +48,51 @@
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.BottomSheet"
tools:visibility="visible">
<org.koitharu.kotatsu.core.ui.widgets.BottomSheetHeaderBar
android:id="@+id/header_chapters"
<LinearLayout
android:id="@+id/layout_bs_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.Kotatsu.MainToolbar"
app:fitStatusBar="true"
tools:menu="@menu/opt_chapters">
android:orientation="vertical">
<ImageView
android:id="@+id/button_dropdown"
android:layout_width="wrap_content"
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:id="@+id/dragHandle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_chapters"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:background="?selectableItemBackgroundBorderless"
android:padding="@dimen/margin_small"
android:src="@drawable/ic_expand_more" />
android:theme="@style/ThemeOverlay.Kotatsu.MainToolbar"
tools:menu="@menu/opt_chapters">
<com.google.android.material.button.MaterialButton
android:id="@+id/button_read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
android:enabled="false"
android:text="@string/read"
android:textAllCaps="false"
app:iconGravity="textStart"
app:iconPadding="8dp"
tools:enabled="true"
tools:icon="@drawable/ic_read" />
<ImageView
android:id="@+id/button_dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/translations"
android:padding="@dimen/margin_small"
android:src="@drawable/ic_expand_more" />
</org.koitharu.kotatsu.core.ui.widgets.BottomSheetHeaderBar>
<com.google.android.material.button.MaterialButton
android:id="@+id/button_read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
android:enabled="false"
android:text="@string/read"
android:textAllCaps="false"
app:iconGravity="textStart"
app:iconPadding="8dp"
tools:enabled="true"
tools:icon="@drawable/ic_read" />
</com.google.android.material.appbar.MaterialToolbar>
</LinearLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container_chapters"

View File

@@ -22,7 +22,6 @@
android:paddingRight="@dimen/list_spacing"
android:paddingBottom="@dimen/grid_spacing_outer"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:trackColor="?attr/colorOutline"
tools:listitem="@layout/item_feed" />
</FrameLayout>

View File

@@ -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 @@
</LinearLayout>
<ImageView
android:id="@+id/imageView_new"
android:id="@+id/imageView_bookmarked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="?android:listPreferredItemPaddingEnd"
android:src="@drawable/ic_new"
app:tint="?colorError" />
android:contentDescription="@string/bookmarks"
app:srcCompat="@drawable/ic_bookmark" />
<ImageView
android:id="@+id/imageView_downloaded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="?android:listPreferredItemPaddingEnd"
android:src="@drawable/ic_save_ok" />
android:contentDescription="@string/downloaded"
app:srcCompat="@drawable/ic_save_ok" />
</LinearLayout>

View File

@@ -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" />
</FrameLayout>

View File

@@ -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" />

View File

@@ -425,4 +425,5 @@
<string name="proxy">Proxy</string>
<string name="invalid_value_message">Invalid value</string>
<string name="manga_branch_title_template">%1$s (%2$s)</string>
<string name="downloaded">Downloaded</string>
</resources>

View File

@@ -133,6 +133,7 @@
<item name="bubbleTextColor">?colorOnTertiary</item>
<item name="trackColor">?colorOutline</item>
<item name="bubbleSize">normal</item>
<item name="scrollerOffset">6dp</item>
</style>
<style name="Widget.Kotatsu.ListItemTextView" parent="">