Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d61df599e8 | ||
|
|
989a0a01c1 | ||
|
|
12108645e9 | ||
|
|
4910ec194d | ||
|
|
f730f43945 | ||
|
|
6e7e668d39 | ||
|
|
2d086a5c6e | ||
|
|
f7e592eac0 | ||
|
|
c13abafb09 | ||
|
|
eeed0720f7 | ||
|
|
f099b74aa6 | ||
|
|
f7f54692a7 | ||
|
|
b97eff4284 | ||
|
|
f234dc61bc | ||
|
|
83b40d2ccb | ||
|
|
a8d3d71ea9 | ||
|
|
621772f359 | ||
|
|
63cd60b237 | ||
|
|
0df8e8522f | ||
|
|
1ebbf6e349 | ||
|
|
1b71f6a60a | ||
|
|
a3241e48df | ||
|
|
69396f3d59 |
No files matched your search
@@ -34,8 +34,8 @@ android {
|
||||
applicationId = "moe.tarsin.ehviewer"
|
||||
minSdk = 28
|
||||
targetSdk = 33
|
||||
versionCode = 180031
|
||||
versionName = "1.8.6.0-alpha01"
|
||||
versionCode = 180033
|
||||
versionName = "1.8.6.1"
|
||||
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") }
|
||||
|
||||
@@ -376,15 +376,15 @@ object GalleryDetailParser {
|
||||
}
|
||||
// time
|
||||
val c3 = JsoupUtils.getElementByClass(element, "c3")
|
||||
var temp = c3!!.ownText()
|
||||
if (temp.endsWith(':')) {
|
||||
temp = temp.substring("Posted on ".length, temp.length - " by:".length)
|
||||
val temp = c3!!.ownText()
|
||||
val time = if (temp.endsWith(':')) {
|
||||
// user
|
||||
comment.user = c3.child(0).text()
|
||||
temp.substring("Posted on ".length, temp.length - " by:".length)
|
||||
} else {
|
||||
temp = temp.substring("Posted on ".length)
|
||||
temp.substring("Posted on ".length)
|
||||
}
|
||||
comment.time = Instant.from(WEB_COMMENT_DATE_FORMAT.parse(temp)).toEpochMilli()
|
||||
comment.time = Instant.from(WEB_COMMENT_DATE_FORMAT.parse(time)).toEpochMilli()
|
||||
// comment
|
||||
val c6 = JsoupUtils.getElementByClass(element, "c6")
|
||||
// fix underline support
|
||||
@@ -407,7 +407,7 @@ object GalleryDetailParser {
|
||||
val e = c8.children().first()
|
||||
if (e != null) {
|
||||
comment.lastEdited =
|
||||
Instant.from(WEB_COMMENT_DATE_FORMAT.parse(temp)).toEpochMilli()
|
||||
Instant.from(WEB_COMMENT_DATE_FORMAT.parse(e.text())).toEpochMilli()
|
||||
}
|
||||
}
|
||||
comment
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ public abstract class PageLoader2 extends PageLoader {
|
||||
// With dot
|
||||
public static final String[] SUPPORT_IMAGE_EXTENSIONS = {
|
||||
".jpg", // Joint Photographic Experts Group
|
||||
".jpeg",
|
||||
".png", // Portable Network Graphics
|
||||
".gif", // Graphics Interchange Format
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -104,7 +105,6 @@ class SpiderQueenWorker(private val queen: SpiderQueen) : CoroutineScope {
|
||||
return queen.updatePageState(index, STATE_FINISHED)
|
||||
}
|
||||
if (force) {
|
||||
spiderDen.remove(index)
|
||||
pTokenLock.withLock {
|
||||
val pToken = spiderInfo.pTokenMap[index]
|
||||
if (pToken == TOKEN_FAILED) spiderInfo.pTokenMap.remove(index)
|
||||
@@ -310,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()
|
||||
}
|
||||
|
||||
@@ -74,7 +74,9 @@ import com.hippo.ehviewer.client.EhRequest
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_NORMAL
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_SUBSCRIPTION
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_TAG
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_TOPLIST
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_WHATS_HOT
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
@@ -98,13 +100,14 @@ import com.hippo.widget.FabLayout
|
||||
import com.hippo.widget.FabLayout.OnClickFabListener
|
||||
import com.hippo.widget.FabLayout.OnExpandListener
|
||||
import com.hippo.yorozuya.AnimationUtils
|
||||
import com.hippo.yorozuya.MathUtils
|
||||
import com.hippo.yorozuya.SimpleAnimatorListener
|
||||
import com.hippo.yorozuya.StringUtils
|
||||
import com.hippo.yorozuya.ViewUtils
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.delay
|
||||
import rikka.core.res.resolveColor
|
||||
import java.io.File
|
||||
import java.time.LocalDateTime
|
||||
@@ -164,7 +167,6 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
|
||||
}
|
||||
}
|
||||
private var mHasFirstRefresh = false
|
||||
private var mNavCheckedId = 0
|
||||
private val mDownloadManager: DownloadManager = downloadManager
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@@ -211,11 +213,19 @@ 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!")
|
||||
}
|
||||
val goto = args?.getString(KEY_GOTO)
|
||||
goto?.let {
|
||||
lifecycleScope.launchUI {
|
||||
delay(100)
|
||||
mHelper?.goTo(it, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -288,25 +298,6 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
|
||||
})
|
||||
}
|
||||
|
||||
private fun wrapTagKeyword(keyword: String?): String {
|
||||
var keyword = keyword
|
||||
keyword = keyword!!.trim { it <= ' ' }
|
||||
val index1 = keyword.indexOf(':')
|
||||
if (index1 == -1 || index1 >= keyword.length - 1) {
|
||||
// Can't find :, or : is the last char
|
||||
return keyword
|
||||
}
|
||||
if (keyword[index1 + 1] == '"') {
|
||||
// The char after : is ", the word must be quoted
|
||||
return keyword
|
||||
}
|
||||
val index2 = keyword.indexOf(' ')
|
||||
return if (index2 <= index1) {
|
||||
// Can't find space, or space is before :
|
||||
keyword
|
||||
} else keyword.substring(0, index1 + 1) + "\"" + keyword.substring(index1 + 1) + "$\""
|
||||
}
|
||||
|
||||
// Update search bar title, drawer checked item
|
||||
private fun onUpdateUrlBuilder() {
|
||||
_binding ?: return
|
||||
@@ -324,13 +315,16 @@ 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) {
|
||||
if (mode == ListUrlBuilder.MODE_TAG) {
|
||||
keyword = wrapTagKeyword(keyword)
|
||||
if (mode == MODE_TAG) {
|
||||
keyword = wrapTagKeyword(keyword!!)
|
||||
}
|
||||
setSearchBarText(keyword)
|
||||
}
|
||||
@@ -543,9 +537,10 @@ 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
|
||||
}
|
||||
@@ -992,7 +987,7 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
|
||||
val oldMode = mUrlBuilder.mode
|
||||
// If it's MODE_SUBSCRIPTION, keep it
|
||||
val newMode =
|
||||
if (oldMode == MODE_SUBSCRIPTION) MODE_SUBSCRIPTION else ListUrlBuilder.MODE_NORMAL
|
||||
if (oldMode == MODE_SUBSCRIPTION) MODE_SUBSCRIPTION else MODE_NORMAL
|
||||
mUrlBuilder.reset()
|
||||
mUrlBuilder.mode = newMode
|
||||
mUrlBuilder.keyword = query
|
||||
@@ -1152,8 +1147,7 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
|
||||
val goto = if (i != -1) q.name!!.substring(i + 1) else null
|
||||
val args = ListUrlBuilder().apply {
|
||||
set(q)
|
||||
setIndex(goto)
|
||||
}.toStartArgs()
|
||||
}.toStartArgs().apply { putString(KEY_GOTO, goto) }
|
||||
navigate(R.id.galleryListScene, args, true)
|
||||
setState(STATE_NORMAL)
|
||||
closeDrawer(GravityCompat.END)
|
||||
@@ -1428,6 +1422,7 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
|
||||
|
||||
companion object {
|
||||
const val KEY_ACTION = "action"
|
||||
const val KEY_GOTO = "goto"
|
||||
const val ACTION_HOMEPAGE = "action_homepage"
|
||||
const val ACTION_SUBSCRIPTION = "action_subscription"
|
||||
const val ACTION_WHATS_HOT = "action_whats_hot"
|
||||
@@ -1441,38 +1436,6 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
|
||||
private const val STATE_SEARCH = 2
|
||||
private const val STATE_SEARCH_SHOW_LIST = 3
|
||||
private const val ANIMATE_TIME = 300L
|
||||
private fun getSuitableTitleForUrlBuilder(
|
||||
resources: Resources, urlBuilder: ListUrlBuilder, appName: Boolean
|
||||
): String? {
|
||||
val keyword = urlBuilder.keyword
|
||||
val category = urlBuilder.category
|
||||
return if (ListUrlBuilder.MODE_NORMAL == urlBuilder.mode && EhUtils.NONE == category &&
|
||||
TextUtils.isEmpty(keyword) && urlBuilder.advanceSearch == -1 && urlBuilder.minRating == -1 && urlBuilder.pageFrom == -1 && urlBuilder.pageTo == -1
|
||||
) {
|
||||
resources.getString(if (appName) R.string.app_name else R.string.homepage)
|
||||
} else if (MODE_SUBSCRIPTION == urlBuilder.mode && EhUtils.NONE == category &&
|
||||
TextUtils.isEmpty(keyword) && urlBuilder.advanceSearch == -1 && urlBuilder.minRating == -1 && urlBuilder.pageFrom == -1 && urlBuilder.pageTo == -1
|
||||
) {
|
||||
resources.getString(R.string.subscription)
|
||||
} else if (MODE_WHATS_HOT == urlBuilder.mode) {
|
||||
resources.getString(R.string.whats_hot)
|
||||
} else if (MODE_TOPLIST == urlBuilder.mode) {
|
||||
when (urlBuilder.keyword) {
|
||||
"11" -> resources.getString(R.string.toplist_alltime)
|
||||
"12" -> resources.getString(R.string.toplist_pastyear)
|
||||
"13" -> resources.getString(R.string.toplist_pastmonth)
|
||||
"15" -> resources.getString(R.string.toplist_yesterday)
|
||||
else -> null
|
||||
}
|
||||
} else if (!TextUtils.isEmpty(keyword)) {
|
||||
keyword
|
||||
} else if (MathUtils.hammingWeight(category) == 1) {
|
||||
EhUtils.getCategory(category)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun ListUrlBuilder.toStartArgs(): Bundle {
|
||||
val args = Bundle()
|
||||
args.putString(KEY_ACTION, ACTION_LIST_URL_BUILDER)
|
||||
@@ -1480,4 +1443,40 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
|
||||
return args
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSuitableTitleForUrlBuilder(
|
||||
resources: Resources, urlBuilder: ListUrlBuilder, appName: Boolean
|
||||
): String? {
|
||||
val keyword = urlBuilder.keyword
|
||||
val category = urlBuilder.category
|
||||
val mode = urlBuilder.mode
|
||||
return if (mode == MODE_WHATS_HOT) {
|
||||
resources.getString(R.string.whats_hot)
|
||||
} else if (!keyword.isNullOrEmpty()) {
|
||||
when (mode) {
|
||||
MODE_TOPLIST -> {
|
||||
when (keyword) {
|
||||
"11" -> resources.getString(R.string.toplist_alltime)
|
||||
"12" -> resources.getString(R.string.toplist_pastyear)
|
||||
"13" -> resources.getString(R.string.toplist_pastmonth)
|
||||
"15" -> resources.getString(R.string.toplist_yesterday)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
MODE_TAG -> wrapTagKeyword(keyword)
|
||||
else -> keyword
|
||||
}
|
||||
} else if (category == EhUtils.NONE && urlBuilder.advanceSearch == -1) {
|
||||
when (mode) {
|
||||
MODE_NORMAL -> resources.getString(if (appName) R.string.app_name else R.string.homepage)
|
||||
MODE_SUBSCRIPTION -> resources.getString(R.string.subscription)
|
||||
else -> null
|
||||
}
|
||||
} else if (category.countOneBits() == 1) {
|
||||
EhUtils.getCategory(category)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -127,6 +127,7 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
|
||||
|
||||
fun setSearchBarText(text: String?) {
|
||||
binding.toolbar.text = text
|
||||
binding.searchview.setText(text)
|
||||
}
|
||||
|
||||
fun setEditTextHint(hint: String?) {
|
||||
@@ -168,18 +169,6 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
|
||||
mAllowEmptySearch = allowEmptySearch
|
||||
}
|
||||
|
||||
private fun wrapTagKeyword(keyword: String): String {
|
||||
return if (keyword.endsWith(':')) {
|
||||
keyword
|
||||
} else if (keyword.contains(' ')) {
|
||||
val tag = keyword.substringAfter(':')
|
||||
val prefix = keyword.dropLast(tag.length)
|
||||
"$prefix\"$tag$\" "
|
||||
} else {
|
||||
"$keyword$ "
|
||||
}
|
||||
}
|
||||
|
||||
interface SuggestionProvider {
|
||||
|
||||
fun providerSuggestions(text: String): List<Suggestion>?
|
||||
@@ -268,7 +257,7 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
|
||||
edittext.let {
|
||||
val keywords = it.text.toString().substringBeforeLast(' ', "")
|
||||
val keyword = wrapTagKeyword(mKeyword)
|
||||
val newKeywords = if (keywords.isNotEmpty()) "$keywords $keyword" else keyword
|
||||
val newKeywords = if (keywords.isNotEmpty()) "$keywords $keyword " else "$keyword "
|
||||
it.setText(newKeywords)
|
||||
it.setSelection(newKeywords.length)
|
||||
}
|
||||
@@ -370,3 +359,15 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun wrapTagKeyword(keyword: String): String {
|
||||
return if (keyword.endsWith(':')) {
|
||||
keyword
|
||||
} else if (keyword.contains(' ')) {
|
||||
val tag = keyword.substringAfter(':')
|
||||
val prefix = keyword.dropLast(tag.length)
|
||||
"$prefix\"$tag$\""
|
||||
} else {
|
||||
"$keyword$"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -56,8 +56,8 @@ class Image private constructor(private val src: CloseableSource) {
|
||||
(mObtainedDrawable as? AnimatedImageDrawable)?.stop()
|
||||
(mObtainedDrawable as? BitmapDrawable)?.bitmap?.recycle()
|
||||
mObtainedDrawable?.callback = null
|
||||
mObtainedDrawable = null
|
||||
(mObtainedDrawable as? AnimatedImageDrawable)?.let { src.close() }
|
||||
mObtainedDrawable = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user