Compare commits

...
9 Commits
Author SHA1 Message Date
tarsin 45eed26788 Bump a bugfix version
Releases / Build (push) Has been cancelled
2023-03-07 13:33:27 +08:00
tarsin 3482882648 fixup! Resolve #931 2023-03-07 13:32:36 +08:00
tarsin 2f92c9a2aa Resolve #931 2023-03-07 13:32:28 +08:00
tarsin 01b9c1d0f2 Fix restore download 2023-03-07 13:32:20 +08:00
tarsin 022cf9bdcf Respect NinePatchDrawable 2023-03-07 13:32:07 +08:00
FooIbar 0326f52077 Update translation 2023-03-07 13:31:45 +08:00
tarsin 0bb18c703b Resolve #903 2023-03-07 13:30:29 +08:00
tarsin 5784dc9a9d Don't create an arraylist per request 2023-03-07 13:30:11 +08:00
tarsin cbaa826fbd fixup! Implement image request merging in standalone Interceptor 2023-03-07 13:29:59 +08:00
9 changed files with 61 additions and 45 deletions

No files matched your search

+2 -2
View File
@@ -53,8 +53,8 @@ android {
applicationId = "moe.tarsin.ehviewer" applicationId = "moe.tarsin.ehviewer"
minSdk = 28 minSdk = 28
targetSdk = 33 targetSdk = 33
versionCode = 180035 versionCode = 180036
versionName = "1.8.7.0" versionName = "1.8.7.1"
resourceConfigurations.addAll( resourceConfigurations.addAll(
listOf( listOf(
"zh", "zh",
@@ -3,6 +3,7 @@ package com.hippo.ehviewer.coil
import coil.intercept.Interceptor import coil.intercept.Interceptor
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.request.ImageResult import coil.request.ImageResult
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@@ -15,17 +16,17 @@ import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
object MergeInterceptor : Interceptor { object MergeInterceptor : Interceptor {
private val pendingContinuationMap: HashMap<String, MutableList<Continuation<Unit>>> = private val pendingContinuationMap: HashMap<String, MutableList<Continuation<Unit>>> = hashMapOf()
hashMapOf()
private val pendingContinuationMapLock = Mutex() private val pendingContinuationMapLock = Mutex()
private val notifyScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val notifyScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val EMPTY_LIST = mutableListOf<Continuation<Unit>>()
override suspend fun intercept(chain: Interceptor.Chain): ImageResult { override suspend fun intercept(chain: Interceptor.Chain): ImageResult {
val originRequest = chain.request val originRequest = chain.request
val originListener = originRequest.listener val originListener = originRequest.listener
val key = originRequest.data as String val key = originRequest.data as String
val newRequest = ImageRequest.Builder(originRequest).listener( val newRequest = ImageRequest.Builder(originRequest).listener(
{}, { originListener?.onStart(it) },
{}, // Only original request's onStart and onCancel will get called { originListener?.onCancel(it) },
{ request, result -> { request, result ->
originListener?.onError(request, result) originListener?.onError(request, result)
// Wake all pending continuations since this request is to be failed // Wake all pending continuations since this request is to be failed
@@ -49,10 +50,11 @@ object MergeInterceptor : Interceptor {
pendingContinuationMapLock.lock() pendingContinuationMapLock.lock()
val existPendingContinuations = pendingContinuationMap[key] val existPendingContinuations = pendingContinuationMap[key]
if (existPendingContinuations == null) { if (existPendingContinuations == null) {
pendingContinuationMap[key] = mutableListOf() pendingContinuationMap[key] = EMPTY_LIST
pendingContinuationMapLock.unlock() pendingContinuationMapLock.unlock()
} else { } else {
existPendingContinuations.apply { if (existPendingContinuations === EMPTY_LIST) pendingContinuationMap[key] = mutableListOf()
pendingContinuationMap[key]!!.apply {
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
add(continuation) add(continuation)
pendingContinuationMapLock.unlock() pendingContinuationMapLock.unlock()
@@ -61,18 +63,18 @@ object MergeInterceptor : Interceptor {
} }
} }
return withContext(originRequest.interceptorDispatcher) { chain.proceed(newRequest) } try {
} return withContext(originRequest.interceptorDispatcher) { chain.proceed(newRequest) }
} catch (e: CancellationException) {
fun onCancel(key: String) { notifyScope.launch {
notifyScope.launch { pendingContinuationMapLock.withLock {
pendingContinuationMapLock.withLock { // Wake up a pending continuation to continue executing task
// Wake up a pending continuation to continue executing task val successor = pendingContinuationMap[key]?.removeFirstOrNull()?.apply { resume(Unit) }
val successor = // If no successor, delete this entry from hashmap
pendingContinuationMap[key]?.removeFirstOrNull()?.apply { resume(Unit) } successor ?: pendingContinuationMap.remove(key)
// If no successor, delete this entry from hashmap }
successor ?: pendingContinuationMap.remove(key)
} }
throw e
} }
} }
} }
@@ -51,8 +51,8 @@ class RestoreDownloadPreference @JvmOverloads constructor(
val siFile = file.findFile(SpiderQueen.SPIDER_INFO_FILENAME) ?: return null val siFile = file.findFile(SpiderQueen.SPIDER_INFO_FILENAME) ?: return null
return runCatching { return runCatching {
val spiderInfo = readCompatFromUniFile(siFile) ?: return null val spiderInfo = readCompatFromUniFile(siFile) ?: return null
var gid = spiderInfo.gid val gid = spiderInfo.gid
var dirname = file.name val dirname = file.name
if (mManager.containDownloadInfo(gid)) { if (mManager.containDownloadInfo(gid)) {
// Restore download dir to avoid redownload // Restore download dir to avoid redownload
val dbdirname = EhDB.getDownloadDirname(gid) val dbdirname = EhDB.getDownloadDirname(gid)
@@ -62,11 +62,13 @@ class RestoreDownloadPreference @JvmOverloads constructor(
} }
return null return null
} }
RestoreItem().apply { RestoreItem().also {
gid = spiderInfo.gid it.gid = spiderInfo.gid
token = spiderInfo.token it.token = spiderInfo.token
dirname = file.name it.dirname = file.name
} }
}.onFailure {
it.printStackTrace()
}.getOrNull() }.getOrNull()
} }
@@ -76,6 +78,8 @@ class RestoreDownloadPreference @JvmOverloads constructor(
val restoreItemList = files.mapNotNull { getRestoreItem(it) } val restoreItemList = files.mapNotNull { getRestoreItem(it) }
return runSuspendCatching { return runSuspendCatching {
fillGalleryListByApi(restoreItemList, EhUrl.referer) fillGalleryListByApi(restoreItemList, EhUrl.referer)
}.onFailure {
it.printStackTrace()
}.getOrNull() }.getOrNull()
} }
@@ -41,7 +41,6 @@ import com.hippo.image.Image
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import com.hippo.util.ExceptionUtils import com.hippo.util.ExceptionUtils
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -240,8 +239,15 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
} }
private fun stop() { private fun stop() {
launchNonCancellable { runCatching { writeSpiderInfoToLocal() } } val queenScope = this
cancel() launchIO {
queenScope.cancel()
runCatching {
writeSpiderInfoToLocal()
}.onFailure {
it.printStackTrace()
}
}
} }
val size val size
@@ -18,13 +18,16 @@ package com.hippo.ehviewer.ui.fragment
import android.app.Activity import android.app.Activity
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.text.format.DateFormat.is24HourFormat
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.MaterialTimePicker.INPUT_MODE_CLOCK
import com.google.android.material.timepicker.TimeFormat.CLOCK_12H import com.google.android.material.timepicker.TimeFormat.CLOCK_12H
import com.google.android.material.timepicker.TimeFormat.CLOCK_24H
import com.hippo.ehviewer.EhApplication import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.R import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings import com.hippo.ehviewer.Settings
@@ -80,7 +83,8 @@ class EhFragment : BasePreferenceFragment() {
schedHour?.let { setHour(it) } schedHour?.let { setHour(it) }
schedMinute?.let { setMinute(it) } schedMinute?.let { setMinute(it) }
} }
.setTimeFormat(CLOCK_12H) .setTimeFormat(if (is24HourFormat(requireContext())) CLOCK_24H else CLOCK_12H)
.setInputMode(INPUT_MODE_CLOCK)
.build() .build()
.apply { .apply {
addOnPositiveButtonClickListener { addOnPositiveButtonClickListener {
+5 -7
View File
@@ -21,7 +21,7 @@ import android.graphics.ColorSpace
import android.graphics.ImageDecoder import android.graphics.ImageDecoder
import android.graphics.ImageDecoder.ImageInfo import android.graphics.ImageDecoder.ImageInfo
import android.graphics.ImageDecoder.Source import android.graphics.ImageDecoder.Source
import android.graphics.drawable.AnimatedImageDrawable import android.graphics.drawable.Animatable
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
@@ -42,21 +42,19 @@ class Image private constructor(private val src: CloseableSource) {
calculateSampleSize(info, 2 * screenHeight, 2 * screenWidth) calculateSampleSize(info, 2 * screenHeight, 2 * screenWidth)
) )
}.also { }.also {
(it as? BitmapDrawable)?.run { if (it !is Animatable) src.close()
src.close()
}
} }
private set private set
val size: Int val size: Int
get() = mObtainedDrawable!!.run { intrinsicHeight * intrinsicWidth * 4 * if (this is AnimatedImageDrawable) 4 else 1 } get() = mObtainedDrawable!!.run { intrinsicHeight * intrinsicWidth * 4 * if (this is Animatable) 4 else 1 }
@Synchronized @Synchronized
fun recycle() { fun recycle() {
(mObtainedDrawable as? AnimatedImageDrawable)?.stop() (mObtainedDrawable as? Animatable)?.stop()
(mObtainedDrawable as? BitmapDrawable)?.bitmap?.recycle() (mObtainedDrawable as? BitmapDrawable)?.bitmap?.recycle()
mObtainedDrawable?.callback = null mObtainedDrawable?.callback = null
(mObtainedDrawable as? AnimatedImageDrawable)?.let { src.close() } if (mObtainedDrawable is Animatable) src.close()
mObtainedDrawable = null mObtainedDrawable = null
} }
@@ -27,7 +27,6 @@ import coil.size.Size
import com.hippo.drawable.PreciselyClipDrawable import com.hippo.drawable.PreciselyClipDrawable
import com.hippo.ehviewer.EhApplication import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.R import com.hippo.ehviewer.R
import com.hippo.ehviewer.coil.MergeInterceptor
private val application = EhApplication.application private val application = EhApplication.application
private val errorDrawable = AppCompatResources.getDrawable(application, R.drawable.image_failed) private val errorDrawable = AppCompatResources.getDrawable(application, R.drawable.image_failed)
@@ -85,10 +84,7 @@ open class LoadImageView @JvmOverloads constructor(
if (!crossfade) crossfade(false) if (!crossfade) crossfade(false)
listener( listener(
{ setRetry(false) }, { setRetry(false) },
{ { setRetry(true) },
MergeInterceptor.onCancel(url)
setRetry(true)
},
{ _, _ -> { _, _ ->
super.setImageDrawable(errorDrawable) super.setImageDrawable(errorDrawable)
setRetry(true) setRetry(true)
+7 -1
View File
@@ -226,6 +226,12 @@
<string name="check_vote_status">查看投票情况</string> <string name="check_vote_status">查看投票情况</string>
<string name="click_more_comments">点击加载更多评论</string> <string name="click_more_comments">点击加载更多评论</string>
<string name="last_edited">上次修改时间:%s</string> <string name="last_edited">上次修改时间:%s</string>
<string name="format_bold"><b>粗体</b></string>
<string name="format_italic"><i>斜体</i></string>
<string name="format_underline"><u>下划线</u></string>
<string name="format_strikethrough"><strike>删除线</strike></string>
<string name="format_url">链接</string>
<string name="format_plain">纯文本</string>
<!-- Gallery Previews --> <!-- Gallery Previews -->
<string name="gallery_previews">画廊预览</string> <string name="gallery_previews">画廊预览</string>
<string name="go_to">跳页</string> <string name="go_to">跳页</string>
@@ -328,7 +334,7 @@
<string name="settings_eh_list_mode">列表模式</string> <string name="settings_eh_list_mode">列表模式</string>
<string name="settings_eh_list_mode_detail">详情</string> <string name="settings_eh_list_mode_detail">详情</string>
<string name="settings_eh_list_mode_thumb">缩略图</string> <string name="settings_eh_list_mode_thumb">缩略图</string>
<string name="settings_eh_detail_size">详情大小</string> <string name="settings_eh_detail_size">详情宽度</string>
<string name="settings_eh_detail_size_long">长</string> <string name="settings_eh_detail_size_long">长</string>
<string name="settings_eh_detail_size_short">短</string> <string name="settings_eh_detail_size_short">短</string>
<string name="settings_eh_thumb_size">缩略图大小</string> <string name="settings_eh_thumb_size">缩略图大小</string>
+2 -2
View File
@@ -272,7 +272,7 @@
<string name="format_bold"><b>Bold</b></string> <string name="format_bold"><b>Bold</b></string>
<string name="format_italic"><i>Italic</i></string> <string name="format_italic"><i>Italic</i></string>
<string name="format_underline"><u>Underline</u></string> <string name="format_underline"><u>Underline</u></string>
<string name="format_strikethrough"><s>Strikethrough</s></string> <string name="format_strikethrough"><strike>Strikethrough</strike></string>
<string name="format_url">URL</string> <string name="format_url">URL</string>
<string name="format_plain">Plain text</string> <string name="format_plain">Plain text</string>
<!-- Gallery Previews --> <!-- Gallery Previews -->
@@ -376,7 +376,7 @@
<string name="settings_eh_list_mode">List mode</string> <string name="settings_eh_list_mode">List mode</string>
<string name="settings_eh_list_mode_detail">Detail</string> <string name="settings_eh_list_mode_detail">Detail</string>
<string name="settings_eh_list_mode_thumb">Thumb</string> <string name="settings_eh_list_mode_thumb">Thumb</string>
<string name="settings_eh_detail_size">Detail size</string> <string name="settings_eh_detail_size">Detail width</string>
<string name="settings_eh_detail_size_long">Long</string> <string name="settings_eh_detail_size_long">Long</string>
<string name="settings_eh_detail_size_short">Short</string> <string name="settings_eh_detail_size_short">Short</string>
<string name="settings_eh_thumb_size">Thumb size</string> <string name="settings_eh_thumb_size">Thumb size</string>