Global color filter initial implementation #562

This commit is contained in:
Koitharu
2023-12-02 15:45:49 +02:00
parent 963d7d8d42
commit 53e00e4689
6 changed files with 103 additions and 21 deletions

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.core.prefs package org.koitharu.kotatsu.core.prefs
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.ConnectivityManager import android.net.ConnectivityManager
@@ -13,12 +14,16 @@ import androidx.core.content.edit
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.json.JSONArray import org.json.JSONArray
import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.core.network.DoHProvider import org.koitharu.kotatsu.core.network.DoHProvider
import org.koitharu.kotatsu.core.util.ext.connectivityManager import org.koitharu.kotatsu.core.util.ext.connectivityManager
import org.koitharu.kotatsu.core.util.ext.getEnumValue import org.koitharu.kotatsu.core.util.ext.getEnumValue
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.processLifecycleScope
import org.koitharu.kotatsu.core.util.ext.putEnumValue import org.koitharu.kotatsu.core.util.ext.putEnumValue
import org.koitharu.kotatsu.core.util.ext.takeIfReadable import org.koitharu.kotatsu.core.util.ext.takeIfReadable
import org.koitharu.kotatsu.core.util.ext.toUriOrNull import org.koitharu.kotatsu.core.util.ext.toUriOrNull
@@ -28,6 +33,7 @@ import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.find import org.koitharu.kotatsu.parsers.util.find
import org.koitharu.kotatsu.parsers.util.mapNotNullToSet import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.reader.domain.ReaderColorFilter
import java.io.File import java.io.File
import java.net.Proxy import java.net.Proxy
import java.util.Locale import java.util.Locale
@@ -293,6 +299,27 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
val isReaderKeepScreenOn: Boolean val isReaderKeepScreenOn: Boolean
get() = prefs.getBoolean(KEY_READER_SCREEN_ON, true) get() = prefs.getBoolean(KEY_READER_SCREEN_ON, true)
var readerColorFilter: ReaderColorFilter?
get() {
if (!prefs.getBoolean(KEY_CF_ENABLED, false)) {
return null
}
val brightness = prefs.getFloat(KEY_CF_BRIGHTNESS, 0f)
val contrast = prefs.getFloat(KEY_CF_CONTRAST, 0f)
val inverted = prefs.getBoolean(KEY_CF_INVERTED, false)
return ReaderColorFilter(brightness, contrast, inverted)
}
set(value) {
prefs.edit {
putBoolean(KEY_CF_ENABLED, value != null)
if (value != null) {
putFloat(KEY_CF_BRIGHTNESS, value.brightness)
putFloat(KEY_CF_CONTRAST, value.contrast)
putBoolean(KEY_CF_INVERTED, value.isInverted)
}
}
}
val isImagesProxyEnabled: Boolean val isImagesProxyEnabled: Boolean
get() = prefs.getBoolean(KEY_IMAGES_PROXY, false) get() = prefs.getBoolean(KEY_IMAGES_PROXY, false)
@@ -425,6 +452,17 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
return result return result
} }
@SuppressLint("ApplySharedPref")
private inline fun SharedPreferences.editAsync(
action: SharedPreferences.Editor.() -> Unit
) {
val editor = edit()
action(editor)
processLifecycleScope.launch(Dispatchers.IO, CoroutineStart.ATOMIC) {
editor.commit()
}
}
companion object { companion object {
const val PAGE_SWITCH_TAPS = "taps" const val PAGE_SWITCH_TAPS = "taps"
@@ -535,6 +573,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_CONTRAST = "cf_contrast"
const val KEY_CF_INVERTED = "cf_inverted"
// About // About
const val KEY_APP_UPDATE = "app_update" const val KEY_APP_UPDATE = "app_update"

View File

@@ -90,7 +90,12 @@ class ColorFilterConfigActivity :
override fun onClick(v: View) { override fun onClick(v: View) {
when (v.id) { when (v.id) {
R.id.button_done -> viewModel.save() R.id.button_done -> if (viewBinding.checkBoxGlobal.isChecked) {
viewModel.saveGlobally()
} else {
viewModel.save()
}
R.id.button_reset -> viewModel.reset() R.id.button_reset -> viewModel.reset()
} }
} }

View File

@@ -38,7 +38,7 @@ class ColorFilterConfigViewModel @Inject constructor(
init { init {
launchLoadingJob { launchLoadingJob {
initialColorFilter = mangaDataRepository.getColorFilter(manga.id) initialColorFilter = mangaDataRepository.getColorFilter(manga.id) ?: settings.readerColorFilter
colorFilter.value = initialColorFilter colorFilter.value = initialColorFilter
} }
} }
@@ -83,4 +83,9 @@ class ColorFilterConfigViewModel @Inject constructor(
onDismiss.call(Unit) onDismiss.call(Unit)
} }
} }
fun saveGlobally() {
settings.readerColorFilter = colorFilter.value
onDismiss.call(Unit)
}
} }

