Update reader actions bar
parent
5d91e20844
commit
09590cfab0
@ -0,0 +1,239 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.database.ContentObserver
|
||||
import android.provider.Settings
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.google.android.material.slider.Slider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderControl
|
||||
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.Companion.TAB_PAGES
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderControlDelegate.OnInteractionListener
|
||||
import javax.inject.Inject
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ReaderActionsView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
@AttrRes defStyleAttr: Int = 0,
|
||||
) : LinearLayout(context, attrs, defStyleAttr),
|
||||
View.OnClickListener,
|
||||
SharedPreferences.OnSharedPreferenceChangeListener,
|
||||
Slider.OnChangeListener,
|
||||
Slider.OnSliderTouchListener {
|
||||
|
||||
@Inject
|
||||
lateinit var settings: AppSettings
|
||||
|
||||
private val binding = LayoutReaderActionsBinding.inflate(LayoutInflater.from(context), this)
|
||||
private val rotationObserver = object : ContentObserver(handler) {
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
updateRotationButton()
|
||||
}
|
||||
}
|
||||
private var isSliderChanged = false
|
||||
private var isSliderTracking = false
|
||||
|
||||
var isSliderEnabled: Boolean
|
||||
get() = binding.slider.isEnabled
|
||||
set(value) {
|
||||
binding.slider.isEnabled = value
|
||||
binding.slider.setThumbVisible(value)
|
||||
}
|
||||
|
||||
var isNextEnabled: Boolean
|
||||
get() = binding.buttonNext.isEnabled
|
||||
set(value) {
|
||||
binding.buttonNext.isEnabled = value
|
||||
}
|
||||
|
||||
var isPrevEnabled: Boolean
|
||||
get() = binding.buttonPrev.isEnabled
|
||||
set(value) {
|
||||
binding.buttonPrev.isEnabled = value
|
||||
}
|
||||
|
||||
var listener: OnInteractionListener? = null
|
||||
|
||||
init {
|
||||
orientation = HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
binding.buttonNext.initAction()
|
||||
binding.buttonPrev.initAction()
|
||||
binding.buttonSave.initAction()
|
||||
binding.buttonOptions.initAction()
|
||||
binding.buttonScreenRotation.initAction()
|
||||
binding.buttonPagesThumbs.initAction()
|
||||
binding.slider.setLabelFormatter(PageLabelFormatter())
|
||||
binding.slider.addOnChangeListener(this)
|
||||
binding.slider.addOnSliderTouchListener(this)
|
||||
updateControlsVisibility()
|
||||
updatePagesSheetButton()
|
||||
updateRotationButton()
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
settings.subscribe(this)
|
||||
context.contentResolver.registerContentObserver(
|
||||
Settings.System.CONTENT_URI, true, rotationObserver,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
settings.unsubscribe(this)
|
||||
context.contentResolver.unregisterContentObserver(rotationObserver)
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.button_prev -> listener?.switchChapterBy(-1)
|
||||
R.id.button_next -> listener?.switchChapterBy(1)
|
||||
R.id.button_save -> listener?.onSavePageClick()
|
||||
R.id.button_pages_thumbs -> AppRouter.from(this)?.showChapterPagesSheet()
|
||||
R.id.button_screen_rotation -> listener?.toggleScreenOrientation()
|
||||
R.id.button_options -> listener?.openMenu()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
if (isSliderTracking) {
|
||||
isSliderChanged = true
|
||||
} else {
|
||||
listener?.switchPageTo(value.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(slider: Slider) {
|
||||
isSliderChanged = false
|
||||
isSliderTracking = true
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(slider: Slider) {
|
||||
isSliderTracking = false
|
||||
if (isSliderChanged) {
|
||||
listener?.switchPageTo(slider.value.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
when (key) {
|
||||
AppSettings.KEY_READER_CONTROLS -> updateControlsVisibility()
|
||||
AppSettings.KEY_PAGES_TAB,
|
||||
AppSettings.KEY_DETAILS_TAB,
|
||||
AppSettings.KEY_DETAILS_LAST_TAB -> updatePagesSheetButton()
|
||||
}
|
||||
}
|
||||
|
||||
fun setSliderValue(value: Int, max: Int) {
|
||||
binding.slider.valueTo = max.toFloat()
|
||||
binding.slider.setValueRounded(value.toFloat())
|
||||
}
|
||||
|
||||
fun setSliderReversed(reversed: Boolean) {
|
||||
binding.slider.isRtl = reversed != isRtl
|
||||
}
|
||||
|
||||
private fun updateControlsVisibility() {
|
||||
val controls = settings.readerControls
|
||||
binding.buttonPrev.isVisible = ReaderControl.PREV_CHAPTER in controls
|
||||
binding.buttonNext.isVisible = ReaderControl.NEXT_CHAPTER in controls
|
||||
binding.buttonPagesThumbs.isVisible = ReaderControl.PAGES_SHEET in controls
|
||||
binding.buttonScreenRotation.isVisible = ReaderControl.SCREEN_ROTATION in controls
|
||||
binding.buttonSave.isVisible = ReaderControl.SAVE_PAGE in controls
|
||||
binding.slider.isVisible = ReaderControl.SLIDER in controls
|
||||
adjustLayoutParams()
|
||||
}
|
||||
|
||||
private fun updatePagesSheetButton() {
|
||||
val isPagesMode = settings.defaultDetailsTab == TAB_PAGES
|
||||
val button = binding.buttonPagesThumbs
|
||||
button.setIconResource(
|
||||
if (isPagesMode) R.drawable.ic_grid else R.drawable.ic_list,
|
||||
)
|
||||
button.setTitle(
|
||||
if (isPagesMode) R.string.pages else R.string.chapters,
|
||||
)
|
||||
}
|
||||
|
||||
private fun adjustLayoutParams() {
|
||||
val isSliderVisible = binding.slider.isVisible
|
||||
repeat(childCount) { i ->
|
||||
val child = getChildAt(i)
|
||||
if (child is FrameLayout) {
|
||||
child.updateLayoutParams<LayoutParams> {
|
||||
width = if (isSliderVisible) LayoutParams.WRAP_CONTENT else 0
|
||||
weight = if (isSliderVisible) 0f else 1f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRotationButton() {
|
||||
val button = binding.buttonScreenRotation
|
||||
when {
|
||||
!button.isVisible -> return
|
||||
isAutoRotationEnabled() -> {
|
||||
button.setTitle(R.string.lock_screen_rotation)
|
||||
button.setIconResource(R.drawable.ic_screen_rotation_lock)
|
||||
}
|
||||
|
||||
else -> {
|
||||
button.setTitle(R.string.rotate_screen)
|
||||
button.setIconResource(R.drawable.ic_screen_rotation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Button.initAction() {
|
||||
setOnClickListener(this@ReaderActionsView)
|
||||
ViewCompat.setTooltipText(this, contentDescription)
|
||||
}
|
||||
|
||||
private fun Button.setTitle(@StringRes titleResId: Int) {
|
||||
val text = resources.getString(titleResId)
|
||||
contentDescription = text
|
||||
ViewCompat.setTooltipText(this, text)
|
||||
}
|
||||
|
||||
private fun isAutoRotationEnabled(): Boolean = Settings.System.getInt(
|
||||
context.contentResolver,
|
||||
Settings.System.ACCELEROMETER_ROTATION,
|
||||
0,
|
||||
) == 1
|
||||
|
||||
private fun Slider.setThumbVisible(visible: Boolean) {
|
||||
thumbWidth = if (visible) {
|
||||
resources.getDimensionPixelSize(materialR.dimen.m3_comp_slider_active_handle_width)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
thumbHeight = if (visible) {
|
||||
resources.getDimensionPixelSize(materialR.dimen.m3_comp_slider_active_handle_height)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.nav.router
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderControl
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderConfigSheet
|
||||
|
||||
class ReaderMenuBottomProvider(
|
||||
private val activity: FragmentActivity,
|
||||
private val readerManager: ReaderManager,
|
||||
private val screenOrientationHelper: ScreenOrientationHelper,
|
||||
private val configCallback: ReaderConfigSheet.Callback,
|
||||
private val viewModel: ReaderViewModel,
|
||||
) : MenuProvider {
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.opt_reader_bottom, menu)
|
||||
onPrepareMenu(menu) // fix, not called in toolbar
|
||||
}
|
||||
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
val readerControls = viewModel.readerControls.value
|
||||
val hasPages = viewModel.content.value.pages.isNotEmpty()
|
||||
val isPagesSheetEnabled = hasPages && ReaderControl.PAGES_SHEET in readerControls
|
||||
menu.findItem(R.id.action_pages_thumbs).run {
|
||||
isVisible = isPagesSheetEnabled
|
||||
if (isPagesSheetEnabled) {
|
||||
setIcon(if (viewModel.isPagesSheetEnabled.value) R.drawable.ic_grid else R.drawable.ic_list)
|
||||
}
|
||||
}
|
||||
menu.findItem(R.id.action_screen_rotation).run {
|
||||
isVisible = ReaderControl.SCREEN_ROTATION in readerControls
|
||||
when {
|
||||
!isVisible -> Unit
|
||||
!screenOrientationHelper.isAutoRotationEnabled -> {
|
||||
setTitle(R.string.rotate_screen)
|
||||
setIcon(R.drawable.ic_screen_rotation)
|
||||
}
|
||||
|
||||
else -> {
|
||||
setTitle(R.string.lock_screen_rotation)
|
||||
setIcon(R.drawable.ic_screen_rotation_lock)
|
||||
}
|
||||
}
|
||||
}
|
||||
menu.findItem(R.id.action_save_page)?.run {
|
||||
isVisible = hasPages && ReaderControl.SAVE_PAGE in readerControls
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_screen_rotation -> {
|
||||
toggleScreenRotation()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_save_page -> {
|
||||
configCallback.onSavePageClick()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_pages_thumbs -> {
|
||||
activity.router.showChapterPagesSheet()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_options -> {
|
||||
viewModel.saveCurrentState(readerManager.currentReader?.getCurrentState())
|
||||
val currentMode = readerManager.currentMode ?: return false
|
||||
activity.router.showReaderConfigSheet(currentMode)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_bookmark -> {
|
||||
if (viewModel.isBookmarkAdded.value) {
|
||||
viewModel.removeBookmark()
|
||||
} else {
|
||||
viewModel.addBookmark()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleScreenRotation() = with(screenOrientationHelper) {
|
||||
if (isAutoRotationEnabled) {
|
||||
val newValue = !isLocked
|
||||
isLocked = newValue
|
||||
Toast.makeText(
|
||||
activity,
|
||||
if (newValue) {
|
||||
R.string.screen_rotation_locked
|
||||
} else {
|
||||
R.string.screen_rotation_unlocked
|
||||
},
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
} else {
|
||||
isLandscape = !isLandscape
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import com.google.android.material.slider.Slider
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
|
||||
class ReaderSliderListener(
|
||||
private val viewModel: ReaderViewModel,
|
||||
private val callback: ReaderNavigationCallback,
|
||||
) : Slider.OnChangeListener, Slider.OnSliderTouchListener {
|
||||
|
||||
private var isChanged = false
|
||||
private var isTracking = false
|
||||
|
||||
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
if (isTracking) {
|
||||
isChanged = true
|
||||
} else {
|
||||
switchPageToIndex(value.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(slider: Slider) {
|
||||
isChanged = false
|
||||
isTracking = true
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(slider: Slider) {
|
||||
isTracking = false
|
||||
if (isChanged) {
|
||||
switchPageToIndex(slider.value.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
fun attachToSlider(slider: Slider) {
|
||||
slider.addOnChangeListener(this)
|
||||
slider.addOnSliderTouchListener(this)
|
||||
}
|
||||
|
||||
private fun switchPageToIndex(index: Int) {
|
||||
val pages = viewModel.getCurrentChapterPages()
|
||||
val page = pages?.getOrNull(index) ?: return
|
||||
val chapterId = viewModel.getCurrentState()?.chapterId ?: return
|
||||
callback.onPageSelected(ReaderPage(page, index, chapterId))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:layout_height="wrap_content"
|
||||
tools:layout_width="match_parent"
|
||||
tools:orientation="horizontal"
|
||||
tools:parentTag="android.widget.LinearLayout">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_prev"
|
||||
style="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/prev_chapter"
|
||||
app:icon="@drawable/ic_prev" />
|
||||
</FrameLayout>
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:stepSize="1.0"
|
||||
android:valueFrom="0"
|
||||
android:visibility="visible"
|
||||
app:labelBehavior="floating"
|
||||
tools:value="6"
|
||||
tools:valueTo="20" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_next"
|
||||
style="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/next_chapter"
|
||||
app:icon="@drawable/ic_next" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<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"
|
||||
android:contentDescription="@string/save_page"
|
||||
app:icon="@drawable/ic_save" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_screen_rotation"
|
||||
style="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/screen_orientation"
|
||||
app:icon="@drawable/ic_screen_rotation" />
|
||||
</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="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/pages"
|
||||
app:icon="@drawable/ic_grid" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_options"
|
||||
style="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/options"
|
||||
app:icon="@drawable/abc_ic_menu_overflow_material" />
|
||||
</FrameLayout>
|
||||
|
||||
</merge>
|
||||
@ -1,39 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="AlwaysShowAction">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_save_page"
|
||||
android:icon="@drawable/ic_save"
|
||||
android:title="@string/save_page"
|
||||
android:visible="false"
|
||||
app:showAsAction="always"
|
||||
tools:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_screen_rotation"
|
||||
android:icon="@drawable/ic_screen_rotation"
|
||||
android:title="@string/screen_orientation"
|
||||
android:visible="false"
|
||||
app:showAsAction="always"
|
||||
tools:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_pages_thumbs"
|
||||
android:icon="@drawable/ic_grid"
|
||||
android:title="@string/pages"
|
||||
android:visible="false"
|
||||
app:showAsAction="always"
|
||||
tools:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_options"
|
||||
android:icon="@drawable/abc_ic_menu_overflow_material"
|
||||
android:title="@string/options"
|
||||
app:showAsAction="always"
|
||||
tools:visible="true" />
|
||||
|
||||
</menu>
|
||||
Loading…
Reference in New Issue