mirror of
https://github.com/mihonapp/mihon.git
synced 2025-02-06 15:25:00 +01:00
Compare commits
No commits in common. "88aff2c77fbaed52ab101ce75c2cbe72f1747579" and "36d5ee0763be2b0bcc65f9d061961d86359fe6f6" have entirely different histories.
88aff2c77f
...
36d5ee0763
@ -18,6 +18,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
|||||||
- Fixed "currentTab was used multiple times"
|
- Fixed "currentTab was used multiple times"
|
||||||
- Fixed a rare crash when invoking "Mark previous as read" action
|
- Fixed a rare crash when invoking "Mark previous as read" action
|
||||||
- Fixed long strip images not loading in some old devices
|
- Fixed long strip images not loading in some old devices
|
||||||
|
- Added option to always use SSIV for image decoding
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
- Bangumi search now shows the score and summary of a search result ([@MajorTanya](https://github.com/MajorTanya)) ([#1396](https://github.com/mihonapp/mihon/pull/1396))
|
- Bangumi search now shows the score and summary of a search result ([@MajorTanya](https://github.com/MajorTanya)) ([#1396](https://github.com/mihonapp/mihon/pull/1396))
|
||||||
|
@ -2,7 +2,6 @@ package eu.kanade.domain.base
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
import eu.kanade.tachiyomi.util.system.GLUtil
|
|
||||||
import tachiyomi.core.common.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
import tachiyomi.core.common.preference.PreferenceStore
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
@ -32,5 +31,5 @@ class BasePreferences(
|
|||||||
|
|
||||||
fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "")
|
fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "")
|
||||||
|
|
||||||
fun hardwareBitmapThreshold() = preferenceStore.getInt("pref_hardware_bitmap_threshold", GLUtil.SAFE_TEXTURE_LIMIT)
|
fun alwaysUseSSIVToDecode() = preferenceStore.getBoolean("pref_always_use_ssiv_to_decode", false)
|
||||||
}
|
}
|
||||||
|
@ -162,12 +162,12 @@ sealed class Preference {
|
|||||||
|
|
||||||
data class CustomPreference(
|
data class CustomPreference(
|
||||||
override val title: String,
|
override val title: String,
|
||||||
val content: @Composable () -> Unit,
|
val content: @Composable (PreferenceItem<String>) -> Unit,
|
||||||
) : PreferenceItem<Unit>() {
|
) : PreferenceItem<String>() {
|
||||||
override val enabled: Boolean = true
|
override val enabled: Boolean = true
|
||||||
override val subtitle: String? = null
|
override val subtitle: String? = null
|
||||||
override val icon: ImageVector? = null
|
override val icon: ImageVector? = null
|
||||||
override val onValueChanged: suspend (newValue: Unit) -> Boolean = { true }
|
override val onValueChanged: suspend (newValue: String) -> Boolean = { true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ internal fun PreferenceItem(
|
|||||||
InfoWidget(text = item.title)
|
InfoWidget(text = item.title)
|
||||||
}
|
}
|
||||||
is Preference.PreferenceItem.CustomPreference -> {
|
is Preference.PreferenceItem.CustomPreference -> {
|
||||||
item.content()
|
item.content(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,6 @@ import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9
|
|||||||
import eu.kanade.tachiyomi.network.PREF_DOH_SHECAN
|
import eu.kanade.tachiyomi.network.PREF_DOH_SHECAN
|
||||||
import eu.kanade.tachiyomi.ui.more.OnboardingScreen
|
import eu.kanade.tachiyomi.ui.more.OnboardingScreen
|
||||||
import eu.kanade.tachiyomi.util.CrashLogUtil
|
import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||||
import eu.kanade.tachiyomi.util.system.GLUtil
|
|
||||||
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
||||||
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
||||||
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
|
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
|
||||||
@ -332,23 +331,9 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
basePreferences.displayProfile().set(uri.toString())
|
basePreferences.displayProfile().set(uri.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val hardwareBitmapThresholdPref = basePreferences.hardwareBitmapThreshold()
|
|
||||||
val hardwareBitmapThreshold by hardwareBitmapThresholdPref.collectAsState()
|
|
||||||
return Preference.PreferenceGroup(
|
return Preference.PreferenceGroup(
|
||||||
title = stringResource(MR.strings.pref_category_reader),
|
title = stringResource(MR.strings.pref_category_reader),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
|
||||||
pref = hardwareBitmapThresholdPref,
|
|
||||||
title = stringResource(MR.strings.pref_hardware_bitmap_threshold),
|
|
||||||
subtitle = stringResource(
|
|
||||||
MR.strings.pref_hardware_bitmap_threshold_summary,
|
|
||||||
hardwareBitmapThreshold,
|
|
||||||
),
|
|
||||||
enabled = GLUtil.DEVICE_TEXTURE_LIMIT > GLUtil.SAFE_TEXTURE_LIMIT,
|
|
||||||
entries = GLUtil.CUSTOM_TEXTURE_LIMIT_OPTIONS
|
|
||||||
.associateWith { it.toString() }
|
|
||||||
.toImmutableMap(),
|
|
||||||
),
|
|
||||||
Preference.PreferenceItem.TextPreference(
|
Preference.PreferenceItem.TextPreference(
|
||||||
title = stringResource(MR.strings.pref_display_profile),
|
title = stringResource(MR.strings.pref_display_profile),
|
||||||
subtitle = basePreferences.displayProfile().get(),
|
subtitle = basePreferences.displayProfile().get(),
|
||||||
@ -356,6 +341,10 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
chooseColorProfile.launch(arrayOf("*/*"))
|
chooseColorProfile.launch(arrayOf("*/*"))
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
|
pref = basePreferences.alwaysUseSSIVToDecode(),
|
||||||
|
title = stringResource(MR.strings.pref_always_use_ssiv_to_decode),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,6 @@ import eu.kanade.tachiyomi.network.NetworkHelper
|
|||||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||||
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
|
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||||
import eu.kanade.tachiyomi.util.system.GLUtil
|
|
||||||
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
||||||
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
||||||
import eu.kanade.tachiyomi.util.system.cancelNotification
|
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||||
@ -59,7 +58,6 @@ import org.conscrypt.Conscrypt
|
|||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.core.common.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
import tachiyomi.core.common.preference.PreferenceStore
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
import tachiyomi.core.common.util.system.ImageUtil
|
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.widget.WidgetManager
|
import tachiyomi.presentation.widget.WidgetManager
|
||||||
@ -144,14 +142,6 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
.onEach(FirebaseConfig::setCrashlyticsEnabled)
|
.onEach(FirebaseConfig::setCrashlyticsEnabled)
|
||||||
.launchIn(scope)
|
.launchIn(scope)
|
||||||
|
|
||||||
basePreferences.hardwareBitmapThreshold().let { preference ->
|
|
||||||
if (!preference.isSet()) preference.set(GLUtil.DEVICE_TEXTURE_LIMIT)
|
|
||||||
}
|
|
||||||
|
|
||||||
basePreferences.hardwareBitmapThreshold().changes()
|
|
||||||
.onEach { ImageUtil.hardwareBitmapThreshold = it }
|
|
||||||
.launchIn(scope)
|
|
||||||
|
|
||||||
setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode().get())
|
setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode().get())
|
||||||
|
|
||||||
// Updates widget update
|
// Updates widget update
|
||||||
|
@ -10,6 +10,7 @@ import coil3.decode.ImageSource
|
|||||||
import coil3.fetch.SourceFetchResult
|
import coil3.fetch.SourceFetchResult
|
||||||
import coil3.request.Options
|
import coil3.request.Options
|
||||||
import coil3.request.bitmapConfig
|
import coil3.request.bitmapConfig
|
||||||
|
import eu.kanade.tachiyomi.util.system.GLUtil
|
||||||
import okio.BufferedSource
|
import okio.BufferedSource
|
||||||
import tachiyomi.core.common.util.system.ImageUtil
|
import tachiyomi.core.common.util.system.ImageUtil
|
||||||
import tachiyomi.decoder.ImageDecoder
|
import tachiyomi.decoder.ImageDecoder
|
||||||
@ -45,7 +46,10 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
|
|||||||
|
|
||||||
check(bitmap != null) { "Failed to decode image" }
|
check(bitmap != null) { "Failed to decode image" }
|
||||||
|
|
||||||
if (options.bitmapConfig == Bitmap.Config.HARDWARE && ImageUtil.canUseHardwareBitmap(bitmap)) {
|
if (
|
||||||
|
options.bitmapConfig == Bitmap.Config.HARDWARE &&
|
||||||
|
maxOf(bitmap.width, bitmap.height) <= GLUtil.maxTextureSize
|
||||||
|
) {
|
||||||
val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false)
|
val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false)
|
||||||
if (hwBitmap != null) {
|
if (hwBitmap != null) {
|
||||||
bitmap.recycle()
|
bitmap.recycle()
|
||||||
|
@ -71,7 +71,6 @@ object Notifications {
|
|||||||
const val CHANNEL_APP_UPDATE = "app_apk_update_channel"
|
const val CHANNEL_APP_UPDATE = "app_apk_update_channel"
|
||||||
const val ID_APP_UPDATER = 1
|
const val ID_APP_UPDATER = 1
|
||||||
const val ID_APP_UPDATE_PROMPT = 2
|
const val ID_APP_UPDATE_PROMPT = 2
|
||||||
const val ID_APP_UPDATE_ERROR = 3
|
|
||||||
const val CHANNEL_EXTENSIONS_UPDATE = "ext_apk_update_channel"
|
const val CHANNEL_EXTENSIONS_UPDATE = "ext_apk_update_channel"
|
||||||
const val ID_UPDATES_TO_EXTS = -401
|
const val ID_UPDATES_TO_EXTS = -401
|
||||||
const val ID_EXTENSION_INSTALLER = -402
|
const val ID_EXTENSION_INSTALLER = -402
|
||||||
|
@ -181,9 +181,9 @@ internal class AppUpdateNotifier(private val context: Context) {
|
|||||||
addAction(
|
addAction(
|
||||||
R.drawable.ic_close_24dp,
|
R.drawable.ic_close_24dp,
|
||||||
context.stringResource(MR.strings.action_cancel),
|
context.stringResource(MR.strings.action_cancel),
|
||||||
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_APP_UPDATE_ERROR),
|
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_APP_UPDATER),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
notificationBuilder.show(Notifications.ID_APP_UPDATE_ERROR)
|
notificationBuilder.show(Notifications.ID_APP_UPDATER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
@ -28,7 +29,6 @@ import eu.kanade.tachiyomi.ui.home.HomeScreen
|
|||||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewScreen
|
import eu.kanade.tachiyomi.ui.webview.WebViewScreen
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import mihon.presentation.core.util.collectAsLazyPagingItems
|
|
||||||
import tachiyomi.core.common.Constants
|
import tachiyomi.core.common.Constants
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
@ -86,7 +86,7 @@ data class SourceSearchScreen(
|
|||||||
}
|
}
|
||||||
BrowseSourceContent(
|
BrowseSourceContent(
|
||||||
source = screenModel.source,
|
source = screenModel.source,
|
||||||
mangaList = screenModel.mangaPagerFlowFlow.collectAsLazyPagingItems(),
|
mangaList = screenModel.mangaPagerFlow.collectAsLazyPagingItems(),
|
||||||
columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation),
|
columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation),
|
||||||
displayMode = screenModel.displayMode,
|
displayMode = screenModel.displayMode,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
|
@ -31,6 +31,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
|||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
@ -55,7 +56,6 @@ import eu.kanade.tachiyomi.ui.webview.WebViewScreen
|
|||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.receiveAsFlow
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
import mihon.presentation.core.util.collectAsLazyPagingItems
|
|
||||||
import tachiyomi.core.common.Constants
|
import tachiyomi.core.common.Constants
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.domain.source.model.StubSource
|
import tachiyomi.domain.source.model.StubSource
|
||||||
@ -208,7 +208,7 @@ data class BrowseSourceScreen(
|
|||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
BrowseSourceContent(
|
BrowseSourceContent(
|
||||||
source = screenModel.source,
|
source = screenModel.source,
|
||||||
mangaList = screenModel.mangaPagerFlowFlow.collectAsLazyPagingItems(),
|
mangaList = screenModel.mangaPagerFlow.collectAsLazyPagingItems(),
|
||||||
columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation),
|
columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation),
|
||||||
displayMode = screenModel.displayMode,
|
displayMode = screenModel.displayMode,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
|
@ -26,11 +26,10 @@ import eu.kanade.tachiyomi.source.model.FilterList
|
|||||||
import eu.kanade.tachiyomi.util.removeCovers
|
import eu.kanade.tachiyomi.util.removeCovers
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.firstOrNull
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
@ -106,9 +105,9 @@ class BrowseSourceScreenModel(
|
|||||||
* Flow of Pager flow tied to [State.listing]
|
* Flow of Pager flow tied to [State.listing]
|
||||||
*/
|
*/
|
||||||
private val hideInLibraryItems = sourcePreferences.hideInLibraryItems().get()
|
private val hideInLibraryItems = sourcePreferences.hideInLibraryItems().get()
|
||||||
val mangaPagerFlowFlow = state.map { it.listing }
|
val mangaPagerFlow = state.map { it.listing }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.map { listing ->
|
.flatMapLatest { listing ->
|
||||||
Pager(PagingConfig(pageSize = 25)) {
|
Pager(PagingConfig(pageSize = 25)) {
|
||||||
getRemoteManga.subscribe(sourceId, listing.query ?: "", listing.filters)
|
getRemoteManga.subscribe(sourceId, listing.query ?: "", listing.filters)
|
||||||
}.flow.map { pagingData ->
|
}.flow.map { pagingData ->
|
||||||
@ -120,9 +119,8 @@ class BrowseSourceScreenModel(
|
|||||||
}
|
}
|
||||||
.filter { !hideInLibraryItems || !it.value.favorite }
|
.filter { !hideInLibraryItems || !it.value.favorite }
|
||||||
}
|
}
|
||||||
.cachedIn(ioCoroutineScope)
|
|
||||||
}
|
}
|
||||||
.stateIn(ioCoroutineScope, SharingStarted.Lazily, emptyFlow())
|
.cachedIn(ioCoroutineScope)
|
||||||
|
|
||||||
fun getColumnsPreference(orientation: Int): GridCells {
|
fun getColumnsPreference(orientation: Int): GridCells {
|
||||||
val isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE
|
val isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||||
|
@ -33,13 +33,17 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT
|
|||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_OUT_QUAD
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_OUT_QUAD
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE
|
||||||
import com.github.chrisbanes.photoview.PhotoView
|
import com.github.chrisbanes.photoview.PhotoView
|
||||||
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.tachiyomi.data.coil.cropBorders
|
import eu.kanade.tachiyomi.data.coil.cropBorders
|
||||||
import eu.kanade.tachiyomi.data.coil.customDecoder
|
import eu.kanade.tachiyomi.data.coil.customDecoder
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView
|
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView
|
||||||
|
import eu.kanade.tachiyomi.util.system.GLUtil
|
||||||
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
||||||
import eu.kanade.tachiyomi.util.view.isVisibleOnScreen
|
import eu.kanade.tachiyomi.util.view.isVisibleOnScreen
|
||||||
import okio.BufferedSource
|
import okio.BufferedSource
|
||||||
import tachiyomi.core.common.util.system.ImageUtil
|
import tachiyomi.core.common.util.system.ImageUtil
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper view for showing page image.
|
* A wrapper view for showing page image.
|
||||||
@ -57,6 +61,8 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
|||||||
private val isWebtoon: Boolean = false,
|
private val isWebtoon: Boolean = false,
|
||||||
) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) {
|
) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) {
|
||||||
|
|
||||||
|
private val alwaysUseSSIVToDecode by lazy { Injekt.get<BasePreferences>().alwaysUseSSIVToDecode().get() }
|
||||||
|
|
||||||
private var pageView: View? = null
|
private var pageView: View? = null
|
||||||
|
|
||||||
private var config: Config? = null
|
private var config: Config? = null
|
||||||
@ -233,7 +239,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
|||||||
} else {
|
} else {
|
||||||
SubsamplingScaleImageView(context)
|
SubsamplingScaleImageView(context)
|
||||||
}.apply {
|
}.apply {
|
||||||
setMaxTileSize(ImageUtil.hardwareBitmapThreshold)
|
setMaxTileSize(GLUtil.maxTextureSize)
|
||||||
setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER)
|
setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER)
|
||||||
setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE)
|
setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE)
|
||||||
setMinimumTileDpi(180)
|
setMinimumTileDpi(180)
|
||||||
@ -294,34 +300,32 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
|||||||
isVisible = true
|
isVisible = true
|
||||||
}
|
}
|
||||||
is BufferedSource -> {
|
is BufferedSource -> {
|
||||||
if (!isWebtoon) {
|
if (alwaysUseSSIVToDecode || !isWebtoon || !ImageUtil.canUseCoilToDecode(data)) {
|
||||||
setHardwareConfig(ImageUtil.canUseHardwareBitmap(data))
|
|
||||||
setImage(ImageSource.inputStream(data.inputStream()))
|
setImage(ImageSource.inputStream(data.inputStream()))
|
||||||
isVisible = true
|
isVisible = true
|
||||||
return@apply
|
} else {
|
||||||
|
val request = ImageRequest.Builder(context)
|
||||||
|
.data(data)
|
||||||
|
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||||
|
.diskCachePolicy(CachePolicy.DISABLED)
|
||||||
|
.target(
|
||||||
|
onSuccess = { result ->
|
||||||
|
val image = result as BitmapImage
|
||||||
|
setImage(ImageSource.bitmap(image.bitmap))
|
||||||
|
isVisible = true
|
||||||
|
},
|
||||||
|
onError = {
|
||||||
|
this@ReaderPageImageView.onImageLoadError()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.size(ViewSizeResolver(this@ReaderPageImageView))
|
||||||
|
.precision(Precision.INEXACT)
|
||||||
|
.cropBorders(config.cropBorders)
|
||||||
|
.customDecoder(true)
|
||||||
|
.crossfade(false)
|
||||||
|
.build()
|
||||||
|
context.imageLoader.enqueue(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageRequest.Builder(context)
|
|
||||||
.data(data)
|
|
||||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
|
||||||
.diskCachePolicy(CachePolicy.DISABLED)
|
|
||||||
.target(
|
|
||||||
onSuccess = { result ->
|
|
||||||
val image = result as BitmapImage
|
|
||||||
setImage(ImageSource.bitmap(image.bitmap))
|
|
||||||
isVisible = true
|
|
||||||
},
|
|
||||||
onError = {
|
|
||||||
onImageLoadError()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.size(ViewSizeResolver(this@ReaderPageImageView))
|
|
||||||
.precision(Precision.INEXACT)
|
|
||||||
.cropBorders(config.cropBorders)
|
|
||||||
.customDecoder(true)
|
|
||||||
.crossfade(false)
|
|
||||||
.build()
|
|
||||||
.let(context.imageLoader::enqueue)
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}")
|
throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}")
|
||||||
|
@ -3,9 +3,10 @@ package eu.kanade.tachiyomi.util.system
|
|||||||
import javax.microedition.khronos.egl.EGL10
|
import javax.microedition.khronos.egl.EGL10
|
||||||
import javax.microedition.khronos.egl.EGLConfig
|
import javax.microedition.khronos.egl.EGLConfig
|
||||||
import javax.microedition.khronos.egl.EGLContext
|
import javax.microedition.khronos.egl.EGLContext
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
object GLUtil {
|
object GLUtil {
|
||||||
val DEVICE_TEXTURE_LIMIT: Int by lazy {
|
val maxTextureSize: Int by lazy {
|
||||||
// Get EGL Display
|
// Get EGL Display
|
||||||
val egl = EGLContext.getEGL() as EGL10
|
val egl = EGLContext.getEGL() as EGL10
|
||||||
val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
|
val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
|
||||||
@ -37,20 +38,10 @@ object GLUtil {
|
|||||||
// Release
|
// Release
|
||||||
egl.eglTerminate(display)
|
egl.eglTerminate(display)
|
||||||
|
|
||||||
// Return largest texture size found (after making it a multiplier of [Multiplier]), or default
|
// Return largest texture size found, or default
|
||||||
if (maximumTextureSize > SAFE_TEXTURE_LIMIT) {
|
max(maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION)
|
||||||
(maximumTextureSize / MULTIPLIER) * MULTIPLIER
|
|
||||||
} else {
|
|
||||||
SAFE_TEXTURE_LIMIT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const val SAFE_TEXTURE_LIMIT: Int = 2048
|
|
||||||
|
|
||||||
val CUSTOM_TEXTURE_LIMIT_OPTIONS: List<Int> by lazy {
|
|
||||||
val steps = ((DEVICE_TEXTURE_LIMIT / MULTIPLIER) - 1)
|
|
||||||
List(steps) { (it + 2) * MULTIPLIER }.asReversed()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val MULTIPLIER: Int = 1024
|
// Safe minimum default size
|
||||||
|
private const val IMAGE_MAX_BITMAP_DIMENSION = 2048
|
||||||
|
@ -310,20 +310,9 @@ object ImageUtil {
|
|||||||
val bottomOffset = topOffset + splitHeight
|
val bottomOffset = topOffset + splitHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canUseHardwareBitmap(bitmap: Bitmap): Boolean {
|
fun canUseCoilToDecode(imageSource: BufferedSource): Boolean {
|
||||||
return canUseHardwareBitmap(bitmap.width, bitmap.height)
|
val options = extractImageOptions(imageSource)
|
||||||
}
|
return maxOf(options.outWidth, options.outHeight) <= GLUtil.maxTextureSize
|
||||||
|
|
||||||
fun canUseHardwareBitmap(imageSource: BufferedSource): Boolean {
|
|
||||||
return with(extractImageOptions(imageSource)) {
|
|
||||||
canUseHardwareBitmap(outWidth, outHeight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var hardwareBitmapThreshold: Int = GLUtil.SAFE_TEXTURE_LIMIT
|
|
||||||
|
|
||||||
private fun canUseHardwareBitmap(width: Int, height: Int): Boolean {
|
|
||||||
return maxOf(width, height) <= hardwareBitmapThreshold
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,7 +48,7 @@ coil-gif = { module = "io.coil-kt.coil3:coil-gif" }
|
|||||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose" }
|
coil-compose = { module = "io.coil-kt.coil3:coil-compose" }
|
||||||
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" }
|
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" }
|
||||||
|
|
||||||
subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:66e0db195d"
|
subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:b8e1b0ed2b"
|
||||||
image-decoder = "com.github.tachiyomiorg:image-decoder:41c059e540"
|
image-decoder = "com.github.tachiyomiorg:image-decoder:41c059e540"
|
||||||
|
|
||||||
natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1"
|
natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1"
|
||||||
|
@ -392,8 +392,7 @@
|
|||||||
<string name="pref_show_reading_mode">Show reading mode</string>
|
<string name="pref_show_reading_mode">Show reading mode</string>
|
||||||
<string name="pref_show_reading_mode_summary">Briefly show current mode when reader is opened</string>
|
<string name="pref_show_reading_mode_summary">Briefly show current mode when reader is opened</string>
|
||||||
<string name="pref_display_profile">Custom display profile</string>
|
<string name="pref_display_profile">Custom display profile</string>
|
||||||
<string name="pref_hardware_bitmap_threshold">Custom hardware bitmap threshold</string>
|
<string name="pref_always_use_ssiv_to_decode">Always use SSIV to decode long strip images</string>
|
||||||
<string name="pref_hardware_bitmap_threshold_summary">If reader loads a blank image incrementally reduce the threshold.\nSelected: %s</string>
|
|
||||||
<string name="pref_crop_borders">Crop borders</string>
|
<string name="pref_crop_borders">Crop borders</string>
|
||||||
<string name="pref_custom_brightness">Custom brightness</string>
|
<string name="pref_custom_brightness">Custom brightness</string>
|
||||||
<string name="pref_grayscale">Grayscale</string>
|
<string name="pref_grayscale">Grayscale</string>
|
||||||
|
@ -42,7 +42,5 @@ dependencies {
|
|||||||
implementation(compose.ui.tooling.preview)
|
implementation(compose.ui.tooling.preview)
|
||||||
implementation(compose.ui.util)
|
implementation(compose.ui.util)
|
||||||
|
|
||||||
implementation(androidx.paging.runtime)
|
|
||||||
implementation(androidx.paging.compose)
|
|
||||||
implementation(kotlinx.immutables)
|
implementation(kotlinx.immutables)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
package mihon.presentation.core.util
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.paging.PagingData
|
|
||||||
import androidx.paging.compose.LazyPagingItems
|
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun <T: Any> StateFlow<Flow<PagingData<T>>>.collectAsLazyPagingItems(): LazyPagingItems<T> {
|
|
||||||
val flow by collectAsState()
|
|
||||||
return flow.collectAsLazyPagingItems()
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user