1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-05 07:20:23 +08:00
修复
1. 详情页按钮在某些机型显示不全
2. 图床代理不正确打开导致的闪退
3. 下载中取消选择则闪退
4. 断点继续下载的zip错误
5. 浏览历史标记显示错误
6. 浏览已下载章节时不能记录到云端
7. 进其他书返回后漫画下载变为最上层
优化
1. 进一步协程化CardList, HTTP/文件读写等
2. 各模块加载速度
This commit is contained in:
源文雨
2024-03-10 03:14:23 +09:00
parent 7f5f183d32
commit 335d860b0d
35 changed files with 988 additions and 811 deletions

View File

@@ -3,7 +3,9 @@
<words>
<w>imgs</w>
<w>lowpan</w>
<w>mangafuna</w>
<w>nisi</w>
<w>pausable</w>
<w>reilia</w>
</words>
</dictionary>

View File

@@ -8,19 +8,32 @@ android {
applicationId 'top.fumiama.copymanga'
minSdkVersion 23
targetSdkVersion 34
versionCode 46
versionName '2.1.3'
versionCode 47
versionName '2.2.0'
resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
cruncherEnabled = false
}
}
signingConfigs {
release {
enableV1Signing true
enableV2Signing true
enableV3Signing true
enableV4Signing true
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
/*winrelease {
minifyEnabled true

View File

@@ -156,7 +156,7 @@ class MainActivity : AppCompatActivity() {
true
}
R.id.action_download -> {
bookHandler?.sendEmptyMessage(6)
bookHandler.get()?.sendEmptyMessage(6)
true
}
R.id.action_sort -> {
@@ -213,6 +213,7 @@ class MainActivity : AppCompatActivity() {
Glide.with(this@MainActivity).load(avatar)
.apply(RequestOptions.bitmapTransform(CircleCrop()))
.into(navhicon)
else navhicon.setImageResource(R.mipmap.ic_launcher)
}
}
@@ -371,7 +372,7 @@ class MainActivity : AppCompatActivity() {
startActivity(Intent(this, LoginActivity::class.java))
}
companion object{
companion object {
var mainWeakReference: WeakReference<MainActivity>? = null
var isDrawerClosed = true
var ime: InputMethodManager? = null

View File

@@ -0,0 +1,10 @@
package top.fumiama.copymanga.manga
class Book(val pathWord: String, val readLocally: Boolean = false) {
/**
* 更新云端最新信息
*/
suspend fun update() {
}
}

View File

@@ -4,7 +4,7 @@ import android.util.Log
import com.google.gson.Gson
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.Chapter2Return
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.tools.http.DownloadPool
import java.io.File
@@ -19,18 +19,18 @@ class MangaDlTools {
get() = pool?.wait
set(value) { if (value != null) { pool?.wait = value } }
fun downloadChapterInVol(url: CharSequence, chapterName: CharSequence, group: CharSequence, index: Int) {
suspend fun downloadChapterInVol(url: CharSequence, chapterName: CharSequence, group: CharSequence, index: Int) {
Log.d("MyMDT", "下载:$url, index$index")
AutoDownloadThread(url.toString(), 1000) { data ->
PausableDownloader(url.toString(), 1000) { data ->
try {
Gson().fromJson(data?.decodeToString(), Chapter2Return::class.java)?.let {
Gson().fromJson(data.decodeToString(), Chapter2Return::class.java)?.let {
getChapterInfo(it, index, chapterName, group)
}
} catch (e: Exception) {
e.printStackTrace()
onDownloadedListener?.handleMessage(index, false, e.localizedMessage?:"Gson parsing error")
}
}.start()
}.run()
}
@Synchronized private fun prepareDownloadListener() {

View File

@@ -51,8 +51,13 @@ class Shelf(private val token: String, getString: (Int) -> String) {
}
suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
return@withContext DownloadTools.getHttpContent(queryApiUrl.format(hostUrl, pathWord), referer, ua)?.let {
Gson().fromJson(it.decodeToString(), BookQueryStructure::class.java)
try {
Gson().fromJson(DownloadTools.getHttpContent(
queryApiUrl.format(hostUrl, pathWord), referer, ua
).decodeToString(), BookQueryStructure::class.java)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}

View File

@@ -7,7 +7,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import top.fumiama.copymanga.tools.ui.UITools
open class ActivityTemplate:AppCompatActivity() {
open class ActivityTemplate: AppCompatActivity() {
lateinit var toolsBox: UITools
val pb = BoolPref()
private val allFullScreen

View File

@@ -18,7 +18,7 @@ open class NoBackRefreshFragment(private val layoutToLoad: Int): Fragment() {
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if(_rootView == null){
if(_rootView == null) {
isFirstInflate = true
_rootView = inflater.inflate(layoutToLoad, container, false)
Log.d("MyNBRF", "is first inflate")

View File

@@ -4,21 +4,30 @@ import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.google.gson.Gson
import top.fumiama.dmzj.copymanga.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.ReturnBase
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.copymanga.tools.thread.TimeThread
import top.fumiama.dmzj.copymanga.R
import java.io.File
import java.lang.Thread.sleep
import java.security.MessageDigest
open class AutoDownloadHandler(private val url: String, private val jsonClass: Class<*>, looper: Looper, private val callCheckMsg: Int = -1, private val loadFromCache: Boolean = false, private val customCacheFile: File? = null): Handler(looper) {
var exit = false
open class AutoDownloadHandler(
private val url: String, private val jsonClass: Class<*>,
private val context: LifecycleOwner?,
private val callCheckMsg: Int = -1,
private val loadFromCache: Boolean = false,
private val customCacheFile: File? = null): Handler(Looper.myLooper()!!) {
private var timeThread: TimeThread? = null
private var checkTimes = 0
private var cnt = 0
var exit = false
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){
@@ -29,19 +38,21 @@ open class AutoDownloadHandler(private val url: String, private val jsonClass: C
open fun setGsonItem(gsonObj: Any): Boolean = true
open fun getGsonItem(): ReturnBase? = null
open fun onError() {}
open fun doWhenFinishDownload() {}
open suspend fun doWhenFinishDownload() {}
fun startLoad() {
sendEmptyMessage(0)
}
fun destroy() {
exit = true
}
private fun download() {
Thread{ dlThread() }.start()
private suspend fun download() = withContext(Dispatchers.IO) {
checkTimes = 0
timeThread = TimeThread(this, callCheckMsg)
timeThread?.canDo = true
timeThread?.start()
TimeThread(this@AutoDownloadHandler, callCheckMsg, 100).let {
timeThread = it
it.canDo = true
it.start()
}
downloadCoroutine()
}
private fun toHexStr(byteArray: ByteArray) =
with(StringBuilder()) {
@@ -53,7 +64,7 @@ open class AutoDownloadHandler(private val url: String, private val jsonClass: C
}
toString()
}
private fun dlThread() {
private suspend fun downloadCoroutine() = withContext(Dispatchers.IO) {
val cacheName = toHexStr(MessageDigest.getInstance("MD5").digest(url.encodeToByteArray()))
val cacheFile = customCacheFile?:(mainWeakReference?.get()?.externalCacheDir?.let { File(it, cacheName) })
if(loadFromCache) {
@@ -67,31 +78,29 @@ open class AutoDownloadHandler(private val url: String, private val jsonClass: C
e.printStackTrace()
}
fi.close()
if (pass) return
if (pass) return@withContext
}
}
}
DownloadTools.getHttpContent(url, null, mainWeakReference?.get()?.getString(R.string.pc_ua)!!).let {
if(exit) return
if(it == null) {
if (cnt++>3) return
sleep(2000)
dlThread()
return
}
val fi = it.inputStream()
var pass = true
var cnt = 0
while (cnt++ <= 3) {
try {
pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass))
val data = DownloadTools.getHttpContent(url, null, mainWeakReference?.get()?.getString(R.string.pc_ua)!!)
if(exit) return@withContext
val fi = data.inputStream()
val pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass))
if (pass && loadFromCache) {
cacheFile?.writeBytes(it)
cacheFile?.writeBytes(data)
}
fi.close()
if(!pass) {
sleep(2000)
continue
}
break
} catch (e: Exception) {
e.printStackTrace()
}
fi.close()
if(!pass) {
dlThread()
sleep(2000)
}
}
}
@@ -102,9 +111,9 @@ open class AutoDownloadHandler(private val url: String, private val jsonClass: C
if(g.code == 200) sendEmptyMessage(0)
else onError()
Log.d("MyADH", "[${g.code}]${g.message}")
} else if(checkTimes++ > 3) timeThread?.canDo = false
} else if(checkTimes++ > 1000) timeThread?.canDo = false
}
private fun setLayouts() {
private fun setLayouts() = context?.lifecycleScope?.launch {
if(getGsonItem() == null) download()
else doWhenFinishDownload()
}

View File

@@ -1,25 +0,0 @@
package top.fumiama.copymanga.template.http
import android.util.Log
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.dmzj.copymanga.R
import kotlin.random.Random
class AutoDownloadThread(private val url: String, private val waitMilliseconds: Long = 0, private val whenFinish: (result: ByteArray?)->Unit): Thread() {
var exit = false
override fun run() {
super.run()
var re: ByteArray? = null
var c = 0
while (!exit && re == null && c++ < 3) {
if (waitMilliseconds > 0) sleep(200+Random.nextLong(waitMilliseconds))
re = DownloadTools.getHttpContent(url,
mainWeakReference?.get()?.getString(R.string.referer)!!,
mainWeakReference?.get()?.getString(R.string.pc_ua)!!
)
}
if(!exit) whenFinish(re)
Log.d("MyADT", "found exit = $exit")
}
}

View File

@@ -0,0 +1,30 @@
package top.fumiama.copymanga.template.http
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
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 whenFinish: suspend (result: ByteArray)->Unit) {
var exit = false
suspend fun run() = withContext(Dispatchers.IO) {
var c = 0
while (!exit && c++ < 3) {
try {
whenFinish(DownloadTools.getHttpContent(url,
mainWeakReference?.get()?.getString(R.string.referer)!!,
mainWeakReference?.get()?.getString(R.string.pc_ua)!!
))
break
} catch (e: Exception) {
e.printStackTrace()
if (waitMilliseconds > 0) sleep(200+Random.nextLong(waitMilliseconds))
}
}
Log.d("MyPD", "found exit = $exit")
}
}

View File

@@ -10,6 +10,8 @@ import com.bumptech.glide.load.model.GlideUrl
import kotlinx.android.synthetic.main.card_book.view.*
import kotlinx.android.synthetic.main.line_horizonal_empty.view.*
import kotlinx.android.synthetic.main.line_lazybooklines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
import top.fumiama.dmzj.copymanga.R
@@ -31,7 +33,7 @@ class CardList(
var initClickListeners: InitClickListeners? = null
var exitCardList = false
fun reset(){
fun reset() {
rows = arrayOfNulls(20)
index = 0
count = 0
@@ -39,18 +41,18 @@ class CardList(
exitCardList = false
}
private fun manageRow() {
private suspend fun manageRow() {
if(!exitCardList && count++ % cardPerRow == 0) inflateRow()
Log.d("MyCL", "index: $index, cardPR: $cardPerRow")
}
private fun inflateRow(){
private suspend fun inflateRow() = withContext(Dispatchers.IO) {
that?.apply {
layoutInflater.inflate(R.layout.line_horizonal_empty, mydll, false)?.let {
if(exitCardList) return
if(exitCardList) return@withContext
it.layoutParams.height = cardHeight + 16
activity?.runOnUiThread {
if(exitCardList) return@runOnUiThread
withContext(Dispatchers.Main) withMainContext@ {
if(exitCardList) return@withMainContext
mydll?.addView(it)
}
recycleOneRow(it)
@@ -58,14 +60,14 @@ class CardList(
}
}
}
private fun recycleOneRow(v:View?){
private suspend fun recycleOneRow(v:View?) = withContext(Dispatchers.IO) {
val relativeIndex = index % 20
if(rows[relativeIndex] == null) rows[relativeIndex] = v
else {
val victim = rows[relativeIndex]
that?.apply {
activity?.runOnUiThread {
if(exitCardList) return@runOnUiThread
withContext(Dispatchers.Main) withMainContext@ {
if(exitCardList) return@withMainContext
mydll?.removeView(victim)
mys?.scrollY = mys?.scrollY?.minus(cardHeight + 16)?:0
}
@@ -75,8 +77,9 @@ class CardList(
}
@ExperimentalStdlibApi
fun addCard(name: String, append: String? = null, head: String? = null, path: String? = null, chapterUUID: String? = null, pn: Int? = null, isFinish: Boolean = false, isNew: Boolean = false) {
if (exitCardList) return
suspend fun addCard(name: String, append: String? = null, head: String? = null, path: String? = null, chapterUUID: String? = null, pn: Int? = null, isFinish: Boolean = false, isNew: Boolean = false) =
withContext(Dispatchers.IO) {
if (exitCardList) return@withContext
manageRow()
that?.apply {
layoutInflater.inflate(R.layout.card_book, mydll?.ltbtn, false)?.let {
@@ -90,26 +93,23 @@ class CardList(
card.pageNumber = pn
card.isFinish = isFinish
card.isNew = isNew
activity?.runOnUiThread {
if (exitCardList) return@runOnUiThread
addCard(it)
}
addCard(it)
}
}
}
@SuppressLint("SetTextI18n")
@ExperimentalStdlibApi
fun addCard(cardFrame: View) {
private suspend fun addCard(cardFrame: View) = withContext(Dispatchers.IO) withIO@ {
val card = cardFrame.cic
if (card.index < 0) return
if (card.index < 0) return@withIO
val name = card.name + (card.append?:"")
val head = card.headImageUrl
val file = File(that?.context?.getExternalFilesDir(""), card.name)
if(exitCardList) return
if(exitCardList) return@withIO
cardFrame.let {
it.tic.text = name
if(!file.exists()){
withContext(Dispatchers.Main) { it.tic.text = name }
if(!file.exists()) {
if(head != null) {
that?.context?.let { context ->
val waitMillis = cardLoadingWaits.getAndIncrement().toLong()*200
@@ -119,35 +119,45 @@ class CardList(
if (exitCardList) return@GlideHideLottieViewListener
cardLoadingWaits.decrementAndGet()
})
if (waitMillis > 0) it.imic.postDelayed({
if (exitCardList) return@postDelayed
g.into(it.imic)
}, waitMillis) else g.into(it.imic)
withContext(Dispatchers.Main) {
if (waitMillis > 0) it.imic.postDelayed({
if (exitCardList) return@postDelayed
g.into(it.imic)
}, waitMillis) else g.into(it.imic)
}
}
} else {
} else withContext(Dispatchers.Main) {
it.laic.pauseAnimation()
it.laic.visibility = View.GONE
it.imic.setImageResource(R.drawable.img_defmask)
}
} else {
val img = File(file, "head.jpg")
it.laic.pauseAnimation()
it.laic.visibility = View.GONE
withContext(Dispatchers.Main) {
it.laic.pauseAnimation()
it.laic.visibility = View.GONE
}
if(img.exists()) {
it.imic.setImageURI(Uri.fromFile(img))
withContext(Dispatchers.Main) {
it.imic.setImageURI(Uri.fromFile(img))
}
} else {
it.imic.setImageResource(R.drawable.img_defmask)
withContext(Dispatchers.Main) {
it.imic.setImageResource(R.drawable.img_defmask)
}
}
}
if(card.isFinish) it.sgnic.visibility = View.VISIBLE
if(card.isNew) it.sgnnew.visibility = View.VISIBLE
initClickListeners?.prepareListeners(card, card.name, card.path, card.chapterUUID, card.pageNumber)
rows[card.index % 20]?.ltbtn?.addView(it)
it.layoutParams?.height = cardHeight
it.layoutParams?.width = cardWidth
withContext(Dispatchers.Main) {
if(card.isFinish) it.sgnic.visibility = View.VISIBLE
if(card.isNew) it.sgnnew.visibility = View.VISIBLE
initClickListeners?.prepareListeners(card, card.name, card.path, card.chapterUUID, card.pageNumber)
rows[card.index % 20]?.ltbtn?.addView(it)
it.layoutParams?.height = cardHeight
it.layoutParams?.width = cardWidth
}
}
}
interface InitClickListeners{
interface InitClickListeners {
fun prepareListeners(v: View, name: String, path: String?, chapterUUID: String?, pn: Int?)
}
}
}

View File

@@ -4,15 +4,17 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.gson.Gson
import kotlinx.android.synthetic.main.line_lazybooklines.*
import kotlinx.coroutines.launch
import top.fumiama.copymanga.json.BookListStructure
import top.fumiama.copymanga.json.HistoryBookListStructure
import top.fumiama.copymanga.json.ShelfStructure
import top.fumiama.copymanga.json.TypeBookListStructure
import top.fumiama.copymanga.template.general.MangaPagesFragmentTemplate
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.tools.ui.Navigate
import java.lang.ref.WeakReference
@@ -20,18 +22,12 @@ import java.lang.ref.WeakReference
open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isTypeBook: Boolean = false, private val isHistoryBook: Boolean = false, private val isShelfBook: Boolean = false): MangaPagesFragmentTemplate(inflateRes) {
var offset = 0
private val subUrl get() = getApiUrl()
var ad: AutoDownloadThread? = null
var ad: PausableDownloader? = null
override fun addPage(){
override fun addPage() {
super.addPage()
ad = AutoDownloadThread(subUrl) { data ->
if (data == null) {
activity?.runOnUiThread {
findNavController().popBackStack()
}
return@AutoDownloadThread
}
if(isRefresh){
ad = PausableDownloader(subUrl) { data ->
if(isRefresh) {
page = 0
isRefresh = false
}
@@ -42,7 +38,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
if(results.offset < results.total) {
if(code == 200) {
results.list.forEach { book ->
if(ad?.exit == true) return@AutoDownloadThread
if(ad?.exit == true) return@PausableDownloader
cardList?.addCard(
book?.comic?.name?:"null", null, book?.comic?.cover,
book?.comic?.path_word, null, null,
@@ -61,9 +57,9 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
if(results.offset < results.total) {
if(code == 200) {
results?.list?.forEach{ book ->
if(ad?.exit == true) return@AutoDownloadThread
if(ad?.exit == true) return@PausableDownloader
cardList?.addCard(
book?.comic?.name?:"null", "\n最新${book?.last_chapter_name}", book?.comic?.cover,
book?.comic?.name?:"null", "\n云读至${book?.last_chapter_name}", book?.comic?.cover,
book?.comic?.path_word, null, null,
book?.comic?.status==1
)
@@ -80,7 +76,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
if(results.offset < results.total) {
if(code == 200) {
results?.list?.forEach{ book ->
if(ad?.exit == true) return@AutoDownloadThread
if(ad?.exit == true) return@PausableDownloader
cardList?.addCard(
book?.comic?.name?:"null", "\n${book?.last_browse?.last_browse_name?.let { "读到$it" }?:"未读"}", book?.comic?.cover,
book?.comic?.path_word, null, null,
@@ -100,7 +96,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
if(results.offset < results.total) {
if(code == 200) {
results?.list?.forEach{ book ->
if(ad?.exit == true) return@AutoDownloadThread
if(ad?.exit == true) return@PausableDownloader
cardList?.addCard(book?.name?:"null", null, book?.cover, book?.path_word, null, null, false)
}
offset += results.list.size
@@ -111,7 +107,16 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
}
onLoadFinish()
}
ad?.start()
lifecycleScope.launch {
try {
ad?.run()
} catch (e: Exception) {
e.printStackTrace()
try {
findNavController().popBackStack()
} catch (_: Exception) {}
}
}
}
override fun initCardList(weakReference: WeakReference<Fragment>) {
super.initCardList(weakReference)

View File

@@ -1,31 +1,44 @@
package top.fumiama.copymanga.tools.api
import android.util.Base64
import androidx.preference.PreferenceManager
import com.bumptech.glide.load.model.LazyHeaders
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.copymanga.tools.http.Proxy
import top.fumiama.copymanga.tools.http.Resolution
import top.fumiama.dmzj.copymanga.R
import java.io.File
import java.net.URLEncoder
object CMApi {
var proxy = if(Proxy.useImageProxy) Proxy(R.string.imgProxyApiUrl, R.string.imgProxyApiPrefix, R.string.imgProxyKeyID) else null
var proxy = if (Proxy.useImageProxy) Proxy(
R.string.imgProxyApiUrl,
R.string.imgProxyApiRegex,
R.string.imgProxyKeyID
) else null
var resolution = Resolution(Regex("c\\d+x\\."))
var myGlideHeaders: LazyHeaders? = null
get() {
MainActivity.mainWeakReference?.get()?.let {
PreferenceManager.getDefaultSharedPreferences(it).apply {
if(field === null)
if (field === null)
field = LazyHeaders.Builder()
.addHeader("referer", MainActivity.mainWeakReference?.get()?.getString(R.string.referer)!!)
.addHeader("User-Agent", MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)!!)
.addHeader(
"referer",
MainActivity.mainWeakReference?.get()?.getString(R.string.referer)!!
)
.addHeader(
"User-Agent",
MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)!!
)
.addHeader("source", "copyApp")
.addHeader("webp", "1")
.addHeader("version", MainActivity.mainWeakReference?.get()?.getString(R.string.app_ver)!!)
.addHeader("region", if(!getBoolean("settings_cat_net", false)) "1" else "0")
.addHeader(
"version",
MainActivity.mainWeakReference?.get()?.getString(R.string.app_ver)!!
)
.addHeader(
"region",
if (!getBoolean("settings_cat_net", false)) "1" else "0"
)
.addHeader("platform", "3")
.build()
}
@@ -34,11 +47,11 @@ object CMApi {
}
var myHostApiUrl: String = ""
get() {
if(field != "") return field
if (field != "") return field
MainActivity.mainWeakReference?.get()?.let {
PreferenceManager.getDefaultSharedPreferences(it).apply {
getString("settings_cat_net_et_api_url", "")?.let { host ->
if(host != "") {
if (host != "") {
field = host
return host
}
@@ -49,25 +62,14 @@ object CMApi {
return field
}
fun getZipFile(exDir: File?, manga: String, caption: CharSequence, name: CharSequence) = File(exDir, "$manga/$caption/$name.zip")
fun getZipFile(exDir: File?, manga: String, caption: CharSequence, name: CharSequence) =
File(exDir, "$manga/$caption/$name.zip")
fun getChapterInfoApiUrl(arg1: String?, arg2: String?) =
MainActivity.mainWeakReference?.get()?.getString(R.string.chapterInfoApiUrl)?.format(myHostApiUrl, arg1, arg2)
MainActivity.mainWeakReference?.get()?.getString(R.string.chapterInfoApiUrl)
?.format(myHostApiUrl, arg1, arg2)
fun getGroupInfoApiUrl(arg1: String?, arg2: String?, arg3: Int? = 0) =
MainActivity.mainWeakReference?.get()?.getString(R.string.groupInfoApiUrl)?.format(myHostApiUrl, arg1, arg2, arg3)
fun getLoginConnection(username: String, pwd: String, salt: Int) =
MainActivity.mainWeakReference?.get()?.getString(R.string.loginApiUrl)?.format(myHostApiUrl)?.let {
DownloadTools.getConnection(it, "POST")?.apply {
MainActivity.mainWeakReference?.get()?.let {
PreferenceManager.getDefaultSharedPreferences(it).apply {
doOutput = true
setRequestProperty("content-type", "application/x-www-form-urlencoded;charset=utf-8")
setRequestProperty("platform", "3")
setRequestProperty("accept", "application/json")
val r = if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "1" else "0"
val pwdb64 = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
outputStream.write("username=${URLEncoder.encode(username)}&password=$pwdb64&salt=$salt&platform=3&authorization=Token+&version=1.4.4&source=copyApp&region=$r&webp=1".toByteArray())
}
}
}
}
}
MainActivity.mainWeakReference?.get()?.getString(R.string.groupInfoApiUrl)
?.format(myHostApiUrl, arg1, arg2, arg3)
}

View File

@@ -9,6 +9,7 @@ import java.util.zip.CRC32
import java.util.zip.CheckedOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
import kotlin.random.Random
@@ -33,15 +34,7 @@ class DownloadPool(folder: String) {
}
operator fun plusAssign(quest: Quest) {
packZipFile(quest.fileName, quest.imgUrl, quest.refer)
}
operator fun plusAssign(quests: Array<Quest>) {
Thread{
quests.forEach { quest ->
packZipFile(quest.fileName, quest.imgUrl, quest.refer)
}
}.start()
packZipFile(quest.fileName, quest.imgUrl)
}
fun setOnDownloadListener(onDownloadListener: (String, Boolean, String) -> Unit) {
@@ -52,8 +45,8 @@ class DownloadPool(folder: String) {
mOnPageDownloadListener = onPageDownloadListener
}
private fun packZipFile(fileName: String, imgUrls: Array<String>, refer: String?) {
Thread{
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
@@ -61,9 +54,9 @@ class DownloadPool(folder: String) {
if(f.exists()) {
try {
val zipFile = ZipFile(f)
start = zipFile.size() - 1
start = zipFile.size()
zipFile.close()
Log.d("MyDP", "last downloaded index: $start")
Log.d("MyDP", "next download index: $start")
if (start <= 0 || start >= imgUrls.size) { // error or re-download
f.delete()
f.createNewFile()
@@ -75,8 +68,25 @@ class DownloadPool(folder: String) {
f.createNewFile()
}
} else f.createNewFile()
val zip = ZipOutputStream(CheckedOutputStream(FileOutputStream(f, true), CRC32()))
zip.setLevel(9)
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 {
@@ -93,7 +103,9 @@ class DownloadPool(folder: String) {
zip.closeEntry()
true
}?:false
if(exit) break
if (!s) sleep(2000)
if(exit) break
}
if(!s && tryTimes <= 0) {
succeed = false

View File

@@ -3,7 +3,8 @@ package top.fumiama.copymanga.tools.http
import android.content.Context
import android.util.Log
import androidx.preference.PreferenceManager
import okhttp3.RequestBody
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.dmzj.copymanga.R
import java.net.HttpURLConnection
@@ -12,12 +13,12 @@ import java.util.concurrent.Callable
import java.util.concurrent.FutureTask
object DownloadTools {
fun getConnection(url: String?, method: String = "GET", refer: String? = null, ua: String? = null) =
url?.let {
fun getApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null, timeout: Int = 20000) =
url.let {
val connection = URL(url).openConnection() as HttpURLConnection
connection.requestMethod = method
connection.connectTimeout = 20000
connection.readTimeout = 20000
connection.connectTimeout = timeout
connection.readTimeout = timeout
connection.apply {
setRequestProperty("host", url.substringAfter("://").substringBefore("/"))
ua?.let { setRequestProperty("user-agent", it) }
@@ -37,44 +38,31 @@ object DownloadTools {
}
setRequestProperty("platform", "3")
}
Log.d("Mydl", "getHttp: ${connection.requestProperties.map { "${it.key}: ${it.value}" }.joinToString("\n")}")
Log.d("Mydl", "getConnection: ${connection.requestProperties.map { "${it.key}: ${it.value}" }.joinToString("\n")}")
connection
}
private fun getNormalConnection(url: String?, method: String = "GET", ua: String? = null) =
url?.let {
private fun getNormalConnection(url: String, method: String = "GET", ua: String? = null, timeout: Int = 20000) =
url.let {
val connection = URL(url).openConnection() as HttpURLConnection
connection.requestMethod = method
connection.connectTimeout = 20000
connection.readTimeout = 20000
connection.connectTimeout = timeout
connection.readTimeout = timeout
connection.apply {
setRequestProperty("host", url.substringAfter("://").substringBefore("/"))
ua?.let { setRequestProperty("user-agent", it) }
}
}
fun getHttpContent(u: String, refer: String? = null, ua: String? = null): ByteArray? {
Log.d("Mydl", "getHttp: $u")
var ret: ByteArray? = null
val task = FutureTask(Callable {
try {
getConnection(u, "GET", refer, ua)?.apply {
ret = inputStream.readBytes()
disconnect()
}
} catch (ex: Exception) {
ex.printStackTrace()
suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = null): ByteArray =
withContext(Dispatchers.IO) {
getApiConnection(u, "GET", refer, ua).let {
val ret = it.inputStream.readBytes()
it.disconnect()
Log.d("Mydl", "getHttpContent: ${ret.size} bytes")
ret
}
return@Callable ret
})
Thread(task).start()
return try {
task.get()
} catch (ex: Exception) {
ex.printStackTrace()
null
}
}
fun getHttpContent(u: String, readSize: Int): ByteArray? {
Log.d("Mydl", "getHttp: $u")
@@ -82,13 +70,13 @@ object DownloadTools {
val task = FutureTask(Callable {
try {
val connection = getNormalConnection(u, "GET")
val ci = connection?.inputStream
val ci = connection.inputStream
if(readSize > 0) {
ret = ByteArray(readSize)
ci?.read(ret, 0, readSize)
} else ret = ci?.readBytes()
ci?.close()
connection?.disconnect()
connection.disconnect()
} catch (ex: Exception) {
ex.printStackTrace()
}
@@ -103,7 +91,7 @@ object DownloadTools {
}
}
fun touch(url: String?): FutureTask<ByteArray?>? =
/*fun touch(url: String?): FutureTask<ByteArray?>? =
url?.let {
Log.d("Mydl", "touchHttp: $it")
var ret: ByteArray? = null
@@ -122,7 +110,7 @@ object DownloadTools {
})
Thread(task).start()
task
}
}*/
fun prepare(url: String?): FutureTask<ByteArray?>? =
url?.let {
@@ -156,7 +144,7 @@ object DownloadTools {
var ret: ByteArray? = null
val task = FutureTask(Callable {
try {
getConnection(url, method, refer, ua)?.apply {
getApiConnection(url, method, refer, ua)?.apply {
outputStream.write(body)
ret = inputStream.readBytes()
disconnect()

View File

@@ -6,21 +6,28 @@ import top.fumiama.copymanga.MainActivity
import java.net.URLEncoder
import java.nio.charset.Charset
class Proxy(id: Int, apiPrefixID: Int, keyID: Int? = null) {
class Proxy(id: Int, apiRegexID: Int, keyID: Int? = null) {
private val code = keyID?.let { k ->
MainActivity.mainWeakReference?.get()?.let {
PreferenceManager.getDefaultSharedPreferences(it).getString(it.getString(k), null)
}
}
private val proxyApiUrl = MainActivity.mainWeakReference?.get()?.getString(id)
private val apiPrefix = MainActivity.mainWeakReference?.get()?.getString(apiPrefixID)?:"<no prefix>"
private val apiRegex = Regex(MainActivity.mainWeakReference?.get()?.getString(apiRegexID)?:"<no prefix>")
fun wrap(u: String): String {
if(!u.startsWith(apiPrefix)) return u
if(code != null) {
return proxyApiUrl?.format(code, URLEncoder.encode(u, Charset.defaultCharset().name()))?:u
if(!apiRegex.matches(u)) {
Log.d("MyP", "[N] wrap: $u")
return u
}
return proxyApiUrl?.format(URLEncoder.encode(u, Charset.defaultCharset().name()))?:u
if(!code.isNullOrEmpty()) {
val wu = proxyApiUrl?.format(code, URLEncoder.encode(u, Charset.defaultCharset().name()))?:u
Log.d("MyP", "[M] wrap: $wu")
return wu
}
Log.d("MyP", "[C] wrap: $u")
//return proxyApiUrl?.format(URLEncoder.encode(u, Charset.defaultCharset().name()))?:u
return u
}
companion object {

View File

@@ -2,13 +2,13 @@ package top.fumiama.copymanga.tools.thread
import android.os.Handler
class TimeThread(private val handler: Handler, private val msg: Int) : Thread() {
class TimeThread(private val handler: Handler, private val msg: Int, private val interval: Long = 3000) : Thread() {
var canDo = false
override fun run() {
while (canDo) {
try {
handler.sendEmptyMessage(msg)
sleep(3000)
sleep(interval)
} catch (e: InterruptedException) {
e.printStackTrace()
}

View File

@@ -8,6 +8,8 @@ import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.view.View
import android.widget.Toast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference
import kotlin.math.sqrt
@@ -39,11 +41,11 @@ class UITools(that: Context?, w: WeakReference<Activity>? = null) {
}
} ?: transportStringError
}
fun toastError(s: String, willFinish: Boolean = true) {
suspend fun toastError(s: String, willFinish: Boolean = true) = withContext(Dispatchers.Main) {
Toast.makeText(zis, s, Toast.LENGTH_SHORT).show()
if (willFinish) weak?.get()?.finish()
}
fun toastError(s: Int, willFinish: Boolean = true) {
suspend fun toastError(s: Int, willFinish: Boolean = true) = withContext(Dispatchers.Main) {
Toast.makeText(zis, s, Toast.LENGTH_SHORT).show()
if (willFinish) weak?.get()?.finish()
}

View File

@@ -27,6 +27,7 @@ import top.fumiama.dmzj.copymanga.R
import java.io.File
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicReference
class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
var isOnPause = false
@@ -65,7 +66,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
mBookHandler = BookHandler(WeakReference(this), path)
Log.d("MyBF", "read path: $path")
bookHandler = mBookHandler
bookHandler.set(mBookHandler)
lifecycleScope.launch {
withContext(Dispatchers.IO) {
sleep(600)
@@ -73,14 +74,14 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
}
} else {
bookHandler = mBookHandler
bookHandler.set(mBookHandler)
}
}
override fun onResume() {
super.onResume()
isOnPause = false
bookHandler = mBookHandler
bookHandler.set(mBookHandler)
activity?.apply {
toolbar.title = mBookHandler?.book?.results?.comic?.name
}
@@ -98,7 +99,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
mBookHandler?.ads?.forEach {
it.exit = true
}
bookHandler = null
bookHandler.set(null)
}
fun setStartRead() {
@@ -126,12 +127,14 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
fun setAddToShelf() {
if(mBookHandler?.chapterNames?.isNotEmpty() != true) return
lifecycleScope.launch {
val b = MainActivity.shelf?.query(mBookHandler?.path!!)
mBookHandler?.collect = b?.results?.collect?:-2
Log.d("MyBF", "get collect of ${mBookHandler?.path} = ${mBookHandler?.collect}")
tic.text = b?.results?.browse?.chapter_name?.let { name ->
getString(R.string.text_format_cloud_read_to).format(name)
MainActivity.shelf?.query(mBookHandler?.path!!)?.let { b ->
mBookHandler?.collect = b.results?.collect?:-2
Log.d("MyBF", "get collect of ${mBookHandler?.path} = ${mBookHandler?.collect}")
tic.text = b.results?.browse?.chapter_name?.let { name ->
getString(R.string.text_format_cloud_read_to).format(name)
}
}
mBookHandler?.collect?.let { collect ->
if (collect > 0) {
this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)
@@ -162,12 +165,13 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
}
fun navigate2dl(){
fun navigate2dl() {
val bundle = Bundle()
Log.d("MyBF", "nav2: ${arguments?.getString("path")?:"null"}")
bundle.putString("path", arguments?.getString("path")?:"null")
bundle.putString("name", mBookHandler!!.book?.results?.comic?.name)
if(mBookHandler!!.vols != null) {
bundle.putBoolean("loadJson", true)
if(mBookHandler!!.vols != null && mBookHandler!!.json != null) {
bundle.putString("loadJson", mBookHandler!!.json)
}
bundle.putStringArray("group", mBookHandler!!.gpws)
bundle.putStringArray("groupNames", mBookHandler!!.keys)
@@ -178,6 +182,6 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
companion object {
var bookHandler: BookHandler? = null
var bookHandler: AtomicReference<BookHandler?> = AtomicReference(null)
}
}
}

View File

@@ -2,7 +2,6 @@ package top.fumiama.copymanga.ui.book
import android.graphics.Bitmap
import android.os.Bundle
import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.View
@@ -12,6 +11,7 @@ import android.widget.TextView
import android.widget.Toast
import androidx.core.graphics.drawable.toBitmap
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
@@ -28,6 +28,9 @@ 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.page_nested_list.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.BookInfoStructure
import top.fumiama.copymanga.json.ChapterStructure
@@ -35,7 +38,7 @@ import top.fumiama.copymanga.json.ThemeStructure
import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.template.http.AutoDownloadHandler
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.GlideBlurTransformation
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
@@ -51,7 +54,7 @@ import java.lang.ref.WeakReference
class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
: AutoDownloadHandler(th.get()?.getString(R.string.bookInfoApiUrl)?.format(CMApi.myHostApiUrl, path)?: "",
BookInfoStructure::class.java,
Looper.myLooper()!!){
th.get()){
private val that get() = th.get()
private var hasToastedError = false
get(){
@@ -61,13 +64,14 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
}
var book: BookInfoStructure? = null
private var complete = false
var ads = emptyArray<AutoDownloadThread>()
var ads = emptyArray<PausableDownloader>()
var gpws = arrayOf<String>()
var keys = arrayOf<String>()
var cnts = intArrayOf()
var vols: Array<VolumeStructure>? = null
var chapterNames = arrayOf<String>()
var collect: Int = -1
var json: String? = null
private val divider get() = that?.layoutInflater?.inflate(R.layout.div_h, that?.lbl, false)
var urlArray = arrayOf<String>()
@@ -87,7 +91,7 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
override fun onError() {
super.onError()
if(exit) return
if(!hasToastedError) that?.activity?.runOnUiThread {
if(!hasToastedError) /*that?.activity?.runOnUiThread*/ {
Toast.makeText(that?.context, R.string.null_book, Toast.LENGTH_SHORT).show()
that?.apply { findNavController().popBackStack() }
}
@@ -100,10 +104,11 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
}
override fun getGsonItem() = book
override fun doWhenFinishDownload() {
override suspend fun doWhenFinishDownload() = withContext(Dispatchers.IO) {
super.doWhenFinishDownload()
if(exit) return
if(keys.isEmpty()) book?.results?.groups?.values?.forEach{
if(exit) return@withContext
if(keys.isEmpty()) book?.results?.groups?.values?.forEach {
keys += it.name
gpws += it.path_word
if (it.count == 0) {
@@ -245,14 +250,12 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
}
}
private fun addVolumesView(l: LinearLayout, v: View) {
that?.activity?.runOnUiThread {
l.addView(v)
}
private suspend fun addVolumesView(l: LinearLayout, v: View) = withContext(Dispatchers.Main) {
l.addView(v)
}
private fun setVolume(fbl: LinearLayout, p: Int) = Thread {
if (exit) return@Thread
private suspend fun setVolume(fbl: LinearLayout, p: Int) = withContext(Dispatchers.IO) {
if (exit) return@withContext
that?.apply {
book?.results?.apply {
var i = 0
@@ -261,16 +264,16 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
}
var last = i-1
vols?.get(p)?.let { v ->
if(exit) return@Thread
if(exit) return@withContext
var line: View? = null
last += v.results.list.size
v.results.list.forEach {
val f = CMApi.getZipFile(context?.getExternalFilesDir(""), comic.name, keys[p], it.name)
Log.d("MyBH", "i = $i, last=$last, add chapter ${it.name}, line is null: ${line == null}")
//Log.d("MyBH", "i = $i, last=$last, add chapter ${it.name}, line is null: ${line == null}")
that?.isOnPause?.let { isOnPause ->
while (isOnPause && !exit) sleep(1000)
if (exit) return@Thread
}?:return@Thread
while (isOnPause && !exit) sleep(100)
if (exit) return@withContext
}?:return@withContext
if(line == null) {
if(i == last) {
line = layoutInflater.inflate(R.layout.line_chapter, fbl, false)
@@ -304,10 +307,10 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
}
}
}
}.start()
}
private fun setViewManga() = Thread {
if (exit) return@Thread
private suspend fun setViewManga() = withContext(Dispatchers.IO) {
if (exit) return@withContext
that?.apply {
book?.results?.apply {
ViewMangaActivity.fileArray = arrayOf()
@@ -316,7 +319,7 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
var i = 0
var last = -1
vols?.forEachIndexed { groupIndex, v ->
if(exit) return@Thread
if(exit) return@withContext
last += v.results.list.size
v.results.list.forEach {
urlArray += CMApi.getChapterInfoApiUrl(
@@ -328,16 +331,16 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
chapterNames += it.name
ViewMangaActivity.uuidArray += it.uuid
that?.isOnPause?.let { isOnPause ->
while (isOnPause && !exit) sleep(1000)
if (exit) return@Thread
}?:return@Thread
while (isOnPause && !exit) sleep(100)
if (exit) return@withContext
}?:return@withContext
i++
}
}
}
}
sendEmptyMessage(9) // end set layout
}.start()
}
private fun loadVolume(name: String, path: String, nav: Int){
if(complete) {
@@ -349,7 +352,7 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
}
}
private fun initComicData() = Thread {
private suspend fun initComicData() = withContext(Dispatchers.IO) withIO@ {
var volumes = emptyArray<VolumeStructure>()
val counts = cnts.clone()
gpws.forEachIndexed { i, gpw ->
@@ -358,49 +361,58 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
val times = counts[i] / 100
val remain = counts[i] % 100
val re = arrayOfNulls<VolumeStructure>(if(remain != 0) (times+1) else (times))
if (re.isEmpty()) that?.activity?.runOnUiThread {
Toast.makeText(that?.context, "获取${gpw}失败", Toast.LENGTH_SHORT).show()
return@runOnUiThread
if (re.isEmpty()) {
withContext(Dispatchers.Main) {
Toast.makeText(that?.context, "获取${gpw}失败", Toast.LENGTH_SHORT).show()
}
return@forEachIndexed
}
Log.d("MyBFH", "${i}卷共${if(times == 0) 1 else times}次加载")
do {
counts[i] = counts[i] - 100
CMApi.getGroupInfoApiUrl(path, gpw, offset)?.let {
Log.d("MyBFH", "get api: $it")
if(ComicDlFragment.exit) return@Thread
val ad = AutoDownloadThread(it) { result ->
val r = Gson().fromJson(result?.decodeToString(), VolumeStructure::class.java)
re[r.results.offset / 100] = r
Log.d("MyBFH", "${i}卷返回, 大小: ${r.results.list.size}")
}
ads += ad
ad.start()
offset += 100
sleep(1000)
}
} while (counts[i] > 0)
var c = 0
while (c++ < 80) {
sleep(1000)
if(ComicDlFragment.exit) return@Thread
if(re.all { it != null }) break
}
if(re.isNotEmpty()) {
val r = re[0]
var s = emptyArray<ChapterStructure>()
re.forEach {
it?.results?.list?.forEach {
s += it
if(ComicDlFragment.exit) return@withIO
val ad = PausableDownloader(it) { result ->
try {
val r = Gson().fromJson(result.decodeToString(), VolumeStructure::class.java)
re[r.results.offset / 100] = r
Log.d("MyBFH", "${i}卷返回, 大小: ${r.results.list.size}")
} catch (e: Exception) {
e.printStackTrace()
that?.findNavController()?.popBackStack()
}
}
r?.results?.list = s
r?.apply { volumes += this }
} else re[0]?.apply { volumes += this }
ads += ad
ad.run()
offset += 100
sleep(100)
}
} while (counts[i] > 0)
var c = 0
while (c++ < 80) {
sleep(100)
if(ComicDlFragment.exit) return@withIO
if(re.all { it != null }) break
}
if(re.isNotEmpty()) {
val r = re[0]
var s = emptyArray<ChapterStructure>()
re.forEach { v ->
v?.results?.list?.forEach {
s += it
}
}
r?.results?.list = s
r?.apply { volumes += this }
} else re[0]?.apply { volumes += this }
}
var c = 0
while (c < 80 && volumes.size != gpws.size) {
sleep(1000)
if(ComicDlFragment.exit) return@Thread
sleep(100)
if(ComicDlFragment.exit) return@withIO
Log.d("MyBFH", "已有:${volumes.size} 共:${gpws.size}")
c++
}
@@ -408,7 +420,7 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
saveVolumes(volumes)
that?.fbtab?.let { tab ->
that?.fbvp?.let { vp ->
that?.activity?.runOnUiThread {
withContext(Dispatchers.Main) {
vp.adapter = ViewData(vp).RecyclerViewAdapter()
TabLayoutMediator(tab, vp) { t, p ->
t.text = keys[p]
@@ -418,9 +430,9 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
}
setViewManga()
}
}.start()
}
private fun saveVolumes(volumes: Array<VolumeStructure>) {
private suspend fun saveVolumes(volumes: Array<VolumeStructure>) = withContext(Dispatchers.IO) {
that?.context?.getExternalFilesDir("")?.let { home ->
book?.results?.comic?.name?.let { name ->
val mangaFolder = File(home, name)
@@ -429,27 +441,25 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
File(mangaFolder, "info.json").writeText(json!!)
File(mangaFolder, "grps.json").writeText(Gson().toJson(keys))
that?.apply {
Thread {
var cnt = 0
var success = false
while (cnt++ < 10 && !success) {
sleep(1000)
if (exit) return@Thread
File(mangaFolder, "head.jpg").let { head ->
val fo = head.outputStream()
try {
imic.drawable.toBitmap().compress(Bitmap.CompressFormat.JPEG, 90, fo)
success = true
} catch (e: Exception) {
e.printStackTrace()
}
fo.close()
var cnt = 0
var success = false
while (cnt++ < 10 && !success) {
sleep(100)
if (exit) return@withContext
File(mangaFolder, "head.jpg").let { head ->
val fo = head.outputStream()
try {
imic.drawable.toBitmap().compress(Bitmap.CompressFormat.JPEG, 90, fo)
success = true
} catch (e: Exception) {
e.printStackTrace()
}
fo.close()
}
if (!success) that?.activity?.runOnUiThread {
Toast.makeText(that?.context, R.string.download_cover_timeout, Toast.LENGTH_SHORT).show()
}
}.start()
}
if (!success) withContext(Dispatchers.Main) {
Toast.makeText(that?.context, R.string.download_cover_timeout, Toast.LENGTH_SHORT).show()
}
}
}
}
@@ -463,7 +473,7 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
}
override fun onBindViewHolder(holder: ViewData, position: Int) {
setVolume(holder.itemView.fbl, position)
that?.lifecycleScope?.launch { setVolume(holder.itemView.fbl, position) }
}
override fun getItemCount(): Int = keys.size

View File

@@ -2,12 +2,17 @@ package top.fumiama.copymanga.ui.cardflow.sort
import android.os.Bundle
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.github.zawadz88.materialpopupmenu.popupMenu
import com.google.gson.Gson
import kotlinx.android.synthetic.main.anchor_popular.view.*
import kotlinx.android.synthetic.main.line_sort.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.FilterStructure
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.json.ThemeStructure
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.template.ui.StatusCardFlow
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.dmzj.copymanga.R
@@ -36,102 +41,69 @@ class SortFragment : StatusCardFlow(0, R.id.action_nav_sort_to_nav_book, R.layou
override fun setListeners() {
super.setListeners()
AutoDownloadThread(getString(R.string.filterApiUrl).format(CMApi.myHostApiUrl)) {
if(ad?.exit == true) return@AutoDownloadThread
it?.let {
filter = Gson().fromJson(it.inputStream().reader(), FilterStructure::class.java)
if(ad?.exit == true) return@AutoDownloadThread
activity?.runOnUiThread{
if(ad?.exit != true) setClasses()
lifecycleScope.launch {
PausableDownloader(getString(R.string.filterApiUrl).format(CMApi.myHostApiUrl)) {
if(ad?.exit == true) return@PausableDownloader
it.let {
filter = Gson().fromJson(it.inputStream().reader(), FilterStructure::class.java)
if(ad?.exit == true) return@PausableDownloader
withContext(Dispatchers.Main) {
if(ad?.exit != true) setClasses()
}
}
}
}.start()
}.run()
}
}
private fun setClasses(){
private fun setClasses() {
filter?.results?.top?.let { items ->
if(ad?.exit == true) return@let
line_sort_region.apt.text = "全部"
line_sort_region.setOnClickListener {
val popupMenu = popupMenu {
style = R.style.Widget_MPM_Menu_Dark_CustomBackground
section {
item {
label = "全部"
labelColor = it.apt.currentTextColor
callback = {
region = -1
it.apt.text = "全部"
Thread{
sleep(400)
activity?.runOnUiThread {
reset()
addPage()
}
}.start()
}
}
for(i in items.indices) item {
label = items[i].name
labelColor = it.apt.currentTextColor
callback = { //optional
it.apt.text = label
region = i
Thread{
sleep(400)
activity?.runOnUiThread {
reset()
addPage()
}
}.start()
}
}
}
}
this.context?.let { it1 -> popupMenu.show(it1, it) }
}
setMenu(items, line_sort_region)
}
filter?.results?.theme?.let { items ->
if(ad?.exit == true) return@let
line_sort_class.apt.text = "全部"
line_sort_class.setOnClickListener {
val popupMenu = popupMenu {
style = R.style.Widget_MPM_Menu_Dark_CustomBackground
section {
item {
label = "全部"
labelColor = it.apt.currentTextColor
callback = {
theme = -1
it.apt.text = "全部"
Thread{
sleep(400)
activity?.runOnUiThread {
reset()
addPage()
}
}.start()
}
}
for(i in items.indices) item {
label = items[i].name
labelColor = it.apt.currentTextColor
callback = { //optional
it.apt.text = label
theme = i
Thread{
sleep(400)
activity?.runOnUiThread {
reset()
addPage()
}
}.start()
}
}
}
setMenu(items, line_sort_class)
}
}
private fun suspendReset() {
lifecycleScope.launch {
withContext(Dispatchers.IO) {
sleep(400)
withContext(Dispatchers.Main) {
reset()
addPage()
}
this.context?.let { it1 -> popupMenu.show(it1, it) }
}
}
}
}
private fun setMenu(items: Array<out ThemeStructure>, line: View) {
if(ad?.exit == true) return
line.apt.text = "全部"
line.setOnClickListener {
val popupMenu = popupMenu {
style = R.style.Widget_MPM_Menu_Dark_CustomBackground
section {
item {
label = "全部"
labelColor = it.apt.currentTextColor
callback = {
region = -1
it.apt.text = "全部"
suspendReset()
}
}
for(i in items.indices) item {
label = items[i].name
labelColor = it.apt.currentTextColor
callback = { //optional
it.apt.text = label
region = i
suspendReset()
}
}
}
}
context?.let { c -> popupMenu.show(c, it) }
}
}
}

View File

@@ -1,11 +1,15 @@
package top.fumiama.copymanga.ui.cardflow.topic
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import com.google.gson.Gson
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.fragment_topic.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.TopicStructure
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.template.ui.InfoCardLoader
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.dmzj.copymanga.R
@@ -18,23 +22,27 @@ class TopicFragment : InfoCardLoader(R.layout.fragment_topic, R.id.action_nav_to
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AutoDownloadThread(getString(R.string.topicApiUrl).format(CMApi.myHostApiUrl, arguments?.getString("path"))) { data ->
if(ad?.exit == true) return@AutoDownloadThread
data?.apply {
val r = inputStream().reader()
Gson().fromJson(r, TopicStructure::class.java)?.apply {
if(ad?.exit == true) return@AutoDownloadThread
activity?.let {
it.runOnUiThread {
if(ad?.exit == true) return@runOnUiThread
it.toolbar.title = results.title
ftttime.text = results.datetime_created
fttintro.text = results.intro
type = results.type
lifecycleScope.launch {
PausableDownloader(getString(R.string.topicApiUrl).format(CMApi.myHostApiUrl, arguments?.getString("path"))) { data ->
withContext(Dispatchers.IO) {
if(ad?.exit == true) return@withContext
data.apply {
val r = inputStream().reader()
Gson().fromJson(r, TopicStructure::class.java)?.apply {
if(ad?.exit == true) return@withContext
activity?.let {
withContext(Dispatchers.Main) withMain@ {
if(ad?.exit == true) return@withMain
it.toolbar.title = results.title
ftttime.text = results.datetime_created
fttintro.text = results.intro
type = results.type
}
}
}
}
}
}
}.start()
}.run()
}
}
}
}

View File

@@ -4,11 +4,17 @@ import android.os.Bundle
import android.os.Looper
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.gson.Gson
import kotlinx.android.synthetic.main.fragment_dlcomic.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.ChapterStructure
import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.dmzj.copymanga.R
@@ -18,29 +24,31 @@ import java.lang.ref.WeakReference
class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
var ltbtn: View? = null
var ads = emptyArray<AutoDownloadThread>()
private var ads = emptyArray<PausableDownloader>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
exit = false
ldwn?.setPadding(0, 0, 0, navBarHeight)
if(isFirstInflate){
when {
arguments?.getBoolean("callFromOldDL", false) == true -> initOldComicData()
arguments?.getBoolean("loadJson", false) == true -> context?.getExternalFilesDir("")?.let { home ->
arguments?.getString("name")?.let {
Thread{
if(isFirstInflate) lifecycleScope.launch {
withContext(Dispatchers.IO) {
when {
arguments?.getBoolean("callFromOldDL", false) == true -> initOldComicData()
arguments?.containsKey("loadJson") == true -> context?.getExternalFilesDir("")?.let { home ->
arguments?.getString("name")?.let {
sleep(600)
activity?.runOnUiThread {
start2load(loadFromJson(), true, loadGroupsFromFile(File(home, "$it/grps.json")))
}
}.start()
Log.d("MyCDF", "loadJson by arguments")
start2load(
loadFromJson(arguments?.getString("loadJson")!!),
true, loadGroupsFromFile(File(home, "$it/grps.json"))
)
}
}
else -> initComicData(
arguments?.getString("path"),
arguments?.getStringArray("group"),
arguments?.getIntArray("count")
)
}
else -> initComicData(
arguments?.getString("path"),
arguments?.getStringArray("group"),
arguments?.getIntArray("count")
)
}
}
}
@@ -58,13 +66,14 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
handler = null
}
private fun start2load(volumes: Array<VolumeStructure>, isFromFile: Boolean = false, groupArray: Array<String>? =null){
handler = ComicDlHandler(Looper.myLooper()!!,
WeakReference(this),
volumes,
arguments?.getString("name")?:"null",
if(isFromFile) groupArray else arguments?.getStringArray("groupNames"))
if(!isFromFile) Thread{
private suspend fun start2load(volumes: Array<VolumeStructure>, isFromFile: Boolean = false, groupArray: Array<String>? =null) = withContext(Dispatchers.IO) {
withContext(Dispatchers.Main) {
handler = ComicDlHandler(Looper.myLooper()!!, WeakReference(this@ComicDlFragment),
volumes, arguments?.getString("name")?:"null",
if(isFromFile) groupArray else arguments?.getStringArray("groupNames")
)
}
if(!isFromFile) {
context?.getExternalFilesDir("")?.let { home ->
arguments?.getString("name")?.let { name ->
val mangaFolder = File(home, name)
@@ -75,18 +84,18 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
}
}
}
}.start()
}
handler?.startLoad()
}
private fun loadFromJson() = Gson().fromJson(json, Array<VolumeStructure>::class.java)
private fun loadFromJson(json: String) = Gson().fromJson(json, Array<VolumeStructure>::class.java)
private fun loadGroupsFromFile(file: File) = Gson().fromJson(file.reader(), Array<String>::class.java)
/*private fun setMenuInvisible(menu: Menu){
menu.findItem(R.id.action_download)?.isVisible = false
}*/
private fun initComicData(pw: String?, gpws: Array<String>?, counts: IntArray?) = Thread {
private suspend fun initComicData(pw: String?, gpws: Array<String>?, counts: IntArray?) = withContext(Dispatchers.IO) {
var volumes = emptyArray<VolumeStructure>()
if (gpws != null) {
gpws.forEachIndexed { i, gpw ->
@@ -99,21 +108,29 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
do {
counts?.set(i, counts[i] - 100)
CMApi.getGroupInfoApiUrl(pw, gpw, offset)?.let {
if(exit) return@Thread
val ad = AutoDownloadThread(it) { result ->
if(exit) return@withContext
val ad = PausableDownloader(it) { result ->
Log.d("MyCDF", "${i}卷返回")
val r = Gson().fromJson(result?.decodeToString(), VolumeStructure::class.java)
val r = Gson().fromJson(result.decodeToString(), VolumeStructure::class.java)
re[r.results.offset / 100] = r
}
ads += ad
ad.start()
try {
ad.run()
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(context, "加载${gpw}${i}部分失败", Toast.LENGTH_SHORT).show()
findNavController().popBackStack()
}
}
offset += 100
}
} while ((counts?.get(i) ?: 0) > 0)
var c = 0
while (c++ < 80) {
sleep(1000)
if(exit) return@Thread
if(exit) return@withContext
if(re.all { it != null }) break
}
if(re.size > 1) {
@@ -131,21 +148,18 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
var c = 0
while (c < 80 && volumes.size != gpws.size) {
sleep(1000)
if(exit) return@Thread
if(exit) return@withContext
Log.d("MyCDF", "已有:${volumes.size} 共:${gpws.size}")
c++
}
if (volumes.size == gpws.size) {
activity?.runOnUiThread {
start2load(volumes)
}
start2load(volumes)
}
}
}.start()
}
private fun initOldComicData() {
handler = ComicDlHandler(Looper.myLooper()!!,
WeakReference(this),
private suspend fun initOldComicData() = withContext(Dispatchers.IO) {
handler = ComicDlHandler(Looper.myLooper()!!, WeakReference(this@ComicDlFragment),
arguments?.getString("name")?:"null")
handler?.startLoad()
}

View File

@@ -11,6 +11,7 @@ import android.util.Log
import android.view.ViewGroup
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.*
@@ -20,8 +21,12 @@ 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.line_caption.view.*
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
@@ -92,41 +97,35 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
that?.tdwn?.text = "${dldChapter}/${checkedChapter}"
}
6 -> that?.tdwn?.text = "${dldChapter}/${checkedChapter}"
7 -> deleteChapters(msg.obj as File, msg.arg1)
7 -> that?.lifecycleScope?.launch { deleteChapters(msg.obj as File, msg.arg1) }
9 -> that?.cdwn?.setCardBackgroundColor(that!!.resources.getColor(R.color.colorGreen))
10 -> addTbtn(msg.obj as Array<String>)
11 -> addCaption(msg.obj as String)
12 -> addDiv()
//10 -> addButton(msg.obj as Array<String>)
//11 -> addCaption(msg.obj as String)
//12 -> addDiv()
13 -> if(complete) showMultiSelectInfo()
}
}
fun startLoad(){
suspend fun startLoad() = withContext(Dispatchers.IO) {
setComponents()
if(isOld) analyzeOldStructure()
else Thread{
else {
urlArray = arrayOf()
ViewMangaActivity.fileArray = arrayOf()
ViewMangaActivity.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)}")
obtainMessage(11, caption).sendToTarget() //addCaption
vol.results.list.forEach { chapter ->
var data = arrayOf<String>()
data += chapter.name
data += chapter.uuid
data += caption
data += CMApi.getChapterInfoApiUrl(chapter.comic_path_word, chapter.uuid)?:""
obtainMessage(10, data).sendToTarget()
withContext(Dispatchers.Main) {
addCaption(caption) {
addButtons(vol.results.list, caption)
}
}
sendEmptyMessage(12) //addDiv
}
complete = true
}.start()
}
}
private fun addDiv(){
private suspend fun addDiv() = withContext(Dispatchers.Main) {
that?.ldwn?.addView(
that!!.layoutInflater.inflate(R.layout.div_h, that!!.ldwn, false),
ViewGroup.LayoutParams(
@@ -135,7 +134,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
)
)
}
private fun addCaption(title: String){
private suspend fun addCaption(title: String, body: suspend() -> Unit) = withContext(Dispatchers.Main) {
val tc = that?.layoutInflater?.inflate(R.layout.line_caption, that!!.ldwn, false)
tc?.tcptn?.text = title
that?.ldwn?.addView(
@@ -147,13 +146,16 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
)
addDiv()
isNewTitle = true
body()
}
private fun deleteChapter(f: File, v: ChapterToggleButton) {
private suspend fun deleteChapter(f: File, v: ChapterToggleButton) = withContext(Dispatchers.IO) {
f.delete()
v.setBackgroundResource(R.drawable.toggle_button)
v.isChecked = false
withContext(Dispatchers.Main) {
v.setBackgroundResource(R.drawable.toggle_button)
v.isChecked = false
}
}
private fun deleteChapters(zipf: File, index: Int) {
private suspend fun deleteChapters(zipf: File, index: Int) = withContext(Dispatchers.IO) {
if (multiSelect) {
for (i in tbtnlist) {
if (i.isChecked) {
@@ -183,7 +185,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
end
).setDuration(duration).start()
}
private fun setComponents() {
private suspend fun setComponents() = withContext(Dispatchers.Main) {
val widthData = toolsBox.calcWidthFromDpRoot(8, 64)
btnNumPerRow = widthData[0]
btnw = widthData[1]
@@ -210,23 +212,21 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
that!!.pdwn.progress = 0
if (downloading || checkedChapter == 0) {
mangaDlTools.wait = !mangaDlTools.wait!!
} else {
} else that?.lifecycleScope?.launch {
downloading = true
Thread {
sendEmptyMessage(9)
finishMap = arrayOfNulls(tbtnlist.size)
downloadChapterPages()
}.start()
sendEmptyMessage(9)
finishMap = arrayOfNulls(tbtnlist.size)
downloadChapterPages()
}
}
}
that?.cdwn?.setOnLongClickListener {
Thread { sendEmptyMessage(4) }.start()
sendEmptyMessage(4)
return@setOnLongClickListener true
}
mangaDlTools.onDownloadedListener = object :MangaDlTools.OnDownloadedListener{
mangaDlTools.onDownloadedListener = object :MangaDlTools.OnDownloadedListener {
override fun handleMessage(index: Int, isSuccess: Boolean, message: String) {
that?.activity?.runOnUiThread {
that?.lifecycleScope?.launch {
if(isSuccess) onZipDownloadFinish(index)
else onZipDownloadFailure(index, message)
}
@@ -240,7 +240,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
isSuccess: Boolean,
message: String
) {
that?.activity?.runOnUiThread {
that?.lifecycleScope?.launch {
if(isSuccess) {
tbtnlist[index].text = if(downloaded == 0 && total == 0) tbtnlist[index].chapterName else "$downloaded/$total"
} else {
@@ -262,30 +262,38 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
"确定", null, "取消", { multiSelect = true })
}
private fun downloadChapterPages() {
private suspend fun downloadChapterPages() = withContext(Dispatchers.IO) {
tbtnlist.forEach { i ->
if(i.isChecked) {
withContext(Dispatchers.Main) {
i.isEnabled = false
}
i.url?.let {
mangaDlTools.downloadChapterInVol(
it,
i.chapterName,
i.caption?:"null",
i.index
)
Thread {
that?.lifecycleScope?.launch {
mangaDlTools.downloadChapterInVol(
it,
i.chapterName,
i.caption?:"null",
i.index
)
}
}.start()
}
}
}
}
private fun onZipDownloadFinish(index: Int) {
private suspend fun onZipDownloadFinish(index: Int) = withContext(Dispatchers.Main) {
if(index >= 0 && index < tbtnlist.size) {
tbtnlist[index].setBackgroundResource(R.drawable.rndbg_checked)
tbtnlist[index].isChecked = false
tbtnlist[index].isEnabled = true
finishMap[index] = true
updateProgressBar()
that?.apply {
cdwn.postDelayed({
if (mangaDlTools?.exit != false) return@postDelayed
if (mangaDlTools.exit) return@postDelayed
if (dldChapter == checkedChapter) {
checkedChapter = 0
setProgress2(0, 233)
@@ -299,8 +307,9 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
}
}
private fun onZipDownloadFailure(index: Int, message: String) {
private suspend fun onZipDownloadFailure(index: Int, message: String) = withContext(Dispatchers.Main) {
tbtnlist[index].setBackgroundResource(R.drawable.rndbg_error)
tbtnlist[index].isEnabled = true
Toast.makeText(that?.context, "下载${tbtnlist[index].chapterName}失败: $message", Toast.LENGTH_SHORT).show()
updateProgressBar()
}
@@ -314,12 +323,16 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
//ObjectAnimator.ofFloat(dlsdwn, "alpha", 0.9f, 0.3f).setDuration(233).start()
ObjectAnimator.ofFloat(that?.dlsdwn, "translationX", 0f, cdwnWidth.toFloat() * 0.9f).setDuration(233).start()
}
private fun addTbtn(data: Array<String>){
addTbtn(data[0], data[1], data[2], data[3])
urlArray += data[3]
private suspend fun addButtons(chapters: Array<ChapterStructure>, caption: String) = withContext(Dispatchers.IO) {
chapters.forEach { chapter ->
val u = CMApi.getChapterInfoApiUrl(chapter.comic_path_word, chapter.uuid)?:""
addButton(chapter.name, chapter.uuid, caption, u)
urlArray += u
}
addDiv()
}
@SuppressLint("SetTextI18n")
private fun addTbtn(title: String, uuid: String, caption: String, url: String) {
private suspend fun addButton(title: String, uuid: String, caption: String, url: String) = withContext(Dispatchers.Main) {
if ((tbtncnt % btnNumPerRow == 0) || isNewTitle) {
that?.ltbtn = that?.layoutInflater?.inflate(R.layout.line_horizonal_empty, that!!.ldwn, false)
that?.ldwn?.addView(that!!.ltbtn)
@@ -338,82 +351,86 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
//tbv.tbtn.hint = caption
tbv.tbtn.caption = caption
tbv.tbtn.layoutParams.width = btnw
val zipf = CMApi.getZipFile(that!!.context?.getExternalFilesDir(""), comicName, caption, title)
Log.d("MyCD", "Get zipf: $zipf")
ViewMangaActivity.fileArray += zipf
if (zipf.exists()) {
tbv.tbtn.setBackgroundResource(R.drawable.rndbg_checked)
tbv.tbtn.isChecked = false
}
that?.ltbtn?.ltbtn?.addView(tbv)
that?.ltbtn?.invalidate()
tbv.tbtn.setOnClickListener {
if (zipf.exists() && !multiSelect) {
it.tbtn.setBackgroundResource(R.drawable.rndbg_checked)
it.tbtn.isChecked = false
ViewMangaActivity.zipFile = zipf
ViewMangaActivity.dlHandler = this
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)
} else {
it.tbtn.setBackgroundResource(R.drawable.toggle_button)
if (it.tbtn.isChecked) that?.tdwn?.text = "$dldChapter/${++checkedChapter}"
else that?.tdwn?.text = "$dldChapter/${--checkedChapter}"
}
}
tbv.tbtn.setOnLongClickListener {
if (zipf.exists()) {
toolsBox.buildInfo("确认删除${if (multiSelect) "这些" else "本"}章节?",
"该操作将不可撤销",
"确定",
null,
"取消",
{
Thread {
obtainMessage(7, it.tbtn.index, 0, zipf).sendToTarget()
}.start()
})
}else{
toolsBox.buildInfo("直接观看", "不下载而进行观看", "确定",
null, "取消", {
ViewMangaActivity.zipFile = null
ViewMangaActivity.dlHandler = this
ViewMangaActivity.position = it.tbtn.index
dl?.show()
val intent = Intent(that?.context, ViewMangaActivity::class.java)
intent.putExtra("urlArray", urlArray)
that?.startActivity(intent)
}, null, null
)
withContext(Dispatchers.IO) {
val zipf = CMApi.getZipFile(that!!.context?.getExternalFilesDir(""), comicName, caption, title)
Log.d("MyCD", "Get zipf: $zipf")
ViewMangaActivity.fileArray += zipf
if (zipf.exists()) withContext(Dispatchers.Main) {
tbv.tbtn.setBackgroundResource(R.drawable.rndbg_checked)
tbv.tbtn.isChecked = false
}
tbv.tbtn.setOnClickListener {
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)
} else {
it.tbtn.setBackgroundResource(R.drawable.toggle_button)
if (it.tbtn.isChecked) that?.tdwn?.text = "$dldChapter/${++checkedChapter}"
else that?.tdwn?.text = "$dldChapter/${--checkedChapter}"
}
}
tbv.tbtn.setOnLongClickListener {
if (zipf.exists()) {
toolsBox.buildInfo("确认删除${if (multiSelect) "这些" else "本"}章节?",
"该操作将不可撤销",
"确定",
null,
"取消",
{
obtainMessage(7, it.tbtn.index, 0, zipf).sendToTarget()
})
} 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)
}, null, null
)
}
true
}
true
}
}
}
private fun analyzeOldStructure() = Thread{
private suspend fun analyzeOldStructure() = withContext(Dispatchers.IO) {
Gson().fromJson(json?.reader(), Array<ComicStructureOld>::class.java)?.let {
for (group in it) {
that?.layoutInflater?.inflate(R.layout.line_caption, that!!.ldwn, false)?.let { tc ->
tc.tcptn.text = group.name
that!!.ldwn.addView(
tc,
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
withContext(Dispatchers.Main) {
that!!.ldwn.addView(
tc,
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
)
that!!.ldwn.addView(
that!!.layoutInflater.inflate(R.layout.div_h, that!!.ldwn, false),
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
that!!.ldwn.addView(
that!!.layoutInflater.inflate(R.layout.div_h, that!!.ldwn, false),
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
)
}
isNewTitle = true
for (chapter in group.chapters) {
val newUrl = CMApi.getChapterInfoApiUrl(
@@ -421,10 +438,11 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
chapter.url.substringAfterLast('/')
)?:""
Log.d("MyCD", "Generate new url: $newUrl")
obtainMessage(10, arrayOf(chapter.name, "", group.name, newUrl)).sendToTarget()
addButton(chapter.name, "", group.name, newUrl)
urlArray += newUrl
}
}
}
}
}.start()
}
}
}

View File

@@ -84,9 +84,12 @@ class DownloadFragment: NoBackRefreshFragment(R.layout.fragment_download) {
private fun callDownloadFragment(jsonFile: File, isNew: Boolean = false){
val bundle = Bundle()
Log.d("MyDF", "Call dl and is new: $isNew")
bundle.putBoolean(if(isNew) "loadJson" else "callFromOldDL", true)
if(isNew) {
bundle.putString("loadJson", jsonFile.readText())
} else {
bundle.putBoolean("callFromOldDL", true)
}
bundle.putString("name", jsonFile.parentFile?.name?:"Null")
ComicDlFragment.json = jsonFile.readText()
Log.d("MyDF", "root view: $rootView")
Log.d("MyDF", "action_nav_download_to_nav_group")
Navigate.safeNavigateTo(findNavController(), R.id.action_nav_download_to_nav_group, bundle)

View File

@@ -169,9 +169,8 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
private fun callDownloadFragment(name: String){
val bundle = Bundle()
Log.d("MyNDF", "Call dl and is new.")
bundle.putBoolean("loadJson", true)
bundle.putString("loadJson", File(File(extDir, name), "info.json").readText())
bundle.putString("name", name)
ComicDlFragment.json = File(File(extDir, name), "info.json").readText()
Log.d("MyNDF", "root view: $rootView")
Log.d("MyNDF", "action_nav_new_download_to_nav_group")
Navigate.safeNavigateTo(findNavController(), R.id.action_nav_new_download_to_nav_group, bundle)

View File

@@ -11,6 +11,7 @@ import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageButton
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -22,12 +23,15 @@ import kotlinx.android.synthetic.main.card_book_plain.view.*
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.line_word.view.*
import kotlinx.android.synthetic.main.viewpage_horizonal.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.MainActivity.Companion.ime
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.BookListStructure
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
import top.fumiama.copymanga.tools.ui.Navigate
@@ -44,15 +48,15 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
if(isFirstInflate) {
val tb = mainWeakReference?.get()?.toolsBox
val netInfo = tb?.netInfo
if(netInfo != null && netInfo != tb.transportStringNull && netInfo != tb.transportStringError) Thread {
val l = MainActivity.member?.refreshAvatar()
if (l?.code != 200) {
mainWeakReference?.get()?.runOnUiThread {
Toast.makeText(context, "${getString(R.string.login_get_avatar_failed)}: 代码${l?.code}, 信息: ${l?.message}", Toast.LENGTH_SHORT).show()
if(netInfo != null && netInfo != tb.transportStringNull && netInfo != tb.transportStringError)
MainActivity.member?.apply { lifecycleScope.launch {
info().let { l ->
if (l.code != 200 && l.code != 449) {
Toast.makeText(context, l.message, Toast.LENGTH_SHORT).show()
logout()
}
}
MainActivity.member?.logout()
}
}.start()
} }
homeHandler = HomeHandler(WeakReference(this))
val theme = resources.newTheme()
@@ -92,12 +96,14 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
override fun onQueryTextChange(newText: CharSequence): Boolean {
if (newText.contentEquals("__notice_focus_change__") || newText.contentEquals(lastSearch)) return true
postDelayed({
val diff = System.currentTimeMillis() - lastChangeTime
if(diff > 500) {
if (newText.isNotEmpty()) {
Log.d("MyHF", "new text: $newText")
lastSearch = newText.toString()
adapter.refresh(newText)
lifecycleScope.launch {
val diff = System.currentTimeMillis() - lastChangeTime
if(diff > 500) {
if (newText.isNotEmpty()) {
Log.d("MyHF", "new text: $newText")
lastSearch = newText.toString()
adapter.refresh(newText)
}
}
}
}, 1024)
@@ -154,14 +160,16 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
}
}
Thread{
homeHandler.obtainMessage(-1, true).sendToTarget()
while(!MainActivity.isDrawerClosed) sleep(233)
//homeHandler.sendEmptyMessage(6) //removeAllViews
homeHandler.fhib = null
sleep(600)
homeHandler.startLoad()
}.start()
lifecycleScope.launch{
withContext(Dispatchers.IO) {
homeHandler.obtainMessage(-1, true).sendToTarget()
while(!MainActivity.isDrawerClosed) sleep(233)
//homeHandler.sendEmptyMessage(6) //removeAllViews
homeHandler.fhib = null
sleep(600)
homeHandler.startLoad()
}
}
}
}
@@ -261,14 +269,16 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
override fun getItemCount() = (results?.results?.list?.size?:0) + if (query?.isNotEmpty() == true) 1 else 0
fun refresh(q: CharSequence) {
suspend fun refresh(q: CharSequence) = withContext(Dispatchers.IO) {
query = q.toString()
activity?.apply {
AutoDownloadThread(getString(R.string.searchApiUrl).format(CMApi.myHostApiUrl, 0, query, type)) {
results = Gson().fromJson(it?.decodeToString(), BookListStructure::class.java)
PausableDownloader(getString(R.string.searchApiUrl).format(CMApi.myHostApiUrl, 0, query, type)) {
results = Gson().fromJson(it.decodeToString(), BookListStructure::class.java)
count = results?.results?.total?:0
runOnUiThread { notifyDataSetChanged() }
}.start()
withContext(Dispatchers.Main) {
notifyDataSetChanged()
}
}.run()
}
}
}

View File

@@ -3,7 +3,6 @@ package top.fumiama.copymanga.ui.home
import android.animation.ObjectAnimator
import android.graphics.Color
import android.os.Bundle
import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.View
@@ -13,6 +12,7 @@ import android.widget.Toast
import androidx.cardview.widget.CardView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.animation.doOnEnd
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.bumptech.glide.Glide
@@ -23,6 +23,9 @@ import com.to.aboomy.pager2banner.ScaleInTransformer
import kotlinx.android.synthetic.main.card_book.view.*
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.line_1bookline.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.ComicStructure
import top.fumiama.copymanga.json.IndexStructure
import top.fumiama.copymanga.template.http.AutoDownloadHandler
@@ -37,7 +40,7 @@ import java.lang.ref.WeakReference
class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadHandler(
that.get()?.getString(R.string.mainPageApiUrl)!!.format(CMApi.myHostApiUrl),
IndexStructure::class.java,
Looper.myLooper()!!,
that.get(),
9
) {
private val homeF get() = that.get()
@@ -47,7 +50,7 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
Log.d("MyHH", "Get fhib.")
if (field == null) {
field = homeF?.layoutInflater?.inflate(R.layout.viewpage_banner, homeF?.fhl, false)
Thread{ homeF?.homeHandler?.sendEmptyMessage(3) }.start()
homeF?.homeHandler?.sendEmptyMessage(3)
}
return field
}
@@ -104,23 +107,17 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
if(exit) return
Toast.makeText(homeF?.context, R.string.web_error, Toast.LENGTH_SHORT).show()
}
override fun doWhenFinishDownload() {
override suspend fun doWhenFinishDownload() = withContext(Dispatchers.IO) {
super.doWhenFinishDownload()
if(exit) return
try {
Thread {
sendEmptyMessage(2) //setSwipe
sendEmptyMessage(7) //inflateBanner
sendEmptyMessage(1) //inflateCardLines
}.start()
} catch (e: Exception) {
Toast.makeText(homeF?.context, R.string.load_home_error, Toast.LENGTH_SHORT).show()
}
if(exit) return@withContext
sendEmptyMessage(2) //setSwipe
sendEmptyMessage(7) //inflateBanner
sendEmptyMessage(1) //inflateCardLines
}
private fun inflateBanner() = homeF?.fhl?.addView(fhib)
private fun inflateTopics(){
private suspend fun inflateTopics() {
index?.results?.topics?.list?.let {
var comics = arrayOf<ComicStructure>()
for((i, topic) in it.withIndex()){
@@ -135,7 +132,7 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
}
}
private fun inflateRec(){
private suspend fun inflateRec() {
index?.results?.recComics?.list?.let {
var comics = arrayOf<ComicStructure>()
for((i, rec) in it.withIndex()){
@@ -150,7 +147,7 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
}
}
private fun inflateRank(){
private suspend fun inflateRank(){
var comics = arrayOf<ComicStructure>()
index?.results?.rankDayComics?.list?.let {
for((i, book) in it.withIndex()){
@@ -175,7 +172,7 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
}
}
private fun inflateHot(){
private suspend fun inflateHot(){
index?.results?.hotComics?.let {
var comics = arrayOf<ComicStructure>()
for((i, rec) in it.withIndex()){
@@ -186,7 +183,7 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
}
}
private fun inflateNew(){
private suspend fun inflateNew(){
index?.results?.newComics?.let {
var comics = arrayOf<ComicStructure>()
for((i, rec) in it.withIndex()){
@@ -201,7 +198,7 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
}
}
private fun inflateFinish(){
private suspend fun inflateFinish(){
index?.results?.finishComics?.list?.let {
var comics = arrayOf<ComicStructure>()
for((i, rec) in it.withIndex()){
@@ -216,20 +213,24 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
}
}
private fun inflateCardLines() = Thread {
if (indexLines.isNotEmpty()) indexLines = arrayOf()
inflateRec()
inflateTopics()
inflateHot()
inflateNew()
inflateFinish()
inflateRank()
for(i in indexLines.indices) {
obtainMessage(8, i, 0).sendToTarget()
sleep(512)
private fun inflateCardLines() {
homeF?.lifecycleScope?.launch {
withContext(Dispatchers.IO) {
if (indexLines.isNotEmpty()) indexLines = arrayOf()
inflateRec()
inflateTopics()
inflateHot()
inflateNew()
inflateFinish()
inflateRank()
for(i in indexLines.indices) {
obtainMessage(8, i, 0).sendToTarget()
sleep(512)
}
obtainMessage(-1, false).sendToTarget() //closeLoad
}
}
obtainMessage(-1, false).sendToTarget() //closeLoad
}.start()
}
private fun setBanner(v: Banner): Banner {
v.viewTreeObserver.addOnGlobalLayoutListener(object :
@@ -241,7 +242,7 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
v.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
Thread{this.obtainMessage(5, v).sendToTarget()}.start() //setBannerInfo
obtainMessage(5, v).sendToTarget() //setBannerInfo
return v
}
@@ -264,20 +265,23 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
homeF?.fhov?.swipeRefreshLayout = sw
sw.setOnRefreshListener {
Log.d("MyHFH", "Refresh items.")
//index = null
//Thread{this@HomeHandler.obtainMessage(-1, true).sendToTarget()}.start() //startLoad
Thread{
index = null
//fhib = null
indexLines = arrayOf()
this@HomeHandler.sendEmptyMessage(6) //removeAllViews
sleep(300)
this@HomeHandler.sendEmptyMessage(0) //setLayouts
}.start()
homeF?.lifecycleScope?.launch {
withContext(Dispatchers.IO) {
index = null
//fhib = null
indexLines = arrayOf()
this@HomeHandler.sendEmptyMessage(6) //removeAllViews
sleep(300)
this@HomeHandler.sendEmptyMessage(0) //setLayouts
}
}
}
}
private fun allocateLine(title: String, iconResId: Int, comics: Array<ComicStructure>, finish: Boolean = false, isTopic: Boolean = false, onClick: (() -> Unit)? = null): Int{
private suspend fun allocateLine(
title: String, iconResId: Int, comics: Array<ComicStructure>,
finish: Boolean = false, isTopic: Boolean = false, onClick: (() -> Unit)? = null
): Int = withContext(Dispatchers.IO) {
val p = indexLines.size
val c = comics.size / 3
homeF?.layoutInflater?.inflate(
@@ -285,22 +289,24 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
1 -> R.layout.line_1bookline
2 -> R.layout.line_2bookline
3 -> R.layout.line_3bookline
else -> return -1
}, homeF!!.fhl, false)?.apply {
scanCards(this, comics, finish, isTopic)
rttitle.text = title
ir.setImageResource(iconResId)
setLineHeight(this, c)
if(onClick != null) setOnClickListener { onClick() }
else -> return@withContext -1
}, homeF!!.fhl, false)?.apply {
withContext(Dispatchers.Main) {
scanCards(this@apply, comics, finish, isTopic)
rttitle.text = title
ir.setImageResource(iconResId)
setLineHeight(this@apply, c)
if(onClick != null) setOnClickListener { onClick() }
}
indexLines += this
}
return p
return@withContext p
}
private fun scanCards(v: View, comics: Array<ComicStructure>, finish: Boolean, isTopic: Boolean){
private suspend fun scanCards(v: View, comics: Array<ComicStructure>, finish: Boolean, isTopic: Boolean) = withContext(Dispatchers.IO) {
var id = v.rc1.id
var card = v.findViewById<ConstraintLayout>(id)
for (data in comics){
for (data in comics) {
setCards(
card.cic,
data.path_word,
@@ -313,10 +319,10 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
}
}
private fun setCards(cv: CardView, pw: String, name: String, img: String, isFinal: Boolean, isTopic: Boolean) {
private suspend fun setCards(cv: CardView, pw: String, name: String, img: String, isFinal: Boolean, isTopic: Boolean) = withContext(Dispatchers.Main) {
cv.tic.text = name
homeF?.let {
if(img.startsWith("http")) it.activity?.runOnUiThread {
if(img.startsWith("http")) {
Glide.with(it).load(GlideUrl(CMApi.proxy?.wrap(img)?:img, CMApi.myGlideHeaders))
.addListener(GlideHideLottieViewListener(WeakReference(cv.laic)))
.timeout(20000).into(cv.imic)
@@ -348,4 +354,4 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
}
})
}
}
}

View File

@@ -3,19 +3,22 @@ package top.fumiama.copymanga.ui.vm
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Dialog
import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.View
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import androidx.lifecycle.lifecycleScope
import com.google.gson.Gson
import kotlinx.android.synthetic.main.activity_viewmanga.*
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.withContext
import top.fumiama.copymanga.json.Chapter2Return
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
@@ -27,13 +30,12 @@ import java.lang.ref.WeakReference
import java.text.SimpleDateFormat
import java.util.*
class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
url, Chapter2Return::class.java, Looper.myLooper()!!
class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, private val weeks: Array<String>) : AutoDownloadHandler(
chapterUrl, Chapter2Return::class.java, activity
) {
var manga: Chapter2Return? = null
private val wv = WeakReference(activity)
private val drawer = wv.get()?.infcard
private val weeks = wv.get()?.getStringArray(R.array.weeks)
private var hasDrawerShown = false
val dl = activity.let {
val re = Dialog(it)
@@ -50,7 +52,7 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
val cal = Calendar.getInstance()
val w = cal[Calendar.DAY_OF_WEEK]
if (w > 7 || w <= 0) return ""
return weeks?.get(w-1) ?: ""
return weeks[w-1]
}
private var remainingImageCount = 0
@@ -72,7 +74,11 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
LOAD_IMG_ON -> {
val scaleImageView = msg.obj as ScaleImageView
// msg.arg2: isLast
wv.get()?.loadImgOn(scaleImageView, msg.arg1)
wv.get()?.apply {
lifecycleScope.launch {
loadImgOn(scaleImageView, msg.arg1)
}
}
//scaleImageView.setHeight2FitImgWidth()
//if(msg.arg2 == 1) sendEmptyMessage(DELAYED_RESTORE_PAGE_NUMBER)
}
@@ -86,8 +92,8 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
LOAD_ITEM_SCROLL_MODE -> loadScrollMode(msg.arg1, msg.obj as? Runnable?)
LOAD_SCROLL_MODE -> loadScrollMode()
LOAD_ITEM_IMAGES_INTO_LINE -> loadImagesIntoLine(msg.arg1, msg.obj as? Runnable?)
LOAD_IMAGES_INTO_LINE -> loadImagesIntoLine()
LOAD_ITEM_IMAGES_INTO_LINE -> wv.get()?.lifecycleScope?.launch { loadImagesIntoLine(msg.arg1, msg.obj as? Runnable?) }
LOAD_IMAGES_INTO_LINE -> wv.get()?.lifecycleScope?.launch { loadImagesIntoLine() }
RESTORE_PAGE_NUMBER -> {
sendEmptyMessage(DIALOG_HIDE)
wv.get()?.restorePN()
@@ -141,17 +147,22 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
override fun onError() {
super.onError()
if(exit) return
wv.get()?.toolsBox?.toastError(R.string.download_chapter_info_failed)
wv.get()?.apply {
lifecycleScope.launch {
toolsBox.toastError(R.string.download_chapter_info_failed)
}
}
}
override fun doWhenFinishDownload() {
override suspend fun doWhenFinishDownload() {
super.doWhenFinishDownload()
if(exit) return
prepareManga()
}
fun loadFromFile(file: File): Boolean {
return try {
suspend fun loadFromFile(file: File): Boolean = withContext(Dispatchers.IO) {
fakeLoad()
return@withContext try {
val jsonFile = File(file.parentFile, "${file.nameWithoutExtension}.json")
if(jsonFile.exists()) {
manga = Gson().fromJson(jsonFile.reader(), Chapter2Return::class.java)
@@ -180,7 +191,11 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
}
}
private fun prepareManga(){
private suspend fun fakeLoad() {
PausableDownloader(chapterUrl) { _ -> }.run()
}
private suspend fun prepareManga() = withContext(Dispatchers.Main) {
if(comicName == null) {
comicName = manga?.results?.comic?.name
}
@@ -188,7 +203,7 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
wv.get()?.initManga()
wv.get()?.vprog?.visibility = View.GONE
}
private fun loadImagesIntoLine(item: Int = (wv.get()?.currentItem?:0), doAfter: Runnable? = null) = Thread{
private suspend fun loadImagesIntoLine(item: Int = (wv.get()?.currentItem?:0), doAfter: Runnable? = null) = withContext(Dispatchers.IO) {
val maxCount: Int = (wv.get()?.verticalLoadMaxCount?:20)
Log.d("MyVMH", "Fun: loadImagesIntoLine($item, $maxCount)")
wv.get()?.realCount?.let { count ->
@@ -204,11 +219,11 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
if(notFull) obtainMessage(PREPARE_LAST_PAGE, loadCount + 1, maxCount).sendToTarget()
obtainMessage(DO_LAMBDA, Runnable{
doAfter?.run()
wv.get()?.let { it.updateSeekBar(0) }
wv.get()?.updateSeekBar(0)
}).sendToTarget()
}
}
}.start()
}
private fun loadScrollMode() {
sendEmptyMessage(DIALOG_SHOW)

View File

@@ -21,6 +21,7 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.core.animation.doOnEnd
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
@@ -38,9 +39,12 @@ import kotlinx.android.synthetic.main.widget_infodrawer.*
import kotlinx.android.synthetic.main.widget_titlebar.*
import kotlinx.android.synthetic.main.widget_titlebar.view.*
import kotlinx.android.synthetic.main.widget_viewmangainfo.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.template.general.TitleActivityTemplate
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.copymanga.tools.thread.TimeThread
@@ -128,50 +132,59 @@ class ViewMangaActivity : TitleActivityTemplate() {
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
val settingsPref = MainActivity.mainWeakReference?.get()?.let { PreferenceManager.getDefaultSharedPreferences(it) }
settingsPref?.getBoolean("settings_cat_vm_sw_always_dark_bg", false)?.let {
if (it) {
Log.d("MyVM", "force dark")
delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES
} else {
delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
}
postponeEnterTransition()
setContentView(R.layout.activity_viewmanga)
super.onCreate(null)
lifecycleScope.launch {
withContext(Dispatchers.IO) {
val settingsPref = MainActivity.mainWeakReference?.get()?.let { PreferenceManager.getDefaultSharedPreferences(it) }
settingsPref?.getBoolean("settings_cat_vm_sw_always_dark_bg", false)?.let {
if (it) {
Log.d("MyVM", "force dark")
delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES
} else {
delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
}
va = WeakReference(this@ViewMangaActivity)
//dlZip2View = intent.getStringExtra("callFrom") == "Dl" || p["dlZip2View"] == "true"
//zipFirst = intent.getStringExtra("callFrom") == "zipFirst"
intent.getStringArrayExtra("urlArray")?.let { urlArray = it }
cut = pb["useCut"]
r2l = pb["r2l"]
verticalLoadMaxCount = settingsPref?.getInt("settings_cat_vm_sb_vertical_max", 20)?.let { if(it > 0) it else 20 }?:20
isVertical = pb["vertical"]
notUseVP = pb["noVP"] || isVertical
//url = intent.getStringExtra("url")
withContext(Dispatchers.Main) {
handler = VMHandler(this@ViewMangaActivity, if(urlArray.isNotEmpty()) urlArray[position] else "", resources.getStringArray(R.array.weeks))
withContext(Dispatchers.IO) {
settingsPref?.getInt("settings_cat_vm_sb_quality", 100)?.let { q = if (it > 0) it else 100 }
tt = TimeThread(handler, VMHandler.SET_NET_INFO, 10000)
tt.canDo = true
tt.start()
volTurnPage = settingsPref?.getBoolean("settings_cat_vm_sw_vol_turn", false)?:false
am = getSystemService(Service.AUDIO_SERVICE) as AudioManager
if (!noCellarAlert) noCellarAlert = settingsPref?.getBoolean("settings_cat_net_sw_use_cellar", false) == true
fullyHideInfo = settingsPref?.getBoolean("settings_cat_vm_sw_hide_info", false) == true
va = WeakReference(this)
//dlZip2View = intent.getStringExtra("callFrom") == "Dl" || p["dlZip2View"] == "true"
//zipFirst = intent.getStringExtra("callFrom") == "zipFirst"
intent.getStringArrayExtra("urlArray")?.let { urlArray = it }
cut = pb["useCut"]
r2l = pb["r2l"]
verticalLoadMaxCount = settingsPref?.getInt("settings_cat_vm_sb_vertical_max", 20)?.let { if(it > 0) it else 20 }?:20
isVertical = pb["vertical"]
notUseVP = pb["noVP"] || isVertical
//url = intent.getStringExtra("url")
handler = VMHandler(this, if(urlArray.isNotEmpty()) urlArray[position] else "")
settingsPref?.getInt("settings_cat_vm_sb_quality", 100)?.let { q = if (it > 0) it else 100 }
tt = TimeThread(handler, VMHandler.SET_NET_INFO)
tt.canDo = true
tt.start()
volTurnPage = settingsPref?.getBoolean("settings_cat_vm_sw_vol_turn", false)?:false
am = getSystemService(Service.AUDIO_SERVICE) as AudioManager
if (!noCellarAlert) noCellarAlert = settingsPref?.getBoolean("settings_cat_net_sw_use_cellar", false) == true
fullyHideInfo = settingsPref?.getBoolean("settings_cat_vm_sw_hide_info", false) == true
Log.d("MyVM", "Now ZipFile is $zipFile")
try {
if (zipFile != null && zipFile?.exists() == true) {
if (!handler.loadFromFile(zipFile!!)) prepareImgFromWeb()
} else prepareImgFromWeb()
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError(R.string.load_manga_error)
Log.d("MyVM", "Now ZipFile is $zipFile")
try {
if (zipFile != null && zipFile?.exists() == true) {
if (!handler.loadFromFile(zipFile!!)) prepareImgFromWeb()
} else prepareImgFromWeb()
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError(R.string.load_manga_error)
}
withContext(Dispatchers.Main) {
startPostponedEnterTransition()
ObjectAnimator.ofFloat(vcp, "alpha", 0.1f, 1f).setDuration(1000).start()
}
}
}
}
}
startPostponedEnterTransition()
ObjectAnimator.ofFloat(vcp, "alpha", 0.1f, 1f).setDuration(1000).start()
}
@Suppress("DEPRECATION")
@@ -293,12 +306,12 @@ class ViewMangaActivity : TitleActivityTemplate() {
return op.outWidth.toFloat() / op.outHeight.toFloat() > 1
}
fun countZipEntries(doWhenFinish : (count: Int) -> Unit) = Thread{
suspend fun countZipEntries(doWhenFinish : suspend (count: Int) -> Unit) = withContext(Dispatchers.IO) {
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 ->
if(cut) zip.entries().toList().sortedBy{ it.name.substringBefore('.').toInt()}.forEachIndexed { i, it ->
val useCut = canCut(zip.getInputStream(it))
isCut += useCut
indexMap += i + 1
@@ -306,13 +319,11 @@ class ViewMangaActivity : TitleActivityTemplate() {
Log.d("MyVM", "[$i] 分析: ${it.name}, cut: $useCut")
}
} catch (e: Exception) {
runOnUiThread { toolsBox.toastError(R.string.count_zip_entries_error) }
withContext(Dispatchers.Main) { toolsBox.toastError(R.string.count_zip_entries_error) }
}
runOnUiThread {
Log.d("MyVM", "开始加载控件")
doWhenFinish(count)
}
}.start()
Log.d("MyVM", "开始加载控件")
doWhenFinish(count)
}
private fun getPageNumber(): Int {
return if (r2l && !notUseVP) realCount - vp.currentItem
@@ -336,7 +347,9 @@ class ViewMangaActivity : TitleActivityTemplate() {
loadOneImg()
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError(getString(R.string.load_page_number_error).format(currentItem))
lifecycleScope.launch {
toolsBox.toastError(getString(R.string.load_page_number_error).format(currentItem))
}
}
}
} else {
@@ -382,9 +395,9 @@ class ViewMangaActivity : TitleActivityTemplate() {
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, useCut: Boolean, isLeft: Boolean, isPlaceholder: Boolean = true) {
private suspend fun loadImg(imgView: ScaleImageView, bitmap: Bitmap, useCut: Boolean, isLeft: Boolean, isPlaceholder: Boolean = true) = withContext(Dispatchers.IO) {
val bitmap2load = if(!isPlaceholder && useCut) cutBitmap(bitmap, isLeft) else bitmap
runOnUiThread {
withContext(Dispatchers.Main) {
imgView.setImageBitmap(bitmap2load)
if(!isPlaceholder && isVertical) {
imgView.setHeight2FitImgWidth()
@@ -393,11 +406,11 @@ class ViewMangaActivity : TitleActivityTemplate() {
}
}
private fun loadImgUrlInto(imgView: ScaleImageView, url: String, useCut: Boolean, isLeft: Boolean){
private suspend fun loadImgUrlInto(imgView: ScaleImageView, url: String, useCut: Boolean, isLeft: Boolean){
Log.d("MyVM", "Load from adt: $url")
AutoDownloadThread(CMApi.resolution.wrap(CMApi.proxy?.wrap(url)?:url), 1000) {
it?.let { loadImg(imgView, BitmapFactory.decodeByteArray(it, 0, it.size), useCut, isLeft, false) }
}.start()
PausableDownloader(CMApi.resolution.wrap(CMApi.proxy?.wrap(url)?:url), 1000) {
it.let { loadImg(imgView, BitmapFactory.decodeByteArray(it, 0, it.size), useCut, isLeft, false) }
}.run()
}
private fun getLoadingBitmap(position: Int): Bitmap {
@@ -414,9 +427,9 @@ class ViewMangaActivity : TitleActivityTemplate() {
return loading
}
fun loadImgOn(imgView: ScaleImageView, position: Int) {
suspend fun loadImgOn(imgView: ScaleImageView, position: Int) = withContext(Dispatchers.IO) {
Log.d("MyVM", "Load img: $position")
if (position < 0 || position > realCount) return
if (position < 0 || position > realCount) return@withContext
val index2load = if(cut) abs(indexMap[position]) -1 else position
val useCut = cut && isCut[index2load]
val isLeft = cut && indexMap[position] > 0
@@ -427,58 +440,58 @@ class ViewMangaActivity : TitleActivityTemplate() {
loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true)
val sleepTime = loadImgOnWait.getAndIncrement().toLong()*200
Log.d("MyVM", "loadImgOn sleep: $sleepTime ms")
Thread {
val re = tasks?.get(index2load)
if (sleepTime > 0 && re?.isDone != true) Thread.sleep(sleepTime)
if (re != null) {
if(!re.isDone) re.run()
val data = re.get()
if(data != null && data.isNotEmpty()) {
BitmapFactory.decodeByteArray(data, 0, data.size)?.let {
loadImg(imgView, it, useCut, isLeft, false)
runOnUiThread { Log.d("MyVM", "Load position $position from task") }
}?:runOnUiThread { Log.d("MyVM", "null bitmap at $position") }
}
else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, useCut, isLeft) }
val re = tasks?.get(index2load)
if (sleepTime > 0 && re?.isDone != true) Thread.sleep(sleepTime)
if (re != null) {
if(!re.isDone) re.run()
val data = re.get()
if(data != null && data.isNotEmpty()) {
BitmapFactory.decodeByteArray(data, 0, data.size)?.let {
loadImg(imgView, it, useCut, isLeft, false)
Log.d("MyVM", "Load position $position from task")
}?:Log.d("MyVM", "null bitmap at $position")
}
else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, useCut, isLeft) }
loadImgOnWait.decrementAndGet()
tasks?.apply {
if (index2load >= size) return@apply
val p = if (index2load == size-1) index2load-1 else index2load+1
var delta = 1
var isMinus = false
var pos = p
var maxCount = size
while (pos in indices && get(pos)?.isDone != false && tasksRunStatus?.get(pos) != false && maxCount-- > 0) {
runOnUiThread { Log.d("MyVM", "search $pos") }
pos = p + if (isMinus) -delta else delta
if (pos !in indices) {
isMinus = !isMinus
if (!isMinus) delta++
pos = p + if (isMinus) -delta else delta
if (pos !in indices) return@apply
}
}
else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, useCut, isLeft) }
loadImgOnWait.decrementAndGet()
tasks?.apply {
if (index2load >= size) return@apply
val p = if (index2load == size-1) index2load-1 else index2load+1
var delta = 1
var isMinus = false
var pos = p
var maxCount = size
while (pos in indices && get(pos)?.isDone != false && tasksRunStatus?.get(pos) != false && maxCount-- > 0) {
Log.d("MyVM", "search $pos")
pos = p + if (isMinus) -delta else delta
if (pos !in indices) {
isMinus = !isMinus
if (!isMinus) delta++
pos = p + if (isMinus) -delta else delta
if (pos !in indices) return@apply
}
if (pos !in indices || tasksRunStatus?.get(pos) != false) return@apply
runOnUiThread { Log.d("MyVM", "Preload position $pos from task") }
get(pos)?.apply {
if(!isDone) {
tasksRunStatus?.set(pos, true)
run()
}
isMinus = !isMinus
if (!isMinus) delta++
}
if (pos !in indices || tasksRunStatus?.get(pos) != false) return@apply
Log.d("MyVM", "Preload position $pos from task")
get(pos)?.apply {
if(!isDone) {
tasksRunStatus?.set(pos, true)
run()
}
}
}.start()
}
}
imgView.visibility = View.VISIBLE
}
private fun loadOneImg() {
loadImgOn(onei, currentItem)
updateSeekBar()
lifecycleScope.launch {
loadImgOn(onei, currentItem)
updateSeekBar()
}
}
private fun initImgList(){
@@ -494,7 +507,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
// handler.dl?.hide()
}
private fun getImgBitmap(position: Int): Bitmap? =
private suspend fun getImgBitmap(position: Int): Bitmap? = withContext(Dispatchers.IO) {
if (position >= count || position < 0) null
else {
val zip = ZipFile(zipFile)
@@ -511,7 +524,9 @@ class ViewMangaActivity : TitleActivityTemplate() {
} catch (e: Exception) {
if (i == 1) {
e.printStackTrace()
Toast.makeText(this, "加载zip的第${position}项错误", Toast.LENGTH_SHORT).show()
withContext(Dispatchers.Main) {
Toast.makeText(this@ViewMangaActivity, "加载zip的第${position}项错误", Toast.LENGTH_SHORT).show()
}
}
null
}
@@ -522,6 +537,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
}
bitmap
}
}
private fun setIdPosition(position: Int) {
infoDrawerDelta = position.toFloat()
@@ -548,8 +564,10 @@ class ViewMangaActivity : TitleActivityTemplate() {
}*/
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError(R.string.load_chapter_error)
finish()
lifecycleScope.launch {
toolsBox.toastError(R.string.load_chapter_error)
finish()
}
}
}
@@ -812,9 +830,11 @@ class ViewMangaActivity : TitleActivityTemplate() {
val index2load = if(cut) abs(indexMap[pos]) -1 else pos
val useCut = cut && isCut[index2load]
val isLeft = cut && indexMap[pos] > 0
if (zipFile?.exists() == true) getImgBitmap(index2load)?.let {
//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)
if (zipFile?.exists() == true) lifecycleScope.launch {
getImgBitmap(index2load)?.let {
//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){

View File

@@ -1,20 +1,26 @@
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
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.dmzj.copymanga.R
import java.net.URLEncoder
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 {
CMApi.getLoginConnection(username, pwd, salt)?.apply {
Gson().fromJson(inputStream.reader(), LoginInfoStructure::class.java)?.let { data ->
getLoginConnection(username, pwd, salt).apply {
Gson().fromJson<LoginInfoStructure>(
JsonReader(inputStream.reader()), LoginInfoStructure::class.java
)?.let { data ->
disconnect()
if(data.code == 200) {
pref.edit()?.apply {
@@ -23,7 +29,7 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
putString("username", data.results?.username)
putString("nickname", data.results?.nickname)
apply()
return@withContext refreshAvatar()
return@withContext info()
}
}
return@withContext data
@@ -42,37 +48,37 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
}
fun refreshAvatar() : LoginInfoStructure {
/**
* 获得登录信息并更新头像
* @return 登录态
* - **code**: 449: 未登录, 450: 有 Exception
* - **message**: 可以 toast 的信息
*/
suspend fun info() : LoginInfoStructure = withContext(Dispatchers.IO) {
if (!pref.contains("token")) {
val l = LoginInfoStructure()
l.code = 400
l.code = 449
l.message = getString(R.string.noLogin)
return l
return@withContext l
}
try {
DownloadTools.getHttpContent(getString(R.string.memberInfoApiUrl).format(
CMApi.myHostApiUrl))?.decodeToString()?.let {
val l = Gson().fromJson(it, LoginInfoStructure::class.java)
if(l.code == 200) pref.edit()?.apply {
putString("avatar", l.results.avatar)
apply()
}
return l
return@withContext try {
val l = Gson().fromJson(DownloadTools.getHttpContent(
getString(R.string.memberInfoApiUrl).format(CMApi.myHostApiUrl)).decodeToString(),
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 = 400
l.code = 450
l.message = "${getString(R.string.login_get_avatar_failed)}: ${e.localizedMessage}"
return l
l
}
val l = LoginInfoStructure()
l.code = 400
l.message = getString(R.string.login_get_avatar_failed)
return l
}
fun logout() {
suspend fun logout() = withContext(Dispatchers.IO) {
pref.edit()?.apply {
remove("token")
remove("user_id")
@@ -82,4 +88,19 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
apply()
}
}
private fun getLoginConnection(username: String, pwd: String, salt: Int) =
getString(R.string.loginApiUrl).format(CMApi.myHostApiUrl).let {
DownloadTools.getApiConnection(it, "POST").apply {
pref.apply {
doOutput = true
setRequestProperty("content-type", "application/x-www-form-urlencoded;charset=utf-8")
setRequestProperty("platform", "3")
setRequestProperty("accept", "application/json")
val r = if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "1" else "0"
val pwdEncoded = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
outputStream.write("username=${URLEncoder.encode(username, Charset.defaultCharset().name())}&password=$pwdEncoded&salt=$salt&platform=3&authorization=Token+&version=1.4.4&source=copyApp&region=$r&webp=1".toByteArray())
}
}
}
}

View File

@@ -14,6 +14,8 @@ import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.widget.ImageView
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import top.fumiama.copymanga.ui.vm.PagesManager
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
import top.fumiama.dmzj.copymanga.R
@@ -564,7 +566,11 @@ class ScaleImageView : ImageView {
}
}catch (e:Exception){
e.printStackTrace()
ViewMangaActivity.va?.get()?.toolsBox?.toastError(R.string.show_image_error_try_lower_resolution, false)
ViewMangaActivity.va?.get()?.apply {
lifecycleScope.launch {
toolsBox.toastError(R.string.show_image_error_try_lower_resolution, false)
}
}
}
}
////////////////////////////////有效性判断////////////////////////////////

View File

@@ -19,7 +19,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/book_bg_card_padding">
<include
@@ -37,7 +37,7 @@
android:id="@+id/lbitb"
layout="@layout/line_booktandb"
android:layout_width="0dp"
android:layout_height="@dimen/nav_header_height"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintStart_toEndOf="@+id/lbc"

View File

@@ -2,7 +2,7 @@
<!DOCTYPE resources [
<!ENTITY hosturl "api.copymanga.tv">
]>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">拷贝漫画</string>
<string name="action_settings">设定</string>
@@ -81,7 +81,7 @@
<string name="shelfOperateApiUrl">https://%1$s/api/v3/member/collect/comic</string>
<string name="imgProxyApiUrl">https://copymanga.azurewebsites.net/api/img?code=%1$s&amp;url=%2$s</string>
<string name="imgProxyApiPrefix">https://hi77-overseas.mangafuna.xyz/</string>
<string name="imgProxyApiRegex" tools:ignore="TypographyDashes">^https://[0-9a-z-]+\.mangafuna\.xyz/</string>
<string name="imgProxyKeyID">settings_cat_net_et_img_proxy_code</string>
<!--
<string name="apiProxyApiUrl">https://copymanga.azurewebsites.net/api/api?url=%1$s</string>
@@ -170,7 +170,7 @@
<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_get_avatar_failed">恢复登录失败</string>
<string name="login_restart_to_apply">重启应用以彻底退出登录</string>
<string name="old_download_card_name">前往旧版下载</string>