1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-05 07:20:23 +08:00
新增
1. 更改默认 API (fix #131)
2. 按钮颜色随状态改变 (close #123)
修复
1. “我的订阅”中已阅读的话数可能会显示为乱码 (fix #128)
2. 搜索错误显示转码后结果
3. 搜索框输入关键词异常回显 (fix #109)
优化
1. 详情页排版 (fix #124)
升级
1. gson -> 2.13.1
2. lottie -> 6.6.6
This commit is contained in:
源文雨
2025-05-13 17:29:28 +09:00
parent 24eaf48b5f
commit ad11e61eab
9 changed files with 101 additions and 29 deletions

View File

@@ -11,8 +11,8 @@ android {
applicationId 'top.fumiama.copymanga'
minSdkVersion 23
targetSdkVersion 34
versionCode 69
versionName '2.4.2'
versionCode 70
versionName '2.4.3'
resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -114,13 +114,13 @@ dependencies {
implementation 'com.github.bumptech.glide:glide:4.16.0'
//noinspection KaptUsageInsteadOfKsp
kapt 'com.github.bumptech.glide:compiler:4.16.0'
implementation 'com.google.code.gson:gson:2.12.1'
implementation 'com.google.code.gson:gson:2.13.1'
implementation 'com.github.vovaksenov99:OverscrollableScrollView:1.0'
implementation 'com.github.liaoinstan.SpringView:library:0a24d3e9dd'
implementation 'com.github.zawadz88:MaterialPopupMenu:4.1.0'
implementation files('libs/com.lapism/search-2.4.1.aar') // https://stackoverflow.com/a/63029110/28801553
//noinspection GradleDependency
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
implementation 'com.airbnb.android:lottie:6.6.4'
implementation 'com.airbnb.android:lottie:6.6.6'
implementation 'net.java.dev.jna:jna:5.17.0@aar'
}

View File

@@ -29,7 +29,7 @@ object Config {
if (field != null) return field
field = Proxy(
R.string.apiProxyApiUrl,
Regex("^https://(api|www)\\.(copymanga|mangacopy|copy-manga)\\.\\w+/api/"),
Regex("^https://(api|www)\\.(copymanga|mangacopy|copy-manga|copy20)\\.\\w+/api/"),
)
return field
}

View File

@@ -0,0 +1,36 @@
package top.fumiama.copymanga.strings
object Chinese {
/**
* 如果输入字符串中已包含汉字,直接返回。
* 否则尝试以 UTF8 重新解码。
*/
fun fixEncodingIfNeeded(input: String): String {
// 如果本身包含汉字,直接返回
if (containsChinese(input)) {
return input
}
return try {
val decoded = input.toByteArray(Charsets.ISO_8859_1).decodeToString()
// 检测解码后的是否包含汉字
if (containsChinese(decoded)) {
decoded
} else {
input // 解码后也没有汉字,说明可能不是编码问题
}
} catch (e: Exception) {
input
}
}
/**
* 简单检测字符串是否包含常见 CJK中日韩汉字。
*/
fun containsChinese(text: String): Boolean {
val regex = Regex("[\u4E00-\u9FFF]")
return regex.containsMatchIn(text)
}
}

View File

@@ -3,15 +3,18 @@ package top.fumiama.copymanga.ui.book
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context.MODE_PRIVATE
import android.content.res.ColorStateList
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.material.color.MaterialColors
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.card_book.*
import kotlinx.android.synthetic.main.fragment_book.*
import kotlinx.android.synthetic.main.line_bookinfo_text.*
import kotlinx.android.synthetic.main.line_booktandb.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -19,6 +22,7 @@ import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.api.manga.Book
import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.strings.Chinese
import top.fumiama.copymanga.view.template.NoBackRefreshFragment
import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
@@ -98,6 +102,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
activity?.apply {
toolbar.title = book?.name
}
setReadTo()
setStartRead()
}
@@ -118,16 +123,13 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
activity?.apply {
book?.name?.let { name ->
getPreferences(MODE_PRIVATE).getInt(name, -1).let { p ->
this@BookFragment.lbbstart.apply {
var i = 0
if(p >= 0) mBookHandler!!.chapterNames.let {
i = if (p >= it.size) it.size-1 else p
text = it[i]
}
setOnClickListener {
mBookHandler?.apply {
Reader.start2viewManga(name, i, urlArray, uuidArray)
}
var i = 0
if(p >= 0) mBookHandler!!.chapterNames.let {
i = if (p >= it.size) it.size-1 else p
}
this@BookFragment.lbbstart.setOnClickListener {
mBookHandler?.apply {
Reader.start2viewManga(name, i, urlArray, uuidArray)
}
}
}
@@ -135,6 +137,23 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
}
fun setReadTo() {
var chapter = "未读"
if(!mBookHandler?.chapterNames.isNullOrEmpty()) {
activity?.apply {
book?.name?.let { name ->
getPreferences(MODE_PRIVATE).getInt(name, -1).let { p ->
if(p >= 0) mBookHandler!!.chapterNames.let {
chapter = it[if (p >= it.size) it.size-1 else p]
}
}
}
}
}
chapter = "读至 $chapter"
this@BookFragment.bttag.text = chapter
}
private suspend fun prepareHandler() = withContext(Dispatchers.IO) {
arguments?.apply {
if (getBoolean("loadJson")) {
@@ -175,7 +194,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
mBookHandler?.collect = b.results?.collect?:-2
Log.d("MyBF", "get collect of ${book?.path} = ${mBookHandler?.collect}")
tic.text = b.results?.browse?.chapter_name?.let { name ->
getString(R.string.text_format_cloud_read_to).format(name)
getString(R.string.text_format_cloud_read_to).format(Chinese.fixEncodingIfNeeded(name))
}
}
}
@@ -187,7 +206,11 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
queryCollect()
mBookHandler?.collect?.let { collect ->
if (collect > 0) {
this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)
this@BookFragment.lbbsub.apply {
setText(R.string.button_sub_subscribed)
val color = MaterialColors.getColor(this, R.attr.colorButtonNormal)
backgroundTintList = ColorStateList.valueOf(color)
}
}
}
book?.uuid?.let { uuid ->
@@ -199,7 +222,11 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
val re = MainActivity.shelf?.del(collect)
Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
if (re == "请求成功") {
this@BookFragment.lbbsub.setText(R.string.button_sub)
this@BookFragment.lbbsub.apply {
setText(R.string.button_sub)
val color = MaterialColors.getColor(this, R.attr.colorPrimarySurface)
backgroundTintList = ColorStateList.valueOf(color)
}
}
}
return@clickLaunch
@@ -208,7 +235,11 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
if (re == "修改成功") {
queryCollect()
this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)
this@BookFragment.lbbsub.apply {
setText(R.string.button_sub_subscribed)
val color = MaterialColors.getColor(this, R.attr.colorButtonNormal)
backgroundTintList = ColorStateList.valueOf(color)
}
}
}
}

View File

@@ -78,6 +78,7 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
oa.start()
}
complete = true
that?.setReadTo()
that?.setStartRead()
that?.setAddToShelf()
Log.d("MyBH", "Set complete: true")
@@ -126,7 +127,7 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
// tic?.visibility = View.GONE
activity?.toolbar?.title = book?.name
btauth?.text = that?.getString(R.string.text_format_region)?.format(book?.region?:"未知")
bttag?.text = that?.getString(R.string.text_format_img_type)?.format(book?.imageType?:"未知")
// bttag?.text = that?.getString(R.string.text_format_img_type)?.format(book?.imageType?:"未知")
bthit?.text = that?.getString(R.string.text_format_hit)?.format(book?.popular?:-1)
btsub?.text = that?.getString(R.string.text_format_stat)?.format(book?.status?:"未知")
bttime?.text = book?.updateTime?:"未知"

View File

@@ -5,6 +5,8 @@ import android.util.Log
import top.fumiama.copymanga.view.template.InfoCardLoader
import top.fumiama.copymanga.api.Config
import top.fumiama.dmzj.copymanga.R
import java.net.URLEncoder
import java.nio.charset.Charset
@ExperimentalStdlibApi
class SearchFragment : InfoCardLoader(R.layout.fragment_search, R.id.action_nav_search_to_nav_book) {
@@ -16,7 +18,7 @@ class SearchFragment : InfoCardLoader(R.layout.fragment_search, R.id.action_nav_
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (isFirstInflate) {
query = arguments?.getCharSequence("query")?.toString()
query = arguments?.getCharSequence("query")?.toString()?.let { q -> URLEncoder.encode(q, Charset.defaultCharset().name()) }
type = arguments?.getString("type")
Log.d("MySF", "get query=$query, type=$type")
}

View File

@@ -96,13 +96,14 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
var lastChangeTime = 0L
override fun onQueryTextChange(newText: CharSequence): Boolean {
if (newText.contentEquals("__notice_focus_change__") || newText.contentEquals(lastSearch)) return true
lastSearch = newText.toString()
postDelayed({
lifecycleScope.launch {
if (!newText.contentEquals(lastSearch)) return@launch
val diff = System.currentTimeMillis() - lastChangeTime
if(diff > 500) {
if (newText.isNotEmpty()) {
Log.d("MyHF", "new text: $newText")
lastSearch = newText.toString()
adapter.refresh(newText)
}
}
@@ -295,9 +296,10 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
override fun getItemCount() = (results?.results?.list?.size?:0) + if (query?.isNotEmpty() == true) 1 else 0
suspend fun refresh(q: CharSequence) = withContext(Dispatchers.IO) {
query = URLEncoder.encode(q.toString(), Charset.defaultCharset().name())
query = q.toString()
activity?.apply {
PausableDownloader(getString(R.string.searchApiUrl).format(Config.myHostApiUrl.value, 0, query, type)) {
PausableDownloader(getString(R.string.searchApiUrl).format(Config.myHostApiUrl.value, 0,
URLEncoder.encode(q.toString(), Charset.defaultCharset().name()), type)) {
results = Gson().fromJson(it.decodeToString(), BookListStructure::class.java)
count = results?.results?.total?:0
withContext(Dispatchers.Main) {

View File

@@ -16,6 +16,7 @@ import top.fumiama.copymanga.json.HistoryBookListStructure
import top.fumiama.copymanga.json.ShelfStructure
import top.fumiama.copymanga.json.TypeBookListStructure
import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.strings.Chinese
import top.fumiama.copymanga.view.interaction.Navigate
import java.lang.ref.WeakReference
@@ -66,7 +67,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
Log.d("MyICL", "load @ $i")
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?.let { Chinese.fixEncodingIfNeeded(it) }}", book?.comic?.cover,
book?.comic?.path_word, null, null,
book?.comic?.status==1
)
@@ -88,7 +89,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
Log.d("MyICL", "load @ $i")
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?.name?:"null", "\n${book?.last_browse?.last_browse_name?.let { "读到${Chinese.fixEncodingIfNeeded(it)}" }?:"未读"}", book?.comic?.cover,
book?.comic?.path_word, null, null,
book?.comic?.status==1,
book.comic?.browse?.chapter_uuid != book.comic?.last_chapter_id

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
<!ENTITY hosturl "www.copy-manga.com">
<!ENTITY appver "2.2.6">
<!ENTITY hosturl "www.copy20.com">
<!ENTITY appver "2.2.9">
]>
<resources>
<string name="app_name">拷贝漫画</string>
@@ -123,7 +123,6 @@
<string name="text_format_hit">热度 %1$d</string>
<string name="text_format_stat">状态 %1$s</string>
<string name="text_format_region">区域 %1$s</string>
<string name="text_format_img_type">画幅 %1$s</string>
<string name="text_format_cloud_read_to">云读至%1$s</string>
<string name="topics_series">专题系列</string>