Global color filter initial implementation #562
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user