1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-21 01:47:21 +08:00
新增
1. 漫画详情页加载进度条
修复
1. 本地记录的章节数超过云端现有章节数时闪退(fix #97)
优化
1. 卡片流加载进度条增加动画
2. 下载页底部下载按钮上移规避导航栏(fix #96)
升级
1. net.java.dev.jna -> 5.15.0
This commit is contained in:
源文雨
2024-10-19 18:05:50 +09:00
parent 99a225dc51
commit 09f2ef2e4a
12 changed files with 131 additions and 45 deletions

View File

@@ -11,8 +11,8 @@ android {
applicationId 'top.fumiama.copymanga' applicationId 'top.fumiama.copymanga'
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 34 targetSdkVersion 34
versionCode 63 versionCode 64
versionName '2.3.5' versionName '2.3.6'
resourceConfigurations += ['zh', 'zh-rCN'] resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -95,5 +95,5 @@ dependencies {
//noinspection GradleDependency //noinspection GradleDependency
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
implementation 'com.airbnb.android:lottie:6.5.2' implementation 'com.airbnb.android:lottie:6.5.2'
implementation 'net.java.dev.jna:jna:5.14.0@aar' implementation 'net.java.dev.jna:jna:5.15.0@aar'
} }

View File

@@ -53,6 +53,7 @@ import kotlinx.coroutines.withContext
import top.fumiama.copymanga.manga.Shelf import top.fumiama.copymanga.manga.Shelf
import top.fumiama.copymanga.tools.ui.UITools import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.copymanga.ui.book.BookFragment.Companion.bookHandler import top.fumiama.copymanga.ui.book.BookFragment.Companion.bookHandler
import top.fumiama.copymanga.ui.book.BookHandler
import top.fumiama.copymanga.ui.cardflow.rank.RankFragment import top.fumiama.copymanga.ui.cardflow.rank.RankFragment
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
import top.fumiama.copymanga.ui.download.DownloadFragment import top.fumiama.copymanga.ui.download.DownloadFragment
@@ -189,7 +190,7 @@ class MainActivity : AppCompatActivity() {
true true
} }
R.id.action_download -> { R.id.action_download -> {
bookHandler.get()?.sendEmptyMessage(6) bookHandler.get()?.sendEmptyMessage(BookHandler.NAVIGATE_TO_DOWNLOAD)
true true
} }
R.id.action_sort -> { R.id.action_sort -> {

View File

@@ -97,14 +97,16 @@ class Book(val path: String, private val getString: (Int) -> String, private val
/** /**
* 更新云端最新章节信息并缓存到本地 * 更新云端最新章节信息并缓存到本地
*/ */
suspend fun updateVolumes(whenFinish: suspend () -> Unit) = withContext(Dispatchers.IO) withIO@ { suspend fun updateVolumes(setProgress: (Int) -> Unit, whenFinish: suspend () -> Unit) = withContext(Dispatchers.IO) withIO@ {
var isDownload = false var isDownload = false
var volumes = if(loadCache && loadVolumes()) mVolumes else emptyArray<VolumeStructure>() var volumes = if(loadCache && loadVolumes()) mVolumes else emptyArray<VolumeStructure>()
if(mGroupPathWords.isEmpty()) return@withIO
if(volumes.isEmpty()) { if(volumes.isEmpty()) {
isDownload = true isDownload = true
val delta = 100/mGroupPathWords.size
mGroupPathWords.forEachIndexed { i, g -> mGroupPathWords.forEachIndexed { i, g ->
Volume(path, g, getString) { Volume(path, g, getString, { return@Volume exit }) { p ->
return@Volume exit setProgress(i*delta+100*p/delta)
}.updateChapters(mCounts[i])?.let { }.updateChapters(mCounts[i])?.let {
volumes += it volumes += it
} }
@@ -116,6 +118,7 @@ class Book(val path: String, private val getString: (Int) -> String, private val
mVolumes = volumes mVolumes = volumes
} }
goSaveHead(isDownload) goSaveHead(isDownload)
setProgress(100)
whenFinish() whenFinish()
} }
} }

View File

@@ -10,7 +10,7 @@ import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
class Volume(private val path: String, private val groupPathWord: String, getString: (Int) -> String, private val isExit: ()->Boolean) { class Volume(private val path: String, private val groupPathWord: String, getString: (Int) -> String, private val isExit: ()->Boolean, private val setProgress: ((Int) -> Unit)? = null) {
private val mGroupInfoApiUrlTemplate = getString(R.string.groupInfoApiUrl) private val mGroupInfoApiUrlTemplate = getString(R.string.groupInfoApiUrl)
private val exit: Boolean private val exit: Boolean
get() { get() {
@@ -21,12 +21,20 @@ class Volume(private val path: String, private val groupPathWord: String, getStr
} }
private var mDownloaders = arrayOf<PausableDownloader>() private var mDownloaders = arrayOf<PausableDownloader>()
private var mVolume: VolumeStructure? = null private var mVolume: VolumeStructure? = null
private var mProgress = 0
set(value) {
setProgress?.let { it(field) }
field = value
}
private var mDelta = 0
suspend fun updateChapters(count: Int): VolumeStructure? = withContext(Dispatchers.IO) { suspend fun updateChapters(count: Int): VolumeStructure? = withContext(Dispatchers.IO) {
val times = count / 100 val times = count / 100
val remain = count % 100 val remain = count % 100
val re = arrayOfNulls<VolumeStructure>(if(remain != 0) (times+1) else (times)) val re = arrayOfNulls<VolumeStructure>(if(remain != 0) (times+1) else (times))
if (re.isEmpty()) return@withContext null if (re.isEmpty()) return@withContext null
Log.d("MyV", "${groupPathWord}卷共需加载${if(times == 0) 1 else times}") Log.d("MyV", "${groupPathWord}卷共需加载${if(times == 0) 1 else times}")
mProgress = 0
mDelta = 100/re.size
download(re, 0, count) download(re, 0, count)
return@withContext mVolume return@withContext mVolume
} }
@@ -40,6 +48,7 @@ class Volume(private val path: String, private val groupPathWord: String, getStr
mDownloaders += ad mDownloaders += ad
ad.run() ad.run()
} }
mProgress += mDelta
} }
private fun whenFinish(re: Array<VolumeStructure?>, c: Int, offset: Int): suspend (ByteArray) -> Unit = lambda@ { result: ByteArray -> private fun whenFinish(re: Array<VolumeStructure?>, c: Int, offset: Int): suspend (ByteArray) -> Unit = lambda@ { result: ByteArray ->
try { try {

View File

@@ -1,5 +1,6 @@
package top.fumiama.copymanga.template.general package top.fumiama.copymanga.template.general
import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.net.ConnectivityManager import android.net.ConnectivityManager
@@ -8,6 +9,7 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import androidx.core.animation.doOnEnd
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@@ -157,18 +159,31 @@ open class MangaPagesFragmentTemplate(inflateRes:Int, private val isLazy: Boolea
var newP = p var newP = p
mypl?.post { mypl?.post {
if (p == mypl?.progress) return@post if (p == mypl?.progress) return@post
if (newP >= 100) { if (newP >= 100) newP = 100
Log.d("MyMPFT", "set 100, hide")
mypc?.visibility = View.GONE
return@post
}
else if (newP < 0) newP = 0 else if (newP < 0) newP = 0
if (mypl?.progress == 0) {
Log.d("MyMPFT", "set from 0, show")
mypc?.apply {
visibility = View.VISIBLE
invalidate()
ObjectAnimator.ofFloat(this, "alpha", 0f, 1f)
.setDuration(300)
.start()
}
}
if(newP == 100) {
Log.d("MyMPFT", "set to 100, hide")
mypc?.apply {
val oa = ObjectAnimator.ofFloat(this, "alpha", 1f, 0f).setDuration(300)
oa.doOnEnd { visibility = View.GONE }
oa.start()
}
}
mypl?.apply { mypl?.apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { val oa = ObjectAnimator.ofInt(this, "progress", newP).setDuration(100)
setProgress(newP, true) oa.addUpdateListener { invalidate() }
} else progress = newP oa.start()
invalidate() Log.d("MyMPFT", "set $progress")
Log.d("MyMPFT", "set ${mypl?.progress}")
} }
} }
} }

View File

@@ -49,7 +49,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
book?.comic?.path_word, null, null, book?.comic?.path_word, null, null,
isFinish = false, isNew = false isFinish = false, isNew = false
) )
setProgress(20+80*i/size) setProgress(20+80*(i+1)/size)
} }
offset += size offset += size
} }
@@ -71,7 +71,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
book?.comic?.path_word, null, null, book?.comic?.path_word, null, null,
book?.comic?.status==1 book?.comic?.status==1
) )
setProgress(20+80*i/size) setProgress(20+80*(i+1)/size)
} }
offset += size offset += size
} }
@@ -94,7 +94,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
book?.comic?.status==1, book?.comic?.status==1,
book.comic?.browse?.chapter_uuid != book.comic?.last_chapter_id book.comic?.browse?.chapter_uuid != book.comic?.last_chapter_id
) )
setProgress(20+80*i/size) setProgress(20+80*(i+1)/size)
} }
offset += size offset += size
} }
@@ -112,7 +112,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
Log.d("MyICL", "load @ $i") Log.d("MyICL", "load @ $i")
if(ad?.exit == true) return@PausableDownloader if(ad?.exit == true) return@PausableDownloader
cardList?.addCard(book?.name?:"null", null, book?.cover, book?.path_word, null, null, false) cardList?.addCard(book?.name?:"null", null, book?.cover, book?.path_word, null, null, false)
setProgress(20+80*i/size) setProgress(20+80*(i+1)/size)
} }
offset += size offset += size
} }

