1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-07 00:10:22 +08:00
优化
1. 错误打印
This commit is contained in:
源文雨
2025-06-06 23:04:11 +08:00
parent 159e2ad976
commit cd68a5056d
10 changed files with 136 additions and 108 deletions

View File

@@ -12,8 +12,8 @@ android {
minSdkVersion 23
//noinspection OldTargetApi
targetSdkVersion 34
versionCode 77
versionName '2.5.4'
versionCode 78
versionName '2.5.5'
resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -26,7 +26,8 @@ class LoginActivity : AppCompatActivity() {
alblogin.setOnClickListener {
lifecycleScope.launch {
val salt = Random.nextInt(10000)
val username = altusrnm.text?.toString() ?: run {
val username = altusrnm.text?.toString()
if (username.isNullOrEmpty()) {
Toast.makeText(
this@LoginActivity,
R.string.login_null_username,
@@ -34,7 +35,8 @@ class LoginActivity : AppCompatActivity() {
).show()
return@launch
}
val pwd = altpwd.text?.toString() ?: run {
val pwd = altpwd.text?.toString()
if (pwd.isNullOrEmpty()) {
Toast.makeText(this@LoginActivity, R.string.login_null_pwd, Toast.LENGTH_SHORT)
.show()
return@launch
@@ -50,14 +52,23 @@ class LoginActivity : AppCompatActivity() {
finish()
return@launch
}
val l = MainActivity.member?.login(username, pwd, salt)
Log.d("MyLA", "login return code: ${l?.code}")
if (l?.code == 200) {
MainActivity.mainWeakReference?.get()?.refreshUserInfo()
finish()
return@launch
try {
val l = MainActivity.member?.login(username, pwd, salt)
Log.d("MyLA", "login return code: ${l?.code}")
if (l?.code == 200) {
MainActivity.mainWeakReference?.get()?.refreshUserInfo()
finish()
return@launch
}
Toast.makeText(this@LoginActivity, "错误码${l?.code}: ${l?.message}", Toast.LENGTH_LONG).show()
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(
this@LoginActivity,
"${e::class.simpleName} ${e.message}",
Toast.LENGTH_SHORT
).show()
}
Toast.makeText(this@LoginActivity, l?.message, Toast.LENGTH_LONG).show()
}
}
if (Config.general_enable_transparent_system_bar.value) {

View File

@@ -84,7 +84,9 @@ class Book(val path: String, private val getString: (Int) -> String, private val
} catch (e: Exception) {
e.printStackTrace()
MainActivity.mainWeakReference?.get()?.apply {
runOnUiThread { Toast.makeText(this, e.message, Toast.LENGTH_SHORT).show() }
withContext(Dispatchers.Main) {
runOnUiThread { Toast.makeText(this@apply, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show() }
}
}
}
}

View File

@@ -15,7 +15,7 @@ class Shelf(private val getString: (Int) -> String) {
private val delApiUrl get() = "${apiUrl}s?platform=${Config.platform.value}"
suspend fun add(comicId: String): String = withContext(Dispatchers.IO) {
if (comicId.isEmpty()) {
return@withContext "空漫画ID"
throw IllegalArgumentException("空漫画ID")
}
val body = buildString {
append("comic_id=")
@@ -24,19 +24,15 @@ class Shelf(private val getString: (Int) -> String) {
append("")
append(Config.token.value)
}
return@withContext try {
val re = Config.myHostApiUrl.request(
addApiUrl, body.encodeToByteArray(), "POST",
"application/x-www-form-urlencoded;charset=utf-8")
Gson().fromJson(re, ReturnBase::class.java).message
} catch (e: Exception) {
e.message?:e::class.simpleName?:e.toString()
}
val re = Config.myHostApiUrl.request(
addApiUrl, body.encodeToByteArray(), "POST",
"application/x-www-form-urlencoded;charset=utf-8")
Gson().fromJson(re, ReturnBase::class.java).message
}
suspend fun del(vararg bookIds: Int): String = withContext(Dispatchers.IO) {
if (bookIds.isEmpty()) {
return@withContext "空ID列表"
throw IllegalArgumentException("空ID列表")
}
val body = buildString {
bookIds.forEach {
@@ -47,14 +43,10 @@ class Shelf(private val getString: (Int) -> String) {
append("authorization=Token+")
append(Config.token.value)
}
return@withContext try {
val re = Config.myHostApiUrl.request(
delApiUrl, body.encodeToByteArray(),
"DELETE", "application/x-www-form-urlencoded;charset=utf-8")
Gson().fromJson(re, ReturnBase::class.java).message
} catch (e: Exception) {
e.message?:e::class.simpleName?:e.toString()
}
val re = Config.myHostApiUrl.request(
delApiUrl, body.encodeToByteArray(),
"DELETE", "application/x-www-form-urlencoded;charset=utf-8")
Gson().fromJson(re, ReturnBase::class.java).message
}
suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {

View File

@@ -3,6 +3,10 @@ package top.fumiama.copymanga.api.network
import android.util.Log
import android.widget.Toast
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.api.Config.apiProxy
import top.fumiama.copymanga.api.Config.networkApiUrl
@@ -13,13 +17,10 @@ import top.fumiama.copymanga.json.NetworkStructure
import top.fumiama.copymanga.json.ReturnBase
import top.fumiama.copymanga.net.DownloadTools
import top.fumiama.dmzj.copymanga.R
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
class Api {
private var mHostApiUrls = mutableListOf<String>()
private var mu = ReentrantReadWriteLock()
private var mu = Mutex()
fun getApis(): Array<String> {
return mHostApiUrls.toTypedArray()
@@ -28,12 +29,12 @@ class Api {
suspend fun init() {
if (mHostApiUrls.isNotEmpty()) return
if (reverseProxyUrl.value.isNotEmpty() && reverseProxyUrl.value != proxyUrl) {
mu.write { mHostApiUrls = mutableListOf(reverseProxyUrl.value) }
mu.withLock { mHostApiUrls = mutableListOf(reverseProxyUrl.value) }
Log.d("MyApi", "myHostApiUrl set reverse proxy to ${reverseProxyUrl.value}")
return
}
MainActivity.mainWeakReference?.get()?.apply {
mu.write {
mu.withLock {
if (mHostApiUrls.isNotEmpty()) return
try {
val d = get(getString(R.string.networkApiUrl).format(platform.value), networkApiUrl.value)
@@ -48,15 +49,19 @@ class Api {
} catch (e: Exception) {
e.printStackTrace()
mHostApiUrls = mutableListOf(networkApiUrl.value)
runOnUiThread {
Toast.makeText(this, e.message, Toast.LENGTH_SHORT).show()
withContext(Dispatchers.Main) {
runOnUiThread {
Toast.makeText(this@apply, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
if (mHostApiUrls.isEmpty()) {
mHostApiUrls = mutableListOf(networkApiUrl.value)
Log.d("MyApi", "myHostApiUrl set default ${mHostApiUrls[0]}")
runOnUiThread {
Toast.makeText(this, "无法获取API列表", Toast.LENGTH_SHORT).show()
withContext(Dispatchers.Main) {
runOnUiThread {
Toast.makeText(this@apply, "无法获取API列表", Toast.LENGTH_SHORT).show()
}
}
}
}
@@ -65,7 +70,7 @@ class Api {
}
// get throw error on non-json or non-200 or empty apis, path: /api/v3/xxx, return json string
suspend fun get(path: String, forceApi: String? = null): String {
val apis = if (forceApi == null) mu.read { mHostApiUrls.toTypedArray() } else arrayOf(forceApi)
val apis = if (forceApi == null) mu.withLock { mHostApiUrls.toTypedArray() } else arrayOf(forceApi)
if (apis.isEmpty()) {
throw NoSuchElementException("API列表为空")
}
@@ -78,13 +83,22 @@ class Api {
}?: DownloadTools.getApiContent(u)).decodeToString()
r = Gson().fromJson(ret, ReturnBase::class.java)
if (r!!.code != 200) {
mu.write { mHostApiUrls.remove(api) }
withContext(Dispatchers.Main) {
MainActivity.mainWeakReference?.get()?.apply {
runOnUiThread {
Toast.makeText(this, "错误码${r?.code?:-1}, 信息: ${r?.message?:"空"}", Toast.LENGTH_SHORT).show()
}
}
}
} else {
return ret
}
} catch (e: Exception) {
mu.write { mHostApiUrls.remove(api) }
if (i >= apis.size-1) { // throw lase exception
mu.withLock {
if (mHostApiUrls.size <= 1) return@withLock
mHostApiUrls.remove(api)
}
if (i >= apis.size-1) { // throw last exception
throw e
}
}
@@ -93,12 +107,12 @@ class Api {
}
// request throw error on non-json or non-200 or empty apis, path: /api/v3/xxx, return json string
suspend fun request(path: String, body: ByteArray, method: String, contentType: String, forceApi: String? = null): String {
val apis = if (forceApi == null) mu.read { mHostApiUrls } else mutableListOf(forceApi)
val apis = if (forceApi == null) mu.withLock { mHostApiUrls } else mutableListOf(forceApi)
if (apis.isEmpty()) {
throw NoSuchElementException("API列表为空")
}
var r: ReturnBase? = null
apis.forEach { api ->
apis.forEachIndexed { i, api ->
val u = "https://$api$path"
try {
val ret = (apiProxy?.comancry(u) {
@@ -106,21 +120,27 @@ class Api {
}?: DownloadTools.requestApiWithBody(u, method, body, contentType)).decodeToString()
r = Gson().fromJson(ret, ReturnBase::class.java)
if (r!!.code != 200) {
mu.write {
if (mHostApiUrls.size <= 1) return@write
mHostApiUrls.remove(api)
withContext(Dispatchers.Main) {
MainActivity.mainWeakReference?.get()?.apply {
runOnUiThread {
Toast.makeText(this, "错误码${r?.code?:-1}, 信息: ${r?.message?:"空"}", Toast.LENGTH_SHORT).show()
}
}
}
} else {
return ret
}
} catch (e: Exception) {
e.printStackTrace()
mu.write {
if (mHostApiUrls.size <= 1) return@write
mu.withLock {
if (mHostApiUrls.size <= 1) return@withLock
mHostApiUrls.remove(api)
}
if (i >= apis.size-1) { // throw last exception
throw e
}
}
}
throw IllegalStateException("错误码${r!!.code}, 信息: ${r!!.message}")
throw IllegalStateException("错误码${r?.code?:-1}, 信息: ${r?.message?:"空"}")
}
}

View File

@@ -70,7 +70,7 @@ object Update {
Toast.makeText(this@apply, "下载成功", Toast.LENGTH_SHORT).show()
info.dismiss()
install(it, this@apply)
} else runOnUiThread {
} else {
Toast.makeText(this@apply, "文件损坏", Toast.LENGTH_SHORT).show()
info.dismiss()
}

View File

@@ -2,6 +2,7 @@ package top.fumiama.copymanga.api.user
import android.util.Base64
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.api.Config
@@ -14,14 +15,7 @@ class Member(private val getString: (Int) -> String) {
val hasLogin: Boolean get() = Config.token.value?.isNotEmpty() ?: false
suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure =
withContext(Dispatchers.IO) {
return@withContext try {
saveInfo(postLogin(username, pwd, salt))
} catch (e: Exception) {
val l = LoginInfoStructure()
l.code = 400
l.message = e.message.toString()
l
}
return@withContext saveInfo(postLogin(username, pwd, salt))
}
/**
@@ -32,30 +26,13 @@ class Member(private val getString: (Int) -> String) {
*/
suspend fun info(): LoginInfoStructure = withContext(Dispatchers.IO) {
if (!hasLogin) {
val l = LoginInfoStructure()
l.code = 449
l.message = getString(R.string.noLogin)
return@withContext l
}
try {
val u = getString(R.string.memberInfoApiUrl)
.format(Config.platform.value)
try {
val l = Gson().fromJson(Config.myHostApiUrl.get(u), LoginInfoStructure::class.java)
if (l.code == 200) Config.avatar.value = l.results.avatar
l
} catch (e: Exception) {
val l = LoginInfoStructure()
l.code = 450
l.message = "${getString(R.string.login_get_avatar_failed)}: ${e.message}"
l
}
} catch (e: Exception) {
val l = LoginInfoStructure()
l.code = 450
l.message = "${getString(R.string.login_get_avatar_failed)}: ${e.message}"
l
throw IllegalArgumentException(getString(R.string.noLogin))
}
val u = getString(R.string.memberInfoApiUrl)
.format(Config.platform.value)
val l = Gson().fromJson(Config.myHostApiUrl.get(u), LoginInfoStructure::class.java)
if (l.code == 200) Config.avatar.value = l.results.avatar
l
}
suspend fun logout() = withContext(Dispatchers.IO) {
@@ -78,8 +55,8 @@ class Member(private val getString: (Int) -> String) {
}
return@use l
} ?: throw Exception(getString(R.string.login_parse_json_error))
} catch (e: Exception) {
throw Exception(data.decodeToString(), e)
} catch (e: JsonSyntaxException) {
throw JsonSyntaxException(data.decodeToString(), e)
}
}

View File

@@ -207,7 +207,7 @@ object DownloadTools {
Log.d("MyDT", "getHttpContent: ${ret.size} bytes")
if (!u.startsWith("https://$proxyUrl") &&
conn.getHeaderField("Content-type") != "application/json") {
throw IllegalStateException("请求错误: ${ret.decodeToString()}")
throw IllegalStateException("非JSON返回: ${ret.decodeToString()}")
}
decodeBody(ret, conn.getHeaderField("Content-Encoding")?:"")
}

View File

@@ -20,6 +20,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.api.manga.Book
import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.strings.Chinese
@@ -194,8 +195,10 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
}
} catch (e: Exception) {
activity?.runOnUiThread {
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
withContext(Dispatchers.Main) {
activity?.runOnUiThread {
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
}
@@ -217,29 +220,45 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
book?.uuid?.let { uuid ->
this@BookFragment.lbbsub?.setOnClickListener {
lifecycleScope.launch clickLaunch@ {
if (Config.token.value.isNullOrEmpty()) {
Toast.makeText(context, R.string.noLogin, Toast.LENGTH_SHORT).show()
return@clickLaunch
}
if (this@BookFragment.lbbsub.text != getString(R.string.button_sub)) {
mBookHandler?.collect?.let { collect ->
if (collect < 0) return@clickLaunch
val re = MainActivity.shelf?.del(collect)
Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
if (re == "请求成功") {
this@BookFragment.lbbsub.apply {
setText(R.string.button_sub)
val color = MaterialColors.getColor(this, R.attr.colorPrimarySurface)
backgroundTintList = ColorStateList.valueOf(color)
try {
val re = MainActivity.shelf?.del(collect)
Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
if (re == "请求成功") {
this@BookFragment.lbbsub.apply {
setText(R.string.button_sub)
val color = MaterialColors.getColor(this, R.attr.colorPrimarySurface)
backgroundTintList = ColorStateList.valueOf(color)
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
return@clickLaunch
}
val re = MainActivity.shelf?.add(uuid)
Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
if (re == "修改成功") {
queryCollect()
this@BookFragment.lbbsub.apply {
setText(R.string.button_sub_subscribed)
val color = MaterialColors.getColor(this, R.attr.colorButtonNormal)
backgroundTintList = ColorStateList.valueOf(color)
try {
val re = MainActivity.shelf?.add(uuid)
Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
if (re == "修改成功") {
queryCollect()
this@BookFragment.lbbsub.apply {
setText(R.string.button_sub_subscribed)
val color = MaterialColors.getColor(this, R.attr.colorButtonNormal)
backgroundTintList = ColorStateList.valueOf(color)
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}

View File

@@ -52,10 +52,17 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
if(netInfo != tb.transportStringNull && netInfo != tb.transportStringError)
MainActivity.member?.apply { lifecycleScope.launch {
Config.myHostApiUrl.init()
info().let { l ->
if (l.code != 200 && l.code != 449) {
Toast.makeText(context, l.message, Toast.LENGTH_SHORT).show()
logout()
try {
info().let { l ->
if (l.code != 200 && l.code != 449) {
Toast.makeText(context, l.message, Toast.LENGTH_SHORT).show()
logout()
}
}
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show()
}
}
} }