diff --git a/.idea/dictionaries/fumiama.xml b/.idea/dictionaries/fumiama.xml
index 3070a10..c80f935 100644
--- a/.idea/dictionaries/fumiama.xml
+++ b/.idea/dictionaries/fumiama.xml
@@ -5,6 +5,7 @@
azurewebsites
comancry
comandy
+ deviceinfo
downloaders
grps
hotmanga
@@ -20,6 +21,7 @@
reclass
reilia
systembar
+ umstring
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index a8a0bd5..759a97d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,9 +10,10 @@ android {
compileSdk 34
applicationId 'top.fumiama.copymanga'
minSdkVersion 23
+ //noinspection OldTargetApi
targetSdkVersion 34
- versionCode 72
- versionName '2.5.1'
+ versionCode 73
+ versionName '2.5.2'
resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -47,7 +48,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
- /*winrelease {
+ /*winrelease {r
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
@@ -98,7 +99,7 @@ dependencies {
//noinspection GradleDependency
implementation 'androidx.core:core-ktx:1.12.0'
- implementation 'androidx.appcompat:appcompat:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
diff --git a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt
index 337fc87..fb18378 100644
--- a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt
+++ b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt
@@ -157,6 +157,9 @@ class MainActivity : AppCompatActivity() {
isMenuWaiting = true
Log.d("MyMain", "start menu waiting")
lifecycleScope.launch {
+ withContext(Dispatchers.IO) {
+ Config.myHostApiUrl.init()
+ }
withContext(Dispatchers.IO) {
delay(1000)
withContext(Dispatchers.Main) {
@@ -430,7 +433,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.joinToString(", ")}")
+ "当前API: ${Config.myHostApiUrl.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 0e8a9b8..3fc03be 100644
--- a/app/src/main/java/top/fumiama/copymanga/api/Config.kt
+++ b/app/src/main/java/top/fumiama/copymanga/api/Config.kt
@@ -1,14 +1,8 @@
package top.fumiama.copymanga.api
-import android.util.Log
import com.bumptech.glide.load.model.LazyHeaders
-import com.google.gson.Gson
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
import top.fumiama.copymanga.MainActivity
-import top.fumiama.copymanga.json.NetworkStructure
-import top.fumiama.copymanga.net.DownloadTools
+import top.fumiama.copymanga.api.network.Api
import top.fumiama.copymanga.net.Proxy
import top.fumiama.copymanga.net.Resolution
import top.fumiama.copymanga.storage.PreferenceBoolean
@@ -61,49 +55,9 @@ object Config {
}
val proxyUrl = MainActivity.mainWeakReference?.get()?.getString(R.string.proxyUrl)!!
- private val reverseProxyUrl = PreferenceString(R.string.reverseProxyKeyID)
- private val networkApiUrl = PreferenceString("settings_cat_net_et_api_url", R.string.hostUrl)
- private var mHostApiUrls: Array = arrayOf()
- private var mHostApiUrlsMutex = Mutex()
- val myHostApiUrl: Array
- get() {
- if (mHostApiUrls.isNotEmpty()) return mHostApiUrls
- if (reverseProxyUrl.value.isNotEmpty() && reverseProxyUrl.value != proxyUrl) {
- mHostApiUrls = arrayOf(reverseProxyUrl.value)
- Log.d("MyC", "myHostApiUrl set reverse proxy to ${mHostApiUrls[0]}")
- return mHostApiUrls
- }
- MainActivity.mainWeakReference?.get()?.apply {
- runBlocking {
- mHostApiUrlsMutex.withLock {
- if (mHostApiUrls.isNotEmpty()) return@runBlocking
- try {
- val u = getString(R.string.networkApiUrl).format(networkApiUrl.value, platform.value)
- val r = Gson().fromJson((apiProxy?.comancry(u) {
- DownloadTools.getHttpContent(it, referer, pc_ua)
- }?:DownloadTools.getHttpContent(u, referer, pc_ua)).decodeToString(), NetworkStructure::class.java)
- if (r != null) {
- Log.d("MyC", "myHostApiUrl get code ${r.code} msg ${r.message}")
- if (r.code == 200 && r.results != null) {
- // need header umstring
- // r.results.api.forEach { it.forEach { api -> if (!api.isNullOrEmpty() && api !in field) field += api } }
- r.results.share.forEach { api -> if (!api.isNullOrEmpty() && api !in mHostApiUrls) mHostApiUrls += api }
- }
- }
- } catch (e: Exception) {
- e.printStackTrace()
- mHostApiUrls = arrayOf(networkApiUrl.value)
- }
- if (mHostApiUrls.isEmpty()) {
- mHostApiUrls = arrayOf(networkApiUrl.value)
- Log.d("MyC", "myHostApiUrl set default ${mHostApiUrls[0]}")
- }
- }
- }
- }
- Log.d("MyC", "myHostApiUrl get hosts ${mHostApiUrls.joinToString(", ")}")
- return mHostApiUrls
- }
+ 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)
@@ -130,6 +84,7 @@ object Config {
private 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")
val view_manga_inverse_chapters = PreferenceBoolean("settings_cat_vm_sw_inverse_chapters", false)
val view_manga_always_dark_bg = PreferenceBoolean("settings_cat_vm_sw_always_dark_bg", false)
@@ -144,5 +99,5 @@ object Config {
fun getChapterInfoApiUrl(path: String?, uuid: String?, version: Int) =
MainActivity.mainWeakReference?.get()?.getString(R.string.chapterInfoApiUrl)
- ?.format(myHostApiUrl.random(), path, if (version >= 2) "$version" else "" , uuid, platform.value)
+ ?.format(path, if (version >= 2) "$version" else "" , uuid, platform.value)
}
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 1eb276b..f8b03ff 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
@@ -1,11 +1,13 @@
package top.fumiama.copymanga.api.manga
import android.util.Log
+import android.widget.Toast
import com.google.gson.Gson
import kotlinx.android.synthetic.main.card_book.*
import kotlinx.android.synthetic.main.line_booktandb.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.json.BookInfoStructure
import top.fumiama.copymanga.json.ThemeStructure
import top.fumiama.copymanga.json.VolumeStructure
@@ -15,8 +17,7 @@ import top.fumiama.dmzj.copymanga.R
import java.io.File
class Book(val path: String, private val getString: (Int) -> String, private val exDir: File, private val loadCache: Boolean = false, private val mPassName: String? = null) {
- private val mBookApiUrl = getString(R.string.bookInfoApiUrl).format(Config.myHostApiUrl.random(), path, Config.platform.value)
- private val mUserAgent = getString(R.string.pc_ua).format(Config.app_ver.value)
+ private val mBookApiUrl = getString(R.string.bookInfoApiUrl).format(path, Config.platform.value)
private var mBook: BookInfoStructure? = null
private var mGroupPathWords = arrayOf()
private var mKeys = arrayOf()
@@ -57,22 +58,16 @@ class Book(val path: String, private val getString: (Int) -> String, private val
suspend fun updateInfo() = withContext(Dispatchers.IO) {
try {
var isDownload = false
- val data: ByteArray = if (loadCache) {
+ val data: String = if (loadCache) {
name?.let { loadInfo(it) } ?: run {
isDownload = true
- Config.apiProxy?.comancry(mBookApiUrl) { url ->
- DownloadTools.getHttpContent(url, null, mUserAgent)
- }?:DownloadTools.getHttpContent(mBookApiUrl, null, mUserAgent)
+ Config.myHostApiUrl.get(mBookApiUrl)
}
} else {
isDownload = true
- Config.apiProxy?.comancry(mBookApiUrl) { url ->
- DownloadTools.getHttpContent(url, null, mUserAgent)
- }?:DownloadTools.getHttpContent(mBookApiUrl, null, mUserAgent)
- }?:DownloadTools.getHttpContent(mBookApiUrl, null, mUserAgent)
- mBook = data.inputStream().use {
- Gson().fromJson(it.reader(), BookInfoStructure::class.java)
+ Config.myHostApiUrl.get(mBookApiUrl)
}
+ mBook = Gson().fromJson(data, BookInfoStructure::class.java)
if (isDownload) saveInfo(data)
mGroupPathWords = arrayOf()
mKeys = arrayOf()
@@ -88,6 +83,9 @@ class Book(val path: String, private val getString: (Int) -> String, private val
}
} catch (e: Exception) {
e.printStackTrace()
+ MainActivity.mainWeakReference?.get()?.apply {
+ Toast.makeText(this, e.message, Toast.LENGTH_SHORT).show()
+ }
}
}
@@ -162,19 +160,19 @@ class Book(val path: String, private val getString: (Int) -> String, private val
}?:false
}
- private suspend fun saveInfo(data: ByteArray) = withContext(Dispatchers.IO) {
+ private suspend fun saveInfo(data: String) = withContext(Dispatchers.IO) {
name?.let { name ->
val mangaFolder = File(exDir, name)
if(!mangaFolder.exists()) mangaFolder.mkdirs()
- File(mangaFolder, "meta.json").writeBytes(data)
+ File(mangaFolder, "meta.json").writeText(data)
}
}
- private suspend fun loadInfo(name: String): ByteArray? = withContext(Dispatchers.IO) {
+ private suspend fun loadInfo(name: String): String? = withContext(Dispatchers.IO) {
val mangaFolder = File(exDir, name)
if(!mangaFolder.exists()) mangaFolder.mkdirs()
val f = File(mangaFolder, "meta.json")
if (!f.exists()) return@withContext null
- return@withContext f.readBytes()
+ return@withContext f.readBytes().decodeToString()
}
}
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 9ecb5a8..ec850e2 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
@@ -3,14 +3,13 @@ package top.fumiama.copymanga.api.manga
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.json.BookQueryStructure
import top.fumiama.copymanga.json.ReturnBase
-import top.fumiama.copymanga.api.Config
-import top.fumiama.copymanga.net.DownloadTools
import top.fumiama.dmzj.copymanga.R
class Shelf(private val getString: (Int) -> String) {
- private val apiUrl: String get() = getString(R.string.shelfOperateApiUrl).format(Config.myHostApiUrl.random())
+ private val apiUrl: String get() = getString(R.string.shelfOperateApiUrl)
private val queryApiUrlTemplate = getString(R.string.bookUserQueryApiUrl)
private val addApiUrl get() = "$apiUrl?platform=${Config.platform.value}"
private val delApiUrl get() = "${apiUrl}s?platform=${Config.platform.value}"
@@ -25,17 +24,13 @@ class Shelf(private val getString: (Int) -> String) {
append("")
append(Config.token.value)
}
- val re = (Config.apiProxy?.comancry(addApiUrl) { url ->
- DownloadTools.requestWithBody(
- url, "POST", body.encodeToByteArray()
- )
- }?:DownloadTools.requestWithBody(
- addApiUrl, "POST", body.encodeToByteArray()
- ))?.decodeToString() ?: return@withContext "空回应"
return@withContext try {
+ val re = Config.myHostApiUrl.request(
+ addApiUrl, body.encodeToByteArray(), "POST",
+ "application/x-www-form-urlencoded;charset=utf-8")
Gson().fromJson(re, ReturnBase::class.java).message
} catch (e: Exception) {
- "$re ${e.message}"
+ e.message?:e::class.simpleName?:e.toString()
}
}
@@ -52,31 +47,20 @@ class Shelf(private val getString: (Int) -> String) {
append("authorization=Token+")
append(Config.token.value)
}
- val re = (Config.apiProxy?.comancry(delApiUrl) { url ->
- DownloadTools.requestWithBody(
- url, "DELETE", body.encodeToByteArray()
- )
- }?:DownloadTools.requestWithBody(
- delApiUrl, "DELETE", body.encodeToByteArray()
- ))?.decodeToString() ?: return@withContext "空回应"
return@withContext try {
+ val re = Config.myHostApiUrl.request(
+ delApiUrl, body.encodeToByteArray(),
+ "DELETE", "application/x-www-form-urlencoded;charset=utf-8")
Gson().fromJson(re, ReturnBase::class.java).message
} catch (e: Exception) {
- "$re ${e.message}"
+ e.message?:e::class.simpleName?:e.toString()
}
}
suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
- try {
- val queryUrl = queryApiUrlTemplate.format(Config.myHostApiUrl.random(), pathWord, Config.platform.value)
- (Config.apiProxy?.comancry(queryUrl) { url ->
- DownloadTools.getHttpContent(url, Config.referer)
- }?:DownloadTools.getHttpContent(queryUrl, Config.referer)).let {
- Gson().fromJson(it.decodeToString(), BookQueryStructure::class.java)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- null
+ val queryUrl = queryApiUrlTemplate.format(pathWord, Config.platform.value)
+ Config.myHostApiUrl.get(queryUrl).let {
+ Gson().fromJson(it, BookQueryStructure::class.java)
}
}
}
diff --git a/app/src/main/java/top/fumiama/copymanga/api/manga/Volume.kt b/app/src/main/java/top/fumiama/copymanga/api/manga/Volume.kt
index 5da20dc..0eb38c8 100644
--- a/app/src/main/java/top/fumiama/copymanga/api/manga/Volume.kt
+++ b/app/src/main/java/top/fumiama/copymanga/api/manga/Volume.kt
@@ -39,7 +39,7 @@ class Volume(private val path: String, private val groupPathWord: String, getStr
return@withContext mVolume
}
- private fun getApiUrl(offset: Int) = mGroupInfoApiUrlTemplate.format(Config.myHostApiUrl.random(), path, groupPathWord, offset, Config.platform.value)
+ private fun getApiUrl(offset: Int) = mGroupInfoApiUrlTemplate.format(path, groupPathWord, offset, Config.platform.value)
private suspend fun download(re: Array, offset: Int, c: Int) = withContext(Dispatchers.IO) {
Log.d("MyV", "下载偏移: $offset")
getApiUrl(offset).let {
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
new file mode 100644
index 0000000..577b6fa
--- /dev/null
+++ b/app/src/main/java/top/fumiama/copymanga/api/network/Api.kt
@@ -0,0 +1,112 @@
+package top.fumiama.copymanga.api.network
+
+import android.util.Log
+import android.widget.Toast
+import com.google.gson.Gson
+import top.fumiama.copymanga.MainActivity
+import top.fumiama.copymanga.api.Config.apiProxy
+import top.fumiama.copymanga.api.Config.networkApiUrl
+import top.fumiama.copymanga.api.Config.platform
+import top.fumiama.copymanga.api.Config.proxyUrl
+import top.fumiama.copymanga.api.Config.reverseProxyUrl
+import top.fumiama.copymanga.json.NetworkStructure
+import top.fumiama.copymanga.json.ReturnBase
+import top.fumiama.copymanga.net.DownloadTools
+import top.fumiama.dmzj.copymanga.R
+import java.util.concurrent.locks.ReentrantReadWriteLock
+import kotlin.concurrent.read
+import kotlin.concurrent.write
+
+class Api {
+ private var mHostApiUrls = mutableListOf()
+ private var mu = ReentrantReadWriteLock()
+
+ fun getApis(): Array {
+ return mHostApiUrls.toTypedArray()
+ }
+
+ suspend fun init() {
+ if (mHostApiUrls.isNotEmpty()) return
+ if (reverseProxyUrl.value.isNotEmpty() && reverseProxyUrl.value != proxyUrl) {
+ mu.write { mHostApiUrls = mutableListOf(reverseProxyUrl.value) }
+ Log.d("MyApi", "myHostApiUrl set reverse proxy to ${reverseProxyUrl.value}")
+ return
+ }
+ MainActivity.mainWeakReference?.get()?.apply {
+ mu.write {
+ if (mHostApiUrls.isNotEmpty()) return
+ try {
+ val d = get(getString(R.string.networkApiUrl).format(platform.value), networkApiUrl.value)
+ val r = Gson().fromJson(d, NetworkStructure::class.java)
+ if (r != null) {
+ Log.d("MyApi", "myHostApiUrl get code ${r.code} msg ${r.message}")
+ if (r.results != null) {
+ r.results.api.forEach { it.forEach { api -> if (!api.isNullOrEmpty() && api !in mHostApiUrls) mHostApiUrls += api } }
+ r.results.share.forEach { api -> if (!api.isNullOrEmpty() && api !in mHostApiUrls) mHostApiUrls += api }
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ mHostApiUrls = mutableListOf(networkApiUrl.value)
+ Toast.makeText(this, e.message, Toast.LENGTH_SHORT).show()
+ }
+ if (mHostApiUrls.isEmpty()) {
+ mHostApiUrls = mutableListOf(networkApiUrl.value)
+ Log.d("MyApi", "myHostApiUrl set default ${mHostApiUrls[0]}")
+ Toast.makeText(this, "无法获取API列表", Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+ Log.d("MyApi", "myHostApiUrl get hosts ${mHostApiUrls.joinToString(", ")}")
+ }
+ // get throw error on non-json or non-200 or empty apis, path: /api/v3/xxx, return json string
+ suspend fun get(path: String, forceApi: String? = null): String {
+ val apis = if (forceApi == null) mu.read { mHostApiUrls } else mutableListOf(forceApi)
+ if (apis.isEmpty()) {
+ throw NoSuchElementException("API列表为空")
+ }
+ var r: ReturnBase? = null
+ apis.forEach { api ->
+ val u = "https://$api$path"
+ try {
+ val ret = (apiProxy?.comancry(u) {
+ DownloadTools.getApiContent(it)
+ }?: DownloadTools.getApiContent(u)).decodeToString()
+ r = Gson().fromJson(ret, ReturnBase::class.java)
+ if (r!!.code != 200) {
+ mu.write { mHostApiUrls.remove(api) }
+ } else {
+ return ret
+ }
+ } catch (e: Exception) {
+ mu.write { mHostApiUrls.remove(api) }
+ }
+ }
+ throw IllegalStateException("错误码${r!!.code}, 信息: ${r!!.message}")
+ }
+ // request throw error on non-json or non-200 or empty apis, path: /api/v3/xxx, return json string
+ suspend fun request(path: String, body: ByteArray, method: String, contentType: String, forceApi: String? = null): String {
+ val apis = if (forceApi == null) mu.read { mHostApiUrls } else mutableListOf(forceApi)
+ if (apis.isEmpty()) {
+ throw NoSuchElementException("API列表为空")
+ }
+ var r: ReturnBase? = null
+ apis.forEach { api ->
+ val u = "https://$api$path"
+ try {
+ val 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) {
+ mu.write { mHostApiUrls.remove(api) }
+ } else {
+ return ret
+ }
+ } catch (e: Exception) {
+ mu.write { mHostApiUrls.remove(api) }
+ }
+ }
+ throw IllegalStateException("错误码${r!!.code}, 信息: ${r!!.message}")
+ }
+}
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 3925574..637c494 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
@@ -5,10 +5,7 @@ import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.api.Config
-import top.fumiama.copymanga.json.ComandyCapsule
import top.fumiama.copymanga.json.LoginInfoStructure
-import top.fumiama.copymanga.lib.Comandy
-import top.fumiama.copymanga.net.DownloadTools
import top.fumiama.dmzj.copymanga.R
import java.net.URLEncoder
import java.nio.charset.Charset
@@ -17,23 +14,16 @@ class Member(private val getString: (Int) -> String) {
val hasLogin: Boolean get() = Config.token.value?.isNotEmpty() ?: false
suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure =
withContext(Dispatchers.IO) {
- var err = ""
- (if (!Config.net_use_api_proxy.value && Comandy.instance.enabled)
- postComandyLogin(username, pwd, salt)
- else postLogin(username, pwd, salt))?.let { data ->
- try {
- return@withContext saveInfo(data)
- } catch (e: Exception) {
- err = e.message.toString()
- }
- } ?: run { err = getString(R.string.login_get_conn_failed) }
- val l = LoginInfoStructure()
- l.code = 400
- l.message = err
- return@withContext l
+ return@withContext try {
+ saveInfo(postLogin(username, pwd, salt))
+ } catch (e: Exception) {
+ val l = LoginInfoStructure()
+ l.code = 400
+ l.message = e.message.toString()
+ l
+ }
}
-
/**
* 获得登录信息并更新头像
* @return 登录态
@@ -49,24 +39,21 @@ class Member(private val getString: (Int) -> String) {
}
try {
val u = getString(R.string.memberInfoApiUrl)
- .format(Config.myHostApiUrl.random(), Config.platform.value)
- val data = (Config.apiProxy?.comancry(u) {
- DownloadTools.getHttpContent(it)
- }?:DownloadTools.getHttpContent(u)).decodeToString()
+ .format(Config.platform.value)
try {
- val l = Gson().fromJson(data, LoginInfoStructure::class.java)
+ val l = Gson().fromJson(Config.myHostApiUrl.get(u), LoginInfoStructure::class.java)
if (l.code == 200) Config.avatar.value = l.results.avatar
l
} catch (e: Exception) {
val l = LoginInfoStructure()
l.code = 450
- l.message = "${getString(R.string.login_get_avatar_failed)}: $data"
+ l.message = "${getString(R.string.login_get_avatar_failed)}: ${e.message}"
l
}
} catch (e: Exception) {
val l = LoginInfoStructure()
l.code = 450
- l.message = "${getString(R.string.login_get_avatar_failed)}: $e"
+ l.message = "${getString(R.string.login_get_avatar_failed)}: ${e.message}"
l
}
}
@@ -96,69 +83,17 @@ class Member(private val getString: (Int) -> String) {
}
}
- private suspend fun postLogin(username: String, pwd: String, salt: Int): ByteArray? =
- getString(R.string.loginApiUrl).format(Config.myHostApiUrl.random(), Config.platform.value).let { u ->
- val use: suspend (String) -> ByteArray? = { it: String ->
- DownloadTools.getApiConnection(it, "POST").let { c ->
- c.doOutput = true
- c.setRequestProperty(
- "content-type",
- "application/x-www-form-urlencoded;charset=utf-8"
- )
- c.setRequestProperty("platform", Config.platform.value)
- c.setRequestProperty("accept", "application/json")
- val r = if (!Config.net_use_foreign.value) "1" else "0"
- val pwdEncoded =
- Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
- c.outputStream.write(
- "username=${
- URLEncoder.encode(
- username,
- Charset.defaultCharset().name()
- )
- }&password=$pwdEncoded&salt=$salt&platform=${Config.platform.value}&authorization=Token+&version=${Config.app_ver.value}&source=copyApp®ion=$r&webp=1".toByteArray()
- )
- c.outputStream.close()
- val b = c.inputStream.readBytes()
- c.inputStream.close()
- b
- }
- }
- Config.apiProxy?.comancry(u, use)?:use(u)
- }
-
-
- private suspend fun postComandyLogin(username: String, pwd: String, salt: Int) =
- getString(R.string.loginApiUrl).format(Config.myHostApiUrl.random(), Config.platform.value).let { u ->
- val use: suspend (String) -> ByteArray? = { it: String ->
- DownloadTools.getComandyApiConnection(it, "POST", null, Config.pc_ua).apply {
- headers["content-type"] = "application/x-www-form-urlencoded;charset=utf-8"
- headers["platform"] = Config.platform.value
- headers["accept"] = "application/json"
- val r = if (!Config.net_use_foreign.value) "1" else "0"
- val pwdEncoded =
- Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
- data = "username=${
- URLEncoder.encode(
- username,
- Charset.defaultCharset().name()
- )
- }&password=$pwdEncoded&salt=$salt&platform=${Config.platform.value}&authorization=Token+&version=${Config.app_ver.value}&source=copyApp®ion=$r&webp=1"
- }.let { capsule ->
- try {
- val para = Gson().toJson(capsule)
- Comandy.instance.getInstance()?.request(para)?.let { result ->
- Gson().fromJson(result, ComandyCapsule::class.java)!!.let {
- if (it.code != 200) null
- else Base64.decode(it.data, Base64.DEFAULT)
- }
- }
- } catch (e: Exception) {
- e.printStackTrace()
- null
- }
- }
- }
- Config.apiProxy?.comancry(u, use)?:use(u)
- }
+ private suspend fun postLogin(username: String, pwd: String, salt: Int): ByteArray =
+ getString(R.string.loginApiUrl).format(Config.platform.value).let { u ->
+ 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=${
+ URLEncoder.encode(
+ username,
+ Charset.defaultCharset().name()
+ )
+ }&password=$pwdEncoded&salt=$salt&platform=${Config.platform.value}&authorization=Token+&version=${Config.app_ver.value}&source=copyApp®ion=$r&webp=1".encodeToByteArray(),
+ "POST", "application/x-www-form-urlencoded;charset=utf-8")
+ }.encodeToByteArray()
}
diff --git a/app/src/main/java/top/fumiama/copymanga/lib/Comandy.kt b/app/src/main/java/top/fumiama/copymanga/lib/Comandy.kt
index ad302a8..ed354d4 100644
--- a/app/src/main/java/top/fumiama/copymanga/lib/Comandy.kt
+++ b/app/src/main/java/top/fumiama/copymanga/lib/Comandy.kt
@@ -3,27 +3,18 @@ package top.fumiama.copymanga.lib
import android.util.Log
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.lib.template.LazyLibrary
-import top.fumiama.copymanga.net.DownloadTools
class Comandy: LazyLibrary(
ComandyMethods::class.java, "libcomandy.so", "网络增强",
Config.net_use_comandy, Config.comandy_version
) {
- private var mEnabled: Boolean? = null
val enabled: Boolean
get() {
if (isInInit.get()) {
Log.d("MyComandy", "$name block enabled for isInInit")
return false
}
- if (mEnabled != true && DownloadTools.failTimes.get() >= 16) {
- mEnabled = true
- return true
- }
- if (mEnabled != null) return mEnabled!!
- val v = isInUse.value
- mEnabled = v
- return v
+ return isInUse.value
}
val status: String get() = if(enabled) {
if (isInUse.value) "生效(手动)" else "生效(自动)"
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 348d3f5..52266a9 100644
--- a/app/src/main/java/top/fumiama/copymanga/net/DownloadTools.kt
+++ b/app/src/main/java/top/fumiama/copymanga/net/DownloadTools.kt
@@ -6,11 +6,11 @@ import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
-import top.fumiama.copymanga.MainActivity
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.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.io.OutputStream
@@ -21,61 +21,63 @@ import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Callable
import java.util.concurrent.FutureTask
-import java.util.concurrent.atomic.AtomicInteger
+import java.util.zip.GZIPInputStream
object DownloadTools {
- val failTimes = AtomicInteger(0)
- fun getApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null, timeout: Int = 20000): HttpURLConnection {
+ private fun getApiConnection(url: String, method: String = "GET", timeout: Int = 20000): HttpURLConnection {
val connection = URL(url).openConnection() as HttpURLConnection
connection.requestMethod = method
connection.connectTimeout = timeout
connection.readTimeout = timeout
connection.apply {
- /*setRequestProperty("host", if (url.startsWith("https://copymanga.azurewebsites.net")) {
- Uri.parse(url).getQueryParameter("url")?.substringAfter("://")?.substringBefore("/")?:""
- } else {
- url.substringAfter("://").substringBefore("/")
- })*/
- ua?.let { setRequestProperty("user-agent", it) }
- refer?.let { setRequestProperty("referer", it) }
+ setRequestProperty("user-agent", Config.pc_ua)
setRequestProperty("source", "copyApp")
+ // deviceinfo
setRequestProperty("webp", "1")
- setRequestProperty("region", if(!Config.net_use_foreign.value) "1" else "0")
- setRequestProperty("version", Config.app_ver.value)
setRequestProperty("dt", SimpleDateFormat("yyyy.MM.dd", Locale.getDefault()).format(Calendar.getInstance().time))
- Config.token.value?.let { tk ->
- setRequestProperty("authorization", "Token $tk")
- }
+ setRequestProperty("accept-encoding", "gzip")
+ setRequestProperty("authorization", "Token${Config.token.value?.let { tk ->
+ if (tk.isNotEmpty()) " $tk" else ""
+ }}")
setRequestProperty("platform", Config.platform.value)
+ setRequestProperty("referer", Config.referer)
+ setRequestProperty("accept", "application/json")
+ setRequestProperty("version", Config.app_ver.value)
+ setRequestProperty("region", if(!Config.net_use_foreign.value) "1" else "0")
+ // device
+ // host
+ Config.net_umstring.value.let { if (it.isNotEmpty()) setRequestProperty("umstring", it) }
+ setRequestProperty("connection", "close")
}
Log.d("MyDT", "getConnection: $url\n${connection.requestProperties.map { "${it.key}: ${it.value}" }.joinToString("\n")}")
return connection
}
- fun getComandyApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null) =
+ private fun getComandyApiConnection(url: String, method: String = "GET") =
run {
val capsule = ComandyCapsule()
capsule.url = url
capsule.method = method
capsule.headers = hashMapOf()
- /*capsule.headers["host"] = if (url.startsWith("https://copymanga.azurewebsites.net")) {
- Uri.parse(url).getQueryParameter("url")?.substringAfter("://")?.substringBefore("/")?:""
- } else {
- url.substringAfter("://").substringBefore("/")
- }*/
- ua?.let { capsule.headers["user-agent"] = it }
- refer?.let { capsule.headers["referer"] = it }
+ capsule.headers["user-agent"] = Config.pc_ua
capsule.headers["source"] = "copyApp"
+ // deviceinfo
capsule.headers["webp"] = "1"
- MainActivity.mainWeakReference?.get()?.let {
- capsule.headers["region"] = if(!Config.net_use_foreign.value) "1" else "0"
- capsule.headers["version"] = Config.app_ver.value
- Config.token.value?.let { tk ->
- capsule.headers["authorization"] = "Token $tk"
- }
- }
- capsule.headers["platform"] = Config.platform.value
capsule.headers["dt"] = SimpleDateFormat("yyyy.MM.dd", Locale.getDefault()).format(Calendar.getInstance().time)
+ capsule.headers["accept-encoding"] = "gzip"
+ capsule.headers["authorization"] = "Token${Config.token.value?.let { tk ->
+ if (tk.isNotEmpty()) " $tk" else ""
+ }}"
+ capsule.headers["platform"] = Config.platform.value
+ capsule.headers["referer"] = Config.referer
+ capsule.headers["accept"] = "application/json"
+ capsule.headers["version"] = Config.app_ver.value
+ capsule.headers["region"] = if(!Config.net_use_foreign.value) "1" else "0"
+ // device
+ // host
+ Config.net_umstring.value.let { if (it.isNotEmpty()) capsule.headers["umstring"] = it }
+ capsule.headers["connection"] = "close"
+
Log.d("MyDT", "getComandyConnection: $url\n${capsule.headers.map { "${it.key}: ${it.value}" }.joinToString("\n")}")
capsule
}
@@ -123,16 +125,31 @@ object DownloadTools {
return bytesCopied
}
+ private fun decodeBody(ret: ByteArray, coding: String) : ByteArray {
+ return if (coding == "gzip") ByteArrayInputStream(ret).use { byteIn ->
+ GZIPInputStream(byteIn).use useGzip@ { gzipIn ->
+ ByteArrayOutputStream().use { byteOut ->
+ val buffer = ByteArray(4096)
+ var len: Int
+ while (gzipIn.read(buffer).also { len = it } != -1) {
+ byteOut.write(buffer, 0, len)
+ }
+ return@useGzip byteOut.toByteArray()
+ }
+ }
+ } else ret
+ }
+
private fun InputStream.readBytesWithProgress(sz: Int, p: Client.Progress): ByteArray {
val buffer = ByteArrayOutputStream(maxOf(DEFAULT_BUFFER_SIZE, this.available()))
copyToWithProgress(buffer, sz, p)
return buffer.toByteArray()
}
- suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = Config.pc_ua, p: Client.Progress? = null): ByteArray =
+ suspend fun getApiContent(u: String, p: Client.Progress? = null): ByteArray =
withContext(Dispatchers.IO) {
if (!u.startsWith("https://$proxyUrl") && Comandy.instance.enabled) {
- getComandyApiConnection(u, "GET", refer, ua).let { capsule ->
+ getComandyApiConnection(u, "GET").let { capsule ->
val para = Gson().toJson(capsule)
//Log.d("MyDT", "comandy request: $para")
Comandy.instance.getInstance()?.let { ins ->
@@ -154,6 +171,7 @@ object DownloadTools {
Log.d("MyDT", "quit comandy get progress, completed: $completed for url $u")
}.start()
}
+ var coding: String? = null
val r = ins.request(para)?.let { result ->
completed = true
p?.notify(100)
@@ -162,26 +180,29 @@ object DownloadTools {
if (it.code != 200) throw IllegalArgumentException("HTTP${it.code} ${
it.data?.let { d -> Base64.decode(d, Base64.DEFAULT).decodeToString() }
}")
+ coding = it.headers["Content-Encoding"] as String?
Base64.decode(it.data, Base64.DEFAULT)
}
}
completed = true
p?.notify(100)
- r
+ r?.let { ret -> coding?.let { decodeBody(ret, it) } }
}
}.let { if(it?.isNotEmpty() == true ) return@withContext it }
- failTimes.incrementAndGet()
}
- getApiConnection(u, "GET", refer, ua).let {
- val sz = it.getHeaderFieldInt("Content-Length", 0)
+ getApiConnection(u, "GET").let { conn ->
+ val sz = conn.getHeaderFieldInt("Content-Length", 0)
val ret = if (sz > 0 && p != null) {
- it.inputStream.readBytesWithProgress(sz, p)
+ conn.inputStream.readBytesWithProgress(sz, p)
} else {
- it.inputStream.readBytes()
+ conn.inputStream.readBytes()
}
- it.disconnect()
+ conn.disconnect()
Log.d("MyDT", "getHttpContent: ${ret.size} bytes")
- ret
+ if (conn.getHeaderField("Content-type") != "application/json") {
+ throw IllegalStateException("请求错误: ${ret.decodeToString()}")
+ }
+ decodeBody(ret, conn.getHeaderField("Content-Encoding")?:"")
}
}
@@ -263,50 +284,33 @@ object DownloadTools {
})
}
- /*private fun replaceChineseCharacters(string: String?) : String? {
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.M) return string
- else return string?.replace(Regex("(?<=/)[\\w\\s\\d\\u4e00-\\u9fa5.-]+(?=/?)")) { match ->
- return@replace URLEncoder.encode(match.value, "UTF-8")
- }
- }*/
-
- fun requestWithBody(url: String, method: String, body: ByteArray, refer: String? = Config.referer, ua: String? = Config.pc_ua, contentType: String? = "application/x-www-form-urlencoded;charset=utf-8"): ByteArray? {
+ fun requestApiWithBody(url: String, method: String, body: ByteArray, contentType: String): ByteArray {
Log.d("MyDT", "$method Http: $url")
- var ret: ByteArray? = null
- val task = FutureTask(if(!url.startsWith("https://$proxyUrl") && Comandy.instance.enabled) Callable{
- try {
- val capsule = getComandyApiConnection(url, method, refer, ua)
- contentType?.let { capsule.headers["content-type"] = it }
- capsule.data = body.decodeToString()
- runBlocking { Comandy.instance.getInstance() }?.request(Gson().toJson(capsule))?.let { result ->
- Gson().fromJson(result, ComandyCapsule::class.java)?.let {
- it.data?.let { d -> Base64.decode(d, Base64.DEFAULT) }?:"empty comandy data".encodeToByteArray()
- }
+ return if(!url.startsWith("https://$proxyUrl") && Comandy.instance.enabled) {
+ val capsule = getComandyApiConnection(url, method)
+ capsule.headers["content-type"] = contentType
+ capsule.data = body.decodeToString()
+ runBlocking { Comandy.instance.getInstance() }?.request(Gson().toJson(capsule))?.let { result ->
+ Gson().fromJson(result, ComandyCapsule::class.java)?.let { c ->
+ c.data?.let { d ->
+ Base64.decode(d, Base64.DEFAULT).let {
+ (c.headers["Content-Encoding"] as String?)?.let { coding ->
+ decodeBody(it, coding)
+ }?:it
+ }
+ }?: throw IllegalStateException("empty comandy data")
}
- } catch (ex: Exception) {
- ex.printStackTrace()
- failTimes.incrementAndGet()
- ex.message?.encodeToByteArray()
+ }?: throw IllegalStateException("no comandy")
+ } else {
+ var ret: ByteArray
+ var coding = ""
+ getApiConnection(url, method).apply {
+ outputStream.write(body)
+ ret = inputStream.readBytes()
+ disconnect()
+ coding = getHeaderField("Content-Encoding")?:""
}
- }
- else Callable {
- try {
- getApiConnection(url, method, refer, ua).apply {
- outputStream.write(body)
- ret = inputStream.readBytes()
- disconnect()
- }
- } catch (ex: Exception) {
- ex.printStackTrace()
- }
- return@Callable ret
- })
- Thread(task).start()
- return try {
- task.get()
- } catch (ex: Exception) {
- ex.printStackTrace()
- null
+ decodeBody(ret, coding)
}
}
}
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 50309c1..789d698 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
@@ -12,9 +12,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
-import top.fumiama.copymanga.json.ReturnBase
import top.fumiama.copymanga.api.Config
-import top.fumiama.copymanga.net.DownloadTools
+import top.fumiama.copymanga.json.ReturnBase
import java.io.File
import java.security.MessageDigest
@@ -77,16 +76,12 @@ open class AutoDownloadHandler(
var cnt = 0
while (cnt++ <= 3) {
try {
- val data = Config.apiProxy?.comancry(url()) {
- DownloadTools.getHttpContent(it)
- }?:DownloadTools.getHttpContent(url())
+ val data = Config.myHostApiUrl.get(url())
if(exit) return@withContext
- val fi = data.inputStream()
- val pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass))
+ val pass = setGsonItem(Gson().fromJson(data, jsonClass))
if (pass && loadFromCache) {
- cacheFile?.writeBytes(data)
+ cacheFile?.writeText(data)
}
- fi.close()
if(!pass) {
delay(2000)
continue
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 b78c0ea..5f3cd76 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
@@ -5,7 +5,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.api.Config
-import top.fumiama.copymanga.net.DownloadTools
import kotlin.random.Random
class PausableDownloader(private val url: String, private val waitMilliseconds: Long = 0, private val isApi: Boolean = true, private val whenFinish: (suspend (result: ByteArray)->Unit)? = null) {
@@ -14,10 +13,8 @@ class PausableDownloader(private val url: String, private val waitMilliseconds:
var c = 0
while (!exit && c++ < 3) {
try {
- val data = (if (isApi) Config.apiProxy?.comancry(url) {
- DownloadTools.getHttpContent(it, Config.referer)
- } else null)?:DownloadTools.getHttpContent(url, Config.referer)
- whenFinish?.let { it(data) }
+ val data = Config.myHostApiUrl.get(url)
+ whenFinish?.let { it(data.encodeToByteArray()) }
return@withContext true
} catch (e: Exception) {
e.printStackTrace()
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 bdde1bd..0ca6c8b 100644
--- a/app/src/main/java/top/fumiama/copymanga/storage/PreferenceString.kt
+++ b/app/src/main/java/top/fumiama/copymanga/storage/PreferenceString.kt
@@ -8,6 +8,8 @@ data class PreferenceString(private val key: String, private var default: String
constructor(key: Int, default: String?, defaultID: Int): this(
MainActivity.mainWeakReference?.get()?.getString(key) ?:"", default, defaultID)
constructor(key: String, default: Int): this(key, null, default)
+ constructor(key: String, default: String): this(key, default, 0)
+ constructor(key: String): this(key, "")
constructor(key: Int): this(key, "", 0)
private val defaultField: String
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 e109bc7..93d70fa 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
@@ -153,6 +153,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
if (getBoolean("loadJson")) {
getString("name")?.let { name ->
try {
+ Log.d("MyBF", "loadFromCache name $name")
book = Book(name, {
return@Book getString(it)
}, activity?.getExternalFilesDir("")!!)
@@ -184,11 +185,17 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
private suspend fun queryCollect() {
- MainActivity.shelf?.query(book?.path!!)?.let { b ->
- mBookHandler?.collect = b.results?.collect?:-2
- Log.d("MyBF", "get collect of ${book?.path} = ${mBookHandler?.collect}")
- tic.text = b.results?.browse?.chapter_name?.let { name ->
- getString(R.string.text_format_cloud_read_to).format(Chinese.fixEncodingIfNeeded(name))
+ try {
+ MainActivity.shelf?.query(book?.path!!)?.let { b ->
+ mBookHandler?.collect = b.results?.collect?:-2
+ Log.d("MyBF", "get collect of ${book?.path} = ${mBookHandler?.collect}")
+ tic.text = b.results?.browse?.chapter_name?.let { name ->
+ getString(R.string.text_format_cloud_read_to).format(Chinese.fixEncodingIfNeeded(name))
+ }
+ }
+ } catch (e: Exception) {
+ activity?.runOnUiThread {
+ Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}
}
}
@@ -208,7 +215,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
}
book?.uuid?.let { uuid ->
- this@BookFragment.lbbsub.setOnClickListener {
+ this@BookFragment.lbbsub?.setOnClickListener {
lifecycleScope.launch clickLaunch@ {
if (this@BookFragment.lbbsub.text != getString(R.string.button_sub)) {
mBookHandler?.collect?.let { collect ->
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/history/HistoryFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/history/HistoryFragment.kt
index 2728eff..de226b6 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/history/HistoryFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/history/HistoryFragment.kt
@@ -11,7 +11,7 @@ import top.fumiama.dmzj.copymanga.R
@OptIn(ExperimentalStdlibApi::class)
class HistoryFragment : InfoCardLoader(R.layout.fragment_history, R.id.action_nav_history_to_nav_book, isHistoryBook = true) {
override fun getApiUrl() =
- getString(R.string.historyApiUrl).format(Config.myHostApiUrl.random(), page * 21, Config.platform.value)
+ getString(R.string.historyApiUrl).format(page * 21, Config.platform.value)
override fun onCreate(savedInstanceState: Bundle?) {
if (MainActivity.member?.hasLogin != true) {
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/newest/NewestFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/newest/NewestFragment.kt
index 4c79b06..7dd44c6 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/newest/NewestFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/newest/NewestFragment.kt
@@ -7,5 +7,5 @@ import top.fumiama.dmzj.copymanga.R
@ExperimentalStdlibApi
class NewestFragment : InfoCardLoader(R.layout.fragment_newest, R.id.action_nav_newest_to_nav_book, true) {
override fun getApiUrl() =
- getString(R.string.newestApiUrl).format(Config.myHostApiUrl.random(), page * 21, Config.platform.value)
+ getString(R.string.newestApiUrl).format(page * 21, Config.platform.value)
}
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt
index 2c66f5b..90818bb 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt
@@ -48,7 +48,6 @@ class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank
override fun getApiUrl() =
getString(R.string.rankApiUrl).format(
- Config.myHostApiUrl.random(),
page * 21,
sortWay[sortValue],
audienceWay[audience],
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/recommend/RecFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/recommend/RecFragment.kt
index 89f7450..10d6115 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/recommend/RecFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/recommend/RecFragment.kt
@@ -7,5 +7,5 @@ import top.fumiama.dmzj.copymanga.R
@ExperimentalStdlibApi
class RecFragment : InfoCardLoader(R.layout.fragment_recommend, R.id.action_nav_recommend_to_nav_book, true) {
override fun getApiUrl() =
- getString(R.string.recommendApiUrl).format(Config.myHostApiUrl.random(), page * 21, Config.platform.value)
+ getString(R.string.recommendApiUrl).format(page * 21, Config.platform.value)
}
\ No newline at end of file
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/search/SearchFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/search/SearchFragment.kt
index eb1999d..df7df04 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/search/SearchFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/search/SearchFragment.kt
@@ -13,7 +13,7 @@ class SearchFragment : InfoCardLoader(R.layout.fragment_search, R.id.action_nav_
private var query: String? = null
private var type: String? = null
override fun getApiUrl() =
- getString(R.string.searchApiUrl).format(Config.myHostApiUrl.random(), page * 21, query, type, Config.platform.value)
+ getString(R.string.searchApiUrl).format(page * 21, query, type, Config.platform.value)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt
index 90a682d..2e9d74a 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt
@@ -26,7 +26,6 @@ class ShelfFragment : InfoCardLoader(R.layout.fragment_shelf, R.id.action_nav_su
override fun getApiUrl() =
getString(R.string.shelfApiUrl).format(
- Config.myHostApiUrl.random(),
page * 21,
sortWay[sortValue],
Config.platform.value,
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt
index 6a758ee..1ea46c2 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt
@@ -25,7 +25,6 @@ class SortFragment : StatusCardFlow(0, R.id.action_nav_sort_to_nav_book, R.layou
override fun getApiUrl() =
getString(R.string.sortApiUrl).format(
- Config.myHostApiUrl.random(),
page * 21,
sortWay[sortValue],
if(theme >= 0 && theme < (filter?.results?.theme?.size ?: 0)) (filter?.results?.theme?.get(theme)?.path_word ?: "") else "",
@@ -43,7 +42,7 @@ class SortFragment : StatusCardFlow(0, R.id.action_nav_sort_to_nav_book, R.layou
super.setListeners()
lifecycleScope.launch {
setProgress(5)
- PausableDownloader(getString(R.string.filterApiUrl).format(Config.myHostApiUrl.random(), Config.platform.value)) {
+ PausableDownloader(getString(R.string.filterApiUrl).format(Config.platform.value)) {
if(ad?.exit == true) return@PausableDownloader
it.let {
it.inputStream().use { i ->
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt
index 15521b5..99be0c0 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt
@@ -18,13 +18,13 @@ import top.fumiama.dmzj.copymanga.R
class TopicFragment : InfoCardLoader(R.layout.fragment_topic, R.id.action_nav_topic_to_nav_book) {
private var type = 1
override fun getApiUrl() =
- getString(R.string.topicContentApiUrl).format(Config.myHostApiUrl.random(), arguments?.getString("path"), type, offset, Config.platform.value)
+ getString(R.string.topicContentApiUrl).format(arguments?.getString("path"), type, offset, Config.platform.value)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
setProgress(5)
- PausableDownloader(getString(R.string.topicApiUrl).format(Config.myHostApiUrl.random(), arguments?.getString("path"), Config.platform.value)) { data ->
+ PausableDownloader(getString(R.string.topicApiUrl).format(arguments?.getString("path"), Config.platform.value)) { data ->
setProgress(10)
withContext(Dispatchers.IO) {
if(ad?.exit == true) return@withContext
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/download/DownloadFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/download/DownloadFragment.kt
index 1a28c37..5409876 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/download/DownloadFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/download/DownloadFragment.kt
@@ -118,7 +118,6 @@ class DownloadFragment: NoBackRefreshFragment(R.layout.fragment_download) {
bundle.putBoolean("callFromOldDL", true)
}
bundle.putString("name", jsonFile.parentFile?.name?:"Null")
- Log.d("MyDF", "root view: $rootView")
Log.d("MyDF", "action_nav_download_to_nav_group")
Navigate.safeNavigateTo(findNavController(), R.id.action_nav_download_to_nav_group, bundle)
}
@@ -128,7 +127,6 @@ class DownloadFragment: NoBackRefreshFragment(R.layout.fragment_download) {
bundle.putString("title", title)
bundle.putString("file", file.absolutePath)
Log.d("MyDF", "Call self to $title")
- Log.d("MyDF", "root view: $rootView")
Log.d("MyDF", "action_nav_download_self")
Navigate.safeNavigateTo(findNavController(), R.id.action_nav_download_self, bundle)
}
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt
index 3a9a2da..abbbf8a 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt
@@ -244,7 +244,6 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
Log.d("MyNDF", "Call dl and is new.")
bundle.putString("loadJson", File(File(extDir, name), "info.json").readText())
bundle.putString("name", name)
- Log.d("MyNDF", "root view: $rootView")
Log.d("MyNDF", "action_nav_new_download_to_nav_group")
Navigate.safeNavigateTo(findNavController(), R.id.action_nav_new_download_to_nav_group, bundle)
}
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 bce45a5..a05694c 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
@@ -51,6 +51,7 @@ 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()
info().let { l ->
if (l.code != 200 && l.code != 449) {
Toast.makeText(context, l.message, Toast.LENGTH_SHORT).show()
@@ -298,7 +299,7 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
suspend fun refresh(q: CharSequence) = withContext(Dispatchers.IO) {
query = q.toString()
activity?.apply {
- PausableDownloader(getString(R.string.searchApiUrl).format(Config.myHostApiUrl.random(), 0,
+ PausableDownloader(getString(R.string.searchApiUrl).format(0,
URLEncoder.encode(q.toString(), Charset.defaultCharset().name()), type, Config.platform.value)) {
results = Gson().fromJson(it.decodeToString(), BookListStructure::class.java)
count = results?.results?.total?:0
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/home/HomeHandler.kt b/app/src/main/java/top/fumiama/copymanga/ui/home/HomeHandler.kt
index fb711bc..044cfe7 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/home/HomeHandler.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/home/HomeHandler.kt
@@ -40,7 +40,7 @@ import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicInteger
class HomeHandler(private val that: WeakReference) : AutoDownloadHandler({
- that.get()?.getString(R.string.mainPageApiUrl)!!.format(Config.myHostApiUrl.random(), Config.platform.value)
+ that.get()?.getString(R.string.mainPageApiUrl)!!.format(Config.platform.value)
},
IndexStructure::class.java,
that.get()
diff --git a/app/src/main/java/top/fumiama/copymanga/view/template/NoBackRefreshFragment.kt b/app/src/main/java/top/fumiama/copymanga/view/template/NoBackRefreshFragment.kt
index d9ec5cb..3e2f899 100644
--- a/app/src/main/java/top/fumiama/copymanga/view/template/NoBackRefreshFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/view/template/NoBackRefreshFragment.kt
@@ -25,6 +25,7 @@ open class NoBackRefreshFragment(private val layoutToLoad: Int): Fragment() {
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
+ //TODO: 支持自动重建
if(_rootView == null) {
isFirstInflate = true
_rootView = inflater.inflate(layoutToLoad, container, false)
diff --git a/app/src/main/java/top/fumiama/copymanga/view/template/StatusCardFlow.kt b/app/src/main/java/top/fumiama/copymanga/view/template/StatusCardFlow.kt
index ca9f636..89cfd11 100644
--- a/app/src/main/java/top/fumiama/copymanga/view/template/StatusCardFlow.kt
+++ b/app/src/main/java/top/fumiama/copymanga/view/template/StatusCardFlow.kt
@@ -18,7 +18,6 @@ open class StatusCardFlow(private val api: Int, nav: Int, inflateRes: Int,
override fun getApiUrl() =
getString(api).format(
- Config.myHostApiUrl.random(),
page * 21,
sortWay[sortValue],
Config.platform.value,
diff --git a/app/src/main/java/top/fumiama/copymanga/view/template/ThemeCardFlow.kt b/app/src/main/java/top/fumiama/copymanga/view/template/ThemeCardFlow.kt
index 8b14658..fbcab19 100644
--- a/app/src/main/java/top/fumiama/copymanga/view/template/ThemeCardFlow.kt
+++ b/app/src/main/java/top/fumiama/copymanga/view/template/ThemeCardFlow.kt
@@ -12,7 +12,6 @@ open class ThemeCardFlow(private val api: Int, nav: Int) : StatusCardFlow(0, nav
private var theme = ""
override fun getApiUrl() =
getString(api).format(
- Config.myHostApiUrl.random(),
page * 21,
sortWay[sortValue],
theme,
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0ee0956..5d2e005 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -3,7 +3,7 @@
-
+
]>
拷贝漫画
@@ -64,31 +64,30 @@
预载图片头失败
读取图片大小失败
- https://%1$s/api/v3/system/network2?platform=%2$s
- https://%1$s/api/v3/h5/homeIndex?platform=%2$s
- https://%1$s
+ /api/v3/system/network2?platform=%1$s
+ /api/v3/h5/homeIndex?platform=%1$s
&hosturl;
&proxyurl;
- https://%1$s/api/v3/ranks?limit=21&offset=%2$d&date_type=%3$s&audience_type=%4$s&platform=%5$s
- https://%1$s/api/v3/search/comic?limit=21&offset=%2$d&q=%3$s&q_type=%4$s&platform=%5$s
- https://%1$s/api/v3/h5/filter/comic/tags?platform=%2$s
- https://%1$s/api/v3/comics?limit=21&offset=%2$d&ordering=%3$s&theme=%4$s&top=%5$s&platform=%6$s
- https://%1$s/api/v3/comic2/%2$s?platform=%3$s
- https://%1$s/api/v3/comic2/%2$s/query?platform=%3$s
- https://%1$s/api/v3/comic/%2$s/group/%3$s/chapters?limit=100&offset=%4$d&platform=%5$s
- https://%1$s/api/v3/comic/%2$s/chapter%3$s/%4$s?platform=%5$s
- https://%1$s/api/v3/topic/%2$s?platform=%3$s
- https://%1$s/api/v3/topic/%2$s/contents?type=%3$d&limit=21&offset=%4$d&platform=%5$s
- https://%1$s/api/v3/recs?pos=3200102&limit=21&offset=%2$d&platform=%3$s
- https://%1$s/api/v3/update/newest?limit=21&offset=%2$d&platform=%3$s
- https://%1$s/api/v3/comics?limit=21&offset=%2$d&ordering=%3$s&top=finish&platform=%4$s
- https://%1$s/api/v3/comics?limit=21&offset=%2$d&ordering=%3$s&author=%4$s&platform=%5$s
- https://%1$s/api/v3/comics?limit=21&offset=%2$d&ordering=%3$s&theme=%4$s&platform=%5$s
- https://%1$s/api/v3/login?platform=%2$s
- https://%1$s/api/v3/member/info?platform=%2$s
- https://%1$s/api/v3/member/browse/comics?limit=21&offset=%2$d&platform=%3$s
- https://%1$s/api/v3/member/collect/comics?limit=21&offset=%2$d&free_type=1&ordering=%3$s&platform=%4$s
- https://%1$s/api/v3/member/collect/comic
+ /api/v3/ranks?limit=21&offset=%1$d&date_type=%2$s&audience_type=%3$s&platform=%4$s
+ /api/v3/search/comic?limit=21&offset=%1$d&q=%2$s&q_type=%3$s&platform=%4$s
+ /api/v3/h5/filter/comic/tags?platform=%1$s
+ /api/v3/comics?limit=21&offset=%1$d&ordering=%2$s&theme=%3$s&top=%4$s&platform=%5$s
+ /api/v3/comic2/%1$s?platform=%2$s
+ /api/v3/comic2/%1$s/query?platform=%2$s
+ /api/v3/comic/%1$s/group/%2$s/chapters?limit=100&offset=%3$d&platform=%4$s
+ /api/v3/comic/%1$s/chapter%2$s/%3$s?platform=%4$s
+ /api/v3/topic/%1$s?platform=%2$s
+ /api/v3/topic/%1$s/contents?type=%2$d&limit=21&offset=%3$d&platform=%4$s
+ /api/v3/recs?pos=3200102&limit=21&offset=%1$d&platform=%2$s
+ /api/v3/update/newest?limit=21&offset=%1$d&platform=%2$s
+ /api/v3/comics?limit=21&offset=%1$d&ordering=%2$s&top=finish&platform=%3$s
+ /api/v3/comics?limit=21&offset=%1$d&ordering=%2$s&author=%3$s&platform=%4$s
+ /api/v3/comics?limit=21&offset=%1$d&ordering=%2$s&theme=%3$s&platform=%4$s
+ /api/v3/login?platform=%1$s
+ /api/v3/member/info?platform=%1$s
+ /api/v3/member/browse/comics?limit=21&offset=%1$d&platform=%2$s
+ /api/v3/member/collect/comics?limit=21&offset=%1$d&free_type=1&ordering=%2$s&platform=%3$s
+ /api/v3/member/collect/comic
https://&proxyurl;/api/img?code=%1$s&url=%2$s
settings_cat_net_et_img_proxy_code
@@ -178,6 +177,8 @@
一般无需更改,除非拷贝漫画官方更改网址,默认:&hosturl;
反向代理
您可以自建反向代理并填写在此处以解决网络不畅
+ 友盟ID
+ 填写您分配到的友盟ID
使用API代理(需要密钥)
作者自建的API代理,可缓解国内图书详情加载问题,但不保证100%解决,也不保证一直可用
使用图床代理(需要密钥)
@@ -207,7 +208,6 @@
用户名为空
密码为空
- 登录失败
解析返回数据失败
获取用户信息失败
重启应用以彻底退出登录
diff --git a/app/src/main/res/xml/pref_setting.xml b/app/src/main/res/xml/pref_setting.xml
index 9c3153e..6236b87 100644
--- a/app/src/main/res/xml/pref_setting.xml
+++ b/app/src/main/res/xml/pref_setting.xml
@@ -120,6 +120,16 @@
app:enableCopying="true"
app:iconSpaceReserved="false"
app:key="@string/reverseProxyKeyID" />
+