Compare commits

...
19 Commits
Author SHA1 Message Date
tarsin d61df599e8 Release 1.8.6.1
Releases / Build (push) Has been cancelled
2023-02-18 10:38:33 +08:00
tarsin 989a0a01c1 Fix quicksearch progress 2023-02-18 10:37:57 +08:00
tarsin 12108645e9 Fix a stupid memleak 2023-02-18 10:37:45 +08:00
FooIbar 4910ec194d Fix search keyword in tag mode 2023-02-18 10:37:29 +08:00
FooIbar f730f43945 Fix comment last edited time parsing 2023-02-18 10:37:14 +08:00
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
17 changed files with 167 additions and 164 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-alpha03"
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 }
}
}
@@ -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) {
@@ -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()
}
@@ -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
+1 -1
View File
@@ -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
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