View File

@@ -1,11 +1,14 @@
package top.fumiama.copymanga.ui.book package top.fumiama.copymanga.ui.book
import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context.MODE_PRIVATE import android.content.Context.MODE_PRIVATE
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.core.animation.addListener
import androidx.core.animation.doOnEnd
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.app_bar_main.* import kotlinx.android.synthetic.main.app_bar_main.*
@@ -42,6 +45,12 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
lifecycleScope.launch { lifecycleScope.launch {
prepareHandler() prepareHandler()
try { try {
fbloading?.apply {
post {
progress = 0
invalidate()
}
}
book?.updateInfo() book?.updateInfo()
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@@ -52,12 +61,25 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
} }
Log.d("MyBF", "read path: ${book?.path}") Log.d("MyBF", "read path: ${book?.path}")
for (i in 1..3) { for (i in 1..3) {
mBookHandler?.sendEmptyMessageDelayed(i, (i*100).toLong()) mBookHandler?.sendEmptyMessage(i)
} }
try { try {
book?.updateVolumes { fbc?.apply {
delay(300) alpha = 0f
mBookHandler?.sendEmptyMessage(10) visibility = View.VISIBLE
invalidate()
ObjectAnimator.ofFloat(this, "alpha", 0f, 1f)
.setDuration(300)
.start()
}
book?.updateVolumes({ fbloading?.apply { post {
val oa = ObjectAnimator.ofInt(this, "progress", 20 + it*8/10)
.setDuration(128)
oa.addUpdateListener { invalidate() }
oa.start()
Log.d("MyBF", "set progress $it")
} } }) {
mBookHandler?.sendEmptyMessage(BookHandler.SET_VOLUMES)
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@@ -95,14 +117,15 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
} }
fun setStartRead() { fun setStartRead() {
if(mBookHandler?.chapterNames?.isNotEmpty() == true) activity?.apply { if(mBookHandler?.chapterNames?.isNotEmpty() != true) return
activity?.apply {
book?.name?.let { name -> book?.name?.let { name ->
getPreferences(MODE_PRIVATE).getInt(name, -1).let { p -> getPreferences(MODE_PRIVATE).getInt(name, -1).let { p ->
this@BookFragment.lbbstart.apply { this@BookFragment.lbbstart.apply {
var i = 0 var i = 0
if(p >= 0) { if(p >= 0) mBookHandler!!.chapterNames.let {
text = mBookHandler!!.chapterNames[p] i = if (p >= it.size) it.size-1 else p
i = p text = it[i]
} }
setOnClickListener { setOnClickListener {
mBookHandler?.apply { mBookHandler?.apply {

View File

@@ -1,5 +1,6 @@
package top.fumiama.copymanga.ui.book package top.fumiama.copymanga.ui.book
import android.animation.ObjectAnimator
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
@@ -10,6 +11,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.animation.doOnEnd
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@@ -70,9 +72,10 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
private fun endSetLayouts() { private fun endSetLayouts() {
if (exit) return if (exit) return
that?.fbloading?.apply { that?.fbc?.apply {
pauseAnimation() val oa = ObjectAnimator.ofFloat(this, "alpha", 1f, 0f).setDuration(300)
visibility = View.GONE oa.doOnEnd { visibility = View.GONE }
oa.start()
} }
complete = true complete = true
that?.setStartRead() that?.setStartRead()
@@ -291,7 +294,7 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
} }
} }
} }
sendEmptyMessage(9) // end set layout sendEmptyMessage(END_SET_LAYOUTS)
} }
private fun loadVolume(name: String, path: String, nav: Int){ private fun loadVolume(name: String, path: String, nav: Int){
@@ -331,4 +334,10 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
override fun getItemCount(): Int = that?.book?.keys?.size?:0 override fun getItemCount(): Int = that?.book?.keys?.size?:0
} }
} }
companion object {
const val NAVIGATE_TO_DOWNLOAD = 6
const val END_SET_LAYOUTS = 9
const val SET_VOLUMES = 10
}
} }

View File

@@ -8,6 +8,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.gson.Gson import com.google.gson.Gson
import kotlinx.android.synthetic.main.fragment_dlcomic.* import kotlinx.android.synthetic.main.fragment_dlcomic.*
import kotlinx.android.synthetic.main.widget_downloadbar.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -24,6 +25,7 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
exit = false exit = false
ldwn?.setPadding(0, 0, 0, navBarHeight) ldwn?.setPadding(0, 0, 0, navBarHeight)
dlsdwn?.translationY = -navBarHeight.toFloat()
if(isFirstInflate) lifecycleScope.launch { if(isFirstInflate) lifecycleScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
when { when {

View File

@@ -199,8 +199,11 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
dl?.setContentView(R.layout.dialog_unzipping) dl?.setContentView(R.layout.dialog_unzipping)
that?.dlsdwn?.viewTreeObserver?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener{ that?.dlsdwn?.viewTreeObserver?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener{
override fun onGlobalLayout() { override fun onGlobalLayout() {
cdwnWidth = that!!.dlsdwn.width that!!.apply {
Log.d("MyDl", "Get dlsdwn height: $cdwnWidth") cdwnWidth = dlsdwn.width
Log.d("MyDl", "Get dlsdwn width: $cdwnWidth")
ldwn?.setPadding(0, 0, 0, dlsdwn.height+navBarHeight)
}
that!!.dlsdwn.viewTreeObserver.removeOnGlobalLayoutListener(this) that!!.dlsdwn.viewTreeObserver.removeOnGlobalLayoutListener(this)
} }
}) })

View File

@@ -44,12 +44,29 @@
app:layout_constraintTop_toBottomOf="@+id/fbiinf" app:layout_constraintTop_toBottomOf="@+id/fbiinf"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/> app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
<com.airbnb.lottie.LottieAnimationView <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/fbloading" android:id="@+id/fbc"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:alpha="0.5" android:alpha="0">
app:lottie_autoPlay="true"
app:lottie_loop="true" <androidx.cardview.widget.CardView
app:lottie_rawRes="@raw/lottie_loading" /> android:layout_width="@dimen/book_card_width"
android:layout_height="@dimen/icon_size_middle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ProgressBar
android:id="@+id/fbloading"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="16dp" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@@ -34,17 +35,20 @@
android:id="@+id/mypc" android:id="@+id/mypc"
android:layout_width="@dimen/book_card_width" android:layout_width="@dimen/book_card_width"
android:layout_height="@dimen/icon_size_middle" android:layout_height="@dimen/icon_size_middle"
android:alpha="0"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<ProgressBar <ProgressBar
android:id="@+id/mypl" android:id="@+id/mypl"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:padding="16dp" android:padding="16dp" />
style="?android:attr/progressBarStyleHorizontal"/>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>