mirror of
https://github.com/fumiama/copymanga.git
synced 2026-07-01 00:00:25 +08:00
v2.5.6
新增 1. 导入/导出设置 优化 1. 错误显示
This commit is contained in:
@@ -12,8 +12,8 @@ android {
|
|||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
//noinspection OldTargetApi
|
//noinspection OldTargetApi
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode 78
|
versionCode 79
|
||||||
versionName '2.5.5'
|
versionName '2.5.6'
|
||||||
resourceConfigurations += ['zh', 'zh-rCN']
|
resourceConfigurations += ['zh', 'zh-rCN']
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
).show()
|
).show()
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val pwd = altpwd.text?.toString()
|
val pwd = altpwd.text?.toString()?:""
|
||||||
if (pwd.isNullOrEmpty()) {
|
if (!isLogout && pwd.isEmpty()) {
|
||||||
Toast.makeText(this@LoginActivity, R.string.login_null_pwd, Toast.LENGTH_SHORT)
|
Toast.makeText(this@LoginActivity, R.string.login_null_pwd, Toast.LENGTH_SHORT)
|
||||||
.show()
|
.show()
|
||||||
return@launch
|
return@launch
|
||||||
@@ -66,7 +66,7 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
this@LoginActivity,
|
this@LoginActivity,
|
||||||
"${e::class.simpleName} ${e.message}",
|
"${e::class.simpleName} ${e.message}",
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ import top.fumiama.copymanga.api.update.Update
|
|||||||
import top.fumiama.copymanga.api.user.Member
|
import top.fumiama.copymanga.api.user.Member
|
||||||
import top.fumiama.copymanga.lib.Comancry
|
import top.fumiama.copymanga.lib.Comancry
|
||||||
import top.fumiama.copymanga.lib.Comandy
|
import top.fumiama.copymanga.lib.Comandy
|
||||||
|
import top.fumiama.copymanga.storage.DataLoader
|
||||||
|
import top.fumiama.copymanga.strings.Base16384
|
||||||
import top.fumiama.dmzj.copymanga.BuildConfig
|
import top.fumiama.dmzj.copymanga.BuildConfig
|
||||||
import top.fumiama.dmzj.copymanga.R
|
import top.fumiama.dmzj.copymanga.R
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -158,7 +160,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
Log.d("MyMain", "start menu waiting")
|
Log.d("MyMain", "start menu waiting")
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
Config.myHostApiUrl.init()
|
Config.api.init()
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
delay(1000)
|
delay(1000)
|
||||||
@@ -206,6 +208,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
@@ -214,10 +217,39 @@ class MainActivity : AppCompatActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_download -> {
|
R.id.action_download -> {
|
||||||
if (NewDownloadFragment.wn != null) {
|
when (navController?.currentDestination?.id) {
|
||||||
//TODO: fill it
|
R.id.nav_new_download -> {
|
||||||
} else {
|
//TODO: fill it
|
||||||
bookHandler.get()?.sendEmptyMessage(BookHandler.NAVIGATE_TO_DOWNLOAD)
|
}
|
||||||
|
R.id.nav_settings -> {
|
||||||
|
toolsBox.buildInfo("备份管理", "可选择导出或导入base16384格式配置项",
|
||||||
|
"导出", "导入", "取消", { // ok
|
||||||
|
MaterialDialog(this).show {
|
||||||
|
input(prefill = Base16384.encode(DataLoader().toByteArray()))
|
||||||
|
positiveButton(android.R.string.ok)
|
||||||
|
title(null, "请复制配置文本并保存")
|
||||||
|
}
|
||||||
|
}, { // neutral
|
||||||
|
MaterialDialog(this).show {
|
||||||
|
input { _, c ->
|
||||||
|
try {
|
||||||
|
DataLoader(Base16384.decode(c.toString())).settings.export()
|
||||||
|
navController?.apply {
|
||||||
|
currentDestination?.id?.let {
|
||||||
|
popBackStack()
|
||||||
|
navigate(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(this@MainActivity, e.message?:e::class.simpleName, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
positiveButton(android.R.string.ok)
|
||||||
|
title(null, "请粘贴配置文本")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else -> bookHandler.get()?.sendEmptyMessage(BookHandler.NAVIGATE_TO_DOWNLOAD)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -338,6 +370,13 @@ class MainActivity : AppCompatActivity() {
|
|||||||
menuMain?.findItem(R.id.action_sort)?.isVisible = false
|
menuMain?.findItem(R.id.action_sort)?.isVisible = false
|
||||||
menuMain?.findItem(R.id.action_del)?.isVisible = true
|
menuMain?.findItem(R.id.action_del)?.isVisible = true
|
||||||
}
|
}
|
||||||
|
R.id.nav_settings -> {
|
||||||
|
Log.d("MyMain", "enter settings")
|
||||||
|
menuMain?.findItem(R.id.action_info)?.isVisible = false
|
||||||
|
menuMain?.findItem(R.id.action_download)?.isVisible = true
|
||||||
|
menuMain?.findItem(R.id.action_sort)?.isVisible = false
|
||||||
|
menuMain?.findItem(R.id.action_del)?.isVisible = false
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Log.d("MyMain", "enter others")
|
Log.d("MyMain", "enter others")
|
||||||
menuMain?.findItem(R.id.action_info)?.isVisible = false
|
menuMain?.findItem(R.id.action_info)?.isVisible = false
|
||||||
@@ -433,7 +472,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
dl.setMessage("${getString(R.string.app_description)}\n" +
|
dl.setMessage("${getString(R.string.app_description)}\n" +
|
||||||
"\n$comandy\n" +
|
"\n$comandy\n" +
|
||||||
"$comancry\n\n"+ File("/proc/self/cmdline").readText() + "\n" +
|
"$comancry\n\n"+ File("/proc/self/cmdline").readText() + "\n" +
|
||||||
"当前API: ${Config.myHostApiUrl.getApis().joinToString(", ")}")
|
"当前API: ${Config.api.getApis().joinToString(", ")}")
|
||||||
dl.setTitle("${getString(R.string.action_info)} ${BuildConfig.VERSION_NAME}")
|
dl.setTitle("${getString(R.string.action_info)} ${BuildConfig.VERSION_NAME}")
|
||||||
dl.setIcon(R.mipmap.ic_launcher)
|
dl.setIcon(R.mipmap.ic_launcher)
|
||||||
dl.setPositiveButton(android.R.string.ok) { _, _ -> }
|
dl.setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import top.fumiama.dmzj.copymanga.R
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object Config {
|
object Config {
|
||||||
|
val api = Api()
|
||||||
|
|
||||||
var imageProxy: Proxy? = null
|
var imageProxy: Proxy? = null
|
||||||
get() {
|
get() {
|
||||||
if (!net_use_img_proxy.value) return null
|
if (!net_use_img_proxy.value) return null
|
||||||
@@ -55,16 +57,11 @@ object Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val proxyUrl = MainActivity.mainWeakReference?.get()?.getString(R.string.proxyUrl)!!
|
val proxyUrl = MainActivity.mainWeakReference?.get()?.getString(R.string.proxyUrl)!!
|
||||||
val reverseProxyUrl = PreferenceString(R.string.reverseProxyKeyID)
|
|
||||||
val networkApiUrl = PreferenceString("settings_cat_net_et_api_url", R.string.hostUrl)
|
|
||||||
val myHostApiUrl = Api()
|
|
||||||
val navTextInfo = UserPreferenceString("navTextInfo", R.string.navTextInfo)
|
|
||||||
val proxy_key = PreferenceString(R.string.imgProxyCodeKeyID)
|
|
||||||
val app_ver = PreferenceString("settings_cat_general_et_app_version", R.string.app_ver)
|
|
||||||
val platform = PreferenceString("settings_cat_general_et_platform", R.string.platform)
|
|
||||||
val token = UserPreferenceString("token", "", null)
|
|
||||||
val pc_ua get() = MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)?.format(app_ver.value)?:""
|
val pc_ua get() = MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)?.format(app_ver.value)?:""
|
||||||
val referer get() = MainActivity.mainWeakReference?.get()?.getString(R.string.referer)?.format(app_ver.value)?:""
|
val referer get() = MainActivity.mainWeakReference?.get()?.getString(R.string.referer)?.format(app_ver.value)?:""
|
||||||
|
|
||||||
|
val navTextInfo = UserPreferenceString("navTextInfo", R.string.navTextInfo)
|
||||||
|
val token = UserPreferenceString("token", "", null)
|
||||||
val comandy_version = UserPreferenceInt("comandy_version", 0)
|
val comandy_version = UserPreferenceInt("comandy_version", 0)
|
||||||
val comancry_version = UserPreferenceInt("comancry_version", 0)
|
val comancry_version = UserPreferenceInt("comancry_version", 0)
|
||||||
val user_id = UserPreferenceString("user_id")
|
val user_id = UserPreferenceString("user_id")
|
||||||
@@ -72,6 +69,8 @@ object Config {
|
|||||||
val nickname = UserPreferenceString("nickname")
|
val nickname = UserPreferenceString("nickname")
|
||||||
val avatar = UserPreferenceString("avatar")
|
val avatar = UserPreferenceString("avatar")
|
||||||
|
|
||||||
|
val app_ver = PreferenceString("settings_cat_general_et_app_version", R.string.app_ver)
|
||||||
|
val platform = PreferenceString("settings_cat_general_et_platform", R.string.platform)
|
||||||
val general_enable_transparent_system_bar = PreferenceBoolean("settings_cat_general_sw_enable_transparent_systembar", false)
|
val general_enable_transparent_system_bar = PreferenceBoolean("settings_cat_general_sw_enable_transparent_systembar", false)
|
||||||
val general_disable_kanban_animation = PreferenceBoolean("settings_cat_general_sw_disable_kanban_animation", false)
|
val general_disable_kanban_animation = PreferenceBoolean("settings_cat_general_sw_disable_kanban_animation", false)
|
||||||
val general_card_per_row = PreferenceInt("settings_cat_general_sb_card_per_row", 0)
|
val general_card_per_row = PreferenceInt("settings_cat_general_sb_card_per_row", 0)
|
||||||
@@ -79,6 +78,9 @@ object Config {
|
|||||||
val manga_dl_max_batch = PreferenceInt("settings_cat_md_sb_max_batch", 16)
|
val manga_dl_max_batch = PreferenceInt("settings_cat_md_sb_max_batch", 16)
|
||||||
val manga_dl_show_0m_manga = PreferenceBoolean("settings_cat_md_sw_show_0m_manga", false)
|
val manga_dl_show_0m_manga = PreferenceBoolean("settings_cat_md_sw_show_0m_manga", false)
|
||||||
|
|
||||||
|
val reverseProxyUrl = PreferenceString(R.string.reverseProxyKeyID)
|
||||||
|
val networkApiUrl = PreferenceString("settings_cat_net_et_api_url", R.string.hostUrl)
|
||||||
|
val proxy_key = PreferenceString(R.string.imgProxyCodeKeyID)
|
||||||
val net_use_gzip = PreferenceBoolean("settings_cat_net_sw_use_gzip", false)
|
val net_use_gzip = PreferenceBoolean("settings_cat_net_sw_use_gzip", false)
|
||||||
val net_use_json = PreferenceBoolean("settings_cat_net_sw_use_json", false)
|
val net_use_json = PreferenceBoolean("settings_cat_net_sw_use_json", false)
|
||||||
val net_platform = PreferenceBoolean("settings_cat_net_sw_platform", false)
|
val net_platform = PreferenceBoolean("settings_cat_net_sw_platform", false)
|
||||||
@@ -88,7 +90,7 @@ object Config {
|
|||||||
val net_no_webp = PreferenceBoolean("settings_cat_net_no_webp", false)
|
val net_no_webp = PreferenceBoolean("settings_cat_net_no_webp", false)
|
||||||
val net_use_comandy = PreferenceBoolean("settings_cat_net_sw_use_comandy", false)
|
val net_use_comandy = PreferenceBoolean("settings_cat_net_sw_use_comandy", false)
|
||||||
val net_use_foreign = PreferenceBoolean("settings_cat_net_sw_use_foreign", false)
|
val net_use_foreign = PreferenceBoolean("settings_cat_net_sw_use_foreign", false)
|
||||||
private val net_use_img_proxy = PreferenceBoolean("settings_cat_net_sw_use_img_proxy", false)
|
val net_use_img_proxy = PreferenceBoolean("settings_cat_net_sw_use_img_proxy", false)
|
||||||
val net_use_api_proxy = PreferenceBoolean("settings_cat_net_sw_use_api_proxy", false)
|
val net_use_api_proxy = PreferenceBoolean("settings_cat_net_sw_use_api_proxy", false)
|
||||||
val net_img_resolution = PreferenceString(R.string.imgResolutionKeyID)
|
val net_img_resolution = PreferenceString(R.string.imgResolutionKeyID)
|
||||||
val net_umstring = PreferenceString("settings_cat_net_et_umstring")
|
val net_umstring = PreferenceString("settings_cat_net_et_umstring")
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package top.fumiama.copymanga.api.manga
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
import kotlinx.android.synthetic.main.app_bar_main.*
|
||||||
import kotlinx.android.synthetic.main.card_book.*
|
import kotlinx.android.synthetic.main.card_book.*
|
||||||
import kotlinx.android.synthetic.main.line_booktandb.*
|
import kotlinx.android.synthetic.main.line_booktandb.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -56,38 +58,29 @@ class Book(val path: String, private val getString: (Int) -> String, private val
|
|||||||
* 更新云端最新图书信息并缓存到本地
|
* 更新云端最新图书信息并缓存到本地
|
||||||
*/
|
*/
|
||||||
suspend fun updateInfo() = withContext(Dispatchers.IO) {
|
suspend fun updateInfo() = withContext(Dispatchers.IO) {
|
||||||
try {
|
var isDownload = false
|
||||||
var isDownload = false
|
val data: String = if (loadCache) {
|
||||||
val data: String = if (loadCache) {
|
name?.let { loadInfo(it) } ?: run {
|
||||||
name?.let { loadInfo(it) } ?: run {
|
|
||||||
isDownload = true
|
|
||||||
Config.myHostApiUrl.get(mBookApiUrl)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
isDownload = true
|
isDownload = true
|
||||||
Config.myHostApiUrl.get(mBookApiUrl)
|
Config.api.get(mBookApiUrl)
|
||||||
}
|
}
|
||||||
mBook = Gson().fromJson(data, BookInfoStructure::class.java)
|
} else {
|
||||||
if (isDownload) saveInfo(data)
|
isDownload = true
|
||||||
mGroupPathWords = arrayOf()
|
Config.api.get(mBookApiUrl)
|
||||||
mKeys = arrayOf()
|
}
|
||||||
mCounts = intArrayOf()
|
mBook = Gson().fromJson(data, BookInfoStructure::class.java)
|
||||||
mBook?.results?.groups?.values?.forEach {
|
if (isDownload) saveInfo(data)
|
||||||
mKeys += it.name
|
mGroupPathWords = arrayOf()
|
||||||
mGroupPathWords += it.path_word
|
mKeys = arrayOf()
|
||||||
if (it.count == 0) {
|
mCounts = intArrayOf()
|
||||||
it.count = 1
|
mBook?.results?.groups?.values?.forEach {
|
||||||
}
|
mKeys += it.name
|
||||||
mCounts += it.count
|
mGroupPathWords += it.path_word
|
||||||
Log.d("MyB", "Add caption: ${it.name} @ ${it.path_word} of ${it.count}")
|
if (it.count == 0) {
|
||||||
}
|
it.count = 1
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
MainActivity.mainWeakReference?.get()?.apply {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
runOnUiThread { Toast.makeText(this@apply, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
mCounts += it.count
|
||||||
|
Log.d("MyB", "Add caption: ${it.name} @ ${it.path_word} of ${it.count}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Shelf(private val getString: (Int) -> String) {
|
|||||||
append("")
|
append("")
|
||||||
append(Config.token.value)
|
append(Config.token.value)
|
||||||
}
|
}
|
||||||
val re = Config.myHostApiUrl.request(
|
val re = Config.api.request(
|
||||||
addApiUrl, body.encodeToByteArray(), "POST",
|
addApiUrl, body.encodeToByteArray(), "POST",
|
||||||
"application/x-www-form-urlencoded;charset=utf-8")
|
"application/x-www-form-urlencoded;charset=utf-8")
|
||||||
Gson().fromJson(re, ReturnBase::class.java).message
|
Gson().fromJson(re, ReturnBase::class.java).message
|
||||||
@@ -43,7 +43,7 @@ class Shelf(private val getString: (Int) -> String) {
|
|||||||
append("authorization=Token+")
|
append("authorization=Token+")
|
||||||
append(Config.token.value)
|
append(Config.token.value)
|
||||||
}
|
}
|
||||||
val re = Config.myHostApiUrl.request(
|
val re = Config.api.request(
|
||||||
delApiUrl, body.encodeToByteArray(),
|
delApiUrl, body.encodeToByteArray(),
|
||||||
"DELETE", "application/x-www-form-urlencoded;charset=utf-8")
|
"DELETE", "application/x-www-form-urlencoded;charset=utf-8")
|
||||||
Gson().fromJson(re, ReturnBase::class.java).message
|
Gson().fromJson(re, ReturnBase::class.java).message
|
||||||
@@ -51,7 +51,7 @@ class Shelf(private val getString: (Int) -> String) {
|
|||||||
|
|
||||||
suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
|
suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
|
||||||
val queryUrl = queryApiUrlTemplate.format(pathWord, Config.platform.value)
|
val queryUrl = queryApiUrlTemplate.format(pathWord, Config.platform.value)
|
||||||
Config.myHostApiUrl.get(queryUrl).let {
|
Config.api.get(queryUrl).let {
|
||||||
Gson().fromJson(it, BookQueryStructure::class.java)
|
Gson().fromJson(it, BookQueryStructure::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,18 +50,14 @@ class Api {
|
|||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
mHostApiUrls = mutableListOf(networkApiUrl.value)
|
mHostApiUrls = mutableListOf(networkApiUrl.value)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
runOnUiThread {
|
Toast.makeText(this@apply, "${e::class.simpleName} ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
Toast.makeText(this@apply, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mHostApiUrls.isEmpty()) {
|
if (mHostApiUrls.isEmpty()) {
|
||||||
mHostApiUrls = mutableListOf(networkApiUrl.value)
|
mHostApiUrls = mutableListOf(networkApiUrl.value)
|
||||||
Log.d("MyApi", "myHostApiUrl set default ${mHostApiUrls[0]}")
|
Log.d("MyApi", "myHostApiUrl set default ${mHostApiUrls[0]}")
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
runOnUiThread {
|
Toast.makeText(this@apply, "无法获取API列表", Toast.LENGTH_SHORT).show()
|
||||||
Toast.makeText(this@apply, "无法获取API列表", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,22 +73,12 @@ class Api {
|
|||||||
var r: ReturnBase? = null
|
var r: ReturnBase? = null
|
||||||
apis.forEachIndexed { i, api ->
|
apis.forEachIndexed { i, api ->
|
||||||
val u = "https://$api$path"
|
val u = "https://$api$path"
|
||||||
|
var ret = ""
|
||||||
try {
|
try {
|
||||||
val ret = (apiProxy?.comancry(u) {
|
ret = (apiProxy?.comancry(u) {
|
||||||
DownloadTools.getApiContent(it)
|
DownloadTools.getApiContent(it)
|
||||||
}?: DownloadTools.getApiContent(u)).decodeToString()
|
}?: DownloadTools.getApiContent(u)).decodeToString()
|
||||||
r = Gson().fromJson(ret, ReturnBase::class.java)
|
r = Gson().fromJson(ret, ReturnBase::class.java)
|
||||||
if (r!!.code != 200) {
|
|
||||||
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) {
|
} catch (e: Exception) {
|
||||||
mu.withLock {
|
mu.withLock {
|
||||||
if (mHostApiUrls.size <= 1) return@withLock
|
if (mHostApiUrls.size <= 1) return@withLock
|
||||||
@@ -102,11 +88,19 @@ class Api {
|
|||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
r?.let {
|
||||||
|
if (it.code != 200) {
|
||||||
|
throw IllegalArgumentException("错误码${it.code}, 信息: ${it.message?:"空"}")
|
||||||
|
} else {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw IllegalStateException("错误码${r?.code?:-1}, 信息: ${r?.message?:"空"}")
|
throw NoSuchElementException("无可用API")
|
||||||
}
|
}
|
||||||
|
|
||||||
// request throw error on non-json or non-200 or empty apis, path: /api/v3/xxx, return json string
|
// 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 {
|
suspend fun request(path: String, body: ByteArray, method: String, contentType: String, forceApi: String? = null): String {
|
||||||
val apis = if (forceApi == null) mu.withLock { mHostApiUrls } else mutableListOf(forceApi)
|
val apis = if (forceApi == null) mu.withLock { mHostApiUrls } else mutableListOf(forceApi)
|
||||||
if (apis.isEmpty()) {
|
if (apis.isEmpty()) {
|
||||||
throw NoSuchElementException("API列表为空")
|
throw NoSuchElementException("API列表为空")
|
||||||
@@ -114,22 +108,12 @@ class Api {
|
|||||||
var r: ReturnBase? = null
|
var r: ReturnBase? = null
|
||||||
apis.forEachIndexed { i, api ->
|
apis.forEachIndexed { i, api ->
|
||||||
val u = "https://$api$path"
|
val u = "https://$api$path"
|
||||||
|
var ret = ""
|
||||||
try {
|
try {
|
||||||
val ret = (apiProxy?.comancry(u) {
|
ret = (apiProxy?.comancry(u) {
|
||||||
DownloadTools.requestApiWithBody(u, method, body, contentType)
|
DownloadTools.requestApiWithBody(u, method, body, contentType)
|
||||||
}?: DownloadTools.requestApiWithBody(u, method, body, contentType)).decodeToString()
|
}?: DownloadTools.requestApiWithBody(u, method, body, contentType)).decodeToString()
|
||||||
r = Gson().fromJson(ret, ReturnBase::class.java)
|
r = Gson().fromJson(ret, ReturnBase::class.java)
|
||||||
if (r!!.code != 200) {
|
|
||||||
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) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
mu.withLock {
|
mu.withLock {
|
||||||
@@ -140,7 +124,14 @@ class Api {
|
|||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
r?.let {
|
||||||
|
if (it.code != 200) {
|
||||||
|
throw IllegalArgumentException("错误码${it.code}, 信息: ${it.message?:"空"}")
|
||||||
|
} else {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw IllegalStateException("错误码${r?.code?:-1}, 信息: ${r?.message?:"空"}")
|
throw NoSuchElementException("无可用API")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,15 +24,15 @@ class Member(private val getString: (Int) -> String) {
|
|||||||
* - **code**: 449: 未登录, 450: 有 Exception
|
* - **code**: 449: 未登录, 450: 有 Exception
|
||||||
* - **message**: 可以 toast 的信息
|
* - **message**: 可以 toast 的信息
|
||||||
*/
|
*/
|
||||||
suspend fun info(): LoginInfoStructure = withContext(Dispatchers.IO) {
|
suspend fun info(): LoginInfoStructure {
|
||||||
if (!hasLogin) {
|
if (!hasLogin) {
|
||||||
throw IllegalArgumentException(getString(R.string.noLogin))
|
throw IllegalArgumentException(getString(R.string.noLogin))
|
||||||
}
|
}
|
||||||
val u = getString(R.string.memberInfoApiUrl)
|
val u = getString(R.string.memberInfoApiUrl)
|
||||||
.format(Config.platform.value)
|
.format(Config.platform.value)
|
||||||
val l = Gson().fromJson(Config.myHostApiUrl.get(u), LoginInfoStructure::class.java)
|
val l = Gson().fromJson(Config.api.get(u), LoginInfoStructure::class.java)
|
||||||
if (l.code == 200) Config.avatar.value = l.results.avatar
|
if (l.code == 200) Config.avatar.value = l.results.avatar // must be true
|
||||||
l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun logout() = withContext(Dispatchers.IO) {
|
suspend fun logout() = withContext(Dispatchers.IO) {
|
||||||
@@ -43,7 +43,7 @@ class Member(private val getString: (Int) -> String) {
|
|||||||
Config.avatar.value = null
|
Config.avatar.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun saveInfo(data: ByteArray) = data.inputStream().use { dataIn ->
|
private fun saveInfo(data: ByteArray) = data.inputStream().use { dataIn ->
|
||||||
try {
|
try {
|
||||||
Gson().fromJson(dataIn.reader(), LoginInfoStructure::class.java)?.let { l ->
|
Gson().fromJson(dataIn.reader(), LoginInfoStructure::class.java)?.let { l ->
|
||||||
if (l.code == 200) {
|
if (l.code == 200) {
|
||||||
@@ -51,9 +51,8 @@ class Member(private val getString: (Int) -> String) {
|
|||||||
Config.user_id.value = l.results?.user_id
|
Config.user_id.value = l.results?.user_id
|
||||||
Config.username.value = l.results?.username
|
Config.username.value = l.results?.username
|
||||||
Config.nickname.value = l.results.nickname
|
Config.nickname.value = l.results.nickname
|
||||||
return@use info()
|
|
||||||
}
|
}
|
||||||
return@use l
|
l
|
||||||
} ?: throw Exception(getString(R.string.login_parse_json_error))
|
} ?: throw Exception(getString(R.string.login_parse_json_error))
|
||||||
} catch (e: JsonSyntaxException) {
|
} catch (e: JsonSyntaxException) {
|
||||||
throw JsonSyntaxException(data.decodeToString(), e)
|
throw JsonSyntaxException(data.decodeToString(), e)
|
||||||
@@ -65,7 +64,7 @@ class Member(private val getString: (Int) -> String) {
|
|||||||
val r = if (!Config.net_use_foreign.value) "1" else "0"
|
val r = if (!Config.net_use_foreign.value) "1" else "0"
|
||||||
val pwdEncoded =
|
val pwdEncoded =
|
||||||
Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
|
Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
|
||||||
Config.myHostApiUrl.request(u, "username=${
|
Config.api.request(u, "username=${
|
||||||
URLEncoder.encode(
|
URLEncoder.encode(
|
||||||
username,
|
username,
|
||||||
Charset.defaultCharset().name()
|
Charset.defaultCharset().name()
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import com.google.gson.Gson
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.json.JSONObject
|
||||||
import top.fumiama.copymanga.api.Config
|
import top.fumiama.copymanga.api.Config
|
||||||
import top.fumiama.copymanga.api.Config.proxyUrl
|
import top.fumiama.copymanga.api.Config.proxyUrl
|
||||||
import top.fumiama.copymanga.json.ComandyCapsule
|
import top.fumiama.copymanga.json.ComandyCapsule
|
||||||
import top.fumiama.copymanga.lib.Comandy
|
import top.fumiama.copymanga.lib.Comandy
|
||||||
|
import java.io.BufferedReader
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@@ -146,6 +148,19 @@ object DownloadTools {
|
|||||||
} else ret
|
} else ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseErrorResponse(body: String): String {
|
||||||
|
return try {
|
||||||
|
val json = JSONObject(body)
|
||||||
|
if (json.has("detail")) {
|
||||||
|
json.getString("detail")
|
||||||
|
} else {
|
||||||
|
body // 原始 JSON 返回
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"非JSON返回: $body"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun InputStream.readBytesWithProgress(sz: Int, p: Client.Progress): ByteArray {
|
private fun InputStream.readBytesWithProgress(sz: Int, p: Client.Progress): ByteArray {
|
||||||
val buffer = ByteArrayOutputStream(maxOf(DEFAULT_BUFFER_SIZE, this.available()))
|
val buffer = ByteArrayOutputStream(maxOf(DEFAULT_BUFFER_SIZE, this.available()))
|
||||||
copyToWithProgress(buffer, sz, p)
|
copyToWithProgress(buffer, sz, p)
|
||||||
@@ -184,7 +199,7 @@ object DownloadTools {
|
|||||||
//Log.d("MyDT", "comandy reply: $result")
|
//Log.d("MyDT", "comandy reply: $result")
|
||||||
Gson().fromJson(result, ComandyCapsule::class.java)!!.let {
|
Gson().fromJson(result, ComandyCapsule::class.java)!!.let {
|
||||||
if (it.code != 200) throw IllegalArgumentException("HTTP${it.code} ${
|
if (it.code != 200) throw IllegalArgumentException("HTTP${it.code} ${
|
||||||
it.data?.let { d -> Base64.decode(d, Base64.DEFAULT).decodeToString() }
|
it.data?.let { d -> parseErrorResponse(Base64.decode(d, Base64.DEFAULT).decodeToString()) }
|
||||||
}")
|
}")
|
||||||
coding = it.headers["Content-Encoding"] as String?
|
coding = it.headers["Content-Encoding"] as String?
|
||||||
Base64.decode(it.data, Base64.DEFAULT)
|
Base64.decode(it.data, Base64.DEFAULT)
|
||||||
@@ -198,10 +213,14 @@ object DownloadTools {
|
|||||||
}
|
}
|
||||||
getApiConnection(u, "GET").let { conn ->
|
getApiConnection(u, "GET").let { conn ->
|
||||||
val sz = conn.getHeaderFieldInt("Content-Length", 0)
|
val sz = conn.getHeaderFieldInt("Content-Length", 0)
|
||||||
|
if (conn.responseCode >= 400)
|
||||||
|
throw IllegalArgumentException("HTTP${conn.responseCode} " +
|
||||||
|
parseErrorResponse(conn.errorStream.bufferedReader().use(BufferedReader::readText))
|
||||||
|
)
|
||||||
val ret = if (sz > 0 && p != null) {
|
val ret = if (sz > 0 && p != null) {
|
||||||
conn.inputStream.readBytesWithProgress(sz, p)
|
conn.inputStream.use { it.readBytesWithProgress(sz, p) }
|
||||||
} else {
|
} else {
|
||||||
conn.inputStream.readBytes()
|
conn.inputStream.use(InputStream::readBytes)
|
||||||
}
|
}
|
||||||
conn.disconnect()
|
conn.disconnect()
|
||||||
Log.d("MyDT", "getHttpContent: ${ret.size} bytes")
|
Log.d("MyDT", "getHttpContent: ${ret.size} bytes")
|
||||||
@@ -255,7 +274,9 @@ object DownloadTools {
|
|||||||
completed = true
|
completed = true
|
||||||
p?.notify(100)
|
p?.notify(100)
|
||||||
Gson().fromJson(result, ComandyCapsule::class.java)?.let {
|
Gson().fromJson(result, ComandyCapsule::class.java)?.let {
|
||||||
if (it.code != 200) null
|
if (it.code != 200) throw IllegalArgumentException("HTTP${it.code} ${
|
||||||
|
it.data?.let { d -> Base64.decode(d, Base64.DEFAULT).decodeToString() }
|
||||||
|
}")
|
||||||
else it.data?.let { d -> Base64.decode(d, Base64.DEFAULT) }
|
else it.data?.let { d -> Base64.decode(d, Base64.DEFAULT) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,17 +293,17 @@ object DownloadTools {
|
|||||||
var ret: ByteArray? = null
|
var ret: ByteArray? = null
|
||||||
try {
|
try {
|
||||||
val connection = getNormalConnection(u, "GET", Config.pc_ua)
|
val connection = getNormalConnection(u, "GET", Config.pc_ua)
|
||||||
val ci = connection.inputStream
|
|
||||||
val sz = connection.getHeaderFieldInt("Content-Length", 0)
|
val sz = connection.getHeaderFieldInt("Content-Length", 0)
|
||||||
if(readSize > 0) {
|
connection.inputStream.use { ci ->
|
||||||
ret = ByteArray(readSize)
|
if(readSize > 0) {
|
||||||
ci?.read(ret, 0, readSize)
|
ret = ByteArray(readSize)
|
||||||
} else ret = if (sz > 0 && p != null) {
|
ci.read(ret, 0, readSize)
|
||||||
ci.readBytesWithProgress(sz, p)
|
} else ret = if (sz > 0 && p != null) {
|
||||||
} else {
|
ci.readBytesWithProgress(sz, p)
|
||||||
ci.readBytes()
|
} else {
|
||||||
|
ci.readBytes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ci?.close()
|
|
||||||
connection.disconnect()
|
connection.disconnect()
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
@@ -313,7 +334,11 @@ object DownloadTools {
|
|||||||
var coding = ""
|
var coding = ""
|
||||||
getApiConnection(url, method).apply {
|
getApiConnection(url, method).apply {
|
||||||
outputStream.write(body)
|
outputStream.write(body)
|
||||||
ret = inputStream.readBytes()
|
if (responseCode >= 400)
|
||||||
|
throw IllegalArgumentException("HTTP${responseCode} " +
|
||||||
|
parseErrorResponse(errorStream.bufferedReader().use(BufferedReader::readText))
|
||||||
|
)
|
||||||
|
ret = inputStream.use(InputStream::readBytes)
|
||||||
disconnect()
|
disconnect()
|
||||||
coding = getHeaderField("Content-Encoding")?:""
|
coding = getHeaderField("Content-Encoding")?:""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ open class AutoDownloadHandler(
|
|||||||
var cnt = 0
|
var cnt = 0
|
||||||
while (cnt++ <= 3) {
|
while (cnt++ <= 3) {
|
||||||
try {
|
try {
|
||||||
val data = Config.myHostApiUrl.get(url())
|
val data = Config.api.get(url())
|
||||||
if(exit) return@withContext
|
if(exit) return@withContext
|
||||||
val pass = setGsonItem(Gson().fromJson(data, jsonClass))
|
val pass = setGsonItem(Gson().fromJson(data, jsonClass))
|
||||||
if (pass && loadFromCache) {
|
if (pass && loadFromCache) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class PausableDownloader(private val url: String, private val waitMilliseconds:
|
|||||||
var c = 0
|
var c = 0
|
||||||
while (!exit && c++ < 3) {
|
while (!exit && c++ < 3) {
|
||||||
try {
|
try {
|
||||||
val data = Config.myHostApiUrl.get(url)
|
val data = Config.api.get(url)
|
||||||
whenFinish?.let { it(data.encodeToByteArray()) }
|
whenFinish?.let { it(data.encodeToByteArray()) }
|
||||||
return@withContext true
|
return@withContext true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
290
app/src/main/java/top/fumiama/copymanga/storage/DataLoader.kt
Normal file
290
app/src/main/java/top/fumiama/copymanga/storage/DataLoader.kt
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
package top.fumiama.copymanga.storage
|
||||||
|
|
||||||
|
import top.fumiama.copymanga.api.Config
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.util.zip.CRC32
|
||||||
|
|
||||||
|
class DataLoader {
|
||||||
|
data class Settings(
|
||||||
|
var appVer: String,
|
||||||
|
var platform: String,
|
||||||
|
var generalEnableTransparentSystemBar: Boolean,
|
||||||
|
var generalDisableKanbanAnimation: Boolean,
|
||||||
|
var generalCardPerRow: Int,
|
||||||
|
|
||||||
|
var mangaDlMaxBatch: Int,
|
||||||
|
var mangaDlShow0mManga: Boolean,
|
||||||
|
|
||||||
|
var reverseProxyUrl: String,
|
||||||
|
var networkApiUrl: String,
|
||||||
|
var proxyKey: String,
|
||||||
|
|
||||||
|
var netUseGzip: Boolean,
|
||||||
|
var netUseJson: Boolean,
|
||||||
|
var netPlatform: Boolean,
|
||||||
|
var netReferer: Boolean,
|
||||||
|
var netVersion: Boolean,
|
||||||
|
var netRegion: Boolean,
|
||||||
|
var netNoWebp: Boolean,
|
||||||
|
var netUseComandy: Boolean,
|
||||||
|
var netUseForeign: Boolean,
|
||||||
|
var netUseImgProxy: Boolean,
|
||||||
|
var netUseApiProxy: Boolean,
|
||||||
|
|
||||||
|
var netImgResolution: String,
|
||||||
|
var netUmstring: String,
|
||||||
|
var netSource: String,
|
||||||
|
var netUa: String,
|
||||||
|
|
||||||
|
var viewMangaInverseChapters: Boolean,
|
||||||
|
var viewMangaAlwaysDarkBg: Boolean,
|
||||||
|
var viewMangaVerticalMax: Int,
|
||||||
|
var viewMangaQuality: Int,
|
||||||
|
var viewMangaVolTurn: Boolean,
|
||||||
|
var viewMangaUseCellar: Boolean,
|
||||||
|
var viewMangaHideInfo: Boolean,
|
||||||
|
) {
|
||||||
|
fun export() {
|
||||||
|
Config.app_ver.value = appVer
|
||||||
|
Config.platform.value = platform
|
||||||
|
Config.general_enable_transparent_system_bar.value = generalEnableTransparentSystemBar
|
||||||
|
Config.general_disable_kanban_animation.value = generalDisableKanbanAnimation
|
||||||
|
Config.general_card_per_row.value = generalCardPerRow
|
||||||
|
|
||||||
|
Config.manga_dl_max_batch.value = mangaDlMaxBatch
|
||||||
|
Config.manga_dl_show_0m_manga.value = mangaDlShow0mManga
|
||||||
|
|
||||||
|
Config.reverseProxyUrl.value = reverseProxyUrl
|
||||||
|
Config.networkApiUrl.value = networkApiUrl
|
||||||
|
Config.proxy_key.value = proxyKey
|
||||||
|
|
||||||
|
Config.net_use_gzip.value = netUseGzip
|
||||||
|
Config.net_use_json.value = netUseJson
|
||||||
|
Config.net_platform.value = netPlatform
|
||||||
|
Config.net_referer.value = netReferer
|
||||||
|
Config.net_version.value = netVersion
|
||||||
|
Config.net_region.value = netRegion
|
||||||
|
Config.net_no_webp.value = netNoWebp
|
||||||
|
Config.net_use_comandy.value = netUseComandy
|
||||||
|
Config.net_use_foreign.value = netUseForeign
|
||||||
|
Config.net_use_img_proxy.value = netUseImgProxy
|
||||||
|
Config.net_use_api_proxy.value = netUseApiProxy
|
||||||
|
|
||||||
|
Config.net_img_resolution.value = netImgResolution
|
||||||
|
Config.net_umstring.value = netUmstring
|
||||||
|
Config.net_source.value = netSource
|
||||||
|
Config.net_ua.value = netUa
|
||||||
|
|
||||||
|
Config.view_manga_inverse_chapters.value = viewMangaInverseChapters
|
||||||
|
Config.view_manga_always_dark_bg.value = viewMangaAlwaysDarkBg
|
||||||
|
Config.view_manga_vertical_max.value = viewMangaVerticalMax
|
||||||
|
Config.view_manga_quality.value = viewMangaQuality
|
||||||
|
Config.view_manga_vol_turn.value = viewMangaVolTurn
|
||||||
|
Config.view_manga_use_cellar.value = viewMangaUseCellar
|
||||||
|
Config.view_manga_hide_info.value = viewMangaHideInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val settings: Settings
|
||||||
|
|
||||||
|
constructor(): this(Settings(
|
||||||
|
appVer = Config.app_ver.value,
|
||||||
|
platform = Config.platform.value,
|
||||||
|
generalEnableTransparentSystemBar = Config.general_enable_transparent_system_bar.value,
|
||||||
|
generalDisableKanbanAnimation = Config.general_disable_kanban_animation.value,
|
||||||
|
generalCardPerRow = Config.general_card_per_row.value,
|
||||||
|
|
||||||
|
mangaDlMaxBatch = Config.manga_dl_max_batch.value,
|
||||||
|
mangaDlShow0mManga = Config.manga_dl_show_0m_manga.value,
|
||||||
|
|
||||||
|
reverseProxyUrl = Config.reverseProxyUrl.value,
|
||||||
|
networkApiUrl = Config.networkApiUrl.value,
|
||||||
|
proxyKey = Config.proxy_key.value,
|
||||||
|
|
||||||
|
netUseGzip = Config.net_use_gzip.value,
|
||||||
|
netUseJson = Config.net_use_json.value,
|
||||||
|
netPlatform = Config.net_platform.value,
|
||||||
|
netReferer = Config.net_referer.value,
|
||||||
|
netVersion = Config.net_version.value,
|
||||||
|
netRegion = Config.net_region.value,
|
||||||
|
netNoWebp = Config.net_no_webp.value,
|
||||||
|
netUseComandy = Config.net_use_comandy.value,
|
||||||
|
netUseForeign = Config.net_use_foreign.value,
|
||||||
|
netUseImgProxy = Config.net_use_img_proxy.value,
|
||||||
|
netUseApiProxy = Config.net_use_api_proxy.value,
|
||||||
|
|
||||||
|
netImgResolution = Config.net_img_resolution.value,
|
||||||
|
netUmstring = Config.net_umstring.value,
|
||||||
|
netSource = Config.net_source.value,
|
||||||
|
netUa = Config.net_ua.value,
|
||||||
|
|
||||||
|
viewMangaInverseChapters = Config.view_manga_inverse_chapters.value,
|
||||||
|
viewMangaAlwaysDarkBg = Config.view_manga_always_dark_bg.value,
|
||||||
|
viewMangaVerticalMax = Config.view_manga_vertical_max.value,
|
||||||
|
viewMangaQuality = Config.view_manga_quality.value,
|
||||||
|
viewMangaVolTurn = Config.view_manga_vol_turn.value,
|
||||||
|
viewMangaUseCellar = Config.view_manga_use_cellar.value,
|
||||||
|
viewMangaHideInfo = Config.view_manga_hide_info.value,
|
||||||
|
))
|
||||||
|
|
||||||
|
constructor(settings: Settings) {
|
||||||
|
this.settings = settings
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(data: ByteArray) {
|
||||||
|
if (data.size < 4) throw IllegalArgumentException("Data too short for CRC")
|
||||||
|
val payload = data.copyOfRange(0, data.size - 4)
|
||||||
|
val expectedCrc = ByteBuffer.wrap(data, data.size - 4, 4).int
|
||||||
|
val actualCrc = CRC32().apply { update(payload) }.value.toInt()
|
||||||
|
if (expectedCrc != actualCrc) throw IllegalArgumentException("CRC32 check failed")
|
||||||
|
|
||||||
|
val buffer = ByteBuffer.wrap(payload)
|
||||||
|
|
||||||
|
val netFlags = buffer.short.toInt() and 0xFFFF
|
||||||
|
val miscFlags = buffer.get().toInt() and 0xFF
|
||||||
|
fun bit(value: Int, pos: Int) = ((value shr pos) and 1) != 0
|
||||||
|
|
||||||
|
val generalCardPerRow = buffer.int
|
||||||
|
val mangaDlMaxBatch = buffer.int
|
||||||
|
val viewMangaVerticalMax = buffer.int
|
||||||
|
val viewMangaQuality = buffer.int
|
||||||
|
|
||||||
|
fun readString(): String {
|
||||||
|
val len = buffer.short.toInt()
|
||||||
|
val bytes = ByteArray(len)
|
||||||
|
buffer.get(bytes)
|
||||||
|
return String(bytes, Charsets.UTF_8)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = Settings(
|
||||||
|
appVer = readString(),
|
||||||
|
platform = readString(),
|
||||||
|
generalEnableTransparentSystemBar = bit(miscFlags, 7),
|
||||||
|
generalDisableKanbanAnimation = bit(miscFlags, 6),
|
||||||
|
generalCardPerRow = generalCardPerRow,
|
||||||
|
|
||||||
|
mangaDlMaxBatch = mangaDlMaxBatch,
|
||||||
|
mangaDlShow0mManga = bit(miscFlags, 5),
|
||||||
|
|
||||||
|
reverseProxyUrl = readString(),
|
||||||
|
networkApiUrl = readString(),
|
||||||
|
proxyKey = readString(),
|
||||||
|
|
||||||
|
netUseGzip = bit(netFlags, 15),
|
||||||
|
netUseJson = bit(netFlags, 14),
|
||||||
|
netPlatform = bit(netFlags, 13),
|
||||||
|
netReferer = bit(netFlags, 12),
|
||||||
|
netVersion = bit(netFlags, 11),
|
||||||
|
netRegion = bit(netFlags, 10),
|
||||||
|
netNoWebp = bit(netFlags, 9),
|
||||||
|
netUseComandy = bit(netFlags, 8),
|
||||||
|
netUseForeign = bit(netFlags, 7),
|
||||||
|
netUseImgProxy = bit(netFlags, 6),
|
||||||
|
netUseApiProxy = bit(netFlags, 5),
|
||||||
|
|
||||||
|
netImgResolution = readString(),
|
||||||
|
netUmstring = readString(),
|
||||||
|
netSource = readString(),
|
||||||
|
netUa = readString(),
|
||||||
|
|
||||||
|
viewMangaInverseChapters = bit(miscFlags, 4),
|
||||||
|
viewMangaAlwaysDarkBg = bit(miscFlags, 3),
|
||||||
|
viewMangaVerticalMax = viewMangaVerticalMax,
|
||||||
|
viewMangaQuality = viewMangaQuality,
|
||||||
|
viewMangaVolTurn = bit(miscFlags, 2),
|
||||||
|
viewMangaUseCellar = bit(miscFlags, 1),
|
||||||
|
viewMangaHideInfo = bit(miscFlags, 0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toByteArray(): ByteArray {
|
||||||
|
val strFields = listOf(
|
||||||
|
settings.appVer, settings.platform,
|
||||||
|
settings.reverseProxyUrl, settings.networkApiUrl, settings.proxyKey,
|
||||||
|
settings.netImgResolution, settings.netUmstring, settings.netSource, settings.netUa
|
||||||
|
)
|
||||||
|
val strBytes = strFields.map { it.toByteArray(Charsets.UTF_8) }
|
||||||
|
|
||||||
|
val netFlags = wrapBooleans(
|
||||||
|
settings.netUseGzip,
|
||||||
|
settings.netUseJson,
|
||||||
|
settings.netPlatform,
|
||||||
|
settings.netReferer,
|
||||||
|
settings.netVersion,
|
||||||
|
settings.netRegion,
|
||||||
|
settings.netNoWebp,
|
||||||
|
settings.netUseComandy,
|
||||||
|
settings.netUseForeign,
|
||||||
|
settings.netUseImgProxy,
|
||||||
|
settings.netUseApiProxy,
|
||||||
|
false, false, false, false, false
|
||||||
|
)
|
||||||
|
val miscFlags = wrapBooleans(
|
||||||
|
settings.generalEnableTransparentSystemBar,
|
||||||
|
settings.generalDisableKanbanAnimation,
|
||||||
|
settings.mangaDlShow0mManga,
|
||||||
|
settings.viewMangaInverseChapters,
|
||||||
|
settings.viewMangaAlwaysDarkBg,
|
||||||
|
settings.viewMangaVolTurn,
|
||||||
|
settings.viewMangaUseCellar,
|
||||||
|
settings.viewMangaHideInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
val strTotal = strBytes.sumOf { 2 + it.size }
|
||||||
|
val buffer = ByteBuffer.allocate(
|
||||||
|
2 + // netFlags: UShort (16 bits)
|
||||||
|
1 + // miscFlags: UByte (8 bits)
|
||||||
|
4 * 4 + // Ints: 4 fields x 4 bytes
|
||||||
|
strTotal + // all string fields with 2-byte length prefix
|
||||||
|
4 // CRC32
|
||||||
|
)
|
||||||
|
buffer.putShort(netFlags.toShort())
|
||||||
|
buffer.put(miscFlags.toByte())
|
||||||
|
buffer.putInt(settings.generalCardPerRow)
|
||||||
|
buffer.putInt(settings.mangaDlMaxBatch)
|
||||||
|
buffer.putInt(settings.viewMangaVerticalMax)
|
||||||
|
buffer.putInt(settings.viewMangaQuality)
|
||||||
|
for (b in strBytes) {
|
||||||
|
buffer.putShort(b.size.toShort())
|
||||||
|
buffer.put(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
val payload = buffer.array().copyOfRange(0, buffer.position())
|
||||||
|
val crc = CRC32().apply { update(payload) }.value
|
||||||
|
val finalBuffer = ByteBuffer.allocate(payload.size + 4)
|
||||||
|
finalBuffer.put(payload)
|
||||||
|
finalBuffer.putInt(crc.toInt())
|
||||||
|
|
||||||
|
return finalBuffer.array()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun wrapBooleans(
|
||||||
|
b0: Boolean, b1: Boolean, b2: Boolean, b3: Boolean,
|
||||||
|
b4: Boolean, b5: Boolean, b6: Boolean, b7: Boolean
|
||||||
|
): UByte {
|
||||||
|
var result: UByte = 0u
|
||||||
|
val flags = listOf(b0, b1, b2, b3, b4, b5, b6, b7)
|
||||||
|
for ((i, flag) in flags.withIndex()) {
|
||||||
|
if (flag) result = result or (1u shl (7 - i)).toUByte()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun wrapBooleans(
|
||||||
|
b0: Boolean, b1: Boolean, b2: Boolean, b3: Boolean,
|
||||||
|
b4: Boolean, b5: Boolean, b6: Boolean, b7: Boolean,
|
||||||
|
b8: Boolean, b9: Boolean, b10: Boolean, b11: Boolean,
|
||||||
|
b12: Boolean, b13: Boolean, b14: Boolean, b15: Boolean
|
||||||
|
): UShort {
|
||||||
|
var result: UShort = 0u
|
||||||
|
val flags = listOf(
|
||||||
|
b0, b1, b2, b3, b4, b5, b6, b7,
|
||||||
|
b8, b9, b10, b11, b12, b13, b14, b15
|
||||||
|
)
|
||||||
|
for ((i, flag) in flags.withIndex()) {
|
||||||
|
if (flag) result = result or (1u shl (15 - i)).toUShort()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,20 +1,30 @@
|
|||||||
package top.fumiama.copymanga.storage
|
package top.fumiama.copymanga.storage
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.core.content.edit
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import top.fumiama.copymanga.MainActivity
|
import top.fumiama.copymanga.MainActivity
|
||||||
|
|
||||||
data class PreferenceBoolean(private val key: String, private var default: Boolean) {
|
data class PreferenceBoolean(private val key: String, private var default: Boolean) {
|
||||||
val value: Boolean
|
var value: Boolean = default
|
||||||
get() {
|
get() {
|
||||||
MainActivity.mainWeakReference?.get()?.let {
|
MainActivity.mainWeakReference?.get()?.let {
|
||||||
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
||||||
getBoolean(key, default).let { v ->
|
getBoolean(key, field).let { v ->
|
||||||
Log.d("MyPB", "get key $key value $v")
|
Log.d("MyPB", "get key $key value $v")
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return default
|
return field
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
Log.d("MyUPI", "set key $key value $value")
|
||||||
|
MainActivity.mainWeakReference?.get()?.let {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
||||||
|
edit(commit = true) { putBoolean(key, value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,30 @@
|
|||||||
package top.fumiama.copymanga.storage
|
package top.fumiama.copymanga.storage
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.core.content.edit
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import top.fumiama.copymanga.MainActivity
|
import top.fumiama.copymanga.MainActivity
|
||||||
|
|
||||||
data class PreferenceInt(private val key: String, private var default: Int) {
|
data class PreferenceInt(private val key: String, private var default: Int) {
|
||||||
val value: Int
|
var value: Int = default
|
||||||
get() {
|
get() {
|
||||||
MainActivity.mainWeakReference?.get()?.let {
|
MainActivity.mainWeakReference?.get()?.let {
|
||||||
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
||||||
getInt(key, default).let { v ->
|
getInt(key, field).let { v ->
|
||||||
Log.d("MyPI", "get key $key value $v")
|
Log.d("MyPI", "get key $key value $v")
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return default
|
return field
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
Log.d("MyUPI", "set key $key value $value")
|
||||||
|
MainActivity.mainWeakReference?.get()?.let {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
||||||
|
edit(commit = true) { putInt(key, value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package top.fumiama.copymanga.storage
|
package top.fumiama.copymanga.storage
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.core.content.edit
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import top.fumiama.copymanga.MainActivity
|
import top.fumiama.copymanga.MainActivity
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ data class PreferenceString(private val key: String, private var default: String
|
|||||||
}
|
}
|
||||||
return default?:""
|
return default?:""
|
||||||
}
|
}
|
||||||
val value: String
|
var value: String = defaultField
|
||||||
get() {
|
get() {
|
||||||
MainActivity.mainWeakReference?.get()?.let {
|
MainActivity.mainWeakReference?.get()?.let {
|
||||||
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
||||||
@@ -32,7 +33,16 @@ data class PreferenceString(private val key: String, private var default: String
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.d("MyPS", "get default key $key value $defaultField")
|
Log.d("MyPS", "get default key $key value $field")
|
||||||
return defaultField
|
return field
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
Log.d("MyUPI", "set key $key value $value")
|
||||||
|
MainActivity.mainWeakReference?.get()?.let {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
||||||
|
edit(commit = true) { putString(key, value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
package top.fumiama.copymanga.storage
|
|
||||||
//PropertiesTools.kt
|
|
||||||
//created by fumiama 20200724
|
|
||||||
import android.util.Log
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class PropertiesTools(private val f: File):Properties() {
|
|
||||||
private var cache = hashMapOf<String, String>()
|
|
||||||
|
|
||||||
init {
|
|
||||||
if(!f.exists()) {
|
|
||||||
if(f.parentFile?.exists() != true) f.parentFile?.mkdirs()
|
|
||||||
if(f.parentFile?.canWrite() != true) f.parentFile?.setWritable(true)
|
|
||||||
createNew(f)
|
|
||||||
} else if(f.isDirectory) {
|
|
||||||
if(f.parentFile?.canWrite() != true) f.parentFile?.setWritable(true)
|
|
||||||
f.delete()
|
|
||||||
createNew(f)
|
|
||||||
}
|
|
||||||
if(f.parentFile?.canWrite() != true) f.parentFile?.setWritable(true)
|
|
||||||
if(f.parentFile?.canRead() != true) f.parentFile?.setReadable(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createNew(f: File) {
|
|
||||||
f.createNewFile()
|
|
||||||
f.outputStream().use { o ->
|
|
||||||
this.storeToXML(o, "store")
|
|
||||||
}
|
|
||||||
Log.d("MyPT", "Generate new prop.")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadFromXml(`in`: InputStream?): PropertiesTools {
|
|
||||||
this.loadFromXML(`in`)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setProp(key: String?, value: String?): PropertiesTools {
|
|
||||||
this.setProperty(key, value)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(key: String): String{
|
|
||||||
return if(cache.containsKey(key)) cache[key]?:"null"
|
|
||||||
else {
|
|
||||||
f.inputStream().use { i ->
|
|
||||||
val re = this.loadFromXml(i).getProperty(key)?:"null"
|
|
||||||
Log.d("MyPT", "Read $key = $re")
|
|
||||||
cache[key] = re
|
|
||||||
re
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun set(key: String, value: String) {
|
|
||||||
cache[key] = value
|
|
||||||
f.outputStream().use { o ->
|
|
||||||
this.setProp(key, value).storeToXML(o, "store")
|
|
||||||
}
|
|
||||||
Log.d("MyPT", "Set $key = $value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
73
app/src/main/java/top/fumiama/copymanga/strings/Base16384.kt
Normal file
73
app/src/main/java/top/fumiama/copymanga/strings/Base16384.kt
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package top.fumiama.copymanga.strings
|
||||||
|
|
||||||
|
import android.util.Base64
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
object Base16384 {
|
||||||
|
private const val BASE = 16384
|
||||||
|
private const val FIRST_CHAR = 0x4E00
|
||||||
|
private const val PAD_START = 0x3D00
|
||||||
|
|
||||||
|
fun encode(input: ByteArray): String {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
var buffer = 0
|
||||||
|
var bitCount = 0
|
||||||
|
|
||||||
|
for (b in input) {
|
||||||
|
buffer = (buffer shl 8) or (b.toInt() and 0xFF)
|
||||||
|
bitCount += 8
|
||||||
|
while (bitCount >= 14) {
|
||||||
|
bitCount -= 14
|
||||||
|
val index = (buffer shr bitCount) and (BASE - 1)
|
||||||
|
sb.append(Char(FIRST_CHAR + index))
|
||||||
|
buffer = buffer and ((1 shl bitCount) - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitCount in 1..6) {
|
||||||
|
buffer = (buffer shl (14 - bitCount))
|
||||||
|
val index = buffer and (BASE - 1)
|
||||||
|
sb.append(Char(FIRST_CHAR + index))
|
||||||
|
val padIndex = PAD_START + bitCount
|
||||||
|
sb.append(Char(padIndex))
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("MyB14", "encode bitCount $bitCount, len ${input.size}, data: ${Base64.encode(input, Base64.DEFAULT).decodeToString()}")
|
||||||
|
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(input: String): ByteArray {
|
||||||
|
val bits = mutableListOf<Boolean>()
|
||||||
|
var tailBits = 0
|
||||||
|
for (c in input) {
|
||||||
|
when (val code = c.code) {
|
||||||
|
in FIRST_CHAR until FIRST_CHAR + BASE -> {
|
||||||
|
val idx = code - FIRST_CHAR
|
||||||
|
for (i in 13 downTo 0) {
|
||||||
|
bits.add(((idx shr i) and 1) == 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in (PAD_START + 1)..(PAD_START + 6) -> {
|
||||||
|
tailBits = code - PAD_START
|
||||||
|
break
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("Invalid base16384 char: $c")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val totalBits = bits.size - (14 - tailBits)
|
||||||
|
val byteCount = totalBits / 8
|
||||||
|
val out = ByteArray(byteCount)
|
||||||
|
for (i in 0 until byteCount) {
|
||||||
|
var byteVal = 0
|
||||||
|
for (j in 0 until 8) {
|
||||||
|
if (bits[i * 8 + j]) byteVal = byteVal or (1 shl (7 - j))
|
||||||
|
}
|
||||||
|
out[i] = byteVal.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("MyB14", "decode totalBits $totalBits, tailBits $tailBits, len ${out.size}, data: ${Base64.encode(out, Base64.DEFAULT).decodeToString()}")
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,9 +28,8 @@ object Chinese {
|
|||||||
/**
|
/**
|
||||||
* 简单检测字符串是否包含常见 CJK(中日韩)汉字。
|
* 简单检测字符串是否包含常见 CJK(中日韩)汉字。
|
||||||
*/
|
*/
|
||||||
fun containsChinese(text: String): Boolean {
|
private fun containsChinese(text: String): Boolean {
|
||||||
val regex = Regex("[\u4E00-\u9FFF]")
|
val regex = Regex("[\u4E00-\u9FFF]")
|
||||||
return regex.containsMatchIn(text)
|
return regex.containsMatchIn(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import android.widget.Toast
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.app_bar_main.*
|
import kotlinx.android.synthetic.main.app_bar_main.*
|
||||||
import kotlinx.android.synthetic.main.card_book.*
|
import kotlinx.android.synthetic.main.card_book.*
|
||||||
import kotlinx.android.synthetic.main.fragment_book.*
|
import kotlinx.android.synthetic.main.fragment_book.*
|
||||||
@@ -57,8 +58,9 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
if(mBookHandler?.exit != false) return@launch
|
if(mBookHandler?.exit != false) return@launch
|
||||||
Toast.makeText(context, R.string.null_book, Toast.LENGTH_SHORT).show()
|
Snackbar.make(view, "${e::class.simpleName} ${e.message}", Snackbar.LENGTH_LONG).setTextMaxLines(10).setDuration(10000).show()
|
||||||
findNavController().popBackStack()
|
// Toast.makeText(context, R.string.null_book, Toast.LENGTH_SHORT).show()
|
||||||
|
// findNavController().popBackStack()
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
Log.d("MyBF", "read path: ${book?.path}")
|
Log.d("MyBF", "read path: ${book?.path}")
|
||||||
@@ -196,9 +198,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
|||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
activity?.runOnUiThread {
|
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,7 +239,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
|||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,7 +258,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
|||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.model.GlideUrl
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.lapism.search.internal.SearchLayout
|
import com.lapism.search.internal.SearchLayout
|
||||||
import kotlinx.android.synthetic.main.card_book_plain.view.*
|
import kotlinx.android.synthetic.main.card_book_plain.view.*
|
||||||
@@ -29,12 +29,12 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import top.fumiama.copymanga.MainActivity
|
import top.fumiama.copymanga.MainActivity
|
||||||
import top.fumiama.copymanga.MainActivity.Companion.ime
|
import top.fumiama.copymanga.MainActivity.Companion.ime
|
||||||
import top.fumiama.copymanga.json.BookListStructure
|
|
||||||
import top.fumiama.copymanga.view.template.NoBackRefreshFragment
|
|
||||||
import top.fumiama.copymanga.net.template.PausableDownloader
|
|
||||||
import top.fumiama.copymanga.api.Config
|
import top.fumiama.copymanga.api.Config
|
||||||
import top.fumiama.copymanga.view.operation.GlideHideLottieViewListener
|
import top.fumiama.copymanga.json.BookListStructure
|
||||||
|
import top.fumiama.copymanga.net.template.PausableDownloader
|
||||||
import top.fumiama.copymanga.view.interaction.Navigate
|
import top.fumiama.copymanga.view.interaction.Navigate
|
||||||
|
import top.fumiama.copymanga.view.operation.GlideHideLottieViewListener
|
||||||
|
import top.fumiama.copymanga.view.template.NoBackRefreshFragment
|
||||||
import top.fumiama.dmzj.copymanga.R
|
import top.fumiama.dmzj.copymanga.R
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
@@ -51,18 +51,15 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
|
|||||||
val netInfo = tb.netInfo
|
val netInfo = tb.netInfo
|
||||||
if(netInfo != tb.transportStringNull && netInfo != tb.transportStringError)
|
if(netInfo != tb.transportStringNull && netInfo != tb.transportStringError)
|
||||||
MainActivity.member?.apply { lifecycleScope.launch {
|
MainActivity.member?.apply { lifecycleScope.launch {
|
||||||
Config.myHostApiUrl.init()
|
withContext(Dispatchers.IO) {
|
||||||
try {
|
Config.api.init()
|
||||||
info().let { l ->
|
try {
|
||||||
if (l.code != 200 && l.code != 449) {
|
info()
|
||||||
Toast.makeText(context, l.message, Toast.LENGTH_SHORT).show()
|
} catch (e: Exception) {
|
||||||
logout()
|
Snackbar
|
||||||
}
|
.make(view, "${e::class.simpleName} ${e.message}", Snackbar.LENGTH_LONG)
|
||||||
}
|
.setTextMaxLines(10)
|
||||||
} catch (e: Exception) {
|
.show()
|
||||||
e.printStackTrace()
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} }
|
} }
|
||||||
|
|||||||
@@ -148,9 +148,7 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
|
|||||||
override suspend fun onError() {
|
override suspend fun onError() {
|
||||||
super.onError()
|
super.onError()
|
||||||
if(exit) return
|
if(exit) return
|
||||||
withContext(Dispatchers.Main) {
|
wv.get()?.toolsBox?.toastErrorAndFinish(R.string.download_chapter_info_failed)
|
||||||
wv.get()?.toolsBox?.toastError(R.string.download_chapter_info_failed)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun doWhenFinishDownload() {
|
override suspend fun doWhenFinishDownload() {
|
||||||
@@ -187,7 +185,7 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
|
|||||||
true
|
true
|
||||||
} catch (e: Exception){
|
} catch (e: Exception){
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
wv.get()?.toolsBox?.toastError(R.string.load_local_chapter_info_failed)
|
wv.get()?.toolsBox?.toastErrorAndFinish(R.string.load_local_chapter_info_failed)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ class ViewMangaActivity : TitleActivityTemplate() {
|
|||||||
var clicked = 0
|
var clicked = 0
|
||||||
private var isInSeek = false
|
private var isInSeek = false
|
||||||
private var isInScroll = true
|
private var isInScroll = true
|
||||||
//private var progressLog: PropertiesTools? = null
|
|
||||||
var scrollImages = arrayOf<ScaleImageView>()
|
var scrollImages = arrayOf<ScaleImageView>()
|
||||||
var scrollButtons = arrayOf<Button>()
|
var scrollButtons = arrayOf<Button>()
|
||||||
var scrollPositions = arrayOf<Int>()
|
var scrollPositions = arrayOf<Int>()
|
||||||
@@ -192,7 +191,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
|
|||||||
} else prepareImgFromWeb()
|
} else prepareImgFromWeb()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
toolsBox.toastError(R.string.load_manga_error)
|
toolsBox.toastErrorAndFinish(R.string.load_manga_error)
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
startPostponedEnterTransition()
|
startPostponedEnterTransition()
|
||||||
@@ -350,7 +349,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
|
|||||||
Log.d("MyVM", "[$i] 分析: ${it.name}, cut: $useCut")
|
Log.d("MyVM", "[$i] 分析: ${it.name}, cut: $useCut")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
withContext(Dispatchers.Main) { toolsBox.toastError(R.string.count_zip_entries_error) }
|
toolsBox.toastErrorAndFinish(R.string.count_zip_entries_error)
|
||||||
}
|
}
|
||||||
Log.d("MyVM", "开始加载控件")
|
Log.d("MyVM", "开始加载控件")
|
||||||
doWhenFinish(count)
|
doWhenFinish(count)
|
||||||
@@ -377,9 +376,8 @@ class ViewMangaActivity : TitleActivityTemplate() {
|
|||||||
loadOneImg()
|
loadOneImg()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
withContext(Dispatchers.Main) {
|
toolsBox.toastError(getString(R.string.load_page_number_error).format(currentItem))
|
||||||
toolsBox.toastError(getString(R.string.load_page_number_error).format(currentItem))
|
finish()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -637,7 +635,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
|
|||||||
}*/
|
}*/
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
toolsBox.toastError(R.string.load_chapter_error)
|
toolsBox.toastErrorAndFinish(R.string.load_chapter_error)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -564,11 +564,11 @@ class ScaleImageView : ImageView {
|
|||||||
} else {
|
} else {
|
||||||
super.onDraw(canvas)
|
super.onDraw(canvas)
|
||||||
}
|
}
|
||||||
}catch (e:Exception){
|
} catch (e:Exception){
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
ViewMangaActivity.va?.get()?.apply {
|
ViewMangaActivity.va?.get()?.apply {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
toolsBox.toastError(R.string.show_image_error_try_lower_resolution, false)
|
toolsBox.toastErrorAndFinish(R.string.show_image_error_try_lower_resolution, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,11 +43,10 @@ class UITools(that: Context?, w: WeakReference<Activity>? = null) {
|
|||||||
}
|
}
|
||||||
} ?: transportStringError
|
} ?: transportStringError
|
||||||
}
|
}
|
||||||
suspend fun toastError(s: String, willFinish: Boolean = true) = withContext(Dispatchers.Main) {
|
suspend fun toastError(s: String) = withContext(Dispatchers.Main) {
|
||||||
Toast.makeText(zis, s, Toast.LENGTH_SHORT).show()
|
Toast.makeText(zis, s, Toast.LENGTH_SHORT).show()
|
||||||
if (willFinish) weak?.get()?.finish()
|
|
||||||
}
|
}
|
||||||
suspend fun toastError(s: Int, willFinish: Boolean = true) = withContext(Dispatchers.Main) {
|
suspend fun toastErrorAndFinish(s: Int, willFinish: Boolean = true) = withContext(Dispatchers.Main) {
|
||||||
Toast.makeText(zis, s, Toast.LENGTH_SHORT).show()
|
Toast.makeText(zis, s, Toast.LENGTH_SHORT).show()
|
||||||
if (willFinish) weak?.get()?.finish()
|
if (willFinish) weak?.get()?.finish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/appbar_coord"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="top.fumiama.copymanga.MainActivity">
|
tools:context="top.fumiama.copymanga.MainActivity">
|
||||||
@@ -31,6 +32,6 @@
|
|||||||
layout="@layout/content_main"
|
layout="@layout/content_main"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
<string name="analyze_img_size_error">读取图片大小失败</string>
|
<string name="analyze_img_size_error">读取图片大小失败</string>
|
||||||
|
|
||||||
<string name="networkApiUrl">/api/v3/system/network2?platform=%1$s</string>
|
<string name="networkApiUrl">/api/v3/system/network2?platform=%1$s</string>
|
||||||
<string name="mainPageApiUrl">/api/v3/h5/homeIndex?platform=%1$s</string>
|
<string name="mainPageApiUrl">/api/v3/h5/homeIndex2?platform=%1$s</string>
|
||||||
<string name="hostUrl">&hosturl;</string>
|
<string name="hostUrl">&hosturl;</string>
|
||||||
<string name="proxyUrl">&proxyurl;</string>
|
<string name="proxyUrl">&proxyurl;</string>
|
||||||
<string name="rankApiUrl">/api/v3/ranks?limit=21&offset=%1$d&date_type=%2$s&audience_type=%3$s&platform=%4$s</string>
|
<string name="rankApiUrl">/api/v3/ranks?limit=21&offset=%1$d&date_type=%2$s&audience_type=%3$s&platform=%4$s</string>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = "$cm_kotlin_version"
|
ext.kotlin_version = "$cm_kotlin_version"
|
||||||
repositories {
|
repositories {
|
||||||
|
gradlePluginPortal()
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url 'https://maven.google.com' }
|
maven { url 'https://maven.google.com' }
|
||||||
|
|||||||
Reference in New Issue
Block a user