From 335d860b0ddbc2b927dfa76c64a44cc5b0c617e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sun, 10 Mar 2024 03:14:23 +0900 Subject: [PATCH] =?UTF-8?q?v2.2.0=20=E4=BF=AE=E5=A4=8D=201.=20=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E6=8C=89=E9=92=AE=E5=9C=A8=E6=9F=90=E4=BA=9B?= =?UTF-8?q?=E6=9C=BA=E5=9E=8B=E6=98=BE=E7=A4=BA=E4=B8=8D=E5=85=A8=202.=20?= =?UTF-8?q?=E5=9B=BE=E5=BA=8A=E4=BB=A3=E7=90=86=E4=B8=8D=E6=AD=A3=E7=A1=AE?= =?UTF-8?q?=E6=89=93=E5=BC=80=E5=AF=BC=E8=87=B4=E7=9A=84=E9=97=AA=E9=80=80?= =?UTF-8?q?=203.=20=E4=B8=8B=E8=BD=BD=E4=B8=AD=E5=8F=96=E6=B6=88=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=88=99=E9=97=AA=E9=80=80=204.=20=E6=96=AD=E7=82=B9?= =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E4=B8=8B=E8=BD=BD=E7=9A=84zip=E9=94=99?= =?UTF-8?q?=E8=AF=AF=205.=20=E6=B5=8F=E8=A7=88=E5=8E=86=E5=8F=B2=E6=A0=87?= =?UTF-8?q?=E8=AE=B0=E6=98=BE=E7=A4=BA=E9=94=99=E8=AF=AF=206.=20=E6=B5=8F?= =?UTF-8?q?=E8=A7=88=E5=B7=B2=E4=B8=8B=E8=BD=BD=E7=AB=A0=E8=8A=82=E6=97=B6?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E8=AE=B0=E5=BD=95=E5=88=B0=E4=BA=91=E7=AB=AF?= =?UTF-8?q?=207.=20=E8=BF=9B=E5=85=B6=E4=BB=96=E4=B9=A6=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E5=90=8E=E6=BC=AB=E7=94=BB=E4=B8=8B=E8=BD=BD=E5=8F=98=E4=B8=BA?= =?UTF-8?q?=E6=9C=80=E4=B8=8A=E5=B1=82=20=E4=BC=98=E5=8C=96=201.=20?= =?UTF-8?q?=E8=BF=9B=E4=B8=80=E6=AD=A5=E5=8D=8F=E7=A8=8B=E5=8C=96CardList,?= =?UTF-8?q?=20HTTP/=E6=96=87=E4=BB=B6=E8=AF=BB=E5=86=99=E7=AD=89=202.=20?= =?UTF-8?q?=E5=90=84=E6=A8=A1=E5=9D=97=E5=8A=A0=E8=BD=BD=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/dictionaries/fumiama.xml | 2 + app/build.gradle | 17 +- .../top/fumiama/copymanga/MainActivity.kt | 5 +- .../java/top/fumiama/copymanga/manga/Book.kt | 10 + .../fumiama/copymanga/manga/MangaDlTools.kt | 10 +- .../java/top/fumiama/copymanga/manga/Shelf.kt | 9 +- .../template/general/ActivityTemplate.kt | 2 +- .../template/general/NoBackRefreshFragment.kt | 2 +- .../template/http/AutoDownloadHandler.kt | 69 ++--- .../template/http/AutoDownloadThread.kt | 25 -- .../template/http/PausableDownloader.kt | 30 +++ .../fumiama/copymanga/template/ui/CardList.kt | 84 +++--- .../copymanga/template/ui/InfoCardLoader.kt | 39 +-- .../top/fumiama/copymanga/tools/api/CMApi.kt | 66 ++--- .../copymanga/tools/http/DownloadPool.kt | 42 +-- .../copymanga/tools/http/DownloadTools.kt | 58 ++--- .../top/fumiama/copymanga/tools/http/Proxy.kt | 19 +- .../copymanga/tools/thread/TimeThread.kt | 4 +- .../top/fumiama/copymanga/tools/ui/UITools.kt | 6 +- .../fumiama/copymanga/ui/book/BookFragment.kt | 32 ++- .../fumiama/copymanga/ui/book/BookHandler.kt | 176 +++++++------ .../ui/cardflow/sort/SortFragment.kt | 152 +++++------ .../ui/cardflow/topic/TopicFragment.kt | 42 +-- .../copymanga/ui/comicdl/ComicDlFragment.kt | 94 ++++--- .../copymanga/ui/comicdl/ComicDlHandler.kt | 244 ++++++++++-------- .../copymanga/ui/download/DownloadFragment.kt | 7 +- .../ui/download/NewDownloadFragment.kt | 3 +- .../fumiama/copymanga/ui/home/HomeFragment.kt | 66 +++-- .../fumiama/copymanga/ui/home/HomeHandler.kt | 122 ++++----- .../top/fumiama/copymanga/ui/vm/VMHandler.kt | 49 ++-- .../copymanga/ui/vm/ViewMangaActivity.kt | 228 ++++++++-------- .../java/top/fumiama/copymanga/user/Member.kt | 67 +++-- .../fumiama/copymanga/views/ScaleImageView.kt | 8 +- app/src/main/res/layout/line_bookinfo.xml | 4 +- app/src/main/res/values/strings.xml | 6 +- 35 files changed, 988 insertions(+), 811 deletions(-) create mode 100644 app/src/main/java/top/fumiama/copymanga/manga/Book.kt delete mode 100644 app/src/main/java/top/fumiama/copymanga/template/http/AutoDownloadThread.kt create mode 100644 app/src/main/java/top/fumiama/copymanga/template/http/PausableDownloader.kt diff --git a/.idea/dictionaries/fumiama.xml b/.idea/dictionaries/fumiama.xml index 9904d8f..fe0a67e 100644 --- a/.idea/dictionaries/fumiama.xml +++ b/.idea/dictionaries/fumiama.xml @@ -3,7 +3,9 @@ imgs lowpan + mangafuna nisi + pausable reilia diff --git a/app/build.gradle b/app/build.gradle index 4c9f419..8ed6a3b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,19 +8,32 @@ android { applicationId 'top.fumiama.copymanga' minSdkVersion 23 targetSdkVersion 34 - versionCode 46 - versionName '2.1.3' + versionCode 47 + versionName '2.2.0' resourceConfigurations += ['zh', 'zh-rCN'] testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + aaptOptions { + cruncherEnabled = false + } } + signingConfigs { + release { + enableV1Signing true + enableV2Signing true + enableV3Signing true + enableV4Signing true + } + } buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release } /*winrelease { minifyEnabled true diff --git a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt index 07b4942..d96738d 100644 --- a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt +++ b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt @@ -156,7 +156,7 @@ class MainActivity : AppCompatActivity() { true } R.id.action_download -> { - bookHandler?.sendEmptyMessage(6) + bookHandler.get()?.sendEmptyMessage(6) true } R.id.action_sort -> { @@ -213,6 +213,7 @@ class MainActivity : AppCompatActivity() { Glide.with(this@MainActivity).load(avatar) .apply(RequestOptions.bitmapTransform(CircleCrop())) .into(navhicon) + else navhicon.setImageResource(R.mipmap.ic_launcher) } } @@ -371,7 +372,7 @@ class MainActivity : AppCompatActivity() { startActivity(Intent(this, LoginActivity::class.java)) } - companion object{ + companion object { var mainWeakReference: WeakReference? = null var isDrawerClosed = true var ime: InputMethodManager? = null diff --git a/app/src/main/java/top/fumiama/copymanga/manga/Book.kt b/app/src/main/java/top/fumiama/copymanga/manga/Book.kt new file mode 100644 index 0000000..b5d1c60 --- /dev/null +++ b/app/src/main/java/top/fumiama/copymanga/manga/Book.kt @@ -0,0 +1,10 @@ +package top.fumiama.copymanga.manga + +class Book(val pathWord: String, val readLocally: Boolean = false) { + /** + * 更新云端最新信息 + */ + suspend fun update() { + + } +} \ No newline at end of file diff --git a/app/src/main/java/top/fumiama/copymanga/manga/MangaDlTools.kt b/app/src/main/java/top/fumiama/copymanga/manga/MangaDlTools.kt index 10cb03f..77983e6 100644 --- a/app/src/main/java/top/fumiama/copymanga/manga/MangaDlTools.kt +++ b/app/src/main/java/top/fumiama/copymanga/manga/MangaDlTools.kt @@ -4,7 +4,7 @@ import android.util.Log import com.google.gson.Gson import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference import top.fumiama.copymanga.json.Chapter2Return -import top.fumiama.copymanga.template.http.AutoDownloadThread +import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.tools.http.DownloadPool import java.io.File @@ -19,18 +19,18 @@ class MangaDlTools { get() = pool?.wait set(value) { if (value != null) { pool?.wait = value } } - fun downloadChapterInVol(url: CharSequence, chapterName: CharSequence, group: CharSequence, index: Int) { + suspend fun downloadChapterInVol(url: CharSequence, chapterName: CharSequence, group: CharSequence, index: Int) { Log.d("MyMDT", "下载:$url, index:$index") - AutoDownloadThread(url.toString(), 1000) { data -> + PausableDownloader(url.toString(), 1000) { data -> try { - Gson().fromJson(data?.decodeToString(), Chapter2Return::class.java)?.let { + Gson().fromJson(data.decodeToString(), Chapter2Return::class.java)?.let { getChapterInfo(it, index, chapterName, group) } } catch (e: Exception) { e.printStackTrace() onDownloadedListener?.handleMessage(index, false, e.localizedMessage?:"Gson parsing error") } - }.start() + }.run() } @Synchronized private fun prepareDownloadListener() { diff --git a/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt b/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt index 8974803..428f32f 100644 --- a/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt +++ b/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt @@ -51,8 +51,13 @@ class Shelf(private val token: String, getString: (Int) -> String) { } suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) { - return@withContext DownloadTools.getHttpContent(queryApiUrl.format(hostUrl, pathWord), referer, ua)?.let { - Gson().fromJson(it.decodeToString(), BookQueryStructure::class.java) + try { + Gson().fromJson(DownloadTools.getHttpContent( + queryApiUrl.format(hostUrl, pathWord), referer, ua + ).decodeToString(), BookQueryStructure::class.java) + } catch (e: Exception) { + e.printStackTrace() + null } } } diff --git a/app/src/main/java/top/fumiama/copymanga/template/general/ActivityTemplate.kt b/app/src/main/java/top/fumiama/copymanga/template/general/ActivityTemplate.kt index c3d8dfb..9e09190 100644 --- a/app/src/main/java/top/fumiama/copymanga/template/general/ActivityTemplate.kt +++ b/app/src/main/java/top/fumiama/copymanga/template/general/ActivityTemplate.kt @@ -7,7 +7,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.edit import top.fumiama.copymanga.tools.ui.UITools -open class ActivityTemplate:AppCompatActivity() { +open class ActivityTemplate: AppCompatActivity() { lateinit var toolsBox: UITools val pb = BoolPref() private val allFullScreen diff --git a/app/src/main/java/top/fumiama/copymanga/template/general/NoBackRefreshFragment.kt b/app/src/main/java/top/fumiama/copymanga/template/general/NoBackRefreshFragment.kt index 84ff2e0..29b338d 100644 --- a/app/src/main/java/top/fumiama/copymanga/template/general/NoBackRefreshFragment.kt +++ b/app/src/main/java/top/fumiama/copymanga/template/general/NoBackRefreshFragment.kt @@ -18,7 +18,7 @@ open class NoBackRefreshFragment(private val layoutToLoad: Int): Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - if(_rootView == null){ + if(_rootView == null) { isFirstInflate = true _rootView = inflater.inflate(layoutToLoad, container, false) Log.d("MyNBRF", "is first inflate") diff --git a/app/src/main/java/top/fumiama/copymanga/template/http/AutoDownloadHandler.kt b/app/src/main/java/top/fumiama/copymanga/template/http/AutoDownloadHandler.kt index 1da6e33..5de469a 100644 --- a/app/src/main/java/top/fumiama/copymanga/template/http/AutoDownloadHandler.kt +++ b/app/src/main/java/top/fumiama/copymanga/template/http/AutoDownloadHandler.kt @@ -4,21 +4,30 @@ import android.os.Handler import android.os.Looper import android.os.Message import android.util.Log +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope import com.google.gson.Gson -import top.fumiama.dmzj.copymanga.R +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference import top.fumiama.copymanga.json.ReturnBase import top.fumiama.copymanga.tools.http.DownloadTools import top.fumiama.copymanga.tools.thread.TimeThread +import top.fumiama.dmzj.copymanga.R import java.io.File import java.lang.Thread.sleep import java.security.MessageDigest -open class AutoDownloadHandler(private val url: String, private val jsonClass: Class<*>, looper: Looper, private val callCheckMsg: Int = -1, private val loadFromCache: Boolean = false, private val customCacheFile: File? = null): Handler(looper) { - var exit = false +open class AutoDownloadHandler( + private val url: String, private val jsonClass: Class<*>, + private val context: LifecycleOwner?, + private val callCheckMsg: Int = -1, + private val loadFromCache: Boolean = false, + private val customCacheFile: File? = null): Handler(Looper.myLooper()!!) { private var timeThread: TimeThread? = null private var checkTimes = 0 - private var cnt = 0 + var exit = false override fun handleMessage(msg: Message) { super.handleMessage(msg) when(msg.what){ @@ -29,19 +38,21 @@ open class AutoDownloadHandler(private val url: String, private val jsonClass: C open fun setGsonItem(gsonObj: Any): Boolean = true open fun getGsonItem(): ReturnBase? = null open fun onError() {} - open fun doWhenFinishDownload() {} + open suspend fun doWhenFinishDownload() {} fun startLoad() { sendEmptyMessage(0) } fun destroy() { exit = true } - private fun download() { - Thread{ dlThread() }.start() + private suspend fun download() = withContext(Dispatchers.IO) { checkTimes = 0 - timeThread = TimeThread(this, callCheckMsg) - timeThread?.canDo = true - timeThread?.start() + TimeThread(this@AutoDownloadHandler, callCheckMsg, 100).let { + timeThread = it + it.canDo = true + it.start() + } + downloadCoroutine() } private fun toHexStr(byteArray: ByteArray) = with(StringBuilder()) { @@ -53,7 +64,7 @@ open class AutoDownloadHandler(private val url: String, private val jsonClass: C } toString() } - private fun dlThread() { + private suspend fun downloadCoroutine() = withContext(Dispatchers.IO) { val cacheName = toHexStr(MessageDigest.getInstance("MD5").digest(url.encodeToByteArray())) val cacheFile = customCacheFile?:(mainWeakReference?.get()?.externalCacheDir?.let { File(it, cacheName) }) if(loadFromCache) { @@ -67,31 +78,29 @@ open class AutoDownloadHandler(private val url: String, private val jsonClass: C e.printStackTrace() } fi.close() - if (pass) return + if (pass) return@withContext } } } - DownloadTools.getHttpContent(url, null, mainWeakReference?.get()?.getString(R.string.pc_ua)!!).let { - if(exit) return - if(it == null) { - if (cnt++>3) return - sleep(2000) - dlThread() - return - } - val fi = it.inputStream() - var pass = true + var cnt = 0 + while (cnt++ <= 3) { try { - pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass)) + val data = DownloadTools.getHttpContent(url, null, mainWeakReference?.get()?.getString(R.string.pc_ua)!!) + if(exit) return@withContext + val fi = data.inputStream() + val pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass)) if (pass && loadFromCache) { - cacheFile?.writeBytes(it) + cacheFile?.writeBytes(data) } + fi.close() + if(!pass) { + sleep(2000) + continue + } + break } catch (e: Exception) { e.printStackTrace() - } - fi.close() - if(!pass) { - dlThread() + sleep(2000) } } } @@ -102,9 +111,9 @@ open class AutoDownloadHandler(private val url: String, private val jsonClass: C if(g.code == 200) sendEmptyMessage(0) else onError() Log.d("MyADH", "[${g.code}]${g.message}") - } else if(checkTimes++ > 3) timeThread?.canDo = false + } else if(checkTimes++ > 1000) timeThread?.canDo = false } - private fun setLayouts() { + private fun setLayouts() = context?.lifecycleScope?.launch { if(getGsonItem() == null) download() else doWhenFinishDownload() } diff --git a/app/src/main/java/top/fumiama/copymanga/template/http/AutoDownloadThread.kt b/app/src/main/java/top/fumiama/copymanga/template/http/AutoDownloadThread.kt deleted file mode 100644 index 1137c35..0000000 --- a/app/src/main/java/top/fumiama/copymanga/template/http/AutoDownloadThread.kt +++ /dev/null @@ -1,25 +0,0 @@ -package top.fumiama.copymanga.template.http - -import android.util.Log -import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference -import top.fumiama.copymanga.tools.http.DownloadTools -import top.fumiama.dmzj.copymanga.R -import kotlin.random.Random - -class AutoDownloadThread(private val url: String, private val waitMilliseconds: Long = 0, private val whenFinish: (result: ByteArray?)->Unit): Thread() { - var exit = false - override fun run() { - super.run() - var re: ByteArray? = null - var c = 0 - while (!exit && re == null && c++ < 3) { - if (waitMilliseconds > 0) sleep(200+Random.nextLong(waitMilliseconds)) - re = DownloadTools.getHttpContent(url, - mainWeakReference?.get()?.getString(R.string.referer)!!, - mainWeakReference?.get()?.getString(R.string.pc_ua)!! - ) - } - if(!exit) whenFinish(re) - Log.d("MyADT", "found exit = $exit") - } -} \ No newline at end of file diff --git a/app/src/main/java/top/fumiama/copymanga/template/http/PausableDownloader.kt b/app/src/main/java/top/fumiama/copymanga/template/http/PausableDownloader.kt new file mode 100644 index 0000000..99a38d1 --- /dev/null +++ b/app/src/main/java/top/fumiama/copymanga/template/http/PausableDownloader.kt @@ -0,0 +1,30 @@ +package top.fumiama.copymanga.template.http + +import android.util.Log +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference +import top.fumiama.copymanga.tools.http.DownloadTools +import top.fumiama.dmzj.copymanga.R +import java.lang.Thread.sleep +import kotlin.random.Random + +class PausableDownloader(private val url: String, private val waitMilliseconds: Long = 0, private val whenFinish: suspend (result: ByteArray)->Unit) { + var exit = false + suspend fun run() = withContext(Dispatchers.IO) { + var c = 0 + while (!exit && c++ < 3) { + try { + whenFinish(DownloadTools.getHttpContent(url, + mainWeakReference?.get()?.getString(R.string.referer)!!, + mainWeakReference?.get()?.getString(R.string.pc_ua)!! + )) + break + } catch (e: Exception) { + e.printStackTrace() + if (waitMilliseconds > 0) sleep(200+Random.nextLong(waitMilliseconds)) + } + } + Log.d("MyPD", "found exit = $exit") + } +} diff --git a/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt b/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt index 7f9b742..bcc9912 100644 --- a/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt +++ b/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt @@ -10,6 +10,8 @@ import com.bumptech.glide.load.model.GlideUrl import kotlinx.android.synthetic.main.card_book.view.* import kotlinx.android.synthetic.main.line_horizonal_empty.view.* import kotlinx.android.synthetic.main.line_lazybooklines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener import top.fumiama.dmzj.copymanga.R @@ -31,7 +33,7 @@ class CardList( var initClickListeners: InitClickListeners? = null var exitCardList = false - fun reset(){ + fun reset() { rows = arrayOfNulls(20) index = 0 count = 0 @@ -39,18 +41,18 @@ class CardList( exitCardList = false } - private fun manageRow() { + private suspend fun manageRow() { if(!exitCardList && count++ % cardPerRow == 0) inflateRow() Log.d("MyCL", "index: $index, cardPR: $cardPerRow") } - private fun inflateRow(){ + private suspend fun inflateRow() = withContext(Dispatchers.IO) { that?.apply { layoutInflater.inflate(R.layout.line_horizonal_empty, mydll, false)?.let { - if(exitCardList) return + if(exitCardList) return@withContext it.layoutParams.height = cardHeight + 16 - activity?.runOnUiThread { - if(exitCardList) return@runOnUiThread + withContext(Dispatchers.Main) withMainContext@ { + if(exitCardList) return@withMainContext mydll?.addView(it) } recycleOneRow(it) @@ -58,14 +60,14 @@ class CardList( } } } - private fun recycleOneRow(v:View?){ + private suspend fun recycleOneRow(v:View?) = withContext(Dispatchers.IO) { val relativeIndex = index % 20 if(rows[relativeIndex] == null) rows[relativeIndex] = v else { val victim = rows[relativeIndex] that?.apply { - activity?.runOnUiThread { - if(exitCardList) return@runOnUiThread + withContext(Dispatchers.Main) withMainContext@ { + if(exitCardList) return@withMainContext mydll?.removeView(victim) mys?.scrollY = mys?.scrollY?.minus(cardHeight + 16)?:0 } @@ -75,8 +77,9 @@ class CardList( } @ExperimentalStdlibApi - fun addCard(name: String, append: String? = null, head: String? = null, path: String? = null, chapterUUID: String? = null, pn: Int? = null, isFinish: Boolean = false, isNew: Boolean = false) { - if (exitCardList) return + suspend fun addCard(name: String, append: String? = null, head: String? = null, path: String? = null, chapterUUID: String? = null, pn: Int? = null, isFinish: Boolean = false, isNew: Boolean = false) = + withContext(Dispatchers.IO) { + if (exitCardList) return@withContext manageRow() that?.apply { layoutInflater.inflate(R.layout.card_book, mydll?.ltbtn, false)?.let { @@ -90,26 +93,23 @@ class CardList( card.pageNumber = pn card.isFinish = isFinish card.isNew = isNew - activity?.runOnUiThread { - if (exitCardList) return@runOnUiThread - addCard(it) - } + addCard(it) } } } @SuppressLint("SetTextI18n") @ExperimentalStdlibApi - fun addCard(cardFrame: View) { + private suspend fun addCard(cardFrame: View) = withContext(Dispatchers.IO) withIO@ { val card = cardFrame.cic - if (card.index < 0) return + if (card.index < 0) return@withIO val name = card.name + (card.append?:"") val head = card.headImageUrl val file = File(that?.context?.getExternalFilesDir(""), card.name) - if(exitCardList) return + if(exitCardList) return@withIO cardFrame.let { - it.tic.text = name - if(!file.exists()){ + withContext(Dispatchers.Main) { it.tic.text = name } + if(!file.exists()) { if(head != null) { that?.context?.let { context -> val waitMillis = cardLoadingWaits.getAndIncrement().toLong()*200 @@ -119,35 +119,45 @@ class CardList( if (exitCardList) return@GlideHideLottieViewListener cardLoadingWaits.decrementAndGet() }) - if (waitMillis > 0) it.imic.postDelayed({ - if (exitCardList) return@postDelayed - g.into(it.imic) - }, waitMillis) else g.into(it.imic) + withContext(Dispatchers.Main) { + if (waitMillis > 0) it.imic.postDelayed({ + if (exitCardList) return@postDelayed + g.into(it.imic) + }, waitMillis) else g.into(it.imic) + } } - } else { + } else withContext(Dispatchers.Main) { it.laic.pauseAnimation() it.laic.visibility = View.GONE it.imic.setImageResource(R.drawable.img_defmask) } } else { val img = File(file, "head.jpg") - it.laic.pauseAnimation() - it.laic.visibility = View.GONE + withContext(Dispatchers.Main) { + it.laic.pauseAnimation() + it.laic.visibility = View.GONE + } if(img.exists()) { - it.imic.setImageURI(Uri.fromFile(img)) + withContext(Dispatchers.Main) { + it.imic.setImageURI(Uri.fromFile(img)) + } } else { - it.imic.setImageResource(R.drawable.img_defmask) + withContext(Dispatchers.Main) { + it.imic.setImageResource(R.drawable.img_defmask) + } } } - if(card.isFinish) it.sgnic.visibility = View.VISIBLE - if(card.isNew) it.sgnnew.visibility = View.VISIBLE - initClickListeners?.prepareListeners(card, card.name, card.path, card.chapterUUID, card.pageNumber) - rows[card.index % 20]?.ltbtn?.addView(it) - it.layoutParams?.height = cardHeight - it.layoutParams?.width = cardWidth + withContext(Dispatchers.Main) { + if(card.isFinish) it.sgnic.visibility = View.VISIBLE + if(card.isNew) it.sgnnew.visibility = View.VISIBLE + initClickListeners?.prepareListeners(card, card.name, card.path, card.chapterUUID, card.pageNumber) + rows[card.index % 20]?.ltbtn?.addView(it) + it.layoutParams?.height = cardHeight + it.layoutParams?.width = cardWidth + } } } - interface InitClickListeners{ + interface InitClickListeners { fun prepareListeners(v: View, name: String, path: String?, chapterUUID: String?, pn: Int?) } -} \ No newline at end of file +} diff --git a/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt b/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt index 1d8c55e..02148fc 100644 --- a/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt +++ b/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt @@ -4,15 +4,17 @@ import android.os.Bundle import android.util.Log import android.view.View import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import com.google.gson.Gson import kotlinx.android.synthetic.main.line_lazybooklines.* +import kotlinx.coroutines.launch import top.fumiama.copymanga.json.BookListStructure import top.fumiama.copymanga.json.HistoryBookListStructure import top.fumiama.copymanga.json.ShelfStructure import top.fumiama.copymanga.json.TypeBookListStructure import top.fumiama.copymanga.template.general.MangaPagesFragmentTemplate -import top.fumiama.copymanga.template.http.AutoDownloadThread +import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.tools.ui.Navigate import java.lang.ref.WeakReference @@ -20,18 +22,12 @@ import java.lang.ref.WeakReference open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isTypeBook: Boolean = false, private val isHistoryBook: Boolean = false, private val isShelfBook: Boolean = false): MangaPagesFragmentTemplate(inflateRes) { var offset = 0 private val subUrl get() = getApiUrl() - var ad: AutoDownloadThread? = null + var ad: PausableDownloader? = null - override fun addPage(){ + override fun addPage() { super.addPage() - ad = AutoDownloadThread(subUrl) { data -> - if (data == null) { - activity?.runOnUiThread { - findNavController().popBackStack() - } - return@AutoDownloadThread - } - if(isRefresh){ + ad = PausableDownloader(subUrl) { data -> + if(isRefresh) { page = 0 isRefresh = false } @@ -42,7 +38,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT if(results.offset < results.total) { if(code == 200) { results.list.forEach { book -> - if(ad?.exit == true) return@AutoDownloadThread + if(ad?.exit == true) return@PausableDownloader cardList?.addCard( book?.comic?.name?:"null", null, book?.comic?.cover, book?.comic?.path_word, null, null, @@ -61,9 +57,9 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT if(results.offset < results.total) { if(code == 200) { results?.list?.forEach{ book -> - if(ad?.exit == true) return@AutoDownloadThread + if(ad?.exit == true) return@PausableDownloader cardList?.addCard( - book?.comic?.name?:"null", "\n最新${book?.last_chapter_name}", book?.comic?.cover, + book?.comic?.name?:"null", "\n云读至${book?.last_chapter_name}", book?.comic?.cover, book?.comic?.path_word, null, null, book?.comic?.status==1 ) @@ -80,7 +76,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT if(results.offset < results.total) { if(code == 200) { results?.list?.forEach{ book -> - if(ad?.exit == true) return@AutoDownloadThread + if(ad?.exit == true) return@PausableDownloader cardList?.addCard( book?.comic?.name?:"null", "\n${book?.last_browse?.last_browse_name?.let { "读到$it" }?:"未读"}", book?.comic?.cover, book?.comic?.path_word, null, null, @@ -100,7 +96,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT if(results.offset < results.total) { if(code == 200) { results?.list?.forEach{ book -> - if(ad?.exit == true) return@AutoDownloadThread + if(ad?.exit == true) return@PausableDownloader cardList?.addCard(book?.name?:"null", null, book?.cover, book?.path_word, null, null, false) } offset += results.list.size @@ -111,7 +107,16 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT } onLoadFinish() } - ad?.start() + lifecycleScope.launch { + try { + ad?.run() + } catch (e: Exception) { + e.printStackTrace() + try { + findNavController().popBackStack() + } catch (_: Exception) {} + } + } } override fun initCardList(weakReference: WeakReference) { super.initCardList(weakReference) diff --git a/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt b/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt index 904e2ab..badbb0a 100644 --- a/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt +++ b/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt @@ -1,31 +1,44 @@ package top.fumiama.copymanga.tools.api -import android.util.Base64 import androidx.preference.PreferenceManager import com.bumptech.glide.load.model.LazyHeaders -import top.fumiama.dmzj.copymanga.R import top.fumiama.copymanga.MainActivity -import top.fumiama.copymanga.tools.http.DownloadTools import top.fumiama.copymanga.tools.http.Proxy import top.fumiama.copymanga.tools.http.Resolution +import top.fumiama.dmzj.copymanga.R import java.io.File -import java.net.URLEncoder object CMApi { - var proxy = if(Proxy.useImageProxy) Proxy(R.string.imgProxyApiUrl, R.string.imgProxyApiPrefix, R.string.imgProxyKeyID) else null + var proxy = if (Proxy.useImageProxy) Proxy( + R.string.imgProxyApiUrl, + R.string.imgProxyApiRegex, + R.string.imgProxyKeyID + ) else null var resolution = Resolution(Regex("c\\d+x\\.")) var myGlideHeaders: LazyHeaders? = null get() { MainActivity.mainWeakReference?.get()?.let { PreferenceManager.getDefaultSharedPreferences(it).apply { - if(field === null) + if (field === null) field = LazyHeaders.Builder() - .addHeader("referer", MainActivity.mainWeakReference?.get()?.getString(R.string.referer)!!) - .addHeader("User-Agent", MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)!!) + .addHeader( + "referer", + MainActivity.mainWeakReference?.get()?.getString(R.string.referer)!! + ) + .addHeader( + "User-Agent", + MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)!! + ) .addHeader("source", "copyApp") .addHeader("webp", "1") - .addHeader("version", MainActivity.mainWeakReference?.get()?.getString(R.string.app_ver)!!) - .addHeader("region", if(!getBoolean("settings_cat_net", false)) "1" else "0") + .addHeader( + "version", + MainActivity.mainWeakReference?.get()?.getString(R.string.app_ver)!! + ) + .addHeader( + "region", + if (!getBoolean("settings_cat_net", false)) "1" else "0" + ) .addHeader("platform", "3") .build() } @@ -34,11 +47,11 @@ object CMApi { } var myHostApiUrl: String = "" get() { - if(field != "") return field + if (field != "") return field MainActivity.mainWeakReference?.get()?.let { PreferenceManager.getDefaultSharedPreferences(it).apply { getString("settings_cat_net_et_api_url", "")?.let { host -> - if(host != "") { + if (host != "") { field = host return host } @@ -49,25 +62,14 @@ object CMApi { return field } - fun getZipFile(exDir: File?, manga: String, caption: CharSequence, name: CharSequence) = File(exDir, "$manga/$caption/$name.zip") + fun getZipFile(exDir: File?, manga: String, caption: CharSequence, name: CharSequence) = + File(exDir, "$manga/$caption/$name.zip") + fun getChapterInfoApiUrl(arg1: String?, arg2: String?) = - MainActivity.mainWeakReference?.get()?.getString(R.string.chapterInfoApiUrl)?.format(myHostApiUrl, arg1, arg2) + MainActivity.mainWeakReference?.get()?.getString(R.string.chapterInfoApiUrl) + ?.format(myHostApiUrl, arg1, arg2) + fun getGroupInfoApiUrl(arg1: String?, arg2: String?, arg3: Int? = 0) = - MainActivity.mainWeakReference?.get()?.getString(R.string.groupInfoApiUrl)?.format(myHostApiUrl, arg1, arg2, arg3) - fun getLoginConnection(username: String, pwd: String, salt: Int) = - MainActivity.mainWeakReference?.get()?.getString(R.string.loginApiUrl)?.format(myHostApiUrl)?.let { - DownloadTools.getConnection(it, "POST")?.apply { - MainActivity.mainWeakReference?.get()?.let { - PreferenceManager.getDefaultSharedPreferences(it).apply { - doOutput = true - setRequestProperty("content-type", "application/x-www-form-urlencoded;charset=utf-8") - setRequestProperty("platform", "3") - setRequestProperty("accept", "application/json") - val r = if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "1" else "0" - val pwdb64 = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString() - outputStream.write("username=${URLEncoder.encode(username)}&password=$pwdb64&salt=$salt&platform=3&authorization=Token+&version=1.4.4&source=copyApp®ion=$r&webp=1".toByteArray()) - } - } - } - } - } + MainActivity.mainWeakReference?.get()?.getString(R.string.groupInfoApiUrl) + ?.format(myHostApiUrl, arg1, arg2, arg3) +} diff --git a/app/src/main/java/top/fumiama/copymanga/tools/http/DownloadPool.kt b/app/src/main/java/top/fumiama/copymanga/tools/http/DownloadPool.kt index c8eba27..0bf22a0 100644 --- a/app/src/main/java/top/fumiama/copymanga/tools/http/DownloadPool.kt +++ b/app/src/main/java/top/fumiama/copymanga/tools/http/DownloadPool.kt @@ -9,6 +9,7 @@ import java.util.zip.CRC32 import java.util.zip.CheckedOutputStream import java.util.zip.ZipEntry import java.util.zip.ZipFile +import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream import kotlin.random.Random @@ -33,15 +34,7 @@ class DownloadPool(folder: String) { } operator fun plusAssign(quest: Quest) { - packZipFile(quest.fileName, quest.imgUrl, quest.refer) - } - - operator fun plusAssign(quests: Array) { - Thread{ - quests.forEach { quest -> - packZipFile(quest.fileName, quest.imgUrl, quest.refer) - } - }.start() + packZipFile(quest.fileName, quest.imgUrl) } fun setOnDownloadListener(onDownloadListener: (String, Boolean, String) -> Unit) { @@ -52,8 +45,8 @@ class DownloadPool(folder: String) { mOnPageDownloadListener = onPageDownloadListener } - private fun packZipFile(fileName: String, imgUrls: Array, refer: String?) { - Thread{ + private fun packZipFile(fileName: String, imgUrls: Array) { + Thread { File(saveFolder, "$fileName.tmp").let { f -> f.parentFile?.let { if(!it.exists()) it.mkdirs() } var start = 0 @@ -61,9 +54,9 @@ class DownloadPool(folder: String) { if(f.exists()) { try { val zipFile = ZipFile(f) - start = zipFile.size() - 1 + start = zipFile.size() zipFile.close() - Log.d("MyDP", "last downloaded index: $start") + Log.d("MyDP", "next download index: $start") if (start <= 0 || start >= imgUrls.size) { // error or re-download f.delete() f.createNewFile() @@ -75,8 +68,25 @@ class DownloadPool(folder: String) { f.createNewFile() } } else f.createNewFile() - val zip = ZipOutputStream(CheckedOutputStream(FileOutputStream(f, true), CRC32())) - zip.setLevel(9) + val zip: ZipOutputStream + if (start > 0) { + val fromZip = ZipInputStream(f.readBytes().inputStream()) + zip = ZipOutputStream(CheckedOutputStream(FileOutputStream(f), CRC32())) + zip.setLevel(9) + fromZip.use { z -> + var e = z.nextEntry + while (e != null) { + zip.putNextEntry(e) + z.copyTo(zip) + zip.closeEntry() + z.closeEntry() + e = z.nextEntry + } + } + } else { + zip = ZipOutputStream(CheckedOutputStream(FileOutputStream(f), CRC32())) + zip.setLevel(9) + } var succeed = true var lastIndex = -8 try { @@ -93,7 +103,9 @@ class DownloadPool(folder: String) { zip.closeEntry() true }?:false + if(exit) break if (!s) sleep(2000) + if(exit) break } if(!s && tryTimes <= 0) { succeed = false diff --git a/app/src/main/java/top/fumiama/copymanga/tools/http/DownloadTools.kt b/app/src/main/java/top/fumiama/copymanga/tools/http/DownloadTools.kt index 3d7d06c..94cea10 100644 --- a/app/src/main/java/top/fumiama/copymanga/tools/http/DownloadTools.kt +++ b/app/src/main/java/top/fumiama/copymanga/tools/http/DownloadTools.kt @@ -3,7 +3,8 @@ package top.fumiama.copymanga.tools.http import android.content.Context import android.util.Log import androidx.preference.PreferenceManager -import okhttp3.RequestBody +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import top.fumiama.copymanga.MainActivity import top.fumiama.dmzj.copymanga.R import java.net.HttpURLConnection @@ -12,12 +13,12 @@ import java.util.concurrent.Callable import java.util.concurrent.FutureTask object DownloadTools { - fun getConnection(url: String?, method: String = "GET", refer: String? = null, ua: String? = null) = - url?.let { + fun getApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null, timeout: Int = 20000) = + url.let { val connection = URL(url).openConnection() as HttpURLConnection connection.requestMethod = method - connection.connectTimeout = 20000 - connection.readTimeout = 20000 + connection.connectTimeout = timeout + connection.readTimeout = timeout connection.apply { setRequestProperty("host", url.substringAfter("://").substringBefore("/")) ua?.let { setRequestProperty("user-agent", it) } @@ -37,44 +38,31 @@ object DownloadTools { } setRequestProperty("platform", "3") } - Log.d("Mydl", "getHttp: ${connection.requestProperties.map { "${it.key}: ${it.value}" }.joinToString("\n")}") + Log.d("Mydl", "getConnection: ${connection.requestProperties.map { "${it.key}: ${it.value}" }.joinToString("\n")}") connection } - private fun getNormalConnection(url: String?, method: String = "GET", ua: String? = null) = - url?.let { + private fun getNormalConnection(url: String, method: String = "GET", ua: String? = null, timeout: Int = 20000) = + url.let { val connection = URL(url).openConnection() as HttpURLConnection connection.requestMethod = method - connection.connectTimeout = 20000 - connection.readTimeout = 20000 + connection.connectTimeout = timeout + connection.readTimeout = timeout connection.apply { setRequestProperty("host", url.substringAfter("://").substringBefore("/")) ua?.let { setRequestProperty("user-agent", it) } } } - fun getHttpContent(u: String, refer: String? = null, ua: String? = null): ByteArray? { - Log.d("Mydl", "getHttp: $u") - var ret: ByteArray? = null - val task = FutureTask(Callable { - try { - getConnection(u, "GET", refer, ua)?.apply { - ret = inputStream.readBytes() - disconnect() - } - } catch (ex: Exception) { - ex.printStackTrace() + suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = null): ByteArray = + withContext(Dispatchers.IO) { + getApiConnection(u, "GET", refer, ua).let { + val ret = it.inputStream.readBytes() + it.disconnect() + Log.d("Mydl", "getHttpContent: ${ret.size} bytes") + ret } - return@Callable ret - }) - Thread(task).start() - return try { - task.get() - } catch (ex: Exception) { - ex.printStackTrace() - null } - } fun getHttpContent(u: String, readSize: Int): ByteArray? { Log.d("Mydl", "getHttp: $u") @@ -82,13 +70,13 @@ object DownloadTools { val task = FutureTask(Callable { try { val connection = getNormalConnection(u, "GET") - val ci = connection?.inputStream + val ci = connection.inputStream if(readSize > 0) { ret = ByteArray(readSize) ci?.read(ret, 0, readSize) } else ret = ci?.readBytes() ci?.close() - connection?.disconnect() + connection.disconnect() } catch (ex: Exception) { ex.printStackTrace() } @@ -103,7 +91,7 @@ object DownloadTools { } } - fun touch(url: String?): FutureTask? = + /*fun touch(url: String?): FutureTask? = url?.let { Log.d("Mydl", "touchHttp: $it") var ret: ByteArray? = null @@ -122,7 +110,7 @@ object DownloadTools { }) Thread(task).start() task - } + }*/ fun prepare(url: String?): FutureTask? = url?.let { @@ -156,7 +144,7 @@ object DownloadTools { var ret: ByteArray? = null val task = FutureTask(Callable { try { - getConnection(url, method, refer, ua)?.apply { + getApiConnection(url, method, refer, ua)?.apply { outputStream.write(body) ret = inputStream.readBytes() disconnect() diff --git a/app/src/main/java/top/fumiama/copymanga/tools/http/Proxy.kt b/app/src/main/java/top/fumiama/copymanga/tools/http/Proxy.kt index 9b80b76..ea0d5d8 100644 --- a/app/src/main/java/top/fumiama/copymanga/tools/http/Proxy.kt +++ b/app/src/main/java/top/fumiama/copymanga/tools/http/Proxy.kt @@ -6,21 +6,28 @@ import top.fumiama.copymanga.MainActivity import java.net.URLEncoder import java.nio.charset.Charset -class Proxy(id: Int, apiPrefixID: Int, keyID: Int? = null) { +class Proxy(id: Int, apiRegexID: Int, keyID: Int? = null) { private val code = keyID?.let { k -> MainActivity.mainWeakReference?.get()?.let { PreferenceManager.getDefaultSharedPreferences(it).getString(it.getString(k), null) } } private val proxyApiUrl = MainActivity.mainWeakReference?.get()?.getString(id) - private val apiPrefix = MainActivity.mainWeakReference?.get()?.getString(apiPrefixID)?:"" + private val apiRegex = Regex(MainActivity.mainWeakReference?.get()?.getString(apiRegexID)?:"") fun wrap(u: String): String { - if(!u.startsWith(apiPrefix)) return u - if(code != null) { - return proxyApiUrl?.format(code, URLEncoder.encode(u, Charset.defaultCharset().name()))?:u + if(!apiRegex.matches(u)) { + Log.d("MyP", "[N] wrap: $u") + return u } - return proxyApiUrl?.format(URLEncoder.encode(u, Charset.defaultCharset().name()))?:u + if(!code.isNullOrEmpty()) { + val wu = proxyApiUrl?.format(code, URLEncoder.encode(u, Charset.defaultCharset().name()))?:u + Log.d("MyP", "[M] wrap: $wu") + return wu + } + Log.d("MyP", "[C] wrap: $u") + //return proxyApiUrl?.format(URLEncoder.encode(u, Charset.defaultCharset().name()))?:u + return u } companion object { diff --git a/app/src/main/java/top/fumiama/copymanga/tools/thread/TimeThread.kt b/app/src/main/java/top/fumiama/copymanga/tools/thread/TimeThread.kt index 5d29f1b..ac755a8 100644 --- a/app/src/main/java/top/fumiama/copymanga/tools/thread/TimeThread.kt +++ b/app/src/main/java/top/fumiama/copymanga/tools/thread/TimeThread.kt @@ -2,13 +2,13 @@ package top.fumiama.copymanga.tools.thread import android.os.Handler -class TimeThread(private val handler: Handler, private val msg: Int) : Thread() { +class TimeThread(private val handler: Handler, private val msg: Int, private val interval: Long = 3000) : Thread() { var canDo = false override fun run() { while (canDo) { try { handler.sendEmptyMessage(msg) - sleep(3000) + sleep(interval) } catch (e: InterruptedException) { e.printStackTrace() } diff --git a/app/src/main/java/top/fumiama/copymanga/tools/ui/UITools.kt b/app/src/main/java/top/fumiama/copymanga/tools/ui/UITools.kt index 4d9eec4..d0b57c0 100644 --- a/app/src/main/java/top/fumiama/copymanga/tools/ui/UITools.kt +++ b/app/src/main/java/top/fumiama/copymanga/tools/ui/UITools.kt @@ -8,6 +8,8 @@ import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.view.View import android.widget.Toast +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import top.fumiama.dmzj.copymanga.R import java.lang.ref.WeakReference import kotlin.math.sqrt @@ -39,11 +41,11 @@ class UITools(that: Context?, w: WeakReference? = null) { } } ?: transportStringError } - fun toastError(s: String, willFinish: Boolean = true) { + suspend fun toastError(s: String, willFinish: Boolean = true) = withContext(Dispatchers.Main) { Toast.makeText(zis, s, Toast.LENGTH_SHORT).show() if (willFinish) weak?.get()?.finish() } - fun toastError(s: Int, willFinish: Boolean = true) { + suspend fun toastError(s: Int, willFinish: Boolean = true) = withContext(Dispatchers.Main) { Toast.makeText(zis, s, Toast.LENGTH_SHORT).show() if (willFinish) weak?.get()?.finish() } diff --git a/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt index b73fa00..3c6060c 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt @@ -27,6 +27,7 @@ import top.fumiama.dmzj.copymanga.R import java.io.File import java.lang.Thread.sleep import java.lang.ref.WeakReference +import java.util.concurrent.atomic.AtomicReference class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { var isOnPause = false @@ -65,7 +66,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { } mBookHandler = BookHandler(WeakReference(this), path) Log.d("MyBF", "read path: $path") - bookHandler = mBookHandler + bookHandler.set(mBookHandler) lifecycleScope.launch { withContext(Dispatchers.IO) { sleep(600) @@ -73,14 +74,14 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { } } } else { - bookHandler = mBookHandler + bookHandler.set(mBookHandler) } } override fun onResume() { super.onResume() isOnPause = false - bookHandler = mBookHandler + bookHandler.set(mBookHandler) activity?.apply { toolbar.title = mBookHandler?.book?.results?.comic?.name } @@ -98,7 +99,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { mBookHandler?.ads?.forEach { it.exit = true } - bookHandler = null + bookHandler.set(null) } fun setStartRead() { @@ -126,12 +127,14 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { fun setAddToShelf() { if(mBookHandler?.chapterNames?.isNotEmpty() != true) return lifecycleScope.launch { - val b = MainActivity.shelf?.query(mBookHandler?.path!!) - mBookHandler?.collect = b?.results?.collect?:-2 - Log.d("MyBF", "get collect of ${mBookHandler?.path} = ${mBookHandler?.collect}") - tic.text = b?.results?.browse?.chapter_name?.let { name -> - getString(R.string.text_format_cloud_read_to).format(name) + MainActivity.shelf?.query(mBookHandler?.path!!)?.let { b -> + mBookHandler?.collect = b.results?.collect?:-2 + Log.d("MyBF", "get collect of ${mBookHandler?.path} = ${mBookHandler?.collect}") + tic.text = b.results?.browse?.chapter_name?.let { name -> + getString(R.string.text_format_cloud_read_to).format(name) + } } + mBookHandler?.collect?.let { collect -> if (collect > 0) { this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed) @@ -162,12 +165,13 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { } } - fun navigate2dl(){ + fun navigate2dl() { val bundle = Bundle() + Log.d("MyBF", "nav2: ${arguments?.getString("path")?:"null"}") bundle.putString("path", arguments?.getString("path")?:"null") bundle.putString("name", mBookHandler!!.book?.results?.comic?.name) - if(mBookHandler!!.vols != null) { - bundle.putBoolean("loadJson", true) + if(mBookHandler!!.vols != null && mBookHandler!!.json != null) { + bundle.putString("loadJson", mBookHandler!!.json) } bundle.putStringArray("group", mBookHandler!!.gpws) bundle.putStringArray("groupNames", mBookHandler!!.keys) @@ -178,6 +182,6 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { } companion object { - var bookHandler: BookHandler? = null + var bookHandler: AtomicReference = AtomicReference(null) } -} \ No newline at end of file +} diff --git a/app/src/main/java/top/fumiama/copymanga/ui/book/BookHandler.kt b/app/src/main/java/top/fumiama/copymanga/ui/book/BookHandler.kt index 5317835..f7e5dbf 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/book/BookHandler.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/book/BookHandler.kt @@ -2,7 +2,6 @@ package top.fumiama.copymanga.ui.book import android.graphics.Bitmap import android.os.Bundle -import android.os.Looper import android.os.Message import android.util.Log import android.view.View @@ -12,6 +11,7 @@ import android.widget.TextView import android.widget.Toast import androidx.core.graphics.drawable.toBitmap import androidx.core.widget.NestedScrollView +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide @@ -28,6 +28,9 @@ import kotlinx.android.synthetic.main.line_bookinfo_text.* import kotlinx.android.synthetic.main.line_caption.view.* import kotlinx.android.synthetic.main.line_chapter.view.* import kotlinx.android.synthetic.main.page_nested_list.view.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference import top.fumiama.copymanga.json.BookInfoStructure import top.fumiama.copymanga.json.ChapterStructure @@ -35,7 +38,7 @@ import top.fumiama.copymanga.json.ThemeStructure import top.fumiama.copymanga.json.VolumeStructure import top.fumiama.copymanga.manga.Reader import top.fumiama.copymanga.template.http.AutoDownloadHandler -import top.fumiama.copymanga.template.http.AutoDownloadThread +import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.copymanga.tools.ui.GlideBlurTransformation import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener @@ -51,7 +54,7 @@ import java.lang.ref.WeakReference class BookHandler(private val th: WeakReference, val path: String) : AutoDownloadHandler(th.get()?.getString(R.string.bookInfoApiUrl)?.format(CMApi.myHostApiUrl, path)?: "", BookInfoStructure::class.java, - Looper.myLooper()!!){ + th.get()){ private val that get() = th.get() private var hasToastedError = false get(){ @@ -61,13 +64,14 @@ class BookHandler(private val th: WeakReference, val path: String) } var book: BookInfoStructure? = null private var complete = false - var ads = emptyArray() + var ads = emptyArray() var gpws = arrayOf() var keys = arrayOf() var cnts = intArrayOf() var vols: Array? = null var chapterNames = arrayOf() var collect: Int = -1 + var json: String? = null private val divider get() = that?.layoutInflater?.inflate(R.layout.div_h, that?.lbl, false) var urlArray = arrayOf() @@ -87,7 +91,7 @@ class BookHandler(private val th: WeakReference, val path: String) override fun onError() { super.onError() if(exit) return - if(!hasToastedError) that?.activity?.runOnUiThread { + if(!hasToastedError) /*that?.activity?.runOnUiThread*/ { Toast.makeText(that?.context, R.string.null_book, Toast.LENGTH_SHORT).show() that?.apply { findNavController().popBackStack() } } @@ -100,10 +104,11 @@ class BookHandler(private val th: WeakReference, val path: String) } override fun getGsonItem() = book - override fun doWhenFinishDownload() { + override suspend fun doWhenFinishDownload() = withContext(Dispatchers.IO) { super.doWhenFinishDownload() - if(exit) return - if(keys.isEmpty()) book?.results?.groups?.values?.forEach{ + if(exit) return@withContext + + if(keys.isEmpty()) book?.results?.groups?.values?.forEach { keys += it.name gpws += it.path_word if (it.count == 0) { @@ -245,14 +250,12 @@ class BookHandler(private val th: WeakReference, val path: String) } } - private fun addVolumesView(l: LinearLayout, v: View) { - that?.activity?.runOnUiThread { - l.addView(v) - } + private suspend fun addVolumesView(l: LinearLayout, v: View) = withContext(Dispatchers.Main) { + l.addView(v) } - private fun setVolume(fbl: LinearLayout, p: Int) = Thread { - if (exit) return@Thread + private suspend fun setVolume(fbl: LinearLayout, p: Int) = withContext(Dispatchers.IO) { + if (exit) return@withContext that?.apply { book?.results?.apply { var i = 0 @@ -261,16 +264,16 @@ class BookHandler(private val th: WeakReference, val path: String) } var last = i-1 vols?.get(p)?.let { v -> - if(exit) return@Thread + if(exit) return@withContext var line: View? = null last += v.results.list.size v.results.list.forEach { val f = CMApi.getZipFile(context?.getExternalFilesDir(""), comic.name, keys[p], it.name) - Log.d("MyBH", "i = $i, last=$last, add chapter ${it.name}, line is null: ${line == null}") + //Log.d("MyBH", "i = $i, last=$last, add chapter ${it.name}, line is null: ${line == null}") that?.isOnPause?.let { isOnPause -> - while (isOnPause && !exit) sleep(1000) - if (exit) return@Thread - }?:return@Thread + while (isOnPause && !exit) sleep(100) + if (exit) return@withContext + }?:return@withContext if(line == null) { if(i == last) { line = layoutInflater.inflate(R.layout.line_chapter, fbl, false) @@ -304,10 +307,10 @@ class BookHandler(private val th: WeakReference, val path: String) } } } - }.start() + } - private fun setViewManga() = Thread { - if (exit) return@Thread + private suspend fun setViewManga() = withContext(Dispatchers.IO) { + if (exit) return@withContext that?.apply { book?.results?.apply { ViewMangaActivity.fileArray = arrayOf() @@ -316,7 +319,7 @@ class BookHandler(private val th: WeakReference, val path: String) var i = 0 var last = -1 vols?.forEachIndexed { groupIndex, v -> - if(exit) return@Thread + if(exit) return@withContext last += v.results.list.size v.results.list.forEach { urlArray += CMApi.getChapterInfoApiUrl( @@ -328,16 +331,16 @@ class BookHandler(private val th: WeakReference, val path: String) chapterNames += it.name ViewMangaActivity.uuidArray += it.uuid that?.isOnPause?.let { isOnPause -> - while (isOnPause && !exit) sleep(1000) - if (exit) return@Thread - }?:return@Thread + while (isOnPause && !exit) sleep(100) + if (exit) return@withContext + }?:return@withContext i++ } } } } sendEmptyMessage(9) // end set layout - }.start() + } private fun loadVolume(name: String, path: String, nav: Int){ if(complete) { @@ -349,7 +352,7 @@ class BookHandler(private val th: WeakReference, val path: String) } } - private fun initComicData() = Thread { + private suspend fun initComicData() = withContext(Dispatchers.IO) withIO@ { var volumes = emptyArray() val counts = cnts.clone() gpws.forEachIndexed { i, gpw -> @@ -358,49 +361,58 @@ class BookHandler(private val th: WeakReference, val path: String) val times = counts[i] / 100 val remain = counts[i] % 100 val re = arrayOfNulls(if(remain != 0) (times+1) else (times)) - if (re.isEmpty()) that?.activity?.runOnUiThread { - Toast.makeText(that?.context, "获取${gpw}失败", Toast.LENGTH_SHORT).show() - return@runOnUiThread + if (re.isEmpty()) { + withContext(Dispatchers.Main) { + Toast.makeText(that?.context, "获取${gpw}失败", Toast.LENGTH_SHORT).show() + } + return@forEachIndexed } Log.d("MyBFH", "${i}卷共${if(times == 0) 1 else times}次加载") do { counts[i] = counts[i] - 100 CMApi.getGroupInfoApiUrl(path, gpw, offset)?.let { Log.d("MyBFH", "get api: $it") - if(ComicDlFragment.exit) return@Thread - val ad = AutoDownloadThread(it) { result -> - val r = Gson().fromJson(result?.decodeToString(), VolumeStructure::class.java) - re[r.results.offset / 100] = r - Log.d("MyBFH", "第${i}卷返回, 大小: ${r.results.list.size}") - } - ads += ad - ad.start() - offset += 100 - sleep(1000) - } - } while (counts[i] > 0) - var c = 0 - while (c++ < 80) { - sleep(1000) - if(ComicDlFragment.exit) return@Thread - if(re.all { it != null }) break - } - if(re.isNotEmpty()) { - val r = re[0] - var s = emptyArray() - re.forEach { - it?.results?.list?.forEach { - s += it + if(ComicDlFragment.exit) return@withIO + val ad = PausableDownloader(it) { result -> + try { + val r = Gson().fromJson(result.decodeToString(), VolumeStructure::class.java) + re[r.results.offset / 100] = r + Log.d("MyBFH", "第${i}卷返回, 大小: ${r.results.list.size}") + } catch (e: Exception) { + e.printStackTrace() + that?.findNavController()?.popBackStack() } } - r?.results?.list = s - r?.apply { volumes += this } - } else re[0]?.apply { volumes += this } + ads += ad + ad.run() + offset += 100 + sleep(100) + } + } while (counts[i] > 0) + + var c = 0 + while (c++ < 80) { + sleep(100) + if(ComicDlFragment.exit) return@withIO + if(re.all { it != null }) break + } + if(re.isNotEmpty()) { + val r = re[0] + var s = emptyArray() + re.forEach { v -> + v?.results?.list?.forEach { + s += it + } + } + r?.results?.list = s + r?.apply { volumes += this } + } else re[0]?.apply { volumes += this } } + var c = 0 while (c < 80 && volumes.size != gpws.size) { - sleep(1000) - if(ComicDlFragment.exit) return@Thread + sleep(100) + if(ComicDlFragment.exit) return@withIO Log.d("MyBFH", "已有:${volumes.size} 共:${gpws.size}") c++ } @@ -408,7 +420,7 @@ class BookHandler(private val th: WeakReference, val path: String) saveVolumes(volumes) that?.fbtab?.let { tab -> that?.fbvp?.let { vp -> - that?.activity?.runOnUiThread { + withContext(Dispatchers.Main) { vp.adapter = ViewData(vp).RecyclerViewAdapter() TabLayoutMediator(tab, vp) { t, p -> t.text = keys[p] @@ -418,9 +430,9 @@ class BookHandler(private val th: WeakReference, val path: String) } setViewManga() } - }.start() + } - private fun saveVolumes(volumes: Array) { + private suspend fun saveVolumes(volumes: Array) = withContext(Dispatchers.IO) { that?.context?.getExternalFilesDir("")?.let { home -> book?.results?.comic?.name?.let { name -> val mangaFolder = File(home, name) @@ -429,27 +441,25 @@ class BookHandler(private val th: WeakReference, val path: String) File(mangaFolder, "info.json").writeText(json!!) File(mangaFolder, "grps.json").writeText(Gson().toJson(keys)) that?.apply { - Thread { - var cnt = 0 - var success = false - while (cnt++ < 10 && !success) { - sleep(1000) - if (exit) return@Thread - File(mangaFolder, "head.jpg").let { head -> - val fo = head.outputStream() - try { - imic.drawable.toBitmap().compress(Bitmap.CompressFormat.JPEG, 90, fo) - success = true - } catch (e: Exception) { - e.printStackTrace() - } - fo.close() + var cnt = 0 + var success = false + while (cnt++ < 10 && !success) { + sleep(100) + if (exit) return@withContext + File(mangaFolder, "head.jpg").let { head -> + val fo = head.outputStream() + try { + imic.drawable.toBitmap().compress(Bitmap.CompressFormat.JPEG, 90, fo) + success = true + } catch (e: Exception) { + e.printStackTrace() } + fo.close() } - if (!success) that?.activity?.runOnUiThread { - Toast.makeText(that?.context, R.string.download_cover_timeout, Toast.LENGTH_SHORT).show() - } - }.start() + } + if (!success) withContext(Dispatchers.Main) { + Toast.makeText(that?.context, R.string.download_cover_timeout, Toast.LENGTH_SHORT).show() + } } } } @@ -463,7 +473,7 @@ class BookHandler(private val th: WeakReference, val path: String) } override fun onBindViewHolder(holder: ViewData, position: Int) { - setVolume(holder.itemView.fbl, position) + that?.lifecycleScope?.launch { setVolume(holder.itemView.fbl, position) } } override fun getItemCount(): Int = keys.size diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt index 426346c..8e61dc6 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt @@ -2,12 +2,17 @@ package top.fumiama.copymanga.ui.cardflow.sort import android.os.Bundle import android.view.View +import androidx.lifecycle.lifecycleScope import com.github.zawadz88.materialpopupmenu.popupMenu import com.google.gson.Gson import kotlinx.android.synthetic.main.anchor_popular.view.* import kotlinx.android.synthetic.main.line_sort.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import top.fumiama.copymanga.json.FilterStructure -import top.fumiama.copymanga.template.http.AutoDownloadThread +import top.fumiama.copymanga.json.ThemeStructure +import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.template.ui.StatusCardFlow import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.dmzj.copymanga.R @@ -36,102 +41,69 @@ class SortFragment : StatusCardFlow(0, R.id.action_nav_sort_to_nav_book, R.layou override fun setListeners() { super.setListeners() - AutoDownloadThread(getString(R.string.filterApiUrl).format(CMApi.myHostApiUrl)) { - if(ad?.exit == true) return@AutoDownloadThread - it?.let { - filter = Gson().fromJson(it.inputStream().reader(), FilterStructure::class.java) - if(ad?.exit == true) return@AutoDownloadThread - activity?.runOnUiThread{ - if(ad?.exit != true) setClasses() + lifecycleScope.launch { + PausableDownloader(getString(R.string.filterApiUrl).format(CMApi.myHostApiUrl)) { + if(ad?.exit == true) return@PausableDownloader + it.let { + filter = Gson().fromJson(it.inputStream().reader(), FilterStructure::class.java) + if(ad?.exit == true) return@PausableDownloader + withContext(Dispatchers.Main) { + if(ad?.exit != true) setClasses() + } } - } - }.start() + }.run() + } } - private fun setClasses(){ + private fun setClasses() { filter?.results?.top?.let { items -> - if(ad?.exit == true) return@let - line_sort_region.apt.text = "全部" - line_sort_region.setOnClickListener { - val popupMenu = popupMenu { - style = R.style.Widget_MPM_Menu_Dark_CustomBackground - section { - item { - label = "全部" - labelColor = it.apt.currentTextColor - callback = { - region = -1 - it.apt.text = "全部" - Thread{ - sleep(400) - activity?.runOnUiThread { - reset() - addPage() - } - }.start() - } - } - for(i in items.indices) item { - label = items[i].name - labelColor = it.apt.currentTextColor - callback = { //optional - it.apt.text = label - region = i - Thread{ - sleep(400) - activity?.runOnUiThread { - reset() - addPage() - } - }.start() - } - } - } - } - this.context?.let { it1 -> popupMenu.show(it1, it) } - } + setMenu(items, line_sort_region) } filter?.results?.theme?.let { items -> - if(ad?.exit == true) return@let - line_sort_class.apt.text = "全部" - line_sort_class.setOnClickListener { - val popupMenu = popupMenu { - style = R.style.Widget_MPM_Menu_Dark_CustomBackground - section { - item { - label = "全部" - labelColor = it.apt.currentTextColor - callback = { - theme = -1 - it.apt.text = "全部" - Thread{ - sleep(400) - activity?.runOnUiThread { - reset() - addPage() - } - }.start() - } - } - for(i in items.indices) item { - label = items[i].name - labelColor = it.apt.currentTextColor - callback = { //optional - it.apt.text = label - theme = i - Thread{ - sleep(400) - activity?.runOnUiThread { - reset() - addPage() - } - }.start() - } - } - } + setMenu(items, line_sort_class) + } + } + + private fun suspendReset() { + lifecycleScope.launch { + withContext(Dispatchers.IO) { + sleep(400) + withContext(Dispatchers.Main) { + reset() + addPage() } - this.context?.let { it1 -> popupMenu.show(it1, it) } } } } -} \ No newline at end of file + + private fun setMenu(items: Array, line: View) { + if(ad?.exit == true) return + line.apt.text = "全部" + line.setOnClickListener { + val popupMenu = popupMenu { + style = R.style.Widget_MPM_Menu_Dark_CustomBackground + section { + item { + label = "全部" + labelColor = it.apt.currentTextColor + callback = { + region = -1 + it.apt.text = "全部" + suspendReset() + } + } + for(i in items.indices) item { + label = items[i].name + labelColor = it.apt.currentTextColor + callback = { //optional + it.apt.text = label + region = i + suspendReset() + } + } + } + } + context?.let { c -> popupMenu.show(c, it) } + } + } +} diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt index 0e5bcad..3b24688 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt @@ -1,11 +1,15 @@ package top.fumiama.copymanga.ui.cardflow.topic import android.os.Bundle +import androidx.lifecycle.lifecycleScope import com.google.gson.Gson import kotlinx.android.synthetic.main.app_bar_main.* import kotlinx.android.synthetic.main.fragment_topic.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import top.fumiama.copymanga.json.TopicStructure -import top.fumiama.copymanga.template.http.AutoDownloadThread +import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.template.ui.InfoCardLoader import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.dmzj.copymanga.R @@ -18,23 +22,27 @@ class TopicFragment : InfoCardLoader(R.layout.fragment_topic, R.id.action_nav_to override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - AutoDownloadThread(getString(R.string.topicApiUrl).format(CMApi.myHostApiUrl, arguments?.getString("path"))) { data -> - if(ad?.exit == true) return@AutoDownloadThread - data?.apply { - val r = inputStream().reader() - Gson().fromJson(r, TopicStructure::class.java)?.apply { - if(ad?.exit == true) return@AutoDownloadThread - activity?.let { - it.runOnUiThread { - if(ad?.exit == true) return@runOnUiThread - it.toolbar.title = results.title - ftttime.text = results.datetime_created - fttintro.text = results.intro - type = results.type + lifecycleScope.launch { + PausableDownloader(getString(R.string.topicApiUrl).format(CMApi.myHostApiUrl, arguments?.getString("path"))) { data -> + withContext(Dispatchers.IO) { + if(ad?.exit == true) return@withContext + data.apply { + val r = inputStream().reader() + Gson().fromJson(r, TopicStructure::class.java)?.apply { + if(ad?.exit == true) return@withContext + activity?.let { + withContext(Dispatchers.Main) withMain@ { + if(ad?.exit == true) return@withMain + it.toolbar.title = results.title + ftttime.text = results.datetime_created + fttintro.text = results.intro + type = results.type + } + } } } } - } - }.start() + }.run() + } } -} \ No newline at end of file +} diff --git a/app/src/main/java/top/fumiama/copymanga/ui/comicdl/ComicDlFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/comicdl/ComicDlFragment.kt index a2fff93..448e092 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/comicdl/ComicDlFragment.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/comicdl/ComicDlFragment.kt @@ -4,11 +4,17 @@ import android.os.Bundle import android.os.Looper import android.util.Log import android.view.View +import android.widget.Toast +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController import com.google.gson.Gson import kotlinx.android.synthetic.main.fragment_dlcomic.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import top.fumiama.copymanga.json.ChapterStructure import top.fumiama.copymanga.json.VolumeStructure -import top.fumiama.copymanga.template.http.AutoDownloadThread +import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.template.general.NoBackRefreshFragment import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.dmzj.copymanga.R @@ -18,29 +24,31 @@ import java.lang.ref.WeakReference class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) { var ltbtn: View? = null - var ads = emptyArray() + private var ads = emptyArray() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) exit = false ldwn?.setPadding(0, 0, 0, navBarHeight) - if(isFirstInflate){ - when { - arguments?.getBoolean("callFromOldDL", false) == true -> initOldComicData() - arguments?.getBoolean("loadJson", false) == true -> context?.getExternalFilesDir("")?.let { home -> - arguments?.getString("name")?.let { - Thread{ + if(isFirstInflate) lifecycleScope.launch { + withContext(Dispatchers.IO) { + when { + arguments?.getBoolean("callFromOldDL", false) == true -> initOldComicData() + arguments?.containsKey("loadJson") == true -> context?.getExternalFilesDir("")?.let { home -> + arguments?.getString("name")?.let { sleep(600) - activity?.runOnUiThread { - start2load(loadFromJson(), true, loadGroupsFromFile(File(home, "$it/grps.json"))) - } - }.start() + Log.d("MyCDF", "loadJson by arguments") + start2load( + loadFromJson(arguments?.getString("loadJson")!!), + true, loadGroupsFromFile(File(home, "$it/grps.json")) + ) + } } + else -> initComicData( + arguments?.getString("path"), + arguments?.getStringArray("group"), + arguments?.getIntArray("count") + ) } - else -> initComicData( - arguments?.getString("path"), - arguments?.getStringArray("group"), - arguments?.getIntArray("count") - ) } } } @@ -58,13 +66,14 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) { handler = null } - private fun start2load(volumes: Array, isFromFile: Boolean = false, groupArray: Array? =null){ - handler = ComicDlHandler(Looper.myLooper()!!, - WeakReference(this), - volumes, - arguments?.getString("name")?:"null", - if(isFromFile) groupArray else arguments?.getStringArray("groupNames")) - if(!isFromFile) Thread{ + private suspend fun start2load(volumes: Array, isFromFile: Boolean = false, groupArray: Array? =null) = withContext(Dispatchers.IO) { + withContext(Dispatchers.Main) { + handler = ComicDlHandler(Looper.myLooper()!!, WeakReference(this@ComicDlFragment), + volumes, arguments?.getString("name")?:"null", + if(isFromFile) groupArray else arguments?.getStringArray("groupNames") + ) + } + if(!isFromFile) { context?.getExternalFilesDir("")?.let { home -> arguments?.getString("name")?.let { name -> val mangaFolder = File(home, name) @@ -75,18 +84,18 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) { } } } - }.start() + } handler?.startLoad() } - private fun loadFromJson() = Gson().fromJson(json, Array::class.java) + private fun loadFromJson(json: String) = Gson().fromJson(json, Array::class.java) private fun loadGroupsFromFile(file: File) = Gson().fromJson(file.reader(), Array::class.java) /*private fun setMenuInvisible(menu: Menu){ menu.findItem(R.id.action_download)?.isVisible = false }*/ - private fun initComicData(pw: String?, gpws: Array?, counts: IntArray?) = Thread { + private suspend fun initComicData(pw: String?, gpws: Array?, counts: IntArray?) = withContext(Dispatchers.IO) { var volumes = emptyArray() if (gpws != null) { gpws.forEachIndexed { i, gpw -> @@ -99,21 +108,29 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) { do { counts?.set(i, counts[i] - 100) CMApi.getGroupInfoApiUrl(pw, gpw, offset)?.let { - if(exit) return@Thread - val ad = AutoDownloadThread(it) { result -> + if(exit) return@withContext + val ad = PausableDownloader(it) { result -> Log.d("MyCDF", "第${i}卷返回") - val r = Gson().fromJson(result?.decodeToString(), VolumeStructure::class.java) + val r = Gson().fromJson(result.decodeToString(), VolumeStructure::class.java) re[r.results.offset / 100] = r } ads += ad - ad.start() + try { + ad.run() + } catch (e: Exception) { + e.printStackTrace() + withContext(Dispatchers.Main) { + Toast.makeText(context, "加载${gpw}第${i}部分失败", Toast.LENGTH_SHORT).show() + findNavController().popBackStack() + } + } offset += 100 } } while ((counts?.get(i) ?: 0) > 0) var c = 0 while (c++ < 80) { sleep(1000) - if(exit) return@Thread + if(exit) return@withContext if(re.all { it != null }) break } if(re.size > 1) { @@ -131,21 +148,18 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) { var c = 0 while (c < 80 && volumes.size != gpws.size) { sleep(1000) - if(exit) return@Thread + if(exit) return@withContext Log.d("MyCDF", "已有:${volumes.size} 共:${gpws.size}") c++ } if (volumes.size == gpws.size) { - activity?.runOnUiThread { - start2load(volumes) - } + start2load(volumes) } } - }.start() + } - private fun initOldComicData() { - handler = ComicDlHandler(Looper.myLooper()!!, - WeakReference(this), + private suspend fun initOldComicData() = withContext(Dispatchers.IO) { + handler = ComicDlHandler(Looper.myLooper()!!, WeakReference(this@ComicDlFragment), arguments?.getString("name")?:"null") handler?.startLoad() } diff --git a/app/src/main/java/top/fumiama/copymanga/ui/comicdl/ComicDlHandler.kt b/app/src/main/java/top/fumiama/copymanga/ui/comicdl/ComicDlHandler.kt index 6305acf..353385d 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/comicdl/ComicDlHandler.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/comicdl/ComicDlHandler.kt @@ -11,6 +11,7 @@ import android.util.Log import android.view.ViewGroup import android.view.ViewTreeObserver import android.widget.Toast +import androidx.lifecycle.lifecycleScope import com.google.gson.Gson import kotlinx.android.synthetic.main.fragment_book.* import kotlinx.android.synthetic.main.line_chapter.view.* @@ -20,8 +21,12 @@ import kotlinx.android.synthetic.main.line_horizonal_empty.view.* import kotlinx.android.synthetic.main.button_tbutton.* import kotlinx.android.synthetic.main.button_tbutton.view.* import kotlinx.android.synthetic.main.line_caption.view.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import top.fumiama.dmzj.copymanga.R import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference +import top.fumiama.copymanga.json.ChapterStructure import top.fumiama.copymanga.json.ComicStructureOld import top.fumiama.copymanga.json.VolumeStructure import top.fumiama.copymanga.tools.api.CMApi @@ -92,41 +97,35 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference that?.tdwn?.text = "${dldChapter}/${checkedChapter}" - 7 -> deleteChapters(msg.obj as File, msg.arg1) + 7 -> that?.lifecycleScope?.launch { deleteChapters(msg.obj as File, msg.arg1) } 9 -> that?.cdwn?.setCardBackgroundColor(that!!.resources.getColor(R.color.colorGreen)) - 10 -> addTbtn(msg.obj as Array) - 11 -> addCaption(msg.obj as String) - 12 -> addDiv() + //10 -> addButton(msg.obj as Array) + //11 -> addCaption(msg.obj as String) + //12 -> addDiv() 13 -> if(complete) showMultiSelectInfo() } } - fun startLoad(){ + suspend fun startLoad() = withContext(Dispatchers.IO) { setComponents() if(isOld) analyzeOldStructure() - else Thread{ + else { urlArray = arrayOf() ViewMangaActivity.fileArray = arrayOf() ViewMangaActivity.uuidArray = arrayOf() vols.forEachIndexed { i, vol -> val caption = groupNames?.get(i)?:vol.results.list[0].group_path_word Log.d("MyCDH", "caption: $caption, group name: ${groupNames?.get(i)}") - obtainMessage(11, caption).sendToTarget() //addCaption - vol.results.list.forEach { chapter -> - var data = arrayOf() - data += chapter.name - data += chapter.uuid - data += caption - data += CMApi.getChapterInfoApiUrl(chapter.comic_path_word, chapter.uuid)?:"" - obtainMessage(10, data).sendToTarget() + withContext(Dispatchers.Main) { + addCaption(caption) { + addButtons(vol.results.list, caption) + } } - sendEmptyMessage(12) //addDiv } complete = true - }.start() - + } } - private fun addDiv(){ + private suspend fun addDiv() = withContext(Dispatchers.Main) { that?.ldwn?.addView( that!!.layoutInflater.inflate(R.layout.div_h, that!!.ldwn, false), ViewGroup.LayoutParams( @@ -135,7 +134,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference Unit) = withContext(Dispatchers.Main) { val tc = that?.layoutInflater?.inflate(R.layout.line_caption, that!!.ldwn, false) tc?.tcptn?.text = title that?.ldwn?.addView( @@ -147,13 +146,16 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference if(i.isChecked) { + withContext(Dispatchers.Main) { + i.isEnabled = false + } i.url?.let { - mangaDlTools.downloadChapterInVol( - it, - i.chapterName, - i.caption?:"null", - i.index - ) + Thread { + that?.lifecycleScope?.launch { + mangaDlTools.downloadChapterInVol( + it, + i.chapterName, + i.caption?:"null", + i.index + ) + } + }.start() } } } } - private fun onZipDownloadFinish(index: Int) { + private suspend fun onZipDownloadFinish(index: Int) = withContext(Dispatchers.Main) { if(index >= 0 && index < tbtnlist.size) { tbtnlist[index].setBackgroundResource(R.drawable.rndbg_checked) tbtnlist[index].isChecked = false + tbtnlist[index].isEnabled = true finishMap[index] = true updateProgressBar() that?.apply { cdwn.postDelayed({ - if (mangaDlTools?.exit != false) return@postDelayed + if (mangaDlTools.exit) return@postDelayed if (dldChapter == checkedChapter) { checkedChapter = 0 setProgress2(0, 233) @@ -299,8 +307,9 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference){ - addTbtn(data[0], data[1], data[2], data[3]) - urlArray += data[3] + private suspend fun addButtons(chapters: Array, caption: String) = withContext(Dispatchers.IO) { + chapters.forEach { chapter -> + val u = CMApi.getChapterInfoApiUrl(chapter.comic_path_word, chapter.uuid)?:"" + addButton(chapter.name, chapter.uuid, caption, u) + urlArray += u + } + addDiv() } @SuppressLint("SetTextI18n") - private fun addTbtn(title: String, uuid: String, caption: String, url: String) { + private suspend fun addButton(title: String, uuid: String, caption: String, url: String) = withContext(Dispatchers.Main) { if ((tbtncnt % btnNumPerRow == 0) || isNewTitle) { that?.ltbtn = that?.layoutInflater?.inflate(R.layout.line_horizonal_empty, that!!.ldwn, false) that?.ldwn?.addView(that!!.ltbtn) @@ -338,82 +351,86 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference::class.java)?.let { for (group in it) { that?.layoutInflater?.inflate(R.layout.line_caption, that!!.ldwn, false)?.let { tc -> tc.tcptn.text = group.name - that!!.ldwn.addView( - tc, - ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT + withContext(Dispatchers.Main) { + that!!.ldwn.addView( + tc, + ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) ) - ) - that!!.ldwn.addView( - that!!.layoutInflater.inflate(R.layout.div_h, that!!.ldwn, false), - ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT + that!!.ldwn.addView( + that!!.layoutInflater.inflate(R.layout.div_h, that!!.ldwn, false), + ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) ) - ) + } isNewTitle = true for (chapter in group.chapters) { val newUrl = CMApi.getChapterInfoApiUrl( @@ -421,10 +438,11 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference + if (l.code != 200 && l.code != 449) { + Toast.makeText(context, l.message, Toast.LENGTH_SHORT).show() + logout() + } } - MainActivity.member?.logout() - } - }.start() + } } homeHandler = HomeHandler(WeakReference(this)) val theme = resources.newTheme() @@ -92,12 +96,14 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) { override fun onQueryTextChange(newText: CharSequence): Boolean { if (newText.contentEquals("__notice_focus_change__") || newText.contentEquals(lastSearch)) return true postDelayed({ - val diff = System.currentTimeMillis() - lastChangeTime - if(diff > 500) { - if (newText.isNotEmpty()) { - Log.d("MyHF", "new text: $newText") - lastSearch = newText.toString() - adapter.refresh(newText) + lifecycleScope.launch { + val diff = System.currentTimeMillis() - lastChangeTime + if(diff > 500) { + if (newText.isNotEmpty()) { + Log.d("MyHF", "new text: $newText") + lastSearch = newText.toString() + adapter.refresh(newText) + } } } }, 1024) @@ -154,14 +160,16 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) { } } - Thread{ - homeHandler.obtainMessage(-1, true).sendToTarget() - while(!MainActivity.isDrawerClosed) sleep(233) - //homeHandler.sendEmptyMessage(6) //removeAllViews - homeHandler.fhib = null - sleep(600) - homeHandler.startLoad() - }.start() + lifecycleScope.launch{ + withContext(Dispatchers.IO) { + homeHandler.obtainMessage(-1, true).sendToTarget() + while(!MainActivity.isDrawerClosed) sleep(233) + //homeHandler.sendEmptyMessage(6) //removeAllViews + homeHandler.fhib = null + sleep(600) + homeHandler.startLoad() + } + } } } @@ -261,14 +269,16 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) { override fun getItemCount() = (results?.results?.list?.size?:0) + if (query?.isNotEmpty() == true) 1 else 0 - fun refresh(q: CharSequence) { + suspend fun refresh(q: CharSequence) = withContext(Dispatchers.IO) { query = q.toString() activity?.apply { - AutoDownloadThread(getString(R.string.searchApiUrl).format(CMApi.myHostApiUrl, 0, query, type)) { - results = Gson().fromJson(it?.decodeToString(), BookListStructure::class.java) + PausableDownloader(getString(R.string.searchApiUrl).format(CMApi.myHostApiUrl, 0, query, type)) { + results = Gson().fromJson(it.decodeToString(), BookListStructure::class.java) count = results?.results?.total?:0 - runOnUiThread { notifyDataSetChanged() } - }.start() + withContext(Dispatchers.Main) { + notifyDataSetChanged() + } + }.run() } } } diff --git a/app/src/main/java/top/fumiama/copymanga/ui/home/HomeHandler.kt b/app/src/main/java/top/fumiama/copymanga/ui/home/HomeHandler.kt index c5d67c4..5209065 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/home/HomeHandler.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/home/HomeHandler.kt @@ -3,7 +3,6 @@ package top.fumiama.copymanga.ui.home import android.animation.ObjectAnimator import android.graphics.Color import android.os.Bundle -import android.os.Looper import android.os.Message import android.util.Log import android.view.View @@ -13,6 +12,7 @@ import android.widget.Toast import androidx.cardview.widget.CardView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.animation.doOnEnd +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.bumptech.glide.Glide @@ -23,6 +23,9 @@ import com.to.aboomy.pager2banner.ScaleInTransformer import kotlinx.android.synthetic.main.card_book.view.* import kotlinx.android.synthetic.main.fragment_home.* import kotlinx.android.synthetic.main.line_1bookline.view.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import top.fumiama.copymanga.json.ComicStructure import top.fumiama.copymanga.json.IndexStructure import top.fumiama.copymanga.template.http.AutoDownloadHandler @@ -37,7 +40,7 @@ import java.lang.ref.WeakReference class HomeHandler(private val that: WeakReference) : AutoDownloadHandler( that.get()?.getString(R.string.mainPageApiUrl)!!.format(CMApi.myHostApiUrl), IndexStructure::class.java, - Looper.myLooper()!!, + that.get(), 9 ) { private val homeF get() = that.get() @@ -47,7 +50,7 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH Log.d("MyHH", "Get fhib.") if (field == null) { field = homeF?.layoutInflater?.inflate(R.layout.viewpage_banner, homeF?.fhl, false) - Thread{ homeF?.homeHandler?.sendEmptyMessage(3) }.start() + homeF?.homeHandler?.sendEmptyMessage(3) } return field } @@ -104,23 +107,17 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH if(exit) return Toast.makeText(homeF?.context, R.string.web_error, Toast.LENGTH_SHORT).show() } - override fun doWhenFinishDownload() { + override suspend fun doWhenFinishDownload() = withContext(Dispatchers.IO) { super.doWhenFinishDownload() - if(exit) return - try { - Thread { - sendEmptyMessage(2) //setSwipe - sendEmptyMessage(7) //inflateBanner - sendEmptyMessage(1) //inflateCardLines - }.start() - } catch (e: Exception) { - Toast.makeText(homeF?.context, R.string.load_home_error, Toast.LENGTH_SHORT).show() - } + if(exit) return@withContext + sendEmptyMessage(2) //setSwipe + sendEmptyMessage(7) //inflateBanner + sendEmptyMessage(1) //inflateCardLines } private fun inflateBanner() = homeF?.fhl?.addView(fhib) - private fun inflateTopics(){ + private suspend fun inflateTopics() { index?.results?.topics?.list?.let { var comics = arrayOf() for((i, topic) in it.withIndex()){ @@ -135,7 +132,7 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH } } - private fun inflateRec(){ + private suspend fun inflateRec() { index?.results?.recComics?.list?.let { var comics = arrayOf() for((i, rec) in it.withIndex()){ @@ -150,7 +147,7 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH } } - private fun inflateRank(){ + private suspend fun inflateRank(){ var comics = arrayOf() index?.results?.rankDayComics?.list?.let { for((i, book) in it.withIndex()){ @@ -175,7 +172,7 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH } } - private fun inflateHot(){ + private suspend fun inflateHot(){ index?.results?.hotComics?.let { var comics = arrayOf() for((i, rec) in it.withIndex()){ @@ -186,7 +183,7 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH } } - private fun inflateNew(){ + private suspend fun inflateNew(){ index?.results?.newComics?.let { var comics = arrayOf() for((i, rec) in it.withIndex()){ @@ -201,7 +198,7 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH } } - private fun inflateFinish(){ + private suspend fun inflateFinish(){ index?.results?.finishComics?.list?.let { var comics = arrayOf() for((i, rec) in it.withIndex()){ @@ -216,20 +213,24 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH } } - private fun inflateCardLines() = Thread { - if (indexLines.isNotEmpty()) indexLines = arrayOf() - inflateRec() - inflateTopics() - inflateHot() - inflateNew() - inflateFinish() - inflateRank() - for(i in indexLines.indices) { - obtainMessage(8, i, 0).sendToTarget() - sleep(512) + private fun inflateCardLines() { + homeF?.lifecycleScope?.launch { + withContext(Dispatchers.IO) { + if (indexLines.isNotEmpty()) indexLines = arrayOf() + inflateRec() + inflateTopics() + inflateHot() + inflateNew() + inflateFinish() + inflateRank() + for(i in indexLines.indices) { + obtainMessage(8, i, 0).sendToTarget() + sleep(512) + } + obtainMessage(-1, false).sendToTarget() //closeLoad + } } - obtainMessage(-1, false).sendToTarget() //closeLoad - }.start() + } private fun setBanner(v: Banner): Banner { v.viewTreeObserver.addOnGlobalLayoutListener(object : @@ -241,7 +242,7 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH v.viewTreeObserver.removeOnGlobalLayoutListener(this) } }) - Thread{this.obtainMessage(5, v).sendToTarget()}.start() //setBannerInfo + obtainMessage(5, v).sendToTarget() //setBannerInfo return v } @@ -264,20 +265,23 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH homeF?.fhov?.swipeRefreshLayout = sw sw.setOnRefreshListener { Log.d("MyHFH", "Refresh items.") - //index = null - //Thread{this@HomeHandler.obtainMessage(-1, true).sendToTarget()}.start() //startLoad - Thread{ - index = null - //fhib = null - indexLines = arrayOf() - this@HomeHandler.sendEmptyMessage(6) //removeAllViews - sleep(300) - this@HomeHandler.sendEmptyMessage(0) //setLayouts - }.start() + homeF?.lifecycleScope?.launch { + withContext(Dispatchers.IO) { + index = null + //fhib = null + indexLines = arrayOf() + this@HomeHandler.sendEmptyMessage(6) //removeAllViews + sleep(300) + this@HomeHandler.sendEmptyMessage(0) //setLayouts + } + } } } - private fun allocateLine(title: String, iconResId: Int, comics: Array, finish: Boolean = false, isTopic: Boolean = false, onClick: (() -> Unit)? = null): Int{ + private suspend fun allocateLine( + title: String, iconResId: Int, comics: Array, + finish: Boolean = false, isTopic: Boolean = false, onClick: (() -> Unit)? = null + ): Int = withContext(Dispatchers.IO) { val p = indexLines.size val c = comics.size / 3 homeF?.layoutInflater?.inflate( @@ -285,22 +289,24 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH 1 -> R.layout.line_1bookline 2 -> R.layout.line_2bookline 3 -> R.layout.line_3bookline - else -> return -1 - }, homeF!!.fhl, false)?.apply { - scanCards(this, comics, finish, isTopic) - rttitle.text = title - ir.setImageResource(iconResId) - setLineHeight(this, c) - if(onClick != null) setOnClickListener { onClick() } + else -> return@withContext -1 + }, homeF!!.fhl, false)?.apply { + withContext(Dispatchers.Main) { + scanCards(this@apply, comics, finish, isTopic) + rttitle.text = title + ir.setImageResource(iconResId) + setLineHeight(this@apply, c) + if(onClick != null) setOnClickListener { onClick() } + } indexLines += this } - return p + return@withContext p } - private fun scanCards(v: View, comics: Array, finish: Boolean, isTopic: Boolean){ + private suspend fun scanCards(v: View, comics: Array, finish: Boolean, isTopic: Boolean) = withContext(Dispatchers.IO) { var id = v.rc1.id var card = v.findViewById(id) - for (data in comics){ + for (data in comics) { setCards( card.cic, data.path_word, @@ -313,10 +319,10 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH } } - private fun setCards(cv: CardView, pw: String, name: String, img: String, isFinal: Boolean, isTopic: Boolean) { + private suspend fun setCards(cv: CardView, pw: String, name: String, img: String, isFinal: Boolean, isTopic: Boolean) = withContext(Dispatchers.Main) { cv.tic.text = name homeF?.let { - if(img.startsWith("http")) it.activity?.runOnUiThread { + if(img.startsWith("http")) { Glide.with(it).load(GlideUrl(CMApi.proxy?.wrap(img)?:img, CMApi.myGlideHeaders)) .addListener(GlideHideLottieViewListener(WeakReference(cv.laic))) .timeout(20000).into(cv.imic) @@ -348,4 +354,4 @@ class HomeHandler(private val that: WeakReference) : AutoDownloadH } }) } -} \ No newline at end of file +} diff --git a/app/src/main/java/top/fumiama/copymanga/ui/vm/VMHandler.kt b/app/src/main/java/top/fumiama/copymanga/ui/vm/VMHandler.kt index 1b97597..40b07fc 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/vm/VMHandler.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/vm/VMHandler.kt @@ -3,19 +3,22 @@ package top.fumiama.copymanga.ui.vm import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.app.Dialog -import android.os.Looper import android.os.Message import android.util.Log import android.view.View -import com.afollestad.materialdialogs.utils.MDUtil.getStringArray +import androidx.lifecycle.lifecycleScope import com.google.gson.Gson import kotlinx.android.synthetic.main.activity_viewmanga.* import kotlinx.android.synthetic.main.widget_infodrawer.* import kotlinx.android.synthetic.main.widget_infodrawer.view.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import top.fumiama.copymanga.json.Chapter2Return import top.fumiama.copymanga.json.ChapterWithContent import top.fumiama.copymanga.json.ComicStructure import top.fumiama.copymanga.template.http.AutoDownloadHandler +import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.comicName import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.pn import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.position @@ -27,13 +30,12 @@ import java.lang.ref.WeakReference import java.text.SimpleDateFormat import java.util.* -class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler( - url, Chapter2Return::class.java, Looper.myLooper()!! +class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, private val weeks: Array) : AutoDownloadHandler( + chapterUrl, Chapter2Return::class.java, activity ) { var manga: Chapter2Return? = null private val wv = WeakReference(activity) private val drawer = wv.get()?.infcard - private val weeks = wv.get()?.getStringArray(R.array.weeks) private var hasDrawerShown = false val dl = activity.let { val re = Dialog(it) @@ -50,7 +52,7 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler( val cal = Calendar.getInstance() val w = cal[Calendar.DAY_OF_WEEK] if (w > 7 || w <= 0) return "" - return weeks?.get(w-1) ?: "" + return weeks[w-1] } private var remainingImageCount = 0 @@ -72,7 +74,11 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler( LOAD_IMG_ON -> { val scaleImageView = msg.obj as ScaleImageView // msg.arg2: isLast - wv.get()?.loadImgOn(scaleImageView, msg.arg1) + wv.get()?.apply { + lifecycleScope.launch { + loadImgOn(scaleImageView, msg.arg1) + } + } //scaleImageView.setHeight2FitImgWidth() //if(msg.arg2 == 1) sendEmptyMessage(DELAYED_RESTORE_PAGE_NUMBER) } @@ -86,8 +92,8 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler( LOAD_ITEM_SCROLL_MODE -> loadScrollMode(msg.arg1, msg.obj as? Runnable?) LOAD_SCROLL_MODE -> loadScrollMode() - LOAD_ITEM_IMAGES_INTO_LINE -> loadImagesIntoLine(msg.arg1, msg.obj as? Runnable?) - LOAD_IMAGES_INTO_LINE -> loadImagesIntoLine() + LOAD_ITEM_IMAGES_INTO_LINE -> wv.get()?.lifecycleScope?.launch { loadImagesIntoLine(msg.arg1, msg.obj as? Runnable?) } + LOAD_IMAGES_INTO_LINE -> wv.get()?.lifecycleScope?.launch { loadImagesIntoLine() } RESTORE_PAGE_NUMBER -> { sendEmptyMessage(DIALOG_HIDE) wv.get()?.restorePN() @@ -141,17 +147,22 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler( override fun onError() { super.onError() if(exit) return - wv.get()?.toolsBox?.toastError(R.string.download_chapter_info_failed) + wv.get()?.apply { + lifecycleScope.launch { + toolsBox.toastError(R.string.download_chapter_info_failed) + } + } } - override fun doWhenFinishDownload() { + override suspend fun doWhenFinishDownload() { super.doWhenFinishDownload() if(exit) return prepareManga() } - fun loadFromFile(file: File): Boolean { - return try { + suspend fun loadFromFile(file: File): Boolean = withContext(Dispatchers.IO) { + fakeLoad() + return@withContext try { val jsonFile = File(file.parentFile, "${file.nameWithoutExtension}.json") if(jsonFile.exists()) { manga = Gson().fromJson(jsonFile.reader(), Chapter2Return::class.java) @@ -180,7 +191,11 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler( } } - private fun prepareManga(){ + private suspend fun fakeLoad() { + PausableDownloader(chapterUrl) { _ -> }.run() + } + + private suspend fun prepareManga() = withContext(Dispatchers.Main) { if(comicName == null) { comicName = manga?.results?.comic?.name } @@ -188,7 +203,7 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler( wv.get()?.initManga() wv.get()?.vprog?.visibility = View.GONE } - private fun loadImagesIntoLine(item: Int = (wv.get()?.currentItem?:0), doAfter: Runnable? = null) = Thread{ + private suspend fun loadImagesIntoLine(item: Int = (wv.get()?.currentItem?:0), doAfter: Runnable? = null) = withContext(Dispatchers.IO) { val maxCount: Int = (wv.get()?.verticalLoadMaxCount?:20) Log.d("MyVMH", "Fun: loadImagesIntoLine($item, $maxCount)") wv.get()?.realCount?.let { count -> @@ -204,11 +219,11 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler( if(notFull) obtainMessage(PREPARE_LAST_PAGE, loadCount + 1, maxCount).sendToTarget() obtainMessage(DO_LAMBDA, Runnable{ doAfter?.run() - wv.get()?.let { it.updateSeekBar(0) } + wv.get()?.updateSeekBar(0) }).sendToTarget() } } - }.start() + } private fun loadScrollMode() { sendEmptyMessage(DIALOG_SHOW) diff --git a/app/src/main/java/top/fumiama/copymanga/ui/vm/ViewMangaActivity.kt b/app/src/main/java/top/fumiama/copymanga/ui/vm/ViewMangaActivity.kt index 0210737..0414ebe 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/vm/ViewMangaActivity.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/vm/ViewMangaActivity.kt @@ -21,6 +21,7 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.core.animation.doOnEnd import androidx.core.content.ContextCompat import androidx.core.content.edit +import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 @@ -38,9 +39,12 @@ import kotlinx.android.synthetic.main.widget_infodrawer.* import kotlinx.android.synthetic.main.widget_titlebar.* import kotlinx.android.synthetic.main.widget_titlebar.view.* import kotlinx.android.synthetic.main.widget_viewmangainfo.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import top.fumiama.copymanga.MainActivity import top.fumiama.copymanga.template.general.TitleActivityTemplate -import top.fumiama.copymanga.template.http.AutoDownloadThread +import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.copymanga.tools.http.DownloadTools import top.fumiama.copymanga.tools.thread.TimeThread @@ -128,50 +132,59 @@ class ViewMangaActivity : TitleActivityTemplate() { @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { - val settingsPref = MainActivity.mainWeakReference?.get()?.let { PreferenceManager.getDefaultSharedPreferences(it) } - settingsPref?.getBoolean("settings_cat_vm_sw_always_dark_bg", false)?.let { - if (it) { - Log.d("MyVM", "force dark") - delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES - } else { - delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - } - } postponeEnterTransition() setContentView(R.layout.activity_viewmanga) super.onCreate(null) + lifecycleScope.launch { + withContext(Dispatchers.IO) { + val settingsPref = MainActivity.mainWeakReference?.get()?.let { PreferenceManager.getDefaultSharedPreferences(it) } + settingsPref?.getBoolean("settings_cat_vm_sw_always_dark_bg", false)?.let { + if (it) { + Log.d("MyVM", "force dark") + delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES + } else { + delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + } + } + va = WeakReference(this@ViewMangaActivity) + //dlZip2View = intent.getStringExtra("callFrom") == "Dl" || p["dlZip2View"] == "true" + //zipFirst = intent.getStringExtra("callFrom") == "zipFirst" + intent.getStringArrayExtra("urlArray")?.let { urlArray = it } + cut = pb["useCut"] + r2l = pb["r2l"] + verticalLoadMaxCount = settingsPref?.getInt("settings_cat_vm_sb_vertical_max", 20)?.let { if(it > 0) it else 20 }?:20 + isVertical = pb["vertical"] + notUseVP = pb["noVP"] || isVertical + //url = intent.getStringExtra("url") + withContext(Dispatchers.Main) { + handler = VMHandler(this@ViewMangaActivity, if(urlArray.isNotEmpty()) urlArray[position] else "", resources.getStringArray(R.array.weeks)) + withContext(Dispatchers.IO) { + settingsPref?.getInt("settings_cat_vm_sb_quality", 100)?.let { q = if (it > 0) it else 100 } + tt = TimeThread(handler, VMHandler.SET_NET_INFO, 10000) + tt.canDo = true + tt.start() + volTurnPage = settingsPref?.getBoolean("settings_cat_vm_sw_vol_turn", false)?:false + am = getSystemService(Service.AUDIO_SERVICE) as AudioManager + if (!noCellarAlert) noCellarAlert = settingsPref?.getBoolean("settings_cat_net_sw_use_cellar", false) == true + fullyHideInfo = settingsPref?.getBoolean("settings_cat_vm_sw_hide_info", false) == true - va = WeakReference(this) - //dlZip2View = intent.getStringExtra("callFrom") == "Dl" || p["dlZip2View"] == "true" - //zipFirst = intent.getStringExtra("callFrom") == "zipFirst" - intent.getStringArrayExtra("urlArray")?.let { urlArray = it } - cut = pb["useCut"] - r2l = pb["r2l"] - verticalLoadMaxCount = settingsPref?.getInt("settings_cat_vm_sb_vertical_max", 20)?.let { if(it > 0) it else 20 }?:20 - isVertical = pb["vertical"] - notUseVP = pb["noVP"] || isVertical - //url = intent.getStringExtra("url") - handler = VMHandler(this, if(urlArray.isNotEmpty()) urlArray[position] else "") - settingsPref?.getInt("settings_cat_vm_sb_quality", 100)?.let { q = if (it > 0) it else 100 } - tt = TimeThread(handler, VMHandler.SET_NET_INFO) - tt.canDo = true - tt.start() - volTurnPage = settingsPref?.getBoolean("settings_cat_vm_sw_vol_turn", false)?:false - am = getSystemService(Service.AUDIO_SERVICE) as AudioManager - if (!noCellarAlert) noCellarAlert = settingsPref?.getBoolean("settings_cat_net_sw_use_cellar", false) == true - fullyHideInfo = settingsPref?.getBoolean("settings_cat_vm_sw_hide_info", false) == true - - Log.d("MyVM", "Now ZipFile is $zipFile") - try { - if (zipFile != null && zipFile?.exists() == true) { - if (!handler.loadFromFile(zipFile!!)) prepareImgFromWeb() - } else prepareImgFromWeb() - } catch (e: Exception) { - e.printStackTrace() - toolsBox.toastError(R.string.load_manga_error) + Log.d("MyVM", "Now ZipFile is $zipFile") + try { + if (zipFile != null && zipFile?.exists() == true) { + if (!handler.loadFromFile(zipFile!!)) prepareImgFromWeb() + } else prepareImgFromWeb() + } catch (e: Exception) { + e.printStackTrace() + toolsBox.toastError(R.string.load_manga_error) + } + withContext(Dispatchers.Main) { + startPostponedEnterTransition() + ObjectAnimator.ofFloat(vcp, "alpha", 0.1f, 1f).setDuration(1000).start() + } + } + } + } } - startPostponedEnterTransition() - ObjectAnimator.ofFloat(vcp, "alpha", 0.1f, 1f).setDuration(1000).start() } @Suppress("DEPRECATION") @@ -293,12 +306,12 @@ class ViewMangaActivity : TitleActivityTemplate() { return op.outWidth.toFloat() / op.outHeight.toFloat() > 1 } - fun countZipEntries(doWhenFinish : (count: Int) -> Unit) = Thread{ + suspend fun countZipEntries(doWhenFinish : suspend (count: Int) -> Unit) = withContext(Dispatchers.IO) { if (zipFile != null) try { Log.d("MyVM", "zip: $zipFile") val zip = ZipFile(zipFile) count = zip.size() - if(cut) zip.entries().toList().sortedBy{it.name.substringBefore('.').toInt()}.forEachIndexed { i, it -> + if(cut) zip.entries().toList().sortedBy{ it.name.substringBefore('.').toInt()}.forEachIndexed { i, it -> val useCut = canCut(zip.getInputStream(it)) isCut += useCut indexMap += i + 1 @@ -306,13 +319,11 @@ class ViewMangaActivity : TitleActivityTemplate() { Log.d("MyVM", "[$i] 分析: ${it.name}, cut: $useCut") } } catch (e: Exception) { - runOnUiThread { toolsBox.toastError(R.string.count_zip_entries_error) } + withContext(Dispatchers.Main) { toolsBox.toastError(R.string.count_zip_entries_error) } } - runOnUiThread { - Log.d("MyVM", "开始加载控件") - doWhenFinish(count) - } - }.start() + Log.d("MyVM", "开始加载控件") + doWhenFinish(count) + } private fun getPageNumber(): Int { return if (r2l && !notUseVP) realCount - vp.currentItem @@ -336,7 +347,9 @@ class ViewMangaActivity : TitleActivityTemplate() { loadOneImg() } catch (e: Exception) { e.printStackTrace() - toolsBox.toastError(getString(R.string.load_page_number_error).format(currentItem)) + lifecycleScope.launch { + toolsBox.toastError(getString(R.string.load_page_number_error).format(currentItem)) + } } } } else { @@ -382,9 +395,9 @@ class ViewMangaActivity : TitleActivityTemplate() { private fun cutBitmap(bitmap: Bitmap, isEnd: Boolean) = Bitmap.createBitmap(bitmap, if(!isEnd) 0 else (bitmap.width/2), 0, bitmap.width/2, bitmap.height) - private fun loadImg(imgView: ScaleImageView, bitmap: Bitmap, useCut: Boolean, isLeft: Boolean, isPlaceholder: Boolean = true) { + private suspend fun loadImg(imgView: ScaleImageView, bitmap: Bitmap, useCut: Boolean, isLeft: Boolean, isPlaceholder: Boolean = true) = withContext(Dispatchers.IO) { val bitmap2load = if(!isPlaceholder && useCut) cutBitmap(bitmap, isLeft) else bitmap - runOnUiThread { + withContext(Dispatchers.Main) { imgView.setImageBitmap(bitmap2load) if(!isPlaceholder && isVertical) { imgView.setHeight2FitImgWidth() @@ -393,11 +406,11 @@ class ViewMangaActivity : TitleActivityTemplate() { } } - private fun loadImgUrlInto(imgView: ScaleImageView, url: String, useCut: Boolean, isLeft: Boolean){ + private suspend fun loadImgUrlInto(imgView: ScaleImageView, url: String, useCut: Boolean, isLeft: Boolean){ Log.d("MyVM", "Load from adt: $url") - AutoDownloadThread(CMApi.resolution.wrap(CMApi.proxy?.wrap(url)?:url), 1000) { - it?.let { loadImg(imgView, BitmapFactory.decodeByteArray(it, 0, it.size), useCut, isLeft, false) } - }.start() + PausableDownloader(CMApi.resolution.wrap(CMApi.proxy?.wrap(url)?:url), 1000) { + it.let { loadImg(imgView, BitmapFactory.decodeByteArray(it, 0, it.size), useCut, isLeft, false) } + }.run() } private fun getLoadingBitmap(position: Int): Bitmap { @@ -414,9 +427,9 @@ class ViewMangaActivity : TitleActivityTemplate() { return loading } - fun loadImgOn(imgView: ScaleImageView, position: Int) { + suspend fun loadImgOn(imgView: ScaleImageView, position: Int) = withContext(Dispatchers.IO) { Log.d("MyVM", "Load img: $position") - if (position < 0 || position > realCount) return + if (position < 0 || position > realCount) return@withContext val index2load = if(cut) abs(indexMap[position]) -1 else position val useCut = cut && isCut[index2load] val isLeft = cut && indexMap[position] > 0 @@ -427,58 +440,58 @@ class ViewMangaActivity : TitleActivityTemplate() { loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true) val sleepTime = loadImgOnWait.getAndIncrement().toLong()*200 Log.d("MyVM", "loadImgOn sleep: $sleepTime ms") - Thread { - val re = tasks?.get(index2load) - if (sleepTime > 0 && re?.isDone != true) Thread.sleep(sleepTime) - if (re != null) { - if(!re.isDone) re.run() - val data = re.get() - if(data != null && data.isNotEmpty()) { - BitmapFactory.decodeByteArray(data, 0, data.size)?.let { - loadImg(imgView, it, useCut, isLeft, false) - runOnUiThread { Log.d("MyVM", "Load position $position from task") } - }?:runOnUiThread { Log.d("MyVM", "null bitmap at $position") } - } - else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, useCut, isLeft) } + val re = tasks?.get(index2load) + if (sleepTime > 0 && re?.isDone != true) Thread.sleep(sleepTime) + if (re != null) { + if(!re.isDone) re.run() + val data = re.get() + if(data != null && data.isNotEmpty()) { + BitmapFactory.decodeByteArray(data, 0, data.size)?.let { + loadImg(imgView, it, useCut, isLeft, false) + Log.d("MyVM", "Load position $position from task") + }?:Log.d("MyVM", "null bitmap at $position") } else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, useCut, isLeft) } - loadImgOnWait.decrementAndGet() - tasks?.apply { - if (index2load >= size) return@apply - val p = if (index2load == size-1) index2load-1 else index2load+1 - var delta = 1 - var isMinus = false - var pos = p - var maxCount = size - while (pos in indices && get(pos)?.isDone != false && tasksRunStatus?.get(pos) != false && maxCount-- > 0) { - runOnUiThread { Log.d("MyVM", "search $pos") } - pos = p + if (isMinus) -delta else delta - if (pos !in indices) { - isMinus = !isMinus - if (!isMinus) delta++ - pos = p + if (isMinus) -delta else delta - if (pos !in indices) return@apply - } + } + else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, useCut, isLeft) } + loadImgOnWait.decrementAndGet() + tasks?.apply { + if (index2load >= size) return@apply + val p = if (index2load == size-1) index2load-1 else index2load+1 + var delta = 1 + var isMinus = false + var pos = p + var maxCount = size + while (pos in indices && get(pos)?.isDone != false && tasksRunStatus?.get(pos) != false && maxCount-- > 0) { + Log.d("MyVM", "search $pos") + pos = p + if (isMinus) -delta else delta + if (pos !in indices) { isMinus = !isMinus if (!isMinus) delta++ + pos = p + if (isMinus) -delta else delta + if (pos !in indices) return@apply } - if (pos !in indices || tasksRunStatus?.get(pos) != false) return@apply - runOnUiThread { Log.d("MyVM", "Preload position $pos from task") } - get(pos)?.apply { - if(!isDone) { - tasksRunStatus?.set(pos, true) - run() - } + isMinus = !isMinus + if (!isMinus) delta++ + } + if (pos !in indices || tasksRunStatus?.get(pos) != false) return@apply + Log.d("MyVM", "Preload position $pos from task") + get(pos)?.apply { + if(!isDone) { + tasksRunStatus?.set(pos, true) + run() } } - }.start() + } } imgView.visibility = View.VISIBLE } private fun loadOneImg() { - loadImgOn(onei, currentItem) - updateSeekBar() + lifecycleScope.launch { + loadImgOn(onei, currentItem) + updateSeekBar() + } } private fun initImgList(){ @@ -494,7 +507,7 @@ class ViewMangaActivity : TitleActivityTemplate() { // handler.dl?.hide() } - private fun getImgBitmap(position: Int): Bitmap? = + private suspend fun getImgBitmap(position: Int): Bitmap? = withContext(Dispatchers.IO) { if (position >= count || position < 0) null else { val zip = ZipFile(zipFile) @@ -511,7 +524,9 @@ class ViewMangaActivity : TitleActivityTemplate() { } catch (e: Exception) { if (i == 1) { e.printStackTrace() - Toast.makeText(this, "加载zip的第${position}项错误", Toast.LENGTH_SHORT).show() + withContext(Dispatchers.Main) { + Toast.makeText(this@ViewMangaActivity, "加载zip的第${position}项错误", Toast.LENGTH_SHORT).show() + } } null } @@ -522,6 +537,7 @@ class ViewMangaActivity : TitleActivityTemplate() { } bitmap } + } private fun setIdPosition(position: Int) { infoDrawerDelta = position.toFloat() @@ -548,8 +564,10 @@ class ViewMangaActivity : TitleActivityTemplate() { }*/ } catch (e: Exception) { e.printStackTrace() - toolsBox.toastError(R.string.load_chapter_error) - finish() + lifecycleScope.launch { + toolsBox.toastError(R.string.load_chapter_error) + finish() + } } } @@ -812,9 +830,11 @@ class ViewMangaActivity : TitleActivityTemplate() { val index2load = if(cut) abs(indexMap[pos]) -1 else pos val useCut = cut && isCut[index2load] val isLeft = cut && indexMap[pos] > 0 - if (zipFile?.exists() == true) getImgBitmap(index2load)?.let { - //Glide.with(this@ViewMangaActivity).load(if(useCut) cutBitmap(it, isLeft) else it).into(holder.itemView.onei) - holder.itemView.onei.setImageBitmap(if(useCut) cutBitmap(it, isLeft) else it) + if (zipFile?.exists() == true) lifecycleScope.launch { + getImgBitmap(index2load)?.let { + //Glide.with(this@ViewMangaActivity).load(if(useCut) cutBitmap(it, isLeft) else it).into(holder.itemView.onei) + holder.itemView.onei.setImageBitmap(if(useCut) cutBitmap(it, isLeft) else it) + } } else getImgUrl(index2load)?.let{ if(useCut){ diff --git a/app/src/main/java/top/fumiama/copymanga/user/Member.kt b/app/src/main/java/top/fumiama/copymanga/user/Member.kt index 28abaef..244d4eb 100644 --- a/app/src/main/java/top/fumiama/copymanga/user/Member.kt +++ b/app/src/main/java/top/fumiama/copymanga/user/Member.kt @@ -1,20 +1,26 @@ package top.fumiama.copymanga.user import android.content.SharedPreferences +import android.util.Base64 import com.google.gson.Gson +import com.google.gson.stream.JsonReader import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import top.fumiama.copymanga.json.LoginInfoStructure import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.copymanga.tools.http.DownloadTools import top.fumiama.dmzj.copymanga.R +import java.net.URLEncoder +import java.nio.charset.Charset class Member(private val pref: SharedPreferences, private val getString: (Int) -> String) { val hasLogin: Boolean get() = pref.getString("token", "")?.isNotEmpty()?:false suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure = withContext(Dispatchers.IO) { try { - CMApi.getLoginConnection(username, pwd, salt)?.apply { - Gson().fromJson(inputStream.reader(), LoginInfoStructure::class.java)?.let { data -> + getLoginConnection(username, pwd, salt).apply { + Gson().fromJson( + JsonReader(inputStream.reader()), LoginInfoStructure::class.java + )?.let { data -> disconnect() if(data.code == 200) { pref.edit()?.apply { @@ -23,7 +29,7 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) - putString("username", data.results?.username) putString("nickname", data.results?.nickname) apply() - return@withContext refreshAvatar() + return@withContext info() } } return@withContext data @@ -42,37 +48,37 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) - } - - fun refreshAvatar() : LoginInfoStructure { + /** + * 获得登录信息并更新头像 + * @return 登录态 + * - **code**: 449: 未登录, 450: 有 Exception + * - **message**: 可以 toast 的信息 + */ + suspend fun info() : LoginInfoStructure = withContext(Dispatchers.IO) { if (!pref.contains("token")) { val l = LoginInfoStructure() - l.code = 400 + l.code = 449 l.message = getString(R.string.noLogin) - return l + return@withContext l } - try { - DownloadTools.getHttpContent(getString(R.string.memberInfoApiUrl).format( - CMApi.myHostApiUrl))?.decodeToString()?.let { - val l = Gson().fromJson(it, LoginInfoStructure::class.java) - if(l.code == 200) pref.edit()?.apply { - putString("avatar", l.results.avatar) - apply() - } - return l + return@withContext try { + val l = Gson().fromJson(DownloadTools.getHttpContent( + getString(R.string.memberInfoApiUrl).format(CMApi.myHostApiUrl)).decodeToString(), + LoginInfoStructure::class.java) + if(l.code == 200) pref.edit()?.apply { + putString("avatar", l.results.avatar) + apply() } + l } catch (e: Exception) { val l = LoginInfoStructure() - l.code = 400 + l.code = 450 l.message = "${getString(R.string.login_get_avatar_failed)}: ${e.localizedMessage}" - return l + l } - val l = LoginInfoStructure() - l.code = 400 - l.message = getString(R.string.login_get_avatar_failed) - return l } - fun logout() { + suspend fun logout() = withContext(Dispatchers.IO) { pref.edit()?.apply { remove("token") remove("user_id") @@ -82,4 +88,19 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) - apply() } } + + private fun getLoginConnection(username: String, pwd: String, salt: Int) = + getString(R.string.loginApiUrl).format(CMApi.myHostApiUrl).let { + DownloadTools.getApiConnection(it, "POST").apply { + pref.apply { + doOutput = true + setRequestProperty("content-type", "application/x-www-form-urlencoded;charset=utf-8") + setRequestProperty("platform", "3") + setRequestProperty("accept", "application/json") + val r = if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "1" else "0" + val pwdEncoded = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString() + outputStream.write("username=${URLEncoder.encode(username, Charset.defaultCharset().name())}&password=$pwdEncoded&salt=$salt&platform=3&authorization=Token+&version=1.4.4&source=copyApp®ion=$r&webp=1".toByteArray()) + } + } + } } diff --git a/app/src/main/java/top/fumiama/copymanga/views/ScaleImageView.kt b/app/src/main/java/top/fumiama/copymanga/views/ScaleImageView.kt index ff2eb5e..0bc40a9 100644 --- a/app/src/main/java/top/fumiama/copymanga/views/ScaleImageView.kt +++ b/app/src/main/java/top/fumiama/copymanga/views/ScaleImageView.kt @@ -14,6 +14,8 @@ import android.view.GestureDetector import android.view.GestureDetector.SimpleOnGestureListener import android.view.MotionEvent import android.widget.ImageView +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch import top.fumiama.copymanga.ui.vm.PagesManager import top.fumiama.copymanga.ui.vm.ViewMangaActivity import top.fumiama.dmzj.copymanga.R @@ -564,7 +566,11 @@ class ScaleImageView : ImageView { } }catch (e:Exception){ e.printStackTrace() - ViewMangaActivity.va?.get()?.toolsBox?.toastError(R.string.show_image_error_try_lower_resolution, false) + ViewMangaActivity.va?.get()?.apply { + lifecycleScope.launch { + toolsBox.toastError(R.string.show_image_error_try_lower_resolution, false) + } + } } } ////////////////////////////////有效性判断//////////////////////////////// diff --git a/app/src/main/res/layout/line_bookinfo.xml b/app/src/main/res/layout/line_bookinfo.xml index b6bae54..c02c370 100644 --- a/app/src/main/res/layout/line_bookinfo.xml +++ b/app/src/main/res/layout/line_bookinfo.xml @@ -19,7 +19,7 @@ ]> - + 拷贝漫画 设定 @@ -81,7 +81,7 @@ https://%1$s/api/v3/member/collect/comic https://copymanga.azurewebsites.net/api/img?code=%1$s&url=%2$s - https://hi77-overseas.mangafuna.xyz/ + ^https://[0-9a-z-]+\.mangafuna\.xyz/ settings_cat_net_et_img_proxy_code