Grayscale color filter

This commit is contained in:
Koitharu
2023-12-04 16:21:02 +02:00
parent 3008b7b89a
commit 5183d5e882
13 changed files with 128 additions and 104 deletions

View File

@@ -29,6 +29,7 @@ import org.koitharu.kotatsu.core.db.migrations.Migration13To14
import org.koitharu.kotatsu.core.db.migrations.Migration14To15 import org.koitharu.kotatsu.core.db.migrations.Migration14To15
import org.koitharu.kotatsu.core.db.migrations.Migration15To16 import org.koitharu.kotatsu.core.db.migrations.Migration15To16
import org.koitharu.kotatsu.core.db.migrations.Migration16To17 import org.koitharu.kotatsu.core.db.migrations.Migration16To17
import org.koitharu.kotatsu.core.db.migrations.Migration17To18
import org.koitharu.kotatsu.core.db.migrations.Migration1To2 import org.koitharu.kotatsu.core.db.migrations.Migration1To2
import org.koitharu.kotatsu.core.db.migrations.Migration2To3 import org.koitharu.kotatsu.core.db.migrations.Migration2To3
import org.koitharu.kotatsu.core.db.migrations.Migration3To4 import org.koitharu.kotatsu.core.db.migrations.Migration3To4
@@ -53,7 +54,7 @@ import org.koitharu.kotatsu.tracker.data.TrackEntity
import org.koitharu.kotatsu.tracker.data.TrackLogEntity import org.koitharu.kotatsu.tracker.data.TrackLogEntity
import org.koitharu.kotatsu.tracker.data.TracksDao import org.koitharu.kotatsu.tracker.data.TracksDao
const val DATABASE_VERSION = 17 const val DATABASE_VERSION = 18
@Database( @Database(
entities = [ entities = [
@@ -108,6 +109,7 @@ fun getDatabaseMigrations(context: Context): Array<Migration> = arrayOf(
Migration14To15(), Migration14To15(),
Migration15To16(), Migration15To16(),
Migration16To17(context), Migration16To17(context),
Migration17To18(),
) )
fun MangaDatabase(context: Context): MangaDatabase = Room fun MangaDatabase(context: Context): MangaDatabase = Room

View File

@@ -24,4 +24,5 @@ data class MangaPrefsEntity(
@ColumnInfo(name = "cf_brightness") val cfBrightness: Float, @ColumnInfo(name = "cf_brightness") val cfBrightness: Float,
@ColumnInfo(name = "cf_contrast") val cfContrast: Float, @ColumnInfo(name = "cf_contrast") val cfContrast: Float,
@ColumnInfo(name = "cf_invert") val cfInvert: Boolean, @ColumnInfo(name = "cf_invert") val cfInvert: Boolean,
@ColumnInfo(name = "cf_grayscale") val cfGrayscale: Boolean,
) )

View File

@@ -0,0 +1,11 @@
package org.koitharu.kotatsu.core.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration17To18 : Migration(17, 18) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE preferences ADD COLUMN `cf_grayscale` INTEGER NOT NULL DEFAULT 0")
}
}

View File

@@ -98,8 +98,8 @@ class MangaDataRepository @Inject constructor(
} }
private fun MangaPrefsEntity.getColorFilterOrNull(): ReaderColorFilter? { private fun MangaPrefsEntity.getColorFilterOrNull(): ReaderColorFilter? {
return if (cfBrightness != 0f || cfContrast != 0f || cfInvert) { return if (cfBrightness != 0f || cfContrast != 0f || cfInvert || cfGrayscale) {
ReaderColorFilter(cfBrightness, cfContrast, cfInvert) ReaderColorFilter(cfBrightness, cfContrast, cfInvert, cfGrayscale)
} else { } else {
null null
} }
@@ -111,5 +111,6 @@ class MangaDataRepository @Inject constructor(
cfBrightness = 0f, cfBrightness = 0f,
cfContrast = 0f, cfContrast = 0f,
cfInvert = false, cfInvert = false,
cfGrayscale = false,
) )
} }

View File

