1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-05 07:20:23 +08:00
新增
1. 双页切分显示预载进度
2. 阅览漫画标题栏显示漫画名
修复
1. 一次下载过多漫画可能导致的闪退
优化
1. 阅览漫画定位逻辑
2. VM参数传递, 调用方式
3. 登录错误提示
This commit is contained in:
源文雨
2024-03-16 23:44:11 +09:00
parent f0e7802eb2
commit aa0190fbb7
17 changed files with 315 additions and 2727 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +0,0 @@
<changelist name="Uncommitted_changes_before_Update_at_2024_3_9,_12_23_AM_[Changes]" date="1709911381682" recycled="true" deleted="true">
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/Uncommitted_changes_before_Update_at_2024_3_9,_12_23_AM_[Changes]/shelved.patch" />
<option name="DESCRIPTION" value="Uncommitted changes before Update at 2024/3/9, 12:23 AM [Changes]" />
</changelist>

View File

@@ -8,8 +8,8 @@ android {
applicationId 'top.fumiama.copymanga'
minSdkVersion 23
targetSdkVersion 34
versionCode 53
versionName '2.2.5'
versionCode 54
versionName '2.2.6'
resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -24,13 +24,13 @@ class MangaDlTools {
PausableDownloader(url.toString(), 1000) { data ->
try {
Gson().fromJson(data.decodeToString(), Chapter2Return::class.java)?.let {
getChapterInfo(it, index, chapterName, group)
downloadChapter(it, index, chapterName, group)
}
} catch (e: Exception) {
e.printStackTrace()
onDownloadedListener?.handleMessage(index, false, e.localizedMessage?:"Gson parsing error")
}
}.run()
}.run().let { if (!it) onDownloadedListener?.handleMessage(index, false, "获取章节信息错误") }
}
@Synchronized private fun prepareDownloadListener() {
@@ -57,8 +57,8 @@ class MangaDlTools {
indexMap[f] = index
}
private fun getChapterInfo(chapter2Return: Chapter2Return, index: Int, chapterName: CharSequence, group: CharSequence) {
if(index >= 0){
private fun downloadChapter(chapter2Return: Chapter2Return, index: Int, chapterName: CharSequence, group: CharSequence) {
if(index >= 0) {
val f = "$chapterName.zip"
setPool(chapter2Return.results.comic.name, group)
setIndexMap(f, index)

View File

@@ -5,38 +5,61 @@ import android.content.Intent
import android.util.Log
import androidx.core.content.edit
import com.google.gson.Gson
import kotlinx.android.synthetic.main.button_tbutton.view.*
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
import java.io.File
object Reader {
fun start2viewManga(name: String, pos: Int, urlArray: Array<String>, fromFirstPage: Boolean = false) {
var fileArray = arrayOf<File>()
fun start2viewManga(name: String?, pos: Int, urlArray: Array<String>, uuidArray: Array<String>, fromFirstPage: Boolean = false) {
Log.d("MyR", "viewMangaAt name $name, pos $pos")
mainWeakReference?.get()?.apply {
getPreferences(Context.MODE_PRIVATE)?.edit {
putInt(name, pos)
apply()
Log.d("MyR", "记录 $name 阅读到第 ${pos+1}")
}?: Log.d("MyR", "无法获得 main pref")
// ViewMangaActivity.dlhandler = null
ViewMangaActivity.position = pos
ViewMangaActivity.comicName = name
val zipf = ViewMangaActivity.fileArray[pos]
val intent = Intent(this, ViewMangaActivity::class.java)
name?.let { n ->
getPreferences(Context.MODE_PRIVATE)?.edit {
putInt(n, pos)
apply()
Log.d("MyR", "记录 $n 阅读到第 ${pos+1}")
}?: Log.d("MyR", "无法获得 main pref")
intent.putExtra("comicName", name)
}
intent.putExtra("position", pos)
intent.putExtra("urlArray", urlArray)
if(!fromFirstPage) {
intent.putExtra("uuidArray", uuidArray)
if (!fromFirstPage) {
intent.putExtra("function", "log")
ViewMangaActivity.pn = -2
intent.putExtra("pn", -2)
}
if (zipf.exists()) {
ViewMangaActivity.zipFile = zipf
val zipFile = fileArray[pos]
if (zipFile.exists()) {
intent.putExtra("zipFile", zipFile.absolutePath)
//intent.putExtra("callFrom", "zipFirst")
startActivity(intent)
} else {
ViewMangaActivity.zipFile = null
startActivity(intent)
}
startActivity(intent)
}
}
fun viewOldMangaZipFile(fileArray: Array<File>, name: String, pos: Int, zipFile: File) {
Reader.fileArray = fileArray
mainWeakReference?.get()?.apply {
val intent = Intent(this, ViewMangaActivity::class.java)
intent.putExtra("comicName", name)
intent.putExtra("position", pos)
intent.putExtra("zipFile", zipFile.absolutePath)
startActivity(intent)
}
}
fun viewMangaZipFile(pos: Int, urlArray: Array<String>, uuidArray: Array<String>, zipFile: File) {
mainWeakReference?.get()?.apply {
val intent = Intent(this, ViewMangaActivity::class.java)
intent.putExtra("position", pos)
.putExtra("urlArray", urlArray)
.putExtra("uuidArray", uuidArray)
.putExtra("callFrom", "zipFirst")
.putExtra("zipFile", zipFile.absolutePath)
startActivity(intent)
}
}
fun getComicPathWordInFolder(file: File): String {

View File

@@ -8,12 +8,11 @@ import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.dmzj.copymanga.R
import java.lang.Thread.sleep
import kotlin.random.Random
class PausableDownloader(private val url: String, private val waitMilliseconds: Long = 0, private val isApi: Boolean = true, private val whenFinish: (suspend (result: ByteArray)->Unit)? = null) {
var exit = false
suspend fun run() = withContext(Dispatchers.IO) {
suspend fun run(): Boolean = withContext(Dispatchers.IO) {
var c = 0
while (!exit && c++ < 3) {
try {
@@ -23,12 +22,13 @@ class PausableDownloader(private val url: String, private val waitMilliseconds:
mainWeakReference?.get()?.getString(R.string.pc_ua)!!
))
whenFinish?.let { it(data) }
break
return@withContext true
} catch (e: Exception) {
e.printStackTrace()
if (waitMilliseconds > 0) delay(200+Random.nextLong(waitMilliseconds))
}
}
Log.d("MyPD", "found exit = $exit")
return@withContext false
}
}

View File

@@ -1,6 +1,9 @@
package top.fumiama.copymanga.tools.http
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.tools.api.CMApi
import java.io.File
import java.io.FileOutputStream
@@ -14,7 +17,7 @@ import java.util.zip.ZipOutputStream
import kotlin.random.Random
class DownloadPool(folder: String) {
class Quest(val fileName: String, val imgUrl: Array<String>, val refer: String? = null)
class Quest(val fileName: String, val imgUrl: Array<String>)
var exit = false
set(value) {
if(value) {
@@ -33,8 +36,9 @@ class DownloadPool(folder: String) {
if(!saveFolder.exists()) saveFolder.mkdirs()
}
operator fun plusAssign(quest: Quest) {
operator fun plusAssign(quest: Quest): Unit {
packZipFile(quest.fileName, quest.imgUrl)
Log.d("MyDP", "+= ${quest.fileName}, size: ${quest.imgUrl.size}")
}
fun setOnDownloadListener(onDownloadListener: (String, Boolean, String) -> Unit) {
@@ -46,83 +50,81 @@ class DownloadPool(folder: String) {
}
private fun packZipFile(fileName: String, imgUrls: Array<String>) {
Thread {
File(saveFolder, "$fileName.tmp").let { f ->
f.parentFile?.let { if(!it.exists()) it.mkdirs() }
var start = 0
Log.d("MyDP", "Zip file: ${f.absolutePath}")
if(f.exists()) {
try {
val zipFile = ZipFile(f)
start = zipFile.size()
zipFile.close()
Log.d("MyDP", "next download index: $start")
if (start <= 0 || start >= imgUrls.size) { // error or re-download
f.delete()
f.createNewFile()
start = 0
}
} catch (e: Exception) {
e.printStackTrace()
File(saveFolder, "$fileName.tmp").let { f ->
f.parentFile?.let { if(!it.exists()) it.mkdirs() }
var start = 0
Log.d("MyDP", "Zip file: ${f.absolutePath}")
if(f.exists()) {
try {
val zipFile = ZipFile(f)
start = zipFile.size()
zipFile.close()
Log.d("MyDP", "next download index: $start")
if (start <= 0 || start >= imgUrls.size) { // error or re-download
f.delete()
f.createNewFile()
start = 0
}
} else f.createNewFile()
val zip: ZipOutputStream
if (start > 0) {
val fromZip = ZipInputStream(f.readBytes().inputStream())
zip = ZipOutputStream(CheckedOutputStream(FileOutputStream(f), CRC32()))
zip.setLevel(9)
fromZip.use { z ->
var e = z.nextEntry
while (e != null) {
zip.putNextEntry(e)
z.copyTo(zip)
zip.closeEntry()
z.closeEntry()
e = z.nextEntry
}
}
} else {
zip = ZipOutputStream(CheckedOutputStream(FileOutputStream(f), CRC32()))
zip.setLevel(9)
}
var succeed = true
var lastIndex = -8
try {
for(index in start until imgUrls.size) {
while (wait && !exit) sleep(100+Random.nextLong(1000))
if(exit) break
var tryTimes = 3
var s = false
while (!s && tryTimes-- > 0) {
val u = imgUrls[index]
s = (DownloadTools.getHttpContent(CMApi.resolution.wrap(CMApi.imageProxy?.wrap(u)?:u), -1))?.let {
zip.putNextEntry(ZipEntry("$index.${if(imgUrls[index].contains(".webp")) "webp" else "jpg"}"))
zip.write(it)
zip.closeEntry()
true
}?:false
if(exit) break
if (!s) sleep(2000)
if(exit) break
}
if(!s && tryTimes <= 0) {
succeed = false
mOnPageDownloadListener?.let { it(fileName, index + 1, imgUrls.size, false, "超过最大重试次数") }
break
} else mOnPageDownloadListener?.let { it(fileName, index + 1, imgUrls.size, true, "") }
lastIndex = index
}
zip.close()
if (succeed && lastIndex+1 >= imgUrls.size) f.renameTo(File(saveFolder, fileName))
mOnPageDownloadListener?.let { it(fileName, 0, 0, true, "") }
mOnDownloadListener?.let { it(fileName, succeed, "") }
} catch (e: Exception) {
e.printStackTrace()
mOnDownloadListener?.let { it(fileName, false, e.localizedMessage?:"packZipFile") }
f.delete()
f.createNewFile()
}
} else f.createNewFile()
val zip: ZipOutputStream
if (start > 0) {
val fromZip = ZipInputStream(f.readBytes().inputStream())
zip = ZipOutputStream(CheckedOutputStream(FileOutputStream(f), CRC32()))
zip.setLevel(9)
fromZip.use { z ->
var e = z.nextEntry
while (e != null) {
zip.putNextEntry(e)
z.copyTo(zip)
zip.closeEntry()
z.closeEntry()
e = z.nextEntry
}
}
} else {
zip = ZipOutputStream(CheckedOutputStream(FileOutputStream(f), CRC32()))
zip.setLevel(9)
}
}.start()
var succeed = true
var lastIndex = -8
try {
for(index in start until imgUrls.size) {
while (wait && !exit) sleep(100+Random.nextLong(1000))
if(exit) break
var tryTimes = 3
var s = false
while (!s && tryTimes-- > 0) {
val u = imgUrls[index]
s = (DownloadTools.getHttpContent(CMApi.resolution.wrap(CMApi.imageProxy?.wrap(u)?:u), -1))?.let {
zip.putNextEntry(ZipEntry("$index.${if(imgUrls[index].contains(".webp")) "webp" else "jpg"}"))
zip.write(it)
zip.closeEntry()
true
}?:false
if(exit) break
if (!s) sleep(2000)
if(exit) break
}
if(!s && tryTimes <= 0) {
succeed = false
mOnPageDownloadListener?.let { it(fileName, index + 1, imgUrls.size, false, "超过最大重试次数") }
break
} else mOnPageDownloadListener?.let { it(fileName, index + 1, imgUrls.size, true, "") }
lastIndex = index
}
zip.close()
if (succeed && lastIndex+1 >= imgUrls.size) f.renameTo(File(saveFolder, fileName))
mOnPageDownloadListener?.let { it(fileName, 0, 0, true, "") }
mOnDownloadListener?.let { it(fileName, succeed, "") }
} catch (e: Exception) {
e.printStackTrace()
mOnDownloadListener?.let { it(fileName, false, e.localizedMessage?:"packZipFile") }
}
}
}
}

View File

@@ -115,15 +115,14 @@ object DownloadTools {
fun prepare(url: String?): FutureTask<ByteArray?>? =
url?.let {
Log.d("Mydl", "prepareHttp: $it")
var ret: ByteArray? = null
val task = FutureTask(Callable {
var ret: ByteArray? = null
try {
val connection = getNormalConnection(it, "GET")
val ci = connection?.inputStream
ret = ci?.readBytes()
ci?.close()
connection?.disconnect()
connection.inputStream?.use { ci ->
ret = ci.readBytes()
}
connection.disconnect()
} catch (ex: Exception) {
ex.printStackTrace()
}

View File

@@ -105,8 +105,8 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
i = p
}
setOnClickListener {
mBookHandler?.urlArray?.let {
Reader.start2viewManga(name, i, it)
mBookHandler?.apply {
Reader.start2viewManga(name, i, urlArray, uuidArray)
}
}
}

View File

@@ -49,6 +49,7 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
var chapterNames = arrayOf<String>()
var collect: Int = -1
var urlArray = arrayOf<String>()
var uuidArray = arrayOf<String>()
var exit = false
override fun handleMessage(msg: Message) {
@@ -210,7 +211,7 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
Log.d("MyBH", "add last single chapter ${it.name}")
val index = i
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray) }
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray, uuidArray) }
}
line?.let { l -> addVolumesView(fbl, l) }
} else {
@@ -219,14 +220,14 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
lct.text = it.name
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
val index = i
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray) }
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray, uuidArray) }
}
}
} else line?.l2cr?.apply {
lct.text = it.name
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
val index = i
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray) }
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray, uuidArray) }
line?.let { l -> addVolumesView(fbl, l) }
line = null
}
@@ -242,9 +243,9 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
that?.apply {
book?.apply {
val comicName = name?:return@withContext
ViewMangaActivity.fileArray = arrayOf()
Reader.fileArray = arrayOf()
urlArray = arrayOf()
ViewMangaActivity.uuidArray = arrayOf()
uuidArray = arrayOf()
var i = 0
var last = -1
volumes.forEachIndexed { groupIndex, v ->
@@ -256,9 +257,9 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
it.uuid
)?:""
val f = CMApi.getZipFile(context?.getExternalFilesDir(""), comicName, keys[groupIndex], it.name)
ViewMangaActivity.fileArray += f
Reader.fileArray += f
chapterNames += it.name
ViewMangaActivity.uuidArray += it.uuid
uuidArray += it.uuid
that?.isOnPause?.let { isOnPause ->
while (isOnPause && !exit) delay(500)
if (exit) return@withContext

View File

@@ -3,7 +3,6 @@ package top.fumiama.copymanga.ui.comicdl
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.os.Message
@@ -13,29 +12,31 @@ import android.view.ViewTreeObserver
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import com.google.gson.Gson
import kotlinx.android.synthetic.main.fragment_book.*
import kotlinx.android.synthetic.main.line_chapter.view.*
import kotlinx.android.synthetic.main.widget_downloadbar.*
import kotlinx.android.synthetic.main.fragment_dlcomic.*
import kotlinx.android.synthetic.main.line_horizonal_empty.view.*
import kotlinx.android.synthetic.main.button_tbutton.*
import kotlinx.android.synthetic.main.button_tbutton.view.*
import kotlinx.android.synthetic.main.fragment_book.*
import kotlinx.android.synthetic.main.fragment_dlcomic.*
import kotlinx.android.synthetic.main.line_caption.view.*
import kotlinx.android.synthetic.main.line_chapter.view.*
import kotlinx.android.synthetic.main.line_horizonal_empty.view.*
import kotlinx.android.synthetic.main.widget_downloadbar.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.ChapterStructure
import top.fumiama.copymanga.json.ComicStructureOld
import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.manga.MangaDlTools
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.exit
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.json
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
import top.fumiama.copymanga.views.ChapterToggleButton
import top.fumiama.copymanga.views.LazyScrollView
import top.fumiama.dmzj.copymanga.R
import java.io.File
import java.lang.ref.WeakReference
@@ -62,6 +63,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
private var finishMap = arrayOf<Boolean?>()
var downloading = false
private var urlArray = arrayOf<String>()
private var uuidArray = arrayOf<String>()
@SuppressLint("SetTextI18n")
override fun handleMessage(msg: Message) {
@@ -111,8 +113,8 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
if(isOld) analyzeOldStructure()
else {
urlArray = arrayOf()
ViewMangaActivity.fileArray = arrayOf()
ViewMangaActivity.uuidArray = arrayOf()
Reader.fileArray = arrayOf()
uuidArray = arrayOf()
vols.forEachIndexed { i, vol ->
val caption = groupNames?.get(i)?:vol.results.list[0].group_path_word
Log.d("MyCDH", "caption: $caption, group name: ${groupNames?.get(i)}")
@@ -269,16 +271,14 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
i.isEnabled = false
}
i.url?.let {
Thread {
that?.lifecycleScope?.launch {
mangaDlTools.downloadChapterInVol(
it,
i.chapterName,
i.caption?:"null",
i.index
)
}
}.start()
launch {
mangaDlTools.downloadChapterInVol(
it,
i.chapterName,
i.caption?:"null",
i.index
)
}
}
}
}
@@ -308,6 +308,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
}
private suspend fun onZipDownloadFailure(index: Int, message: String) = withContext(Dispatchers.Main) {
if (exit) return@withContext
tbtnlist[index].setBackgroundResource(R.drawable.rndbg_error)
tbtnlist[index].isEnabled = true
Toast.makeText(that?.context, "下载${tbtnlist[index].chapterName}失败: $message", Toast.LENGTH_SHORT).show()
@@ -345,7 +346,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
tbtncnt++
tbv.tbtn.uuid = uuid
ViewMangaActivity.uuidArray += uuid
uuidArray += uuid
tbv.tbtn.chapterName = title
tbv.tbtn.url = url
//tbv.tbtn.hint = caption
@@ -358,7 +359,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
withContext(Dispatchers.IO) {
val zipf = CMApi.getZipFile(that!!.context?.getExternalFilesDir(""), comicName, caption, title)
Log.d("MyCD", "Get zipf: $zipf")
ViewMangaActivity.fileArray += zipf
Reader.fileArray += zipf
if (zipf.exists()) withContext(Dispatchers.Main) {
tbv.tbtn.setBackgroundResource(R.drawable.rndbg_checked)
tbv.tbtn.isChecked = false
@@ -367,13 +368,9 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
if (zipf.exists() && !multiSelect) {
it.tbtn.setBackgroundResource(R.drawable.rndbg_checked)
it.tbtn.isChecked = false
ViewMangaActivity.zipFile = zipf
ViewMangaActivity.dlHandler = this@ComicDlHandler
ViewMangaActivity.position = it.tbtn.index
dl?.show()
val intent = Intent(that?.context, ViewMangaActivity::class.java)
intent.putExtra("urlArray", urlArray).putExtra("callFrom", "zipFirst")
that?.startActivity(intent)
Reader.viewMangaZipFile(it.tbtn.index, urlArray, uuidArray, zipf)
} else {
it.tbtn.setBackgroundResource(R.drawable.toggle_button)
if (it.tbtn.isChecked) that?.tdwn?.text = "$dldChapter/${++checkedChapter}"
@@ -393,14 +390,9 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
} else {
toolsBox.buildInfo("直接观看", "不下载而进行观看", "确定",
null, "取消", {
ViewMangaActivity.zipFile = null
ViewMangaActivity.dlHandler = this@ComicDlHandler
ViewMangaActivity.position = it.tbtn.index
dl?.show()
val intent = Intent(that?.context, ViewMangaActivity::class.java)
intent.putExtra("urlArray", urlArray)
that?.startActivity(intent)
Reader.start2viewManga(null, it.tbtn.index, urlArray, uuidArray)
}, null, null
)
}

View File

@@ -1,7 +1,6 @@
package top.fumiama.copymanga.ui.download
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
@@ -15,10 +14,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
import top.fumiama.copymanga.tools.file.FileUtils
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
import top.fumiama.dmzj.copymanga.R
import java.io.File
import java.util.regex.Pattern
@@ -64,12 +63,10 @@ class DownloadFragment: NoBackRefreshFragment(R.layout.fragment_download) {
}
chosenFile.name.endsWith(".zip") -> {
Toast.makeText(context, "加载中...", Toast.LENGTH_SHORT).show()
ViewMangaActivity.zipFile = chosenFile
ViewMangaActivity.comicName = it[position]
ViewMangaActivity.position = position
ViewMangaActivity.fileArray = it.map { File(cd, it) }.toTypedArray()
// ViewMangaActivity.urlArray = Array(it.size) {return@Array ""}
startActivity(Intent(context, ViewMangaActivity::class.java))
Reader.viewOldMangaZipFile(
it.map { File(cd, it) }.toTypedArray(),
it[position], position, chosenFile
)
}
}
}

View File

@@ -2,8 +2,6 @@ package top.fumiama.copymanga.ui.vm
import android.widget.Toast
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.comicName
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.position
import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference
@@ -38,7 +36,7 @@ class PagesManager(private val w: WeakReference<ViewMangaActivity>) {
}
return
}
val chapterPosition = position + if(goNext) 1 else -1
val chapterPosition = v.position + if(goNext) 1 else -1
if (v.urlArray.isEmpty()) return
if(chapterPosition < 0 || chapterPosition >= v.urlArray.size) {
Toast.makeText(v.applicationContext, R.string.end_of_chapter, Toast.LENGTH_SHORT).show()
@@ -48,7 +46,7 @@ class PagesManager(private val w: WeakReference<ViewMangaActivity>) {
//if(v.zipFirst) intent.putExtra("callFrom", "zipFirst")
v.tt.canDo = false
//ViewMangaActivity.dlhandler = null
comicName?.let { Reader.start2viewManga(it, chapterPosition, v.urlArray, goNext) }
v.comicName?.let { Reader.start2viewManga(it, chapterPosition, v.urlArray, v.uuidArray, goNext) }
v.finish()
return
}

View File

@@ -9,10 +9,12 @@ import android.view.View
import androidx.lifecycle.lifecycleScope
import com.google.gson.Gson
import kotlinx.android.synthetic.main.activity_viewmanga.*
import kotlinx.android.synthetic.main.dialog_unzipping.*
import kotlinx.android.synthetic.main.widget_infodrawer.*
import kotlinx.android.synthetic.main.widget_infodrawer.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.json.Chapter2Return
@@ -20,10 +22,6 @@ import top.fumiama.copymanga.json.ChapterWithContent
import top.fumiama.copymanga.json.ComicStructure
import top.fumiama.copymanga.template.http.AutoDownloadHandler
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.comicName
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.pn
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.position
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.uuidArray
import top.fumiama.copymanga.views.ScaleImageView
import top.fumiama.dmzj.copymanga.R
import java.io.File
@@ -97,11 +95,11 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
LOAD_IMAGES_INTO_LINE -> wv.get()?.lifecycleScope?.launch { loadImagesIntoLine() }
RESTORE_PAGE_NUMBER -> {
sendEmptyMessage(DIALOG_HIDE)
wv.get()?.restorePN()
wv.get()?.apply { lifecycleScope.launch { restorePN() } }
}
LOAD_PAGE_FROM_ITEM -> {
val verticalMaxCount = wv.get()?.verticalLoadMaxCount?:20
val item = (pn - 1) / verticalMaxCount * verticalMaxCount
val item = ((wv.get()?.pn?:1) - 1) / verticalMaxCount * verticalMaxCount
loadScrollMode(item)
Log.d("MyVMH", "Load page from $item")
}
@@ -130,6 +128,7 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
}
DO_LAMBDA -> (msg.obj as? Runnable?)?.run()
SET_NET_INFO -> wv.get()?.idtime?.text = SimpleDateFormat("HH:mm").format(Date()) + week + wv.get()?.toolsBox?.netInfo
SET_DL_TEXT -> dl.tunz.text = msg.obj as String
}
}
override fun getGsonItem() = manga
@@ -159,9 +158,9 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
prepareManga()
}
suspend fun loadFromFile(file: File): Boolean = withContext(Dispatchers.IO) {
suspend fun loadFromFile(file: File): Boolean {
fakeLoad()
return@withContext try {
return try {
val jsonFile = File(file.parentFile, "${file.nameWithoutExtension}.json")
if(jsonFile.exists()) {
manga = Gson().fromJson(jsonFile.reader(), Chapter2Return::class.java)
@@ -172,13 +171,15 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
manga?.let {
it.results = Chapter2Return.Results()
it.results.comic = ComicStructure()
it.results.comic.name = file.parentFile?.name
it.results.comic.name = file.parentFile?.parentFile?.name
it.results.chapter = ChapterWithContent()
it.results.chapter.name = file.nameWithoutExtension
it.results.chapter.uuid = uuidArray[position]
wv.get()?.countZipEntries { c ->
it.results.chapter.size = c
prepareManga()
wv.get()?.apply {
it.results.chapter.uuid = uuidArray[position]
countZipEntries { c ->
it.results.chapter.size = c
prepareManga()
}
}
}
}
@@ -190,19 +191,21 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
}
}
private suspend fun fakeLoad() = withContext(Dispatchers.IO) {
if(MainActivity.member?.hasLogin == true) launch {
PausableDownloader(chapterUrl) { _ -> }.run()
}
private fun fakeLoad() {
if (MainActivity.member?.hasLogin == true) Thread {
runBlocking { PausableDownloader(chapterUrl) { _ -> }.run() }
}.start()
}
private suspend fun prepareManga() = withContext(Dispatchers.Main) {
if(comicName == null) {
comicName = manga?.results?.comic?.name
wv.get()?.apply {
if(comicName == null) {
comicName = manga?.results?.comic?.name
}
count = manga?.results?.chapter?.size?:0
initManga()
vprog?.visibility = View.GONE
}
wv.get()?.count = manga?.results?.chapter?.size?:0
wv.get()?.initManga()
wv.get()?.vprog?.visibility = View.GONE
}
private suspend fun loadImagesIntoLine(item: Int = (wv.get()?.currentItem?:0), doAfter: Runnable? = null) = withContext(Dispatchers.IO) {
val maxCount: Int = (wv.get()?.verticalLoadMaxCount?:20)
@@ -220,7 +223,7 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
if(notFull) obtainMessage(PREPARE_LAST_PAGE, loadCount + 1, maxCount).sendToTarget()
obtainMessage(DO_LAMBDA, Runnable{
doAfter?.run()
wv.get()?.updateSeekBar(0)
wv.get()?.apply { lifecycleScope.launch { updateSeekBar(0) } }
}).sendToTarget()
}
}
@@ -283,5 +286,6 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
const val DECREASE_IMAGE_COUNT_AND_RESTORE_PAGE_NUMBER_AT_ZERO = 20
const val DO_LAMBDA = 21
const val SET_NET_INFO = 22
const val SET_DL_TEXT = 23
}
}

View File

@@ -12,10 +12,14 @@ import android.graphics.drawable.Drawable
import android.media.AudioManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.util.TypedValue
import android.view.*
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowInsetsController
import android.widget.SeekBar
import android.widget.Toast
import androidx.appcompat.app.AppCompatDelegate
@@ -115,6 +119,11 @@ class ViewMangaActivity : TitleActivityTemplate() {
val realCount get() = if(cut) indexMap.size else count
var urlArray = arrayOf<String>()
var uuidArray = arrayOf<String>()
var position = 0
var comicName: String? = null
private var zipFile: File? = null
var pn = 0
private val loadImgOnWait = AtomicInteger()
@@ -149,6 +158,11 @@ class ViewMangaActivity : TitleActivityTemplate() {
//dlZip2View = intent.getStringExtra("callFrom") == "Dl" || p["dlZip2View"] == "true"
//zipFirst = intent.getStringExtra("callFrom") == "zipFirst"
intent.getStringArrayExtra("urlArray")?.let { urlArray = it }
intent.getStringArrayExtra("uuidArray")?.let { uuidArray = it }
position = intent.getIntExtra("position", 0)
comicName = intent.getStringExtra("comicName")
zipFile = intent.getStringExtra("zipFile")?.let { File(it) }
pn = intent.getIntExtra("pn", 0)
cut = pb["useCut"]
r2l = pb["r2l"]
verticalLoadMaxCount = settingsPref?.getInt("settings_cat_vm_sb_vertical_max", 20)?.let { if(it > 0) it else 20 }?:20
@@ -221,7 +235,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
)
}
fun restorePN() {
suspend fun restorePN() = withContext(Dispatchers.Main) {
if (isPnValid) {
isInScroll = false
pageNum = pn
@@ -242,41 +256,51 @@ class ViewMangaActivity : TitleActivityTemplate() {
}
@ExperimentalStdlibApi
private fun doPrepareWebImg() = Thread {
private suspend fun doPrepareWebImg() = withContext(Dispatchers.IO) {
getImgUrlArray()?.apply {
if(cut) {
Log.d("MyVM", "is cut, load all pages...")
handler.sendEmptyMessage(VMHandler.DIALOG_SHOW) //showDl
handler.sendEmptyMessage(VMHandler.DIALOG_SHOW) // showDl
isCut = BooleanArray(size)
val analyzedCnt = BooleanArray(size)
forEachIndexed { i, it ->
if(it != null) {
Thread{
DownloadTools.getHttpContent(CMApi.resolution.wrap(CMApi.imageProxy?.wrap(it)?:it), 1024)?.inputStream()?.let {
isCut[i] = canCut(it)
analyzedCnt[i] = true
handler.obtainMessage(VMHandler.SET_DL_TEXT, "$i/$size").sendToTarget()
if(it != null) try {
DownloadTools.getHttpContent(CMApi.resolution.wrap(CMApi.imageProxy?.wrap(it)?:it), 1024)?.inputStream()?.let {
isCut[i] = canCut(it)
}?:run {
withContext(Dispatchers.Main) {
Toast.makeText(this@ViewMangaActivity, R.string.touch_img_error, Toast.LENGTH_SHORT)
.show()
finish()
}
}.start()
Thread.sleep(22)
return@withContext
}
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(this@ViewMangaActivity, R.string.analyze_img_size_error, Toast.LENGTH_SHORT)
.show()
finish()
}
return@withContext
}
}
while (analyzedCnt.count { it } != size) Thread.sleep(233)
isCut.forEachIndexed { index, b ->
Log.d("MyVM", "[$index] cut: $b")
indexMap += index+1
if(b) indexMap += -(index+1)
}
handler.sendEmptyMessage(15) //hideDl
handler.sendEmptyMessage(15) // hideDl
Log.d("MyVM", "load all pages finished")
}
count = size
runOnUiThread { prepareItems() }
prepareItems()
if (notUseVP) prepareDownloadTasks()
}
}.start()
}
@OptIn(ExperimentalStdlibApi::class)
fun initManga() {
suspend fun initManga() = withContext(Dispatchers.IO) {
val uuid = handler.manga?.results?.chapter?.uuid
Log.d("MyVM", "initManga, chapter uuid: $uuid")
if (uuid != null && uuid != "") {
@@ -333,14 +357,13 @@ class ViewMangaActivity : TitleActivityTemplate() {
Log.d("MyVM", "setPageNumber($num)")
if (r2l && !notUseVP) vp.currentItem = realCount - num
else if (notUseVP) {
if(isVertical){
if(isVertical) {
currentItem = num - 1
val offset = currentItem % verticalLoadMaxCount
Log.d("MyVM", "Current: $currentItem, Height: ${psivl.height}, scrollY: ${psivs.scrollY}")
if (!isInScroll || isInSeek) psivs.scrollY = psivl.height * offset / size
updateSeekBar()
}
else {
lifecycleScope.launch { updateSeekBar() }
} else {
currentItem = num - 1
try {
loadOneImg()
@@ -353,23 +376,24 @@ class ViewMangaActivity : TitleActivityTemplate() {
}
} else {
Log.d("MyVM", "Set vp current: ${num-1}")
var delta = num - 1 - vp.currentItem
if(delta >= 1) Thread{
while (delta-- > 0){
Thread.sleep(23)
runOnUiThread {
vp.currentItem++
//var delta = num - 1 - vp.currentItem
vp.currentItem = num - 1
/*lifecycleScope.launch {
withContext(Dispatchers.IO) {
if(delta >= 1) while (delta-- > 0){
delay(20)
withContext(Dispatchers.Main) {
vp.currentItem++
}
}
else if(delta <= -1) while (delta++ < 0){
delay(20)
withContext(Dispatchers.Main) {
vp.currentItem--
}
}
}
}.start()
else if(delta <= -1) Thread{
while (delta++ < 0){
Thread.sleep(23)
runOnUiThread {
vp.currentItem--
}
}
}.start()
}*/
}
}
@@ -565,7 +589,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
@ExperimentalStdlibApi
@SuppressLint("SetTextI18n")
private fun prepareItems() {
private suspend fun prepareItems(): Unit = withContext(Dispatchers.Main) {
try {
prepareVP()
prepareInfoBar()
@@ -588,7 +612,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
}
}
private fun setProgress() {
private suspend fun setProgress() = withContext(Dispatchers.IO) {
handler.manga?.results?.chapter?.uuid?.let {
getPreferences(MODE_PRIVATE).edit {
//it["chapterId"] = hm.chapterId.toString()
@@ -653,18 +677,18 @@ class ViewMangaActivity : TitleActivityTemplate() {
vp.adapter = ViewData(vp).RecyclerViewAdapter()
vp.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
updateSeekBar()
super.onPageSelected(position)
lifecycleScope.launch { updateSeekBar() }
}
})
if (r2l && !isPnValid) vp.currentItem = realCount - 1
}
}
fun updateSeekBar(p: Int = 0) {
suspend fun updateSeekBar(p: Int = 0) = withContext(Dispatchers.Main) {
if (p > 0) {
updateSeekText(p)
return
return@withContext
}
if (!isInSeek) hideDrawer()
updateSeekText()
@@ -677,7 +701,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
oneinfo.alpha = 0F
infseek.visibility = View.GONE
isearch.visibility = View.GONE
inftitle.ttitle.text = handler.manga?.results?.chapter?.name
inftitle.ttitle.text = "$comicName ${handler.manga?.results?.chapter?.name}"
inftxtprogress.text = "$pageNum/$realCount"
infseek.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
var p = 0
@@ -726,7 +750,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
} else isInSeek = false
}
private fun after() {
if(manualCount++ < 3) p = pageNum else updateSeekBar(p)
if(manualCount++ < 3) p = pageNum else lifecycleScope.launch { updateSeekBar(p) }
}
})
isearch.setImageResource(R.drawable.ic_author)
@@ -786,7 +810,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
if(isVertical && (pageNum-1) % verticalLoadMaxCount == 0) {
Log.d("MyVM", "Do scroll back, isVertical: $isVertical, pageNum: $pageNum")
if (isInSeek) {
updateSeekBar(pageNum-1)
(pageNum-1).let { lifecycleScope.launch { updateSeekBar(it) } }
return
}
handler.obtainMessage(
@@ -805,7 +829,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
pageNum++
if(isVertical && (pageNum-1) % verticalLoadMaxCount == 0) {
if (isInSeek) {
updateSeekBar(pageNum+1)
(pageNum+1).let { lifecycleScope.launch { updateSeekBar(it) } }
return
}
handler.sendEmptyMessage(VMHandler.LOAD_SCROLL_MODE)
@@ -917,14 +941,8 @@ class ViewMangaActivity : TitleActivityTemplate() {
}
companion object {
var comicName: String? = null
var uuidArray = arrayOf<String>()
var fileArray = arrayOf<File>()
var position = 0
var zipFile: File? = null
var dlHandler: Handler? = null
var va: WeakReference<ViewMangaActivity>? = null
var pn = 0
var noCellarAlert = false
}
}

View File

@@ -3,7 +3,6 @@ package top.fumiama.copymanga.user
import android.content.SharedPreferences
import android.util.Base64
import com.google.gson.Gson
import com.google.gson.stream.JsonReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.LoginInfoStructure
@@ -16,35 +15,38 @@ import java.nio.charset.Charset
class Member(private val pref: SharedPreferences, private val getString: (Int) -> String) {
val hasLogin: Boolean get() = pref.getString("token", "")?.isNotEmpty()?:false
suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure = withContext(Dispatchers.IO) {
try {
getLoginConnection(username, pwd, salt).apply {
Gson().fromJson<LoginInfoStructure>(
JsonReader(inputStream.reader()), LoginInfoStructure::class.java
)?.let { data ->
disconnect()
if(data.code == 200) {
pref.edit()?.apply {
putString("token", data.results?.token)
putString("user_id", data.results?.user_id)
putString("username", data.results?.username)
putString("nickname", data.results?.nickname)
apply()
return@withContext info()
var err = ""
getLoginConnection(username, pwd, salt).apply {
inputStream.use {
it?.readBytes()?.let { data ->
data.inputStream().use { dataIn ->
try {
Gson().fromJson<LoginInfoStructure>(
dataIn.reader(), LoginInfoStructure::class.java
)?.let { info ->
if(info.code == 200) {
pref.edit()?.apply {
putString("token", info.results?.token)
putString("user_id", info.results?.user_id)
putString("username", info.results?.username)
putString("nickname", info.results?.nickname)
apply()
return@withContext info()
}
}
return@withContext info
}?: run { err = getString(R.string.login_parse_json_error) }
} catch (e: Exception) {
err = data.decodeToString()
}
}
return@withContext data
}
}?: run { err = getString(R.string.login_get_conn_failed) }
}
val l = LoginInfoStructure()
l.code = 400
l.message = getString(R.string.login_get_conn_failed)
return@withContext l
} catch (e: Exception) {
val l = LoginInfoStructure()
l.code = 400
l.message = e.toString()
return@withContext l
}
val l = LoginInfoStructure()
l.code = 400
l.message = err
return@withContext l
}
@@ -61,18 +63,25 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
l.message = getString(R.string.noLogin)
return@withContext l
}
return@withContext try {
val l = Gson().fromJson(DownloadTools.getHttpContent(
try {
val data = DownloadTools.getHttpContent(
getString(R.string.memberInfoApiUrl).format(CMApi.myHostApiUrl).let {
CMApi.apiProxy?.wrap(it)?:it
}
).decodeToString(),
LoginInfoStructure::class.java)
if(l.code == 200) pref.edit()?.apply {
putString("avatar", l.results.avatar)
apply()
).decodeToString()
try {
val l = Gson().fromJson(data, LoginInfoStructure::class.java)
if(l.code == 200) pref.edit()?.apply {
putString("avatar", l.results.avatar)
apply()
}
l
} catch (e : Exception) {
val l = LoginInfoStructure()
l.code = 450
l.message = "${getString(R.string.login_get_avatar_failed)}: $data"
l
}
l
} catch (e: Exception) {
val l = LoginInfoStructure()
l.code = 450

View File

@@ -56,6 +56,8 @@
<string name="load_page_number_error">第%1$d页加载异常</string>
<string name="load_chapter_error">加载章节错误</string>
<string name="show_image_error_try_lower_resolution">图片加载错误,请尝试下载后使用较低图片质量查看</string>
<string name="touch_img_error">预载图片头失败</string>
<string name="analyze_img_size_error">读取图片大小失败</string>
<string name="mainPageApiUrl">https://%1$s/api/v3/h5/homeIndex?platform=3</string>
<string name="referUrl">https://%1$s</string>
@@ -171,7 +173,8 @@
<string name="login_null_username">用户名为空</string>
<string name="login_null_pwd">密码为空</string>
<string name="login_get_conn_failed">登录失败</string>
<string name="login_get_avatar_failed">恢复登录失败</string>
<string name="login_parse_json_error">解析返回数据失败</string>
<string name="login_get_avatar_failed">获取用户信息失败</string>
<string name="login_restart_to_apply">重启应用以彻底退出登录</string>
<string name="old_download_card_name">前往旧版下载</string>