From 92aa96a64417945cd96cb23bc39be94188e16b93 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 27 Aug 2022 16:37:46 +0300 Subject: [PATCH] Color filter support in reader --- app/src/main/AndroidManifest.xml | 2 +- .../base/domain/MangaDataRepository.kt | 50 ++++++- .../koitharu/kotatsu/core/db/MangaDatabase.kt | 3 +- .../kotatsu/core/db/dao/PreferencesDao.kt | 6 +- .../core/db/entity/MangaPrefsEntity.kt | 17 ++- .../core/db/migrations/Migration14To15.kt | 12 ++ .../koitharu/kotatsu/main/ui/MainViewModel.kt | 3 +- .../reader/domain/ReaderColorFilter.kt | 52 +++++++ .../kotatsu/reader/ui/ReaderViewModel.kt | 23 ++- .../colorfilter/ColorFilterConfigActivity.kt | 141 ++++++++++++------ .../ColorFilterConfigBackPressedDispatcher.kt | 39 +++++ .../colorfilter/ColorFilterConfigViewModel.kt | 74 +++++++++ .../ui/config/ReaderConfigBottomSheet.kt | 3 +- .../reader/ui/config/ReaderSettings.kt | 68 +++++++++ .../kotatsu/reader/ui/pager/BasePageHolder.kt | 16 +- .../reader/ui/pager/BaseReaderAdapter.kt | 18 ++- .../reader/ui/pager/PageHolderDelegate.kt | 34 +++-- .../ui/pager/reversed/ReversedPageHolder.kt | 21 +-- .../ui/pager/reversed/ReversedPagesAdapter.kt | 14 +- .../pager/reversed/ReversedReaderFragment.kt | 10 +- .../reader/ui/pager/standard/PageHolder.kt | 9 +- .../ui/pager/standard/PagerReaderFragment.kt | 10 +- .../reader/ui/pager/standard/PagesAdapter.kt | 14 +- .../reader/ui/pager/webtoon/WebtoonAdapter.kt | 15 +- .../reader/ui/pager/webtoon/WebtoonHolder.kt | 14 +- .../ui/pager/webtoon/WebtoonReaderFragment.kt | 7 +- .../main/res/layout/activity_color_filter.xml | 70 +++++++-- .../main/res/layout/sheet_reader_config.xml | 2 +- app/src/main/res/values/strings.xml | 8 +- 29 files changed, 578 insertions(+), 177 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration14To15.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/reader/domain/ReaderColorFilter.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigBackPressedDispatcher.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e5d92b0e3..e662f0327 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -135,7 +135,7 @@ android:label="@string/sync" /> + android:label="@string/color_correction" /> { + return db.preferencesDao.observe(mangaId) + .map { it?.getColorFilterOrNull() } + .distinctUntilChanged() + } + suspend fun findMangaById(mangaId: Long): Manga? { return db.mangaDao.find(mangaId)?.toManga() } @@ -71,6 +92,14 @@ class MangaDataRepository @Inject constructor( return db.tagsDao.findTags(source.name).toMangaTags() } + private fun MangaPrefsEntity.getColorFilterOrNull(): ReaderColorFilter? { + return if (cfBrightness != 0f || cfContrast != 0f) { + ReaderColorFilter(cfBrightness, cfContrast) + } else { + null + } + } + /** * Automatic determine type of manga by page size * @return ReaderMode.WEBTOON if page is wide @@ -104,6 +133,13 @@ class MangaDataRepository @Inject constructor( return size.width * MIN_WEBTOON_RATIO < size.height } + private fun newEntity(mangaId: Long) = MangaPrefsEntity( + mangaId = mangaId, + mode = -1, + cfBrightness = 0f, + cfContrast = 0f, + ) + companion object { suspend fun getImageMimeType(file: File): String? = runInterruptible(Dispatchers.IO) { diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt index 5cbc17385..cdc781bfb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt @@ -35,7 +35,7 @@ import org.koitharu.kotatsu.tracker.data.TrackLogEntity import org.koitharu.kotatsu.tracker.data.TracksDao import org.koitharu.kotatsu.utils.ext.processLifecycleScope -const val DATABASE_VERSION = 14 +const val DATABASE_VERSION = 15 @Database( entities = [ @@ -86,6 +86,7 @@ val databaseMigrations: Array Migration11To12(), Migration12To13(), Migration13To14(), + Migration14To15(), ) fun MangaDatabase(context: Context): MangaDatabase = Room diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/dao/PreferencesDao.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/dao/PreferencesDao.kt index 9a957b206..b0c48b39c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/dao/PreferencesDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/dao/PreferencesDao.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.core.db.dao import androidx.room.* +import kotlinx.coroutines.flow.Flow import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity @Dao @@ -9,6 +10,9 @@ abstract class PreferencesDao { @Query("SELECT * FROM preferences WHERE manga_id = :mangaId") abstract suspend fun find(mangaId: Long): MangaPrefsEntity? + @Query("SELECT * FROM preferences WHERE manga_id = :mangaId") + abstract fun observe(mangaId: Long): Flow + @Insert(onConflict = OnConflictStrategy.IGNORE) abstract suspend fun insert(pref: MangaPrefsEntity): Long @@ -21,4 +25,4 @@ abstract class PreferencesDao { insert(pref) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/entity/MangaPrefsEntity.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/entity/MangaPrefsEntity.kt index a09ccd884..a5ccdb765 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/entity/MangaPrefsEntity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/entity/MangaPrefsEntity.kt @@ -12,12 +12,15 @@ import androidx.room.PrimaryKey entity = MangaEntity::class, parentColumns = ["manga_id"], childColumns = ["manga_id"], - onDelete = ForeignKey.CASCADE - ) - ] + onDelete = ForeignKey.CASCADE, + ), + ], ) -class MangaPrefsEntity( +data class MangaPrefsEntity( @PrimaryKey(autoGenerate = false) - @ColumnInfo(name = "manga_id") val mangaId: Long, - @ColumnInfo(name = "mode") val mode: Int -) \ No newline at end of file + @ColumnInfo(name = "manga_id") + val mangaId: Long, + @ColumnInfo(name = "mode") val mode: Int, + @ColumnInfo(name = "cf_brightness") val cfBrightness: Float, + @ColumnInfo(name = "cf_contrast") val cfContrast: Float, +) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration14To15.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration14To15.kt new file mode 100644 index 000000000..3dda50a12 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration14To15.kt @@ -0,0 +1,12 @@ +package org.koitharu.kotatsu.core.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration14To15 : Migration(14, 15) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE preferences ADD COLUMN `cf_brightness` REAL NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE preferences ADD COLUMN `cf_contrast` REAL NOT NULL DEFAULT 0") + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt index 0805d872e..72ea00cf0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt @@ -17,6 +17,7 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.sync.domain.SyncController import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.utils.SingleLiveEvent +import org.koitharu.kotatsu.utils.asFlowLiveData import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct @HiltViewModel @@ -32,7 +33,7 @@ class MainViewModel @Inject constructor( val isResumeEnabled = historyRepository .observeHasItems() - .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, false) + .asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, false) val counters = combine( appUpdateRepository.observeAvailableUpdate(), diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/domain/ReaderColorFilter.kt b/app/src/main/java/org/koitharu/kotatsu/reader/domain/ReaderColorFilter.kt new file mode 100644 index 000000000..1511a29fc --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/reader/domain/ReaderColorFilter.kt @@ -0,0 +1,52 @@ +package org.koitharu.kotatsu.reader.domain + +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter + +class ReaderColorFilter( + val brightness: Float, + val contrast: Float, +) { + + val isEmpty: Boolean + get() = brightness == 0f && contrast == 0f + + fun toColorFilter(): ColorMatrixColorFilter { + val cm = ColorMatrix() + val scale = brightness + 1f + cm.setScale(scale, scale, scale, 1f) + 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.setContrast(contrast: Float) { + val scale = contrast + 1f + val translate = (-.5f * scale + .5f) * 255f + val array = floatArrayOf( + scale, 0f, 0f, 0f, translate, + 0f, scale, 0f, 0f, translate, + 0f, 0f, scale, 0f, translate, + 0f, 0f, 0f, 1f, 0f, + ) + val matrix = ColorMatrix(array) + postConcat(matrix) + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt index dc558dc3b..147f8cd9f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt @@ -31,6 +31,7 @@ import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.reader.data.filterChapters import org.koitharu.kotatsu.reader.domain.ChaptersLoader import org.koitharu.kotatsu.reader.domain.PageLoader +import org.koitharu.kotatsu.reader.ui.config.ReaderSettings import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.asFlowLiveData @@ -86,6 +87,14 @@ class ReaderViewModel @AssistedInject constructor( valueProducer = { isReaderBarEnabled }, ) + val readerSettings = ReaderSettings( + parentScope = viewModelScope, + settings = settings, + colorFilterFlow = mangaData.flatMapLatest { + if (it == null) flowOf(null) else dataRepository.observeColorFilter(it.id) + }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null), + ) + val isScreenshotsBlockEnabled = combine( mangaData, settings.observeAsFlow(AppSettings.KEY_SCREENSHOTS_POLICY) { screenshotsPolicy }, @@ -94,8 +103,6 @@ class ReaderViewModel @AssistedInject constructor( (policy == ScreenshotsPolicy.BLOCK_NSFW && manga != null && manga.isNsfw) }.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, false) - val onZoomChanged = SingleLiveEvent() - val isBookmarkAdded: LiveData = currentState.flatMapLatest { state -> val manga = mangaData.value if (state == null || manga == null) { @@ -108,7 +115,6 @@ class ReaderViewModel @AssistedInject constructor( init { loadImpl() - subscribeToSettings() } override fun onCleared() { @@ -124,7 +130,7 @@ class ReaderViewModel @AssistedInject constructor( fun switchMode(newMode: ReaderMode) { launchJob { val manga = checkNotNull(mangaData.value) - dataRepository.savePreferences( + dataRepository.saveReaderMode( manga = manga, mode = newMode, ) @@ -300,13 +306,6 @@ class ReaderViewModel @AssistedInject constructor( } } - private fun subscribeToSettings() { - settings.observe() - .onEach { key -> - if (key == AppSettings.KEY_ZOOM_MODE) onZoomChanged.postCall(Unit) - }.launchIn(viewModelScope + Dispatchers.Default) - } - private fun List.trySublist(fromIndex: Int, toIndex: Int): List { val fromIndexBounded = fromIndex.coerceAtMost(lastIndex) val toIndexBounded = toIndex.coerceIn(fromIndexBounded, lastIndex) @@ -331,7 +330,7 @@ class ReaderViewModel @AssistedInject constructor( val isWebtoon = dataRepository.determineMangaIsWebtoon(repo, pages) if (isWebtoon) ReaderMode.WEBTOON else defaultMode }.onSuccess { - dataRepository.savePreferences(manga, it) + dataRepository.saveReaderMode(manga, it) }.onFailure { it.printStackTraceDebug() }.getOrDefault(defaultMode) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt index 48ce32c2f..e3953a555 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt @@ -2,40 +2,54 @@ package org.koitharu.kotatsu.reader.ui.colorfilter import android.content.Context import android.content.Intent -import android.graphics.Color -import android.graphics.LightingColorFilter +import android.content.res.Resources import android.os.Bundle +import android.view.View import android.view.ViewGroup import androidx.core.graphics.Insets import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding -import androidx.lifecycle.lifecycleScope import coil.ImageLoader import coil.request.ImageRequest import coil.size.Scale import coil.size.ViewSizeResolver +import com.google.android.material.R as materialR +import com.google.android.material.slider.LabelFormatter import com.google.android.material.slider.Slider import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch -import org.koitharu.kotatsu.base.ui.BaseActivity -import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaPages -import org.koitharu.kotatsu.core.parser.MangaRepository -import org.koitharu.kotatsu.databinding.ActivityColorFilterBinding -import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.utils.ext.enqueueWith -import org.koitharu.kotatsu.utils.ext.referer import javax.inject.Inject -import kotlin.math.roundToInt -import com.google.android.material.R as materialR +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.base.ui.BaseActivity +import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaPages +import org.koitharu.kotatsu.databinding.ActivityColorFilterBinding +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaPage +import org.koitharu.kotatsu.parsers.util.format +import org.koitharu.kotatsu.reader.domain.ReaderColorFilter +import org.koitharu.kotatsu.utils.ext.assistedViewModels +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.getParcelableExtraCompat +import org.koitharu.kotatsu.utils.ext.referer @AndroidEntryPoint -class ColorFilterConfigActivity : BaseActivity(), Slider.OnChangeListener { +class ColorFilterConfigActivity : + BaseActivity(), + Slider.OnChangeListener, + View.OnClickListener { @Inject lateinit var coil: ImageLoader @Inject - lateinit var mangaRepositoryFacotry: MangaRepository.Factory + lateinit var viewModelFactory: ColorFilterConfigViewModel.Factory + + private val viewModel: ColorFilterConfigViewModel by assistedViewModels { + viewModelFactory.create( + manga = checkNotNull(intent.getParcelableExtraCompat(EXTRA_MANGA)?.manga), + page = checkNotNull(intent.getParcelableExtraCompat(EXTRA_PAGES)?.pages?.firstOrNull()), + ) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -44,14 +58,38 @@ class ColorFilterConfigActivity : BaseActivity(), Sl setDisplayHomeAsUpEnabled(true) setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material) } - binding.sliderLightness.addOnChangeListener(this) - binding.sliderSaturation.addOnChangeListener(this) - initPreview() - updateFilter() + binding.sliderBrightness.addOnChangeListener(this) + binding.sliderContrast.addOnChangeListener(this) + val formatter = PercentLabelFormatter(resources) + binding.sliderContrast.setLabelFormatter(formatter) + binding.sliderBrightness.setLabelFormatter(formatter) + binding.buttonDone.setOnClickListener(this) + binding.buttonReset.setOnClickListener(this) + + onBackPressedDispatcher.addCallback(ColorFilterConfigBackPressedDispatcher(this, viewModel)) + + viewModel.colorFilter.observe(this, this::onColorFilterChanged) + viewModel.isLoading.observe(this, this::onLoadingChanged) + viewModel.preview.observe(this, this::onPreviewChanged) + viewModel.onDismiss.observe(this) { + finishAfterTransition() + } } override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) { - updateFilter() + if (fromUser) { + when (slider.id) { + R.id.slider_brightness -> viewModel.setBrightness(value) + R.id.slider_contrast -> viewModel.setContrast(value) + } + } + } + + override fun onClick(v: View) { + when (v.id) { + R.id.button_done -> viewModel.save() + R.id.button_reset -> viewModel.reset() + } } override fun onWindowInsetsChanged(insets: Insets) { @@ -67,42 +105,49 @@ class ColorFilterConfigActivity : BaseActivity(), Sl } } - private fun updateFilter() { - - fun Int.toColor() = Color.rgb(this, this, this) - - val cf = LightingColorFilter( - binding.sliderSaturation.value.roundToInt().toColor(), - binding.sliderLightness.value.roundToInt().toColor(), - ) - binding.imageViewAfter.colorFilter = cf + private fun onColorFilterChanged(readerColorFilter: ReaderColorFilter?) { + binding.sliderBrightness.value = readerColorFilter?.brightness ?: 0f + binding.sliderContrast.value = readerColorFilter?.contrast ?: 0f + binding.imageViewAfter.colorFilter = readerColorFilter?.toColorFilter() } - private fun initPreview() { - val page = intent?.getParcelableExtra(EXTRA_PAGES)?.pages?.firstOrNull() - if (page == null) { - finishAfterTransition() - return - } - lifecycleScope.launch { - val repository = mangaRepositoryFacotry.create(page.source) - val url = repository.getPageUrl(page) - ImageRequest.Builder(this@ColorFilterConfigActivity) - .data(url) - .referer(page.referer) - .scale(Scale.FILL) - .size(ViewSizeResolver(binding.imageViewBefore)) - .allowRgb565(false) - .target(ShadowViewTarget(binding.imageViewBefore, binding.imageViewAfter)) - .enqueueWith(coil) + private fun onPreviewChanged(preview: MangaPage?) { + if (preview == null) return + ImageRequest.Builder(this@ColorFilterConfigActivity) + .data(preview.url) + .referer(preview.referer) + .scale(Scale.FILL) + .error(R.drawable.ic_error_placeholder) + .size(ViewSizeResolver(binding.imageViewBefore)) + .allowRgb565(false) + .target(ShadowViewTarget(binding.imageViewBefore, binding.imageViewAfter)) + .enqueueWith(coil) + } + + private fun onLoadingChanged(isLoading: Boolean) { + binding.sliderContrast.isEnabled = !isLoading + binding.sliderBrightness.isEnabled = !isLoading + binding.buttonDone.isEnabled = !isLoading + } + + private class PercentLabelFormatter(resources: Resources) : LabelFormatter { + + private val pattern = resources.getString(R.string.percent_string_pattern) + + override fun getFormattedValue(value: Float): String { + val percent = ((value + 1f) * 100).format(0) + return pattern.format(percent) } } companion object { private const val EXTRA_PAGES = "pages" + private const val EXTRA_MANGA = "manga_id" - fun newIntent(context: Context, page: MangaPage) = Intent(context, ColorFilterConfigActivity::class.java) - .putExtra(EXTRA_PAGES, ParcelableMangaPages(listOf(page))) + fun newIntent(context: Context, manga: Manga, page: MangaPage) = + Intent(context, ColorFilterConfigActivity::class.java) + .putExtra(EXTRA_MANGA, ParcelableManga(manga, false)) + .putExtra(EXTRA_PAGES, ParcelableMangaPages(listOf(page))) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigBackPressedDispatcher.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigBackPressedDispatcher.kt new file mode 100644 index 000000000..a7d017c15 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigBackPressedDispatcher.kt @@ -0,0 +1,39 @@ +package org.koitharu.kotatsu.reader.ui.colorfilter + +import android.content.Context +import android.content.DialogInterface +import androidx.activity.OnBackPressedCallback +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.koitharu.kotatsu.R + +class ColorFilterConfigBackPressedDispatcher( + private val context: Context, + private val viewModel: ColorFilterConfigViewModel, +) : OnBackPressedCallback(true), DialogInterface.OnClickListener { + + override fun handleOnBackPressed() { + if (viewModel.isChanged) { + showConfirmation() + } else { + viewModel.onDismiss.call(Unit) + } + } + + override fun onClick(dialog: DialogInterface, which: Int) { + when (which) { + DialogInterface.BUTTON_NEGATIVE -> viewModel.onDismiss.call(Unit) + DialogInterface.BUTTON_NEUTRAL -> dialog.dismiss() + DialogInterface.BUTTON_POSITIVE -> viewModel.save() + } + } + + private fun showConfirmation() { + MaterialAlertDialogBuilder(context) + .setTitle(R.string.color_correction) + .setMessage(R.string.text_unsaved_changes_prompt) + .setNegativeButton(R.string.discard, this) + .setNeutralButton(android.R.string.cancel, this) + .setPositiveButton(R.string.save, this) + .show() + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt new file mode 100644 index 000000000..36074c4d7 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt @@ -0,0 +1,74 @@ +package org.koitharu.kotatsu.reader.ui.colorfilter + +import androidx.lifecycle.MutableLiveData +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import org.koitharu.kotatsu.base.domain.MangaDataRepository +import org.koitharu.kotatsu.base.ui.BaseViewModel +import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaPage +import org.koitharu.kotatsu.reader.domain.ReaderColorFilter +import org.koitharu.kotatsu.utils.SingleLiveEvent + +class ColorFilterConfigViewModel @AssistedInject constructor( + @Assisted private val manga: Manga, + @Assisted page: MangaPage, + private val mangaRepositoryFactory: MangaRepository.Factory, + private val mangaDataRepository: MangaDataRepository, +) : BaseViewModel() { + + private var initialColorFilter: ReaderColorFilter? = null + val colorFilter = MutableLiveData(null) + val onDismiss = SingleLiveEvent() + val preview = MutableLiveData(null) + + val isChanged: Boolean + get() = colorFilter.value != initialColorFilter + + init { + launchLoadingJob { + initialColorFilter = mangaDataRepository.getColorFilter(manga.id) + colorFilter.value = initialColorFilter + } + launchLoadingJob { + val repository = mangaRepositoryFactory.create(page.source) + val url = repository.getPageUrl(page) + preview.value = MangaPage( + id = page.id, + url = url, + referer = page.referer, + preview = page.preview, + source = page.source, + ) + } + } + + fun setBrightness(brightness: Float) { + val cf = colorFilter.value + colorFilter.value = ReaderColorFilter(brightness, cf?.contrast ?: 0f).takeUnless { it.isEmpty } + } + + fun setContrast(contrast: Float) { + val cf = colorFilter.value + colorFilter.value = ReaderColorFilter(cf?.brightness ?: 0f, contrast).takeUnless { it.isEmpty } + } + + fun reset() { + colorFilter.value = null + } + + fun save() { + launchLoadingJob { + mangaDataRepository.saveColorFilter(manga, colorFilter.value) + onDismiss.call(Unit) + } + } + + @AssistedFactory + interface Factory { + + fun create(manga: Manga, page: MangaPage): ColorFilterConfigViewModel + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt index 1ba6d2413..0eb155935 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt @@ -84,7 +84,8 @@ class ReaderConfigBottomSheet : } R.id.button_color_filter -> { val page = viewModel.getCurrentPage() ?: return - startActivity(ColorFilterConfigActivity.newIntent(v.context, page)) + val manga = viewModel.manga ?: return + startActivity(ColorFilterConfigActivity.newIntent(v.context, manga, page)) } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt new file mode 100644 index 000000000..9619e53df --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt @@ -0,0 +1,68 @@ +package org.koitharu.kotatsu.reader.ui.config + +import android.content.SharedPreferences +import androidx.lifecycle.MediatorLiveData +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.StateFlow +import org.koitharu.kotatsu.core.model.ZoomMode +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.reader.domain.ReaderColorFilter + +class ReaderSettings( + private val parentScope: CoroutineScope, + private val settings: AppSettings, + private val colorFilterFlow: StateFlow, +) : MediatorLiveData() { + + private val internalObserver = InternalObserver() + private var collectJob: Job? = null + + val zoomMode: ZoomMode + get() = settings.zoomMode + + val colorFilter: ReaderColorFilter? + get() = colorFilterFlow.value + + val isPagesNumbersEnabled: Boolean + get() = settings.isPagesNumbersEnabled + + override fun onInactive() { + super.onInactive() + settings.unsubscribe(internalObserver) + collectJob?.cancel() + collectJob = null + } + + override fun onActive() { + super.onActive() + settings.subscribe(internalObserver) + collectJob?.cancel() + collectJob = parentScope.launch { + colorFilterFlow.collect(internalObserver) + } + } + + override fun getValue() = this + + private fun notifyChanged() { + value = value + } + + private inner class InternalObserver : + FlowCollector, + SharedPreferences.OnSharedPreferenceChangeListener { + + override suspend fun emit(value: ReaderColorFilter?) { + withContext(Dispatchers.Main.immediate) { + notifyChanged() + } + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { + if (key == AppSettings.KEY_ZOOM_MODE || key == AppSettings.KEY_PAGES_NUMBERS) { + notifyChanged() + } + } + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt index d3980c687..0f752c861 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt @@ -5,15 +5,15 @@ import androidx.annotation.CallSuper import androidx.recyclerview.widget.RecyclerView import androidx.viewbinding.ViewBinding import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.LayoutPageInfoBinding import org.koitharu.kotatsu.reader.domain.PageLoader +import org.koitharu.kotatsu.reader.ui.config.ReaderSettings abstract class BasePageHolder( protected val binding: B, loader: PageLoader, - settings: AppSettings, - exceptionResolver: ExceptionResolver + settings: ReaderSettings, + exceptionResolver: ExceptionResolver, ) : RecyclerView.ViewHolder(binding.root), PageHolderDelegate.Callback { @Suppress("LeakingThis") @@ -37,8 +37,16 @@ abstract class BasePageHolder( protected abstract fun onBind(data: ReaderPage) + fun onAttachedToWindow() { + delegate.onAttachedToWindow() + } + + fun onDetachedFromWindow() { + delegate.onDetachedFromWindow() + } + @CallSuper open fun onRecycled() { delegate.onRecycle() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt index 0529c8ebe..33456c7d3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt @@ -7,14 +7,14 @@ import androidx.recyclerview.widget.RecyclerView import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.reader.domain.PageLoader +import org.koitharu.kotatsu.reader.ui.config.ReaderSettings import org.koitharu.kotatsu.utils.ext.resetTransformations @Suppress("LeakingThis") abstract class BaseReaderAdapter>( private val loader: PageLoader, - private val settings: AppSettings, + private val readerSettings: ReaderSettings, private val exceptionResolver: ExceptionResolver, ) : RecyclerView.Adapter() { @@ -35,6 +35,16 @@ abstract class BaseReaderAdapter>( super.onViewRecycled(holder) } + override fun onViewAttachedToWindow(holder: H) { + super.onViewAttachedToWindow(holder) + holder.onAttachedToWindow() + } + + override fun onViewDetachedFromWindow(holder: H) { + holder.onDetachedFromWindow() + super.onViewDetachedFromWindow(holder) + } + open fun getItem(position: Int): ReaderPage = differ.currentList[position] open fun getItemOrNull(position: Int) = differ.currentList.getOrNull(position) @@ -46,7 +56,7 @@ abstract class BaseReaderAdapter>( final override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, - ): H = onCreateViewHolder(parent, loader, settings, exceptionResolver) + ): H = onCreateViewHolder(parent, loader, readerSettings, exceptionResolver) suspend fun setItems(items: List) = suspendCoroutine { cont -> differ.submitList(items) { @@ -57,7 +67,7 @@ abstract class BaseReaderAdapter>( protected abstract fun onCreateViewHolder( parent: ViewGroup, loader: PageLoader, - settings: AppSettings, + settings: ReaderSettings, exceptionResolver: ExceptionResolver, ): H diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt index 5630cb1ba..ca83488bd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt @@ -2,26 +2,26 @@ package org.koitharu.kotatsu.reader.ui.pager import android.net.Uri import androidx.core.net.toUri +import androidx.lifecycle.Observer import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView +import java.io.File +import java.io.IOException import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver -import org.koitharu.kotatsu.core.model.ZoomMode -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.reader.domain.PageLoader -import java.io.File -import java.io.IOException +import org.koitharu.kotatsu.reader.ui.config.ReaderSettings class PageHolderDelegate( private val loader: PageLoader, - private val settings: AppSettings, + private val readerSettings: ReaderSettings, private val callback: Callback, - private val exceptionResolver: ExceptionResolver -) : SubsamplingScaleImageView.DefaultOnImageEventListener() { + private val exceptionResolver: ExceptionResolver, +) : SubsamplingScaleImageView.DefaultOnImageEventListener(), Observer { private val scope = loader.loaderScope + Dispatchers.Main.immediate private var state = State.EMPTY @@ -49,6 +49,14 @@ class PageHolderDelegate( } } + fun onAttachedToWindow() { + readerSettings.observeForever(this) + } + + fun onDetachedFromWindow() { + readerSettings.removeObserver(this) + } + fun onRecycle() { state = State.EMPTY file = null @@ -59,7 +67,7 @@ class PageHolderDelegate( override fun onReady() { state = State.SHOWING error = null - callback.onImageShowing(settings.zoomMode) + callback.onImageShowing(readerSettings) } override fun onImageLoaded() { @@ -79,6 +87,12 @@ class PageHolderDelegate( } } + override fun onChanged(t: ReaderSettings?) { + if (state == State.SHOWN) { + callback.onImageShowing(readerSettings) + } + } + private fun tryConvert(file: File, e: Exception) { val prevJob = job job = scope.launch { @@ -134,10 +148,10 @@ class PageHolderDelegate( fun onImageReady(uri: Uri) - fun onImageShowing(zoom: ZoomMode) + fun onImageShowing(settings: ReaderSettings) fun onImageShown() fun onProgressChanged(progress: Int) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPageHolder.kt index 33920e631..cdc5b53a9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPageHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPageHolder.kt @@ -6,16 +6,16 @@ import android.widget.FrameLayout import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.model.ZoomMode -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.ItemPageBinding import org.koitharu.kotatsu.reader.domain.PageLoader +import org.koitharu.kotatsu.reader.ui.config.ReaderSettings import org.koitharu.kotatsu.reader.ui.pager.standard.PageHolder class ReversedPageHolder( binding: ItemPageBinding, loader: PageLoader, - settings: AppSettings, - exceptionResolver: ExceptionResolver + settings: ReaderSettings, + exceptionResolver: ExceptionResolver, ) : PageHolder(binding, loader, settings, exceptionResolver) { init { @@ -23,13 +23,14 @@ class ReversedPageHolder( .gravity = Gravity.START or Gravity.BOTTOM } - override fun onImageShowing(zoom: ZoomMode) { + override fun onImageShowing(settings: ReaderSettings) { with(binding.ssiv) { maxScale = 2f * maxOf( width / sWidth.toFloat(), - height / sHeight.toFloat() + height / sHeight.toFloat(), ) - when (zoom) { + binding.ssiv.colorFilter = settings.colorFilter?.toColorFilter() + when (settings.zoomMode) { ZoomMode.FIT_CENTER -> { setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE) resetScaleAndCenter() @@ -39,7 +40,7 @@ class ReversedPageHolder( minScale = height / sHeight.toFloat() setScaleAndCenter( minScale, - PointF(sWidth.toFloat(), sHeight / 2f) + PointF(sWidth.toFloat(), sHeight / 2f), ) } ZoomMode.FIT_WIDTH -> { @@ -47,17 +48,17 @@ class ReversedPageHolder( minScale = width / sWidth.toFloat() setScaleAndCenter( minScale, - PointF(sWidth / 2f, 0f) + PointF(sWidth / 2f, 0f), ) } ZoomMode.KEEP_START -> { setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE) setScaleAndCenter( maxScale, - PointF(sWidth.toFloat(), 0f) + PointF(sWidth.toFloat(), 0f), ) } } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPagesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPagesAdapter.kt index aebb58b18..46c1f1690 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPagesAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPagesAdapter.kt @@ -3,26 +3,26 @@ package org.koitharu.kotatsu.reader.ui.pager.reversed import android.view.LayoutInflater import android.view.ViewGroup import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.ItemPageBinding import org.koitharu.kotatsu.reader.domain.PageLoader +import org.koitharu.kotatsu.reader.ui.config.ReaderSettings import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter class ReversedPagesAdapter( loader: PageLoader, - settings: AppSettings, - exceptionResolver: ExceptionResolver + settings: ReaderSettings, + exceptionResolver: ExceptionResolver, ) : BaseReaderAdapter(loader, settings, exceptionResolver) { override fun onCreateViewHolder( parent: ViewGroup, loader: PageLoader, - settings: AppSettings, - exceptionResolver: ExceptionResolver + settings: ReaderSettings, + exceptionResolver: ExceptionResolver, ) = ReversedPageHolder( binding = ItemPageBinding.inflate(LayoutInflater.from(parent.context), parent, false), loader = loader, settings = settings, - exceptionResolver = exceptionResolver + exceptionResolver = exceptionResolver, ) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt index 30a8b7331..9a1c297f2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt @@ -7,10 +7,8 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.children import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import kotlin.math.absoluteValue import kotlinx.coroutines.async -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.pager.BaseReader @@ -25,9 +23,6 @@ import org.koitharu.kotatsu.utils.ext.viewLifecycleScope @AndroidEntryPoint class ReversedReaderFragment : BaseReader() { - @Inject - lateinit var settings: AppSettings - private var pagerAdapter: ReversedPagesAdapter? = null override fun onInflateView( @@ -38,7 +33,7 @@ class ReversedReaderFragment : BaseReader() { @SuppressLint("NotifyDataSetChanged") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - pagerAdapter = ReversedPagesAdapter(viewModel.pageLoader, settings, exceptionResolver) + pagerAdapter = ReversedPagesAdapter(viewModel.pageLoader, viewModel.readerSettings, exceptionResolver) with(binding.pager) { adapter = pagerAdapter offscreenPageLimit = 2 @@ -54,9 +49,6 @@ class ReversedReaderFragment : BaseReader() { } } } - viewModel.onZoomChanged.observe(viewLifecycleOwner) { - pagerAdapter?.notifyDataSetChanged() - } } override fun onDestroyView() { diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt index 397ce0b4e..fba2d45cf 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt @@ -12,9 +12,9 @@ import kotlinx.coroutines.asExecutor import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.model.ZoomMode -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.ItemPageBinding import org.koitharu.kotatsu.reader.domain.PageLoader +import org.koitharu.kotatsu.reader.ui.config.ReaderSettings import org.koitharu.kotatsu.reader.ui.pager.BasePageHolder import org.koitharu.kotatsu.reader.ui.pager.ReaderPage import org.koitharu.kotatsu.utils.ext.* @@ -22,7 +22,7 @@ import org.koitharu.kotatsu.utils.ext.* open class PageHolder( binding: ItemPageBinding, loader: PageLoader, - settings: AppSettings, + settings: ReaderSettings, exceptionResolver: ExceptionResolver, ) : BasePageHolder(binding, loader, settings, exceptionResolver), View.OnClickListener { @@ -66,12 +66,13 @@ open class PageHolder( binding.ssiv.setImage(ImageSource.uri(uri)) } - override fun onImageShowing(zoom: ZoomMode) { + override fun onImageShowing(settings: ReaderSettings) { binding.ssiv.maxScale = 2f * maxOf( binding.ssiv.width / binding.ssiv.sWidth.toFloat(), binding.ssiv.height / binding.ssiv.sHeight.toFloat(), ) - when (zoom) { + binding.ssiv.colorFilter = settings.colorFilter?.toColorFilter() + when (settings.zoomMode) { ZoomMode.FIT_CENTER -> { binding.ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE) binding.ssiv.resetScaleAndCenter() diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt index bb3a583c8..a7d526d95 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt @@ -7,10 +7,8 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.children import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import kotlin.math.absoluteValue import kotlinx.coroutines.async -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.pager.BaseReader @@ -24,9 +22,6 @@ import org.koitharu.kotatsu.utils.ext.viewLifecycleScope @AndroidEntryPoint class PagerReaderFragment : BaseReader() { - @Inject - lateinit var settings: AppSettings - private var pagesAdapter: PagesAdapter? = null override fun onInflateView( @@ -37,7 +32,7 @@ class PagerReaderFragment : BaseReader() { @SuppressLint("NotifyDataSetChanged") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - pagesAdapter = PagesAdapter(viewModel.pageLoader, settings, exceptionResolver) + pagesAdapter = PagesAdapter(viewModel.pageLoader, viewModel.readerSettings, exceptionResolver) with(binding.pager) { adapter = pagesAdapter offscreenPageLimit = 2 @@ -53,9 +48,6 @@ class PagerReaderFragment : BaseReader() { } } } - viewModel.onZoomChanged.observe(viewLifecycleOwner) { - pagesAdapter?.notifyDataSetChanged() - } } override fun onDestroyView() { diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagesAdapter.kt index 553139c76..57badce92 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagesAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagesAdapter.kt @@ -3,26 +3,26 @@ package org.koitharu.kotatsu.reader.ui.pager.standard import android.view.LayoutInflater import android.view.ViewGroup import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.ItemPageBinding import org.koitharu.kotatsu.reader.domain.PageLoader +import org.koitharu.kotatsu.reader.ui.config.ReaderSettings import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter class PagesAdapter( loader: PageLoader, - settings: AppSettings, - exceptionResolver: ExceptionResolver + settings: ReaderSettings, + exceptionResolver: ExceptionResolver, ) : BaseReaderAdapter(loader, settings, exceptionResolver) { override fun onCreateViewHolder( parent: ViewGroup, loader: PageLoader, - settings: AppSettings, - exceptionResolver: ExceptionResolver + settings: ReaderSettings, + exceptionResolver: ExceptionResolver, ) = PageHolder( binding = ItemPageBinding.inflate(LayoutInflater.from(parent.context), parent, false), loader = loader, settings = settings, - exceptionResolver = exceptionResolver + exceptionResolver = exceptionResolver, ) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonAdapter.kt index a089d4d17..ea76b62f8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonAdapter.kt @@ -6,27 +6,28 @@ import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding import org.koitharu.kotatsu.reader.domain.PageLoader +import org.koitharu.kotatsu.reader.ui.config.ReaderSettings import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter class WebtoonAdapter( loader: PageLoader, - settings: AppSettings, - exceptionResolver: ExceptionResolver + settings: ReaderSettings, + exceptionResolver: ExceptionResolver, ) : BaseReaderAdapter(loader, settings, exceptionResolver) { override fun onCreateViewHolder( parent: ViewGroup, loader: PageLoader, - settings: AppSettings, - exceptionResolver: ExceptionResolver + settings: ReaderSettings, + exceptionResolver: ExceptionResolver, ) = WebtoonHolder( binding = ItemPageWebtoonBinding.inflate( LayoutInflater.from(parent.context), parent, - false + false, ), loader = loader, settings = settings, - exceptionResolver = exceptionResolver + exceptionResolver = exceptionResolver, ) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt index 4d9fc1ed8..89fb75aa6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt @@ -7,10 +7,9 @@ import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver -import org.koitharu.kotatsu.core.model.ZoomMode -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding import org.koitharu.kotatsu.reader.domain.PageLoader +import org.koitharu.kotatsu.reader.ui.config.ReaderSettings import org.koitharu.kotatsu.reader.ui.pager.BasePageHolder import org.koitharu.kotatsu.reader.ui.pager.ReaderPage import org.koitharu.kotatsu.utils.GoneOnInvisibleListener @@ -19,7 +18,7 @@ import org.koitharu.kotatsu.utils.ext.* class WebtoonHolder( binding: ItemPageWebtoonBinding, loader: PageLoader, - settings: AppSettings, + settings: ReaderSettings, exceptionResolver: ExceptionResolver, ) : BasePageHolder(binding, loader, settings, exceptionResolver), View.OnClickListener { @@ -60,7 +59,8 @@ class WebtoonHolder( binding.ssiv.setImage(ImageSource.uri(uri)) } - override fun onImageShowing(zoom: ZoomMode) { + override fun onImageShowing(settings: ReaderSettings) { + binding.ssiv.colorFilter = settings.colorFilter?.toColorFilter() with(binding.ssiv) { setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM) minScale = width / sWidth.toFloat() @@ -70,7 +70,7 @@ class WebtoonHolder( scrollToRestore != 0 -> scrollToRestore itemView.top < 0 -> getScrollRange() else -> 0 - } + }, ) scrollToRestore = 0 } @@ -89,7 +89,7 @@ class WebtoonHolder( override fun onError(e: Throwable) { bindingInfo.textViewError.text = e.getDisplayMessage(context.resources) bindingInfo.buttonRetry.setText( - ExceptionResolver.getResolveStringId(e).ifZero { R.string.try_again } + ExceptionResolver.getResolveStringId(e).ifZero { R.string.try_again }, ) bindingInfo.layoutError.isVisible = true bindingInfo.progressBar.hideCompat() @@ -104,4 +104,4 @@ class WebtoonHolder( scrollToRestore = scroll } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt index cfdf3e9c5..e91f1467c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt @@ -6,9 +6,7 @@ import android.view.View import android.view.ViewGroup import android.view.animation.AccelerateDecelerateInterpolator import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import kotlinx.coroutines.async -import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.databinding.FragmentReaderWebtoonBinding import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.pager.BaseReader @@ -21,9 +19,6 @@ import org.koitharu.kotatsu.utils.ext.viewLifecycleScope @AndroidEntryPoint class WebtoonReaderFragment : BaseReader() { - @Inject - lateinit var settings: AppSettings - private val scrollInterpolator = AccelerateDecelerateInterpolator() private var webtoonAdapter: WebtoonAdapter? = null @@ -34,7 +29,7 @@ class WebtoonReaderFragment : BaseReader() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - webtoonAdapter = WebtoonAdapter(viewModel.pageLoader, settings, exceptionResolver) + webtoonAdapter = WebtoonAdapter(viewModel.pageLoader, viewModel.readerSettings, exceptionResolver) with(binding.recyclerView) { setHasFixedSize(true) adapter = webtoonAdapter diff --git a/app/src/main/res/layout/activity_color_filter.xml b/app/src/main/res/layout/activity_color_filter.xml index 782673328..bacb8124d 100644 --- a/app/src/main/res/layout/activity_color_filter.xml +++ b/app/src/main/res/layout/activity_color_filter.xml @@ -12,7 +12,7 @@ android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" tools:navigationIcon="@drawable/abc_ic_clear_material" - tools:title="@string/color_filter"> + tools:title="@string/color_correction">