Compare commits

...
16 Commits
Author SHA1 Message Date
tarsin 6e7e668d39 Release 1.8.6.0
Releases / Build (push) Has been cancelled
2023-02-17 11:39:05 +08:00
tarsin 2d086a5c6e Resolve #820 2023-02-17 11:36:36 +08:00
renovate[bot] f7e592eac0 Update plugin com.android.application to v8.0.0-beta03 2023-02-17 10:39:32 +08:00
tarsin c13abafb09 Release 1.8.6.0-beta01
Releases / Build (push) Has been cancelled
2023-02-16 09:22:37 +08:00
tarsin eeed0720f7 Resolve #814 2023-02-16 09:21:14 +08:00
FooIbar f099b74aa6 fixup! Use .jpg instead of .jpeg for image/jpeg 2023-02-15 08:46:57 +08:00
FooIbar f7f54692a7 Resolve #806 2023-02-15 08:46:30 +08:00
FooIbar b97eff4284 Resolve #805 2023-02-15 08:46:30 +08:00
FooIbar f234dc61bc Singletonize DownloadManager 2023-02-15 02:18:57 +08:00
FooIbar 83b40d2ccb Fix #808 2023-02-15 02:17:56 +08:00
FooIbar a8d3d71ea9 Close navigation drawer when reselected 2023-02-15 02:17:23 +08:00
FooIbar 621772f359 Implement #646 2023-02-15 02:17:23 +08:00
tarsin 63cd60b237 Release 1.8.6.0-alpha04
Releases / Build (push) Has been cancelled
2023-02-14 16:40:49 +08:00
tarsin 0df8e8522f Fix #791 2023-02-14 16:39:10 +08:00
tarsin 1ebbf6e349 Release 1.8.6.0-alpha03
Releases / Build (push) Has been cancelled
2023-02-14 16:22:12 +08:00
tarsin 1b71f6a60a Fix issue that download progress being reset when entering reader 2023-02-14 16:20:38 +08:00
15 changed files with 98 additions and 92 deletions

No files matched your search

