1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-23 02:50:23 +08:00

2.0.beta6

1. 修复漫画封面为空导致的主页闪退
2. 增加详情页导航到作者、标签
3. 增加阅览缓存、音量键页、双页切分
4. 优化卷轴模式、下载逻辑
This commit is contained in:
fumiama
2021-05-14 22:34:29 +08:00
parent c366d62098
commit 16dd13e902
21 changed files with 665 additions and 352 deletions

View File

@@ -4,6 +4,7 @@
<w>copymanga</w> <w>copymanga</w>
<w>fileprovider</w> <w>fileprovider</w>
<w>fumiama</w> <w>fumiama</w>
<w>statuscardflow</w>
</words> </words>
</dictionary> </dictionary>
</component> </component>

View File

@@ -28,8 +28,8 @@ android {
applicationId 'top.fumiama.copymanga' applicationId 'top.fumiama.copymanga'
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 30 targetSdkVersion 30
versionCode 16 versionCode 17
versionName '2.0.beta5' versionName '2.0.beta6'
resConfigs "zh", "zh-rCN" resConfigs "zh", "zh-rCN"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -0,0 +1,75 @@
package top.fumiama.copymanga.template.ui
import android.animation.ObjectAnimator
import android.view.View
import kotlinx.android.synthetic.main.anchor_popular.view.*
import kotlinx.android.synthetic.main.line_finish.*
import kotlinx.android.synthetic.main.line_lazybooklines.*
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.dmzj.copymanga.R
@ExperimentalStdlibApi
open class StatusCardFlow(private val api: Int, nav: Int) : InfoCardLoader(R.layout.fragment_statuscardflow, nav) {
val sortWay = listOf("datetime_updated", "-datetime_updated", "popular", "-popular")
var sortValue = 0
override fun getApiUrl() =
getString(api).let {
String.format(
it,
page * 21,
sortWay[sortValue]
)
}
override fun setListeners() {
super.setListeners()
setUpdate(line_finish_time)
setHot(line_finish_pop)
}
override fun onLoadFinish() {
super.onLoadFinish()
mainWeakReference?.get()?.runOnUiThread {
mypl.visibility = View.GONE
}
}
open fun setUpdate(that: View) {
that.apply {
apt.setText(R.string.menu_update_time)
setOnClickListener {
sortValue = if(apim.rotation == 0f) {
ObjectAnimator.ofFloat(apim, "rotation", 0f, 180f).setDuration(233).start()
1
}else{
ObjectAnimator.ofFloat(apim, "rotation", 180f, 0f).setDuration(233).start()
0
}
Thread{
Thread.sleep(400)
mh?.sendEmptyMessage(4)
}.start()
}
}
}
open fun setHot(that: View) {
that.apply {
apt.setText(R.string.menu_hot)
setOnClickListener {
sortValue = if (apim.rotation == 0f) {
ObjectAnimator.ofFloat(apim, "rotation", 0f, 180f).setDuration(233).start()
1
} else {
ObjectAnimator.ofFloat(apim, "rotation", 180f, 0f).setDuration(233).start()
0
}
Thread {
Thread.sleep(400)
mh?.sendEmptyMessage(4)
}.start()
}
}
}
}

View File

@@ -0,0 +1,36 @@
package top.fumiama.copymanga.template.ui
import android.os.Bundle
import kotlinx.android.synthetic.main.app_bar_main.*
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
@ExperimentalStdlibApi
open class ThemeCardFlow(private val api: Int, nav: Int) : StatusCardFlow(0, nav) {
private var theme = ""
override fun getApiUrl() =
getString(api).let {
String.format(
it,
page * 21,
sortWay[sortValue],
theme
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.apply {
getString("path")?.apply { theme = this }
getString("name")?.apply {
mainWeakReference?.get()?.toolbar?.title = this
}
}
}
override fun onResume() {
super.onResume()
arguments?.getString("name")?.apply {
mainWeakReference?.get()?.toolbar?.title = this
}
}
}

View File

@@ -3,7 +3,6 @@ package top.fumiama.copymanga.tools.http
import android.util.Log import android.util.Log
import top.fumiama.copymanga.tools.ssl.AllTrustManager import top.fumiama.copymanga.tools.ssl.AllTrustManager
import top.fumiama.copymanga.tools.ssl.IgnoreHostNameVerifier import top.fumiama.copymanga.tools.ssl.IgnoreHostNameVerifier
import java.io.File
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
import java.security.SecureRandom import java.security.SecureRandom
@@ -25,24 +24,31 @@ object DownloadTools {
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.socketFactory) HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.socketFactory)
} }
private fun getConnection(url: String?, method: String = "GET") =
url?.let {
val connection = URL(url).openConnection() as HttpURLConnection
connection.requestMethod = method
connection.connectTimeout = 20000
connection.readTimeout = 20000
connection
}
fun getHttpContent(Url: String, refer: String? = null, ua: String? = null): ByteArray? { fun getHttpContent(Url: String, refer: String? = null, ua: String? = null): ByteArray? {
Log.d("Mydl", "getHttp: $Url") Log.d("Mydl", "getHttp: $Url")
var ret: ByteArray? = null var ret: ByteArray? = null
val task = FutureTask(Callable { val task = FutureTask(Callable {
try { try {
val connection = URL(Url).openConnection() as HttpURLConnection getConnection(Url)?.apply {
connection.requestMethod = "GET" refer?.let { setRequestProperty("referer", it) }
connection.connectTimeout = 20000 setRequestProperty("source", "copyApp")
connection.readTimeout = 20000 setRequestProperty("webp", "1")
refer?.let { connection.setRequestProperty("referer", it) } setRequestProperty("region", "1")
connection.setRequestProperty("source", "copyApp") setRequestProperty("platform", "3")
connection.setRequestProperty("webp", "1") ua?.let { setRequestProperty("User-agent", it) }
connection.setRequestProperty("region", "1")
connection.setRequestProperty("platform", "3")
ua?.let { connection.setRequestProperty("User-agent", it) }
ret = connection.inputStream.readBytes() ret = inputStream.readBytes()
connection.disconnect() disconnect()
}
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
} }
@@ -56,78 +62,17 @@ object DownloadTools {
null null
} }
} }
fun downloadUsingUrlRet(Url: String?, f: File): Boolean {
Log.d("Mydl", "Ret Get Url: $Url, File: $f")
val task = FutureTask(Callable {
try {
val connection = URL(Url).openConnection() as HttpURLConnection
connection.requestMethod = "GET"
connection.connectTimeout = 20000
connection.readTimeout = 20000
connection.setRequestProperty("source", "copyApp")
connection.setRequestProperty("webp", "1")
connection.setRequestProperty("region", "1")
connection.setRequestProperty("platform", "3")
if (f.exists()) f.delete()
else f.parentFile?.mkdirs()
f.parentFile?.let {
if (!it.canRead()) it.setReadable(true)
if (!it.canWrite()) it.setWritable(true)
}
connection.inputStream.buffered().copyTo(f.outputStream())
connection.disconnect()
return@Callable true
} catch (ex: Exception) {
ex.printStackTrace()
return@Callable false
}
})
Thread(task).start()
return try {
task.get()
} catch (ex: Exception) {
ex.printStackTrace()
false
}
}
fun downloadUsingUrl(Url: String?, f: File, refer: String? = null) {
Log.d("Mydl", "Get Url: $Url, File: $f")
Thread(Runnable {
try {
val connection = URL(Url).openConnection() as HttpURLConnection
connection.requestMethod = "GET"
connection.connectTimeout = 20000
connection.readTimeout = 20000
refer?.let { connection.setRequestProperty("referer", it) }
if (f.exists()) f.delete()
else f.parentFile?.mkdirs()
f.parentFile?.let {
if (!it.canRead()) it.setReadable(true)
if (!it.canWrite()) it.setWritable(true)
}
connection.inputStream.buffered().copyTo(f.outputStream())
connection.disconnect()
} catch (ex: Exception) {
ex.printStackTrace()
}
}).start()
}
fun getHttpContent(Url: String, refer: String? = null): ByteArray? { fun getHttpContent(Url: String, refer: String? = null): ByteArray? {
Log.d("Mydl", "getHttp: $Url") Log.d("Mydl", "getHttp: $Url")
var ret: ByteArray? = null var ret: ByteArray? = null
val task = FutureTask(Callable { val task = FutureTask(Callable {
try { try {
val connection = URL(Url).openConnection() as HttpURLConnection val connection = getConnection(Url)
connection.requestMethod = "GET" refer?.let { connection?.setRequestProperty("referer", it) }
connection.connectTimeout = 20000
connection.readTimeout = 20000
refer?.let { connection.setRequestProperty("referer", it) }
ret = connection.inputStream.readBytes() ret = connection?.inputStream?.readBytes()
connection.disconnect() connection?.disconnect()
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
} }
@@ -141,4 +86,55 @@ object DownloadTools {
null null
} }
} }
fun getHttpContent(Url: String, readSize: Int? = null, refer: String? = "https://api.copymanga.com"): ByteArray? {
Log.d("Mydl", "getHttp: $Url")
var ret: ByteArray? = null
val task = FutureTask(Callable {
try {
val connection = getConnection(Url)
refer?.let { connection?.setRequestProperty("referer", it) }
val ci = connection?.inputStream
if(readSize != null) {
ret = ByteArray(readSize)
ci?.read(ret, 0, readSize)
} else ret = ci?.readBytes()
ci?.close()
connection?.disconnect()
} catch (ex: Exception) {
ex.printStackTrace()
}
return@Callable ret
})
Thread(task).start()
return try {
task.get()
} catch (ex: Exception) {
ex.printStackTrace()
null
}
}
fun touch(url: String?, refer: String? = "https://www.dmzj1.com"): FutureTask<ByteArray?>? =
url?.let {
Log.d("Mydl", "touchHttp: $it")
var ret: ByteArray? = null
val task = FutureTask(Callable {
try {
val connection = getConnection(it)
refer?.let { connection?.setRequestProperty("referer", it) }
val ci = connection?.inputStream
ret = ci?.readBytes()
ci?.close()
connection?.disconnect()
} catch (ex: Exception) {
ex.printStackTrace()
}
return@Callable ret
})
Thread(task).start()
task
}
} }

View File

@@ -19,6 +19,7 @@ class MangaDlTools {
private var comicFileRelative: String? = null private var comicFileRelative: String? = null
var size = 0 var size = 0
var complete = false var complete = false
var wait = false
fun downloadChapterInVol(url: CharSequence, chapterName: CharSequence, group: CharSequence, index: Int){ fun downloadChapterInVol(url: CharSequence, chapterName: CharSequence, group: CharSequence, index: Int){
comicFileRelative = "$group/$chapterName.zip" comicFileRelative = "$group/$chapterName.zip"
@@ -72,6 +73,8 @@ class MangaDlTools {
zip.setLevel(9) zip.setLevel(9)
var succeed = true var succeed = true
for (i in urls.indices) { for (i in urls.indices) {
while (wait && !exit) sleep(1000)
if (exit) break
zip.putNextEntry(ZipEntry("$i.webp")) zip.putNextEntry(ZipEntry("$i.webp"))
var tryTimes = 3 var tryTimes = 3
var s = false var s = false
@@ -89,7 +92,6 @@ class MangaDlTools {
if (!s && tryTimes <= 0) succeed = false if (!s && tryTimes <= 0) succeed = false
onDownloadedListener?.handleMessage(s, i + 1) onDownloadedListener?.handleMessage(s, i + 1)
zip.flush() zip.flush()
if (exit) break
} }
zip.close() zip.close()
onDownloadedListener?.handleMessage(succeed) onDownloadedListener?.handleMessage(succeed)

View File

@@ -5,6 +5,7 @@ import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.View import android.view.View
import androidx.navigation.Navigation import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.app_bar_main.*
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.template.general.NoBackRefreshFragment import top.fumiama.copymanga.template.general.NoBackRefreshFragment
@@ -28,7 +29,10 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
mainWeakReference?.get()?.menuMain?.let { setMenuVisible(it) } mainWeakReference?.get()?.apply {
menuMain?.let { setMenuVisible(it) }
toolbar.title = bookHandler.book?.results?.comic?.name
}
} }
override fun onDestroy() { override fun onDestroy() {
@@ -37,6 +41,11 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
bookHandler.destroy() bookHandler.destroy()
} }
override fun onPause() {
super.onPause()
mainWeakReference?.get()?.menuMain?.let { setMenuInvisible(it) }
}
private fun setMenuInvisible(menu: Menu){ private fun setMenuInvisible(menu: Menu){
menu.findItem(R.id.action_download)?.isVisible = false menu.findItem(R.id.action_download)?.isVisible = false
} }

View File

@@ -17,6 +17,7 @@ import kotlinx.android.synthetic.main.fragment_book.*
import kotlinx.android.synthetic.main.line_2chapters.view.* import kotlinx.android.synthetic.main.line_2chapters.view.*
import kotlinx.android.synthetic.main.line_bookinfo.* import kotlinx.android.synthetic.main.line_bookinfo.*
import kotlinx.android.synthetic.main.line_bookinfo_text.* import kotlinx.android.synthetic.main.line_bookinfo_text.*
import kotlinx.android.synthetic.main.line_caption.view.*
import kotlinx.android.synthetic.main.line_chapter.view.* import kotlinx.android.synthetic.main.line_chapter.view.*
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
@@ -52,7 +53,7 @@ class BookHandler(that: WeakReference<BookFragment>, path: String)
1 -> setCover() 1 -> setCover()
2 -> setTexts() 2 -> setTexts()
3 -> fbibinfo?.let { setInfoHeight(it) } 3 -> fbibinfo?.let { setInfoHeight(it) }
//4 -> setThemes() 4 -> setThemes()
5 -> setOverScale() 5 -> setOverScale()
6 -> endSetLayouts() 6 -> endSetLayouts()
} }
@@ -147,44 +148,60 @@ class BookHandler(that: WeakReference<BookFragment>, path: String)
} }
} }
private fun setThemes(){ private fun setTheme(caption: String, themeStructure: Array<ThemeStructure>, nav: Int) {
book?.results?.groups?.let { that?.apply {
val keyIterator = it.keys.iterator() val t = layoutInflater.inflate(R.layout.line_caption, fbl, false)
for(i in 0 until it.size){ t.tcptn.text = caption
if(i % 2 == 0){ fbl.addView(t)
that?.fbl?.addView(if(i < it.size - 1){ fbl.addView(layoutInflater.inflate(R.layout.div_h, fbl, false))
val line = that.layoutInflater.inflate(R.layout.line_2chapters, that.fbl, false) }
val leftKey = keyIterator.next() var line: View? = null
line?.l2cl?.lct?.text = it[leftKey]?.name val last = themeStructure.size - 1
line?.l2cl?.setOnClickListener { _-> themeStructure.onEachIndexed { i, it ->
loadVolume(it[leftKey]?.path_word?:"null") if(line == null) {
if(i == last) {
line = that?.layoutInflater?.inflate(R.layout.line_chapter, that.fbl, false)
line?.lcc?.apply {
lct.text = it.name
setOnClickListener { _ ->
loadVolume(it.name, it.path_word, nav)
} }
val rightKey = keyIterator.next() }
line?.l2cr?.lct?.text = it[rightKey]?.name that?.fbl?.addView(line)
line?.l2cr?.setOnClickListener { _-> } else {
loadVolume(it[rightKey]?.path_word?:"null") line = that?.layoutInflater?.inflate(R.layout.line_2chapters, that.fbl, false)
line?.l2cl?.apply {
lct.text = it.name
setOnClickListener { _ ->
loadVolume(it.name, it.path_word, nav)
} }
line }
}else{
//Log.d("MyBH", "Add chapter: ${vol[i].volume_name}")
val line = that.layoutInflater.inflate(R.layout.line_chapter, that.fbl, false)
val key = keyIterator.next()
line?.lct?.text = it[key]?.name
line?.lcc?.setOnClickListener { _->
loadVolume(it[key]?.path_word?:"null")
}
line
})
} }
} else line?.l2cr?.apply {
lct.text = it.name
setOnClickListener { _ ->
loadVolume(it.name, it.path_word, nav)
}
that?.fbl?.addView(line)
line = null
} }
} }
} }
private fun loadVolume(gpw: String){ private fun setThemes(){
that?.apply {
book?.results?.comic?.apply {
author?.let { setTheme(getString(R.string.author), it, R.id.action_nav_book_to_nav_author) }
theme?.let { setTheme(getString(R.string.caption), it, R.id.action_nav_book_to_nav_caption) }
}
}
}
private fun loadVolume(name: String, path: String, nav: Int){
Log.d("MyBH", "start to load chapter") Log.d("MyBH", "start to load chapter")
val bundle = Bundle() val bundle = Bundle()
bundle.putString("group", gpw) bundle.putString("name", name)
book?.results?.comic?.path_word?.let { bundle.putString("path", it) } bundle.putString("path", path)
that?.rootView?.let { Navigation.findNavController(it).navigate(R.id.action_nav_book_to_nav_chapter, bundle) } that?.rootView?.let { Navigation.findNavController(it).navigate(nav, bundle) }
} }
} }

