Move bookmark button to bottom reader bar

This commit is contained in:
Koitharu
2025-05-11 16:26:17 +03:00
parent 4ec9a91644
commit 1229e9626e
12 changed files with 166 additions and 104 deletions

View File

@@ -4,7 +4,7 @@ import java.util.EnumSet
enum class ReaderControl {
PREV_CHAPTER, NEXT_CHAPTER, SLIDER, PAGES_SHEET, SCREEN_ROTATION, SAVE_PAGE, TIMER;
PREV_CHAPTER, NEXT_CHAPTER, SLIDER, PAGES_SHEET, SCREEN_ROTATION, SAVE_PAGE, TIMER, BOOKMARK;
companion object {

View File

@@ -26,6 +26,7 @@ import org.koitharu.kotatsu.core.util.ext.hasVisibleChildren
import org.koitharu.kotatsu.core.util.ext.isRtl
import org.koitharu.kotatsu.core.util.ext.setValueRounded
import org.koitharu.kotatsu.databinding.LayoutReaderActionsBinding
import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet
import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet.Companion.TAB_PAGES
import org.koitharu.kotatsu.reader.ui.ReaderControlDelegate.OnInteractionListener
import javax.inject.Inject
@@ -40,7 +41,7 @@ class ReaderActionsView @JvmOverloads constructor(
View.OnClickListener,
SharedPreferences.OnSharedPreferenceChangeListener,
Slider.OnChangeListener,
Slider.OnSliderTouchListener {
Slider.OnSliderTouchListener, View.OnLongClickListener {
@Inject
lateinit var settings: AppSettings
@@ -73,6 +74,14 @@ class ReaderActionsView @JvmOverloads constructor(
binding.buttonPrev.isEnabled = value
}
var isBookmarkAdded: Boolean = false
set(value) {
if (field != value) {
field = value
updateBookmarkButton()
}
}
var listener: OnInteractionListener? = null
init {
@@ -85,6 +94,7 @@ class ReaderActionsView @JvmOverloads constructor(
binding.buttonScreenRotation.initAction()
binding.buttonPagesThumbs.initAction()
binding.buttonTimer.initAction()
binding.buttonBookmark.initAction()
binding.slider.setLabelFormatter(PageLabelFormatter())
binding.slider.addOnChangeListener(this)
binding.slider.addOnSliderTouchListener(this)
@@ -112,13 +122,22 @@ class ReaderActionsView @JvmOverloads constructor(
R.id.button_prev -> listener?.switchChapterBy(-1)
R.id.button_next -> listener?.switchChapterBy(1)
R.id.button_save -> listener?.onSavePageClick()
R.id.button_timer -> listener?.onScrollTimerClick()
R.id.button_timer -> listener?.onScrollTimerClick(isLongClick = false)
R.id.button_pages_thumbs -> AppRouter.from(this)?.showChapterPagesSheet()
R.id.button_screen_rotation -> listener?.toggleScreenOrientation()
R.id.button_options -> listener?.openMenu()
R.id.button_bookmark -> listener?.onBookmarkClick()
}
}
override fun onLongClick(v: View): Boolean = when (v.id) {
R.id.button_bookmark -> AppRouter.from(this)
?.showChapterPagesSheet(ChaptersPagesSheet.TAB_BOOKMARKS)
R.id.button_timer -> listener?.onScrollTimerClick(isLongClick = true)
R.id.button_options -> AppRouter.from(this)?.openReaderSettings()
else -> null
} != null
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
if (fromUser) {
if (isSliderTracking) {
@@ -175,6 +194,7 @@ class ReaderActionsView @JvmOverloads constructor(
binding.buttonScreenRotation.isVisible = ReaderControl.SCREEN_ROTATION in controls
binding.buttonSave.isVisible = ReaderControl.SAVE_PAGE in controls
binding.buttonTimer.isVisible = ReaderControl.TIMER in controls
binding.buttonBookmark.isVisible = ReaderControl.BOOKMARK in controls
binding.slider.isVisible = ReaderControl.SLIDER in controls
adjustLayoutParams()
}
@@ -190,6 +210,16 @@ class ReaderActionsView @JvmOverloads constructor(
)
}
private fun updateBookmarkButton() {
val button = binding.buttonBookmark
button.setIconResource(
if (isBookmarkAdded) R.drawable.ic_bookmark_added else R.drawable.ic_bookmark,
)
button.setTitle(
if (isBookmarkAdded) R.string.bookmark_remove else R.string.bookmark_add,
)
}
private fun adjustLayoutParams() {
val isSliderVisible = binding.slider.isVisible
repeat(childCount) { i ->
@@ -222,6 +252,7 @@ class ReaderActionsView @JvmOverloads constructor(
private fun Button.initAction() {
setOnClickListener(this@ReaderActionsView)
setOnLongClickListener(this@ReaderActionsView)
ViewCompat.setTooltipText(this, contentDescription)
}

View File

@@ -114,6 +114,7 @@ class ReaderActivity :
viewBinding.actionsView.listener = this
idlingDetector.bindToLifecycle(this)
screenOrientationHelper.applySettings()
viewModel.isBookmarkAdded.observe(this) { viewBinding.actionsView.isBookmarkAdded = it }
scrollTimer.isActive.observe(this) { viewBinding.actionsView.setTimerActive(it) }
viewBinding.timerControl.attach(scrollTimer, this)
if (resources.getBoolean(R.bool.is_tablet)) {
@@ -371,12 +372,20 @@ class ReaderActivity :
return reader.isResumed && supportFragmentManager.fragments.lastOrNull() === reader
}
override fun onBookmarkClick() {
viewModel.toggleBookmark()
}
override fun onSavePageClick() {
viewModel.saveCurrentPage(pageSaveHelper)
}
override fun onScrollTimerClick() {
viewBinding.timerControl.showOrHide()
override fun onScrollTimerClick(isLongClick: Boolean) {
if (isLongClick) {
scrollTimer.setActive(!scrollTimer.isActive.value)
} else {
viewBinding.timerControl.showOrHide()
}
}
override fun toggleScreenOrientation() {

View File

@@ -138,11 +138,13 @@ class ReaderControlDelegate(
fun toggleUiVisibility()
fun onBookmarkClick()
fun openMenu()
fun onSavePageClick()
fun onScrollTimerClick()
fun onScrollTimerClick(isLongClick: Boolean)
fun toggleScreenOrientation()

View File

@@ -14,26 +14,10 @@ class ReaderMenuProvider(
menuInflater.inflate(R.menu.opt_reader, menu)
}
override fun onPrepareMenu(menu: Menu) {
menu.findItem(R.id.action_bookmark)?.let { bookmarkItem ->
val hasPages = viewModel.content.value.pages.isNotEmpty()
bookmarkItem.isEnabled = hasPages
if (hasPages) {
val hasBookmark = viewModel.isBookmarkAdded.value
bookmarkItem.setTitle(if (hasBookmark) R.string.bookmark_remove else R.string.bookmark_add)
bookmarkItem.setIcon(if (hasBookmark) R.drawable.ic_bookmark_added else R.drawable.ic_bookmark)
}
}
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.action_bookmark -> {
if (viewModel.isBookmarkAdded.value) {
viewModel.removeBookmark()
} else {
viewModel.addBookmark()
}
R.id.action_info -> {
// TODO
true
}

View File

@@ -346,39 +346,32 @@ class ReaderViewModel @Inject constructor(
}
}
fun addBookmark() {
fun toggleBookmark() {
if (bookmarkJob?.isActive == true) {
return
}
bookmarkJob = launchJob(Dispatchers.Default) {
loadingJob?.join()
val state = checkNotNull(readingState.value)
val page = checkNotNull(getCurrentPage()) { "Page not found" }
val bookmark = Bookmark(
manga = requireManga(),
pageId = page.id,
chapterId = state.chapterId,
page = state.page,
scroll = state.scroll,
imageUrl = page.preview.ifNullOrEmpty { page.url },
createdAt = Instant.now(),
percent = computePercent(state.chapterId, state.page),
)
bookmarksRepository.addBookmark(bookmark)
onShowToast.call(R.string.bookmark_added)
}
}
fun removeBookmark() {
if (bookmarkJob?.isActive == true) {
return
}
bookmarkJob = launchJob {
loadingJob?.join()
val manga = requireManga()
val state = checkNotNull(getCurrentState())
bookmarksRepository.removeBookmark(manga.id, state.chapterId, state.page)
onShowToast.call(R.string.bookmark_removed)
if (isBookmarkAdded.value) {
val manga = requireManga()
bookmarksRepository.removeBookmark(manga.id, state.chapterId, state.page)
onShowToast.call(R.string.bookmark_removed)
} else {
val page = checkNotNull(getCurrentPage()) { "Page not found" }
val bookmark = Bookmark(
manga = requireManga(),
pageId = page.id,
chapterId = state.chapterId,
page = state.page,
scroll = state.scroll,
imageUrl = page.preview.ifNullOrEmpty { page.url },
createdAt = Instant.now(),
percent = computePercent(state.chapterId, state.page),
)
bookmarksRepository.addBookmark(bookmark)
onShowToast.call(R.string.bookmark_added)
}
}
}

View File

@@ -4,8 +4,10 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.CompoundButton
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.children
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
@@ -24,6 +26,7 @@ import org.koitharu.kotatsu.core.prefs.ReaderMode
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import org.koitharu.kotatsu.core.util.ext.consume
import org.koitharu.kotatsu.core.util.ext.findParentCallback
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope
import org.koitharu.kotatsu.databinding.SheetReaderConfigBinding
import org.koitharu.kotatsu.reader.domain.PageLoader
@@ -93,8 +96,16 @@ class ReaderConfigSheet :
binding.buttonImageServer.setOnClickListener(this)
binding.buttonColorFilter.setOnClickListener(this)
binding.buttonScrollTimer.setOnClickListener(this)
binding.buttonBookmark.setOnClickListener(this)
binding.switchDoubleReader.setOnCheckedChangeListener(this)
viewModel.isBookmarkAdded.observe(viewLifecycleOwner) {
binding.buttonBookmark.setText(if (it) R.string.bookmark_remove else R.string.bookmark_add)
binding.buttonBookmark.setCompoundDrawablesRelativeWithIntrinsicBounds(
if (it) R.drawable.ic_bookmark_checked else R.drawable.ic_bookmark, 0, 0, 0,
)
}
viewLifecycleScope.launch {
val isAvailable = imageServerDelegate.isAvailable()
if (isAvailable) {
@@ -120,7 +131,7 @@ class ReaderConfigSheet :
}
R.id.button_scroll_timer -> {
findParentCallback(Callback::class.java)?.onScrollTimerClick() ?: return
findParentCallback(Callback::class.java)?.onScrollTimerClick(false) ?: return
dismissAllowingStateLoss()
}
@@ -133,6 +144,10 @@ class ReaderConfigSheet :
orientationHelper.isLandscape = !orientationHelper.isLandscape
}
R.id.button_bookmark -> {
viewModel.toggleBookmark()
}
R.id.button_color_filter -> {
val page = viewModel.getCurrentPage() ?: return
val manga = viewModel.getMangaOrNull() ?: return
@@ -219,6 +234,8 @@ class ReaderConfigSheet :
fun onSavePageClick()
fun onScrollTimerClick()
fun onScrollTimerClick(isLongClick: Boolean)
fun onBookmarkClick()
}
}

View File

@@ -6,7 +6,8 @@
tools:layout_height="wrap_content"
tools:layout_width="match_parent"
tools:orientation="horizontal"
tools:parentTag="android.widget.LinearLayout">
tools:parentTag="android.widget.LinearLayout"
tools:style="?dockedToolbarStyle">
<FrameLayout
android:layout_width="wrap_content"
@@ -14,9 +15,10 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/button_prev"
style="@style/Widget.Kotatsu.IconButton.Action"
style="?materialIconButtonStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/prev_chapter"
app:icon="@drawable/ic_prev" />
</FrameLayout>
@@ -39,9 +41,10 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/button_next"
style="@style/Widget.Kotatsu.IconButton.Action"
style="?materialIconButtonStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/next_chapter"
app:icon="@drawable/ic_next" />
</FrameLayout>
@@ -52,9 +55,10 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/button_save"
style="@style/Widget.Kotatsu.IconButton.Action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?materialIconButtonStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/save_page"
app:icon="@drawable/ic_save" />
</FrameLayout>
@@ -65,10 +69,11 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/button_timer"
style="@style/Widget.Kotatsu.IconButton.Action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/save_page"
style="?materialIconButtonStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/automatic_scroll"
app:icon="@drawable/ic_timer" />
</FrameLayout>
@@ -78,9 +83,10 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/button_screen_rotation"
style="@style/Widget.Kotatsu.IconButton.Action"
style="?materialIconButtonStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/screen_orientation"
app:icon="@drawable/ic_screen_rotation" />
</FrameLayout>
@@ -90,10 +96,25 @@
android:layout_height="wrap_content">
<com.google.android.material.button.MaterialButton
android:id="@+id/button_pages_thumbs"
style="@style/Widget.Kotatsu.IconButton.Action"
android:id="@+id/button_bookmark"
style="?materialIconButtonStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/bookmark_add"
app:icon="@drawable/ic_bookmark" />
</FrameLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.google.android.material.button.MaterialButton
android:id="@+id/button_pages_thumbs"
style="?materialIconButtonStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/pages"
app:icon="@drawable/ic_grid" />
</FrameLayout>
@@ -104,9 +125,10 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/button_options"
style="@style/Widget.Kotatsu.IconButton.Action"
style="?materialIconButtonStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/options"
app:icon="@drawable/abc_ic_menu_overflow_material" />
</FrameLayout>

View File

@@ -38,32 +38,27 @@
app:drawableStartCompat="@drawable/ic_save" />
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_screen_rotate"
android:id="@+id/button_bookmark"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/rotate_screen"
android:text="@string/bookmark_add"
android:textAppearance="?attr/textAppearanceButton"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_screen_rotation"
tools:visibility="visible" />
app:drawableStartCompat="@drawable/ic_bookmark" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_screen_lock_rotation"
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_image_server"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:ellipsize="end"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:singleLine="true"
android:text="@string/lock_screen_rotation"
android:text="@string/image_server"
android:textAppearance="?attr/textAppearanceButton"
android:textColor="?colorOnSurfaceVariant"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_screen_rotation_lock"
app:drawableStartCompat="@drawable/ic_images"
tools:visibility="visible" />
<TextView
@@ -137,15 +132,42 @@
android:layout_height="?android:listPreferredItemHeightSmall"
android:layout_marginTop="@dimen/margin_normal"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:ellipsize="end"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:singleLine="true"
android:text="@string/use_two_pages_landscape"
android:textAppearance="?attr/textAppearanceButton"
android:textColor="?colorOnSurfaceVariant"
app:drawableStartCompat="@drawable/ic_split_horizontal" />
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_screen_rotate"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/rotate_screen"
android:textAppearance="?attr/textAppearanceButton"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_screen_rotation"
tools:visibility="visible" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_screen_lock_rotation"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:ellipsize="end"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:singleLine="true"
android:text="@string/lock_screen_rotation"
android:textAppearance="?attr/textAppearanceButton"
android:textColor="?colorOnSurfaceVariant"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_screen_rotation_lock"
tools:visibility="visible" />
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_scroll_timer"
android:layout_width="match_parent"
@@ -168,19 +190,6 @@
android:textAppearance="?attr/textAppearanceButton"
app:drawableStartCompat="@drawable/ic_appearance" />
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_image_server"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/image_server"
android:textAppearance="?attr/textAppearanceButton"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_images"
tools:visibility="visible" />
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_settings"
android:layout_width="match_parent"

View File

@@ -4,10 +4,10 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_bookmark"
android:enabled="false"
android:icon="@drawable/ic_bookmark"
android:title="@string/bookmark_add"
android:id="@+id/action_info"
android:icon="@drawable/ic_info_outline"
android:title="@string/details"
android:visible="false"
app:showAsAction="always" />
</menu>

View File

@@ -132,6 +132,7 @@
<item>@string/screen_orientation</item>
<item>@string/save_page</item>
<item>@string/automatic_scroll</item>
<item>@string/bookmark_add</item>
</string-array>
<string-array name="list_badges" translatable="false">
<item>@string/favourites</item>

View File

@@ -118,10 +118,6 @@
<item name="android:minHeight">42dp</item>
</style>
<style name="Widget.Kotatsu.IconButton.Action" parent="Widget.Material3.Button.IconButton">
<item name="iconTint">?colorControlNormal</item>
</style>
<style name="Widget.Kotatsu.ToggleButton" parent="Widget.Material3.Button.OutlinedButton">
<item name="android:checkable">true</item>
<item name="android:textAlignment">textStart</item>
@@ -132,8 +128,6 @@
<item name="android:textAlignment">center</item>
<item name="iconPadding">2dp</item>
<item name="android:singleLine">false</item>
<item name="android:lines">2</item>
<item name="android:maxLines">2</item>
<item name="iconGravity">top</item>
<item name="android:paddingTop">12dp</item>
<item name="android:paddingBottom">10dp</item>