+2 -2
View File
@@ -34,8 +34,8 @@ android {
applicationId = "moe.tarsin.ehviewer"
minSdk = 28
targetSdk = 33
versionCode = 180031
versionName = "1.8.6.0-alpha02"
versionCode = 180032
versionName = "1.8.6.0"
resourceConfigurations.addAll(
listOf(
"zh",
@@ -9,7 +9,7 @@ import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavOptions
fun onNavDestinationSelected2(item: MenuItem, navController: NavController): Boolean {
if (navController.currentDestination?.matchDestination(item.itemId) == true) return false
if (navController.currentDestination?.matchDestination(item.itemId) == true) return true
val builder = NavOptions.Builder().setLaunchSingleTop(true)
if (
navController.currentDestination!!.parent!!.findNode(item.itemId)
@@ -301,7 +301,7 @@ class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactor
}
@JvmStatic
val downloadManager by lazy { DownloadManager() }
val downloadManager by lazy { DownloadManager }
@JvmStatic
val hosts by lazy { Hosts(application, "hosts.db") }
@@ -15,13 +15,11 @@
*/
package com.hippo.ehviewer.download
import android.annotation.SuppressLint
import android.os.AsyncTask
import android.util.Log
import android.util.SparseLongArray
import androidx.collection.LongSparseArray
import androidx.collection.keyIterator
import com.hippo.ehviewer.EhDB
import com.hippo.ehviewer.client.data.BaseGalleryInfo
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.ehviewer.dao.DownloadInfo
import com.hippo.ehviewer.dao.DownloadLabel
@@ -31,16 +29,18 @@ import com.hippo.ehviewer.spider.SpiderQueen.OnSpiderListener
import com.hippo.ehviewer.spider.readCompatFromUniFile
import com.hippo.ehviewer.spider.write
import com.hippo.image.Image
import com.hippo.util.IoThreadPoolExecutor
import com.hippo.yorozuya.ConcurrentPool
import com.hippo.yorozuya.MathUtils
import com.hippo.yorozuya.ObjectUtils
import com.hippo.yorozuya.SimpleHandler
import com.hippo.yorozuya.collect.LongList
import java.io.IOException
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import java.util.LinkedList
class DownloadManager : OnSpiderListener {
object DownloadManager : OnSpiderListener {
private val downloadManager = this
// All download info list
private val mAllInfoList: LinkedList<DownloadInfo>
@@ -564,35 +564,25 @@ class DownloadManager : OnSpiderListener {
}
}
@SuppressLint("StaticFieldLeak")
fun resetAllReadingProgress() {
val list = LinkedList(mAllInfoList)
object : AsyncTask<Void?, Void?, Void?>() {
@Deprecated("Deprecated in Java")
override fun doInBackground(vararg voids: Void?): Void? {
val galleryInfo: GalleryInfo = BaseGalleryInfo()
for (downloadInfo in list) {
galleryInfo.gid = downloadInfo.gid
galleryInfo.token = downloadInfo.token
galleryInfo.title = downloadInfo.title
galleryInfo.thumb = downloadInfo.thumb
galleryInfo.category = downloadInfo.category
galleryInfo.posted = downloadInfo.posted
galleryInfo.uploader = downloadInfo.uploader
galleryInfo.rating = downloadInfo.rating
val downloadDir = SpiderDen.getGalleryDownloadDir(galleryInfo.gid) ?: continue
val file = downloadDir.findFile(SpiderQueen.SPIDER_INFO_FILENAME) ?: continue
val spiderInfo = readCompatFromUniFile(file) ?: continue
spiderInfo.startPage = 0
try {
spiderInfo.write(file)
} catch (e: IOException) {
Log.e(TAG, "Can't write SpiderInfo", e)
}
suspend fun resetAllReadingProgress() = coroutineScope {
mAllInfoMap.keyIterator().forEach { gid ->
launch {
runCatching {
resetReadingProgress(gid)
Log.d(TAG, "Current thread: ${Thread.currentThread().name}")
}.onFailure {
Log.e(TAG, "Can't write SpiderInfo", it)
}
return null
}
}.executeOnExecutor(IoThreadPoolExecutor.getInstance())
}
}
private fun resetReadingProgress(gid: Long) {
val downloadDir = SpiderDen.getGalleryDownloadDir(gid) ?: return
val file = downloadDir.findFile(SpiderQueen.SPIDER_INFO_FILENAME) ?: return
val spiderInfo = readCompatFromUniFile(file) ?: return
spiderInfo.startPage = 0
spiderInfo.write(file)
}
// Update in DB
@@ -948,7 +938,7 @@ class DownloadManager : OnSpiderListener {
fun onCancel(info: DownloadInfo)
}
private inner class NotifyTask : Runnable {
private class NotifyTask : Runnable {
private var mType = 0
private var mPages = 0
private var mIndex = 0
@@ -960,12 +950,12 @@ class DownloadManager : OnSpiderListener {
private var mDownloaded = 0
private var mTotal = 0
fun setOnGetPagesData(pages: Int) {
mType = Companion.TYPE_ON_GET_PAGES
mType = TYPE_ON_GET_PAGES
mPages = pages
}
fun setOnGet509Data(index: Int) {
mType = Companion.TYPE_ON_GET_509
mType = TYPE_ON_GET_509
mIndex = index
}
@@ -975,7 +965,7 @@ class DownloadManager : OnSpiderListener {
receivedSize: Long,
bytesRead: Int
) {
mType = Companion.TYPE_ON_PAGE_DOWNLOAD
mType = TYPE_ON_PAGE_DOWNLOAD
mIndex = index
mContentLength = contentLength
mReceivedSize = receivedSize
@@ -983,7 +973,7 @@ class DownloadManager : OnSpiderListener {
}
fun setOnPageSuccessData(index: Int, finished: Int, downloaded: Int, total: Int) {
mType = Companion.TYPE_ON_PAGE_SUCCESS
mType = TYPE_ON_PAGE_SUCCESS
mIndex = index
mFinished = finished
mDownloaded = downloaded
@@ -997,7 +987,7 @@ class DownloadManager : OnSpiderListener {
downloaded: Int,
total: Int
) {
mType = Companion.TYPE_ON_PAGE_FAILURE
mType = TYPE_ON_PAGE_FAILURE
mIndex = index
mError = error
mFinished = finished
@@ -1006,7 +996,7 @@ class DownloadManager : OnSpiderListener {
}
fun setOnFinishDate(finished: Int, downloaded: Int, total: Int) {
mType = Companion.TYPE_ON_FINISH
mType = TYPE_ON_FINISH
mFinished = finished
mDownloaded = downloaded
mTotal = total
@@ -1014,7 +1004,7 @@ class DownloadManager : OnSpiderListener {
override fun run() {
when (mType) {
Companion.TYPE_ON_GET_PAGES -> {
TYPE_ON_GET_PAGES -> {
val info = mCurrentTask
if (info == null) {
Log.e(TAG, "Current task is null, but it should not be")
@@ -1029,20 +1019,20 @@ class DownloadManager : OnSpiderListener {
}
}
Companion.TYPE_ON_GET_509 -> {
TYPE_ON_GET_509 -> {
if (mDownloadListener != null) {
mDownloadListener!!.onGet509()
}
}
Companion.TYPE_ON_PAGE_DOWNLOAD -> mSpeedReminder.onDownload(
TYPE_ON_PAGE_DOWNLOAD -> mSpeedReminder.onDownload(
mIndex,
mContentLength,
mReceivedSize,
mBytesRead
)
Companion.TYPE_ON_PAGE_SUCCESS -> {
TYPE_ON_PAGE_SUCCESS -> {
mSpeedReminder.onDone(mIndex)
val info = mCurrentTask
if (info == null) {
@@ -1063,7 +1053,7 @@ class DownloadManager : OnSpiderListener {
}
}
Companion.TYPE_ON_PAGE_FAILURE -> {
TYPE_ON_PAGE_FAILURE -> {
mSpeedReminder.onDone(mIndex)
val info = mCurrentTask
if (info == null) {
@@ -1081,7 +1071,7 @@ class DownloadManager : OnSpiderListener {
}
}
Companion.TYPE_ON_FINISH -> {
TYPE_ON_FINISH -> {
mSpeedReminder.onFinish()
// Download done
val info = mCurrentTask
@@ -1090,7 +1080,7 @@ class DownloadManager : OnSpiderListener {
mCurrentSpider = null
// Release spider
if (spider != null) {
spider.removeOnSpiderListener(this@DownloadManager)
spider.removeOnSpiderListener(downloadManager)
SpiderQueen.releaseSpiderQueen(spider, SpiderQueen.MODE_DOWNLOAD)
}
// Check null
@@ -1130,7 +1120,7 @@ class DownloadManager : OnSpiderListener {
}
}
internal inner class SpeedReminder : Runnable {
internal class SpeedReminder : Runnable {
private val mContentLengthMap = SparseLongArray()
private val mReceivedSizeMap = SparseLongArray()
private var mStop = true
@@ -1219,17 +1209,15 @@ class DownloadManager : OnSpiderListener {
}
}
companion object {
private val TAG = DownloadManager::class.java.simpleName
private const val TYPE_ON_GET_PAGES = 0
private const val TYPE_ON_GET_509 = 1
private const val TYPE_ON_PAGE_DOWNLOAD = 2
private const val TYPE_ON_PAGE_SUCCESS = 3
private const val TYPE_ON_PAGE_FAILURE = 4
private const val TYPE_ON_FINISH = 5
private val TAG = DownloadManager::class.java.simpleName
private const val TYPE_ON_GET_PAGES = 0
private const val TYPE_ON_GET_509 = 1
private const val TYPE_ON_PAGE_DOWNLOAD = 2
private const val TYPE_ON_PAGE_SUCCESS = 3
private const val TYPE_ON_PAGE_FAILURE = 4
private const val TYPE_ON_FINISH = 5
fun MutableList<DownloadInfo>.sortByDateDescending() {
sortByDescending { it.time }
}
private fun MutableList<DownloadInfo>.sortByDateDescending() {
sortByDescending { it.time }
}
}
@@ -333,6 +333,8 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
).build()
}
private val COMPAT_IMAGE_EXTENSIONS = PageLoader2.SUPPORT_IMAGE_EXTENSIONS + ".jpeg"
fun getGalleryDownloadDir(gid: Long): UniFile? {
val dir = Settings.getDownloadLocation()
// Read from DB
@@ -355,7 +357,7 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
}
private fun findImageFile(dir: UniFile, index: Int): UniFile? {
for (extension in PageLoader2.SUPPORT_IMAGE_EXTENSIONS) {
for (extension in COMPAT_IMAGE_EXTENSIONS) {
val filename = generateImageFilename(index, extension)
val file = dir.findFile(filename)
if (file != null) {
@@ -262,7 +262,7 @@ public final class SpiderQueen implements Runnable {
}
}
private boolean shouldIntoDownload = false;
private boolean mInDownloadMode = false;
private void updateMode() {
int mode;
@@ -277,7 +277,7 @@ public final class SpiderQueen implements Runnable {
// Update download page
boolean intoDownloadMode = mode == MODE_DOWNLOAD;
if (intoDownloadMode && mPageStateArray != null) {
if (intoDownloadMode && !mInDownloadMode && mPageStateArray != null) {
// Clear download state
synchronized (mPageStateLock) {
int[] temp = mPageStateArray;
@@ -292,9 +292,9 @@ public final class SpiderQueen implements Runnable {
mPageErrorMap.clear();
mPagePercentMap.clear();
}
} else if (intoDownloadMode && mPageStateArray == null) {
shouldIntoDownload = true;
}
mInDownloadMode = intoDownloadMode;
}
private void setMode(@Mode int mode) {
@@ -631,7 +631,7 @@ public final class SpiderQueen implements Runnable {
// Notify get pages
notifyGetPages(spiderInfo.getPages());
if (shouldIntoDownload) mWorkerScope.enterDownloadMode();
if (mInDownloadMode) mWorkerScope.enterDownloadMode();
// Wait finish
synchronized (mQueenLock) {
@@ -88,6 +88,7 @@ class SpiderQueenWorker(private val queen: SpiderQueen) : CoroutineScope {
fun launch(index: Int, force: Boolean = false) {
check(index in 0 until size)
if (!isDownloadMode) synchronized(mFetcherJobMap) { doLaunchDownloadJob(index, force) }
if (force) decoder.cancel(index)
decoder.launch(index)
}
@@ -309,11 +310,8 @@ class SpiderQueenWorker(private val queen: SpiderQueen) : CoroutineScope {
}
private suspend fun doInJob(index: Int) {
val src = spiderDen.getImageSource(index) ?: run {
delay(1000)
mFetcherJobMap[index]?.takeIf { it.isActive }?.join()
spiderDen.getImageSource(index)
} ?: return
mFetcherJobMap[index]?.takeIf { it.isActive }?.join()
val src = spiderDen.getImageSource(index) ?: return
val image = mSemaphore.withPermit { decode(src) }
runCatching {
currentCoroutineContext().ensureActive()
@@ -77,6 +77,7 @@ import com.hippo.yorozuya.ViewUtils
import com.hippo.yorozuya.collect.LongList
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.withUIContext
import rikka.core.res.resolveColor
@@ -377,7 +378,9 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
if (mDownloadManager != null) {
mDownloadManager!!.resetAllReadingProgress()
lifecycleScope.launchNonCancellable {
mDownloadManager!!.resetAllReadingProgress()
}
}
}.show()
return true
@@ -253,7 +253,7 @@ class GalleryCommentsScene : BaseToolbarScene(), View.OnClickListener, OnRefresh
text.clearSpan(start, end, false)
}
else -> {}
else -> return false
}
mode?.finish()
}
@@ -164,7 +164,6 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
}
}
private var mHasFirstRefresh = false
private var mNavCheckedId = 0
private val mDownloadManager: DownloadManager = downloadManager
@SuppressLint("NotifyDataSetChanged")
@@ -211,8 +210,9 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
ACTION_SUBSCRIPTION -> ListUrlBuilder(MODE_SUBSCRIPTION)
ACTION_WHATS_HOT -> ListUrlBuilder(MODE_WHATS_HOT)
ACTION_TOP_LIST -> ListUrlBuilder(MODE_TOPLIST, mKeyword = "11")
ACTION_LIST_URL_BUILDER -> args?.getParcelable<ListUrlBuilder>(KEY_LIST_URL_BUILDER)
?.copy() ?: ListUrlBuilder()
ACTION_LIST_URL_BUILDER ->
args?.getParcelableCompat<ListUrlBuilder>(KEY_LIST_URL_BUILDER)?.copy()
?: ListUrlBuilder()
else -> throw IllegalStateException("Wrong KEY_ACTION:${args?.getString(KEY_ACTION)} when handle args!")
}
@@ -324,8 +324,11 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
binding.fabLayout.setSecondaryFabVisibilityAt(0, !isPopular)
binding.fabLayout.setSecondaryFabVisibilityAt(2, !isPopular)
// Update normal search mode
// Update normal search mode and category
binding.searchLayout.setNormalSearchMode(if (mode == MODE_SUBSCRIPTION) R.id.search_subscription_search else R.id.search_normal_search)
if (category != EhUtils.NONE) {
binding.searchLayout.setCategory(category)
}
// Update search edit text
if (!mIsTopList) {
@@ -543,9 +546,9 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
val qsDrawerAdapter = QsDrawerAdapter(inflater)
qsDrawerAdapter.setHasStableIds(true)
drawerBinding.recyclerViewDrawer.adapter = qsDrawerAdapter
val itemTouchHelper = ItemTouchHelper(GalleryListQSItemTouchHelperCallback(qsDrawerAdapter))
itemTouchHelper.attachToRecyclerView(drawerBinding.recyclerViewDrawer)
if (!mIsTopList) {
val itemTouchHelper = ItemTouchHelper(GalleryListQSItemTouchHelperCallback(qsDrawerAdapter))
itemTouchHelper.attachToRecyclerView(drawerBinding.recyclerViewDrawer)
drawerBinding.tip.visibility = View.VISIBLE
drawerBinding.recyclerViewDrawer.visibility = View.GONE
}
@@ -29,6 +29,7 @@ import android.view.ViewGroup
import android.widget.*
import androidx.annotation.IntDef
import androidx.core.content.edit
import androidx.core.view.forEach
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
@@ -103,8 +104,8 @@ class SearchLayout @JvmOverloads constructor(
mChip.isChecked = NumberUtils.int2boolean(mPair.first and mCategoryStored)
mChip.setOnLongClickListener {
if (mChip.isChecked) {
for (i in 0 until mCategoryGroup.childCount) {
(mCategoryGroup.getChildAt(i) as Chip).isChecked = true
mCategoryGroup.forEach {
(it as IdentifiedChip).isChecked = true
}
mChip.isChecked = false
} else {
@@ -115,10 +116,10 @@ class SearchLayout @JvmOverloads constructor(
}
mCategoryGroup.addView(mChip)
}
mCategoryGroup.setOnCheckedStateChangeListener { _, checkedIds ->
mCategoryGroup.setOnCheckedStateChangeListener { group, checkedIds ->
var mCategory = 0
for (index in checkedIds) {
mCategory = mCategory or findViewById<IdentifiedChip>(index).idt
mCategory = mCategory or group.findViewById<IdentifiedChip>(index).idt
}
mSharePref.edit { putInt(SEARCH_CATEGORY_PREF, mCategory) }
}
@@ -160,6 +161,14 @@ class SearchLayout @JvmOverloads constructor(
mNormalSearchMode.check(id)
}
fun setCategory(category: Int) {
mCategoryGroup.forEach {
(it as IdentifiedChip).apply {
isChecked = idt and category != 0
}
}
}
override fun onSelectImage() {
if (mHelper != null) {
mHelper!!.onSelectImage()
@@ -245,7 +254,7 @@ class SearchLayout @JvmOverloads constructor(
post { setSearchMode(tab.position) }
}
fun setSearchMode(@SearchMode mode: Int) {
private fun setSearchMode(@SearchMode mode: Int) {
val oldItemCount = mAdapter.itemCount
mSearchMode = mode
val newItemCount = mAdapter.itemCount
@@ -51,6 +51,7 @@ abstract class PageLoader {
}
fun retryPage(page: ReaderPage) {
page.status.value = Page.State.QUEUE
onForceRequest(mPages.indexOf(page))
}
@@ -60,8 +61,8 @@ abstract class PageLoader {
protected abstract fun onForceRequest(index: Int)
fun cancelRequest(index: Int) {
onCancelRequest(index)
fun cancelRequest(page: ReaderPage) {
onCancelRequest(mPages.indexOf(page))
}
protected abstract fun onCancelRequest(index: Int)
@@ -62,7 +62,8 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
}
override fun destroyView(container: ViewGroup, position: Int, view: View) {
currentChapter?.cancelRequest(position)
val item = items[position]
currentChapter?.cancelRequest(item as ReaderPage)
}
/**
@@ -84,10 +84,11 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter<RecyclerV
* Recycles an existing view [holder] before adding it to the view pool.
*/
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
currentChapter?.cancelRequest(holder.bindingAdapterPosition)
when (holder) {
is WebtoonPageHolder -> holder.recycle()
}
val item = items.getOrNull(holder.bindingAdapterPosition) ?: return
currentChapter?.cancelRequest(item as ReaderPage)
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
plugins {
id("com.android.application") version "8.0.0-beta02" apply false
id("com.android.application") version "8.0.0-beta03" apply false
kotlin("android") version "1.8.10" apply false
kotlin("plugin.serialization") version "1.8.10" apply false
id("com.google.devtools.ksp") version "1.8.10-1.0.9" apply false