Merge branch 'pr8'

This commit is contained in:
jmir1 2021-05-01 22:50:24 +02:00
commit 2e9357c53e
14 changed files with 288 additions and 127 deletions

View file

@ -2,14 +2,20 @@ package eu.kanade.tachiyomi
import android.app.ActivityManager
import android.app.Application
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
import android.os.Build
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.getSystemService
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.multidex.MultiDex
import coil.ImageLoader
import coil.ImageLoaderFactory
@ -22,6 +28,9 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.notification
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.acra.ACRA
import org.acra.annotation.AcraCore
import org.acra.annotation.AcraHttpSender
@ -45,6 +54,8 @@ open class App : Application(), LifecycleObserver, ImageLoaderFactory {
private val preferences: PreferencesHelper by injectLazy()
private val disableIncognitoReceiver = DisableIncognitoReceiver()
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree())
@ -65,6 +76,34 @@ open class App : Application(), LifecycleObserver, ImageLoaderFactory {
// Reset Incognito Mode on relaunch
preferences.incognitoMode().set(false)
// Show notification to disable Incognito Mode when it's enabled
preferences.incognitoMode().asFlow()
.onEach { enabled ->
val notificationManager = NotificationManagerCompat.from(this)
if (enabled) {
disableIncognitoReceiver.register()
val notification = notification(Notifications.CHANNEL_INCOGNITO_MODE) {
setContentTitle(getString(R.string.pref_incognito_mode))
setContentText(getString(R.string.notification_incognito_text))
setSmallIcon(R.drawable.ic_glasses_black_24dp)
setOngoing(true)
val pendingIntent = PendingIntent.getBroadcast(
this@App,
0,
Intent(ACTION_DISABLE_INCOGNITO_MODE),
PendingIntent.FLAG_ONE_SHOT
)
setContentIntent(pendingIntent)
}
notificationManager.notify(Notifications.ID_INCOGNITO_MODE, notification)
} else {
disableIncognitoReceiver.unregister()
notificationManager.cancel(Notifications.ID_INCOGNITO_MODE)
}
}
.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
}
override fun attachBaseContext(base: Context) {
@ -111,4 +150,30 @@ open class App : Application(), LifecycleObserver, ImageLoaderFactory {
protected open fun setupNotificationChannels() {
Notifications.createChannels(this)
}
private inner class DisableIncognitoReceiver : BroadcastReceiver() {
private var registered = false
override fun onReceive(context: Context, intent: Intent) {
preferences.incognitoMode().set(false)
}
fun register() {
if (!registered) {
registerReceiver(this, IntentFilter(ACTION_DISABLE_INCOGNITO_MODE))
registered = true
}
}
fun unregister() {
if (registered) {
unregisterReceiver(this)
registered = false
}
}
}
companion object {
private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE"
}
}

View file

@ -71,6 +71,12 @@ object Notifications {
const val CHANNEL_CRASH_LOGS = "crash_logs_channel"
const val ID_CRASH_LOGS = -601
/**
* Notification channel used for Incognito Mode
*/
const val CHANNEL_INCOGNITO_MODE = "incognito_mode_channel"
const val ID_INCOGNITO_MODE = -701
private val deprecatedChannels = listOf(
"downloader_channel",
"backup_restore_complete_channel"
@ -157,6 +163,11 @@ object Notifications {
CHANNEL_CRASH_LOGS,
context.getString(R.string.channel_crash_logs),
NotificationManager.IMPORTANCE_HIGH
),
NotificationChannel(
CHANNEL_INCOGNITO_MODE,
context.getString(R.string.pref_incognito_mode),
NotificationManager.IMPORTANCE_LOW
)
).forEach(context.notificationManager::createNotificationChannel)

View file

@ -49,6 +49,8 @@ object PreferenceKeys {
const val colorFilterMode = "color_filter_mode"
const val grayscale = "pref_grayscale"
const val defaultReadingMode = "pref_default_reading_mode_key"
const val defaultOrientationType = "pref_default_orientation_type_key"

View file

@ -122,6 +122,8 @@ class PreferencesHelper(val context: Context) {
fun colorFilterMode() = flowPrefs.getInt(Keys.colorFilterMode, 0)
fun grayscale() = flowPrefs.getBoolean(Keys.grayscale, false)
fun defaultReadingMode() = prefs.getInt(Keys.defaultReadingMode, ReadingModeType.RIGHT_TO_LEFT.flagValue)
fun defaultOrientationType() = prefs.getInt(Keys.defaultOrientationType, OrientationType.FREE.flagValue)

View file

@ -42,6 +42,7 @@ import eu.kanade.tachiyomi.ui.base.controller.TabbedController
import eu.kanade.tachiyomi.ui.base.controller.ToolbarLiftOnScrollController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.BrowseController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
import eu.kanade.tachiyomi.ui.download.DownloadController
import eu.kanade.tachiyomi.ui.library.LibraryController
@ -229,7 +230,17 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
.launchIn(lifecycleScope)
preferences.incognitoMode()
.asImmediateFlow { binding.incognitoMode.isVisible = it }
.asImmediateFlow {
binding.incognitoMode.isVisible = it
// Close BrowseSourceController and its MangaController child when incognito mode is disabled
if (!it) {
val fg = router.backstack.last().controller()
if (fg is BrowseSourceController || fg is MangaController && fg.fromSource) {
router.popToRoot()
}
}
}
.launchIn(lifecycleScope)
}

View file

@ -127,7 +127,7 @@ class MangaController :
var source: Source? = null
private set
private val fromSource = args.getBoolean(FROM_SOURCE_EXTRA, false)
val fromSource = args.getBoolean(FROM_SOURCE_EXTRA, false)
private val preferences: PreferencesHelper by injectLazy()
private val coverCache: CoverCache by injectLazy()

View file

@ -8,12 +8,16 @@ import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.Paint
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View.LAYER_TYPE_HARDWARE
import android.view.WindowManager
import android.view.animation.Animation
import android.view.animation.AnimationUtils
@ -163,6 +167,12 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
window.decorView.setOnSystemUiVisibilityChangeListener {
setMenuVisibility(menuVisible, animate = false)
}
// Finish when incognito mode is disabled
preferences.incognitoMode().asFlow()
.drop(1)
.onEach { if (!it) finish() }
.launchIn(lifecycleScope)
}
/**
@ -789,6 +799,16 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
*/
private inner class ReaderConfig {
private val grayscalePaint by lazy {
Paint().apply {
colorFilter = ColorMatrixColorFilter(
ColorMatrix().apply {
setSaturation(0f)
}
)
}
}
/**
* Initializes the reader subscriptions.
*/
@ -827,6 +847,10 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
preferences.colorFilterMode().asFlow()
.onEach { setColorFilter(preferences.colorFilter().get()) }
.launchIn(lifecycleScope)
preferences.grayscale().asFlow()
.onEach { setGrayscale(it) }
.launchIn(lifecycleScope)
}
/**
@ -934,5 +958,10 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
binding.colorOverlay.isVisible = true
binding.colorOverlay.setFilterColor(value, preferences.colorFilterMode().get())
}
private fun setGrayscale(enabled: Boolean) {
val paint = if (enabled) grayscalePaint else null
binding.viewerContainer.setLayerType(LAYER_TYPE_HARDWARE, paint)
}
}
}

View file

@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
@ -143,6 +144,8 @@ class ReaderPresenter(
hasTrackers = tracks.size > 0
}
private val incognitoMode = preferences.incognitoMode().get()
/**
* Called when the presenter is created. It retrieves the saved active chapter if the process
* was restored.
@ -367,9 +370,14 @@ class ReaderPresenter(
val selectedChapter = page.chapter
// Insert page doesn't change page progress
if (page is InsertPage) {
return
}
// Save last page read and mark as read if needed
selectedChapter.chapter.last_page_read = page.index
val shouldTrack = !preferences.incognitoMode().get() || hasTrackers
val shouldTrack = !incognitoMode || hasTrackers
if (selectedChapter.pages?.lastIndex == page.index && shouldTrack) {
selectedChapter.chapter.read = true
updateTrackChapterRead(selectedChapter)
@ -424,7 +432,7 @@ class ReaderPresenter(
* If incognito mode isn't on or has at least 1 tracker
*/
private fun saveChapterProgress(chapter: ReaderChapter) {
if (!preferences.incognitoMode().get() || hasTrackers) {
if (!incognitoMode || hasTrackers) {
db.updateChapterProgress(chapter.chapter).asRxCompletable()
.onErrorComplete()
.subscribeOn(Schedulers.io())
@ -436,7 +444,7 @@ class ReaderPresenter(
* Saves this [chapter] last read history if incognito mode isn't on.
*/
private fun saveChapterHistory(chapter: ReaderChapter) {
if (!preferences.incognitoMode().get()) {
if (!incognitoMode) {
val history = History.create(chapter.chapter).apply { last_read = Date().time }
db.updateHistoryLastRead(history).asRxCompletable()
.onErrorComplete()

View file

@ -14,7 +14,7 @@ import androidx.lifecycle.lifecycleScope
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.ReaderColorFilterSettingsBinding
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import eu.kanade.tachiyomi.util.preference.bindToPreference
import eu.kanade.tachiyomi.widget.SimpleSeekBarListener
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@ -63,20 +63,10 @@ class ReaderColorFilterSettings @JvmOverloads constructor(context: Context, attr
binding.seekbarColorFilterBlue.progress = argb[3]
// Set listeners
binding.switchColorFilter.isChecked = preferences.colorFilter().get()
binding.switchColorFilter.setOnCheckedChangeListener { _, isChecked ->
preferences.colorFilter().set(isChecked)
}
binding.customBrightness.isChecked = preferences.customBrightness().get()
binding.customBrightness.setOnCheckedChangeListener { _, isChecked ->
preferences.customBrightness().set(isChecked)
}
binding.colorFilterMode.onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
preferences.colorFilterMode().set(position)
}
binding.colorFilterMode.setSelection(preferences.colorFilterMode().get(), false)
binding.switchColorFilter.bindToPreference(preferences.colorFilter())
binding.customBrightness.bindToPreference(preferences.customBrightness())
binding.colorFilterMode.bindToPreference(preferences.colorFilterMode())
binding.grayscale.bindToPreference(preferences.grayscale())
binding.seekbarColorFilterAlpha.setOnSeekBarChangeListener(
object : SimpleSeekBarListener() {

View file

@ -230,7 +230,7 @@ class PagerPageHolder(
readImageHeaderSubscription = Observable
.fromCallable {
val stream = streamFn().buffered(16)
openStream = process(stream)
openStream = process(item, stream)
ImageUtil.findImageType(stream) == ImageUtil.ImageType.GIF
}
@ -249,7 +249,7 @@ class PagerPageHolder(
.subscribe({}, {})
}
private fun process(imageStream: InputStream): InputStream {
private fun process(page: ReaderPage, imageStream: InputStream): InputStream {
if (!viewer.config.dualPageSplit) {
return imageStream
}
@ -263,7 +263,7 @@ class PagerPageHolder(
return imageStream
}
onPageSplit()
onPageSplit(page)
return splitInHalf(imageStream)
}
@ -287,7 +287,7 @@ class PagerPageHolder(
return ImageUtil.splitInHalf(imageStream, side)
}
private fun onPageSplit() {
private fun onPageSplit(page: ReaderPage) {
val newPage = InsertPage(page)
viewer.onPageSplit(page, newPage)
}

View file

@ -192,6 +192,11 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
Timber.d("onReaderPageSelected: ${page.number}/${pages.size}")
activity.onPageSelected(page)
// Skip preload on inserts it causes unwanted page jumping
if (page is InsertPage) {
return
}
// Preload next chapter once we're within the last 5 pages of the current chapter
val inPreloadRange = pages.size - page.number < 5
if (inPreloadRange && allowPreload && page.chapter == adapter.currentChapter) {
@ -387,7 +392,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
fun onPageSplit(currentPage: ReaderPage, newPage: InsertPage) {
activity.runOnUiThread {
// Need to insert on UI thread else images will go blank
adapter.onPageSplit(currentPage, newPage, this::class.java)
adapter.onPageSplit(currentPage, newPage)
}
}

View file

@ -22,6 +22,11 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
var items: MutableList<Any> = mutableListOf()
private set
/**
* Holds preprocessed items so they don't get removed when changing chapter
*/
private var preprocessed: MutableMap<Int, InsertPage> = mutableMapOf()
var nextTransition: ChapterTransition.Next? = null
private set
@ -54,10 +59,25 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
newItems.add(ChapterTransition.Prev(chapters.currChapter, chapters.prevChapter))
}
var insertPageLastPage: InsertPage? = null
// Add current chapter.
val currPages = chapters.currChapter.pages
if (currPages != null) {
newItems.addAll(currPages)
val pages = currPages.toMutableList()
val lastPage = pages.last()
// Insert preprocessed pages into current page list
preprocessed.keys.sortedDescending()
.forEach { key ->
if (lastPage.index == key) {
insertPageLastPage = preprocessed[key]
}
preprocessed[key]?.let { pages.add(key + 1, it) }
}
newItems.addAll(pages)
}
currentChapter = chapters.currChapter
@ -88,8 +108,14 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
newItems.reverse()
}
preprocessed = mutableMapOf()
items = newItems
notifyDataSetChanged()
// Will skip insert page otherwise
if (insertPageLastPage != null) {
viewer.moveToPage(insertPageLastPage!!)
}
}
/**
@ -125,20 +151,25 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
return POSITION_NONE
}
fun onPageSplit(current: Any?, newPage: InsertPage, clazz: Class<out PagerViewer>) {
if (current !is ReaderPage) return
fun onPageSplit(currentPage: Any?, newPage: InsertPage) {
if (currentPage !is ReaderPage) return
val currentIndex = items.indexOf(current)
val currentIndex = items.indexOf(currentPage)
val placeAtIndex = when {
clazz.isAssignableFrom(L2RPagerViewer::class.java) -> currentIndex + 1
clazz.isAssignableFrom(VerticalPagerViewer::class.java) -> currentIndex + 1
clazz.isAssignableFrom(R2LPagerViewer::class.java) -> currentIndex
// Put aside preprocessed pages for next chapter so they don't get removed when changing chapter
if (currentPage.chapter.chapter.id != currentChapter?.chapter?.id) {
preprocessed[newPage.index] = newPage
return
}
val placeAtIndex = when (viewer) {
is L2RPagerViewer,
is VerticalPagerViewer -> currentIndex + 1
else -> currentIndex
}
// It will enter a endless cycle of insert pages
if (clazz.isAssignableFrom(R2LPagerViewer::class.java) && items[placeAtIndex - 1] is InsertPage) {
if (viewer is R2LPagerViewer && placeAtIndex - 1 >= 0 && items[placeAtIndex - 1] is InsertPage) {
return
}

View file

@ -3,8 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@ -17,12 +16,25 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/pref_custom_color_filter"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- Red filter -->
<TextView
android:id="@+id/txt_color_filter_red_symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:text="@string/color_filter_r_value"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_red"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_red" />
<SeekBar
android:id="@+id/seekbar_color_filter_red"
android:layout_width="0dp"
@ -30,33 +42,36 @@
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:max="255"
android:padding="@dimen/material_component_text_fields_floating_label_padding_between_label_and_input_text"
android:padding="8dp"
app:layout_constraintEnd_toStartOf="@id/txt_color_filter_red_value"
app:layout_constraintStart_toEndOf="@id/color_filter_symbols_barrier"
app:layout_constraintTop_toBottomOf="@id/switch_color_filter" />
<TextView
android:id="@+id/txt_color_filter_red_symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/color_filter_r_value"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_red"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_red" />
<TextView
android:id="@+id/txt_color_filter_red_value"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="16dp"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_red"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_red" />
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_red"
tools:text="255" />
<!-- Green filter -->
<TextView
android:id="@+id/txt_color_filter_green_symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:text="@string/color_filter_g_value"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_green"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_green" />
<SeekBar
android:id="@+id/seekbar_color_filter_green"
android:layout_width="0dp"
@ -64,33 +79,36 @@
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:max="255"
android:padding="@dimen/material_component_text_fields_floating_label_padding_between_label_and_input_text"
android:padding="8dp"
app:layout_constraintEnd_toStartOf="@id/txt_color_filter_green_value"
app:layout_constraintStart_toEndOf="@id/color_filter_symbols_barrier"
app:layout_constraintTop_toBottomOf="@id/seekbar_color_filter_red" />
<TextView
android:id="@+id/txt_color_filter_green_symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/color_filter_g_value"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_green"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_green" />
<TextView
android:id="@+id/txt_color_filter_green_value"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="16dp"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_green"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_green" />
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_green"
tools:text="255" />
<!-- Blue filter -->
<TextView
android:id="@+id/txt_color_filter_blue_symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/color_filter_b_value"
android:paddingStart="16dp"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_blue"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_blue" />
<SeekBar
android:id="@+id/seekbar_color_filter_blue"
android:layout_width="0dp"
@ -98,33 +116,36 @@
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:max="255"
android:padding="@dimen/material_component_text_fields_floating_label_padding_between_label_and_input_text"
android:padding="8dp"
app:layout_constraintEnd_toStartOf="@id/txt_color_filter_blue_value"
app:layout_constraintStart_toEndOf="@id/color_filter_symbols_barrier"
app:layout_constraintTop_toBottomOf="@id/seekbar_color_filter_green" />
<TextView
android:id="@+id/txt_color_filter_blue_symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/color_filter_b_value"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_blue"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_blue" />
<TextView
android:id="@+id/txt_color_filter_blue_value"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="16dp"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_blue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_blue" />
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_blue"
tools:text="255" />
<!-- Alpha filter -->
<TextView
android:id="@+id/txt_color_filter_alpha_symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:text="@string/color_filter_a_value"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_alpha"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_alpha" />
<SeekBar
android:id="@+id/seekbar_color_filter_alpha"
android:layout_width="0dp"
@ -132,51 +153,45 @@
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:max="255"
android:padding="@dimen/material_component_text_fields_floating_label_padding_between_label_and_input_text"
android:padding="8dp"
app:layout_constraintEnd_toStartOf="@id/txt_color_filter_alpha_value"
app:layout_constraintStart_toEndOf="@id/color_filter_symbols_barrier"
app:layout_constraintTop_toBottomOf="@id/seekbar_color_filter_blue" />
<TextView
android:id="@+id/txt_color_filter_alpha_symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/color_filter_a_value"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_alpha"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_alpha" />
<TextView
android:id="@+id/txt_color_filter_alpha_value"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_alignParentEnd="true"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/seekbar_color_filter_alpha"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_alpha" />
app:layout_constraintTop_toTopOf="@id/seekbar_color_filter_alpha"
tools:text="255" />
<!-- Filter mode -->
<TextView
android:id="@+id/color_filter_mode_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/pref_color_filter_mode"
app:layout_constraintBaseline_toBaselineOf="@id/color_filter_mode"
app:layout_constraintEnd_toStartOf="@id/color_filter_mode"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatSpinner
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/color_filter_mode"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:entries="@array/color_filter_modes"
app:layout_constraintEnd_toStartOf="@id/spinner_end"
app:layout_constraintStart_toEndOf="@id/verticalcenter"
app:layout_constraintTop_toBottomOf="@id/seekbar_color_filter_alpha" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/seekbar_color_filter_alpha"
app:title="@string/pref_color_filter_mode" />
<!-- Grayscale -->
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/grayscale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="16dp"
android:paddingStart="16dp"
android:text="@string/pref_grayscale"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/color_filter_mode" />
<!-- Brightness -->
@ -185,11 +200,25 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingEnd="16dp"
android:paddingStart="16dp"
android:text="@string/pref_custom_brightness"
app:layout_constraintTop_toBottomOf="@id/color_filter_mode_text" />
app:layout_constraintTop_toBottomOf="@id/grayscale" />
<!-- Brightness value -->
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/txt_brightness_seekbar_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
android:tint="?attr/colorOnBackground"
app:layout_constraintBottom_toBottomOf="@id/brightness_seekbar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/brightness_seekbar"
app:srcCompat="@drawable/ic_brightness_5_24dp" />
<eu.kanade.tachiyomi.widget.NegativeSeekBar
android:id="@+id/brightness_seekbar"
android:layout_width="0dp"
@ -203,25 +232,16 @@
app:max_seek="100"
app:min_seek="-75" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/txt_brightness_seekbar_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
android:tint="?attr/colorOnBackground"
app:layout_constraintBottom_toBottomOf="@id/brightness_seekbar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/brightness_seekbar"
app:srcCompat="@drawable/ic_brightness_5_24dp" />
<TextView
android:id="@+id/txt_brightness_seekbar_value"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:textAppearance="@style/TextAppearance.Medium.SubHeading"
app:layout_constraintBottom_toBottomOf="@id/brightness_seekbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/brightness_seekbar" />
app:layout_constraintTop_toTopOf="@id/brightness_seekbar"
tools:text="50" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/color_filter_symbols_barrier"
@ -230,20 +250,6 @@
app:barrierDirection="end"
app:constraint_referenced_ids="txt_color_filter_alpha_symbol,txt_color_filter_blue_symbol,txt_color_filter_red_symbol,txt_color_filter_green_symbol" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/verticalcenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<android.widget.Space
android:id="@+id/spinner_end"
android:layout_width="16dp"
android:layout_height="0dp"
app:layout_constraintStart_toEndOf="parent"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -278,6 +278,7 @@
<string name="pref_true_color_summary">Reduces banding, but impacts performance</string>
<string name="pref_crop_borders">Crop borders</string>
<string name="pref_custom_brightness">Custom brightness</string>
<string name="pref_grayscale">Grayscale</string>
<string name="pref_custom_color_filter">Custom color filter</string>
<string name="pref_color_filter_mode">Color filter blend mode</string>
<string name="filter_mode_default">Default</string>
@ -473,6 +474,7 @@
<string name="label_downloaded_only">Downloaded only</string>
<string name="pref_incognito_mode">Incognito mode</string>
<string name="pref_incognito_mode_summary">Pauses reading history</string>
<string name="notification_incognito_text">Disable incognito mode</string>
<string name="downloaded_only_summary">Filters all manga in your library</string>
<plurals name="download_queue_summary">
<item quantity="one">1 remaining</item>
@ -802,5 +804,4 @@
<!-- S Pen actions -->
<string name="spen_previous_page">Previous page</string>
<string name="spen_next_page">Next page</string>
</resources>