Skip to content

Commit 98b2d1a

Browse files
committed
v2.5.0
新增 1. 导出已下载漫画为zip压缩包 2. 浅色和深色模式切换 (fix #136) 3. 支持自建反向API代理 (不支持图片) 4. 我的下载显示本地阅读进度 (fix #46) 5. 倒序排列每一章节
1 parent ecd2f9d commit 98b2d1a

37 files changed

+508
-167
lines changed

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ android {
1111
applicationId 'top.fumiama.copymanga'
1212
minSdkVersion 23
1313
targetSdkVersion 34
14-
versionCode 70
15-
versionName '2.4.3'
14+
versionCode 71
15+
versionName '2.5.0'
1616
resourceConfigurations += ['zh', 'zh-rCN']
1717

1818
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

app/src/main/java/top/fumiama/copymanga/MainActivity.kt

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import android.widget.Toast
2323
import androidx.activity.result.contract.ActivityResultContracts
2424
import androidx.appcompat.app.AlertDialog
2525
import androidx.appcompat.app.AppCompatActivity
26+
import androidx.appcompat.app.AppCompatDelegate
2627
import androidx.core.app.ActivityCompat
2728
import androidx.core.content.ContextCompat
2829
import androidx.core.net.toUri
@@ -81,6 +82,19 @@ class MainActivity : AppCompatActivity() {
8182
override fun onCreate(savedInstanceState: Bundle?) {
8283
super.onCreate(null)
8384

85+
val darkModePrefKey = getString(R.string.darkModeKeyID)
86+
PreferenceManager.getDefaultSharedPreferences(this)?.apply {
87+
if (contains(darkModePrefKey)) getString(darkModePrefKey, "0")?.toInt()?.let {
88+
if (it > 0) {
89+
AppCompatDelegate.setDefaultNightMode(listOf(
90+
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM,
91+
AppCompatDelegate.MODE_NIGHT_NO,
92+
AppCompatDelegate.MODE_NIGHT_YES,
93+
)[it])
94+
}
95+
}
96+
}
97+
8498
// must init before setContentView because HomeF need them to init
8599
mainWeakReference = WeakReference(this)
86100
toolsBox = UITools(this)
@@ -172,13 +186,13 @@ class MainActivity : AppCompatActivity() {
172186
)[it])
173187
}
174188
}
175-
if (Config.general_enable_transparent_system_bar.value) {
176-
WindowCompat.setDecorFitsSystemWindows(window, false)
177-
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
178-
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
179-
window.statusBarColor = 0
180-
window.navigationBarColor = 0
181-
}
189+
}
190+
if (Config.general_enable_transparent_system_bar.value) {
191+
WindowCompat.setDecorFitsSystemWindows(window, false)
192+
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
193+
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
194+
window.statusBarColor = 0
195+
window.navigationBarColor = 0
182196
}
183197
}
184198

