From d60c8d96a4411314aef0dfde637b16ae8cb6990d 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: Mon, 11 Mar 2024 02:55:36 +0900 Subject: [PATCH] =?UTF-8?q?v2.2.1=20=E6=96=B0=E5=A2=9E=201.=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=97=A0=E7=BD=91=E7=BB=9C=E6=97=B6=E4=BB=8E=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E8=AE=BF=E9=97=AE=E6=BC=AB=E7=94=BB=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E9=A1=B5=20=E4=BF=AE=E5=A4=8D=201.=20=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E5=B0=81=E9=9D=A2=E8=B6=85=E6=97=B6=E6=97=B6=E9=97=B4=E8=BF=87?= =?UTF-8?q?=E7=9F=AD=202.=20=E7=AB=96=E5=90=91=E7=BF=BB=E9=A1=B5=E5=9B=9E?= =?UTF-8?q?=E5=88=B0=E4=B8=8A=E4=B8=80=E6=AE=B5=E9=97=AA=E9=80=80=203.=20?= =?UTF-8?q?=E6=97=A0=E5=8A=A8=E7=94=BB=E9=98=85=E8=A7=88=E6=97=B6=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E4=B8=8B=E4=B8=80=E9=A1=B5=E9=97=AA=E5=B1=8F=204.=20?= =?UTF-8?q?=E6=8E=92=E8=A1=8C=E9=A1=B5=E5=BF=AB=E9=80=9F=E7=82=B9=E5=87=BB?= =?UTF-8?q?tab=E6=97=B6=E6=98=BE=E7=A4=BA=E9=94=99=E4=B9=B1=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=201.=20=E6=88=91=E7=9A=84=E4=B8=8B=E8=BD=BD=E9=A1=B5?= =?UTF-8?q?=E7=9A=84=E6=97=A0=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/dictionaries/fumiama.xml | 2 + app/build.gradle | 4 +- .../top/fumiama/copymanga/MainActivity.kt | 23 +- .../java/top/fumiama/copymanga/manga/Book.kt | 171 ++++++++++- .../top/fumiama/copymanga/manga/Volume.kt | 76 +++++ .../template/http/AutoDownloadHandler.kt | 12 +- .../template/http/PausableDownloader.kt | 5 +- .../copymanga/tools/file/PropertiesTools.kt | 24 +- .../fumiama/copymanga/ui/book/BookFragment.kt | 86 +++--- .../fumiama/copymanga/ui/book/BookHandler.kt | 283 ++++-------------- .../ui/cardflow/rank/RankFragment.kt | 61 ++-- .../ui/cardflow/sort/SortFragment.kt | 4 +- .../ui/cardflow/topic/TopicFragment.kt | 18 +- .../copymanga/ui/comicdl/ComicDlFragment.kt | 81 +---- .../ui/download/NewDownloadFragment.kt | 20 +- .../top/fumiama/copymanga/ui/vm/VMHandler.kt | 5 +- .../copymanga/ui/vm/ViewMangaActivity.kt | 44 ++- .../java/top/fumiama/copymanga/user/Member.kt | 2 +- 18 files changed, 470 insertions(+), 451 deletions(-) create mode 100644 app/src/main/java/top/fumiama/copymanga/manga/Volume.kt diff --git a/.idea/dictionaries/fumiama.xml b/.idea/dictionaries/fumiama.xml index fe0a67e..7d7a688 100644 --- a/.idea/dictionaries/fumiama.xml +++ b/.idea/dictionaries/fumiama.xml @@ -1,6 +1,8 @@ + downloaders + grps imgs lowpan mangafuna diff --git a/app/build.gradle b/app/build.gradle index 8ed6a3b..d4a7b7c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId 'top.fumiama.copymanga' minSdkVersion 23 targetSdkVersion 34 - versionCode 47 - versionName '2.2.0' + versionCode 48 + versionName '2.2.1' resourceConfigurations += ['zh', 'zh-rCN'] testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt index d96738d..2a51ba3 100644 --- a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt +++ b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt @@ -287,16 +287,15 @@ class MainActivity : AppCompatActivity() { } private fun saveFile(uri: Uri) { - //val f = File(getExternalFilesDir(""), "headPic") - val fd = contentResolver.openFileDescriptor(uri, "r") - fd?.fileDescriptor?.let { - val fi = FileInputStream(it) - val fo = headPic.outputStream() - fi.copyTo(fo) - fi.close() - fo.close() + contentResolver.openFileDescriptor(uri, "r")?.use { + it.fileDescriptor?.let { fd -> + FileInputStream(fd).use { fi -> + headPic.outputStream().use { fo -> + fi.copyTo(fo) + } + } + } } - fd?.close() } private fun checkHeadPicture() { @@ -306,9 +305,9 @@ class MainActivity : AppCompatActivity() { private var cropLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == Activity.RESULT_OK) { - val fi = headPic.inputStream() - navhbg.setImageBitmap(BitmapFactory.decodeStream(fi)) - fi.close() + headPic.inputStream().use { fi -> + navhbg.setImageBitmap(BitmapFactory.decodeStream(fi)) + } } else Toast.makeText(this, R.string.err_crop_img, Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/java/top/fumiama/copymanga/manga/Book.kt b/app/src/main/java/top/fumiama/copymanga/manga/Book.kt index b5d1c60..e6803ce 100644 --- a/app/src/main/java/top/fumiama/copymanga/manga/Book.kt +++ b/app/src/main/java/top/fumiama/copymanga/manga/Book.kt @@ -1,10 +1,169 @@ package top.fumiama.copymanga.manga -class Book(val pathWord: String, val readLocally: Boolean = false) { - /** - * 更新云端最新信息 - */ - suspend fun update() { +import android.util.Log +import com.google.gson.Gson +import kotlinx.android.synthetic.main.card_book.* +import kotlinx.android.synthetic.main.line_booktandb.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import top.fumiama.copymanga.json.BookInfoStructure +import top.fumiama.copymanga.json.ThemeStructure +import top.fumiama.copymanga.json.VolumeStructure +import top.fumiama.copymanga.tools.api.CMApi +import top.fumiama.copymanga.tools.http.DownloadTools +import top.fumiama.dmzj.copymanga.R +import java.io.File +class Book(val path: String, private val getString: (Int) -> String, private val exDir: File, private val loadCache: Boolean = false, private val mPassName: String? = null) { + private val mBookApiUrl = getString(R.string.bookInfoApiUrl).format(CMApi.myHostApiUrl, path) + private val mUserAgent = getString(R.string.pc_ua) + private var mBook: BookInfoStructure? = null + private var mGroupPathWords = arrayOf() + private var mKeys = arrayOf() + private var mCounts = intArrayOf() + private var mVolumes = arrayOf() + private var mJsonString = "" + var exit = false + val name: String? get() = mBook?.results?.comic?.name?:mPassName + val cover: String? get() = mBook?.results?.comic?.cover + val cachedCover: File? + get() { + val mangaFolder = name?.let { File(exDir, it) }?:return null + val head = File(mangaFolder, "head.jpg") + if (!head.exists()) return null + return head + } + val region get() = mBook?.results?.comic?.region?.display?:"未知" + val author: Array? get() = mBook?.results?.comic?.author + val theme: Array? get() = mBook?.results?.comic?.theme + val keys get() = mKeys + val imageType: String + get() = when(mBook?.results?.comic?.img_type) { + 1 -> "条漫" + 2 -> "普通" + else -> "未知类型${mBook?.results?.comic?.img_type}" + } + val popular get() = mBook?.results?.comic?.popular?:0 + val status get() = mBook?.results?.comic?.status?.display?:"未知" + val updateTime get() = mBook?.results?.comic?.datetime_updated?:"未知" + val brief get() = mBook?.results?.comic?.brief?:"空简介" + val volumes get() = mVolumes + val uuid get() = mBook?.results?.comic?.uuid + val json get() = mJsonString + + constructor(name: String, getString: (Int) -> String, exDir: File): this( + Gson().fromJson(File(File(exDir, name), "info.json").readText(), Array::class.java).let{ + if (it.isEmpty() || it[0].results.list.isEmpty()) { + throw IllegalArgumentException("$name/info.json无效") + } + it[0].results.list[0].comic_path_word + }, getString, exDir, true, name + ) + + /** + * 更新云端最新图书信息并缓存到本地 + */ + suspend fun updateInfo() = withContext(Dispatchers.IO) { + try { + var isDownload = false + val data: ByteArray = if (loadCache) { + name?.let { loadInfo(it) } ?: run { + isDownload = true + DownloadTools.getHttpContent(mBookApiUrl, null, mUserAgent) + } + } else { + isDownload = true + DownloadTools.getHttpContent(mBookApiUrl, null, mUserAgent) + } + mBook = data.inputStream().use { + Gson().fromJson(it.reader(), BookInfoStructure::class.java) + } + if (isDownload) saveInfo(data) + mGroupPathWords = arrayOf() + mKeys = arrayOf() + mCounts = intArrayOf() + mBook?.results?.groups?.values?.forEach { + mKeys += it.name + mGroupPathWords += it.path_word + if (it.count == 0) { + it.count = 1 + } + mCounts += it.count + Log.d("MyB", "Add caption: ${it.name} @ ${it.path_word} of ${it.count}") + } + } catch (e: Exception) { + e.printStackTrace() + } } -} \ No newline at end of file + + /** + * 更新云端最新章节信息并缓存到本地 + */ + suspend fun updateVolumes(whenFinish: suspend () -> Unit) = withContext(Dispatchers.IO) withIO@ { + var isDownload = false + var volumes = if(loadCache && loadVolumes()) mVolumes else emptyArray() + if(volumes.isEmpty()) { + isDownload = true + mGroupPathWords.forEachIndexed { i, g -> + Volume(path, g, getString) { + return@Volume exit + }.updateChapters(mCounts[i])?.let { volumes += it } + } + } + if (!exit && volumes.size == mGroupPathWords.size) { + if(isDownload) { + saveVolumes(volumes) + mVolumes = volumes + } + whenFinish() + } + } + + private suspend fun saveVolumes(volumes: Array) = withContext(Dispatchers.IO) { + name?.let { name -> + val mangaFolder = File(exDir, name) + if(!mangaFolder.exists()) mangaFolder.mkdirs() + mJsonString = Gson().toJson(volumes) + File(mangaFolder, "info.json").writeText(mJsonString) + File(mangaFolder, "grps.json").writeText(Gson().toJson(mKeys)) + (cover?.let { CMApi.proxy?.wrap(it) } ?:cover)?.let { + DownloadTools.getHttpContent(it, null, mUserAgent) + }?.let { data -> + File(mangaFolder, "head.jpg").writeBytes(data) + } + } + } + + private suspend fun loadVolumes(): Boolean = withContext(Dispatchers.IO) { + name?.let { name -> + val mangaFolder = File(exDir, name) + if(!mangaFolder.exists()) mangaFolder.mkdirs() + val jsonFile = File(mangaFolder, "info.json") + if (!jsonFile.exists()) return@let false + mJsonString = jsonFile.readText() + mVolumes = Gson().fromJson(mJsonString, Array::class.java) + val groupFile = File(mangaFolder, "grps.json") + if (!groupFile.exists()) return@let false + groupFile.inputStream().use { + mKeys = Gson().fromJson(it.reader(), Array::class.java) + } + return@let true + }?:false + } + + private suspend fun saveInfo(data: ByteArray) = withContext(Dispatchers.IO) { + name?.let { name -> + val mangaFolder = File(exDir, name) + if(!mangaFolder.exists()) mangaFolder.mkdirs() + File(mangaFolder, "meta.json").writeBytes(data) + } + } + + private suspend fun loadInfo(name: String): ByteArray? = withContext(Dispatchers.IO) { + val mangaFolder = File(exDir, name) + if(!mangaFolder.exists()) mangaFolder.mkdirs() + val f = File(mangaFolder, "meta.json") + if (!f.exists()) return@withContext null + return@withContext f.readBytes() + } +} diff --git a/app/src/main/java/top/fumiama/copymanga/manga/Volume.kt b/app/src/main/java/top/fumiama/copymanga/manga/Volume.kt new file mode 100644 index 0000000..c703d2c --- /dev/null +++ b/app/src/main/java/top/fumiama/copymanga/manga/Volume.kt @@ -0,0 +1,76 @@ +package top.fumiama.copymanga.manga + +import android.util.Log +import com.google.gson.Gson +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import top.fumiama.copymanga.json.ChapterStructure +import top.fumiama.copymanga.json.VolumeStructure +import top.fumiama.copymanga.template.http.PausableDownloader +import top.fumiama.copymanga.tools.api.CMApi +import top.fumiama.dmzj.copymanga.R + +class Volume(private val path: String, private val groupPathWord: String, getString: (Int) -> String, private val isExit: ()->Boolean) { + private val mGroupInfoApiUrlTemplate = getString(R.string.groupInfoApiUrl) + private val exit: Boolean + get() { + if (!isExit()) return false + // destroy + mDownloaders.forEach { it.exit = true } + return true + } + private var mDownloaders = arrayOf() + private var mVolume: VolumeStructure? = null + suspend fun updateChapters(count: Int): VolumeStructure? = withContext(Dispatchers.IO) { + val times = count / 100 + val remain = count % 100 + val re = arrayOfNulls(if(remain != 0) (times+1) else (times)) + if (re.isEmpty()) return@withContext null + Log.d("MyV", "${groupPathWord}卷共需加载${if(times == 0) 1 else times}次") + download(re, 0, count) + return@withContext mVolume + } + + private fun getApiUrl(offset: Int) = mGroupInfoApiUrlTemplate.format(CMApi.myHostApiUrl, path, groupPathWord, offset) + private suspend fun download(re: Array, offset: Int, c: Int) = withContext(Dispatchers.IO) { + Log.d("MyV", "下载偏移: $offset") + getApiUrl(offset).let { + if (exit) return@withContext + val ad = PausableDownloader(it, whenFinish = whenFinish(re, c-100, offset+100)) + mDownloaders += ad + ad.run() + } + } + private fun whenFinish(re: Array, c: Int, offset: Int): suspend (ByteArray) -> Unit = lambda@ { result: ByteArray -> + try { + val r = Gson().fromJson(result.decodeToString(), VolumeStructure::class.java) + val o = r.results.offset / 100 + re[o] = r + Log.d("MyV", "获得${groupPathWord}卷的${r.results.list.size}章内容, 偏移$o*100=${r.results.offset}, 共${re.size}") + if (c > 0) { + download(re, offset, c) + return@lambda + } + if (re.any { it == null }) { // have uncompleted items + Log.d("MyV", "下载未完成, 存在空卷") + return@lambda + } + if(re.isNotEmpty()) { // safer check, likely + re[0]?.let { + var s = emptyArray() + re.forEach { v -> + v?.results?.list?.forEach { chapter -> + s += chapter + } + } + it.results?.list = s + } + mVolume = re[0] + } + return@lambda + } catch (e: Exception) { + e.printStackTrace() + return@lambda + } + } +} \ No newline at end of file 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 5de469a..c4092cd 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 @@ -71,13 +71,13 @@ open class AutoDownloadHandler( cacheFile?.let { if (it.exists()) { var pass = true - val fi = it.inputStream() - try { - pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass)) - } catch (e: Exception) { - e.printStackTrace() + it.inputStream().use { fi-> + try { + pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass)) + } catch (e: Exception) { + e.printStackTrace() + } } - fi.close() if (pass) return@withContext } } 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 index 99a38d1..29ccdd3 100644 --- a/app/src/main/java/top/fumiama/copymanga/template/http/PausableDownloader.kt +++ b/app/src/main/java/top/fumiama/copymanga/template/http/PausableDownloader.kt @@ -9,16 +9,17 @@ 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) { +class PausableDownloader(private val url: String, private val waitMilliseconds: Long = 0, private val whenFinish: (suspend (result: ByteArray)->Unit)? = null) { var exit = false suspend fun run() = withContext(Dispatchers.IO) { var c = 0 while (!exit && c++ < 3) { try { - whenFinish(DownloadTools.getHttpContent(url, + val data = (DownloadTools.getHttpContent(url, mainWeakReference?.get()?.getString(R.string.referer)!!, mainWeakReference?.get()?.getString(R.string.pc_ua)!! )) + whenFinish?.let { it(data) } break } catch (e: Exception) { e.printStackTrace() diff --git a/app/src/main/java/top/fumiama/copymanga/tools/file/PropertiesTools.kt b/app/src/main/java/top/fumiama/copymanga/tools/file/PropertiesTools.kt index a02d961..7bf9840 100644 --- a/app/src/main/java/top/fumiama/copymanga/tools/file/PropertiesTools.kt +++ b/app/src/main/java/top/fumiama/copymanga/tools/file/PropertiesTools.kt @@ -25,10 +25,10 @@ class PropertiesTools(private val f: File):Properties() { private fun createNew(f: File) { f.createNewFile() - val o = f.outputStream() - this.storeToXML(o, "store") + f.outputStream().use { o -> + this.storeToXML(o, "store") + } Log.d("MyPT", "Generate new prop.") - o.close() } private fun loadFromXml(`in`: InputStream?): PropertiesTools { @@ -44,20 +44,20 @@ class PropertiesTools(private val f: File):Properties() { operator fun get(key: String): String{ return if(cache.containsKey(key)) cache[key]?:"null" else { - val i = f.inputStream() - val re = this.loadFromXml(i).getProperty(key)?:"null" - Log.d("MyPT", "Read $key = $re") - i.close() - cache[key] = re - re + f.inputStream().use { i -> + val re = this.loadFromXml(i).getProperty(key)?:"null" + Log.d("MyPT", "Read $key = $re") + cache[key] = re + re + } } } operator fun set(key: String, value: String) { cache[key] = value - val o = f.outputStream() - this.setProp(key, value).storeToXML(o, "store") + f.outputStream().use { o -> + this.setProp(key, value).storeToXML(o, "store") + } Log.d("MyPT", "Set $key = $value") - o.close() } } 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 3c6060c..0d6545b 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 @@ -8,31 +8,27 @@ 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.app_bar_main.* import kotlinx.android.synthetic.main.card_book.* import kotlinx.android.synthetic.main.fragment_book.* -import kotlinx.android.synthetic.main.line_bookinfo_text.* import kotlinx.android.synthetic.main.line_booktandb.* -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import top.fumiama.copymanga.MainActivity -import top.fumiama.copymanga.json.VolumeStructure +import top.fumiama.copymanga.manga.Book import top.fumiama.copymanga.manga.Reader import top.fumiama.copymanga.template.general.NoBackRefreshFragment import top.fumiama.copymanga.tools.ui.Navigate import top.fumiama.copymanga.ui.comicdl.ComicDlFragment 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 + var book: Book? = null private var mBookHandler: BookHandler? = null + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -40,37 +36,48 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { fbvp?.setPadding(0, 0, 0, navBarHeight) if(isFirstInflate) { - var path = "" arguments?.apply { if (getBoolean("loadJson")) { getString("name")?.let { name -> - activity?.getExternalFilesDir("")?.let { - Gson().fromJson(File(File(it, name), "info.json").readText(), Array::class.java) - }?.apply { - if (isEmpty() || get(0).results.list.isEmpty()) { - findNavController().popBackStack() - return - } - else { - path = get(0).results.list[0].comic_path_word - } + try { + book = Book(name, { + return@Book getString(it) + }, activity?.getExternalFilesDir("")!!) + } catch (e: Exception) { + e.printStackTrace() + Toast.makeText(context, R.string.null_book, Toast.LENGTH_SHORT).show() + findNavController().popBackStack() + return } } } else getString("path").let { - if (it != null) path = it + if (it != null) book = Book(it, { id -> + return@Book getString(id) + }, activity?.getExternalFilesDir("")!!, false) else { findNavController().popBackStack() return } } } - mBookHandler = BookHandler(WeakReference(this), path) - Log.d("MyBF", "read path: $path") + mBookHandler = BookHandler(WeakReference(this)) bookHandler.set(mBookHandler) + lifecycleScope.launch { - withContext(Dispatchers.IO) { - sleep(600) - mBookHandler?.startLoad() + try { + book?.updateInfo() + } catch (e: Exception) { + e.printStackTrace() + Toast.makeText(context, R.string.null_book, Toast.LENGTH_SHORT).show() + findNavController().popBackStack() + return@launch + } + Log.d("MyBF", "read path: ${book?.path}") + for (i in 1..4) { + mBookHandler?.sendEmptyMessageDelayed(i, (100*i).toLong()) + } + book?.updateVolumes { + mBookHandler?.sendEmptyMessage(10) } } } else { @@ -83,7 +90,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { isOnPause = false bookHandler.set(mBookHandler) activity?.apply { - toolbar.title = mBookHandler?.book?.results?.comic?.name + toolbar.title = book?.name } setStartRead() } @@ -95,17 +102,15 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { override fun onDestroy() { super.onDestroy() - mBookHandler?.destroy() - mBookHandler?.ads?.forEach { - it.exit = true - } + mBookHandler?.exit = true + book?.exit = true bookHandler.set(null) } fun setStartRead() { if(mBookHandler?.chapterNames?.isNotEmpty() == true) activity?.apply { - mBookHandler?.book?.results?.comic?.let { comic -> - getPreferences(MODE_PRIVATE).getInt(comic.name, -1).let { p -> + book?.name?.let { name -> + getPreferences(MODE_PRIVATE).getInt(name, -1).let { p -> this@BookFragment.lbbstart.apply { var i = 0 if(p >= 0) { @@ -114,7 +119,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { } setOnClickListener { mBookHandler?.urlArray?.let { - Reader.viewMangaAt(comic.name, i, it) + Reader.viewMangaAt(name, i, it) } } } @@ -127,9 +132,9 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { fun setAddToShelf() { if(mBookHandler?.chapterNames?.isNotEmpty() != true) return lifecycleScope.launch { - MainActivity.shelf?.query(mBookHandler?.path!!)?.let { b -> + MainActivity.shelf?.query(book?.path!!)?.let { b -> mBookHandler?.collect = b.results?.collect?:-2 - Log.d("MyBF", "get collect of ${mBookHandler?.path} = ${mBookHandler?.collect}") + Log.d("MyBF", "get collect of ${book?.path} = ${mBookHandler?.collect}") tic.text = b.results?.browse?.chapter_name?.let { name -> getString(R.string.text_format_cloud_read_to).format(name) } @@ -140,7 +145,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed) } } - mBookHandler?.book?.results?.comic?.let { comic -> + book?.uuid?.let { uuid -> this@BookFragment.lbbsub.setOnClickListener { lifecycleScope.launch clickLaunch@ { if (this@BookFragment.lbbsub.text != getString(R.string.button_sub)) { @@ -154,7 +159,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { } return@clickLaunch } - val re = MainActivity.shelf?.add(comic.uuid) + val re = MainActivity.shelf?.add(uuid) Toast.makeText(context, re, Toast.LENGTH_SHORT).show() if (re == "修改成功") { this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed) @@ -169,13 +174,10 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { 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 && mBookHandler!!.json != null) { - bundle.putString("loadJson", mBookHandler!!.json) + bundle.putString("name", book!!.name!!) + if(book?.volumes != null && book?.json != null) { + bundle.putString("loadJson", book!!.json) } - bundle.putStringArray("group", mBookHandler!!.gpws) - bundle.putStringArray("groupNames", mBookHandler!!.keys) - bundle.putIntArray("count", mBookHandler!!.cnts) findNavController().let { Navigate.safeNavigateTo(it, R.id.action_nav_book_to_nav_group, bundle) } 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 f7e5dbf..701d88e 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 @@ -1,15 +1,14 @@ package top.fumiama.copymanga.ui.book -import android.graphics.Bitmap import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.os.Message import android.util.Log import android.view.View import android.view.ViewGroup import android.widget.LinearLayout 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 @@ -18,7 +17,6 @@ import com.bumptech.glide.Glide import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.request.RequestOptions import com.google.android.material.tabs.TabLayoutMediator -import com.google.gson.Gson import kotlinx.android.synthetic.main.app_bar_main.* import kotlinx.android.synthetic.main.card_book.* import kotlinx.android.synthetic.main.fragment_book.* @@ -32,49 +30,26 @@ 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 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.PausableDownloader import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.copymanga.tools.ui.GlideBlurTransformation import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener import top.fumiama.copymanga.tools.ui.Navigate -import top.fumiama.copymanga.ui.comicdl.ComicDlFragment -import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.json import top.fumiama.copymanga.ui.vm.ViewMangaActivity import top.fumiama.dmzj.copymanga.R -import java.io.File import java.lang.Thread.sleep 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, - th.get()){ +class BookHandler(private val th: WeakReference): Handler(Looper.myLooper()!!) { private val that get() = th.get() - private var hasToastedError = false - get(){ - val re = field - field = true - return re - } - var book: BookInfoStructure? = null private var complete = false - 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 chapterNames = arrayOf() + var collect: Int = -1 var urlArray = arrayOf() + var exit = false override fun handleMessage(msg: Message) { super.handleMessage(msg) @@ -85,44 +60,10 @@ class BookHandler(private val th: WeakReference, val path: String) 3 -> setAuthorsAndTags() 6 -> if(complete) that?.navigate2dl() 9 -> endSetLayouts() + 10 -> setVolumes() } } - override fun onError() { - super.onError() - if(exit) return - if(!hasToastedError) /*that?.activity?.runOnUiThread*/ { - Toast.makeText(that?.context, R.string.null_book, Toast.LENGTH_SHORT).show() - that?.apply { findNavController().popBackStack() } - } - } - - override fun setGsonItem(gsonObj: Any): Boolean { - val pass = super.setGsonItem(gsonObj) - book = gsonObj as BookInfoStructure - return pass - } - - override fun getGsonItem() = book - override suspend fun doWhenFinishDownload() = withContext(Dispatchers.IO) { - super.doWhenFinishDownload() - if(exit) return@withContext - - if(keys.isEmpty()) book?.results?.groups?.values?.forEach { - keys += it.name - gpws += it.path_word - if (it.count == 0) { - it.count = 1 - } - cnts += it.count - Log.d("MyBFH", "Add caption: ${it.name} @ ${it.path_word} of ${it.count}") - } - for (i in 1..4) { - sendEmptyMessageDelayed(i, (100*i).toLong()) - } - if(vols?.isEmpty() != false) initComicData() - } - private fun endSetLayouts() { if (exit) return that?.fbloading?.apply { @@ -138,50 +79,36 @@ class BookHandler(private val th: WeakReference, val path: String) private fun setCover() { if (exit) return that?.apply { - book?.results?.comic?.cover?.let { cover -> - val load = Glide.with(this).load( - GlideUrl(CMApi.proxy?.wrap(cover)?:cover, CMApi.myGlideHeaders) - ).timeout(10000).addListener(GlideHideLottieViewListener(WeakReference(laic))) - load.into(imic) - context?.let { it1 -> GlideBlurTransformation(it1) } - ?.let { it2 -> RequestOptions.bitmapTransform(it2) } - ?.let { it3 -> load.apply(it3).into(lbibg) } - } + val load = Glide.with(this).load( + if (book?.cover != null) + GlideUrl(CMApi.proxy?.wrap(book?.cover!!)?:book?.cover!!, CMApi.myGlideHeaders) + else book?.cachedCover + ).timeout(10000).addListener(GlideHideLottieViewListener(WeakReference(laic))) + load.into(imic) + context?.let { it1 -> GlideBlurTransformation(it1) } + ?.let { it2 -> RequestOptions.bitmapTransform(it2) } + ?.let { it3 -> load.apply(it3).into(lbibg) } //imf?.visibility = View.GONE //fbl?.addView(divider) } } - /*private fun getThemeSeq(authors: Array): CharSequence{ - var re = "" - for(author in authors) re += author.name + ' ' - return re - }*/ - private fun setTexts() { if (exit) return - //that?.tic?.text = book?.name - //that?.tic?.visibility = View.GONE - mainWeakReference?.get()?.toolbar?.title = book?.results?.comic?.name - that?.btauth?.text = that?.getString(R.string.text_format_region)?.format( - book?.results?.comic?.region?.display - ) - that?.bttag?.text = that?.getString(R.string.text_format_img_type)?.format(when(book?.results?.comic?.img_type) { - 1 -> "条漫" - 2 -> "普通" - else -> "未知类型${book?.results?.comic?.img_type}" - }) - that?.bthit?.text = that?.getString(R.string.text_format_hit)?.format( - book?.results?.comic?.popular - ) - that?.btsub?.text = that?.getString(R.string.text_format_stat)?.format( - book?.results?.comic?.status?.display - ) - that?.bttime?.text = book?.results?.comic?.datetime_updated - val v = that?.layoutInflater?.inflate(R.layout.line_text_info, that?.lbl, false) - (v as TextView).text = book?.results?.comic?.brief - that?.lbl?.addView(v) - that?.lbl?.addView(divider) + that?.apply { + // tic?.text = book?.name + // tic?.visibility = View.GONE + mainWeakReference?.get()?.toolbar?.title = book?.name + btauth?.text = that?.getString(R.string.text_format_region)?.format(book?.region?:"未知") + bttag?.text = that?.getString(R.string.text_format_img_type)?.format(book?.imageType?:"未知") + bthit?.text = that?.getString(R.string.text_format_hit)?.format(book?.popular?:-1) + btsub?.text = that?.getString(R.string.text_format_stat)?.format(book?.status?:"未知") + bttime?.text = book?.updateTime?:"未知" + val v = layoutInflater.inflate(R.layout.line_text_info, lbl, false) + (v as TextView).text = book?.brief + lbl?.addView(v) + lbl?.addView(divider) + } } private fun setTheme(caption: String, themeStructure: Array, nav: Int) { @@ -230,7 +157,7 @@ class BookHandler(private val th: WeakReference, val path: String) private fun setAuthorsAndTags() { if (exit) return that?.apply { - book?.results?.comic?.apply { + book?.apply { author?.let { setTheme( getString(R.string.author), @@ -257,21 +184,22 @@ class BookHandler(private val th: WeakReference, val path: String) private suspend fun setVolume(fbl: LinearLayout, p: Int) = withContext(Dispatchers.IO) { if (exit) return@withContext that?.apply { - book?.results?.apply { + book?.apply { var i = 0 for (j in 0 until p) { - i += vols?.get(j)?.results?.list?.size?:0 + i += volumes[j].results?.list?.size?:0 } var last = i-1 - vols?.get(p)?.let { v -> + val comicName = name?:return@withContext + volumes[p].let { v -> 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) + val f = CMApi.getZipFile(context?.getExternalFilesDir(""), comicName, keys[p], it.name) //Log.d("MyBH", "i = $i, last=$last, add chapter ${it.name}, line is null: ${line == null}") that?.isOnPause?.let { isOnPause -> - while (isOnPause && !exit) sleep(100) + while (isOnPause && !exit) sleep(500) if (exit) return@withContext }?:return@withContext if(line == null) { @@ -282,7 +210,7 @@ class BookHandler(private val th: WeakReference, val path: String) if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success) Log.d("MyBH", "add last single chapter ${it.name}") val index = i - setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) } + setOnClickListener { Reader.viewMangaAt(comicName, index, urlArray) } } line?.let { l -> addVolumesView(fbl, l) } } else { @@ -291,14 +219,14 @@ class BookHandler(private val th: WeakReference, val path: String) lct.text = it.name if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success) val index = i - setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) } + setOnClickListener { Reader.viewMangaAt(comicName, index, urlArray) } } } } else line?.l2cr?.apply { lct.text = it.name if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success) val index = i - setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) } + setOnClickListener { Reader.viewMangaAt(comicName, index, urlArray) } line?.let { l -> addVolumesView(fbl, l) } line = null } @@ -312,26 +240,27 @@ class BookHandler(private val th: WeakReference, val path: String) private suspend fun setViewManga() = withContext(Dispatchers.IO) { if (exit) return@withContext that?.apply { - book?.results?.apply { + book?.apply { + val comicName = name?:return@withContext ViewMangaActivity.fileArray = arrayOf() urlArray = arrayOf() ViewMangaActivity.uuidArray = arrayOf() var i = 0 var last = -1 - vols?.forEachIndexed { groupIndex, v -> + volumes.forEachIndexed { groupIndex, v -> if(exit) return@withContext last += v.results.list.size v.results.list.forEach { urlArray += CMApi.getChapterInfoApiUrl( - comic.path_word, + path, it.uuid )?:"" - val f = CMApi.getZipFile(context?.getExternalFilesDir(""), comic.name, keys[groupIndex], it.name) + val f = CMApi.getZipFile(context?.getExternalFilesDir(""), comicName, keys[groupIndex], it.name) ViewMangaActivity.fileArray += f chapterNames += it.name ViewMangaActivity.uuidArray += it.uuid that?.isOnPause?.let { isOnPause -> - while (isOnPause && !exit) sleep(100) + while (isOnPause && !exit) sleep(500) if (exit) return@withContext }?:return@withContext i++ @@ -352,120 +281,20 @@ class BookHandler(private val th: WeakReference, val path: String) } } - private suspend fun initComicData() = withContext(Dispatchers.IO) withIO@ { - var volumes = emptyArray() - val counts = cnts.clone() - gpws.forEachIndexed { i, gpw -> - Log.d("MyBFH", "下载:$gpw") - var offset = 0 - val times = counts[i] / 100 - val remain = counts[i] % 100 - val re = arrayOfNulls(if(remain != 0) (times+1) else (times)) - 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@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() - } - } - 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(100) - if(ComicDlFragment.exit) return@withIO - Log.d("MyBFH", "已有:${volumes.size} 共:${gpws.size}") - c++ - } - if (volumes.size == gpws.size) { - saveVolumes(volumes) - that?.fbtab?.let { tab -> - that?.fbvp?.let { vp -> - withContext(Dispatchers.Main) { - vp.adapter = ViewData(vp).RecyclerViewAdapter() - TabLayoutMediator(tab, vp) { t, p -> - t.text = keys[p] - }.attach() - } + private fun setVolumes() { + that?.apply { + fbtab?.let { tab -> + fbvp?.let { vp -> + vp.adapter = ViewData(vp).RecyclerViewAdapter() + TabLayoutMediator(tab, vp) { t, p -> + t.text = book?.keys?.get(p) + }.attach() } } - setViewManga() + lifecycleScope.launch { setViewManga() } } } - 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) - if(!mangaFolder.exists()) mangaFolder.mkdirs() - json = Gson().toJson(volumes) - File(mangaFolder, "info.json").writeText(json!!) - File(mangaFolder, "grps.json").writeText(Gson().toJson(keys)) - that?.apply { - 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) withContext(Dispatchers.Main) { - Toast.makeText(that?.context, R.string.download_cover_timeout, Toast.LENGTH_SHORT).show() - } - } - } - } - vols = volumes - } - inner class ViewData(itemView: View): RecyclerView.ViewHolder(itemView) { inner class RecyclerViewAdapter: RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewData { @@ -476,7 +305,7 @@ class BookHandler(private val th: WeakReference, val path: String) that?.lifecycleScope?.launch { setVolume(holder.itemView.fbl, position) } } - override fun getItemCount(): Int = keys.size + override fun getItemCount(): Int = that?.book?.keys?.size?:0 } } } diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt index cbc24f4..e39e4b1 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt @@ -1,9 +1,13 @@ package top.fumiama.copymanga.ui.cardflow.rank import android.os.Bundle +import androidx.lifecycle.lifecycleScope import com.google.android.material.tabs.TabLayout import kotlinx.android.synthetic.main.fragment_rank.* import kotlinx.android.synthetic.main.line_rank.view.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import top.fumiama.copymanga.template.ui.InfoCardLoader import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.copymanga.tools.ui.UITools @@ -17,6 +21,7 @@ class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank private var sortValue = 0 private val audienceWay = listOf("", "male", "female") private var audience = 0 // 0 all 1 male 2 female + private var isLoading = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -39,6 +44,11 @@ class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank ad?.exit = true } + override fun onLoadFinish() { + super.onLoadFinish() + isLoading = false + } + override fun getApiUrl() = getString(R.string.rankApiUrl).format( CMApi.myHostApiUrl, @@ -53,48 +63,45 @@ class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank override fun onTabReselected(tab: TabLayout.Tab?) {} override fun onTabSelected(tab: TabLayout.Tab?) { - setSortValue(tab?.position?:0) + sortValue = tab?.position?:0 + if(!isLoading) delayedRefresh() } override fun onTabUnselected(tab: TabLayout.Tab?) {} }) } - private fun setSortValue(value: Int) { - sortValue = value - Thread{ - sleep(400) - if(ad?.exit != true) activity?.runOnUiThread { - reset() - addPage() + private fun delayedRefresh() { + lifecycleScope.launch { + isLoading = true + withContext(Dispatchers.IO) { + sleep(400) + withContext(Dispatchers.Main) { + reset() + addPage() + } } - }.start() + } } fun showSexInfo(toolsBox: UITools) { if (ad?.exit != false) return toolsBox.buildInfo("切换类型", "选择一种想筛选的漫画类型", "男频", "全部", "女频", { - audience = 1 - reset() - Thread { - sleep(600) - addPage() - }.start() + if(!isLoading) { + audience = 1 + delayedRefresh() + } }, { - audience = 0 - reset() - Thread { - sleep(600) - addPage() - }.start() + if(!isLoading) { + audience = 0 + delayedRefresh() + } }, { - audience = 2 - reset() - Thread { - sleep(600) - addPage() - }.start() + if(!isLoading) { + audience = 2 + delayedRefresh() + } }) } 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 8e61dc6..8ffb47a 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 @@ -45,7 +45,9 @@ class SortFragment : StatusCardFlow(0, R.id.action_nav_sort_to_nav_book, R.layou 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) + it.inputStream().use { i -> + filter = Gson().fromJson(i.reader(), FilterStructure::class.java) + } if(ad?.exit == true) return@PausableDownloader withContext(Dispatchers.Main) { if(ad?.exit != true) setClasses() 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 3b24688..64d5af0 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 @@ -26,18 +26,16 @@ class TopicFragment : InfoCardLoader(R.layout.fragment_topic, R.id.action_nav_to 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() + data.inputStream().use { i -> + val r = i.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 - } + withContext(Dispatchers.Main) withMain@ { + if(ad?.exit == true) return@withMain + activity?.toolbar?.title = results.title + ftttime.text = results.datetime_created + fttintro.text = results.intro + type = results.type } } } 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 448e092..94131f6 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,7 +4,6 @@ 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 @@ -12,11 +11,8 @@ 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.PausableDownloader import top.fumiama.copymanga.template.general.NoBackRefreshFragment -import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.dmzj.copymanga.R import java.io.File import java.lang.Thread.sleep @@ -24,7 +20,6 @@ import java.lang.ref.WeakReference class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) { var ltbtn: View? = null - private var ads = emptyArray() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) exit = false @@ -43,11 +38,7 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) { ) } } - else -> initComicData( - arguments?.getString("path"), - arguments?.getStringArray("group"), - arguments?.getIntArray("count") - ) + else -> findNavController().popBackStack() } } } @@ -59,9 +50,6 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) { handler?.downloading = false handler?.mangaDlTools?.exit = true handler?.dl?.dismiss() - ads.forEach { - it.exit = true - } exit = true handler = null } @@ -91,73 +79,6 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) { 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 suspend fun initComicData(pw: String?, gpws: Array?, counts: IntArray?) = withContext(Dispatchers.IO) { - var volumes = emptyArray() - if (gpws != null) { - gpws.forEachIndexed { i, gpw -> - Log.d("MyCDF", "下载:$gpw") - var offset = 0 - val times = (counts?.get(i)?:1) / 100 - val remain = (counts?.get(i)?:1) % 100 - val re = arrayOfNulls(if(remain != 0) (times+1) else (times)) - Log.d("MyCDF", "${i}卷共${if(times == 0) 1 else times}次加载") - do { - counts?.set(i, counts[i] - 100) - CMApi.getGroupInfoApiUrl(pw, gpw, offset)?.let { - if(exit) return@withContext - val ad = PausableDownloader(it) { result -> - Log.d("MyCDF", "第${i}卷返回") - val r = Gson().fromJson(result.decodeToString(), VolumeStructure::class.java) - re[r.results.offset / 100] = r - } - ads += ad - 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@withContext - if(re.all { it != null }) break - } - if(re.size > 1) { - val r = re[0] - var s = emptyArray() - re.forEach { - it?.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(exit) return@withContext - Log.d("MyCDF", "已有:${volumes.size} 共:${gpws.size}") - c++ - } - if (volumes.size == gpws.size) { - start2load(volumes) - } - } - } - private suspend fun initOldComicData() = withContext(Dispatchers.IO) { handler = ComicDlHandler(Looper.myLooper()!!, WeakReference(this@ComicDlFragment), arguments?.getString("name")?:"null") diff --git a/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt index 0a1a48e..a7b605f 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt @@ -122,7 +122,7 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl Navigate.safeNavigateTo(findNavController(), R.id.action_nav_new_download_to_nav_download) return@setOnClickListener } - callDownloadFragment(name) + callBookFragment(name) } v.setOnLongClickListener { if (name == oldDlCardName && path == oldDlCardName) { @@ -132,7 +132,7 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl AlertDialog.Builder(context) .setIcon(R.drawable.ic_launcher_foreground) .setTitle(R.string.new_download_card_option_hint) - .setItems(arrayOf("删除数据", "前往详情")) { d, p -> + .setItems(arrayOf("删除数据文件夹", "直接前往下载页")) { d, p -> d.cancel() when (p) { 0 -> { @@ -150,12 +150,7 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl }.setNegativeButton(android.R.string.cancel) { _, _ -> } .show() } - 1 -> { - val bundle = Bundle() - bundle.putBoolean("loadJson", true) - bundle.putString("name", name) - Navigate.safeNavigateTo(findNavController(), R.id.action_nav_new_download_to_nav_book, bundle) - } + 1 -> callDownloadFragment(name) } } .show() @@ -166,7 +161,14 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl } } - private fun callDownloadFragment(name: String){ + private fun callBookFragment(name: String) { + val bundle = Bundle() + bundle.putBoolean("loadJson", true) + bundle.putString("name", name) + Navigate.safeNavigateTo(findNavController(), R.id.action_nav_new_download_to_nav_book, bundle) + } + + private fun callDownloadFragment(name: String) { val bundle = Bundle() Log.d("MyNDF", "Call dl and is new.") bundle.putString("loadJson", File(File(extDir, name), "info.json").readText()) 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 40b07fc..5c15e26 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 @@ -14,6 +14,7 @@ import kotlinx.android.synthetic.main.widget_infodrawer.view.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import top.fumiama.copymanga.MainActivity import top.fumiama.copymanga.json.Chapter2Return import top.fumiama.copymanga.json.ChapterWithContent import top.fumiama.copymanga.json.ComicStructure @@ -191,8 +192,8 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri } } - private suspend fun fakeLoad() { - PausableDownloader(chapterUrl) { _ -> }.run() + private suspend fun fakeLoad() = withContext(Dispatchers.IO) { + if(MainActivity.member?.hasLogin == true) PausableDownloader(chapterUrl) { _ -> }.run() } private suspend fun prepareManga() = withContext(Dispatchers.Main) { 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 0414ebe..56c6d24 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 @@ -301,7 +301,9 @@ class ViewMangaActivity : TitleActivityTemplate() { private fun canCut(inputStream: InputStream): Boolean{ val op = BitmapFactory.Options() op.inJustDecodeBounds = true - BitmapFactory.decodeStream(inputStream, null, op) + inputStream.use { + BitmapFactory.decodeStream(it, null, op) + } Log.d("MyVM", "w: ${op.outWidth}, h: ${op.outHeight}") return op.outWidth.toFloat() / op.outHeight.toFloat() > 1 } @@ -437,13 +439,18 @@ class ViewMangaActivity : TitleActivityTemplate() { loadImg(imgView, it, useCut, isLeft, false) } else { - loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true) val sleepTime = loadImgOnWait.getAndIncrement().toLong()*200 Log.d("MyVM", "loadImgOn sleep: $sleepTime ms") val re = tasks?.get(index2load) - if (sleepTime > 0 && re?.isDone != true) Thread.sleep(sleepTime) + if (sleepTime > 0 && re?.isDone != true) { + loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true) + Thread.sleep(sleepTime) + } if (re != null) { - if(!re.isDone) re.run() + if(!re.isDone) { + loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true) + re.run() + } val data = re.get() if(data != null && data.isNotEmpty()) { BitmapFactory.decodeByteArray(data, 0, data.size)?.let { @@ -451,9 +458,15 @@ class ViewMangaActivity : TitleActivityTemplate() { Log.d("MyVM", "Load position $position from task") }?:Log.d("MyVM", "null bitmap at $position") } - else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, useCut, isLeft) } + else getImgUrl(index2load)?.let { + loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true) + loadImgUrlInto(imgView, it, useCut, isLeft) + } + } + else getImgUrl(index2load)?.let { + loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true) + loadImgUrlInto(imgView, it, useCut, isLeft) } - else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, useCut, isLeft) } loadImgOnWait.decrementAndGet() tasks?.apply { if (index2load >= size) return@apply @@ -484,7 +497,9 @@ class ViewMangaActivity : TitleActivityTemplate() { } } } - imgView.visibility = View.VISIBLE + withContext(Dispatchers.Main) { + if(imgView.visibility != View.VISIBLE) imgView.visibility = View.VISIBLE + } } private fun loadOneImg() { @@ -515,11 +530,16 @@ class ViewMangaActivity : TitleActivityTemplate() { for (i in 0..1) { val ext = if((i == 0 && tryWebpFirst) || (i == 1 && !tryWebpFirst)) "webp" else "jpg" bitmap = try { - if (q == 100) BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.$ext"))) - else { - val out = ByteArrayOutputStream() - BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.$ext")))?.compress(Bitmap.CompressFormat.JPEG, q, out) - BitmapFactory.decodeStream(ByteArrayInputStream(out.toByteArray())) + zip.getInputStream(zip.getEntry("${position}.$ext"))?.use { zipInputStream -> + if (q == 100) BitmapFactory.decodeStream(zipInputStream) + else { + ByteArrayOutputStream().use { out -> + BitmapFactory.decodeStream(zipInputStream)?.compress(Bitmap.CompressFormat.JPEG, q, out) + ByteArrayInputStream(out.toByteArray()).use { i -> + BitmapFactory.decodeStream(i) + } + } + } } } catch (e: Exception) { if (i == 1) { 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 244d4eb..69e11da 100644 --- a/app/src/main/java/top/fumiama/copymanga/user/Member.kt +++ b/app/src/main/java/top/fumiama/copymanga/user/Member.kt @@ -73,7 +73,7 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) - } catch (e: Exception) { val l = LoginInfoStructure() l.code = 450 - l.message = "${getString(R.string.login_get_avatar_failed)}: ${e.localizedMessage}" + l.message = "${getString(R.string.login_get_avatar_failed)}: $e" l } }