@@ -301,22 +301,19 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
var readerColorFilter: ReaderColorFilter? var readerColorFilter: ReaderColorFilter?
get() { get() {
if (!prefs.getBoolean(KEY_CF_ENABLED, false)) { val brightness = prefs.getFloat(KEY_CF_BRIGHTNESS, ReaderColorFilter.EMPTY.brightness)
return null val contrast = prefs.getFloat(KEY_CF_CONTRAST, ReaderColorFilter.EMPTY.contrast)
} val inverted = prefs.getBoolean(KEY_CF_INVERTED, ReaderColorFilter.EMPTY.isInverted)
val brightness = prefs.getFloat(KEY_CF_BRIGHTNESS, 0f) val grayscale = prefs.getBoolean(KEY_CF_GRAYSCALE, ReaderColorFilter.EMPTY.isGrayscale)
val contrast = prefs.getFloat(KEY_CF_CONTRAST, 0f) return ReaderColorFilter(brightness, contrast, inverted, grayscale).takeUnless { it.isEmpty }
val inverted = prefs.getBoolean(KEY_CF_INVERTED, false)
return ReaderColorFilter(brightness, contrast, inverted)
} }
set(value) { set(value) {
prefs.edit { prefs.edit {
putBoolean(KEY_CF_ENABLED, value != null) val cf = value ?: ReaderColorFilter.EMPTY
if (value != null) { putFloat(KEY_CF_BRIGHTNESS, cf.brightness)
putFloat(KEY_CF_BRIGHTNESS, value.brightness) putFloat(KEY_CF_CONTRAST, cf.contrast)
putFloat(KEY_CF_CONTRAST, value.contrast) putBoolean(KEY_CF_INVERTED, cf.isInverted)
putBoolean(KEY_CF_INVERTED, value.isInverted) putBoolean(KEY_CF_GRAYSCALE, cf.isGrayscale)
}
} }
} }
@@ -573,10 +570,10 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_32BIT_COLOR = "enhanced_colors" const val KEY_32BIT_COLOR = "enhanced_colors"
const val KEY_SOURCES_ORDER = "sources_sort_order" const val KEY_SOURCES_ORDER = "sources_sort_order"
const val KEY_SOURCES_CATALOG = "sources_catalog" const val KEY_SOURCES_CATALOG = "sources_catalog"
const val KEY_CF_ENABLED = "cf_enabled"
const val KEY_CF_BRIGHTNESS = "cf_brightness" const val KEY_CF_BRIGHTNESS = "cf_brightness"
const val KEY_CF_CONTRAST = "cf_contrast" const val KEY_CF_CONTRAST = "cf_contrast"
const val KEY_CF_INVERTED = "cf_inverted" const val KEY_CF_INVERTED = "cf_inverted"
const val KEY_CF_GRAYSCALE = "cf_grayscale"
// About // About
const val KEY_APP_UPDATE = "app_update" const val KEY_APP_UPDATE = "app_update"

View File

@@ -7,13 +7,17 @@ data class ReaderColorFilter(
val brightness: Float, val brightness: Float,
val contrast: Float, val contrast: Float,
val isInverted: Boolean, val isInverted: Boolean,
val isGrayscale: Boolean,
) { ) {
val isEmpty: Boolean val isEmpty: Boolean
get() = !isInverted && brightness == 0f && contrast == 0f get() = !isGrayscale && !isInverted && brightness == 0f && contrast == 0f
fun toColorFilter(): ColorMatrixColorFilter { fun toColorFilter(): ColorMatrixColorFilter {
val cm = ColorMatrix() val cm = ColorMatrix()
if (isGrayscale) {
cm.grayscale()
}
if (isInverted) { if (isInverted) {
cm.inverted() cm.inverted()
} }
@@ -49,6 +53,20 @@ data class ReaderColorFilter(
0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
) )
set(matrix) postConcat(ColorMatrix(matrix))
}
private fun ColorMatrix.grayscale() {
setSaturation(0f)
}
companion object {
val EMPTY = ReaderColorFilter(
brightness = 0.0f,
contrast = 0.0f,
isInverted = false,
isGrayscale = false,
)
} }
} }

View File