@@ -197,7 +211,11 @@ class MainActivity : AppCompatActivity() {
197211
true
198212
}
199213
R.id.action_download -> {
200-
bookHandler.get()?.sendEmptyMessage(BookHandler.NAVIGATE_TO_DOWNLOAD)
214+
if (NewDownloadFragment.wn != null) {
215+
//TODO: fill it
216+
} else {
217+
bookHandler.get()?.sendEmptyMessage(BookHandler.NAVIGATE_TO_DOWNLOAD)
218+
}
201219
true
202220
}
203221
R.id.action_sort -> {
@@ -412,7 +430,7 @@ class MainActivity : AppCompatActivity() {
412430
dl.setMessage("${getString(R.string.app_description)}\n" +
413431
"\n$comandy\n" +
414432
"$comancry\n\n"+ File("/proc/self/cmdline").readText() + "\n" +
415-
"安装位置: ${applicationInfo.sourceDir}")
433+
"当前API: ${Config.myHostApiUrl.joinToString(", ")}")
416434
dl.setTitle("${getString(R.string.action_info)} ${BuildConfig.VERSION_NAME}")
417435
dl.setIcon(R.mipmap.ic_launcher)
418436
dl.setPositiveButton(android.R.string.ok) { _, _ -> }

app/src/main/java/top/fumiama/copymanga/api/Config.kt

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
package top.fumiama.copymanga.api
22

3+
import android.util.Log
34
import com.bumptech.glide.load.model.LazyHeaders
5+
import com.google.gson.Gson
6+
import kotlinx.coroutines.runBlocking
7+
import kotlinx.coroutines.sync.Mutex
8+
import kotlinx.coroutines.sync.withLock
49
import top.fumiama.copymanga.MainActivity
10+
import top.fumiama.copymanga.json.NetworkStructure
11+
import top.fumiama.copymanga.net.DownloadTools
12+
import top.fumiama.copymanga.net.Proxy
13+
import top.fumiama.copymanga.net.Resolution
514
import top.fumiama.copymanga.storage.PreferenceBoolean
615
import top.fumiama.copymanga.storage.PreferenceInt
716
import top.fumiama.copymanga.storage.PreferenceString
817
import top.fumiama.copymanga.storage.UserPreferenceInt
918
import top.fumiama.copymanga.storage.UserPreferenceString
10-
import top.fumiama.copymanga.net.Proxy
11-
import top.fumiama.copymanga.net.Resolution
1219
import top.fumiama.dmzj.copymanga.R
1320
import java.io.File
1421

@@ -53,9 +60,52 @@ object Config {
5360
return field
5461
}
5562

56-
val myHostApiUrl = PreferenceString("settings_cat_net_et_api_url", R.string.hostUrl)
63+
val proxyUrl = MainActivity.mainWeakReference?.get()?.getString(R.string.proxyUrl)!!
64+
private val reverseProxyUrl = PreferenceString(R.string.reverseProxyKeyID)
65+
private val networkApiUrl = PreferenceString("settings_cat_net_et_api_url", R.string.hostUrl)
66+
private var mHostApiUrls: Array<String> = arrayOf()
67+
private var mHostApiUrlsMutex = Mutex()
68+
val myHostApiUrl: Array<String>
69+
get() {
70+
if (mHostApiUrls.isNotEmpty()) return mHostApiUrls
71+
if (reverseProxyUrl.value.isNotEmpty() && reverseProxyUrl.value != proxyUrl) {
72+
mHostApiUrls = arrayOf(reverseProxyUrl.value)
73+
Log.d("MyC", "myHostApiUrl set reverse proxy to ${mHostApiUrls[0]}")
74+
return mHostApiUrls
75+
}
76+
MainActivity.mainWeakReference?.get()?.apply {
77+
runBlocking {
78+
mHostApiUrlsMutex.withLock {
79+
if (mHostApiUrls.isNotEmpty()) return@runBlocking
80+
try {
81+
val u = getString(R.string.networkApiUrl).format(networkApiUrl.value)
82+
val r = Gson().fromJson((apiProxy?.comancry(u) {
83+
DownloadTools.getHttpContent(it, referer, pc_ua)
84+
}?:DownloadTools.getHttpContent(u, referer, pc_ua)).decodeToString(), NetworkStructure::class.java)
85+
if (r != null) {
86+
Log.d("MyC", "myHostApiUrl get code ${r.code} msg ${r.message}")
87+
if (r.code == 200 && r.results != null) {
88+
// need header umstring
89+
// r.results.api.forEach { it.forEach { api -> if (!api.isNullOrEmpty() && api !in field) field += api } }
90+
r.results.share.forEach { api -> if (!api.isNullOrEmpty() && api !in mHostApiUrls) mHostApiUrls += api }
91+
}
92+
}
93+
} catch (e: Exception) {
94+
e.printStackTrace()
95+
mHostApiUrls = arrayOf(networkApiUrl.value)
96+
}
97+
if (mHostApiUrls.isEmpty()) {
98+
mHostApiUrls = arrayOf(networkApiUrl.value)
99+
Log.d("MyC", "myHostApiUrl set default ${mHostApiUrls[0]}")
100+
}
101+
}
102+
}
103+
}
104+
Log.d("MyC", "myHostApiUrl get hosts ${mHostApiUrls.joinToString(", ")}")
105+
return mHostApiUrls
106+
}
57107
val navTextInfo = UserPreferenceString("navTextInfo", R.string.navTextInfo)
58-
val proxy_key = PreferenceString(R.string.imgProxyKeyID)
108+
val proxy_key = PreferenceString(R.string.imgProxyCodeKeyID)
59109
val app_ver = PreferenceString("settings_cat_general_et_app_version", R.string.app_ver)
60110
val token = UserPreferenceString("token", "", null)
61111
val pc_ua get() = MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)?.format(app_ver.value)?:""
@@ -80,6 +130,7 @@ object Config {
80130
val net_use_api_proxy = PreferenceBoolean("settings_cat_net_sw_use_api_proxy", false)
81131
val net_img_resolution = PreferenceString(R.string.imgResolutionKeyID)
82132

133+
val view_manga_inverse_chapters = PreferenceBoolean("settings_cat_vm_sw_inverse_chapters", false)
83134
val view_manga_always_dark_bg = PreferenceBoolean("settings_cat_vm_sw_always_dark_bg", false)
84135
val view_manga_vertical_max = PreferenceInt("settings_cat_vm_sb_vertical_max", 20)
85136
val view_manga_quality = PreferenceInt("settings_cat_vm_sb_quality", 100)
@@ -92,5 +143,5 @@ object Config {
92143

93144
fun getChapterInfoApiUrl(path: String?, uuid: String?, version: Int) =
94145
MainActivity.mainWeakReference?.get()?.getString(R.string.chapterInfoApiUrl)
95-
?.format(myHostApiUrl.value, path, if (version >= 2) "$version" else "" , uuid)
146+
?.format(myHostApiUrl.random(), path, if (version >= 2) "$version" else "" , uuid)
96147
}

app/src/main/java/top/fumiama/copymanga/api/manga/Book.kt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import top.fumiama.dmzj.copymanga.R
1515
import java.io.File
1616

1717
class Book(val path: String, private val getString: (Int) -> String, private val exDir: File, private val loadCache: Boolean = false, private val mPassName: String? = null) {
18-
private val mBookApiUrl = getString(R.string.bookInfoApiUrl).format(Config.myHostApiUrl.value, path)
18+
private val mBookApiUrl = getString(R.string.bookInfoApiUrl).format(Config.myHostApiUrl.random(), path)
1919
private val mUserAgent = getString(R.string.pc_ua).format(Config.app_ver.value)
2020
private var mBook: BookInfoStructure? = null
2121
private var mGroupPathWords = arrayOf<String>()
@@ -37,12 +37,6 @@ class Book(val path: String, private val getString: (Int) -> String, private val
3737
val author: Array<ThemeStructure>? get() = mBook?.results?.comic?.author
3838
val theme: Array<ThemeStructure>? get() = mBook?.results?.comic?.theme
3939
val keys get() = mKeys
40-
val imageType: String
41-
get() = when(mBook?.results?.comic?.img_type) {
42-
1 -> "条漫"
43-
2 -> "普通"
44-
else -> "未知类型${mBook?.results?.comic?.img_type}"
45-
}
4640
val popular get() = mBook?.results?.comic?.popular?:0
4741
val status get() = mBook?.results?.comic?.status?.display?:"未知"
4842
val updateTime get() = mBook?.results?.comic?.datetime_updated?:"未知"

app/src/main/java/top/fumiama/copymanga/api/manga/Reader.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package top.fumiama.copymanga.api.manga
22

33
import android.content.Context
4+
import android.content.Context.MODE_PRIVATE
45
import android.content.Intent
56
import android.util.Log
67
import androidx.core.content.edit
8+
import androidx.fragment.app.FragmentActivity
79
import com.google.gson.Gson
810
import kotlinx.android.synthetic.main.button_tbutton.view.*
11+
import kotlinx.coroutines.delay
912
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
13+
import top.fumiama.copymanga.api.Config
1014
import top.fumiama.copymanga.json.VolumeStructure
1115
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
1216
import java.io.File
@@ -81,4 +85,37 @@ object Reader {
8185
}
8286
return "N/A:null_gson"
8387
}
88+
fun getComicChapterNamesInFolder(file: File): Array<String> {
89+
if(!file.exists()) {
90+
return arrayOf()
91+
}
92+
val jsonFile = File(file, "info.json")
93+
if(!jsonFile.exists()) {
94+
return arrayOf()
95+
}
96+
Gson().fromJson(jsonFile.readText(), Array<VolumeStructure>::class.java)?.let { volumes ->
97+
if(volumes.isEmpty()) {
98+
return arrayOf()
99+
}
100+
var arr = arrayOf<String>()
101+
volumes.forEach { v ->
102+
v.results?.list?.forEach {
103+
arr += it.name?:""
104+
}
105+
}
106+
return arr
107+
}
108+
return arrayOf()
109+
}
110+
fun getLocalReadingProgress(activity: FragmentActivity, name: String, chapterNames: Array<String>): String {
111+
var chapter = "未读"
112+
activity.apply {
113+
getPreferences(MODE_PRIVATE).getInt(name, -1).let { p ->
114+
if(p >= 0) chapterNames.let {
115+
chapter = it[if (p >= it.size) it.size-1 else p]
116+
}
117+
}
118+
}
119+
return chapter
120+
}
84121
}

app/src/main/java/top/fumiama/copymanga/api/manga/Shelf.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import top.fumiama.copymanga.net.DownloadTools
1010
import top.fumiama.dmzj.copymanga.R
1111

1212
class Shelf(private val getString: (Int) -> String) {
13-
private val apiUrl: String get() = getString(R.string.shelfOperateApiUrl).format(Config.myHostApiUrl.value)
13+
private val apiUrl: String get() = getString(R.string.shelfOperateApiUrl).format(Config.myHostApiUrl.random())
1414
private val queryApiUrlTemplate = getString(R.string.bookUserQueryApiUrl)
1515
private val addApiUrl get() = "$apiUrl?platform=3"
1616
private val delApiUrl get() = "${apiUrl}s?platform=3"
@@ -68,7 +68,7 @@ class Shelf(private val getString: (Int) -> String) {
6868

6969
suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
7070
try {
71-
val queryUrl = queryApiUrlTemplate.format(Config.myHostApiUrl.value, pathWord)
71+
val queryUrl = queryApiUrlTemplate.format(Config.myHostApiUrl.random(), pathWord)
7272
(Config.apiProxy?.comancry(queryUrl) { url ->
7373
DownloadTools.getHttpContent(url, Config.referer)
7474
}?:DownloadTools.getHttpContent(queryUrl, Config.referer)).let {

app/src/main/java/top/fumiama/copymanga/api/manga/Volume.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class Volume(private val path: String, private val groupPathWord: String, getStr
3939
return@withContext mVolume
4040
}
4141

42-
private fun getApiUrl(offset: Int) = mGroupInfoApiUrlTemplate.format(Config.myHostApiUrl.value, path, groupPathWord, offset)
42+
private fun getApiUrl(offset: Int) = mGroupInfoApiUrlTemplate.format(Config.myHostApiUrl.random(), path, groupPathWord, offset)
4343
private suspend fun download(re: Array<VolumeStructure?>, offset: Int, c: Int) = withContext(Dispatchers.IO) {
4444
Log.d("MyV", "下载偏移: $offset")
4545
getApiUrl(offset).let {

app/src/main/java/top/fumiama/copymanga/api/user/Member.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class Member(private val getString: (Int) -> String) {
4949
}
5050
try {
5151
val u = getString(R.string.memberInfoApiUrl)
52-
.format(Config.myHostApiUrl.value)
52+
.format(Config.myHostApiUrl.random())
5353
val data = (Config.apiProxy?.comancry(u) {
5454
DownloadTools.getHttpContent(it)
5555
}?:DownloadTools.getHttpContent(u)).decodeToString()
@@ -97,7 +97,7 @@ class Member(private val getString: (Int) -> String) {
9797
}
9898

9999
private suspend fun postLogin(username: String, pwd: String, salt: Int): ByteArray? =
100-
getString(R.string.loginApiUrl).format(Config.myHostApiUrl.value).let { u ->
100+
getString(R.string.loginApiUrl).format(Config.myHostApiUrl.random()).let { u ->
101101
val use: suspend (String) -> ByteArray? = { it: String ->
102102
DownloadTools.getApiConnection(it, "POST").let { c ->
103103
c.doOutput = true
@@ -129,7 +129,7 @@ class Member(private val getString: (Int) -> String) {
129129

130130

131131
private suspend fun postComandyLogin(username: String, pwd: String, salt: Int) =
132-
getString(R.string.loginApiUrl).format(Config.myHostApiUrl.value).let { u ->
132+
getString(R.string.loginApiUrl).format(Config.myHostApiUrl.random()).let { u ->
133133
val use: suspend (String) -> ByteArray? = { it: String ->
134134
DownloadTools.getComandyApiConnection(it, "POST", null, Config.pc_ua).apply {
135135
headers["content-type"] = "application/x-www-form-urlencoded;charset=utf-8"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package top.fumiama.copymanga.json;
2+
3+
public class NetworkStructure extends ReturnBase {
4+
public Results results;
5+
public static class Results {
6+
public String[] share;
7+
public String[][] api;
8+
}
9+
}

app/src/main/java/top/fumiama/copymanga/lib/Comandy.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Comandy: LazyLibrary<ComandyMethods>(
1616
Log.d("MyComandy", "$name block enabled for isInInit")
1717
return false
1818
}
19-
if (mEnabled != true && DownloadTools.failTimes.get() >= 3) {
19+
if (mEnabled != true && DownloadTools.failTimes.get() >= 16) {
2020
mEnabled = true
2121
return true
2222
}

0 commit comments

Comments
 (0)