mirror of
https://github.com/fumiama/copymanga.git
synced 2026-06-21 09:50:23 +08:00
v2.2.1
新增 1. 支持无网络时从下载访问漫画详情页 修复 1. 保存封面超时时间过短 2. 竖向翻页回到上一段闪退 3. 无动画阅览时加载下一页闪屏 4. 排行页快速点击tab时显示错乱 优化 1. 我的下载页的无用代码
This commit is contained in:
2
.idea/dictionaries/fumiama.xml
generated
2
.idea/dictionaries/fumiama.xml
generated
@@ -1,6 +1,8 @@
|
|||||||
<component name="ProjectDictionaryState">
|
<component name="ProjectDictionaryState">
|
||||||
<dictionary name="fumiama">
|
<dictionary name="fumiama">
|
||||||
<words>
|
<words>
|
||||||
|
<w>downloaders</w>
|
||||||
|
<w>grps</w>
|
||||||
<w>imgs</w>
|
<w>imgs</w>
|
||||||
<w>lowpan</w>
|
<w>lowpan</w>
|
||||||
<w>mangafuna</w>
|
<w>mangafuna</w>
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ android {
|
|||||||
applicationId 'top.fumiama.copymanga'
|
applicationId 'top.fumiama.copymanga'
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode 47
|
versionCode 48
|
||||||
versionName '2.2.0'
|
versionName '2.2.1'
|
||||||
resourceConfigurations += ['zh', 'zh-rCN']
|
resourceConfigurations += ['zh', 'zh-rCN']
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|||||||
@@ -287,16 +287,15 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun saveFile(uri: Uri) {
|
private fun saveFile(uri: Uri) {
|
||||||
//val f = File(getExternalFilesDir(""), "headPic")
|
contentResolver.openFileDescriptor(uri, "r")?.use {
|
||||||
val fd = contentResolver.openFileDescriptor(uri, "r")
|
it.fileDescriptor?.let { fd ->
|
||||||
fd?.fileDescriptor?.let {
|
FileInputStream(fd).use { fi ->
|
||||||
val fi = FileInputStream(it)
|
headPic.outputStream().use { fo ->
|
||||||
val fo = headPic.outputStream()
|
fi.copyTo(fo)
|
||||||
fi.copyTo(fo)
|
}
|
||||||
fi.close()
|
}
|
||||||
fo.close()
|
}
|
||||||
}
|
}
|
||||||
fd?.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkHeadPicture() {
|
private fun checkHeadPicture() {
|
||||||
@@ -306,9 +305,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private var cropLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
private var cropLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
val fi = headPic.inputStream()
|
headPic.inputStream().use { fi ->
|
||||||
navhbg.setImageBitmap(BitmapFactory.decodeStream(fi))
|
navhbg.setImageBitmap(BitmapFactory.decodeStream(fi))
|
||||||
fi.close()
|
}
|
||||||
} else Toast.makeText(this, R.string.err_crop_img, Toast.LENGTH_SHORT).show()
|
} else Toast.makeText(this, R.string.err_crop_img, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,169 @@
|
|||||||
package top.fumiama.copymanga.manga
|
package top.fumiama.copymanga.manga
|
||||||
|
|
||||||
class Book(val pathWord: String, val readLocally: Boolean = false) {
|
import android.util.Log
|
||||||
/**
|
import com.google.gson.Gson
|
||||||
* 更新云端最新信息
|
import kotlinx.android.synthetic.main.card_book.*
|
||||||
*/
|
import kotlinx.android.synthetic.main.line_booktandb.*
|
||||||
suspend fun update() {
|
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 {
|
cacheFile?.let {
|
||||||
if (it.exists()) {
|
if (it.exists()) {
|
||||||
var pass = true
|
var pass = true
|
||||||
val fi = it.inputStream()
|
it.inputStream().use { fi->
|
||||||
try {
|
try {
|
||||||
pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass))
|
pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fi.close()
|
|
||||||
if (pass) return@withContext
|
if (pass) return@withContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,17 @@ import top.fumiama.dmzj.copymanga.R
|
|||||||
import java.lang.Thread.sleep
|
import java.lang.Thread.sleep
|
||||||
import kotlin.random.Random
|
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
|
var exit = false
|
||||||
suspend fun run() = withContext(Dispatchers.IO) {
|
suspend fun run() = withContext(Dispatchers.IO) {
|
||||||
var c = 0
|
var c = 0
|
||||||
while (!exit && c++ < 3) {
|
while (!exit && c++ < 3) {
|
||||||
try {
|
try {
|
||||||
whenFinish(DownloadTools.getHttpContent(url,
|
val data = (DownloadTools.getHttpContent(url,
|
||||||
mainWeakReference?.get()?.getString(R.string.referer)!!,
|
mainWeakReference?.get()?.getString(R.string.referer)!!,
|
||||||
mainWeakReference?.get()?.getString(R.string.pc_ua)!!
|
mainWeakReference?.get()?.getString(R.string.pc_ua)!!
|
||||||
))
|
))
|
||||||
|
whenFinish?.let { it(data) }
|
||||||
break
|
break
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ class PropertiesTools(private val f: File):Properties() {
|
|||||||
|
|
||||||
private fun createNew(f: File) {
|
private fun createNew(f: File) {
|
||||||
f.createNewFile()
|
f.createNewFile()
|
||||||
val o = f.outputStream()
|
f.outputStream().use { o ->
|
||||||
this.storeToXML(o, "store")
|
this.storeToXML(o, "store")
|
||||||
|
}
|
||||||
Log.d("MyPT", "Generate new prop.")
|
Log.d("MyPT", "Generate new prop.")
|
||||||
o.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFromXml(`in`: InputStream?): PropertiesTools {
|
private fun loadFromXml(`in`: InputStream?): PropertiesTools {
|
||||||
@@ -44,20 +44,20 @@ class PropertiesTools(private val f: File):Properties() {
|
|||||||
operator fun get(key: String): String{
|
operator fun get(key: String): String{
|
||||||
return if(cache.containsKey(key)) cache[key]?:"null"
|
return if(cache.containsKey(key)) cache[key]?:"null"
|
||||||
else {
|
else {
|
||||||
val i = f.inputStream()
|
f.inputStream().use { i ->
|
||||||
val re = this.loadFromXml(i).getProperty(key)?:"null"
|
val re = this.loadFromXml(i).getProperty(key)?:"null"
|
||||||
Log.d("MyPT", "Read $key = $re")
|
Log.d("MyPT", "Read $key = $re")
|
||||||
i.close()
|
cache[key] = re
|
||||||
cache[key] = re
|
re
|
||||||
re
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun set(key: String, value: String) {
|
operator fun set(key: String, value: String) {
|
||||||
cache[key] = value
|
cache[key] = value
|
||||||
val o = f.outputStream()
|
f.outputStream().use { o ->
|
||||||
this.setProp(key, value).storeToXML(o, "store")
|
this.setProp(key, value).storeToXML(o, "store")
|
||||||
|
}
|
||||||
Log.d("MyPT", "Set $key = $value")
|
Log.d("MyPT", "Set $key = $value")
|
||||||
o.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,31 +8,27 @@ import android.view.View
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.gson.Gson
|
|
||||||
import kotlinx.android.synthetic.main.app_bar_main.*
|
import kotlinx.android.synthetic.main.app_bar_main.*
|
||||||
import kotlinx.android.synthetic.main.card_book.*
|
import kotlinx.android.synthetic.main.card_book.*
|
||||||
import kotlinx.android.synthetic.main.fragment_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.android.synthetic.main.line_booktandb.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import top.fumiama.copymanga.MainActivity
|
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.manga.Reader
|
||||||
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
|
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
|
||||||
import top.fumiama.copymanga.tools.ui.Navigate
|
import top.fumiama.copymanga.tools.ui.Navigate
|
||||||
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
|
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
|
||||||
import top.fumiama.dmzj.copymanga.R
|
import top.fumiama.dmzj.copymanga.R
|
||||||
import java.io.File
|
|
||||||
import java.lang.Thread.sleep
|
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
||||||
var isOnPause = false
|
var isOnPause = false
|
||||||
|
var book: Book? = null
|
||||||
private var mBookHandler: BookHandler? = null
|
private var mBookHandler: BookHandler? = null
|
||||||
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
@@ -40,37 +36,48 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
|||||||
fbvp?.setPadding(0, 0, 0, navBarHeight)
|
fbvp?.setPadding(0, 0, 0, navBarHeight)
|
||||||
|
|
||||||
if(isFirstInflate) {
|
if(isFirstInflate) {
|
||||||
var path = ""
|
|
||||||
arguments?.apply {
|
arguments?.apply {
|
||||||
if (getBoolean("loadJson")) {
|
if (getBoolean("loadJson")) {
|
||||||
getString("name")?.let { name ->
|
getString("name")?.let { name ->
|
||||||
activity?.getExternalFilesDir("")?.let {
|
try {
|
||||||
Gson().fromJson(File(File(it, name), "info.json").readText(), Array<VolumeStructure>::class.java)
|
book = Book(name, {
|
||||||
}?.apply {
|
return@Book getString(it)
|
||||||
if (isEmpty() || get(0).results.list.isEmpty()) {
|
}, activity?.getExternalFilesDir("")!!)
|
||||||
findNavController().popBackStack()
|
} catch (e: Exception) {
|
||||||
return
|
e.printStackTrace()
|
||||||
}
|
Toast.makeText(context, R.string.null_book, Toast.LENGTH_SHORT).show()
|
||||||
else {
|
findNavController().popBackStack()
|
||||||
path = get(0).results.list[0].comic_path_word
|
return
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else getString("path").let {
|
} else getString("path").let {
|
||||||
if (it != null) path = it
|
if (it != null) book = Book(it, { id ->
|
||||||
|
return@Book getString(id)
|
||||||
|
}, activity?.getExternalFilesDir("")!!, false)
|
||||||
else {
|
else {
|
||||||
findNavController().popBackStack()
|
findNavController().popBackStack()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mBookHandler = BookHandler(WeakReference(this), path)
|
mBookHandler = BookHandler(WeakReference(this))
|
||||||
Log.d("MyBF", "read path: $path")
|
|
||||||
bookHandler.set(mBookHandler)
|
bookHandler.set(mBookHandler)
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
try {
|
||||||
sleep(600)
|
book?.updateInfo()
|
||||||
mBookHandler?.startLoad()
|
} 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 {
|
} else {
|
||||||
@@ -83,7 +90,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
|||||||
isOnPause = false
|
isOnPause = false
|
||||||
bookHandler.set(mBookHandler)
|
bookHandler.set(mBookHandler)
|
||||||
activity?.apply {
|
activity?.apply {
|
||||||
toolbar.title = mBookHandler?.book?.results?.comic?.name
|
toolbar.title = book?.name
|
||||||
}
|
}
|
||||||
setStartRead()
|
setStartRead()
|
||||||
}
|
}
|
||||||
@@ -95,17 +102,15 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
mBookHandler?.destroy()
|
mBookHandler?.exit = true
|
||||||
mBookHandler?.ads?.forEach {
|
book?.exit = true
|
||||||
it.exit = true
|
|
||||||
}
|
|
||||||
bookHandler.set(null)
|
bookHandler.set(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setStartRead() {
|
fun setStartRead() {
|
||||||
if(mBookHandler?.chapterNames?.isNotEmpty() == true) activity?.apply {
|
if(mBookHandler?.chapterNames?.isNotEmpty() == true) activity?.apply {
|
||||||
mBookHandler?.book?.results?.comic?.let { comic ->
|
book?.name?.let { name ->
|
||||||
getPreferences(MODE_PRIVATE).getInt(comic.name, -1).let { p ->
|
getPreferences(MODE_PRIVATE).getInt(name, -1).let { p ->
|
||||||
this@BookFragment.lbbstart.apply {
|
this@BookFragment.lbbstart.apply {
|
||||||
var i = 0
|
var i = 0
|
||||||
if(p >= 0) {
|
if(p >= 0) {
|
||||||
@@ -114,7 +119,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
|||||||
}
|
}
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
mBookHandler?.urlArray?.let {
|
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() {
|
fun setAddToShelf() {
|
||||||
if(mBookHandler?.chapterNames?.isNotEmpty() != true) return
|
if(mBookHandler?.chapterNames?.isNotEmpty() != true) return
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
MainActivity.shelf?.query(mBookHandler?.path!!)?.let { b ->
|
MainActivity.shelf?.query(book?.path!!)?.let { b ->
|
||||||
mBookHandler?.collect = b.results?.collect?:-2
|
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 ->
|
tic.text = b.results?.browse?.chapter_name?.let { name ->
|
||||||
getString(R.string.text_format_cloud_read_to).format(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)
|
this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mBookHandler?.book?.results?.comic?.let { comic ->
|
book?.uuid?.let { uuid ->
|
||||||
this@BookFragment.lbbsub.setOnClickListener {
|
this@BookFragment.lbbsub.setOnClickListener {
|
||||||
lifecycleScope.launch clickLaunch@ {
|
lifecycleScope.launch clickLaunch@ {
|
||||||
if (this@BookFragment.lbbsub.text != getString(R.string.button_sub)) {
|
if (this@BookFragment.lbbsub.text != getString(R.string.button_sub)) {
|
||||||
@@ -154,7 +159,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
|||||||
}
|
}
|
||||||
return@clickLaunch
|
return@clickLaunch
|
||||||
}
|
}
|
||||||
val re = MainActivity.shelf?.add(comic.uuid)
|
val re = MainActivity.shelf?.add(uuid)
|
||||||
Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
|
||||||
if (re == "修改成功") {
|
if (re == "修改成功") {
|
||||||
this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)
|
this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)
|
||||||
@@ -169,13 +174,10 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
|||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
Log.d("MyBF", "nav2: ${arguments?.getString("path")?:"null"}")
|
Log.d("MyBF", "nav2: ${arguments?.getString("path")?:"null"}")
|
||||||
bundle.putString("path", arguments?.getString("path")?:"null")
|
bundle.putString("path", arguments?.getString("path")?:"null")
|
||||||
bundle.putString("name", mBookHandler!!.book?.results?.comic?.name)
|
bundle.putString("name", book!!.name!!)
|
||||||
if(mBookHandler!!.vols != null && mBookHandler!!.json != null) {
|
if(book?.volumes != null && book?.json != null) {
|
||||||
bundle.putString("loadJson", mBookHandler!!.json)
|
bundle.putString("loadJson", book!!.json)
|
||||||
}
|
}
|
||||||
bundle.putStringArray("group", mBookHandler!!.gpws)
|
|
||||||
bundle.putStringArray("groupNames", mBookHandler!!.keys)
|
|
||||||
bundle.putIntArray("count", mBookHandler!!.cnts)
|
|
||||||
findNavController().let {
|
findNavController().let {
|
||||||
Navigate.safeNavigateTo(it, R.id.action_nav_book_to_nav_group, bundle)
|
Navigate.safeNavigateTo(it, R.id.action_nav_book_to_nav_group, bundle)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
package top.fumiama.copymanga.ui.book
|
package top.fumiama.copymanga.ui.book
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import android.os.Message
|
import android.os.Message
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
|
||||||
import androidx.core.widget.NestedScrollView
|
import androidx.core.widget.NestedScrollView
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
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.load.model.GlideUrl
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
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.app_bar_main.*
|
||||||
import kotlinx.android.synthetic.main.card_book.*
|
import kotlinx.android.synthetic.main.card_book.*
|
||||||
import kotlinx.android.synthetic.main.fragment_book.*
|
import kotlinx.android.synthetic.main.fragment_book.*
|
||||||
@@ -32,49 +30,26 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
|
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.ThemeStructure
|
||||||
import top.fumiama.copymanga.json.VolumeStructure
|
|
||||||
import top.fumiama.copymanga.manga.Reader
|
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.api.CMApi
|
||||||
import top.fumiama.copymanga.tools.ui.GlideBlurTransformation
|
import top.fumiama.copymanga.tools.ui.GlideBlurTransformation
|
||||||
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
|
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
|
||||||
import top.fumiama.copymanga.tools.ui.Navigate
|
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.copymanga.ui.vm.ViewMangaActivity
|
||||||
import top.fumiama.dmzj.copymanga.R
|
import top.fumiama.dmzj.copymanga.R
|
||||||
import java.io.File
|
|
||||||
import java.lang.Thread.sleep
|
import java.lang.Thread.sleep
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
|
class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.myLooper()!!) {
|
||||||
: AutoDownloadHandler(th.get()?.getString(R.string.bookInfoApiUrl)?.format(CMApi.myHostApiUrl, path)?: "",
|
|
||||||
BookInfoStructure::class.java,
|
|
||||||
th.get()){
|
|
||||||
private val that get() = th.get()
|
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
|
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)
|
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 urlArray = arrayOf<String>()
|
||||||
|
var exit = false
|
||||||
|
|
||||||
override fun handleMessage(msg: Message) {
|
override fun handleMessage(msg: Message) {
|
||||||
super.handleMessage(msg)
|
super.handleMessage(msg)
|
||||||
@@ -85,44 +60,10 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
|
|||||||
3 -> setAuthorsAndTags()
|
3 -> setAuthorsAndTags()
|
||||||
6 -> if(complete) that?.navigate2dl()
|
6 -> if(complete) that?.navigate2dl()
|
||||||
9 -> endSetLayouts()
|
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() {
|
private fun endSetLayouts() {
|
||||||
if (exit) return
|
if (exit) return
|
||||||
that?.fbloading?.apply {
|
that?.fbloading?.apply {
|
||||||
@@ -138,50 +79,36 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
|
|||||||
private fun setCover() {
|
private fun setCover() {
|
||||||
if (exit) return
|
if (exit) return
|
||||||
that?.apply {
|
that?.apply {
|
||||||
book?.results?.comic?.cover?.let { cover ->
|
val load = Glide.with(this).load(
|
||||||
val load = Glide.with(this).load(
|
if (book?.cover != null)
|
||||||
GlideUrl(CMApi.proxy?.wrap(cover)?:cover, CMApi.myGlideHeaders)
|
GlideUrl(CMApi.proxy?.wrap(book?.cover!!)?:book?.cover!!, CMApi.myGlideHeaders)
|
||||||
).timeout(10000).addListener(GlideHideLottieViewListener(WeakReference(laic)))
|
else book?.cachedCover
|
||||||
load.into(imic)
|
).timeout(10000).addListener(GlideHideLottieViewListener(WeakReference(laic)))
|
||||||
context?.let { it1 -> GlideBlurTransformation(it1) }
|
load.into(imic)
|
||||||
?.let { it2 -> RequestOptions.bitmapTransform(it2) }
|
context?.let { it1 -> GlideBlurTransformation(it1) }
|
||||||
?.let { it3 -> load.apply(it3).into(lbibg) }
|
?.let { it2 -> RequestOptions.bitmapTransform(it2) }
|
||||||
}
|
?.let { it3 -> load.apply(it3).into(lbibg) }
|
||||||
//imf?.visibility = View.GONE
|
//imf?.visibility = View.GONE
|
||||||
//fbl?.addView(divider)
|
//fbl?.addView(divider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private fun getThemeSeq(authors: Array<ThemeStructure>): CharSequence{
|
|
||||||
var re = ""
|
|
||||||
for(author in authors) re += author.name + ' '
|
|
||||||
return re
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private fun setTexts() {
|
private fun setTexts() {
|
||||||
if (exit) return
|
if (exit) return
|
||||||
//that?.tic?.text = book?.name
|
that?.apply {
|
||||||
//that?.tic?.visibility = View.GONE
|
// tic?.text = book?.name
|
||||||
mainWeakReference?.get()?.toolbar?.title = book?.results?.comic?.name
|
// tic?.visibility = View.GONE
|
||||||
that?.btauth?.text = that?.getString(R.string.text_format_region)?.format(
|
mainWeakReference?.get()?.toolbar?.title = book?.name
|
||||||
book?.results?.comic?.region?.display
|
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?:"未知")
|
||||||
that?.bttag?.text = that?.getString(R.string.text_format_img_type)?.format(when(book?.results?.comic?.img_type) {
|
bthit?.text = that?.getString(R.string.text_format_hit)?.format(book?.popular?:-1)
|
||||||
1 -> "条漫"
|
btsub?.text = that?.getString(R.string.text_format_stat)?.format(book?.status?:"未知")
|
||||||
2 -> "普通"
|
bttime?.text = book?.updateTime?:"未知"
|
||||||
else -> "未知类型${book?.results?.comic?.img_type}"
|
val v = layoutInflater.inflate(R.layout.line_text_info, lbl, false)
|
||||||
})
|
(v as TextView).text = book?.brief
|
||||||
that?.bthit?.text = that?.getString(R.string.text_format_hit)?.format(
|
lbl?.addView(v)
|
||||||
book?.results?.comic?.popular
|
lbl?.addView(divider)
|
||||||
)
|
}
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setTheme(caption: String, themeStructure: Array<ThemeStructure>, nav: Int) {
|
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() {
|
private fun setAuthorsAndTags() {
|
||||||
if (exit) return
|
if (exit) return
|
||||||
that?.apply {
|
that?.apply {
|
||||||
book?.results?.comic?.apply {
|
book?.apply {
|
||||||
author?.let {
|
author?.let {
|
||||||
setTheme(
|
setTheme(
|
||||||
getString(R.string.author),
|
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) {
|
private suspend fun setVolume(fbl: LinearLayout, p: Int) = withContext(Dispatchers.IO) {
|
||||||
if (exit) return@withContext
|
if (exit) return@withContext
|
||||||
that?.apply {
|
that?.apply {
|
||||||
book?.results?.apply {
|
book?.apply {
|
||||||
var i = 0
|
var i = 0
|
||||||
for (j in 0 until p) {
|
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
|
var last = i-1
|
||||||
vols?.get(p)?.let { v ->
|
val comicName = name?:return@withContext
|
||||||
|
volumes[p].let { v ->
|
||||||
if(exit) return@withContext
|
if(exit) return@withContext
|
||||||
var line: View? = null
|
var line: View? = null
|
||||||
last += v.results.list.size
|
last += v.results.list.size
|
||||||
v.results.list.forEach {
|
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}")
|
//Log.d("MyBH", "i = $i, last=$last, add chapter ${it.name}, line is null: ${line == null}")
|
||||||
that?.isOnPause?.let { isOnPause ->
|
that?.isOnPause?.let { isOnPause ->
|
||||||
while (isOnPause && !exit) sleep(100)
|
while (isOnPause && !exit) sleep(500)
|
||||||
if (exit) return@withContext
|
if (exit) return@withContext
|
||||||
}?:return@withContext
|
}?:return@withContext
|
||||||
if(line == null) {
|
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)
|
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
||||||
Log.d("MyBH", "add last single chapter ${it.name}")
|
Log.d("MyBH", "add last single chapter ${it.name}")
|
||||||
val index = i
|
val index = i
|
||||||
setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) }
|
setOnClickListener { Reader.viewMangaAt(comicName, index, urlArray) }
|
||||||
}
|
}
|
||||||
line?.let { l -> addVolumesView(fbl, l) }
|
line?.let { l -> addVolumesView(fbl, l) }
|
||||||
} else {
|
} else {
|
||||||
@@ -291,14 +219,14 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
|
|||||||
lct.text = it.name
|
lct.text = it.name
|
||||||
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
||||||
val index = i
|
val index = i
|
||||||
setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) }
|
setOnClickListener { Reader.viewMangaAt(comicName, index, urlArray) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else line?.l2cr?.apply {
|
} else line?.l2cr?.apply {
|
||||||
lct.text = it.name
|
lct.text = it.name
|
||||||
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
||||||
val index = i
|
val index = i
|
||||||
setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) }
|
setOnClickListener { Reader.viewMangaAt(comicName, index, urlArray) }
|
||||||
line?.let { l -> addVolumesView(fbl, l) }
|
line?.let { l -> addVolumesView(fbl, l) }
|
||||||
line = null
|
line = null
|
||||||
}
|
}
|
||||||
@@ -312,26 +240,27 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
|
|||||||
private suspend fun setViewManga() = withContext(Dispatchers.IO) {
|
private suspend fun setViewManga() = withContext(Dispatchers.IO) {
|
||||||
if (exit) return@withContext
|
if (exit) return@withContext
|
||||||
that?.apply {
|
that?.apply {
|
||||||
book?.results?.apply {
|
book?.apply {
|
||||||
|
val comicName = name?:return@withContext
|
||||||
ViewMangaActivity.fileArray = arrayOf()
|
ViewMangaActivity.fileArray = arrayOf()
|
||||||
urlArray = arrayOf()
|
urlArray = arrayOf()
|
||||||
ViewMangaActivity.uuidArray = arrayOf()
|
ViewMangaActivity.uuidArray = arrayOf()
|
||||||
var i = 0
|
var i = 0
|
||||||
var last = -1
|
var last = -1
|
||||||
vols?.forEachIndexed { groupIndex, v ->
|
volumes.forEachIndexed { groupIndex, v ->
|
||||||
if(exit) return@withContext
|
if(exit) return@withContext
|
||||||
last += v.results.list.size
|
last += v.results.list.size
|
||||||
v.results.list.forEach {
|
v.results.list.forEach {
|
||||||
urlArray += CMApi.getChapterInfoApiUrl(
|
urlArray += CMApi.getChapterInfoApiUrl(
|
||||||
comic.path_word,
|
path,
|
||||||
it.uuid
|
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
|
ViewMangaActivity.fileArray += f
|
||||||
chapterNames += it.name
|
chapterNames += it.name
|
||||||
ViewMangaActivity.uuidArray += it.uuid
|
ViewMangaActivity.uuidArray += it.uuid
|
||||||
that?.isOnPause?.let { isOnPause ->
|
that?.isOnPause?.let { isOnPause ->
|
||||||
while (isOnPause && !exit) sleep(100)
|
while (isOnPause && !exit) sleep(500)
|
||||||
if (exit) return@withContext
|
if (exit) return@withContext
|
||||||
}?:return@withContext
|
}?:return@withContext
|
||||||
i++
|
i++
|
||||||
@@ -352,120 +281,20 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun initComicData() = withContext(Dispatchers.IO) withIO@ {
|
private fun setVolumes() {
|
||||||
var volumes = emptyArray<VolumeStructure>()
|
that?.apply {
|
||||||
val counts = cnts.clone()
|
fbtab?.let { tab ->
|
||||||
gpws.forEachIndexed { i, gpw ->
|
fbvp?.let { vp ->
|
||||||
Log.d("MyBFH", "下载:$gpw")
|
vp.adapter = ViewData(vp).RecyclerViewAdapter()
|
||||||
var offset = 0
|
TabLayoutMediator(tab, vp) { t, p ->
|
||||||
val times = counts[i] / 100
|
t.text = book?.keys?.get(p)
|
||||||
val remain = counts[i] % 100
|
}.attach()
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 ViewData(itemView: View): RecyclerView.ViewHolder(itemView) {
|
||||||
inner class RecyclerViewAdapter: RecyclerView.Adapter<ViewData>() {
|
inner class RecyclerViewAdapter: RecyclerView.Adapter<ViewData>() {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): 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) }
|
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
|
package top.fumiama.copymanga.ui.cardflow.rank
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import kotlinx.android.synthetic.main.fragment_rank.*
|
import kotlinx.android.synthetic.main.fragment_rank.*
|
||||||
import kotlinx.android.synthetic.main.line_rank.view.*
|
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.template.ui.InfoCardLoader
|
||||||
import top.fumiama.copymanga.tools.api.CMApi
|
import top.fumiama.copymanga.tools.api.CMApi
|
||||||
import top.fumiama.copymanga.tools.ui.UITools
|
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 var sortValue = 0
|
||||||
private val audienceWay = listOf("", "male", "female")
|
private val audienceWay = listOf("", "male", "female")
|
||||||
private var audience = 0 // 0 all 1 male 2 female
|
private var audience = 0 // 0 all 1 male 2 female
|
||||||
|
private var isLoading = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -39,6 +44,11 @@ class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank
|
|||||||
ad?.exit = true
|
ad?.exit = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onLoadFinish() {
|
||||||
|
super.onLoadFinish()
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
override fun getApiUrl() =
|
override fun getApiUrl() =
|
||||||
getString(R.string.rankApiUrl).format(
|
getString(R.string.rankApiUrl).format(
|
||||||
CMApi.myHostApiUrl,
|
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 onTabReselected(tab: TabLayout.Tab?) {}
|
||||||
|
|
||||||
override fun onTabSelected(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?) {}
|
override fun onTabUnselected(tab: TabLayout.Tab?) {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSortValue(value: Int) {
|
private fun delayedRefresh() {
|
||||||
sortValue = value
|
lifecycleScope.launch {
|
||||||
Thread{
|
isLoading = true
|
||||||
sleep(400)
|
withContext(Dispatchers.IO) {
|
||||||
if(ad?.exit != true) activity?.runOnUiThread {
|
sleep(400)
|
||||||
reset()
|
withContext(Dispatchers.Main) {
|
||||||
addPage()
|
reset()
|
||||||
|
addPage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.start()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showSexInfo(toolsBox: UITools) {
|
fun showSexInfo(toolsBox: UITools) {
|
||||||
if (ad?.exit != false) return
|
if (ad?.exit != false) return
|
||||||
toolsBox.buildInfo("切换类型", "选择一种想筛选的漫画类型",
|
toolsBox.buildInfo("切换类型", "选择一种想筛选的漫画类型",
|
||||||
"男频", "全部", "女频", {
|
"男频", "全部", "女频", {
|
||||||
audience = 1
|
if(!isLoading) {
|
||||||
reset()
|
audience = 1
|
||||||
Thread {
|
delayedRefresh()
|
||||||
sleep(600)
|
}
|
||||||
addPage()
|
|
||||||
}.start()
|
|
||||||
}, {
|
}, {
|
||||||
audience = 0
|
if(!isLoading) {
|
||||||
reset()
|
audience = 0
|
||||||
Thread {
|
delayedRefresh()
|
||||||
sleep(600)
|
}
|
||||||
addPage()
|
|
||||||
}.start()
|
|
||||||
}, {
|
}, {
|
||||||
audience = 2
|
if(!isLoading) {
|
||||||
reset()
|
audience = 2
|
||||||
Thread {
|
delayedRefresh()
|
||||||
sleep(600)
|
}
|
||||||
addPage()
|
|
||||||
}.start()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)) {
|
PausableDownloader(getString(R.string.filterApiUrl).format(CMApi.myHostApiUrl)) {
|
||||||
if(ad?.exit == true) return@PausableDownloader
|
if(ad?.exit == true) return@PausableDownloader
|
||||||
it.let {
|
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
|
if(ad?.exit == true) return@PausableDownloader
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
if(ad?.exit != true) setClasses()
|
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 ->
|
PausableDownloader(getString(R.string.topicApiUrl).format(CMApi.myHostApiUrl, arguments?.getString("path"))) { data ->
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
if(ad?.exit == true) return@withContext
|
if(ad?.exit == true) return@withContext
|
||||||
data.apply {
|
data.inputStream().use { i ->
|
||||||
val r = inputStream().reader()
|
val r = i.reader()
|
||||||
Gson().fromJson(r, TopicStructure::class.java)?.apply {
|
Gson().fromJson(r, TopicStructure::class.java)?.apply {
|
||||||
if(ad?.exit == true) return@withContext
|
if(ad?.exit == true) return@withContext
|
||||||
activity?.let {
|
withContext(Dispatchers.Main) withMain@ {
|
||||||
withContext(Dispatchers.Main) withMain@ {
|
if(ad?.exit == true) return@withMain
|
||||||
if(ad?.exit == true) return@withMain
|
activity?.toolbar?.title = results.title
|
||||||
it.toolbar.title = results.title
|
ftttime.text = results.datetime_created
|
||||||
ftttime.text = results.datetime_created
|
fttintro.text = results.intro
|
||||||
fttintro.text = results.intro
|
type = results.type
|
||||||
type = results.type
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.os.Bundle
|
|||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
@@ -12,11 +11,8 @@ import kotlinx.android.synthetic.main.fragment_dlcomic.*
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import top.fumiama.copymanga.json.ChapterStructure
|
|
||||||
import top.fumiama.copymanga.json.VolumeStructure
|
import top.fumiama.copymanga.json.VolumeStructure
|
||||||
import top.fumiama.copymanga.template.http.PausableDownloader
|
|
||||||
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
|
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
|
||||||
import top.fumiama.copymanga.tools.api.CMApi
|
|
||||||
import top.fumiama.dmzj.copymanga.R
|
import top.fumiama.dmzj.copymanga.R
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.Thread.sleep
|
import java.lang.Thread.sleep
|
||||||
@@ -24,7 +20,6 @@ import java.lang.ref.WeakReference
|
|||||||
|
|
||||||
class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
|
class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
|
||||||
var ltbtn: View? = null
|
var ltbtn: View? = null
|
||||||
private var ads = emptyArray<PausableDownloader>()
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
exit = false
|
exit = false
|
||||||
@@ -43,11 +38,7 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> initComicData(
|
else -> findNavController().popBackStack()
|
||||||
arguments?.getString("path"),
|
|
||||||
arguments?.getStringArray("group"),
|
|
||||||
arguments?.getIntArray("count")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,9 +50,6 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
|
|||||||
handler?.downloading = false
|
handler?.downloading = false
|
||||||
handler?.mangaDlTools?.exit = true
|
handler?.mangaDlTools?.exit = true
|
||||||
handler?.dl?.dismiss()
|
handler?.dl?.dismiss()
|
||||||
ads.forEach {
|
|
||||||
it.exit = true
|
|
||||||
}
|
|
||||||
exit = true
|
exit = true
|
||||||
handler = null
|
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 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 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) {
|
private suspend fun initOldComicData() = withContext(Dispatchers.IO) {
|
||||||
handler = ComicDlHandler(Looper.myLooper()!!, WeakReference(this@ComicDlFragment),
|
handler = ComicDlHandler(Looper.myLooper()!!, WeakReference(this@ComicDlFragment),
|
||||||
arguments?.getString("name")?:"null")
|
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)
|
Navigate.safeNavigateTo(findNavController(), R.id.action_nav_new_download_to_nav_download)
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
callDownloadFragment(name)
|
callBookFragment(name)
|
||||||
}
|
}
|
||||||
v.setOnLongClickListener {
|
v.setOnLongClickListener {
|
||||||
if (name == oldDlCardName && path == oldDlCardName) {
|
if (name == oldDlCardName && path == oldDlCardName) {
|
||||||
@@ -132,7 +132,7 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
|||||||
AlertDialog.Builder(context)
|
AlertDialog.Builder(context)
|
||||||
.setIcon(R.drawable.ic_launcher_foreground)
|
.setIcon(R.drawable.ic_launcher_foreground)
|
||||||
.setTitle(R.string.new_download_card_option_hint)
|
.setTitle(R.string.new_download_card_option_hint)
|
||||||
.setItems(arrayOf("删除数据", "前往详情")) { d, p ->
|
.setItems(arrayOf("删除数据文件夹", "直接前往下载页")) { d, p ->
|
||||||
d.cancel()
|
d.cancel()
|
||||||
when (p) {
|
when (p) {
|
||||||
0 -> {
|
0 -> {
|
||||||
@@ -150,12 +150,7 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
|||||||
}.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
}.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> callDownloadFragment(name)
|
||||||
val bundle = Bundle()
|
|
||||||
bundle.putBoolean("loadJson", true)
|
|
||||||
bundle.putString("name", name)
|
|
||||||
Navigate.safeNavigateTo(findNavController(), R.id.action_nav_new_download_to_nav_book, bundle)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.show()
|
.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()
|
val bundle = Bundle()
|
||||||
Log.d("MyNDF", "Call dl and is new.")
|
Log.d("MyNDF", "Call dl and is new.")
|
||||||
bundle.putString("loadJson", File(File(extDir, name), "info.json").readText())
|
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.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import top.fumiama.copymanga.MainActivity
|
||||||
import top.fumiama.copymanga.json.Chapter2Return
|
import top.fumiama.copymanga.json.Chapter2Return
|
||||||
import top.fumiama.copymanga.json.ChapterWithContent
|
import top.fumiama.copymanga.json.ChapterWithContent
|
||||||
import top.fumiama.copymanga.json.ComicStructure
|
import top.fumiama.copymanga.json.ComicStructure
|
||||||
@@ -191,8 +192,8 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fakeLoad() {
|
private suspend fun fakeLoad() = withContext(Dispatchers.IO) {
|
||||||
PausableDownloader(chapterUrl) { _ -> }.run()
|
if(MainActivity.member?.hasLogin == true) PausableDownloader(chapterUrl) { _ -> }.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun prepareManga() = withContext(Dispatchers.Main) {
|
private suspend fun prepareManga() = withContext(Dispatchers.Main) {
|
||||||
|
|||||||
@@ -301,7 +301,9 @@ class ViewMangaActivity : TitleActivityTemplate() {
|
|||||||
private fun canCut(inputStream: InputStream): Boolean{
|
private fun canCut(inputStream: InputStream): Boolean{
|
||||||
val op = BitmapFactory.Options()
|
val op = BitmapFactory.Options()
|
||||||
op.inJustDecodeBounds = true
|
op.inJustDecodeBounds = true
|
||||||
BitmapFactory.decodeStream(inputStream, null, op)
|
inputStream.use {
|
||||||
|
BitmapFactory.decodeStream(it, null, op)
|
||||||
|
}
|
||||||
Log.d("MyVM", "w: ${op.outWidth}, h: ${op.outHeight}")
|
Log.d("MyVM", "w: ${op.outWidth}, h: ${op.outHeight}")
|
||||||
return op.outWidth.toFloat() / op.outHeight.toFloat() > 1
|
return op.outWidth.toFloat() / op.outHeight.toFloat() > 1
|
||||||
}
|
}
|
||||||
@@ -437,13 +439,18 @@ class ViewMangaActivity : TitleActivityTemplate() {
|
|||||||
loadImg(imgView, it, useCut, isLeft, false)
|
loadImg(imgView, it, useCut, isLeft, false)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true)
|
|
||||||
val sleepTime = loadImgOnWait.getAndIncrement().toLong()*200
|
val sleepTime = loadImgOnWait.getAndIncrement().toLong()*200
|
||||||
Log.d("MyVM", "loadImgOn sleep: $sleepTime ms")
|
Log.d("MyVM", "loadImgOn sleep: $sleepTime ms")
|
||||||
val re = tasks?.get(index2load)
|
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 != null) {
|
||||||
if(!re.isDone) re.run()
|
if(!re.isDone) {
|
||||||
|
loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true)
|
||||||
|
re.run()
|
||||||
|
}
|
||||||
val data = re.get()
|
val data = re.get()
|
||||||
if(data != null && data.isNotEmpty()) {
|
if(data != null && data.isNotEmpty()) {
|
||||||
BitmapFactory.decodeByteArray(data, 0, data.size)?.let {
|
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", "Load position $position from task")
|
||||||
}?:Log.d("MyVM", "null bitmap at $position")
|
}?: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()
|
loadImgOnWait.decrementAndGet()
|
||||||
tasks?.apply {
|
tasks?.apply {
|
||||||
if (index2load >= size) return@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() {
|
private fun loadOneImg() {
|
||||||
@@ -515,11 +530,16 @@ class ViewMangaActivity : TitleActivityTemplate() {
|
|||||||
for (i in 0..1) {
|
for (i in 0..1) {
|
||||||
val ext = if((i == 0 && tryWebpFirst) || (i == 1 && !tryWebpFirst)) "webp" else "jpg"
|
val ext = if((i == 0 && tryWebpFirst) || (i == 1 && !tryWebpFirst)) "webp" else "jpg"
|
||||||
bitmap = try {
|
bitmap = try {
|
||||||
if (q == 100) BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.$ext")))
|
zip.getInputStream(zip.getEntry("${position}.$ext"))?.use { zipInputStream ->
|
||||||
else {
|
if (q == 100) BitmapFactory.decodeStream(zipInputStream)
|
||||||
val out = ByteArrayOutputStream()
|
else {
|
||||||
BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.$ext")))?.compress(Bitmap.CompressFormat.JPEG, q, out)
|
ByteArrayOutputStream().use { out ->
|
||||||
BitmapFactory.decodeStream(ByteArrayInputStream(out.toByteArray()))
|
BitmapFactory.decodeStream(zipInputStream)?.compress(Bitmap.CompressFormat.JPEG, q, out)
|
||||||
|
ByteArrayInputStream(out.toByteArray()).use { i ->
|
||||||
|
BitmapFactory.decodeStream(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
val l = LoginInfoStructure()
|
val l = LoginInfoStructure()
|
||||||
l.code = 450
|
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
|
l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user