View File

@@ -0,0 +1,7 @@
package top.fumiama.copymanga.ui.cardflow.author
import top.fumiama.copymanga.template.ui.ThemeCardFlow
import top.fumiama.dmzj.copymanga.R
@ExperimentalStdlibApi
class AuthorFragment : ThemeCardFlow(R.string.authorApiUrl, R.id.action_nav_author_to_nav_book)

View File

@@ -0,0 +1,7 @@
package top.fumiama.copymanga.ui.cardflow.caption
import top.fumiama.copymanga.template.ui.ThemeCardFlow
import top.fumiama.dmzj.copymanga.R
@ExperimentalStdlibApi
class CaptionFragment : ThemeCardFlow(R.string.captionApiUrl, R.id.action_nav_caption_to_nav_book)

View File

@@ -1,77 +1,7 @@
package top.fumiama.copymanga.ui.cardflow.finish package top.fumiama.copymanga.ui.cardflow.finish
import android.animation.ObjectAnimator import top.fumiama.copymanga.template.ui.StatusCardFlow
import android.view.View
import kotlinx.android.synthetic.main.anchor_popular.view.*
import kotlinx.android.synthetic.main.line_finish.*
import kotlinx.android.synthetic.main.line_lazybooklines.*
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.template.ui.InfoCardLoader
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.lang.Thread.sleep
@ExperimentalStdlibApi @ExperimentalStdlibApi
class FinishFragment : InfoCardLoader(R.layout.fragment_finish, R.id.action_nav_finish_to_nav_book) { class FinishFragment : StatusCardFlow(R.string.finishApiUrl, R.id.action_nav_finish_to_nav_book)
private val sortWay = listOf("datetime_updated", "-datetime_updated", "popular", "-popular")
private var sortValue = 0
override fun getApiUrl() =
getString(R.string.finishApiUrl).let {
String.format(
it,
page * 21,
sortWay[sortValue]
)
}
override fun setListeners() {
super.setListeners()
setUpdate()
setHot()
}
override fun onLoadFinish() {
super.onLoadFinish()
mainWeakReference?.get()?.runOnUiThread {
mypl.visibility = View.GONE
}
}
private fun setUpdate() {
line_finish_time.apply {
apt.setText(R.string.menu_update_time)
setOnClickListener {
sortValue = if(apim.rotation == 0f) {
ObjectAnimator.ofFloat(apim, "rotation", 0f, 180f).setDuration(233).start()
1
}else{
ObjectAnimator.ofFloat(apim, "rotation", 180f, 0f).setDuration(233).start()
0
}
Thread{
sleep(400)
mh?.sendEmptyMessage(4)
}.start()
}
}
}
private fun setHot() {
line_finish_pop.apply {
apt.setText(R.string.menu_hot)
setOnClickListener {
sortValue = if (apim.rotation == 0f) {
ObjectAnimator.ofFloat(apim, "rotation", 0f, 180f).setDuration(233).start()
1
} else {
ObjectAnimator.ofFloat(apim, "rotation", 180f, 0f).setDuration(233).start()
0
}
Thread {
sleep(400)
mh?.sendEmptyMessage(4)
}.start()
}
}
}
}

