diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/MangaDatabase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/MangaDatabase.kt index 5378e420e..d70dcb983 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/MangaDatabase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/MangaDatabase.kt @@ -24,6 +24,7 @@ import org.koitharu.kotatsu.core.db.migrations.Migration11To12 import org.koitharu.kotatsu.core.db.migrations.Migration12To13 import org.koitharu.kotatsu.core.db.migrations.Migration13To14 import org.koitharu.kotatsu.core.db.migrations.Migration14To15 +import org.koitharu.kotatsu.core.db.migrations.Migration15To16 import org.koitharu.kotatsu.core.db.migrations.Migration1To2 import org.koitharu.kotatsu.core.db.migrations.Migration2To3 import org.koitharu.kotatsu.core.db.migrations.Migration3To4 @@ -48,7 +49,7 @@ import org.koitharu.kotatsu.tracker.data.TrackEntity import org.koitharu.kotatsu.tracker.data.TrackLogEntity import org.koitharu.kotatsu.tracker.data.TracksDao -const val DATABASE_VERSION = 15 +const val DATABASE_VERSION = 16 @Database( entities = [ @@ -100,6 +101,7 @@ val databaseMigrations: Array Migration12To13(), Migration13To14(), Migration14To15(), + Migration15To16(), ) fun MangaDatabase(context: Context): MangaDatabase = Room diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/MangaPrefsEntity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/MangaPrefsEntity.kt index a5ccdb765..98512e8b8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/MangaPrefsEntity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/MangaPrefsEntity.kt @@ -23,4 +23,5 @@ data class MangaPrefsEntity( @ColumnInfo(name = "mode") val mode: Int, @ColumnInfo(name = "cf_brightness") val cfBrightness: Float, @ColumnInfo(name = "cf_contrast") val cfContrast: Float, + @ColumnInfo(name = "cf_invert") val cfInvert: Boolean, ) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/migrations/Migration15To16.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/migrations/Migration15To16.kt new file mode 100644 index 000000000..aba52e885 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/migrations/Migration15To16.kt @@ -0,0 +1,11 @@ +package org.koitharu.kotatsu.core.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration15To16 : Migration(15, 16) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE preferences ADD COLUMN `cf_invert` INTEGER NOT NULL DEFAULT 0") + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt index 45c2e5df9..81b13ada2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt @@ -5,14 +5,12 @@ import dagger.Reusable import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map -import okhttp3.OkHttpClient import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity import org.koitharu.kotatsu.core.db.entity.toEntities import org.koitharu.kotatsu.core.db.entity.toEntity import org.koitharu.kotatsu.core.db.entity.toManga import org.koitharu.kotatsu.core.db.entity.toMangaTags -import org.koitharu.kotatsu.core.network.MangaHttpClient import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource @@ -22,7 +20,6 @@ import javax.inject.Inject @Reusable class MangaDataRepository @Inject constructor( - @MangaHttpClient private val okHttpClient: OkHttpClient, private val db: MangaDatabase, ) { @@ -42,6 +39,7 @@ class MangaDataRepository @Inject constructor( entity.copy( cfBrightness = colorFilter?.brightness ?: 0f, cfContrast = colorFilter?.contrast ?: 0f, + cfInvert = colorFilter?.isInverted ?: false, ), ) } @@ -84,8 +82,8 @@ class MangaDataRepository @Inject constructor( } private fun MangaPrefsEntity.getColorFilterOrNull(): ReaderColorFilter? { - return if (cfBrightness != 0f || cfContrast != 0f) { - ReaderColorFilter(cfBrightness, cfContrast) + return if (cfBrightness != 0f || cfContrast != 0f || cfInvert) { + ReaderColorFilter(cfBrightness, cfContrast, cfInvert) } else { null } @@ -96,5 +94,6 @@ class MangaDataRepository @Inject constructor( mode = -1, cfBrightness = 0f, cfContrast = 0f, + cfInvert = false, ) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/ReaderColorFilter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/ReaderColorFilter.kt index 1511a29fc..8852ff2da 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/ReaderColorFilter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/ReaderColorFilter.kt @@ -6,35 +6,27 @@ import android.graphics.ColorMatrixColorFilter class ReaderColorFilter( val brightness: Float, val contrast: Float, + val isInverted: Boolean, ) { val isEmpty: Boolean - get() = brightness == 0f && contrast == 0f + get() = !isInverted && brightness == 0f && contrast == 0f fun toColorFilter(): ColorMatrixColorFilter { val cm = ColorMatrix() - val scale = brightness + 1f - cm.setScale(scale, scale, scale, 1f) + if (isInverted) { + cm.inverted() + } + cm.setBrightness(brightness) cm.setContrast(contrast) return ColorMatrixColorFilter(cm) } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ReaderColorFilter - - if (brightness != other.brightness) return false - if (contrast != other.contrast) return false - - return true - } - - override fun hashCode(): Int { - var result = brightness.hashCode() - result = 31 * result + contrast.hashCode() - return result + private fun ColorMatrix.setBrightness(brightness: Float) { + val scale = brightness + 1f + val matrix = ColorMatrix() + matrix.setScale(scale, scale, scale, 1f) + postConcat(matrix) } private fun ColorMatrix.setContrast(contrast: Float) { @@ -49,4 +41,32 @@ class ReaderColorFilter( val matrix = ColorMatrix(array) postConcat(matrix) } + + private fun ColorMatrix.inverted() { + val matrix = floatArrayOf( + -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.0f, -1.0f, 0.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, + ) + set(matrix) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ReaderColorFilter + + if (brightness != other.brightness) return false + if (contrast != other.contrast) return false + return isInverted == other.isInverted + } + + override fun hashCode(): Int { + var result = brightness.hashCode() + result = 31 * result + contrast.hashCode() + result = 31 * result + isInverted.hashCode() + return result + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt index d77cdc62c..7bcdc6c63 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt @@ -6,6 +6,7 @@ import android.content.res.Resources import android.os.Bundle import android.view.View import android.view.ViewGroup +import android.widget.CompoundButton import androidx.activity.viewModels import androidx.core.graphics.Insets import androidx.core.view.updateLayoutParams @@ -26,6 +27,7 @@ import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.indicator import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent +import org.koitharu.kotatsu.core.util.ext.setChecked import org.koitharu.kotatsu.core.util.ext.setValueRounded import org.koitharu.kotatsu.databinding.ActivityColorFilterBinding import org.koitharu.kotatsu.parsers.model.Manga @@ -39,7 +41,7 @@ import com.google.android.material.R as materialR class ColorFilterConfigActivity : BaseActivity(), Slider.OnChangeListener, - View.OnClickListener { + View.OnClickListener, CompoundButton.OnCheckedChangeListener { @Inject lateinit var coil: ImageLoader @@ -58,6 +60,7 @@ class ColorFilterConfigActivity : val formatter = PercentLabelFormatter(resources) viewBinding.sliderContrast.setLabelFormatter(formatter) viewBinding.sliderBrightness.setLabelFormatter(formatter) + viewBinding.switchInvert.setOnCheckedChangeListener(this) viewBinding.buttonDone.setOnClickListener(this) viewBinding.buttonReset.setOnClickListener(this) @@ -80,6 +83,10 @@ class ColorFilterConfigActivity : } } + override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) { + viewModel.setInversion(isChecked) + } + override fun onClick(v: View) { when (v.id) { R.id.button_done -> viewModel.save() @@ -103,13 +110,14 @@ class ColorFilterConfigActivity : private fun onColorFilterChanged(readerColorFilter: ReaderColorFilter?) { viewBinding.sliderBrightness.setValueRounded(readerColorFilter?.brightness ?: 0f) viewBinding.sliderContrast.setValueRounded(readerColorFilter?.contrast ?: 0f) + viewBinding.switchInvert.setChecked(readerColorFilter?.isInverted ?: false, false) viewBinding.imageViewAfter.colorFilter = readerColorFilter?.toColorFilter() } private fun onPreviewChanged(preview: MangaPage?) { if (preview == null) return ImageRequest.Builder(this@ColorFilterConfigActivity) - .data(preview.url) + .data(preview) .scale(Scale.FILL) .decodeRegion() .tag(preview.source) @@ -117,7 +125,7 @@ class ColorFilterConfigActivity : .error(R.drawable.ic_error_placeholder) .size(ViewSizeResolver(viewBinding.imageViewBefore)) .allowRgb565(false) - .target(ShadowViewTarget(viewBinding.imageViewBefore, viewBinding.imageViewAfter)) + .target(DoubleViewTarget(viewBinding.imageViewBefore, viewBinding.imageViewAfter)) .enqueueWith(coil) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt index d4568ed2d..74e7d1044 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt @@ -55,12 +55,32 @@ class ColorFilterConfigViewModel @Inject constructor( fun setBrightness(brightness: Float) { val cf = colorFilter.value - colorFilter.value = ReaderColorFilter(brightness, cf?.contrast ?: 0f).takeUnless { it.isEmpty } + colorFilter.value = ReaderColorFilter( + brightness = brightness, + contrast = cf?.contrast ?: 0f, + isInverted = cf?.isInverted ?: false, + ).takeUnless { it.isEmpty } } fun setContrast(contrast: Float) { val cf = colorFilter.value - colorFilter.value = ReaderColorFilter(cf?.brightness ?: 0f, contrast).takeUnless { it.isEmpty } + colorFilter.value = ReaderColorFilter( + brightness = cf?.brightness ?: 0f, + contrast = contrast, + isInverted = cf?.isInverted ?: false, + ).takeUnless { it.isEmpty } + } + + fun setInversion(invert: Boolean) { + val cf = colorFilter.value + if (invert == cf?.isInverted) { + return + } + colorFilter.value = ReaderColorFilter( + brightness = cf?.brightness ?: 0f, + contrast = cf?.contrast ?: 0f, + isInverted = invert, + ).takeUnless { it.isEmpty } } fun reset() { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ShadowViewTarget.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/DoubleViewTarget.kt similarity index 58% rename from app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ShadowViewTarget.kt rename to app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/DoubleViewTarget.kt index fcd05cbd3..28bf4f38c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ShadowViewTarget.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/DoubleViewTarget.kt @@ -4,15 +4,15 @@ import android.graphics.drawable.Drawable import android.widget.ImageView import coil.target.ImageViewTarget -class ShadowViewTarget( - view: ImageView, - private val shadowView: ImageView, -) : ImageViewTarget(view) { +class DoubleViewTarget( + primaryView: ImageView, + private val secondaryView: ImageView, +) : ImageViewTarget(primaryView) { override var drawable: Drawable? get() = super.drawable set(value) { super.drawable = value - shadowView.setImageDrawable(value?.constantState?.newDrawable()) + secondaryView.setImageDrawable(value?.constantState?.newDrawable()) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt index 03a6c49e9..c1b5f5f4b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt @@ -26,7 +26,7 @@ class ReaderSettings( get() = settings.zoomMode val colorFilter: ReaderColorFilter? - get() = colorFilterFlow.value + get() = colorFilterFlow.value?.takeUnless { it.isEmpty } val isPagesNumbersEnabled: Boolean get() = settings.isPagesNumbersEnabled diff --git a/app/src/main/res/layout-w600dp/activity_color_filter.xml b/app/src/main/res/layout-w600dp/activity_color_filter.xml new file mode 100644 index 000000000..7e8bc7dd5 --- /dev/null +++ b/app/src/main/res/layout-w600dp/activity_color_filter.xml @@ -0,0 +1,196 @@ + + + + + +