1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-05 07:20:23 +08:00
新增
1. 自定义拷贝版本号
2. 图片加载失败时显示重新加载按钮
修复
1. APP版本过低 (fix #61)
2. 排行从详情返回后筛选无法使用
优化
1. 默认API改为api.mangacopy.com
2. 弱网络环境下图片加载
This commit is contained in:
源文雨
2024-04-02 01:41:26 +09:00
parent 9347a8d29d
commit 5ea5ad631e
18 changed files with 282 additions and 154 deletions

View File

@@ -8,8 +8,8 @@ android {
applicationId 'top.fumiama.copymanga'
minSdkVersion 23
targetSdkVersion 34
versionCode 55
versionName '2.2.7'
versionCode 56
versionName '2.2.8'
resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -235,6 +235,7 @@ class MainActivity : AppCompatActivity() {
if(avatar != "")
Glide.with(this@MainActivity).load(avatar)
.apply(RequestOptions.bitmapTransform(CircleCrop()))
.timeout(60000)
.into(this@ic)
else setImageResource(R.mipmap.ic_launcher)
} }

View File

@@ -18,7 +18,7 @@ class Book(val path: String, private val getString: (Int) -> String, private val
private val mBookApiUrl = getString(R.string.bookInfoApiUrl).format(CMApi.myHostApiUrl, path).let {
CMApi.apiProxy?.wrap(it)?:it
}
private val mUserAgent = getString(R.string.pc_ua)
private val mUserAgent = getString(R.string.pc_ua).format(DownloadTools.app_ver)
private var mBook: BookInfoStructure? = null
private var mGroupPathWords = arrayOf<String>()
private var mKeys = arrayOf<String>()

View File

@@ -13,8 +13,8 @@ class Shelf(private val token: String, getString: (Int) -> String) {
private val hostUrl: String = getString(R.string.hostUrl)
private val apiUrl: String = getString(R.string.shelfOperateApiUrl).format(hostUrl)
private val queryApiUrlTemplate = getString(R.string.bookUserQueryApiUrl)
private val referer: String = getString(R.string.referer)
private val ua: String = getString(R.string.pc_ua)
private val referer: String = getString(R.string.referer).format(DownloadTools.app_ver)
private val ua: String = getString(R.string.pc_ua).format(DownloadTools.app_ver)
private val addApiUrl get() = "$apiUrl?platform=3".let { CMApi.apiProxy?.wrap(it)?:it }
private val delApiUrl get() = "${apiUrl}s?platform=3".let { CMApi.apiProxy?.wrap(it)?:it }
suspend fun add(comicId: String): String = withContext(Dispatchers.IO) {

View File

@@ -80,7 +80,7 @@ open class AutoDownloadHandler(
try {
val data = DownloadTools.getHttpContent(
CMApi.apiProxy?.wrap(url)?:url, null,
mainWeakReference?.get()?.getString(R.string.pc_ua)!!
DownloadTools.pc_ua
)
if(exit) return@withContext
val fi = data.inputStream()

View File

@@ -18,8 +18,8 @@ class PausableDownloader(private val url: String, private val waitMilliseconds:
try {
val data = (DownloadTools.getHttpContent(
(if(isApi) CMApi.apiProxy?.wrap(url) else null)?:url,
mainWeakReference?.get()?.getString(R.string.referer)!!,
mainWeakReference?.get()?.getString(R.string.pc_ua)!!
DownloadTools.referer,
DownloadTools.pc_ua
))
whenFinish?.let { it(data) }
return@withContext true

View File

@@ -133,7 +133,7 @@ class CardList(
).addListener(GlideHideLottieViewListener(WeakReference(it.laic)) {
if (exitCardList) return@GlideHideLottieViewListener
cardLoadingWaits.decrementAndGet()
})
}).timeout(60000)
if (waitMillis > 0) it.imic.postDelayed({
if (exitCardList) return@postDelayed
g.into(it.imic)

View File

@@ -3,6 +3,7 @@ package top.fumiama.copymanga.tools.api
import androidx.preference.PreferenceManager
import com.bumptech.glide.load.model.LazyHeaders
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
@@ -36,20 +37,11 @@ object CMApi {
PreferenceManager.getDefaultSharedPreferences(it).apply {
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", DownloadTools.referer)
.addHeader("User-Agent", DownloadTools.pc_ua)
.addHeader("source", "copyApp")
.addHeader("webp", "1")
.addHeader(
"version",
MainActivity.mainWeakReference?.get()?.getString(R.string.app_ver)!!
)
.addHeader("version", DownloadTools.app_ver)
.addHeader(
"region",
if (!getBoolean("settings_cat_net", false)) "1" else "0"

View File

@@ -13,6 +13,13 @@ import java.util.concurrent.Callable
import java.util.concurrent.FutureTask
object DownloadTools {
val app_ver = MainActivity.mainWeakReference?.get()?.let { main ->
PreferenceManager.getDefaultSharedPreferences(main)
?.getString("settings_cat_general_et_app_version", main.getString(R.string.app_ver))
?:main.getString(R.string.app_ver)
}!!
val pc_ua = MainActivity.mainWeakReference?.get()!!.getString(R.string.pc_ua).format(app_ver)
val referer = MainActivity.mainWeakReference?.get()!!.getString(R.string.referer).format(app_ver)
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
@@ -30,7 +37,7 @@ object DownloadTools {
setRequestProperty("region", if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "1" else "0")
}
it.getPreferences(Context.MODE_PRIVATE).apply {
setRequestProperty("version", it.getString(R.string.app_ver))
setRequestProperty("version", app_ver)
getString("token", "")?.let { tk ->
setRequestProperty("authorization", "Token $tk")
}

View File

@@ -1,5 +1,6 @@
package top.fumiama.copymanga.ui.book
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -14,8 +15,12 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.card_book.*
@@ -80,17 +85,36 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
private fun setCover() {
if (exit) return
that?.apply {
val load = Glide.with(this).load(
Glide.with(this).load(
if (book?.cover != null)
GlideUrl(CMApi.imageProxy?.wrap(book?.cover!!)?:book?.cover!!, CMApi.myGlideHeaders)
else book?.cachedCover
).addListener(GlideHideLottieViewListener(WeakReference(laic)))
load.into(imic)
context?.let { it1 -> GlideBlurTransformation(it1) }
?.let { it2 -> RequestOptions.bitmapTransform(it2) }
?.let { it3 -> load.apply(it3).into(lbibg) }
//imf?.visibility = View.GONE
//fbl?.addView(divider)
)
.timeout(60000)
.addListener(GlideHideLottieViewListener(WeakReference(laic)))
.addListener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable>?,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
context?.let { it1 -> GlideBlurTransformation(it1) }
?.let { it2 -> RequestOptions.bitmapTransform(it2) }
?.let { it3 -> Glide.with(this@apply).load(resource).apply(it3).into(lbibg) }
return false
}
}).into(imic)
}
}

View File

@@ -4,8 +4,6 @@ import android.os.Bundle
import com.google.android.material.tabs.TabLayout
import kotlinx.android.synthetic.main.fragment_rank.*
import kotlinx.android.synthetic.main.line_rank.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.template.ui.InfoCardLoader
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.UITools
@@ -27,12 +25,14 @@ class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank
override fun onPause() {
super.onPause()
wr = null
ad?.exit = true
}
override fun onResume() {
super.onResume()
ad?.exit = true
wr = WeakReference(this)
ad?.exit = false
}
override fun onDestroy() {
@@ -72,7 +72,7 @@ class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank
}
fun showSexInfo(toolsBox: UITools) {
if (ad?.exit != false) return
if (ad?.exit == true) return
toolsBox.buildInfo("切换类型", "选择一种想筛选的漫画类型",
"男频", "全部", "女频", {
if(!isLoading) {

View File

@@ -205,7 +205,9 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
//Log.d("MyHomeFVP", "Load img: $it")
Glide.with(this@HomeFragment).load(
GlideUrl(CMApi.imageProxy?.wrap(it)?:it, CMApi.myGlideHeaders)
).addListener(GlideHideLottieViewListener(WeakReference(holder.itemView.lai))).into(holder.itemView.vpi)
)
.addListener(GlideHideLottieViewListener(WeakReference(holder.itemView.lai)))
.timeout(60000).into(holder.itemView.vpi)
}
holder.itemView.vpt.text = thisBanner?.brief
holder.itemView.vpc.setOnClickListener {
@@ -244,6 +246,7 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
context?.let {
Glide.with(it).load(R.drawable.img_defmask)
.addListener(GlideHideLottieViewListener(WeakReference(laic)))
.timeout(60000)
.into(imic)
}
cic.isClickable = false
@@ -275,7 +278,7 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
Glide.with(it)
.load(GlideUrl(CMApi.imageProxy?.wrap(cover)?:cover, CMApi.myGlideHeaders))
.addListener(GlideHideLottieViewListener(WeakReference(laic)))
.into(imic)
.timeout(60000).into(imic)
}
lwc.setOnClickListener {
val bundle = Bundle()

View File

@@ -341,7 +341,7 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
val g = Glide.with(it).load(GlideUrl(CMApi.imageProxy?.wrap(img)?:img, CMApi.myGlideHeaders))
.addListener(GlideHideLottieViewListener(WeakReference(cv.laic)) {
cardLoadingWaits.decrementAndGet()
})
}).timeout(60000)
if (waitMillis > 0) cv.imic.postDelayed({
g.into(cv.imic)
}, waitMillis) else cv.imic.post { g.into(cv.imic) }

View File

@@ -70,7 +70,7 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
} else {
showInfCard(); true
}
LOAD_IMG_ON -> {
/*LOAD_IMG_ON -> {
val scaleImageView = msg.obj as ScaleImageView
// msg.arg2: isLast
wv.get()?.apply {
@@ -80,10 +80,9 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
}
//scaleImageView.setHeight2FitImgWidth()
//if(msg.arg2 == 1) sendEmptyMessage(DELAYED_RESTORE_PAGE_NUMBER)
}
}*/
CLEAR_IMG_ON -> {
val img = msg.obj as ScaleImageView
img.visibility = View.GONE
(msg.obj as ScaleImageView).apply { post { visibility = View.GONE } }
//sendEmptyMessage(DECREASE_IMAGE_COUNT_AND_RESTORE_PAGE_NUMBER_AT_ZERO)
}
PREPARE_LAST_PAGE -> wv.get()?.prepareLastPage(msg.arg1, msg.arg2)
@@ -207,7 +206,7 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
vprog?.visibility = View.GONE
}
}
private suspend fun loadImagesIntoLine(item: Int = (wv.get()?.currentItem?:0), doAfter: Runnable? = null) = withContext(Dispatchers.IO) {
private suspend fun loadImagesIntoLine(item: Int = (wv.get()?.currentItem?:0), doAfter: Runnable? = null) {
val maxCount: Int = (wv.get()?.verticalLoadMaxCount?:20)
Log.d("MyVMH", "Fun: loadImagesIntoLine($item, $maxCount)")
wv.get()?.realCount?.let { count ->
@@ -216,8 +215,15 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
val loadCount = (if(notFull) count - item else maxCount) - 1
obtainMessage(INIT_IMAGE_COUNT, loadCount+1, 0).sendToTarget()
Log.d("MyVMH", "count: $count, loadCount: $loadCount, notFull: $notFull")
if(loadCount >= 0) for(i in 0..loadCount) {
obtainMessage(LOAD_IMG_ON,item + i, if(i == loadCount - 1) 1 else 0, wv.get()?.scrollImages?.get(i)).sendToTarget()
if(loadCount >= 0) withContext(Dispatchers.IO) {
for(i in 0..loadCount) {
wv.get()?.apply {
val p = item + i
scrollPositions[i] = p
launch { loadImgOn(scrollImages[i], scrollButtons[i], p, false) }
}
//obtainMessage(LOAD_IMG_ON,item + i, if(i == loadCount - 1) 1 else 0, wv.get()?.scrollImages?.get(i)).sendToTarget()
}
}
//else sendEmptyMessageDelayed(RESTORE_PAGE_NUMBER, 233)
if(notFull) obtainMessage(PREPARE_LAST_PAGE, loadCount + 1, maxCount).sendToTarget()
@@ -267,7 +273,7 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
const val HIDE_INFO_CARD = 1
const val SHOW_INFO_CARD = 2
const val TRIGGER_INFO_CARD = 3
const val LOAD_IMG_ON = 4
//const val LOAD_IMG_ON = 4
const val CLEAR_IMG_ON = 5
const val PREPARE_LAST_PAGE = 6
const val DIALOG_SHOW = 7

View File

@@ -20,6 +20,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowInsetsController
import android.widget.Button
import android.widget.SeekBar
import android.widget.Toast
import androidx.appcompat.app.AppCompatDelegate
@@ -31,8 +32,12 @@ import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
import com.liaoinstan.springview.widget.SpringView
import kotlinx.android.synthetic.main.activity_viewmanga.*
@@ -45,6 +50,7 @@ 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.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
@@ -68,13 +74,15 @@ import kotlin.math.abs
class ViewMangaActivity : TitleActivityTemplate() {
var count = 0
private lateinit var handler: VMHandler
private lateinit var mHandler: VMHandler
lateinit var tt: TimeThread
var clicked = 0
private var isInSeek = false
private var isInScroll = true
//private var progressLog: PropertiesTools? = null
var scrollImages = arrayOf<ScaleImageView>()
var scrollButtons = arrayOf<Button>()
var scrollPositions = arrayOf<Int>()
//var zipFirst = false
//private var useFullScreen = false
var r2l = true
@@ -169,11 +177,11 @@ class ViewMangaActivity : TitleActivityTemplate() {
isVertical = pb["vertical"]
notUseVP = pb["noVP"] || isVertical
//url = intent.getStringExtra("url")
handler = VMHandler(this@ViewMangaActivity, if(urlArray.isNotEmpty()) urlArray[position] else "", resources.getStringArray(R.array.weeks))
mHandler = VMHandler(this@ViewMangaActivity, if(urlArray.isNotEmpty()) urlArray[position] else "", resources.getStringArray(R.array.weeks))
lifecycleScope.launch {
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 = TimeThread(mHandler, VMHandler.SET_NET_INFO, 10000)
tt.canDo = true
tt.start()
volTurnPage = settingsPref?.getBoolean("settings_cat_vm_sw_vol_turn", false)?:false
@@ -184,7 +192,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
Log.d("MyVM", "Now ZipFile is $zipFile")
try {
if (zipFile != null && zipFile?.exists() == true) {
if (!handler.loadFromFile(zipFile!!)) prepareImgFromWeb()
if (!mHandler.loadFromFile(zipFile!!)) prepareImgFromWeb()
} else prepareImgFromWeb()
} catch (e: Exception) {
e.printStackTrace()
@@ -231,7 +239,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
private suspend fun alertCellar() = withContext(Dispatchers.Main) {
toolsBox.buildInfo(
"注意", "要使用使用流量观看吗?", "确定", "本次阅读不再提醒", "取消",
{ handler.startLoad() }, { noCellarAlert = true; handler.startLoad() }, { finish() }
{ mHandler.startLoad() }, { noCellarAlert = true; mHandler.startLoad() }, { finish() }
)
}
@@ -260,10 +268,10 @@ class ViewMangaActivity : TitleActivityTemplate() {
getImgUrlArray()?.apply {
if(cut) {
Log.d("MyVM", "is cut, load all pages...")
handler.sendEmptyMessage(VMHandler.DIALOG_SHOW) // showDl
mHandler.sendEmptyMessage(VMHandler.DIALOG_SHOW) // showDl
isCut = BooleanArray(size)
forEachIndexed { i, it ->
handler.obtainMessage(VMHandler.SET_DL_TEXT, "$i/$size").sendToTarget()
mHandler.obtainMessage(VMHandler.SET_DL_TEXT, "$i/$size").sendToTarget()
if(it != null) try {
DownloadTools.getHttpContent(CMApi.resolution.wrap(CMApi.imageProxy?.wrap(it)?:it), 1024)?.inputStream()?.let {
isCut[i] = canCut(it)
@@ -290,7 +298,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
indexMap += index+1
if(b) indexMap += -(index+1)
}
handler.sendEmptyMessage(15) // hideDl
mHandler.sendEmptyMessage(15) // hideDl
Log.d("MyVM", "load all pages finished")
}
count = size
@@ -301,7 +309,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
@OptIn(ExperimentalStdlibApi::class)
suspend fun initManga() = withContext(Dispatchers.IO) {
val uuid = handler.manga?.results?.chapter?.uuid
val uuid = mHandler.manga?.results?.chapter?.uuid
Log.d("MyVM", "initManga, chapter uuid: $uuid")
if (uuid != null && uuid != "") {
pn = getPreferences(MODE_PRIVATE).getInt(uuid, -4)
@@ -316,7 +324,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
private suspend fun prepareImgFromWeb() {
if(!noCellarAlert && toolsBox.netInfo == getString(R.string.TRANSPORT_CELLULAR)) alertCellar()
else handler.startLoad()
else mHandler.startLoad()
}
private fun canCut(inputStream: InputStream): Boolean{
@@ -353,7 +361,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
else (if (notUseVP) currentItem else vp.currentItem) + 1
}
private fun setPageNumber(num: Int) {
private fun setPageNumber(num: Int) { lifecycleScope.launch {
Log.d("MyVM", "setPageNumber($num)")
if (r2l && !notUseVP) vp.currentItem = realCount - num
else if (notUseVP) {
@@ -362,53 +370,36 @@ class ViewMangaActivity : TitleActivityTemplate() {
val offset = currentItem % verticalLoadMaxCount
Log.d("MyVM", "Current: $currentItem, Height: ${psivl.height}, scrollY: ${psivs.scrollY}")
if (!isInScroll || isInSeek) psivs.scrollY = psivl.height * offset / size
lifecycleScope.launch { updateSeekBar() }
updateSeekBar()
} else {
currentItem = num - 1
try {
loadOneImg()
} catch (e: Exception) {
e.printStackTrace()
lifecycleScope.launch {
withContext(Dispatchers.Main) {
toolsBox.toastError(getString(R.string.load_page_number_error).format(currentItem))
}
}
}
} else {
Log.d("MyVM", "Set vp current: ${num-1}")
//var delta = num - 1 - vp.currentItem
vp.currentItem = num - 1
/*lifecycleScope.launch {
withContext(Dispatchers.IO) {
if(delta >= 1) while (delta-- > 0){
delay(20)
withContext(Dispatchers.Main) {
vp.currentItem++
}
}
else if(delta <= -1) while (delta++ < 0){
delay(20)
withContext(Dispatchers.Main) {
vp.currentItem--
}
}
}
}*/
}
}
} }
/*fun clearImgOn(imgView: ScaleImageView){
imgView.visibility = View.GONE
handler.sendEmptyMessage(VMHandler.DECREASE_IMAGE_COUNT_AND_RESTORE_PAGE_NUMBER_AT_ZERO)
mHandler.sendEmptyMessage(VMHandler.DECREASE_IMAGE_COUNT_AND_RESTORE_PAGE_NUMBER_AT_ZERO)
}*/
//private fun getTempFile(position: Int) = File(cacheDir, "$position")
private fun getImgUrl(position: Int) = handler.manga?.results?.chapter?.let {
private fun getImgUrl(position: Int) = mHandler.manga?.results?.chapter?.let {
it.contents[it.words.indexOf(position)].url
}
private fun getImgUrlArray() = handler.manga?.results?.chapter?.let{
private fun getImgUrlArray() = mHandler.manga?.results?.chapter?.let{
val re = arrayOfNulls<String>(it.contents.size)
for(i in it.contents.indices) {
re[i] = getImgUrl(i)
@@ -420,20 +411,25 @@ class ViewMangaActivity : TitleActivityTemplate() {
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
withContext(Dispatchers.Main) {
imgView.setImageBitmap(bitmap2load)
imgView.apply { post {
setImageBitmap(bitmap2load)
if(!isPlaceholder && isVertical) {
imgView.setHeight2FitImgWidth()
handler.sendEmptyMessage(VMHandler.DECREASE_IMAGE_COUNT_AND_RESTORE_PAGE_NUMBER_AT_ZERO)
setHeight2FitImgWidth()
Log.d("MyVM", "dec remainingImageCount")
mHandler.sendEmptyMessage(VMHandler.DECREASE_IMAGE_COUNT_AND_RESTORE_PAGE_NUMBER_AT_ZERO)
}
}
} }
}
private suspend fun loadImgUrlInto(imgView: ScaleImageView, url: String, useCut: Boolean, isLeft: Boolean){
private suspend fun loadImgUrlInto(imgView: ScaleImageView, button: Button, url: String, useCut: Boolean, isLeft: Boolean, check: (() -> Boolean)? = null): Boolean {
Log.d("MyVM", "Load from adt: $url")
PausableDownloader(CMApi.resolution.wrap(CMApi.imageProxy?.wrap(url)?:url), 1000, false) {
it.let { loadImg(imgView, BitmapFactory.decodeByteArray(it, 0, it.size), useCut, isLeft, false) }
val success = PausableDownloader(CMApi.resolution.wrap(CMApi.imageProxy?.wrap(url)?:url), 1000, false) { data ->
check?.let { it() }?.let { if(it) loadImg(imgView, BitmapFactory.decodeByteArray(data, 0, data.size), useCut, isLeft, false) }
}.run()
if (!success) button.apply { post {
visibility = View.VISIBLE
} }
return success
}
private fun getLoadingBitmap(position: Int): Bitmap {
@@ -450,44 +446,53 @@ class ViewMangaActivity : TitleActivityTemplate() {
return loading
}
suspend fun loadImgOn(imgView: ScaleImageView, position: Int) = withContext(Dispatchers.IO) {
suspend fun loadImgOn(imgView: ScaleImageView, reloadButton: Button, position: Int, isSingle: Boolean = false): Boolean = withContext(Dispatchers.IO) {
Log.d("MyVM", "Load img: $position")
if (position < 0 || position > realCount) return@withContext
if (isSingle && position != currentItem) return@withContext true
if (position < 0 || position > realCount) return@withContext false
val index2load = if(cut) abs(indexMap[position]) -1 else position
val useCut = cut && isCut[index2load]
val isLeft = cut && indexMap[position] > 0
if (zipFile?.exists() == true) getImgBitmap(index2load)?.let {
val success: Boolean = if (zipFile?.exists() == true) getImgBitmap(index2load)?.let {
loadImg(imgView, it, useCut, isLeft, false)
}
true
}?:false
else {
val sleepTime = loadImgOnWait.getAndIncrement().toLong()*200
Log.d("MyVM", "loadImgOn sleep: $sleepTime ms")
val re = tasks?.get(index2load)
if (sleepTime > 0 && re?.isDone != true) {
loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true)
Thread.sleep(sleepTime)
delay(sleepTime)
if (isSingle && position != currentItem) return@withContext true
}
if (re != null) {
val s: Boolean = if (re != null) {
if(!re.isDone) {
loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true)
re.run()
}
val data = re.get()
if (isSingle && position != currentItem) return@withContext true
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")
true
}
else getImgUrl(index2load)?.let {
loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true)
loadImgUrlInto(imgView, it, useCut, isLeft)
}
loadImgUrlInto(imgView, reloadButton, it, useCut, isLeft) {
return@loadImgUrlInto !(isSingle && position != currentItem)
}
}?:false
}
else getImgUrl(index2load)?.let {
loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true)
loadImgUrlInto(imgView, it, useCut, isLeft)
}
loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true)
loadImgUrlInto(imgView, reloadButton, it, useCut, isLeft) {
return@loadImgUrlInto !(isSingle && position != currentItem)
}
}?:false
loadImgOnWait.decrementAndGet()
tasks?.apply {
if (index2load >= size) return@apply
@@ -513,34 +518,61 @@ class ViewMangaActivity : TitleActivityTemplate() {
get(pos)?.apply {
if(!isDone) {
tasksRunStatus?.set(pos, true)
run()
Thread(this).start()
}
}
}
s
}
withContext(Dispatchers.Main) {
if(imgView.visibility != View.VISIBLE) imgView.visibility = View.VISIBLE
}
return@withContext success
}
private fun loadOneImg() {
lifecycleScope.launch {
loadImgOn(onei, currentItem)
updateSeekBar()
}
private suspend fun loadOneImg() {
val img = onei
oneb.apply { post {
if (!hasOnClickListeners()) setOnClickListener {
lifecycleScope.launch {
if (loadImgOn(img, this@apply, currentItem, true)) {
post { visibility = View.GONE }
}
}
}
} }
loadImgOn(onei, oneb, currentItem, true)
updateSeekBar()
}
private fun initImgList(){
private fun initImgList() {
for (i in 0 until verticalLoadMaxCount) {
val newImg = ScaleImageView(this)
scrollImages += newImg
psivl.addView(newImg)
val newOneImage = layoutInflater.inflate(R.layout.page_imgview, psivl, false)
val img = newOneImage.onei
val b = newOneImage.oneb
val p = scrollPositions.size
b.apply { post {
setOnClickListener {
lifecycleScope.launch {
if (loadImgOn(img, this@apply, scrollPositions[p])) {
post { visibility = View.GONE }
}
}
}
} }
scrollImages += img
scrollButtons += b
scrollPositions += -1
psivl.addView(newOneImage)
}
}
fun prepareLastPage(loadCount: Int, maxCount: Int){
for (i in loadCount until maxCount) handler.obtainMessage(VMHandler.CLEAR_IMG_ON, scrollImages[i]).sendToTarget()
// handler.dl?.hide()
for (i in loadCount until maxCount) {
mHandler.obtainMessage(VMHandler.CLEAR_IMG_ON, scrollImages[i]).sendToTarget()
scrollButtons[i].apply { post { visibility = View.GONE } }
}
// mHandler.dl?.hide()
}
private suspend fun getImgBitmap(position: Int): Bitmap? = withContext(Dispatchers.IO) {
@@ -584,7 +616,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
infoDrawerDelta = position.toFloat()
infcard.translationY = infoDrawerDelta
Log.d("MyVM", "Set info drawer delta to $infoDrawerDelta")
handler.sendEmptyMessage(if (fullyHideInfo) 16 else VMHandler.HIDE_INFO_CARD)
mHandler.sendEmptyMessage(if (fullyHideInfo) 16 else VMHandler.HIDE_INFO_CARD)
}
@ExperimentalStdlibApi
@@ -605,15 +637,13 @@ class ViewMangaActivity : TitleActivityTemplate() {
}*/
} catch (e: Exception) {
e.printStackTrace()
lifecycleScope.launch {
toolsBox.toastError(R.string.load_chapter_error)
finish()
}
toolsBox.toastError(R.string.load_chapter_error)
finish()
}
}
private suspend fun setProgress() = withContext(Dispatchers.IO) {
handler.manga?.results?.chapter?.uuid?.let {
mHandler.manga?.results?.chapter?.uuid?.let {
getPreferences(MODE_PRIVATE).edit {
//it["chapterId"] = hm.chapterId.toString()
putInt(it, pageNum)
@@ -623,15 +653,21 @@ class ViewMangaActivity : TitleActivityTemplate() {
}
}
private fun fadeRecreate() {
val oa = ObjectAnimator.ofFloat(vcp, "alpha", 1f, 0.1f).setDuration(1000)
oa.doOnEnd {
onecons?.removeAllViews()
psivl?.removeAllViews()
recreate()
}
oa.start()
}
private fun prepareIdBtCut() {
idtbcut.isChecked = cut
idtbcut.setOnClickListener {
pb["useCut"] = idtbcut.isChecked
val oa = ObjectAnimator.ofFloat(vcp, "alpha", 1f, 0.1f).setDuration(1000)
oa.doOnEnd {
recreate()
}
oa.start()
fadeRecreate()
}
}
@@ -643,11 +679,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
return@setOnClickListener
}
pb["r2l"] = idtblr.isChecked
val oa = ObjectAnimator.ofFloat(vcp, "alpha", 1f, 0.1f).setDuration(1000)
oa.doOnEnd {
recreate()
}
oa.start()
fadeRecreate()
}
}
@@ -659,11 +691,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
return@setOnClickListener
}
pb["noVP"] = idtbvp.isChecked
val oa = ObjectAnimator.ofFloat(vcp, "alpha", 1f, 0.1f).setDuration(1000)
oa.doOnEnd {
recreate()
}
oa.start()
fadeRecreate()
}
}
@@ -701,7 +729,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
oneinfo.alpha = 0F
infseek.visibility = View.GONE
isearch.visibility = View.GONE
inftitle.ttitle.text = "$comicName ${handler.manga?.results?.chapter?.name}"
inftitle.ttitle.text = "$comicName ${mHandler.manga?.results?.chapter?.name}"
inftxtprogress.text = "$pageNum/$realCount"
infseek.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
var p = 0
@@ -734,7 +762,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
val pS = p
Log.d("MyVM", "stop seek at $pS")
if (isVertical && startP/verticalLoadMaxCount != p/verticalLoadMaxCount) {
handler.obtainMessage(
mHandler.obtainMessage(
VMHandler.LOAD_ITEM_SCROLL_MODE,
p / verticalLoadMaxCount * verticalLoadMaxCount,
0,
@@ -755,7 +783,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
})
isearch.setImageResource(R.drawable.ic_author)
isearch.setOnClickListener {
handler.sendEmptyMessage(if (fullyHideInfo) VMHandler.TRIGGER_INFO_CARD_FULL else VMHandler.TRIGGER_INFO_CARD) // trigger info card
mHandler.sendEmptyMessage(if (fullyHideInfo) VMHandler.TRIGGER_INFO_CARD_FULL else VMHandler.TRIGGER_INFO_CARD) // trigger info card
}
}
@@ -783,25 +811,26 @@ class ViewMangaActivity : TitleActivityTemplate() {
vp.visibility = View.GONE
vsp.visibility = View.VISIBLE
initImgList()
handler.sendEmptyMessage(if(isPnValid) VMHandler.LOAD_PAGE_FROM_ITEM else VMHandler.LOAD_SCROLL_MODE)
mHandler.sendEmptyMessage(if(isPnValid) VMHandler.LOAD_PAGE_FROM_ITEM else VMHandler.LOAD_SCROLL_MODE)
psivs.setOnScrollChangeListener { _, _, scrollY, _, _ ->
isInScroll = true
if(!isInSeek) {
val delta = (scrollY.toFloat() * size.toFloat() / psivl.height.toFloat() + 0.5).toInt() - currentItem % verticalLoadMaxCount
if(delta != 0 && !(delta > 0 && pageNum == size)) {
pageNum += delta
Log.d("MyVM", "Scroll to offset $delta")
val fin = pageNum + delta
pageNum = when {
fin <= 0 -> 1
fin%verticalLoadMaxCount == 0 -> fin/verticalLoadMaxCount*verticalLoadMaxCount
else -> fin
}
Log.d("MyVM", "Scroll to offset $delta, page $pageNum")
}
}
}
}
idtbvh.setOnClickListener {
pb["vertical"] = idtbvh.isChecked
val oa = ObjectAnimator.ofFloat(vcp, "alpha", 1f, 0.1f).setDuration(1000)
oa.doOnEnd {
recreate()
}
oa.start()
fadeRecreate()
}
}
@@ -813,7 +842,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
(pageNum-1).let { lifecycleScope.launch { updateSeekBar(it) } }
return
}
handler.obtainMessage(
mHandler.obtainMessage(
VMHandler.LOAD_ITEM_SCROLL_MODE,
currentItem - verticalLoadMaxCount, 0,
Runnable{
@@ -832,7 +861,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
(pageNum+1).let { lifecycleScope.launch { updateSeekBar(it) } }
return
}
handler.sendEmptyMessage(VMHandler.LOAD_SCROLL_MODE)
mHandler.sendEmptyMessage(VMHandler.LOAD_SCROLL_MODE)
}
}
@@ -850,8 +879,8 @@ class ViewMangaActivity : TitleActivityTemplate() {
tt.canDo = false
destroy = true
dlHandler = null
handler.dl.dismiss()
handler.destroy()
mHandler.dl.dismiss()
mHandler.destroy()
super.onDestroy()
}
@@ -875,15 +904,19 @@ class ViewMangaActivity : TitleActivityTemplate() {
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)
holder.itemView.oneb.visibility = View.GONE
}
}
else getImgUrl(index2load)?.let{
if(useCut){
if(useCut) {
val thisOneI = holder.itemView.onei
val thisOneB = holder.itemView.oneb
Glide.with(this@ViewMangaActivity.applicationContext)
.asBitmap()
.load(GlideUrl(CMApi.resolution.wrap(CMApi.imageProxy?.wrap(it)?:it), CMApi.myGlideHeaders))
.placeholder(BitmapDrawable(resources, getLoadingBitmap(pos)))
.timeout(60000)
.addListener(OneButtonRequestListener(thisOneB))
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
thisOneI.setImageBitmap(cutBitmap(resource, isLeft))
@@ -892,7 +925,9 @@ class ViewMangaActivity : TitleActivityTemplate() {
})
} else Glide.with(this@ViewMangaActivity.applicationContext)
.load(GlideUrl(CMApi.resolution.wrap(CMApi.imageProxy?.wrap(it)?:it), CMApi.myGlideHeaders))
.timeout(60000)
.placeholder(BitmapDrawable(resources, getLoadingBitmap(pos)))
.addListener(OneButtonRequestListener(holder.itemView.oneb))
.into(holder.itemView.onei)
}
}
@@ -900,6 +935,38 @@ class ViewMangaActivity : TitleActivityTemplate() {
override fun getItemCount(): Int {
return realCount
}
private inner class OneButtonRequestListener<T>(private val thisOneB: Button) : RequestListener<T> {
var mTarget: Target<T>? = null
init {
thisOneB.apply { post {
setOnClickListener { mTarget?.request?.apply {
clear()
begin()
} }
} }
}
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<T>,
isFirstResource: Boolean
): Boolean {
thisOneB.visibility = View.VISIBLE
mTarget = target
return false
}
override fun onResourceReady(
resource: T & Any,
model: Any,
target: Target<T>?,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
thisOneB.visibility = View.GONE
return false
}
}
}
}
@@ -937,7 +1004,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
isearch.invalidate()
clicked = 0 // false
}, 300)
handler.sendEmptyMessage(if (fullyHideInfo) VMHandler.HIDE_INFO_CARD_FULL else VMHandler.HIDE_INFO_CARD)
mHandler.sendEmptyMessage(if (fullyHideInfo) VMHandler.HIDE_INFO_CARD_FULL else VMHandler.HIDE_INFO_CARD)
}
companion object {

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/onecons"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -9,4 +11,17 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/oneb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_reload"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.64"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
<!ENTITY hosturl "api.copymanga.tv">
<!ENTITY hosturl "api.mangacopy.com">
<!ENTITY appver "2.1.7">
]>
<resources>
<string name="app_name">拷贝漫画</string>
@@ -98,9 +99,9 @@
<string name="TRANSPORT_NULL">无网络</string>
<string name="TRANSPORT_ERROR">网络错误</string>
<string name="pc_ua">COPY/2.0.7</string>
<string name="app_ver">2.0.7</string>
<string name="referer">com.copymanga.app-2.0.7</string>
<string name="pc_ua">COPY/%1$s</string>
<string name="app_ver">&appver;</string>
<string name="referer">com.copymanga.app-%1$s</string>
<string name="menu_update_time">更新时间</string>
<string name="menu_hot">热度</string>
@@ -111,6 +112,7 @@
<string name="button_sub_subscribed">已加书架</string>
<string name="button_start">开始阅读</string>
<string name="button_more">更多</string>
<string name="button_reload">重新加载</string>
<string name="text_format_hit">热度 %1$d</string>
<string name="text_format_stat">状态 %1$s</string>
@@ -135,6 +137,8 @@
<string name="settings_cat_general">通用</string>
<string name="settings_cat_general_sb_title_startup_menu">启动时显示</string>
<string name="settings_cat_general_sb_summary_startup_menu">默认主页</string>
<string name="settings_cat_general_et_title_app_version">拷贝版本号</string>
<string name="settings_cat_general_et_summary_app_version">默认&appver;</string>
<string name="settings_cat_general_sb_card_per_row">每行加载卡片数偏移</string>
<string name="settings_cat_general_sm_card_per_row">默认为0表示无偏移, 在此基础上加减</string>
@@ -170,8 +174,6 @@
<string name="settings_cat_md_sw_show_0m_manga">显示未下载漫画</string>
<string name="settings_cat_md_sm_show_0m_manga">打开后将在我的下载显示所有浏览过详情页的漫画,旧版下载永远全部显示</string>
<string name="login_null_username">用户名为空</string>
<string name="login_null_pwd">密码为空</string>
<string name="login_get_conn_failed">登录失败</string>

View File

@@ -23,6 +23,17 @@
app:showSeekBarValue="true"
app:summary="@string/settings_cat_general_sm_card_per_row"
app:title="@string/settings_cat_general_sb_card_per_row" />
<EditTextPreference
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:defaultValue="@string/app_ver"
android:selectAllOnFocus="false"
android:singleLine="true"
android:title="@string/settings_cat_general_et_title_app_version"
android:summary="@string/settings_cat_general_et_summary_app_version"
app:enableCopying="true"
app:iconSpaceReserved="false"
app:key="settings_cat_general_et_app_version" />
</PreferenceCategory>
<PreferenceCategory
app:iconSpaceReserved="false"