Configurable reader tap actions
This commit is contained in:
@@ -145,6 +145,9 @@
|
||||
<data android:host="sync-settings" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.settings.reader.ReaderTapGridConfigActivity"
|
||||
android:label="@string/reader_actions" />
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.settings.storage.directories.MangaDirectoriesActivity"
|
||||
android:label="@string/local_manga_directories" />
|
||||
|
||||
@@ -101,15 +101,12 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
val readerPageSwitch: Set<String>
|
||||
get() = prefs.getStringSet(KEY_READER_SWITCHERS, null) ?: setOf(PAGE_SWITCH_TAPS)
|
||||
val isReaderVolumeButtonsEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_READER_VOLUME_BUTTONS, false)
|
||||
|
||||
val isReaderZoomButtonsEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_READER_ZOOM_BUTTONS, false)
|
||||
|
||||
val isReaderTapsAdaptive: Boolean
|
||||
get() = !prefs.getBoolean(KEY_READER_TAPS_LTR, false)
|
||||
|
||||
val isReaderOptimizationEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_READER_OPTIMIZE, false)
|
||||
|
||||
@@ -453,7 +450,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
|
||||
companion object {
|
||||
|
||||
const val PAGE_SWITCH_TAPS = "taps"
|
||||
const val PAGE_SWITCH_VOLUME_KEYS = "volume"
|
||||
|
||||
const val TRACK_HISTORY = "history"
|
||||
@@ -476,8 +472,8 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_GRID_SIZE = "grid_size"
|
||||
const val KEY_REMOTE_SOURCES = "remote_sources"
|
||||
const val KEY_LOCAL_STORAGE = "local_storage"
|
||||
const val KEY_READER_SWITCHERS = "reader_switchers"
|
||||
const val KEY_READER_ZOOM_BUTTONS = "reader_zoom_buttons"
|
||||
const val KEY_READER_VOLUME_BUTTONS = "reader_volume_buttons"
|
||||
const val KEY_TRACKER_ENABLED = "tracker_enabled"
|
||||
const val KEY_TRACKER_WIFI_ONLY = "tracker_wifi"
|
||||
const val KEY_TRACK_SOURCES = "track_sources"
|
||||
@@ -530,7 +526,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_READER_BACKGROUND = "reader_background"
|
||||
const val KEY_READER_SCREEN_ON = "reader_screen_on"
|
||||
const val KEY_SHORTCUTS = "dynamic_shortcuts"
|
||||
const val KEY_READER_TAPS_LTR = "reader_taps_ltr"
|
||||
const val KEY_READER_TAP_ACTIONS = "reader_tap_actions"
|
||||
const val KEY_READER_OPTIMIZE = "reader_optimize"
|
||||
const val KEY_LOCAL_LIST_ORDER = "local_order"
|
||||
const val KEY_HISTORY_ORDER = "history_order"
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
package org.koitharu.kotatsu.core.util
|
||||
|
||||
import android.content.Context
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class GridTouchHelper(
|
||||
context: Context,
|
||||
private val listener: OnGridTouchListener,
|
||||
) : GestureDetector.SimpleOnGestureListener() {
|
||||
|
||||
private val detector = GestureDetector(context, this)
|
||||
private val width = context.resources.displayMetrics.widthPixels
|
||||
private val height = context.resources.displayMetrics.heightPixels
|
||||
private var isDispatching = false
|
||||
|
||||
init {
|
||||
detector.setIsLongpressEnabled(true)
|
||||
detector.setOnDoubleTapListener(this)
|
||||
}
|
||||
|
||||
fun dispatchTouchEvent(event: MotionEvent) {
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
isDispatching = listener.onProcessTouch(event.rawX.toInt(), event.rawY.toInt())
|
||||
}
|
||||
detector.onTouchEvent(event)
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
|
||||
if (!isDispatching) {
|
||||
return true
|
||||
}
|
||||
val xIndex = (event.rawX * 2f / width).roundToInt()
|
||||
val yIndex = (event.rawY * 2f / height).roundToInt()
|
||||
listener.onGridTouch(
|
||||
when (xIndex) {
|
||||
0 -> AREA_LEFT
|
||||
1 -> {
|
||||
when (yIndex) {
|
||||
0 -> AREA_TOP
|
||||
1 -> AREA_CENTER
|
||||
2 -> AREA_BOTTOM
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
2 -> AREA_RIGHT
|
||||
else -> return false
|
||||
},
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onLongPress(event: MotionEvent) {
|
||||
super.onLongPress(event)
|
||||
val xIndex = (event.rawX * 2f / width).roundToInt()
|
||||
val yIndex = (event.rawY * 2f / height).roundToInt()
|
||||
listener.onGridLongTouch(
|
||||
when(xIndex) {
|
||||
1 -> {
|
||||
when (yIndex) {
|
||||
1 -> AREA_CENTER
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
else -> -1
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val AREA_CENTER = 1
|
||||
const val AREA_LEFT = 2
|
||||
const val AREA_RIGHT = 3
|
||||
const val AREA_TOP = 4
|
||||
const val AREA_BOTTOM = 5
|
||||
}
|
||||
|
||||
interface OnGridTouchListener {
|
||||
|
||||
fun onGridTouch(area: Int)
|
||||
|
||||
fun onGridLongTouch(area: Int)
|
||||
|
||||
fun onProcessTouch(rawX: Int, rawY: Int): Boolean
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.koitharu.kotatsu.reader.data
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import org.koitharu.kotatsu.core.util.ext.getEnumValue
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.putEnumValue
|
||||
import org.koitharu.kotatsu.reader.domain.TapGridArea
|
||||
import org.koitharu.kotatsu.reader.ui.tapgrid.TapAction
|
||||
import javax.inject.Inject
|
||||
|
||||
class TapGridSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
|
||||
private val prefs = context.getSharedPreferences("tap_grid", Context.MODE_PRIVATE)
|
||||
|
||||
fun getTapAction(area: TapGridArea, isLongTap: Boolean): TapAction? {
|
||||
val key = getPrefKey(area, isLongTap)
|
||||
return if (!isLongTap && key !in prefs) {
|
||||
getDefaultTapAction(area)
|
||||
} else {
|
||||
prefs.getEnumValue(key, TapAction::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun setTapAction(area: TapGridArea, isLongTap: Boolean, action: TapAction?) {
|
||||
val key = getPrefKey(area, isLongTap)
|
||||
prefs.edit { putEnumValue(key, action) }
|
||||
}
|
||||
|
||||
fun observe() = prefs.observe().flowOn(Dispatchers.IO)
|
||||
|
||||
private fun getPrefKey(area: TapGridArea, isLongTap: Boolean): String = if (isLongTap) {
|
||||
area.name + "_long"
|
||||
} else {
|
||||
area.name
|
||||
}
|
||||
|
||||
private fun getDefaultTapAction(area: TapGridArea): TapAction = when (area) {
|
||||
TapGridArea.TOP_LEFT,
|
||||
TapGridArea.TOP_CENTER,
|
||||
TapGridArea.CENTER_LEFT,
|
||||
TapGridArea.BOTTOM_LEFT -> TapAction.PAGE_PREV
|
||||
|
||||
TapGridArea.CENTER -> TapAction.TOGGLE_UI
|
||||
TapGridArea.TOP_RIGHT,
|
||||
TapGridArea.CENTER_RIGHT,
|
||||
TapGridArea.BOTTOM_CENTER,
|
||||
TapGridArea.BOTTOM_RIGHT -> TapAction.PAGE_NEXT
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.koitharu.kotatsu.reader.domain
|
||||
|
||||
enum class TapGridArea {
|
||||
|
||||
TOP_LEFT,
|
||||
TOP_CENTER,
|
||||
TOP_RIGHT,
|
||||
CENTER_LEFT,
|
||||
CENTER,
|
||||
CENTER_RIGHT,
|
||||
BOTTOM_LEFT,
|
||||
BOTTOM_CENTER,
|
||||
BOTTOM_RIGHT;
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -41,10 +40,8 @@ import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.core.ui.BaseFullscreenActivity
|
||||
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
||||
import org.koitharu.kotatsu.core.ui.widgets.ZoomControl
|
||||
import org.koitharu.kotatsu.core.util.GridTouchHelper
|
||||
import org.koitharu.kotatsu.core.util.IdlingDetector
|
||||
import org.koitharu.kotatsu.core.util.ShareHelper
|
||||
import org.koitharu.kotatsu.core.util.ext.DIALOG_THEME_CENTERED
|
||||
import org.koitharu.kotatsu.core.util.ext.hasGlobalPoint
|
||||
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
|
||||
import org.koitharu.kotatsu.core.util.ext.isRtl
|
||||
@@ -57,9 +54,12 @@ import org.koitharu.kotatsu.databinding.ActivityReaderBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.reader.data.TapGridSettings
|
||||
import org.koitharu.kotatsu.reader.domain.TapGridArea
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderConfigSheet
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState
|
||||
import org.koitharu.kotatsu.reader.ui.tapgrid.TapGridDispatcher
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.OnPageSelectListener
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
@@ -68,7 +68,7 @@ import javax.inject.Inject
|
||||
class ReaderActivity :
|
||||
BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
ChaptersSheet.OnChapterChangeListener,
|
||||
GridTouchHelper.OnGridTouchListener,
|
||||
TapGridDispatcher.OnGridTouchListener,
|
||||
OnPageSelectListener,
|
||||
ReaderConfigSheet.Callback,
|
||||
ReaderControlDelegate.OnInteractionListener,
|
||||
@@ -79,6 +79,9 @@ class ReaderActivity :
|
||||
@Inject
|
||||
lateinit var settings: AppSettings
|
||||
|
||||
@Inject
|
||||
lateinit var tapGridSettings: TapGridSettings
|
||||
|
||||
private val idlingDetector = IdlingDetector(TimeUnit.SECONDS.toMillis(10), this)
|
||||
|
||||
private val viewModel: ReaderViewModel by viewModels()
|
||||
@@ -96,7 +99,7 @@ class ReaderActivity :
|
||||
lateinit var scrollTimerFactory: ScrollTimer.Factory
|
||||
|
||||
private lateinit var scrollTimer: ScrollTimer
|
||||
private lateinit var touchHelper: GridTouchHelper
|
||||
private lateinit var touchHelper: TapGridDispatcher
|
||||
private lateinit var controlDelegate: ReaderControlDelegate
|
||||
private var gestureInsets: Insets = Insets.NONE
|
||||
private lateinit var readerManager: ReaderManager
|
||||
@@ -107,9 +110,9 @@ class ReaderActivity :
|
||||
setContentView(ActivityReaderBinding.inflate(layoutInflater))
|
||||
readerManager = ReaderManager(supportFragmentManager, viewBinding.container)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
touchHelper = GridTouchHelper(this, this)
|
||||
touchHelper = TapGridDispatcher(this, this)
|
||||
scrollTimer = scrollTimerFactory.create(this, this)
|
||||
controlDelegate = ReaderControlDelegate(resources, settings, this, this)
|
||||
controlDelegate = ReaderControlDelegate(resources, settings, tapGridSettings, this)
|
||||
viewBinding.slider.setLabelFormatter(PageLabelFormatter())
|
||||
viewBinding.zoomControl.listener = this
|
||||
ReaderSliderListener(this, viewModel).attachToSlider(viewBinding.slider)
|
||||
@@ -202,12 +205,12 @@ class ReaderActivity :
|
||||
viewBinding.toolbarBottom.invalidateMenu()
|
||||
}
|
||||
|
||||
override fun onGridTouch(area: Int) {
|
||||
controlDelegate.onGridTouch(area, viewBinding.container)
|
||||
override fun onGridTouch(area: TapGridArea): Boolean {
|
||||
return controlDelegate.onGridTouch(area)
|
||||
}
|
||||
|
||||
override fun onGridLongTouch(area: Int) {
|
||||
controlDelegate.onGridLongTouch(area, viewBinding.container)
|
||||
override fun onGridLongTouch(area: TapGridArea) {
|
||||
controlDelegate.onGridLongTouch(area)
|
||||
}
|
||||
|
||||
override fun onProcessTouch(rawX: Int, rawY: Int): Boolean {
|
||||
@@ -233,7 +236,7 @@ class ReaderActivity :
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return controlDelegate.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event)
|
||||
return controlDelegate.onKeyDown(keyCode) || super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
@@ -334,6 +337,16 @@ class ReaderActivity :
|
||||
readerManager.currentReader?.switchPageBy(delta)
|
||||
}
|
||||
|
||||
override fun switchChapterBy(delta: Int) {
|
||||
viewModel.switchChapterBy(delta)
|
||||
}
|
||||
|
||||
override fun openMenu() {
|
||||
viewModel.saveCurrentState(readerManager.currentReader?.getCurrentState())
|
||||
val currentMode = readerManager.currentMode ?: return
|
||||
ReaderConfigSheet.show(supportFragmentManager, currentMode)
|
||||
}
|
||||
|
||||
override fun scrollBy(delta: Int, smooth: Boolean): Boolean {
|
||||
return readerManager.currentReader?.scrollBy(delta, smooth) ?: false
|
||||
}
|
||||
@@ -342,13 +355,6 @@ class ReaderActivity :
|
||||
setUiIsVisible(!viewBinding.appbarTop.isVisible)
|
||||
}
|
||||
|
||||
override fun viewDialog() {
|
||||
MaterialAlertDialogBuilder(this, DIALOG_THEME_CENTERED)
|
||||
.setMessage("Called dialog on long press")
|
||||
.setPositiveButton(R.string.got_it, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun isReaderResumed(): Boolean {
|
||||
val reader = readerManager.currentReader ?: return false
|
||||
return reader.isResumed && supportFragmentManager.fragments.lastOrNull() === reader
|
||||
|
||||
@@ -1,92 +1,49 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Resources
|
||||
import android.view.KeyEvent
|
||||
import android.view.SoundEffectConstants
|
||||
import android.view.View
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.core.util.GridTouchHelper
|
||||
import org.koitharu.kotatsu.reader.data.TapGridSettings
|
||||
import org.koitharu.kotatsu.reader.domain.TapGridArea
|
||||
import org.koitharu.kotatsu.reader.ui.tapgrid.TapAction
|
||||
|
||||
class ReaderControlDelegate(
|
||||
resources: Resources,
|
||||
private val settings: AppSettings,
|
||||
private val tapGridSettings: TapGridSettings,
|
||||
private val listener: OnInteractionListener,
|
||||
owner: LifecycleOwner,
|
||||
) : DefaultLifecycleObserver, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
) {
|
||||
|
||||
private var isTapSwitchEnabled: Boolean = true
|
||||
private var isVolumeKeysSwitchEnabled: Boolean = false
|
||||
private var isReaderTapsAdaptive: Boolean = true
|
||||
private var minScrollDelta = resources.getDimensionPixelSize(R.dimen.reader_scroll_delta_min)
|
||||
|
||||
init {
|
||||
owner.lifecycle.addObserver(this)
|
||||
settings.subscribe(this)
|
||||
updateSettings()
|
||||
fun onGridTouch(area: TapGridArea): Boolean {
|
||||
val action = tapGridSettings.getTapAction(
|
||||
area = area,
|
||||
isLongTap = false,
|
||||
) ?: return false
|
||||
processAction(action)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
settings.unsubscribe(this)
|
||||
owner.lifecycle.removeObserver(this)
|
||||
super.onDestroy(owner)
|
||||
fun onGridLongTouch(area: TapGridArea) {
|
||||
val action = tapGridSettings.getTapAction(
|
||||
area = area,
|
||||
isLongTap = true,
|
||||
) ?: return
|
||||
processAction(action)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
updateSettings()
|
||||
}
|
||||
|
||||
fun onGridTouch(area: Int, view: View) {
|
||||
when (area) {
|
||||
GridTouchHelper.AREA_CENTER -> {
|
||||
listener.toggleUiVisibility()
|
||||
view.playSoundEffect(SoundEffectConstants.CLICK)
|
||||
}
|
||||
|
||||
GridTouchHelper.AREA_TOP -> if (isTapSwitchEnabled) {
|
||||
listener.switchPageBy(-1)
|
||||
view.playSoundEffect(SoundEffectConstants.NAVIGATION_UP)
|
||||
}
|
||||
|
||||
GridTouchHelper.AREA_LEFT -> if (isTapSwitchEnabled) {
|
||||
listener.switchPageBy(if (isReaderTapsReversed()) 1 else -1)
|
||||
view.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT)
|
||||
}
|
||||
|
||||
GridTouchHelper.AREA_BOTTOM -> if (isTapSwitchEnabled) {
|
||||
listener.switchPageBy(1)
|
||||
view.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN)
|
||||
}
|
||||
|
||||
GridTouchHelper.AREA_RIGHT -> if (isTapSwitchEnabled) {
|
||||
listener.switchPageBy(if (isReaderTapsReversed()) -1 else 1)
|
||||
view.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onGridLongTouch(area: Int, view: View) {
|
||||
when (area) {
|
||||
GridTouchHelper.AREA_CENTER -> {
|
||||
listener.viewDialog()
|
||||
view.playSoundEffect(SoundEffectConstants.CLICK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onKeyDown(keyCode: Int, @Suppress("UNUSED_PARAMETER") event: KeyEvent?): Boolean = when (keyCode) {
|
||||
KeyEvent.KEYCODE_VOLUME_UP -> if (isVolumeKeysSwitchEnabled) {
|
||||
fun onKeyDown(keyCode: Int): Boolean = when (keyCode) {
|
||||
KeyEvent.KEYCODE_VOLUME_UP -> if (settings.isReaderVolumeButtonsEnabled) {
|
||||
listener.switchPageBy(-1)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN -> if (isVolumeKeysSwitchEnabled) {
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN -> if (settings.isReaderVolumeButtonsEnabled) {
|
||||
listener.switchPageBy(1)
|
||||
true
|
||||
} else {
|
||||
@@ -141,21 +98,23 @@ class ReaderControlDelegate(
|
||||
}
|
||||
|
||||
fun onKeyUp(keyCode: Int, @Suppress("UNUSED_PARAMETER") event: KeyEvent?): Boolean {
|
||||
return (
|
||||
isVolumeKeysSwitchEnabled &&
|
||||
(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)
|
||||
)
|
||||
return (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)
|
||||
&& settings.isReaderVolumeButtonsEnabled
|
||||
}
|
||||
|
||||
private fun updateSettings() {
|
||||
val switch = settings.readerPageSwitch
|
||||
isTapSwitchEnabled = AppSettings.PAGE_SWITCH_TAPS in switch
|
||||
isVolumeKeysSwitchEnabled = AppSettings.PAGE_SWITCH_VOLUME_KEYS in switch
|
||||
isReaderTapsAdaptive = settings.isReaderTapsAdaptive
|
||||
private fun processAction(action: TapAction) {
|
||||
when (action) {
|
||||
TapAction.PAGE_NEXT -> listener.switchPageBy(1)
|
||||
TapAction.PAGE_PREV -> listener.switchPageBy(-1)
|
||||
TapAction.CHAPTER_NEXT -> listener.switchChapterBy(1)
|
||||
TapAction.CHAPTER_PREV -> listener.switchChapterBy(-1)
|
||||
TapAction.TOGGLE_UI -> listener.toggleUiVisibility()
|
||||
TapAction.SHOW_MENU -> listener.openMenu()
|
||||
}
|
||||
}
|
||||
|
||||
private fun isReaderTapsReversed(): Boolean {
|
||||
return isReaderTapsAdaptive && listener.readerMode == ReaderMode.REVERSED
|
||||
return listener.readerMode == ReaderMode.REVERSED
|
||||
}
|
||||
|
||||
interface OnInteractionListener {
|
||||
@@ -164,12 +123,14 @@ class ReaderControlDelegate(
|
||||
|
||||
fun switchPageBy(delta: Int)
|
||||
|
||||
fun viewDialog()
|
||||
fun switchChapterBy(delta: Int)
|
||||
|
||||
fun scrollBy(delta: Int, smooth: Boolean): Boolean
|
||||
|
||||
fun toggleUiVisibility()
|
||||
|
||||
fun openMenu()
|
||||
|
||||
fun isReaderResumed(): Boolean
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +262,24 @@ class ReaderViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun switchChapterBy(delta: Int) {
|
||||
val prevJob = loadingJob
|
||||
loadingJob = launchLoadingJob(Dispatchers.Default) {
|
||||
prevJob?.cancelAndJoin()
|
||||
val currentChapterId = currentState.requireValue().chapterId
|
||||
val allChapters = checkNotNull(manga).allChapters
|
||||
var index = allChapters.indexOfFirst { x -> x.id == currentChapterId }
|
||||
if (index < 0) {
|
||||
return@launchLoadingJob
|
||||
}
|
||||
index += delta
|
||||
val newChapterId = (allChapters.getOrNull(index) ?: return@launchLoadingJob).id
|
||||
content.value = ReaderContent(emptyList(), null)
|
||||
chaptersLoader.loadSingleChapter(newChapterId)
|
||||
content.value = ReaderContent(chaptersLoader.snapshot(), ReaderState(newChapterId, 0, 0))
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
fun onCurrentPageChanged(lowerPos: Int, upperPos: Int) {
|
||||
val prevJob = stateChangeJob
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.koitharu.kotatsu.reader.ui.tapgrid
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
enum class TapAction(
|
||||
@StringRes val nameStringResId: Int,
|
||||
val color: Int,
|
||||
) {
|
||||
|
||||
PAGE_NEXT(R.string.next_page, 0x8BFF00),
|
||||
PAGE_PREV(R.string.prev_page, 0xFF4700),
|
||||
CHAPTER_NEXT(R.string.next_chapter, 0x327E49),
|
||||
CHAPTER_PREV(R.string.prev_chapter, 0x7E1218),
|
||||
TOGGLE_UI(R.string.toggle_ui, 0x3D69C5),
|
||||
SHOW_MENU(R.string.show_menu, 0xAA1AC5),
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.koitharu.kotatsu.reader.ui.tapgrid
|
||||
|
||||
import android.content.Context
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import org.koitharu.kotatsu.reader.domain.TapGridArea
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class TapGridDispatcher(
|
||||
context: Context,
|
||||
private val listener: OnGridTouchListener,
|
||||
) : GestureDetector.SimpleOnGestureListener() {
|
||||
|
||||
private val detector = GestureDetector(context, this)
|
||||
private val width = context.resources.displayMetrics.widthPixels
|
||||
private val height = context.resources.displayMetrics.heightPixels
|
||||
private var isDispatching = false
|
||||
|
||||
init {
|
||||
detector.setIsLongpressEnabled(true)
|
||||
detector.setOnDoubleTapListener(this)
|
||||
}
|
||||
|
||||
fun dispatchTouchEvent(event: MotionEvent) {
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
isDispatching = listener.onProcessTouch(event.rawX.toInt(), event.rawY.toInt())
|
||||
}
|
||||
detector.onTouchEvent(event)
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
|
||||
if (!isDispatching) {
|
||||
return true
|
||||
}
|
||||
return listener.onGridTouch(getArea(event.rawX, event.rawY))
|
||||
}
|
||||
|
||||
override fun onLongPress(event: MotionEvent) {
|
||||
if (isDispatching) {
|
||||
listener.onGridLongTouch(getArea(event.rawX, event.rawY))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getArea(x: Float, y: Float): TapGridArea {
|
||||
val xIndex = (x * 2f / width).roundToInt()
|
||||
val yIndex = (y * 2f / height).roundToInt()
|
||||
val area = when (xIndex) {
|
||||
0 -> when (yIndex) { // LEFT
|
||||
0 -> TapGridArea.TOP_LEFT
|
||||
1 -> TapGridArea.CENTER_LEFT
|
||||
2 -> TapGridArea.BOTTOM_LEFT
|
||||
else -> null
|
||||
}
|
||||
|
||||
1 -> when (yIndex) { // CENTER
|
||||
0 -> TapGridArea.TOP_CENTER
|
||||
1 -> TapGridArea.CENTER
|
||||
2 -> TapGridArea.BOTTOM_CENTER
|
||||
else -> null
|
||||
}
|
||||
|
||||
2 -> when (yIndex) { // RIGHT
|
||||
0 -> TapGridArea.TOP_RIGHT
|
||||
1 -> TapGridArea.CENTER_RIGHT
|
||||
2 -> TapGridArea.BOTTOM_RIGHT
|
||||
else -> null
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
return checkNotNull(area) { "Invalid area ($xIndex, $yIndex)" }
|
||||
}
|
||||
|
||||
interface OnGridTouchListener {
|
||||
|
||||
fun onGridTouch(area: TapGridArea): Boolean
|
||||
|
||||
fun onGridLongTouch(area: TapGridArea)
|
||||
|
||||
fun onProcessTouch(rawX: Int, rawY: Int): Boolean
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.koitharu.kotatsu.settings
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.MultiSelectListPreference
|
||||
import androidx.preference.Preference
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -16,7 +16,7 @@ import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.util.ext.setDefaultValueCompat
|
||||
import org.koitharu.kotatsu.parsers.util.names
|
||||
import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider
|
||||
import org.koitharu.kotatsu.settings.reader.ReaderTapGridConfigActivity
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ReaderSettingsFragment :
|
||||
@@ -26,7 +26,12 @@ class ReaderSettingsFragment :
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_reader)
|
||||
findPreference<ListPreference>(AppSettings.KEY_READER_MODE)?.run {
|
||||
entryValues = ReaderMode.entries.names()
|
||||
entryValues = arrayOf(
|
||||
ReaderMode.STANDARD.name,
|
||||
ReaderMode.REVERSED.name,
|
||||
ReaderMode.VERTICAL.name,
|
||||
ReaderMode.WEBTOON.name,
|
||||
)
|
||||
setDefaultValueCompat(ReaderMode.STANDARD.name)
|
||||
}
|
||||
findPreference<ListPreference>(AppSettings.KEY_READER_BACKGROUND)?.run {
|
||||
@@ -37,9 +42,6 @@ class ReaderSettingsFragment :
|
||||
entryValues = ReaderAnimation.entries.names()
|
||||
setDefaultValueCompat(ReaderAnimation.DEFAULT.name)
|
||||
}
|
||||
findPreference<MultiSelectListPreference>(AppSettings.KEY_READER_SWITCHERS)?.run {
|
||||
summaryProvider = MultiSummaryProvider(R.string.gestures_only)
|
||||
}
|
||||
findPreference<ListPreference>(AppSettings.KEY_ZOOM_MODE)?.run {
|
||||
entryValues = ZoomMode.entries.names()
|
||||
setDefaultValueCompat(ZoomMode.FIT_CENTER.name)
|
||||
@@ -57,6 +59,17 @@ class ReaderSettingsFragment :
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference): Boolean {
|
||||
return when (preference.key) {
|
||||
AppSettings.KEY_READER_TAP_ACTIONS -> {
|
||||
startActivity(Intent(preference.context, ReaderTapGridConfigActivity::class.java))
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onPreferenceTreeClick(preference)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
when (key) {
|
||||
AppSettings.KEY_READER_MODE -> updateReaderModeDependency()
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package org.koitharu.kotatsu.settings.reader
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.text.bold
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.view.updatePadding
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.util.ext.findKeyByValue
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeDrawable
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.databinding.ActivityReaderTapActionsBinding
|
||||
import org.koitharu.kotatsu.reader.data.TapGridSettings
|
||||
import org.koitharu.kotatsu.reader.domain.TapGridArea
|
||||
import org.koitharu.kotatsu.reader.ui.tapgrid.TapAction
|
||||
import java.util.EnumMap
|
||||
import javax.inject.Inject
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ReaderTapGridConfigActivity : BaseActivity<ActivityReaderTapActionsBinding>(), View.OnClickListener,
|
||||
View.OnLongClickListener {
|
||||
|
||||
@Inject
|
||||
lateinit var tapGridSettings: TapGridSettings
|
||||
|
||||
private val controls = EnumMap<TapGridArea, TextView>(TapGridArea::class.java)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityReaderTapActionsBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
controls[TapGridArea.TOP_LEFT] = viewBinding.textViewTopLeft
|
||||
controls[TapGridArea.TOP_CENTER] = viewBinding.textViewTopCenter
|
||||
controls[TapGridArea.TOP_RIGHT] = viewBinding.textViewTopRight
|
||||
controls[TapGridArea.CENTER_LEFT] = viewBinding.textViewCenterLeft
|
||||
controls[TapGridArea.CENTER] = viewBinding.textViewCenter
|
||||
controls[TapGridArea.CENTER_RIGHT] = viewBinding.textViewCenterRight
|
||||
controls[TapGridArea.BOTTOM_LEFT] = viewBinding.textViewBottomLeft
|
||||
controls[TapGridArea.BOTTOM_CENTER] = viewBinding.textViewBottomCenter
|
||||
controls[TapGridArea.BOTTOM_RIGHT] = viewBinding.textViewBottomRight
|
||||
|
||||
controls.forEach { (_, view) ->
|
||||
view.setOnClickListener(this)
|
||||
view.setOnLongClickListener(this)
|
||||
}
|
||||
updateValues()
|
||||
tapGridSettings.observe().observe(this) { updateValues() }
|
||||
}
|
||||
|
||||
override fun onWindowInsetsChanged(insets: Insets) {
|
||||
viewBinding.root.updatePadding(
|
||||
left = insets.left,
|
||||
top = insets.top,
|
||||
right = insets.right,
|
||||
bottom = insets.bottom,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
val area = controls.findKeyByValue(v) ?: return
|
||||
showActionSelector(area, isLongTap = false)
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View?): Boolean {
|
||||
val area = controls.findKeyByValue(v) ?: return false
|
||||
showActionSelector(area, isLongTap = true)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun updateValues() {
|
||||
controls.forEach { (area, view) ->
|
||||
view.text = buildSpannedString {
|
||||
appendLine(getString(R.string.tap_action))
|
||||
bold {
|
||||
appendLine(getTapActionText(area, isLongTap = false))
|
||||
}
|
||||
appendLine()
|
||||
appendLine(getString(R.string.long_tap_action))
|
||||
bold {
|
||||
appendLine(getTapActionText(area, isLongTap = true))
|
||||
}
|
||||
}
|
||||
view.background = createBackground(tapGridSettings.getTapAction(area, false))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTapActionText(area: TapGridArea, isLongTap: Boolean): String {
|
||||
return tapGridSettings.getTapAction(area, isLongTap)?.let {
|
||||
getString(it.nameStringResId)
|
||||
} ?: getString(R.string.none)
|
||||
}
|
||||
|
||||
private fun showActionSelector(area: TapGridArea, isLongTap: Boolean) {
|
||||
val selectedItem = tapGridSettings.getTapAction(area, isLongTap)?.ordinal ?: -1
|
||||
val listener = DialogInterface.OnClickListener { dialog, which ->
|
||||
tapGridSettings.setTapAction(area, isLongTap, TapAction.entries.getOrNull(which - 1))
|
||||
dialog.dismiss()
|
||||
}
|
||||
val names = arrayOfNulls<String>(TapAction.entries.size + 1)
|
||||
names[0] = getString(R.string.none)
|
||||
TapAction.entries.forEachIndexed { index, action -> names[index + 1] = getString(action.nameStringResId) }
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setSingleChoiceItems(names, selectedItem + 1, listener)
|
||||
.setTitle(if (isLongTap) R.string.long_tap_action else R.string.tap_action)
|
||||
.setIcon(R.drawable.ic_tap)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun createBackground(action: TapAction?): Drawable? {
|
||||
val ripple = getThemeDrawable(materialR.attr.selectableItemBackground)
|
||||
return if (action == null) {
|
||||
ripple
|
||||
} else {
|
||||
LayerDrawable(arrayOf(ripple, ColorDrawable(ColorUtils.setAlphaComponent(action.color, 60))))
|
||||
}
|
||||
}
|
||||
}
|
||||
191
app/src/main/res/layout/activity_reader_tap_actions.xml
Normal file
191
app/src/main/res/layout/activity_reader_tap_actions.xml
Normal file
@@ -0,0 +1,191 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.33" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_bottom"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.67" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.33" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.67" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_top_left"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline_top"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline_left"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_top_center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline_top"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline_right"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_left"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_top_right"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline_top"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_right"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_center_left"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toTopOf="@id/textView_bottom_left"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline_left"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_top_left" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toTopOf="@id/textView_bottom_center"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline_right"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_left"
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_top_center" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_center_right"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toTopOf="@id/textView_bottom_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_right"
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_top_right" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_bottom_left"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline_left"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline_bottom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_bottom_center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline_right"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_left"
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline_bottom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_bottom_right"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_right"
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline_bottom" />
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?android:divider"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_right"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_right"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?android:divider"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_left"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_left"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="?android:divider"
|
||||
app:layout_constraintBottom_toBottomOf="@id/guideline_top"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline_top" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="?android:divider"
|
||||
app:layout_constraintBottom_toBottomOf="@id/guideline_bottom"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline_bottom" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="?android:divider"
|
||||
app:layout_constraintBottom_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
@@ -5,10 +5,6 @@
|
||||
<item>@string/light</item>
|
||||
<item>@string/dark</item>
|
||||
</string-array>
|
||||
<string-array name="reader_switchers" translatable="false">
|
||||
<item>@string/taps_on_edges</item>
|
||||
<item>@string/volume_buttons</item>
|
||||
</string-array>
|
||||
<string-array name="zoom_modes" translatable="false">
|
||||
<item>@string/zoom_mode_fit_center</item>
|
||||
<item>@string/zoom_mode_fit_height</item>
|
||||
@@ -43,6 +39,7 @@
|
||||
<string-array name="reader_modes" translatable="false">
|
||||
<item>@string/standard</item>
|
||||
<item>@string/right_to_left</item>
|
||||
<item>@string/vertical</item>
|
||||
<item>@string/webtoon</item>
|
||||
</string-array>
|
||||
<string-array name="scrobbling_statuses" translatable="false">
|
||||
|
||||
@@ -21,13 +21,6 @@
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
<string-array name="values_reader_switchers" translatable="false">
|
||||
<item>taps</item>
|
||||
<item>volume</item>
|
||||
</string-array>
|
||||
<string-array name="values_reader_switchers_default" translatable="false">
|
||||
<item>taps</item>
|
||||
</string-array>
|
||||
<string-array name="values_track_sources" translatable="false">
|
||||
<item>favourites</item>
|
||||
<item>history</item>
|
||||
|
||||
@@ -568,4 +568,17 @@
|
||||
<string name="vertical">Vertical</string>
|
||||
<string name="last_read">Last read</string>
|
||||
<string name="two_pages">Two pages</string>
|
||||
<string name="show_menu">Show menu</string>
|
||||
<string name="toggle_ui">Show/hide UI</string>
|
||||
<string name="prev_chapter">Previous chapter</string>
|
||||
<string name="next_chapter">Next chapter</string>
|
||||
<string name="prev_page">Previous page</string>
|
||||
<string name="next_page">Next page</string>
|
||||
<string name="reader_actions">Reader actions</string>
|
||||
<string name="reader_actions_summary">Configure actions for tappable screen areas</string>
|
||||
<string name="switch_pages_volume_buttons">Enable volume buttons</string>
|
||||
<string name="switch_pages_volume_buttons_summary">Use volume buttons for switching pages</string>
|
||||
<string name="tap_action">Tap action</string>
|
||||
<string name="long_tap_action">Long tap action</string>
|
||||
<string name="none">None</string>
|
||||
</resources>
|
||||
|
||||
@@ -34,19 +34,18 @@
|
||||
android:summary="@string/reader_zoom_buttons_summary"
|
||||
android:title="@string/reader_zoom_buttons" />
|
||||
|
||||
<MultiSelectListPreference
|
||||
android:defaultValue="@array/values_reader_switchers_default"
|
||||
android:entries="@array/reader_switchers"
|
||||
android:entryValues="@array/values_reader_switchers"
|
||||
android:key="reader_switchers"
|
||||
android:title="@string/switch_pages"
|
||||
<Preference
|
||||
android:key="reader_tap_actions"
|
||||
android:persistent="false"
|
||||
android:summary="@string/reader_actions_summary"
|
||||
android:title="@string/reader_actions"
|
||||
app:allowDividerAbove="true" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="reader_taps_ltr"
|
||||
android:summary="@string/reader_control_ltr_summary"
|
||||
android:title="@string/reader_control_ltr" />
|
||||
android:key="reader_volume_buttons"
|
||||
android:summary="@string/switch_pages_volume_buttons_summary"
|
||||
android:title="@string/switch_pages_volume_buttons" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/reader_animation"
|
||||
@@ -58,7 +57,8 @@
|
||||
android:defaultValue="false"
|
||||
android:key="enhanced_colors"
|
||||
android:summary="@string/enhanced_colors_summary"
|
||||
android:title="@string/enhanced_colors" />
|
||||
android:title="@string/enhanced_colors"
|
||||
app:allowDividerAbove="true" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
|
||||
Reference in New Issue
Block a user