@@ -16,6 +16,7 @@ import coil.ImageLoader
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.size.Scale import coil.size.Scale
import coil.size.ViewSizeResolver import coil.size.ViewSizeResolver
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.LabelFormatter import com.google.android.material.slider.LabelFormatter
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@@ -62,6 +63,7 @@ class ColorFilterConfigActivity :
viewBinding.sliderContrast.setLabelFormatter(formatter) viewBinding.sliderContrast.setLabelFormatter(formatter)
viewBinding.sliderBrightness.setLabelFormatter(formatter) viewBinding.sliderBrightness.setLabelFormatter(formatter)
viewBinding.switchInvert.setOnCheckedChangeListener(this) viewBinding.switchInvert.setOnCheckedChangeListener(this)
viewBinding.switchGrayscale.setOnCheckedChangeListener(this)
viewBinding.buttonDone.setOnClickListener(this) viewBinding.buttonDone.setOnClickListener(this)
viewBinding.buttonReset.setOnClickListener(this) viewBinding.buttonReset.setOnClickListener(this)
@@ -84,18 +86,16 @@ class ColorFilterConfigActivity :
} }
} }
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) { override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
viewModel.setInversion(isChecked) when (buttonView.id) {
R.id.switch_invert -> viewModel.setInversion(isChecked)
R.id.switch_grayscale -> viewModel.setGrayscale(isChecked)
}
} }
override fun onClick(v: View) { override fun onClick(v: View) {
when (v.id) { when (v.id) {
R.id.button_done -> if (viewBinding.checkBoxGlobal.isChecked) { R.id.button_done -> showSaveConfirmation()
viewModel.saveGlobally()
} else {
viewModel.save()
}
R.id.button_reset -> viewModel.reset() R.id.button_reset -> viewModel.reset()
} }
} }
@@ -113,10 +113,23 @@ class ColorFilterConfigActivity :
} }
} }
fun showSaveConfirmation() {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.apply)
.setMessage(R.string.color_correction_apply_text)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.this_manga) { _, _ ->
viewModel.saveGlobally()
}.setNeutralButton(R.string.globally) { _, _ ->
viewModel.save()
}.show()
}
private fun onColorFilterChanged(readerColorFilter: ReaderColorFilter?) { private fun onColorFilterChanged(readerColorFilter: ReaderColorFilter?) {
viewBinding.sliderBrightness.setValueRounded(readerColorFilter?.brightness ?: 0f) viewBinding.sliderBrightness.setValueRounded(readerColorFilter?.brightness ?: 0f)
viewBinding.sliderContrast.setValueRounded(readerColorFilter?.contrast ?: 0f) viewBinding.sliderContrast.setValueRounded(readerColorFilter?.contrast ?: 0f)
viewBinding.switchInvert.setChecked(readerColorFilter?.isInverted ?: false, false) viewBinding.switchInvert.setChecked(readerColorFilter?.isInverted ?: false, false)
viewBinding.switchGrayscale.setChecked(readerColorFilter?.isGrayscale ?: false, false)
viewBinding.imageViewAfter.colorFilter = readerColorFilter?.toColorFilter() viewBinding.imageViewAfter.colorFilter = readerColorFilter?.toColorFilter()
} }
@@ -138,6 +151,8 @@ class ColorFilterConfigActivity :
private fun onLoadingChanged(isLoading: Boolean) { private fun onLoadingChanged(isLoading: Boolean) {
viewBinding.sliderContrast.isEnabled = !isLoading viewBinding.sliderContrast.isEnabled = !isLoading
viewBinding.sliderBrightness.isEnabled = !isLoading viewBinding.sliderBrightness.isEnabled = !isLoading
viewBinding.switchInvert.isEnabled = !isLoading
viewBinding.switchGrayscale.isEnabled = !isLoading
viewBinding.buttonDone.isEnabled = !isLoading viewBinding.buttonDone.isEnabled = !isLoading
} }

View File

