mirror of
https://github.com/fumiama/copymanga.git
synced 2026-06-10 10:24:29 +08:00
v2.2.1
新增 1. 支持无网络时从下载访问漫画详情页 修复 1. 保存封面超时时间过短 2. 竖向翻页回到上一段闪退 3. 无动画阅览时加载下一页闪屏 4. 排行页快速点击tab时显示错乱 优化 1. 我的下载页的无用代码
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String>()
|
||||
private var mKeys = arrayOf<String>()
|
||||
private var mCounts = intArrayOf()
|
||||
private var mVolumes = arrayOf<VolumeStructure>()
|
||||
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<ThemeStructure>? get() = mBook?.results?.comic?.author
|
||||
val theme: Array<ThemeStructure>? 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<VolumeStructure>::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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新云端最新章节信息并缓存到本地
|
||||
*/
|
||||
suspend fun updateVolumes(whenFinish: suspend () -> Unit) = withContext(Dispatchers.IO) withIO@ {
|
||||
var isDownload = false
|
||||
var volumes = if(loadCache && loadVolumes()) mVolumes else emptyArray<VolumeStructure>()
|
||||
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<VolumeStructure>) = 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<VolumeStructure>::class.java)
|
||||
val groupFile = File(mangaFolder, "grps.json")
|
||||
if (!groupFile.exists()) return@let false
|
||||
groupFile.inputStream().use {
|
||||
mKeys = Gson().fromJson(it.reader(), Array<String>::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()
|
||||
}
|
||||
}
|
||||
|
||||
76
app/src/main/java/top/fumiama/copymanga/manga/Volume.kt
Normal file
76
app/src/main/java/top/fumiama/copymanga/manga/Volume.kt
Normal file
@@ -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<PausableDownloader>()
|
||||
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<VolumeStructure>(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<VolumeStructure?>, 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<VolumeStructure?>, 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<ChapterStructure>()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<VolumeStructure>::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)
|
||||
}
|
||||
|
||||
@@ -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<BookFragment>, 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<BookFragment>): 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<PausableDownloader>()
|
||||
var gpws = arrayOf<String>()
|
||||
var keys = arrayOf<String>()
|
||||
var cnts = intArrayOf()
|
||||
var vols: Array<VolumeStructure>? = null
|
||||
var chapterNames = arrayOf<String>()
|
||||
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<String>()
|
||||
var collect: Int = -1
|
||||
var urlArray = arrayOf<String>()
|
||||
var exit = false
|
||||
|
||||
override fun handleMessage(msg: Message) {
|
||||
super.handleMessage(msg)
|
||||
@@ -85,44 +60,10 @@ class BookHandler(private val th: WeakReference<BookFragment>, 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<BookFragment>, 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<ThemeStructure>): 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<ThemeStructure>, nav: Int) {
|
||||
@@ -230,7 +157,7 @@ class BookHandler(private val th: WeakReference<BookFragment>, 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<BookFragment>, 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<BookFragment>, 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<BookFragment>, 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<BookFragment>, 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<BookFragment>, val path: String)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun initComicData() = withContext(Dispatchers.IO) withIO@ {
|
||||
var volumes = emptyArray<VolumeStructure>()
|
||||
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<VolumeStructure>(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<ChapterStructure>()
|
||||
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<VolumeStructure>) = 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<ViewData>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewData {
|
||||
@@ -476,7 +305,7 @@ class BookHandler(private val th: WeakReference<BookFragment>, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PausableDownloader>()
|
||||
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<VolumeStructure>::class.java)
|
||||
private fun loadGroupsFromFile(file: File) = Gson().fromJson(file.reader(), Array<String>::class.java)
|
||||
|
||||
/*private fun setMenuInvisible(menu: Menu){
|
||||
menu.findItem(R.id.action_download)?.isVisible = false
|
||||
}*/
|
||||
|
||||
private suspend fun initComicData(pw: String?, gpws: Array<String>?, counts: IntArray?) = withContext(Dispatchers.IO) {
|
||||
var volumes = emptyArray<VolumeStructure>()
|
||||
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<VolumeStructure>(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<ChapterStructure>()
|
||||
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")
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user