View File

@@ -34,7 +34,7 @@ class ReaderSettings(
get() = settings.zoomMode get() = settings.zoomMode
val colorFilter: ReaderColorFilter? val colorFilter: ReaderColorFilter?
get() = colorFilterFlow.value?.takeUnless { it.isEmpty } get() = colorFilterFlow.value?.takeUnless { it.isEmpty } ?: settings.readerColorFilter
val isReaderOptimizationEnabled: Boolean val isReaderOptimizationEnabled: Boolean
get() = settings.isReaderOptimizationEnabled get() = settings.isReaderOptimizationEnabled
@@ -96,6 +96,18 @@ class ReaderSettings(
FlowCollector<ReaderColorFilter?>, FlowCollector<ReaderColorFilter?>,
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
private val settingsKeys = setOf(
AppSettings.KEY_ZOOM_MODE,
AppSettings.KEY_PAGES_NUMBERS,
AppSettings.KEY_READER_BACKGROUND,
AppSettings.KEY_32BIT_COLOR,
AppSettings.KEY_READER_OPTIMIZE,
AppSettings.KEY_CF_ENABLED,
AppSettings.KEY_CF_CONTRAST,
AppSettings.KEY_CF_BRIGHTNESS,
AppSettings.KEY_CF_INVERTED,
)
override suspend fun emit(value: ReaderColorFilter?) { override suspend fun emit(value: ReaderColorFilter?) {
withContext(Dispatchers.Main.immediate) { withContext(Dispatchers.Main.immediate) {
notifyChanged() notifyChanged()
@@ -103,13 +115,7 @@ class ReaderSettings(
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if ( if (key in settingsKeys) {
key == AppSettings.KEY_ZOOM_MODE ||
key == AppSettings.KEY_PAGES_NUMBERS ||
key == AppSettings.KEY_READER_BACKGROUND ||
key == AppSettings.KEY_32BIT_COLOR ||
key == AppSettings.KEY_READER_OPTIMIZE
) {
notifyChanged() notifyChanged()
} }
} }

View File

@@ -179,6 +179,7 @@
app:layout_constraintTop_toBottomOf="@id/textView_contrast" /> app:layout_constraintTop_toBottomOf="@id/textView_contrast" />
<TextView <TextView
android:id="@+id/textView_tip"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_normal" android:layout_marginStart="@dimen/margin_normal"
@@ -189,6 +190,17 @@
app:layout_constraintStart_toEndOf="@id/guideline_vertical" app:layout_constraintStart_toEndOf="@id/guideline_vertical"
app:layout_constraintTop_toBottomOf="@id/slider_contrast" /> 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

@@ -158,25 +158,37 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView_contrast" /> app:layout_constraintTop_toBottomOf="@id/textView_contrast" />
<Button
android:id="@+id/button_reset"
style="?materialButtonOutlinedStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:text="@string/reset"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/slider_contrast" />
<TextView <TextView
android:id="@+id/textView_tip"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:layout_marginEnd="@dimen/margin_normal" android:layout_marginEnd="@dimen/margin_normal"
android:text="@string/color_correction_hint" android:text="@string/color_correction_hint"
android:textAppearance="?textAppearanceBodySmall" android:textAppearance="?textAppearanceBodySmall"
app:layout_constraintEnd_toStartOf="@id/button_reset" app:layout_constraintEnd_toStartOf="@id/button_reset"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/button_reset" /> 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
android:id="@+id/button_reset"
style="?materialButtonOutlinedStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reset"
app:layout_constraintBottom_toBottomOf="@id/checkBox_global"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/textView_tip" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>