@@ -1,6 +1,5 @@
package org.koitharu.kotatsu.reader.ui.colorfilter package org.koitharu.kotatsu.reader.ui.colorfilter
import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -8,7 +7,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.call
class ColorFilterConfigBackPressedDispatcher( class ColorFilterConfigBackPressedDispatcher(
private val context: Context, private val activity: ColorFilterConfigActivity,
private val viewModel: ColorFilterConfigViewModel, private val viewModel: ColorFilterConfigViewModel,
) : OnBackPressedCallback(true), DialogInterface.OnClickListener { ) : OnBackPressedCallback(true), DialogInterface.OnClickListener {
@@ -24,12 +23,12 @@ class ColorFilterConfigBackPressedDispatcher(
when (which) { when (which) {
DialogInterface.BUTTON_NEGATIVE -> viewModel.onDismiss.call(Unit) DialogInterface.BUTTON_NEGATIVE -> viewModel.onDismiss.call(Unit)
DialogInterface.BUTTON_NEUTRAL -> dialog.dismiss() DialogInterface.BUTTON_NEUTRAL -> dialog.dismiss()
DialogInterface.BUTTON_POSITIVE -> viewModel.save() DialogInterface.BUTTON_POSITIVE -> activity.showSaveConfirmation()
} }
} }
private fun showConfirmation() { private fun showConfirmation() {
MaterialAlertDialogBuilder(context) MaterialAlertDialogBuilder(activity)
.setTitle(R.string.color_correction) .setTitle(R.string.color_correction)
.setMessage(R.string.text_unsaved_changes_prompt) .setMessage(R.string.text_unsaved_changes_prompt)
.setNegativeButton(R.string.discard, this) .setNegativeButton(R.string.discard, this)

View File

@@ -44,33 +44,19 @@ class ColorFilterConfigViewModel @Inject constructor(
} }
fun setBrightness(brightness: Float) { fun setBrightness(brightness: Float) {
val cf = colorFilter.value updateColorFilter { it.copy(brightness = brightness) }
colorFilter.value = ReaderColorFilter(
brightness = brightness,
contrast = cf?.contrast ?: 0f,
isInverted = cf?.isInverted ?: false,
).takeUnless { it.isEmpty }
} }
fun setContrast(contrast: Float) { fun setContrast(contrast: Float) {
val cf = colorFilter.value updateColorFilter { it.copy(contrast = contrast) }
colorFilter.value = ReaderColorFilter(
brightness = cf?.brightness ?: 0f,
contrast = contrast,
isInverted = cf?.isInverted ?: false,
).takeUnless { it.isEmpty }
} }
fun setInversion(invert: Boolean) { fun setInversion(invert: Boolean) {
val cf = colorFilter.value updateColorFilter { it.copy(isInverted = invert) }
if (invert == cf?.isInverted) { }
return
} fun setGrayscale(grayscale: Boolean) {
colorFilter.value = ReaderColorFilter( updateColorFilter { it.copy(isGrayscale = grayscale) }
brightness = cf?.brightness ?: 0f,
contrast = cf?.contrast ?: 0f,
isInverted = invert,
).takeUnless { it.isEmpty }
} }
fun reset() { fun reset() {
@@ -85,7 +71,18 @@ class ColorFilterConfigViewModel @Inject constructor(
} }
fun saveGlobally() { fun saveGlobally() {
settings.readerColorFilter = colorFilter.value launchLoadingJob(Dispatchers.Default) {
onDismiss.call(Unit) settings.readerColorFilter = colorFilter.value
if (mangaDataRepository.getColorFilter(manga.id) != null) {
mangaDataRepository.saveColorFilter(manga, colorFilter.value)
}
onDismiss.call(Unit)
}
}
private inline fun updateColorFilter(block: (ReaderColorFilter) -> ReaderColorFilter) {
colorFilter.value = block(
colorFilter.value ?: ReaderColorFilter.EMPTY,
).takeUnless { it.isEmpty }
} }
} }

View File

@@ -102,10 +102,10 @@ class ReaderSettings(
AppSettings.KEY_READER_BACKGROUND, AppSettings.KEY_READER_BACKGROUND,
AppSettings.KEY_32BIT_COLOR, AppSettings.KEY_32BIT_COLOR,
AppSettings.KEY_READER_OPTIMIZE, AppSettings.KEY_READER_OPTIMIZE,
AppSettings.KEY_CF_ENABLED,
AppSettings.KEY_CF_CONTRAST, AppSettings.KEY_CF_CONTRAST,
AppSettings.KEY_CF_BRIGHTNESS, AppSettings.KEY_CF_BRIGHTNESS,
AppSettings.KEY_CF_INVERTED, AppSettings.KEY_CF_INVERTED,
AppSettings.KEY_CF_GRAYSCALE,
) )
override suspend fun emit(value: ReaderColorFilter?) { override suspend fun emit(value: ReaderColorFilter?) {

View File

@@ -127,6 +127,18 @@
app:layout_constraintStart_toEndOf="@id/guideline_vertical" app:layout_constraintStart_toEndOf="@id/guideline_vertical"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_grayscale"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_normal"
android:layout_marginTop="4dp"
android:text="@string/invert_colors"
android:textAppearance="?textAppearanceTitleMedium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline_vertical"
app:layout_constraintTop_toBottomOf="@id/switch_invert" />
<TextView <TextView
android:id="@+id/textView_brightness" android:id="@+id/textView_brightness"
android:layout_width="0dp" android:layout_width="0dp"
@@ -137,7 +149,7 @@
android:textAppearance="?textAppearanceTitleMedium" android:textAppearance="?textAppearanceTitleMedium"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline_vertical" app:layout_constraintStart_toEndOf="@id/guideline_vertical"
app:layout_constraintTop_toBottomOf="@id/switch_invert" /> app:layout_constraintTop_toBottomOf="@id/switch_grayscale" />
<com.google.android.material.slider.Slider <com.google.android.material.slider.Slider
android:id="@+id/slider_brightness" android:id="@+id/slider_brightness"
@@ -178,29 +190,6 @@
app:layout_constraintStart_toEndOf="@id/guideline_vertical" app:layout_constraintStart_toEndOf="@id/guideline_vertical"
app:layout_constraintTop_toBottomOf="@id/textView_contrast" /> app:layout_constraintTop_toBottomOf="@id/textView_contrast" />
<TextView
android:id="@+id/textView_tip"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_normal"
android:layout_marginTop="@dimen/margin_normal"
android:text="@string/color_correction_hint"
android:textAppearance="?textAppearanceBodySmall"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline_vertical"
app:layout_constraintTop_toBottomOf="@id/slider_contrast" />
<CheckBox
android:id="@+id/checkBox_global"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:text="Save these settings globally and apply for all manga"
app:layout_constraintEnd_toEndOf="@id/textView_tip"
app:layout_constraintStart_toStartOf="@id/textView_tip"
app:layout_constraintTop_toBottomOf="@id/textView_tip" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView> </ScrollView>

View File

@@ -111,6 +111,17 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/imageView_before" /> app:layout_constraintTop_toBottomOf="@id/imageView_before" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_grayscale"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/grayscale"
android:textAppearance="?textAppearanceTitleMedium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/switch_invert" />
<TextView <TextView
android:id="@+id/textView_brightness" android:id="@+id/textView_brightness"
android:layout_width="0dp" android:layout_width="0dp"
@@ -120,7 +131,7 @@
android:textAppearance="?textAppearanceTitleMedium" android:textAppearance="?textAppearanceTitleMedium"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/switch_invert" /> app:layout_constraintTop_toBottomOf="@id/switch_grayscale" />
<com.google.android.material.slider.Slider <com.google.android.material.slider.Slider
android:id="@+id/slider_brightness" android:id="@+id/slider_brightness"
@@ -158,37 +169,15 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView_contrast" /> app:layout_constraintTop_toBottomOf="@id/textView_contrast" />
<TextView
android:id="@+id/textView_tip"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:layout_marginEnd="@dimen/margin_normal"
android:text="@string/color_correction_hint"
android:textAppearance="?textAppearanceBodySmall"
app:layout_constraintEnd_toStartOf="@id/button_reset"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/slider_contrast" />
<CheckBox
android:id="@+id/checkBox_global"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:text="Save these settings globally and apply for all manga"
app:layout_constraintEnd_toEndOf="@id/textView_tip"
app:layout_constraintStart_toStartOf="@id/textView_tip"
app:layout_constraintTop_toBottomOf="@id/textView_tip" />
<Button <Button
android:id="@+id/button_reset" android:id="@+id/button_reset"
style="?materialButtonOutlinedStyle" style="?materialButtonOutlinedStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/reset" android:text="@string/reset"
app:layout_constraintBottom_toBottomOf="@id/checkBox_global"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/textView_tip" /> app:layout_constraintTop_toBottomOf="@id/slider_contrast" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -535,4 +535,9 @@
<string name="error_search_not_supported">Search is not supported by this manga source</string> <string name="error_search_not_supported">Search is not supported by this manga source</string>
<string name="downloads_settings_info">You can enable download slowdown for each manga source individually in the source settings if you are having problems with server-side blocking</string> <string name="downloads_settings_info">You can enable download slowdown for each manga source individually in the source settings if you are having problems with server-side blocking</string>
<string name="skip">Skip</string> <string name="skip">Skip</string>
<string name="grayscale">Grayscale</string>
<string name="globally">Globally</string>
<string name="this_manga">This manga</string>
<string name="color_correction_apply_text">These settings can be applied globally or only to the current manga. If applied globally, individual settings will not be overridden.</string>
<string name="apply">Apply</string>
</resources> </resources>