Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc24a18ac5 | ||
|
|
a9a8c68b94 | ||
|
|
193d2f8d9e | ||
|
|
538da9b98f | ||
|
|
071b597552 | ||
|
|
8926d74378 | ||
|
|
39a4b03bc3 | ||
|
|
9202bbc7dc | ||
|
|
f5601ebca1 | ||
|
|
2f978b554e | ||
|
|
f48a90d941 | ||
|
|
4f0d9fa83c | ||
|
|
57a747ed9c |
No files matched your search
@@ -58,7 +58,7 @@ android {
|
||||
minSdk = 28
|
||||
targetSdk = 33
|
||||
versionCode = 180038
|
||||
versionName = "1.8.8.0-alpha"
|
||||
versionName = "1.8.8.0-beta"
|
||||
resourceConfigurations.addAll(
|
||||
listOf(
|
||||
"zh",
|
||||
@@ -95,6 +95,8 @@ android {
|
||||
freeCompilerArgs = listOf(
|
||||
// https://kotlinlang.org/docs/compiler-reference.html#progressive
|
||||
"-progressive",
|
||||
"-Xjvm-default=all",
|
||||
"-Xlambdas=indy",
|
||||
|
||||
"-opt-in=coil.annotation.ExperimentalCoilApi",
|
||||
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
|
||||
|
||||
@@ -11,6 +11,7 @@ import coil.request.ImageRequest
|
||||
import coil.size.Size
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryPreview
|
||||
import com.hippo.ehviewer.client.data.NormalGalleryPreview
|
||||
import com.hippo.ehviewer.client.thumbUrl
|
||||
import com.hippo.ehviewer.client.url
|
||||
|
||||
@@ -19,14 +20,13 @@ fun ImageRequest.Builder.ehUrl(info: GalleryInfo) = apply {
|
||||
data(info.thumbUrl)
|
||||
memoryCacheKey(key)
|
||||
diskCacheKey(key)
|
||||
size(Size.ORIGINAL)
|
||||
}
|
||||
|
||||
fun ImageRequest.Builder.ehPreview(preview: GalleryPreview) = apply {
|
||||
data(preview.url)
|
||||
memoryCacheKey(preview.imageKey)
|
||||
diskCacheKey(preview.imageKey)
|
||||
size(Size.ORIGINAL)
|
||||
if (preview is NormalGalleryPreview) size(Size.ORIGINAL)
|
||||
}
|
||||
|
||||
private val stubResult = DecodeResult(ColorDrawable(Color.BLACK), false)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.hippo.ehviewer.ui.compose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.saveable.LocalSaveableStateRegistry
|
||||
|
||||
private val remembered = hashMapOf<String, Any>()
|
||||
|
||||
// This works mostly like [rememberSaveable], but saves data in global hashmap, to avoid [TransactionTooLargeException]
|
||||
@Composable
|
||||
fun <T : Any> rememberMemorized(
|
||||
vararg inputs: Any?,
|
||||
key: String? = null,
|
||||
init: () -> T,
|
||||
): T {
|
||||
// key is the one provided by the user or the one generated by the compose runtime
|
||||
val finalKey = if (!key.isNullOrEmpty()) {
|
||||
key
|
||||
} else {
|
||||
// currentCompositeKeyHash is unreliable here
|
||||
// Is this Composable inlined or its groups elided?
|
||||
// Or a compose compiler bug?
|
||||
init.javaClass.toString()
|
||||
}
|
||||
|
||||
val registry = LocalSaveableStateRegistry.current
|
||||
// value is restored using the registry or created via [init] lambda
|
||||
val value = remember(*inputs) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val restored = registry?.consumeRestored(finalKey)?.let {
|
||||
remembered.remove(it as String)
|
||||
} as T?
|
||||
restored ?: init()
|
||||
}
|
||||
|
||||
// re-register if the registry or key has been changed
|
||||
if (registry != null) {
|
||||
// we want to use the latest instances of saver and value in the valueProvider lambda
|
||||
// without restarting DisposableEffect as it would cause re-registering the provider in
|
||||
// the different order. so we use rememberUpdatedState.
|
||||
val valueState = rememberUpdatedState(value)
|
||||
|
||||
DisposableEffect(registry, finalKey) {
|
||||
val valueProvider = {
|
||||
finalKey.also { remembered[it] = valueState.value }
|
||||
}
|
||||
val entry = registry.registerProvider(finalKey, valueProvider)
|
||||
onDispose {
|
||||
entry.unregister()
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.databinding.WidgetContentLayoutBinding
|
||||
import com.hippo.ehviewer.ui.legacy.ViewTransition.OnShowViewListener
|
||||
import com.hippo.ehviewer.ui.legacy.easyrecyclerview.HandlerDrawable
|
||||
@@ -41,7 +42,7 @@ import com.hippo.ehviewer.yorozuya.collect.IntList
|
||||
import rikka.core.res.resolveColor
|
||||
|
||||
class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
|
||||
private lateinit var mContentHelper: ContentHelper<*>
|
||||
private lateinit var mContentHelper: ContentHelper
|
||||
private val mRecyclerViewOriginBottom: Int
|
||||
private val mFastScrollerOriginBottom: Int
|
||||
private val binding: WidgetContentLayoutBinding
|
||||
@@ -64,7 +65,7 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
val fastScroller
|
||||
get() = binding.fastScroller
|
||||
|
||||
fun setHelper(helper: ContentHelper<*>) {
|
||||
fun setHelper(helper: ContentHelper) {
|
||||
mContentHelper = helper
|
||||
helper.init(this)
|
||||
}
|
||||
@@ -128,7 +129,7 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
super.onRestoreInstanceState(mContentHelper.restoreInstanceState(state))
|
||||
}
|
||||
|
||||
abstract class ContentHelper<E : Parcelable> : OnShowViewListener {
|
||||
abstract class ContentHelper : OnShowViewListener {
|
||||
/**
|
||||
* Generate task id
|
||||
*/
|
||||
@@ -143,7 +144,7 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
/**
|
||||
* Store data
|
||||
*/
|
||||
private var mData = ArrayList<E>()
|
||||
private var mData = ArrayList<GalleryInfo>()
|
||||
|
||||
/**
|
||||
* Store the page divider index
|
||||
@@ -287,10 +288,10 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
mEmptyString = str
|
||||
}
|
||||
|
||||
val data: List<E>
|
||||
val data: List<GalleryInfo>
|
||||
get() = mData
|
||||
|
||||
fun getDataAtEx(location: Int): E? {
|
||||
fun getDataAtEx(location: Int): GalleryInfo? {
|
||||
return if (location >= 0 && location < mData.size) {
|
||||
mData[location]
|
||||
} else {
|
||||
@@ -298,7 +299,7 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
}
|
||||
}
|
||||
|
||||
val firstVisibleItem: E?
|
||||
val firstVisibleItem: GalleryInfo?
|
||||
get() = getDataAtEx(LayoutManagerUtils.getFirstVisibleItemPosition(binding.recyclerView.layoutManager))
|
||||
|
||||
fun size(): Int {
|
||||
@@ -309,8 +310,8 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
return mCurrentTaskId == taskId
|
||||
}
|
||||
|
||||
protected abstract fun isDuplicate(d1: E, d2: E): Boolean
|
||||
private fun removeDuplicateData(data: List<E>, start: Int, end: Int) {
|
||||
protected abstract fun isDuplicate(d1: GalleryInfo, d2: GalleryInfo): Boolean
|
||||
private fun removeDuplicateData(data: List<GalleryInfo>, start: Int, end: Int) {
|
||||
val slicedData = mData.slice(start.coerceAtLeast(0) until end.coerceAtMost(mData.size))
|
||||
data.dropWhile { d1 ->
|
||||
slicedData.any { d2 ->
|
||||
@@ -319,8 +320,8 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onAddData(data: List<E>) {}
|
||||
protected open fun onRemoveData(data: List<E>) {}
|
||||
protected open fun onAddData(data: List<GalleryInfo>) {}
|
||||
protected open fun onRemoveData(data: List<GalleryInfo>) {}
|
||||
protected open fun onClearData() {}
|
||||
fun onGetPageData(
|
||||
taskId: Int,
|
||||
@@ -328,7 +329,7 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
nextPage: Int,
|
||||
prev: String?,
|
||||
next: String?,
|
||||
data: List<E>,
|
||||
data: List<GalleryInfo>,
|
||||
) {
|
||||
if (mCurrentTaskId == taskId) {
|
||||
val dataSize: Int
|
||||
@@ -734,7 +735,11 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
val shownView = mViewTransition.shownViewIndex
|
||||
bundle.putInt(KEY_SHOWN_VIEW, shownView)
|
||||
bundle.putString(KEY_TIP, binding.tip.text.toString())
|
||||
bundle.putParcelableArrayList(KEY_DATA, mData)
|
||||
synchronized(contentMap) {
|
||||
val size = contentMap.keys.takeIf { it.isNotEmpty() }?.max()?.inc() ?: 0
|
||||
contentMap[size] = mData
|
||||
bundle.putInt(KEY_DATA, size)
|
||||
}
|
||||
bundle.putInt(KEY_NEXT_ID, mIdGenerator.nextId())
|
||||
bundle.putParcelable(KEY_PAGE_DIVIDER, mPageDivider)
|
||||
bundle.putInt(KEY_START_PAGE, mStartPage)
|
||||
@@ -749,11 +754,12 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
return if (state is Bundle) {
|
||||
mViewTransition.showView(state.getInt(KEY_SHOWN_VIEW), false)
|
||||
binding.tip.text = state.getString(KEY_TIP)
|
||||
@Suppress("DEPRECATION")
|
||||
val newData = state.getParcelableArrayList<E>(KEY_DATA)
|
||||
newData?.let { mData = it }
|
||||
val data = state.getInt(KEY_DATA)
|
||||
synchronized(contentMap) {
|
||||
mData = contentMap.remove(data) ?: ArrayList()
|
||||
}
|
||||
mIdGenerator.setNextId(state.getInt(KEY_NEXT_ID))
|
||||
mPageDivider = state.getParcelableCompat(KEY_PAGE_DIVIDER)!!
|
||||
mPageDivider = state.getParcelableCompat(KEY_PAGE_DIVIDER) ?: IntList()
|
||||
mStartPage = state.getInt(KEY_START_PAGE)
|
||||
mEndPage = state.getInt(KEY_END_PAGE)
|
||||
pages = state.getInt(KEY_PAGES)
|
||||
@@ -761,7 +767,7 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
mNext = state.getString(KEY_NEXT)
|
||||
notifyDataSetChanged()
|
||||
notifyDataSetChanged {}
|
||||
if (newData == null) {
|
||||
if (mData.size == 0) {
|
||||
mPageDivider.clear()
|
||||
mStartPage = 0
|
||||
mEndPage = 0
|
||||
@@ -803,3 +809,8 @@ class ContentLayout(context: Context, attrs: AttributeSet? = null) : FrameLayout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// data "ArrayList<GalleryInfo>" is too large, we cannot put it as Parcelable otherwise TransactionTooLargeException would occur
|
||||
// TODO: convert to paging3 source then keep it in a proper window
|
||||
// @see PagingConfig.maxSize
|
||||
private val contentMap = hashMapOf<Int, ArrayList<GalleryInfo>>()
|
||||
@@ -28,7 +28,7 @@ import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
|
||||
abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo>() {
|
||||
abstract class GalleryInfoContentHelper : ContentHelper() {
|
||||
@JvmField
|
||||
var jumpTo: String? = null
|
||||
private val listener: FavouriteStatusRouter.Listener
|
||||
|
||||
@@ -19,7 +19,6 @@ import android.graphics.drawable.AnimatedImageDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import coil.imageLoader
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Size
|
||||
import com.hippo.ehviewer.coil.imageRequest
|
||||
|
||||
class UnikeryDrawable(private val mTextView: ObservedTextView, url: String) :
|
||||
@@ -31,7 +30,6 @@ class UnikeryDrawable(private val mTextView: ObservedTextView, url: String) :
|
||||
mTextView.setOnWindowAttachListener(this)
|
||||
mImageRequest = mTextView.context.imageRequest {
|
||||
target(onSuccess = { onGetValue(it) })
|
||||
size(Size.ORIGINAL)
|
||||
data(url)
|
||||
}
|
||||
load()
|
||||
|
||||
@@ -57,6 +57,7 @@ import com.hippo.ehviewer.coil.imageRequest
|
||||
import com.hippo.ehviewer.coil.justDownload
|
||||
import com.hippo.ehviewer.ui.compose.data.EhPreviewItem
|
||||
import com.hippo.ehviewer.ui.compose.rememberDialogState
|
||||
import com.hippo.ehviewer.ui.compose.rememberMemorized
|
||||
import com.hippo.ehviewer.ui.compose.setMD3Content
|
||||
import com.hippo.ehviewer.ui.legacy.recyclerview.calculateSuitableSpanCount
|
||||
import com.hippo.ehviewer.ui.navToReader
|
||||
@@ -72,7 +73,7 @@ class GalleryPreviewScreen : Fragment() {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return ComposeView(inflater.context).apply {
|
||||
setMD3Content {
|
||||
val galleryDetail = rememberSaveable { requireArguments().getParcelableCompat<GalleryDetail>(KEY_GALLERY_DETAIL)!! }
|
||||
val galleryDetail = remember { requireArguments().getParcelableCompat<GalleryDetail>(KEY_GALLERY_DETAIL)!! }
|
||||
val context = LocalContext.current
|
||||
fun onPreviewCLick(index: Int) = context.navToReader(galleryDetail, index)
|
||||
val toNextPage = rememberSaveable { requireArguments().getBoolean(KEY_NEXT_PAGE) }
|
||||
@@ -118,14 +119,14 @@ class GalleryPreviewScreen : Fragment() {
|
||||
initialKey = goto
|
||||
}
|
||||
|
||||
val previewPagesMap = rememberSaveable { mutableMapOf<Int, PreviewPage>().apply { put(1, galleryDetail.previewList) } }
|
||||
val previewPagesMap = rememberMemorized { mutableMapOf<Int, PreviewPage>().apply { put(1, galleryDetail.previewList) } }
|
||||
val data = remember(initialKey) {
|
||||
Pager(PagingConfig(1, prefetchDistance = 4, enablePlaceholders = false, initialLoadSize = 1), initialKey) {
|
||||
object : PagingSource<Int, PreviewPage>() {
|
||||
override fun getRefreshKey(state: PagingState<Int, PreviewPage>) = null
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, PreviewPage> {
|
||||
val up = params.key ?: 1
|
||||
val end = up + params.loadSize - 1
|
||||
val end = (up + params.loadSize - 1).coerceAtMost(galleryDetail.previewPages)
|
||||
runSuspendCatching {
|
||||
(up..end).mapNotNull { it.takeUnless { previewPagesMap.contains(it) } }
|
||||
.parMap(Dispatchers.IO) { getPreviewListByPage(it - 1).apply { previewPagesMap[it] = this } }
|
||||
|
||||
+1
-1
@@ -15,6 +15,6 @@ tasks.register("Delete", Delete::class) {
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath("com.android.tools:r8:8.2.6-dev")
|
||||
classpath("com.android.tools:r8:8.2.7-dev")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user