diff --git a/app/build.gradle b/app/build.gradle index 1057528..1f3e23b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { minSdkVersion 23 //noinspection OldTargetApi targetSdkVersion 34 - versionCode 78 - versionName '2.5.5' + versionCode 79 + versionName '2.5.6' resourceConfigurations += ['zh', 'zh-rCN'] testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt b/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt index ef5e644..48d8e8b 100644 --- a/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt +++ b/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt @@ -35,8 +35,8 @@ class LoginActivity : AppCompatActivity() { ).show() return@launch } - val pwd = altpwd.text?.toString() - if (pwd.isNullOrEmpty()) { + val pwd = altpwd.text?.toString()?:"" + if (!isLogout && pwd.isEmpty()) { Toast.makeText(this@LoginActivity, R.string.login_null_pwd, Toast.LENGTH_SHORT) .show() return@launch @@ -66,7 +66,7 @@ class LoginActivity : AppCompatActivity() { Toast.makeText( this@LoginActivity, "${e::class.simpleName} ${e.message}", - Toast.LENGTH_SHORT + Toast.LENGTH_LONG ).show() } } diff --git a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt index fb18378..fe3afad 100644 --- a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt +++ b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt @@ -63,6 +63,8 @@ import top.fumiama.copymanga.api.update.Update import top.fumiama.copymanga.api.user.Member import top.fumiama.copymanga.lib.Comancry 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.R import java.io.File @@ -158,7 +160,7 @@ class MainActivity : AppCompatActivity() { Log.d("MyMain", "start menu waiting") lifecycleScope.launch { withContext(Dispatchers.IO) { - Config.myHostApiUrl.init() + Config.api.init() } withContext(Dispatchers.IO) { delay(1000) @@ -206,6 +208,7 @@ class MainActivity : AppCompatActivity() { return true } + @SuppressLint("CheckResult") @OptIn(ExperimentalStdlibApi::class) override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { @@ -214,10 +217,39 @@ class MainActivity : AppCompatActivity() { true } R.id.action_download -> { - if (NewDownloadFragment.wn != null) { - //TODO: fill it - } else { - bookHandler.get()?.sendEmptyMessage(BookHandler.NAVIGATE_TO_DOWNLOAD) + when (navController?.currentDestination?.id) { + R.id.nav_new_download -> { + //TODO: fill it + } + 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 } @@ -338,6 +370,13 @@ class MainActivity : AppCompatActivity() { menuMain?.findItem(R.id.action_sort)?.isVisible = false 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 -> { Log.d("MyMain", "enter others") menuMain?.findItem(R.id.action_info)?.isVisible = false @@ -433,7 +472,7 @@ class MainActivity : AppCompatActivity() { dl.setMessage("${getString(R.string.app_description)}\n" + "\n$comandy\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.setIcon(R.mipmap.ic_launcher) dl.setPositiveButton(android.R.string.ok) { _, _ -> } diff --git a/app/src/main/java/top/fumiama/copymanga/api/Config.kt b/app/src/main/java/top/fumiama/copymanga/api/Config.kt index c4df5e4..f7c9227 100644 --- a/app/src/main/java/top/fumiama/copymanga/api/Config.kt +++ b/app/src/main/java/top/fumiama/copymanga/api/Config.kt @@ -14,6 +14,8 @@ import top.fumiama.dmzj.copymanga.R import java.io.File object Config { + val api = Api() + var imageProxy: Proxy? = null get() { if (!net_use_img_proxy.value) return null @@ -55,16 +57,11 @@ object Config { } 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 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 comancry_version = UserPreferenceInt("comancry_version", 0) val user_id = UserPreferenceString("user_id") @@ -72,6 +69,8 @@ object Config { val nickname = UserPreferenceString("nickname") 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_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) @@ -79,6 +78,9 @@ object Config { 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 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_json = PreferenceBoolean("settings_cat_net_sw_use_json", 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_use_comandy = PreferenceBoolean("settings_cat_net_sw_use_comandy", 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_img_resolution = PreferenceString(R.string.imgResolutionKeyID) val net_umstring = PreferenceString("settings_cat_net_et_umstring") diff --git a/app/src/main/java/top/fumiama/copymanga/api/manga/Book.kt b/app/src/main/java/top/fumiama/copymanga/api/manga/Book.kt index 994023d..a050416 100644 --- a/app/src/main/java/top/fumiama/copymanga/api/manga/Book.kt +++ b/app/src/main/java/top/fumiama/copymanga/api/manga/Book.kt @@ -2,7 +2,9 @@ package top.fumiama.copymanga.api.manga import android.util.Log import android.widget.Toast +import com.google.android.material.snackbar.Snackbar 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.line_booktandb.* 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) { - try { - var isDownload = false - val data: String = if (loadCache) { - name?.let { loadInfo(it) } ?: run { - isDownload = true - Config.myHostApiUrl.get(mBookApiUrl) - } - } else { + var isDownload = false + val data: String = if (loadCache) { + name?.let { loadInfo(it) } ?: run { isDownload = true - Config.myHostApiUrl.get(mBookApiUrl) + Config.api.get(mBookApiUrl) } - mBook = Gson().fromJson(data, BookInfoStructure::class.java) - if (isDownload) saveInfo(data) - mGroupPathWords = arrayOf() - mKeys = arrayOf() - mCounts = intArrayOf() - mBook?.results?.groups?.values?.forEach { - mKeys += it.name - mGroupPathWords += it.path_word - if (it.count == 0) { - it.count = 1 - } - mCounts += it.count - Log.d("MyB", "Add caption: ${it.name} @ ${it.path_word} of ${it.count}") - } - } 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() } - } + } else { + isDownload = true + Config.api.get(mBookApiUrl) + } + mBook = Gson().fromJson(data, BookInfoStructure::class.java) + if (isDownload) saveInfo(data) + mGroupPathWords = arrayOf() + mKeys = arrayOf() + mCounts = intArrayOf() + mBook?.results?.groups?.values?.forEach { + mKeys += it.name + mGroupPathWords += it.path_word + if (it.count == 0) { + it.count = 1 } + mCounts += it.count + Log.d("MyB", "Add caption: ${it.name} @ ${it.path_word} of ${it.count}") } } diff --git a/app/src/main/java/top/fumiama/copymanga/api/manga/Shelf.kt b/app/src/main/java/top/fumiama/copymanga/api/manga/Shelf.kt index 4b7744f..80166c7 100644 --- a/app/src/main/java/top/fumiama/copymanga/api/manga/Shelf.kt +++ b/app/src/main/java/top/fumiama/copymanga/api/manga/Shelf.kt @@ -24,7 +24,7 @@ class Shelf(private val getString: (Int) -> String) { append("") append(Config.token.value) } - val re = Config.myHostApiUrl.request( + val re = Config.api.request( addApiUrl, body.encodeToByteArray(), "POST", "application/x-www-form-urlencoded;charset=utf-8") Gson().fromJson(re, ReturnBase::class.java).message @@ -43,7 +43,7 @@ class Shelf(private val getString: (Int) -> String) { append("authorization=Token+") append(Config.token.value) } - val re = Config.myHostApiUrl.request( + val re = Config.api.request( delApiUrl, body.encodeToByteArray(), "DELETE", "application/x-www-form-urlencoded;charset=utf-8") 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) { val queryUrl = queryApiUrlTemplate.format(pathWord, Config.platform.value) - Config.myHostApiUrl.get(queryUrl).let { + Config.api.get(queryUrl).let { Gson().fromJson(it, BookQueryStructure::class.java) } } diff --git a/app/src/main/java/top/fumiama/copymanga/api/network/Api.kt b/app/src/main/java/top/fumiama/copymanga/api/network/Api.kt index 2dad991..6a5e4d2 100644 --- a/app/src/main/java/top/fumiama/copymanga/api/network/Api.kt +++ b/app/src/main/java/top/fumiama/copymanga/api/network/Api.kt @@ -50,18 +50,14 @@ class Api { e.printStackTrace() mHostApiUrls = mutableListOf(networkApiUrl.value) withContext(Dispatchers.Main) { - runOnUiThread { - Toast.makeText(this@apply, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show() - } + Toast.makeText(this@apply, "${e::class.simpleName} ${e.message}", Toast.LENGTH_LONG).show() } } if (mHostApiUrls.isEmpty()) { mHostApiUrls = mutableListOf(networkApiUrl.value) Log.d("MyApi", "myHostApiUrl set default ${mHostApiUrls[0]}") 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 apis.forEachIndexed { i, api -> val u = "https://$api$path" + var ret = "" try { - val ret = (apiProxy?.comancry(u) { + ret = (apiProxy?.comancry(u) { DownloadTools.getApiContent(it) }?: DownloadTools.getApiContent(u)).decodeToString() 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) { mu.withLock { if (mHostApiUrls.size <= 1) return@withLock @@ -102,11 +88,19 @@ class Api { 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 - 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) if (apis.isEmpty()) { throw NoSuchElementException("API列表为空") @@ -114,22 +108,12 @@ class Api { var r: ReturnBase? = null apis.forEachIndexed { i, api -> val u = "https://$api$path" + var ret = "" try { - val ret = (apiProxy?.comancry(u) { + ret = (apiProxy?.comancry(u) { DownloadTools.requestApiWithBody(u, method, body, contentType) }?: DownloadTools.requestApiWithBody(u, method, body, contentType)).decodeToString() 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) { e.printStackTrace() mu.withLock { @@ -140,7 +124,14 @@ class Api { 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") } } diff --git a/app/src/main/java/top/fumiama/copymanga/api/user/Member.kt b/app/src/main/java/top/fumiama/copymanga/api/user/Member.kt index a323b85..2bc374e 100644 --- a/app/src/main/java/top/fumiama/copymanga/api/user/Member.kt +++ b/app/src/main/java/top/fumiama/copymanga/api/user/Member.kt @@ -24,15 +24,15 @@ class Member(private val getString: (Int) -> String) { * - **code**: 449: 未登录, 450: 有 Exception * - **message**: 可以 toast 的信息 */ - suspend fun info(): LoginInfoStructure = withContext(Dispatchers.IO) { + suspend fun info(): LoginInfoStructure { if (!hasLogin) { 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 + val l = Gson().fromJson(Config.api.get(u), LoginInfoStructure::class.java) + if (l.code == 200) Config.avatar.value = l.results.avatar // must be true + return l } suspend fun logout() = withContext(Dispatchers.IO) { @@ -43,7 +43,7 @@ class Member(private val getString: (Int) -> String) { Config.avatar.value = null } - private suspend fun saveInfo(data: ByteArray) = data.inputStream().use { dataIn -> + private fun saveInfo(data: ByteArray) = data.inputStream().use { dataIn -> try { Gson().fromJson(dataIn.reader(), LoginInfoStructure::class.java)?.let { l -> if (l.code == 200) { @@ -51,9 +51,8 @@ class Member(private val getString: (Int) -> String) { Config.user_id.value = l.results?.user_id Config.username.value = l.results?.username Config.nickname.value = l.results.nickname - return@use info() } - return@use l + l } ?: throw Exception(getString(R.string.login_parse_json_error)) } catch (e: JsonSyntaxException) { 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 pwdEncoded = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString() - Config.myHostApiUrl.request(u, "username=${ + Config.api.request(u, "username=${ URLEncoder.encode( username, Charset.defaultCharset().name() diff --git a/app/src/main/java/top/fumiama/copymanga/net/DownloadTools.kt b/app/src/main/java/top/fumiama/copymanga/net/DownloadTools.kt index fa7608f..22e0a2e 100644 --- a/app/src/main/java/top/fumiama/copymanga/net/DownloadTools.kt +++ b/app/src/main/java/top/fumiama/copymanga/net/DownloadTools.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.json.JSONObject import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config.proxyUrl import top.fumiama.copymanga.json.ComandyCapsule import top.fumiama.copymanga.lib.Comandy +import java.io.BufferedReader import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream @@ -146,6 +148,19 @@ object DownloadTools { } 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 { val buffer = ByteArrayOutputStream(maxOf(DEFAULT_BUFFER_SIZE, this.available())) copyToWithProgress(buffer, sz, p) @@ -184,7 +199,7 @@ object DownloadTools { //Log.d("MyDT", "comandy reply: $result") Gson().fromJson(result, ComandyCapsule::class.java)!!.let { 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? Base64.decode(it.data, Base64.DEFAULT) @@ -198,10 +213,14 @@ object DownloadTools { } getApiConnection(u, "GET").let { conn -> 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) { - conn.inputStream.readBytesWithProgress(sz, p) + conn.inputStream.use { it.readBytesWithProgress(sz, p) } } else { - conn.inputStream.readBytes() + conn.inputStream.use(InputStream::readBytes) } conn.disconnect() Log.d("MyDT", "getHttpContent: ${ret.size} bytes") @@ -255,7 +274,9 @@ object DownloadTools { completed = true p?.notify(100) 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) } } } @@ -272,17 +293,17 @@ object DownloadTools { var ret: ByteArray? = null try { val connection = getNormalConnection(u, "GET", Config.pc_ua) - val ci = connection.inputStream val sz = connection.getHeaderFieldInt("Content-Length", 0) - if(readSize > 0) { - ret = ByteArray(readSize) - ci?.read(ret, 0, readSize) - } else ret = if (sz > 0 && p != null) { - ci.readBytesWithProgress(sz, p) - } else { - ci.readBytes() + connection.inputStream.use { ci -> + if(readSize > 0) { + ret = ByteArray(readSize) + ci.read(ret, 0, readSize) + } else ret = if (sz > 0 && p != null) { + ci.readBytesWithProgress(sz, p) + } else { + ci.readBytes() + } } - ci?.close() connection.disconnect() } catch (ex: Exception) { ex.printStackTrace() @@ -313,7 +334,11 @@ object DownloadTools { var coding = "" getApiConnection(url, method).apply { 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() coding = getHeaderField("Content-Encoding")?:"" } diff --git a/app/src/main/java/top/fumiama/copymanga/net/template/AutoDownloadHandler.kt b/app/src/main/java/top/fumiama/copymanga/net/template/AutoDownloadHandler.kt index 789d698..69dccfa 100644 --- a/app/src/main/java/top/fumiama/copymanga/net/template/AutoDownloadHandler.kt +++ b/app/src/main/java/top/fumiama/copymanga/net/template/AutoDownloadHandler.kt @@ -76,7 +76,7 @@ open class AutoDownloadHandler( var cnt = 0 while (cnt++ <= 3) { try { - val data = Config.myHostApiUrl.get(url()) + val data = Config.api.get(url()) if(exit) return@withContext val pass = setGsonItem(Gson().fromJson(data, jsonClass)) if (pass && loadFromCache) { diff --git a/app/src/main/java/top/fumiama/copymanga/net/template/PausableDownloader.kt b/app/src/main/java/top/fumiama/copymanga/net/template/PausableDownloader.kt index 5f3cd76..4bfab13 100644 --- a/app/src/main/java/top/fumiama/copymanga/net/template/PausableDownloader.kt +++ b/app/src/main/java/top/fumiama/copymanga/net/template/PausableDownloader.kt @@ -13,7 +13,7 @@ class PausableDownloader(private val url: String, private val waitMilliseconds: var c = 0 while (!exit && c++ < 3) { try { - val data = Config.myHostApiUrl.get(url) + val data = Config.api.get(url) whenFinish?.let { it(data.encodeToByteArray()) } return@withContext true } catch (e: Exception) { diff --git a/app/src/main/java/top/fumiama/copymanga/storage/DataLoader.kt b/app/src/main/java/top/fumiama/copymanga/storage/DataLoader.kt new file mode 100644 index 0000000..4b2bf8d --- /dev/null +++ b/app/src/main/java/top/fumiama/copymanga/storage/DataLoader.kt @@ -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 + } + +} \ No newline at end of file diff --git a/app/src/main/java/top/fumiama/copymanga/storage/PreferenceBoolean.kt b/app/src/main/java/top/fumiama/copymanga/storage/PreferenceBoolean.kt index 6a238f3..c9c93e8 100644 --- a/app/src/main/java/top/fumiama/copymanga/storage/PreferenceBoolean.kt +++ b/app/src/main/java/top/fumiama/copymanga/storage/PreferenceBoolean.kt @@ -1,20 +1,30 @@ package top.fumiama.copymanga.storage import android.util.Log +import androidx.core.content.edit import androidx.preference.PreferenceManager import top.fumiama.copymanga.MainActivity data class PreferenceBoolean(private val key: String, private var default: Boolean) { - val value: Boolean + var value: Boolean = default get() { MainActivity.mainWeakReference?.get()?.let { PreferenceManager.getDefaultSharedPreferences(it).apply { - getBoolean(key, default).let { v -> + getBoolean(key, field).let { v -> Log.d("MyPB", "get key $key value $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) } + } + } } } diff --git a/app/src/main/java/top/fumiama/copymanga/storage/PreferenceInt.kt b/app/src/main/java/top/fumiama/copymanga/storage/PreferenceInt.kt index 35ef698..0e72df1 100644 --- a/app/src/main/java/top/fumiama/copymanga/storage/PreferenceInt.kt +++ b/app/src/main/java/top/fumiama/copymanga/storage/PreferenceInt.kt @@ -1,20 +1,30 @@ package top.fumiama.copymanga.storage import android.util.Log +import androidx.core.content.edit import androidx.preference.PreferenceManager import top.fumiama.copymanga.MainActivity data class PreferenceInt(private val key: String, private var default: Int) { - val value: Int + var value: Int = default get() { MainActivity.mainWeakReference?.get()?.let { PreferenceManager.getDefaultSharedPreferences(it).apply { - getInt(key, default).let { v -> + getInt(key, field).let { v -> Log.d("MyPI", "get key $key value $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) } + } + } } } diff --git a/app/src/main/java/top/fumiama/copymanga/storage/PreferenceString.kt b/app/src/main/java/top/fumiama/copymanga/storage/PreferenceString.kt index 0ca6c8b..94dedae 100644 --- a/app/src/main/java/top/fumiama/copymanga/storage/PreferenceString.kt +++ b/app/src/main/java/top/fumiama/copymanga/storage/PreferenceString.kt @@ -1,6 +1,7 @@ package top.fumiama.copymanga.storage import android.util.Log +import androidx.core.content.edit import androidx.preference.PreferenceManager import top.fumiama.copymanga.MainActivity @@ -20,7 +21,7 @@ data class PreferenceString(private val key: String, private var default: String } return default?:"" } - val value: String + var value: String = defaultField get() { MainActivity.mainWeakReference?.get()?.let { 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") - return defaultField + Log.d("MyPS", "get default key $key value $field") + 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) } + } + } } } diff --git a/app/src/main/java/top/fumiama/copymanga/storage/PropertiesTools.kt b/app/src/main/java/top/fumiama/copymanga/storage/PropertiesTools.kt deleted file mode 100644 index 5d79fe2..0000000 --- a/app/src/main/java/top/fumiama/copymanga/storage/PropertiesTools.kt +++ /dev/null @@ -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() - - 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") - } -} diff --git a/app/src/main/java/top/fumiama/copymanga/strings/Base16384.kt b/app/src/main/java/top/fumiama/copymanga/strings/Base16384.kt new file mode 100644 index 0000000..9e7fec1 --- /dev/null +++ b/app/src/main/java/top/fumiama/copymanga/strings/Base16384.kt @@ -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() + 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 + } +} diff --git a/app/src/main/java/top/fumiama/copymanga/strings/Chinese.kt b/app/src/main/java/top/fumiama/copymanga/strings/Chinese.kt index ede19ec..4bd26ef 100644 --- a/app/src/main/java/top/fumiama/copymanga/strings/Chinese.kt +++ b/app/src/main/java/top/fumiama/copymanga/strings/Chinese.kt @@ -28,9 +28,8 @@ object Chinese { /** * 简单检测字符串是否包含常见 CJK(中日韩)汉字。 */ - fun containsChinese(text: String): Boolean { + private fun containsChinese(text: String): Boolean { val regex = Regex("[\u4E00-\u9FFF]") return regex.containsMatchIn(text) } - } \ No newline at end of file diff --git a/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt index cff08e2..6caa2f5 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt @@ -11,6 +11,7 @@ import android.widget.Toast import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController 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.card_book.* import kotlinx.android.synthetic.main.fragment_book.* @@ -57,8 +58,9 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { } catch (e: Exception) { e.printStackTrace() if(mBookHandler?.exit != false) return@launch - Toast.makeText(context, R.string.null_book, Toast.LENGTH_SHORT).show() - findNavController().popBackStack() + Snackbar.make(view, "${e::class.simpleName} ${e.message}", Snackbar.LENGTH_LONG).setTextMaxLines(10).setDuration(10000).show() + // Toast.makeText(context, R.string.null_book, Toast.LENGTH_SHORT).show() + // findNavController().popBackStack() return@launch } Log.d("MyBF", "read path: ${book?.path}") @@ -196,9 +198,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { } } catch (e: Exception) { withContext(Dispatchers.Main) { - activity?.runOnUiThread { - Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_SHORT).show() - } + Toast.makeText(context, "${e::class.simpleName} ${e.message}", Toast.LENGTH_LONG).show() } } } @@ -239,7 +239,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) { } } catch (e: Exception) { 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) { 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() } } } diff --git a/app/src/main/java/top/fumiama/copymanga/ui/home/HomeFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/home/HomeFragment.kt index 26dfe6d..5e32e09 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/home/HomeFragment.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/home/HomeFragment.kt @@ -10,13 +10,13 @@ import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.ImageButton -import android.widget.Toast import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.bumptech.glide.load.model.GlideUrl +import com.google.android.material.snackbar.Snackbar import com.google.gson.Gson import com.lapism.search.internal.SearchLayout import kotlinx.android.synthetic.main.card_book_plain.view.* @@ -29,12 +29,12 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import top.fumiama.copymanga.MainActivity 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.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.operation.GlideHideLottieViewListener +import top.fumiama.copymanga.view.template.NoBackRefreshFragment import top.fumiama.dmzj.copymanga.R import java.lang.ref.WeakReference import java.net.URLEncoder @@ -51,18 +51,15 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) { val netInfo = tb.netInfo if(netInfo != tb.transportStringNull && netInfo != tb.transportStringError) MainActivity.member?.apply { lifecycleScope.launch { - Config.myHostApiUrl.init() - 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() + withContext(Dispatchers.IO) { + Config.api.init() + try { + info() + } catch (e: Exception) { + Snackbar + .make(view, "${e::class.simpleName} ${e.message}", Snackbar.LENGTH_LONG) + .setTextMaxLines(10) + .show() } } } } diff --git a/app/src/main/java/top/fumiama/copymanga/ui/vm/VMHandler.kt b/app/src/main/java/top/fumiama/copymanga/ui/vm/VMHandler.kt index 2101100..955063b 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/vm/VMHandler.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/vm/VMHandler.kt @@ -148,9 +148,7 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri override suspend fun onError() { super.onError() if(exit) return - withContext(Dispatchers.Main) { - wv.get()?.toolsBox?.toastError(R.string.download_chapter_info_failed) - } + wv.get()?.toolsBox?.toastErrorAndFinish(R.string.download_chapter_info_failed) } override suspend fun doWhenFinishDownload() { @@ -187,7 +185,7 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri true } catch (e: Exception){ 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 } } diff --git a/app/src/main/java/top/fumiama/copymanga/ui/vm/ViewMangaActivity.kt b/app/src/main/java/top/fumiama/copymanga/ui/vm/ViewMangaActivity.kt index 96e2d3e..42b3126 100644 --- a/app/src/main/java/top/fumiama/copymanga/ui/vm/ViewMangaActivity.kt +++ b/app/src/main/java/top/fumiama/copymanga/ui/vm/ViewMangaActivity.kt @@ -78,7 +78,6 @@ class ViewMangaActivity : TitleActivityTemplate() { var clicked = 0 private var isInSeek = false private var isInScroll = true - //private var progressLog: PropertiesTools? = null var scrollImages = arrayOf() var scrollButtons = arrayOf