View File

@@ -60,7 +60,6 @@ class ComicDlHandler(looper: Looper, that: WeakReference<ComicDlFragment>, priva
private var checkedChapter = 0 private var checkedChapter = 0
private var dldChapter = 0 private var dldChapter = 0
private var haveDlStarted = false private var haveDlStarted = false
private var canDl = false
private var tbtnlist: Array<ChapterToggleButton> = arrayOf() private var tbtnlist: Array<ChapterToggleButton> = arrayOf()
private var tbtncnt = 0 private var tbtncnt = 0
private var isNewTitle = false private var isNewTitle = false
@@ -215,10 +214,12 @@ class ComicDlHandler(looper: Looper, that: WeakReference<ComicDlFragment>, priva
setProgress2(dldChapter * 100 / checkedChapter, 233) setProgress2(dldChapter * 100 / checkedChapter, 233)
} }
private fun updateProgressBar(pageNow: Int, size: Int) { private fun updateProgressBar(pageNow: Int, size: Int) {
val delta = 100 / checkedChapter if(checkedChapter > 0) {
val start = dldChapter * delta val delta = 100 / checkedChapter
val now = pageNow * delta / size val start = dldChapter * delta
setProgress2(start + now, 64) val now = pageNow * delta / size
setProgress2(start + now, 64)
}
} }
private fun setProgress2(end: Int, duration: Long) { private fun setProgress2(end: Int, duration: Long) {
ObjectAnimator.ofInt( ObjectAnimator.ofInt(
@@ -259,10 +260,10 @@ class ComicDlHandler(looper: Looper, that: WeakReference<ComicDlFragment>, priva
else if(checkedChapter == 0) hideDlCard() else if(checkedChapter == 0) hideDlCard()
else{ else{
that.pdwn.progress = 0 that.pdwn.progress = 0
if (canDl || checkedChapter == 0) canDl = false if(haveDlStarted && checkedChapter != 0) mangaDlTools.wait = !mangaDlTools.wait
else { else {
haveDlStarted = true haveDlStarted = true
canDl = true mangaDlTools.wait = false
Thread{ Thread{
sendEmptyMessage(9) //set dl card color to green sendEmptyMessage(9) //set dl card color to green
downloadMangas() downloadMangas()
@@ -291,16 +292,8 @@ class ComicDlHandler(looper: Looper, that: WeakReference<ComicDlFragment>, priva
private fun downloadMangas(){ private fun downloadMangas(){
for (i in tbtnlist) { for (i in tbtnlist) {
if (i.isChecked) downloadChapterPages(i) if (i.isChecked) downloadChapterPages(i)
if (!canDl) {
checkedChapter -= dldChapter
dldChapter = 0
break
}
}
if (canDl) {
haveDlStarted = false
canDl = false
} }
haveDlStarted = false
} }
private fun downloadChapterPages(i: ChapterToggleButton) { private fun downloadChapterPages(i: ChapterToggleButton) {

View File

@@ -310,7 +310,9 @@ class HomeHandler(that: WeakReference<HomeFragment>) : AutoDownloadHandler(
private fun setCards(cv: CardView, pw: String, name: String, img: String, isFinal: Boolean, isTopic: Boolean) { private fun setCards(cv: CardView, pw: String, name: String, img: String, isFinal: Boolean, isTopic: Boolean) {
cv.tic.text = name cv.tic.text = name
homeF?.let { homeF?.let {
Glide.with(it).load(GlideUrl(img, CMApi.myGlideHeaders)).timeout(20000).into(cv.imic) if(img.startsWith("http")) {
Glide.with(it).load(GlideUrl(img, CMApi.myGlideHeaders)).timeout(20000).into(cv.imic)
}
} }
if (isFinal) cv.sgnic.visibility = View.VISIBLE if (isFinal) cv.sgnic.visibility = View.VISIBLE
cv.setOnClickListener { cv.setOnClickListener {

View File

@@ -21,7 +21,7 @@ class PagesManager(w: WeakReference<ViewMangaActivity>) {
toPage(v?.r2l!=true) toPage(v?.r2l!=true)
} }
private fun judgePrevious() = v?.pageNum?:0 > 1 private fun judgePrevious() = v?.pageNum?:0 > 1
private fun judgeNext() = v?.pageNum?:0 < v?.count?:0 private fun judgeNext() = v?.pageNum?:0 < v?.realCount?:0
@ExperimentalStdlibApi @ExperimentalStdlibApi
fun toPage(goNext:Boolean){ fun toPage(goNext:Boolean){
if (v?.clicked == false) { if (v?.clicked == false) {

View File

@@ -78,19 +78,19 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
} }
4 -> { 4 -> {
val simg = msg.obj as ScaleImageView val simg = msg.obj as ScaleImageView
wv.get()?.loadImgOn(simg, msg.arg1) wv.get()?.loadImgOn(simg, msg.arg1, msg.arg2)
simg.setHeight2FitImgWidth() //simg.setHeight2FitImgWidth()
if(msg.arg2 == 1) sendEmptyMessage(8) //if(msg.arg2 == 1) sendEmptyMessage(8)
} }
5 -> wv.get()?.clearImgOn(msg.obj as ScaleImageView) 5 -> wv.get()?.clearImgOn(msg.obj as ScaleImageView)
6 -> wv.get()?.prepareLastPage(msg.arg1, msg.arg2) 6 -> wv.get()?.prepareLastPage(msg.arg1, msg.arg2)
7 -> dl?.show() 7 -> dl?.show()
8 -> Thread{ 8 -> Thread{
sleep(233) sleep(233)
sendEmptyMessage(13) sendEmptyMessage(13)
}.start() }.start()
9 -> loadThread(msg.arg1) 9 -> loadScrollMode(msg.arg1)
10 -> loadThread() 10 -> loadScrollMode()
11 -> loadImgsIntoLine(msg.arg1) 11 -> loadImgsIntoLine(msg.arg1)
12 -> loadImgsIntoLine() 12 -> loadImgsIntoLine()
13 -> { 13 -> {
@@ -98,10 +98,12 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
wv.get()?.restorePN() wv.get()?.restorePN()
} }
14 -> { 14 -> {
val item = (pn - 1) / (wv.get()?.verticalLoadMaxCount?:40) * (wv.get()?.verticalLoadMaxCount?:40) val item = (pn - 1) / (wv.get()?.verticalLoadMaxCount?:20) * (wv.get()?.verticalLoadMaxCount?:20)
loadThread(item) loadScrollMode(item)
Log.d("MyVMH", "Load page from $item") Log.d("MyVMH", "Load page from $item")
} }
15 -> dl?.hide()
//16 -> wv.get()?.prepareItems()
22 -> wv.get()?.idtime?.text = SimpleDateFormat("HH:mm").format(Date()) + week + wv.get()?.toolsBox?.netinfo 22 -> wv.get()?.idtime?.text = SimpleDateFormat("HH:mm").format(Date()) + week + wv.get()?.toolsBox?.netinfo
} }
} }
@@ -128,7 +130,10 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
fun loadFromFile(file: File): Boolean { fun loadFromFile(file: File): Boolean {
return try { return try {
val jsonFile = File(file.parentFile, "${file.nameWithoutExtension}.json") val jsonFile = File(file.parentFile, "${file.nameWithoutExtension}.json")
if(jsonFile.exists()) manga = Gson().fromJson(jsonFile.reader(), Chapter2Return::class.java) if(jsonFile.exists()) {
manga = Gson().fromJson(jsonFile.reader(), Chapter2Return::class.java)
prepareManga()
}
else{ else{
manga = Chapter2Return() manga = Chapter2Return()
manga?.let { manga?.let {
@@ -137,10 +142,12 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
it.results.comic.name = file.parentFile?.name it.results.comic.name = file.parentFile?.name
it.results.chapter = ChapterWithContent() it.results.chapter = ChapterWithContent()
it.results.chapter.name = file.nameWithoutExtension it.results.chapter.name = file.nameWithoutExtension
it.results.chapter.size = countZipEntries(file) wv.get()?.countZipEntries { c ->
it.results.chapter.size = c
prepareManga()
}
} }
} }
prepareManga()
true true
}catch (e: Exception){ }catch (e: Exception){
e.printStackTrace() e.printStackTrace()
@@ -149,23 +156,6 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
} }
} }
private fun countZipEntries(file: File): Int{
var count = 0
try {
val zip = ZipInputStream(file.inputStream().buffered())
var entry = zip.nextEntry
while (entry != null) {
if (!entry.isDirectory) count++
entry = zip.nextEntry
}
zip.closeEntry()
zip.close()
} catch (e: Exception) {
wv.get()?.toolsBox?.toastError("统计zip图片数错误!")
}
return count
}
@ExperimentalStdlibApi @ExperimentalStdlibApi
private fun prepareManga(){ private fun prepareManga(){
comicName = manga?.results?.comic?.name comicName = manga?.results?.comic?.name
@@ -174,29 +164,33 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
wv.get()?.initManga() wv.get()?.initManga()
wv.get()?.vprog?.visibility = View.GONE wv.get()?.vprog?.visibility = View.GONE
} }
private fun loadImgsIntoLine(item: Int = (wv.get()?.currentItem?:0), maxCount: Int = (wv.get()?.verticalLoadMaxCount?:40)){ private fun loadImgsIntoLine(item: Int = (wv.get()?.currentItem?:0), maxCount: Int = (wv.get()?.verticalLoadMaxCount?:20)) /*= Thread*/{
Log.d("MyVMH", "Fun: loadImgsIntoLine($item)") Log.d("MyVMH", "Fun: loadImgsIntoLine($item, $maxCount)")
val count = wv.get()?.count?.minus(1)?:0 wv.get()?.realCount?.let { count ->
val notFull = item + maxCount > count if(count > 0){
val loadCount = (if(notFull) count - item else maxCount) - 1 val notFull = item + maxCount > count
Log.d("MyVMH", "loadCount: $loadCount") val loadCount = (if(notFull) count - item else maxCount) - 1
if(loadCount >= 0) for(i in 0..loadCount) obtainMessage(4,item + i, if(i == loadCount - 1)1 else 0, wv.get()?.scrollImages?.get(i)).sendToTarget() Log.d("MyVMH", "count: $count, loadCount: $loadCount, notFull: $notFull")
else sendEmptyMessage(8) if(loadCount >= 0) for(i in 0..loadCount) obtainMessage(4,item + i, if(i == loadCount - 1) 1 else 0, wv.get()?.scrollImages?.get(i)).sendToTarget()
if(notFull) obtainMessage(6, loadCount + 1, maxCount).sendToTarget() else sendEmptyMessage(8)
} if(notFull) obtainMessage(6, loadCount + 1, maxCount).sendToTarget()
wv.get()?.updateSeekBar()
}
}
}//.start()
private fun loadThread() = Thread{ private fun loadScrollMode() {
sendEmptyMessage(7) sendEmptyMessage(7)
//sleep(233) //sleep(233)
sendEmptyMessage(12) sendEmptyMessage(12)
}.start() }
private fun loadThread(item: Int) = Thread{ private fun loadScrollMode(item: Int) {
sendEmptyMessage(7) sendEmptyMessage(7)
//sleep(233) //sleep(233)
Log.d("MyVMH", "loadImgsIntoLine($item)") Log.d("MyVMH", "loadImgsIntoLine($item)")
obtainMessage(11, item, 0).sendToTarget() obtainMessage(11, item, 0).sendToTarget()
}.start() }
private fun showInfCard() { private fun showInfCard() {
Log.d("MyVMH", "Read info drawer delta: $delta") Log.d("MyVMH", "Read info drawer delta: $delta")

View File

@@ -2,8 +2,10 @@ package top.fumiama.copymanga.ui.vm
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Service
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.media.AudioManager
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.util.Log import android.util.Log
@@ -14,6 +16,8 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
import com.liaoinstan.springview.widget.SpringView import com.liaoinstan.springview.widget.SpringView
import kotlinx.android.synthetic.main.activity_viewmanga.* import kotlinx.android.synthetic.main.activity_viewmanga.*
import kotlinx.android.synthetic.main.line_header.view.* import kotlinx.android.synthetic.main.line_header.view.*
@@ -28,6 +32,7 @@ import kotlinx.android.synthetic.main.widget_titlebar.view.*
import kotlinx.android.synthetic.main.widget_viewmangainfo.* import kotlinx.android.synthetic.main.widget_viewmangainfo.*
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.template.general.TitleActivityTemplate import top.fumiama.copymanga.template.general.TitleActivityTemplate
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.http.DownloadTools import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.copymanga.tools.thread.TimeThread import top.fumiama.copymanga.tools.thread.TimeThread
@@ -35,8 +40,10 @@ import top.fumiama.copymanga.views.ScaleImageView
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.InputStream
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.* import java.util.*
import java.util.concurrent.FutureTask
import java.util.zip.ZipFile import java.util.zip.ZipFile
class ViewMangaActivity : TitleActivityTemplate() { class ViewMangaActivity : TitleActivityTemplate() {
@@ -49,23 +56,32 @@ class ViewMangaActivity : TitleActivityTemplate() {
//private var progressLog: PropertiesTools? = null //private var progressLog: PropertiesTools? = null
var scrollImages = arrayOf<ScaleImageView>() var scrollImages = arrayOf<ScaleImageView>()
//var zipFirst = false //var zipFirst = false
private var useFullScreen = false //private var useFullScreen = false
var r2l = true var r2l = true
var currentItem = 0 var currentItem = 0
var verticalLoadMaxCount = 40 var verticalLoadMaxCount = 20
private var notUseVP = true private var notUseVP = true
private var isVertical = false private var isVertical = false
private var q = 90 private var q = 100
private val size get() = if(count / verticalLoadMaxCount > currentItem / verticalLoadMaxCount) verticalLoadMaxCount else count % verticalLoadMaxCount private val size get() = if(realCount / verticalLoadMaxCount > currentItem / verticalLoadMaxCount) verticalLoadMaxCount else realCount % verticalLoadMaxCount
var infoDrawerDelta = 0f var infoDrawerDelta = 0f
var pageNum: Int var pageNum: Int
get() = getPageNumber() get() = getPageNumber()
set(value) = setPageNumber(value) set(value) = setPageNumber(value)
//var pn = 0 //var pn = 0
private val isPnValid: Boolean get(){ private val isPnValid: Boolean get(){
if(pn == -2) pn = count if(pn == -2) pn = realCount
return intent.getStringExtra("function") == "log" && pn > 0 return intent.getStringExtra("function") == "log" && pn > 0
} }
private var tasks: Array<FutureTask<ByteArray?>?>? = null
private var destroy = false
private var cut = false
private var isCut = booleanArrayOf()
private var indexMap = intArrayOf()
private var volTurnPage = false
private var am: AudioManager? = null
private var pm: PagesManager? = null
val realCount get() = if(cut) indexMap.size else count
@ExperimentalStdlibApi @ExperimentalStdlibApi
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@@ -76,17 +92,19 @@ class ViewMangaActivity : TitleActivityTemplate() {
//progressLog = PropertiesTools(File("$filesDir/progress/${chapter2Return?.results?.chapter?.comic_id}")) //progressLog = PropertiesTools(File("$filesDir/progress/${chapter2Return?.results?.chapter?.comic_id}"))
//dlZip2View = intent.getStringExtra("callFrom") == "Dl" || p["dlZip2View"] == "true" //dlZip2View = intent.getStringExtra("callFrom") == "Dl" || p["dlZip2View"] == "true"
//zipFirst = intent.getStringExtra("callFrom") == "zipFirst" //zipFirst = intent.getStringExtra("callFrom") == "zipFirst"
useFullScreen = p["useFullScreen"] != "true" cut = p["useCut"] == "true"
r2l = p["r2l"] == "true" r2l = p["r2l"] == "true"
verticalLoadMaxCount = if (p["verticalMax"] != "null") p["verticalMax"].toInt() else 20
isVertical = p["vertical"] == "true" isVertical = p["vertical"] == "true"
notUseVP = p["noVP"] == "true" || isVertical notUseVP = p["noVP"] == "true" || isVertical
//url = intent.getStringExtra("url") //url = intent.getStringExtra("url")
handler = VMHandler(this, if(urlArray.isNotEmpty()) urlArray[position] else "") handler = VMHandler(this, if(urlArray.isNotEmpty()) urlArray[position] else "")
if (p["quality"] != "null") q = p["quality"].toInt() if (p["quality"] != "null") q = p["quality"].toInt()
if (p["verticalMax"] != "null") verticalLoadMaxCount = p["verticalMax"].toInt()
tt = TimeThread(handler, 22) tt = TimeThread(handler, 22)
tt.canDo = true tt.canDo = true
tt.start() tt.start()
volTurnPage = p["volturn"] == "true"
am = getSystemService(Service.AUDIO_SERVICE) as AudioManager
Log.d("MyVM", "Now ZipFile is $zipFile") Log.d("MyVM", "Now ZipFile is $zipFile")
try { try {
@@ -101,15 +119,33 @@ class ViewMangaActivity : TitleActivityTemplate() {
override fun onWindowFocusChanged(hasFocus: Boolean) { override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus) super.onWindowFocusChanged(hasFocus)
if(useFullScreen) { if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R)
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R) window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY else {
else { window.setDecorFitsSystemWindows(false)
window.setDecorFitsSystemWindows(false) window.insetsController?.hide(WindowInsets.Type.statusBars())
window.insetsController?.hide(WindowInsets.Type.statusBars()) //window.insetsController?.hide(WindowInsets.Type.navigationBars())
//window.insetsController?.hide(WindowInsets.Type.navigationBars()) }
}
@ExperimentalStdlibApi
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
var flag = false
if(volTurnPage) when(keyCode) {
KeyEvent.KEYCODE_VOLUME_UP -> {
pm?.toPage(false)
flag = true
}
KeyEvent.KEYCODE_VOLUME_DOWN -> {
pm?.toPage(true)
flag = true
} }
} }
return if(flag) true else super.onKeyDown(keyCode, event)
}
private fun alertCellar() {
toolsBox.buildInfo("注意", "要使用使用流量观看吗?", "确定", null, "取消", {handler.startLoad()}, null, {finish()})
} }
fun restorePN(){ fun restorePN(){
@@ -121,30 +157,122 @@ class ViewMangaActivity : TitleActivityTemplate() {
sendProgress() sendProgress()
} }
private fun preDownloadChapterPages() {
getImgUrlArray()?.let {
val mid = (if(pn in 1 until realCount) (if(cut) Math.abs(indexMap[pn]) else pn) else if(pn == -2 || pn >= realCount) it.size else 1) - 1
val left = if(isVertical && mid > verticalLoadMaxCount) (mid / verticalLoadMaxCount) * verticalLoadMaxCount else (mid-1)
val right = if(isVertical) (mid / verticalLoadMaxCount + 1) * verticalLoadMaxCount else mid
tasks = arrayOfNulls(it.size)
Thread{
for (i in right until it.size) {
if(destroy) break
tasks?.let { tasks ->
tasks[i] = DownloadTools.touch(it[i])
Thread.sleep(1000)
}
}
}.start()
Thread.sleep(500)
Thread{
for (i in left downTo 0) {
if(destroy) break
tasks?.let { tasks ->
tasks[i] = DownloadTools.touch(it[i])
Thread.sleep(1000)
}
}
}.start()
}
}
@ExperimentalStdlibApi
private fun doPrepareWebImg() {
getImgUrlArray()?.apply {
if(cut) {
handler.sendEmptyMessage(7) //showDl
isCut = BooleanArray(size)
val analyzedCnt = BooleanArray(size)
forEachIndexed{ i, it ->
if(it != null) {
Thread{
DownloadTools.getHttpContent(it, 1024)?.inputStream()?.let {
isCut[i] = canCut(it)
analyzedCnt[i] = true
}
}.start()
Thread.sleep(22)
}
}
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
}
count = size
runOnUiThread { prepareItems() }
preDownloadChapterPages()
}
}
@ExperimentalStdlibApi @ExperimentalStdlibApi
fun initManga(){ fun initManga(){
prepareItems(count) if (zipFile?.exists() != true) doPrepareWebImg()
else prepareItems()
if (!isVertical) restorePN() if (!isVertical) restorePN()
} }
@ExperimentalStdlibApi @ExperimentalStdlibApi
private fun prepareImgFromWeb() { private fun prepareImgFromWeb() {
handler.startLoad() if(toolsBox.netinfo == "移动数据") alertCellar()
else handler.startLoad()
} }
private fun canCut(inputStream: InputStream): Boolean{
val op = BitmapFactory.Options()
op.inJustDecodeBounds = true
BitmapFactory.decodeStream(inputStream, null, op)
Log.d("MyVM", "w: ${op.outWidth}, h: ${op.outHeight}")
return op.outWidth.toFloat() / op.outHeight.toFloat() > 1
}
@ExperimentalStdlibApi
fun countZipEntries(doWhenFinish : (count: Int) -> Unit) = Thread{
if (zipFile != null) try {
Log.d("Myvm", "zip: $zipFile")
val zip = ZipFile(zipFile)
count = zip.size()
if(cut) zip.entries().toList().sortedBy{it.name.substringBefore('.').toInt()}.forEachIndexed { i, it ->
val useCut = canCut(zip.getInputStream(it))
isCut += useCut
indexMap += i + 1
if (useCut) indexMap += -(i + 1)
Log.d("Myvm", "[$i] 分析: ${it.name}, cut: $useCut")
}
} catch (e: Exception) {
runOnUiThread { toolsBox.toastError("统计zip图片数错误!") }
}
runOnUiThread {
Log.d("Myvm", "开始加载控件")
doWhenFinish(count)
}
}.start()
private fun getPageNumber(): Int { private fun getPageNumber(): Int {
return if (r2l && !notUseVP) count - vp.currentItem return if (r2l && !notUseVP) realCount - vp.currentItem
else (if (notUseVP) currentItem else vp.currentItem) + 1 else (if (notUseVP) currentItem else vp.currentItem) + 1
} }
private fun setPageNumber(num: Int) { private fun setPageNumber(num: Int) {
if (r2l && !notUseVP) vp.currentItem = count - num if (r2l && !notUseVP) vp.currentItem = realCount - num
else if (notUseVP) { else if (notUseVP) {
if(isVertical){ if(isVertical){
currentItem = num - 1 currentItem = num - 1
val delta = currentItem % verticalLoadMaxCount val offset = currentItem % verticalLoadMaxCount
Log.d("MyVM", "Height: ${psivl.height}, scrollY: ${psivs.scrollY}") Log.d("MyVM", "Current: $currentItem, Height: ${psivl.height}, scrollY: ${psivs.scrollY}")
if (!isInScroll || isInSeek) psivs.scrollY = psivl.height / size * delta if (!isInScroll || isInSeek) psivs.scrollY = psivl.height * offset / size
updateSeekBar() updateSeekBar()
} }
else { else {
@@ -156,31 +284,86 @@ class ViewMangaActivity : TitleActivityTemplate() {
toolsBox.toastError("页数${currentItem}不合法") toolsBox.toastError("页数${currentItem}不合法")
} }
} }
} else vp.currentItem = num - 1 } 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++
}
}
}.start()
else if(delta <= -1) Thread{
while (delta++ < 0){
Thread.sleep(23)
runOnUiThread {
vp.currentItem--
}
}
}.start()
}
} }
fun clearImgOn(imgView: ScaleImageView){ fun clearImgOn(imgView: ScaleImageView){
imgView.visibility = View.GONE imgView.visibility = View.GONE
} }
private fun getTempFile(position: Int) = File(cacheDir, "$position") //private fun getTempFile(position: Int) = File(cacheDir, "$position")
private fun getImgUrl(position: Int) = handler.manga?.results?.chapter?.let { private fun getImgUrl(position: Int) = handler.manga?.results?.chapter?.let {
it.contents[it.words.indexOf(position)].url it.contents[it.words.indexOf(position)].url
} }
fun loadImgOn(imgView: ScaleImageView, position: Int){ private fun getImgUrlArray() = handler.manga?.results?.chapter?.let{
if (zipFile?.exists() == true) imgView.setImageBitmap(getImgBitmap(position)) val re = arrayOfNulls<String>(it.contents.size)
else if(isVertical) { for(i in it.contents.indices) {
val f = getTempFile(position) re[i] = getImgUrl(i)
if(DownloadTools.downloadUsingUrlRet(getImgUrl(position), f)) }
imgView.setImageBitmap(BitmapFactory.decodeFile(f.path)) re
else Toast.makeText(this, "下载第${position}页失败", Toast.LENGTH_SHORT).show() }
private fun cutBitmap(bitmap: Bitmap, isEnd: Boolean) = Bitmap.createBitmap(bitmap, if(!isEnd) 0 else (bitmap.width/2), 0, bitmap.width/2, bitmap.height)
private fun loadImg(imgView: ScaleImageView, bitmap: Bitmap, isLast: Int = 0, useCut: Boolean, isLeft: Boolean){
val bitmap2load = if(useCut) cutBitmap(bitmap, isLeft) else bitmap
runOnUiThread {
imgView.setImageBitmap(bitmap2load)
if(isVertical){
imgView.setHeight2FitImgWidth()
if (isLast == 1) handler.sendEmptyMessage(8)
}
}
}
private fun loadImgUrlInto(imgView: ScaleImageView, url: String, isLast: Int = 0, useCut: Boolean, isLeft: Boolean){
Log.d("MyVM", "Load from adt: $url")
AutoDownloadThread(url) {
it?.let { loadImg(imgView, BitmapFactory.decodeByteArray(it, 0, it.size), isLast, useCut, isLeft) }
}.start()
}
fun loadImgOn(imgView: ScaleImageView, position: Int, isLast: Int = 0){
Log.d("MyVM", "Load img: $position")
val index2load = if(cut) Math.abs(indexMap[position]) -1 else position
val useCut = cut && isCut[index2load]
val isLeft = cut && indexMap[position] > 0
if (zipFile?.exists() == true) getImgBitmap(index2load)?.let {
loadImg(imgView, it, isLast, useCut, isLeft)
}
else {
val re = tasks?.get(index2load)
if (re != null) Thread{
val data = re.get()
if(data != null) {
loadImg(imgView, BitmapFactory.decodeByteArray(data, 0, data.size), isLast, useCut, isLeft)
Log.d("MyVM", "Load from task")
}
else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, isLast, useCut, isLeft) }
}.start()
else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, isLast, useCut, isLeft) }
} }
else Glide.with(this)
.load(GlideUrl(getImgUrl(position), CMApi.myGlideHeaders))
.timeout(10000)
.into(imgView)
imgView.visibility = View.VISIBLE imgView.visibility = View.VISIBLE
} }
@@ -190,7 +373,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
} }
private fun initImgList(){ private fun initImgList(){
for (i in 0..39) { for (i in 0 until verticalLoadMaxCount) {
val newImg = ScaleImageView(this) val newImg = ScaleImageView(this)
scrollImages += newImg scrollImages += newImg
psivl.addView(newImg) psivl.addView(newImg)
@@ -202,24 +385,21 @@ class ViewMangaActivity : TitleActivityTemplate() {
handler.dl?.hide() handler.dl?.hide()
} }
private fun getImgBitmap(position: Int): Bitmap? { private fun getImgBitmap(position: Int): Bitmap? =
Log.d("MyVM", "Get bitmap @$position, count is $count") if (position >= count || position < 0) null
if (position >= count || position < 0) return null else try {
else {
val zip = ZipFile(zipFile) val zip = ZipFile(zipFile)
if (q == 100) return BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.jpg"))) if (q == 100) BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.webp")))
else { else {
val out = ByteArrayOutputStream() val out = ByteArrayOutputStream()
try { BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.webp")))?.compress(Bitmap.CompressFormat.JPEG, q, out)
BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.webp"))) BitmapFactory.decodeStream(ByteArrayInputStream(out.toByteArray()))
} catch (e: Exception) {
e.printStackTrace()
return null
}?.compress(Bitmap.CompressFormat.JPEG, q, out)
return BitmapFactory.decodeStream(ByteArrayInputStream(out.toByteArray()))
} }
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, "加载zip的${position}.webp错误", Toast.LENGTH_SHORT).show()
null
} }
}
private fun setIdPosition(position: Int) { private fun setIdPosition(position: Int) {
infoDrawerDelta = position.toFloat() infoDrawerDelta = position.toFloat()
@@ -229,19 +409,24 @@ class ViewMangaActivity : TitleActivityTemplate() {
@ExperimentalStdlibApi @ExperimentalStdlibApi
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun prepareItems(size: Int) { private fun prepareItems() {
ttitle.text = handler.manga?.results?.chapter?.name try {
prepareVP() prepareVP()
prepareInfoBar(size) //if (!isVertical) restorePN()
if (notUseVP && !isVertical) loadOneImg() prepareInfoBar()
prepareIdBtVH() if (notUseVP && !isVertical && !isPnValid) loadOneImg()
toolsBox.dp2px(67)?.let { setIdPosition(it) } prepareIdBtVH()
prepareIdBtFullScreen() toolsBox.dp2px(67)?.let { setIdPosition(it) }
prepareIdBtVP() prepareIdBtCut()
prepareIdBtLR() prepareIdBtVP()
handler.progressLog?.let { prepareIdBtLR()
//it["uuid"] = handler.manga?.results?.comic?.uuid /*progressLog?.let {
it["name"] = inftitle.ttitle.text it["chapterId"] = hm.chapterId.toString()
it["name"] = inftitle.ttitle.text
}*/
}catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError("准备控件错误")
} }
} }
@@ -253,6 +438,14 @@ class ViewMangaActivity : TitleActivityTemplate() {
} }
} }
private fun prepareIdBtCut() {
idtbcut.isChecked = cut
idtbcut.setOnClickListener {
p["useCut"] = if (idtbcut.isChecked) "true" else "false"
Toast.makeText(this, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
private fun prepareIdBtLR() { private fun prepareIdBtLR() {
idtblr.isChecked = r2l idtblr.isChecked = r2l
idtblr.setOnClickListener { idtblr.setOnClickListener {
@@ -285,11 +478,11 @@ class ViewMangaActivity : TitleActivityTemplate() {
super.onPageSelected(position) super.onPageSelected(position)
} }
}) })
if (r2l) vp.currentItem = count - 1 if (r2l && !isPnValid) vp.currentItem = realCount - 1
} }
} }
private fun updateSeekBar() { fun updateSeekBar() {
if (!isInSeek) hideObjs() if (!isInSeek) hideObjs()
updateSeekText() updateSeekText()
updateSeekProgress() updateSeekProgress()
@@ -297,17 +490,18 @@ class ViewMangaActivity : TitleActivityTemplate() {
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun prepareInfoBar(size: Int) { private fun prepareInfoBar() {
oneinfo.alpha = 0F oneinfo.alpha = 0F
infseek.visibility = View.GONE infseek.visibility = View.GONE
isearch.visibility = View.GONE isearch.visibility = View.GONE
inftitle.ttitle.text = handler.manga?.results?.chapter?.name inftitle.ttitle.text = handler.manga?.results?.chapter?.name
inftxtprogress.text = "$pageNum/$size" inftxtprogress.text = "$pageNum/$realCount"
infseek.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { infseek.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(p0: SeekBar?, p1: Int, isHuman: Boolean) { override fun onProgressChanged(p0: SeekBar?, p1: Int, isHuman: Boolean) {
Log.d("MyVM", "seek to ${p1 * realCount / 100}")
if (isHuman) { if (isHuman) {
if (p1 >= (pageNum + 1) * 100 / size) scrollForward() if (p1 >= (pageNum + 1) * 100 / realCount) scrollForward()
else if (p1 < (pageNum - 1) * 100 / size) scrollBack() else if (p1 < (pageNum - 1) * 100 / realCount) scrollBack()
} }
} }
override fun onStartTrackingTouch(p0: SeekBar?) { override fun onStartTrackingTouch(p0: SeekBar?) {
@@ -318,6 +512,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
isInSeek = false isInSeek = false
} }
}) })
isearch.setImageResource(R.drawable.ic_author)
isearch.setOnClickListener { isearch.setOnClickListener {
handler.sendEmptyMessage(3) handler.sendEmptyMessage(3)
} }
@@ -330,16 +525,16 @@ class ViewMangaActivity : TitleActivityTemplate() {
val vsps = vsp as SpringView val vsps = vsp as SpringView
vsps.footerView.lht.text = "更多" vsps.footerView.lht.text = "更多"
vsps.headerView.lht.text = "更多" vsps.headerView.lht.text = "更多"
val pm = PagesManager(WeakReference(this)) pm = PagesManager(WeakReference(this))
vsps.setListener(object :SpringView.OnFreshListener{ vsps.setListener(object :SpringView.OnFreshListener{
override fun onLoadmore() { override fun onLoadmore() {
//scrollForward() //scrollForward()
pm.toPage(true) pm?.toPage(true)
vsps.onFinishFreshAndLoad() vsps.onFinishFreshAndLoad()
} }
override fun onRefresh() { override fun onRefresh() {
//scrollBack() //scrollBack()
pm.toPage(false) pm?.toPage(false)
vsps.onFinishFreshAndLoad() vsps.onFinishFreshAndLoad()
} }
}) })
@@ -350,8 +545,11 @@ class ViewMangaActivity : TitleActivityTemplate() {
psivs.setOnScrollChangeListener { _, _, scrollY, _, _ -> psivs.setOnScrollChangeListener { _, _, scrollY, _, _ ->
isInScroll = true isInScroll = true
if(!isInSeek){ if(!isInSeek){
val newCurrent = (scrollY.toFloat() * size.toFloat() / psivl.height.toFloat() + 0.5).toInt() val delta = (scrollY.toFloat() * size.toFloat() / psivl.height.toFloat() + 0.5).toInt() - currentItem % verticalLoadMaxCount
pageNum += newCurrent - currentItem % verticalLoadMaxCount if(delta != 0 && !(delta > 0 && pageNum == size)) {
pageNum += delta
Log.d("MyVM", "Scroll to offset $delta")
}
} }
} }
} }
@@ -361,14 +559,6 @@ class ViewMangaActivity : TitleActivityTemplate() {
} }
} }
private fun prepareIdBtFullScreen() {
idtbfullscreen.isChecked = !useFullScreen
idtbfullscreen.setOnClickListener {
p["useFullScreen"] = if (idtbfullscreen.isChecked) "true" else "false"
Toast.makeText(this, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
fun scrollBack() { fun scrollBack() {
isInScroll = false isInScroll = false
if(isVertical && (pageNum-1) % verticalLoadMaxCount == 0){ if(isVertical && (pageNum-1) % verticalLoadMaxCount == 0){
@@ -384,20 +574,19 @@ class ViewMangaActivity : TitleActivityTemplate() {
if(isVertical && (pageNum-1) % verticalLoadMaxCount == 0) handler.sendEmptyMessage(10) if(isVertical && (pageNum-1) % verticalLoadMaxCount == 0) handler.sendEmptyMessage(10)
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun updateSeekText() { private fun updateSeekText() {
inftxtprogress.text = "$pageNum/$count" inftxtprogress.text = "$pageNum/$realCount"
} }
private fun updateSeekProgress() { private fun updateSeekProgress() {
infseek.progress = pageNum * 100 / count infseek.progress = pageNum * 100 / realCount
} }
override fun onDestroy() { override fun onDestroy() {
dlhandler?.sendEmptyMessage(0) dlhandler?.sendEmptyMessage(0)
tt.canDo = false tt.canDo = false
destroy = true
dlhandler = null dlhandler = null
handler.destroy() handler.destroy()
super.onDestroy() super.onDestroy()
@@ -415,22 +604,30 @@ class ViewMangaActivity : TitleActivityTemplate() {
@SuppressLint("ClickableViewAccessibility", "SetTextI18n") @SuppressLint("ClickableViewAccessibility", "SetTextI18n")
override fun onBindViewHolder(holder: ViewData, position: Int) { override fun onBindViewHolder(holder: ViewData, position: Int) {
val pos = if (r2l) count - position - 1 else position val pos = if (r2l) realCount - position - 1 else position
if (zipFile?.exists() == true) getImgBitmap(pos)?.let { val index2load = if(cut) Math.abs(indexMap[pos]) -1 else pos
Glide.with(this@ViewMangaActivity).load(it) val useCut = cut && isCut[index2load]
//.thumbnail(Glide.with(this@ViewMangaActivity).load(R.drawable.load)) val isLeft = cut && indexMap[pos] > 0
.into(holder.itemView.onei) if (zipFile?.exists() == true) getImgBitmap(index2load)?.let {
//holder.itemView.onei.setImageBitmap(it) //Glide.with(this@ViewMangaActivity).load(if(useCut) cutBitmap(it, isLeft) else it).into(holder.itemView.onei)
holder.itemView.onei.setImageBitmap(if(useCut) cutBitmap(it, isLeft) else it)
}
else getImgUrl(index2load)?.let{
if(useCut){
val thisOneI = holder.itemView.onei
Glide.with(this@ViewMangaActivity)
.asBitmap()
.load(GlideUrl(it, CMApi.myGlideHeaders)
).into(object : SimpleTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
thisOneI.setImageBitmap(cutBitmap(resource, isLeft))
} })
} else Glide.with(this@ViewMangaActivity).load(GlideUrl(it, CMApi.myGlideHeaders)).into(holder.itemView.onei)
} }
else Glide.with(this@ViewMangaActivity).load(
GlideUrl(getImgUrl(pos), CMApi.myGlideHeaders))
.timeout(10000)
//.thumbnail(Glide.with(this@ViewMangaActivity).load(R.drawable.load))
.into(holder.itemView.onei)
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return count return realCount
} }
} }
} }

View File

@@ -39,7 +39,7 @@
app:layout_constraintVertical_bias="0.0" /> app:layout_constraintVertical_bias="0.0" />
<ToggleButton <ToggleButton
android:id="@+id/idtbfullscreen" android:id="@+id/idtbcut"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
@@ -47,8 +47,8 @@
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:background="@drawable/toggle_button" android:background="@drawable/toggle_button"
android:textOff="全屏开" android:textOff="切分关闭"
android:textOn="全屏关" android:textOn="双页切分"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/idtbvh" app:layout_constraintEnd_toStartOf="@+id/idtbvh"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@@ -64,8 +64,8 @@
android:textOff="横向" android:textOff="横向"
android:textOn="竖向" android:textOn="竖向"
app:layout_constraintEnd_toStartOf="@id/idtbvp" app:layout_constraintEnd_toStartOf="@id/idtbvp"
app:layout_constraintStart_toEndOf="@+id/idtbfullscreen" app:layout_constraintStart_toEndOf="@+id/idtbcut"
app:layout_constraintTop_toTopOf="@+id/idtbfullscreen" /> app:layout_constraintTop_toTopOf="@+id/idtbcut" />
<ToggleButton <ToggleButton
android:id="@+id/idtbvp" android:id="@+id/idtbvp"
@@ -77,7 +77,7 @@
android:textOn="动画关" android:textOn="动画关"
app:layout_constraintEnd_toStartOf="@+id/idtblr" app:layout_constraintEnd_toStartOf="@+id/idtblr"
app:layout_constraintStart_toEndOf="@id/idtbvh" app:layout_constraintStart_toEndOf="@id/idtbvh"
app:layout_constraintTop_toTopOf="@+id/idtbfullscreen" /> app:layout_constraintTop_toTopOf="@+id/idtbcut" />
<ToggleButton <ToggleButton
android:id="@+id/idtblr" android:id="@+id/idtblr"
@@ -89,7 +89,7 @@
android:textOn="←后 前→" android:textOn="←后 前→"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/idtbvp" app:layout_constraintStart_toEndOf="@+id/idtbvp"
app:layout_constraintTop_toTopOf="@+id/idtbfullscreen" /> app:layout_constraintTop_toTopOf="@+id/idtbcut" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -134,6 +134,20 @@
app:exitAnim="@anim/slide_out_left" app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_out_left_exit" app:popEnterAnim="@anim/slide_out_left_exit"
app:popExitAnim="@anim/slide_in_right_exit"/> app:popExitAnim="@anim/slide_in_right_exit"/>
<action
android:id="@+id/action_nav_book_to_nav_author"
app:destination="@id/nav_author"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_out_left_exit"
app:popExitAnim="@anim/slide_in_right_exit"/>
<action
android:id="@+id/action_nav_book_to_nav_caption"
app:destination="@id/nav_caption"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_out_left_exit"
app:popExitAnim="@anim/slide_in_right_exit"/>
</fragment> </fragment>
<fragment <fragment
@@ -189,7 +203,7 @@
android:id="@+id/nav_finish" android:id="@+id/nav_finish"
android:name="top.fumiama.copymanga.ui.cardflow.finish.FinishFragment" android:name="top.fumiama.copymanga.ui.cardflow.finish.FinishFragment"
android:label="@string/complete" android:label="@string/complete"
tools:layout="@layout/fragment_newest" > tools:layout="@layout/fragment_statuscardflow" >
<action <action
android:id="@+id/action_nav_finish_to_nav_book" android:id="@+id/action_nav_finish_to_nav_book"
app:destination="@id/nav_book" app:destination="@id/nav_book"
@@ -199,6 +213,34 @@
app:popExitAnim="@anim/slide_in_right_exit"/> app:popExitAnim="@anim/slide_in_right_exit"/>
</fragment> </fragment>
<fragment
android:id="@+id/nav_author"
android:name="top.fumiama.copymanga.ui.cardflow.author.AuthorFragment"
android:label="@string/author"
tools:layout="@layout/fragment_statuscardflow" >
<action
android:id="@+id/action_nav_author_to_nav_book"
app:destination="@id/nav_book"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_out_left_exit"
app:popExitAnim="@anim/slide_in_right_exit"/>
</fragment>
<fragment
android:id="@+id/nav_caption"
android:name="top.fumiama.copymanga.ui.cardflow.caption.CaptionFragment"
android:label="@string/caption"
tools:layout="@layout/fragment_statuscardflow" >
<action
android:id="@+id/action_nav_caption_to_nav_book"
app:destination="@id/nav_book"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_out_left_exit"
app:popExitAnim="@anim/slide_in_right_exit"/>
</fragment>
<fragment <fragment
android:id="@+id/nav_group" android:id="@+id/nav_group"
android:name="top.fumiama.copymanga.ui.comicdl.ComicDlFragment" android:name="top.fumiama.copymanga.ui.comicdl.ComicDlFragment"

View File

@@ -44,6 +44,8 @@
<string name="recommendApiUrl">https://api.copymanga.com/api/v3/recs?pos=3200102&amp;limit=21&amp;offset=%1$d&amp;platform=3</string> <string name="recommendApiUrl">https://api.copymanga.com/api/v3/recs?pos=3200102&amp;limit=21&amp;offset=%1$d&amp;platform=3</string>
<string name="newestApiUrl">https://api.copymanga.com/api/v3/update/newest?limit=21&amp;offset=%1$d&amp;platform=3</string> <string name="newestApiUrl">https://api.copymanga.com/api/v3/update/newest?limit=21&amp;offset=%1$d&amp;platform=3</string>
<string name="finishApiUrl">https://api.copymanga.com/api/v3/comics?limit=21&amp;offset=%1$d&amp;ordering=%2$s&amp;top=finish&amp;platform=3</string> <string name="finishApiUrl">https://api.copymanga.com/api/v3/comics?limit=21&amp;offset=%1$d&amp;ordering=%2$s&amp;top=finish&amp;platform=3</string>
<string name="authorApiUrl">https://api.copymanga.com/api/v3/comics?limit=21&amp;offset=%1$d&amp;ordering=%2$s&amp;author=%3$s&amp;platform=3</string>
<string name="captionApiUrl">https://api.copymanga.com/api/v3/comics?limit=21&amp;offset=%1$d&amp;ordering=%2$s&amp;theme=%3$s&amp;platform=3</string>
<string name="complete">已完结</string> <string name="complete">已完结</string>
@@ -75,4 +77,7 @@
<string name="rank_week">近七天</string> <string name="rank_week">近七天</string>
<string name="rank_month">近三十天</string> <string name="rank_month">近三十天</string>
<string name="rank_all">总榜单</string> <string name="rank_all">总榜单</string>
<string name="author">作者</string>
<string name="caption">标签</string>
</resources> </resources>

View File

@@ -8,7 +8,7 @@ buildscript {
maven { url 'https://maven.google.com' } maven { url 'https://maven.google.com' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.2.0' classpath 'com.android.tools.build:gradle:4.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.21' classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.21'