mirror of
https://github.com/fumiama/copymanga.git
synced 2026-06-07 16:20:24 +08:00
v2.5.0
新增 1. 导出已下载漫画为zip压缩包 2. 浅色和深色模式切换 (fix #136) 3. 支持自建反向API代理 (不支持图片) 4. 我的下载显示本地阅读进度 (fix #46) 5. 倒序排列每一章节
This commit is contained in:
@@ -11,8 +11,8 @@ android {
|
||||
applicationId 'top.fumiama.copymanga'
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 34
|
||||
versionCode 70
|
||||
versionName '2.4.3'
|
||||
versionCode 71
|
||||
versionName '2.5.0'
|
||||
resourceConfigurations += ['zh', 'zh-rCN']
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.net.toUri
|
||||
@@ -81,6 +82,19 @@ class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(null)
|
||||
|
||||
val darkModePrefKey = getString(R.string.darkModeKeyID)
|
||||
PreferenceManager.getDefaultSharedPreferences(this)?.apply {
|
||||
if (contains(darkModePrefKey)) getString(darkModePrefKey, "0")?.toInt()?.let {
|
||||
if (it > 0) {
|
||||
AppCompatDelegate.setDefaultNightMode(listOf(
|
||||
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM,
|
||||
AppCompatDelegate.MODE_NIGHT_NO,
|
||||
AppCompatDelegate.MODE_NIGHT_YES,
|
||||
)[it])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// must init before setContentView because HomeF need them to init
|
||||
mainWeakReference = WeakReference(this)
|
||||
toolsBox = UITools(this)
|
||||
@@ -172,13 +186,13 @@ class MainActivity : AppCompatActivity() {
|
||||
)[it])
|
||||
}
|
||||
}
|
||||
if (Config.general_enable_transparent_system_bar.value) {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||
window.statusBarColor = 0
|
||||
window.navigationBarColor = 0
|
||||
}
|
||||
}
|
||||
if (Config.general_enable_transparent_system_bar.value) {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||
window.statusBarColor = 0
|
||||
window.navigationBarColor = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +211,11 @@ class MainActivity : AppCompatActivity() {
|
||||
true
|
||||
}
|
||||
R.id.action_download -> {
|
||||
bookHandler.get()?.sendEmptyMessage(BookHandler.NAVIGATE_TO_DOWNLOAD)
|
||||
if (NewDownloadFragment.wn != null) {
|
||||
//TODO: fill it
|
||||
} else {
|
||||
bookHandler.get()?.sendEmptyMessage(BookHandler.NAVIGATE_TO_DOWNLOAD)
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.action_sort -> {
|
||||
@@ -412,7 +430,7 @@ class MainActivity : AppCompatActivity() {
|
||||
dl.setMessage("${getString(R.string.app_description)}\n" +
|
||||
"\n$comandy\n" +
|
||||
"$comancry\n\n"+ File("/proc/self/cmdline").readText() + "\n" +
|
||||
"安装位置: ${applicationInfo.sourceDir}")
|
||||
"当前API: ${Config.myHostApiUrl.joinToString(", ")}")
|
||||
dl.setTitle("${getString(R.string.action_info)} ${BuildConfig.VERSION_NAME}")
|
||||
dl.setIcon(R.mipmap.ic_launcher)
|
||||
dl.setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
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.net.Proxy
|
||||
import top.fumiama.copymanga.net.Resolution
|
||||
import top.fumiama.copymanga.storage.PreferenceBoolean
|
||||
import top.fumiama.copymanga.storage.PreferenceInt
|
||||
import top.fumiama.copymanga.storage.PreferenceString
|
||||
import top.fumiama.copymanga.storage.UserPreferenceInt
|
||||
import top.fumiama.copymanga.storage.UserPreferenceString
|
||||
import top.fumiama.copymanga.net.Proxy
|
||||
import top.fumiama.copymanga.net.Resolution
|
||||
import top.fumiama.dmzj.copymanga.R
|
||||
import java.io.File
|
||||
|
||||
@@ -53,9 +60,52 @@ object Config {
|
||||
return field
|
||||
}
|
||||
|
||||
val myHostApiUrl = PreferenceString("settings_cat_net_et_api_url", R.string.hostUrl)
|
||||
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<String> = arrayOf()
|
||||
private var mHostApiUrlsMutex = Mutex()
|
||||
val myHostApiUrl: Array<String>
|
||||
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)
|
||||
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 navTextInfo = UserPreferenceString("navTextInfo", R.string.navTextInfo)
|
||||
val proxy_key = PreferenceString(R.string.imgProxyKeyID)
|
||||
val proxy_key = PreferenceString(R.string.imgProxyCodeKeyID)
|
||||
val app_ver = PreferenceString("settings_cat_general_et_app_version", R.string.app_ver)
|
||||
val token = UserPreferenceString("token", "", null)
|
||||
val pc_ua get() = MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)?.format(app_ver.value)?:""
|
||||
@@ -80,6 +130,7 @@ object Config {
|
||||
val net_use_api_proxy = PreferenceBoolean("settings_cat_net_sw_use_api_proxy", false)
|
||||
val net_img_resolution = PreferenceString(R.string.imgResolutionKeyID)
|
||||
|
||||
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)
|
||||
val view_manga_vertical_max = PreferenceInt("settings_cat_vm_sb_vertical_max", 20)
|
||||
val view_manga_quality = PreferenceInt("settings_cat_vm_sb_quality", 100)
|
||||
@@ -92,5 +143,5 @@ object Config {
|
||||
|
||||
fun getChapterInfoApiUrl(path: String?, uuid: String?, version: Int) =
|
||||
MainActivity.mainWeakReference?.get()?.getString(R.string.chapterInfoApiUrl)
|
||||
?.format(myHostApiUrl.value, path, if (version >= 2) "$version" else "" , uuid)
|
||||
?.format(myHostApiUrl.random(), path, if (version >= 2) "$version" else "" , uuid)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,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.value, path)
|
||||
private val mBookApiUrl = getString(R.string.bookInfoApiUrl).format(Config.myHostApiUrl.random(), path)
|
||||
private val mUserAgent = getString(R.string.pc_ua).format(Config.app_ver.value)
|
||||
private var mBook: BookInfoStructure? = null
|
||||
private var mGroupPathWords = arrayOf<String>()
|
||||
@@ -37,12 +37,6 @@ class Book(val path: String, private val getString: (Int) -> String, private val
|
||||
val author: Array<ThemeStructure>? get() = mBook?.results?.comic?.author
|
||||
val theme: Array<ThemeStructure>? get() = mBook?.results?.comic?.theme
|
||||
val keys get() = mKeys
|
||||
val imageType: String
|
||||
get() = when(mBook?.results?.comic?.img_type) {
|
||||
1 -> "条漫"
|
||||
2 -> "普通"
|
||||
else -> "未知类型${mBook?.results?.comic?.img_type}"
|
||||
}
|
||||
val popular get() = mBook?.results?.comic?.popular?:0
|
||||
val status get() = mBook?.results?.comic?.status?.display?:"未知"
|
||||
val updateTime get() = mBook?.results?.comic?.datetime_updated?:"未知"
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package top.fumiama.copymanga.api.manga
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.core.content.edit
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.android.synthetic.main.button_tbutton.view.*
|
||||
import kotlinx.coroutines.delay
|
||||
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
|
||||
import top.fumiama.copymanga.api.Config
|
||||
import top.fumiama.copymanga.json.VolumeStructure
|
||||
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
|
||||
import java.io.File
|
||||
@@ -81,4 +85,37 @@ object Reader {
|
||||
}
|
||||
return "N/A:null_gson"
|
||||
}
|
||||
fun getComicChapterNamesInFolder(file: File): Array<String> {
|
||||
if(!file.exists()) {
|
||||
return arrayOf()
|
||||
}
|
||||
val jsonFile = File(file, "info.json")
|
||||
if(!jsonFile.exists()) {
|
||||
return arrayOf()
|
||||
}
|
||||
Gson().fromJson(jsonFile.readText(), Array<VolumeStructure>::class.java)?.let { volumes ->
|
||||
if(volumes.isEmpty()) {
|
||||
return arrayOf()
|
||||
}
|
||||
var arr = arrayOf<String>()
|
||||
volumes.forEach { v ->
|
||||
v.results?.list?.forEach {
|
||||
arr += it.name?:""
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
return arrayOf()
|
||||
}
|
||||
fun getLocalReadingProgress(activity: FragmentActivity, name: String, chapterNames: Array<String>): String {
|
||||
var chapter = "未读"
|
||||
activity.apply {
|
||||
getPreferences(MODE_PRIVATE).getInt(name, -1).let { p ->
|
||||
if(p >= 0) chapterNames.let {
|
||||
chapter = it[if (p >= it.size) it.size-1 else p]
|
||||
}
|
||||
}
|
||||
}
|
||||
return chapter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ 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.value)
|
||||
private val apiUrl: String get() = getString(R.string.shelfOperateApiUrl).format(Config.myHostApiUrl.random())
|
||||
private val queryApiUrlTemplate = getString(R.string.bookUserQueryApiUrl)
|
||||
private val addApiUrl get() = "$apiUrl?platform=3"
|
||||
private val delApiUrl get() = "${apiUrl}s?platform=3"
|
||||
@@ -68,7 +68,7 @@ class Shelf(private val getString: (Int) -> String) {
|
||||
|
||||
suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val queryUrl = queryApiUrlTemplate.format(Config.myHostApiUrl.value, pathWord)
|
||||
val queryUrl = queryApiUrlTemplate.format(Config.myHostApiUrl.random(), pathWord)
|
||||
(Config.apiProxy?.comancry(queryUrl) { url ->
|
||||
DownloadTools.getHttpContent(url, Config.referer)
|
||||
}?:DownloadTools.getHttpContent(queryUrl, Config.referer)).let {
|
||||
|
||||
@@ -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.value, path, groupPathWord, offset)
|
||||
private fun getApiUrl(offset: Int) = mGroupInfoApiUrlTemplate.format(Config.myHostApiUrl.random(), path, groupPathWord, offset)
|
||||
private suspend fun download(re: Array<VolumeStructure?>, offset: Int, c: Int) = withContext(Dispatchers.IO) {
|
||||
Log.d("MyV", "下载偏移: $offset")
|
||||
getApiUrl(offset).let {
|
||||
|
||||
@@ -49,7 +49,7 @@ class Member(private val getString: (Int) -> String) {
|
||||
}
|
||||
try {
|
||||
val u = getString(R.string.memberInfoApiUrl)
|
||||
.format(Config.myHostApiUrl.value)
|
||||
.format(Config.myHostApiUrl.random())
|
||||
val data = (Config.apiProxy?.comancry(u) {
|
||||
DownloadTools.getHttpContent(it)
|
||||
}?:DownloadTools.getHttpContent(u)).decodeToString()
|
||||
@@ -97,7 +97,7 @@ 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.value).let { u ->
|
||||
getString(R.string.loginApiUrl).format(Config.myHostApiUrl.random()).let { u ->
|
||||
val use: suspend (String) -> ByteArray? = { it: String ->
|
||||
DownloadTools.getApiConnection(it, "POST").let { c ->
|
||||
c.doOutput = true
|
||||
@@ -129,7 +129,7 @@ class Member(private val getString: (Int) -> String) {
|
||||
|
||||
|
||||
private suspend fun postComandyLogin(username: String, pwd: String, salt: Int) =
|
||||
getString(R.string.loginApiUrl).format(Config.myHostApiUrl.value).let { u ->
|
||||
getString(R.string.loginApiUrl).format(Config.myHostApiUrl.random()).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"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package top.fumiama.copymanga.json;
|
||||
|
||||
public class NetworkStructure extends ReturnBase {
|
||||
public Results results;
|
||||
public static class Results {
|
||||
public String[] share;
|
||||
public String[][] api;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class Comandy: LazyLibrary<ComandyMethods>(
|
||||
Log.d("MyComandy", "$name block enabled for isInInit")
|
||||
return false
|
||||
}
|
||||
if (mEnabled != true && DownloadTools.failTimes.get() >= 3) {
|
||||
if (mEnabled != true && DownloadTools.failTimes.get() >= 16) {
|
||||
mEnabled = true
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2,20 +2,18 @@ package top.fumiama.copymanga.lib.template
|
||||
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.gson.Gson
|
||||
import com.sun.jna.Library
|
||||
import com.sun.jna.Native
|
||||
import kotlinx.android.synthetic.main.dialog_progress.view.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import top.fumiama.copymanga.MainActivity
|
||||
import top.fumiama.copymanga.json.ComandyVersion
|
||||
import top.fumiama.copymanga.net.Client
|
||||
import top.fumiama.copymanga.net.DownloadTools
|
||||
import top.fumiama.copymanga.storage.PreferenceBoolean
|
||||
import top.fumiama.copymanga.storage.UserPreferenceInt
|
||||
import top.fumiama.copymanga.net.DownloadTools
|
||||
import top.fumiama.copymanga.net.Client
|
||||
import top.fumiama.dmzj.copymanga.R
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.File
|
||||
@@ -54,68 +52,66 @@ open class LazyLibrary<T: Library>(
|
||||
}?:return null
|
||||
Log.d("MyLazyLibrary", "$name arch: $prefix")
|
||||
MainActivity.mainWeakReference?.get()?.let { ma ->
|
||||
ma.lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
Log.d("MyLazyLibrary", "$name launched")
|
||||
var f = File(ma.filesDir, "libs")
|
||||
if (!f.exists()) f.mkdirs()
|
||||
f = File(f, name)
|
||||
var remoteVersion = 0
|
||||
if (f.exists()) {
|
||||
DownloadTools.getHttpContent(ma.getString(R.string.comandy_version_url).format(repoName), -1)?.let dataLet@{
|
||||
withContext(Dispatchers.IO) {
|
||||
Log.d("MyLazyLibrary", "$name launched")
|
||||
var f = File(ma.filesDir, "libs")
|
||||
if (!f.exists()) f.mkdirs()
|
||||
f = File(f, name)
|
||||
var remoteVersion = 0
|
||||
if (f.exists()) {
|
||||
DownloadTools.getHttpContent(ma.getString(R.string.comandy_version_url).format(repoName), -1)?.let dataLet@{
|
||||
try {
|
||||
val body = Gson().fromJson(it.decodeToString(), ComandyVersion::class.java)?.body
|
||||
if (body?.startsWith("Version: ") == true) {
|
||||
remoteVersion = body.substring(9).toInt()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
val myVersion = version.value?:0
|
||||
if (myVersion >= remoteVersion) {
|
||||
Log.d("MyLazyLibrary", "$name version $myVersion is latest")
|
||||
isInInit.set(false)
|
||||
mLibraryFile = f
|
||||
return@withContext
|
||||
}
|
||||
Log.d("MyLazyLibrary", "$name version $myVersion <= latest $remoteVersion, update...")
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
val progressBar = ma.layoutInflater.inflate(R.layout.dialog_progress, null, false)
|
||||
val progressHandler = object : Client.Progress{
|
||||
override fun notify(progressPercentage: Int) {
|
||||
Log.d("MyLazyLibrary", "Set dl $name progress: $progressPercentage")
|
||||
progressBar.dpp.progress = progressPercentage
|
||||
}
|
||||
}
|
||||
val info = ma.toolsBox.buildAlertWithView("加载${functionName}组件", progressBar, "隐藏")
|
||||
withContext(Dispatchers.IO) {
|
||||
DownloadTools.getHttpContent(ma.getString(R.string.comandy_download_url).format(repoName, prefix, name), -1, progressHandler)?.let {
|
||||
if(f.exists()) f.delete()
|
||||
try {
|
||||
val body = Gson().fromJson(it.decodeToString(), ComandyVersion::class.java)?.body
|
||||
if (body?.startsWith("Version: ") == true) {
|
||||
remoteVersion = body.substring(9).toInt()
|
||||
GZIPInputStream(ByteArrayInputStream(it)).use { dataIn ->
|
||||
f.outputStream().use { dataOut ->
|
||||
dataIn.copyTo(dataOut)
|
||||
}
|
||||
}
|
||||
if (remoteVersion > 0) version.value = remoteVersion
|
||||
Log.d("MyLazyLibrary", "update success")
|
||||
isInInit.set(false)
|
||||
withContext(Dispatchers.Main) {
|
||||
info.dismiss()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
val myVersion = version.value?:0
|
||||
if (myVersion >= remoteVersion) {
|
||||
Log.d("MyLazyLibrary", "$name version $myVersion is latest")
|
||||
isInInit.set(false)
|
||||
mLibraryFile = f
|
||||
return@withContext
|
||||
}
|
||||
Log.d("MyLazyLibrary", "$name version $myVersion <= latest $remoteVersion, update...")
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
val progressBar = ma.layoutInflater.inflate(R.layout.dialog_progress, null, false)
|
||||
val progressHandler = object : Client.Progress{
|
||||
override fun notify(progressPercentage: Int) {
|
||||
Log.d("MyLazyLibrary", "Set dl $name progress: $progressPercentage")
|
||||
progressBar.dpp.progress = progressPercentage
|
||||
}
|
||||
}
|
||||
val info = ma.toolsBox.buildAlertWithView("加载${functionName}组件", progressBar, "隐藏")
|
||||
withContext(Dispatchers.IO) {
|
||||
DownloadTools.getHttpContent(ma.getString(R.string.comandy_download_url).format(repoName, prefix, name), -1, progressHandler)?.let {
|
||||
if(f.exists()) f.delete()
|
||||
try {
|
||||
GZIPInputStream(ByteArrayInputStream(it)).use { dataIn ->
|
||||
f.outputStream().use { dataOut ->
|
||||
dataIn.copyTo(dataOut)
|
||||
}
|
||||
}
|
||||
if (remoteVersion > 0) version.value = remoteVersion
|
||||
Log.d("MyLazyLibrary", "update success")
|
||||
isInInit.set(false)
|
||||
withContext(Dispatchers.Main) {
|
||||
info.dismiss()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
if(f.exists()) f.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mLibraryFile = if(f.exists()) f else null
|
||||
return@withContext
|
||||
}
|
||||
}.join()
|
||||
mLibraryFile = if(f.exists()) f else null
|
||||
return@withContext
|
||||
}
|
||||
}
|
||||
return mLibraryFile
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.bumptech.glide.module.AppGlideModule
|
||||
import com.bumptech.glide.signature.ObjectKey
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import top.fumiama.copymanga.api.Config.proxyUrl
|
||||
import top.fumiama.copymanga.json.ComandyCapsule
|
||||
import top.fumiama.copymanga.lib.Comandy
|
||||
import java.nio.ByteBuffer
|
||||
@@ -86,7 +87,7 @@ class ComandyGlideModule: AppGlideModule() {
|
||||
|
||||
override fun handles(model: GlideUrl): Boolean {
|
||||
return Comandy.instance.enabled && model.toURL().let {
|
||||
it.protocol == "https" && it.host != "copymanga.azurewebsites.net"
|
||||
it.protocol == "https" && it.host != proxyUrl
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +106,7 @@ class ComandyGlideModule: AppGlideModule() {
|
||||
override fun handles(model: String): Boolean {
|
||||
return Comandy.instance.enabled &&
|
||||
model.startsWith("https://") &&
|
||||
!model.startsWith("https://copymanga.azurewebsites.net")
|
||||
!model.startsWith("https://$proxyUrl")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ 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.ByteArrayOutputStream
|
||||
@@ -16,34 +17,40 @@ import java.io.OutputStream
|
||||
import java.lang.Thread.sleep
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.FutureTask
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
object DownloadTools {
|
||||
val failTimes = AtomicInteger(0)
|
||||
fun getApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null, timeout: Int = 20000) =
|
||||
url.let {
|
||||
val connection = URL(url).openConnection() as HttpURLConnection
|
||||
connection.requestMethod = method
|
||||
connection.connectTimeout = timeout
|
||||
connection.readTimeout = timeout
|
||||
connection.apply {
|
||||
setRequestProperty("host", url.substringAfter("://").substringBefore("/"))
|
||||
ua?.let { setRequestProperty("user-agent", it) }
|
||||
refer?.let { setRequestProperty("referer", it) }
|
||||
setRequestProperty("source", "copyApp")
|
||||
setRequestProperty("webp", "1")
|
||||
setRequestProperty("region", if(!Config.net_use_foreign.value) "1" else "0")
|
||||
setRequestProperty("version", Config.app_ver.value)
|
||||
Config.token.value?.let { tk ->
|
||||
setRequestProperty("authorization", "Token $tk")
|
||||
}
|
||||
setRequestProperty("platform", "3")
|
||||
fun getApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null, 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("source", "copyApp")
|
||||
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")
|
||||
}
|
||||
Log.d("MyDT", "getConnection: $url\n${connection.requestProperties.map { "${it.key}: ${it.value}" }.joinToString("\n")}")
|
||||
connection
|
||||
setRequestProperty("platform", "3")
|
||||
}
|
||||
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) =
|
||||
run {
|
||||
@@ -51,7 +58,11 @@ object DownloadTools {
|
||||
capsule.url = url
|
||||
capsule.method = method
|
||||
capsule.headers = hashMapOf()
|
||||
capsule.headers["host"] = url.substringAfter("://").substringBefore("/")
|
||||
/*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["source"] = "copyApp"
|
||||
@@ -64,6 +75,7 @@ object DownloadTools {
|
||||
}
|
||||
}
|
||||
capsule.headers["platform"] = "3"
|
||||
capsule.headers["dt"] = SimpleDateFormat("yyyy.MM.dd", Locale.getDefault()).format(Calendar.getInstance().time)
|
||||
Log.d("MyDT", "getComandyConnection: $url\n${capsule.headers.map { "${it.key}: ${it.value}" }.joinToString("\n")}")
|
||||
capsule
|
||||
}
|
||||
@@ -119,7 +131,7 @@ object DownloadTools {
|
||||
|
||||
suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = Config.pc_ua, p: Client.Progress? = null): ByteArray =
|
||||
withContext(Dispatchers.IO) {
|
||||
if (!u.startsWith("https://copymanga.azurewebsites.net") && Comandy.instance.enabled) {
|
||||
if (!u.startsWith("https://$proxyUrl") && Comandy.instance.enabled) {
|
||||
getComandyApiConnection(u, "GET", refer, ua).let { capsule ->
|
||||
val para = Gson().toJson(capsule)
|
||||
//Log.d("MyDT", "comandy request: $para")
|
||||
@@ -188,7 +200,7 @@ object DownloadTools {
|
||||
// prepare p only take effect when readSize is -1
|
||||
fun prepare(u: String, readSize: Int = -1, p: Client.Progress? = null) = run {
|
||||
Log.d("MyDT", "prepareHttp: $u")
|
||||
FutureTask(if (!u.startsWith("https://copymanga.azurewebsites.net") && Comandy.instance.enabled) Callable{
|
||||
FutureTask(if (!u.startsWith("https://$proxyUrl") && Comandy.instance.enabled) Callable{
|
||||
try {
|
||||
runBlocking { Comandy.instance.getInstance() }?.let { ins ->
|
||||
runBlocking {
|
||||
@@ -261,7 +273,7 @@ object DownloadTools {
|
||||
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? {
|
||||
Log.d("MyDT", "$method Http: $url")
|
||||
var ret: ByteArray? = null
|
||||
val task = FutureTask(if(!url.startsWith("https://copymanga.azurewebsites.net") && Comandy.instance.enabled) Callable{
|
||||
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 }
|
||||
|
||||
@@ -44,6 +44,7 @@ class Proxy(id: Int, private val apiRegex: Regex) {
|
||||
?:u
|
||||
Log.d("MyP", "[M] comancry: $wu, sd: $sourceDir")
|
||||
return use(wu)?.let { data ->
|
||||
Log.d("MyP", "[M] comancry: decrypt ${data.size} bytes data")
|
||||
Comancry.instance.decrypt(sourceDir, data)?.encodeToByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.io.File
|
||||
import java.security.MessageDigest
|
||||
|
||||
open class AutoDownloadHandler(
|
||||
private val url: String, private val jsonClass: Class<*>,
|
||||
private val url: () -> String, private val jsonClass: Class<*>,
|
||||
private val context: LifecycleOwner?,
|
||||
private val loadFromCache: Boolean = false,
|
||||
private val customCacheFile: File? = null): Handler(Looper.myLooper()!!) {
|
||||
@@ -57,7 +57,7 @@ open class AutoDownloadHandler(
|
||||
toString()
|
||||
}
|
||||
private suspend fun downloadCoroutine() = withContext(Dispatchers.IO) {
|
||||
val cacheName = toHexStr(MessageDigest.getInstance("MD5").digest(url.encodeToByteArray()))
|
||||
val cacheName = toHexStr(MessageDigest.getInstance("MD5").digest(url().encodeToByteArray()))
|
||||
val cacheFile = customCacheFile?:(mainWeakReference?.get()?.externalCacheDir?.let { File(it, cacheName) })
|
||||
if(loadFromCache) {
|
||||
cacheFile?.let {
|
||||
@@ -77,9 +77,9 @@ open class AutoDownloadHandler(
|
||||
var cnt = 0
|
||||
while (cnt++ <= 3) {
|
||||
try {
|
||||
val data = Config.apiProxy?.comancry(url) {
|
||||
val data = Config.apiProxy?.comancry(url()) {
|
||||
DownloadTools.getHttpContent(it)
|
||||
}?:DownloadTools.getHttpContent(url)
|
||||
}?:DownloadTools.getHttpContent(url())
|
||||
if(exit) return@withContext
|
||||
val fi = data.inputStream()
|
||||
val pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass))
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
package top.fumiama.copymanga.storage
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.fragment.app.Fragment
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
object FileUtils {
|
||||
fun recursiveRemove(f: File) {
|
||||
@@ -19,4 +28,52 @@ object FileUtils {
|
||||
}
|
||||
return size
|
||||
}
|
||||
fun registerZipExportLauncher(
|
||||
fragment: Fragment,
|
||||
callback: (Uri?) -> Unit
|
||||
): ActivityResultLauncher<String> {
|
||||
return fragment.registerForActivityResult(ActivityResultContracts.CreateDocument("application/zip"), callback)
|
||||
}
|
||||
fun compressToUserFile(
|
||||
context: Context,
|
||||
sourceDir: File,
|
||||
targetUri: Uri,
|
||||
setProgress: (Int) -> Unit = {}
|
||||
) {
|
||||
val allFiles = collectAllFiles(sourceDir)
|
||||
val totalFiles = allFiles.size
|
||||
var filesDone = 0
|
||||
|
||||
setProgress(0) // 确保初始进度为0%
|
||||
|
||||
context.contentResolver.openOutputStream(targetUri)?.use { outputStream ->
|
||||
ZipOutputStream(BufferedOutputStream(outputStream)).use { zipOut ->
|
||||
for (file in allFiles) {
|
||||
val relativePath = sourceDir.toURI().relativize(file.toURI()).path
|
||||
val entry = ZipEntry(relativePath)
|
||||
zipOut.putNextEntry(entry)
|
||||
|
||||
FileInputStream(file).use { input ->
|
||||
input.copyTo(zipOut)
|
||||
}
|
||||
|
||||
zipOut.closeEntry()
|
||||
filesDone++
|
||||
val progress = (filesDone * 100 / totalFiles).coerceIn(0, 100)
|
||||
setProgress(progress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setProgress(100) // 确保最终进度为100%
|
||||
}
|
||||
|
||||
private fun collectAllFiles(root: File): List<File> {
|
||||
val files = mutableListOf<File>()
|
||||
root.walkTopDown().forEach {
|
||||
if (it.isFile) files.add(it)
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
}
|
||||
@@ -140,17 +140,11 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
|
||||
fun setReadTo() {
|
||||
var chapter = "未读"
|
||||
if(!mBookHandler?.chapterNames.isNullOrEmpty()) {
|
||||
activity?.apply {
|
||||
chapter = "读至 ${activity?.let { a ->
|
||||
book?.name?.let { name ->
|
||||
getPreferences(MODE_PRIVATE).getInt(name, -1).let { p ->
|
||||
if(p >= 0) mBookHandler!!.chapterNames.let {
|
||||
chapter = it[if (p >= it.size) it.size-1 else p]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reader.getLocalReadingProgress(a, name, mBookHandler!!.chapterNames)
|
||||
} }}"
|
||||
}
|
||||
chapter = "读至 $chapter"
|
||||
this@BookFragment.bttag.text = chapter
|
||||
}
|
||||
|
||||
|
||||
@@ -218,13 +218,16 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
|
||||
}
|
||||
var last = i-1
|
||||
val comicName = name?:return@withContext
|
||||
val init = i
|
||||
Log.d("MyBH", "i = $i, last=$last, add comic $comicName, volume $p")
|
||||
volumes[p].let { v ->
|
||||
if(exit) return@withContext
|
||||
var line: View? = null
|
||||
last += v.results.list.size
|
||||
v.results.list.forEach {
|
||||
val inv = Config.view_manga_inverse_chapters.value
|
||||
(if(!inv) v.results.list.toList() else v.results.list.reversed()).forEach {
|
||||
val f = Config.getZipFile(context?.getExternalFilesDir(""), comicName, keys[p], it.name)
|
||||
//Log.d("MyBH", "i = $i, last=$last, add chapter ${it.name}, line is null: ${line == null}")
|
||||
Log.d("MyBH", "i = $i, last=$last, add chapter ${it.name}, line is null: ${line == null}")
|
||||
that?.isOnPause?.let { isOnPause ->
|
||||
while (isOnPause && !exit) delay(500)
|
||||
if (exit) return@withContext
|
||||
@@ -236,7 +239,7 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
|
||||
lct.text = it.name
|
||||
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
||||
Log.d("MyBH", "add last single chapter ${it.name}")
|
||||
val index = i
|
||||
val index = if (!inv) i else init
|
||||
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray, uuidArray) }
|
||||
}
|
||||
line?.let { l -> addVolumesView(fbl, l) }
|
||||
@@ -245,14 +248,14 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
|
||||
line?.l2cl?.apply {
|
||||
lct.text = it.name
|
||||
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
||||
val index = i
|
||||
val index = if (!inv) i else (init+last-i)
|
||||
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray, uuidArray) }
|
||||
}
|
||||
}
|
||||
} else line?.l2cr?.apply {
|
||||
lct.text = it.name
|
||||
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
||||
val index = i
|
||||
val index = if (!inv) i else (init+last-i)
|
||||
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray, uuidArray) }
|
||||
line?.let { l -> addVolumesView(fbl, l) }
|
||||
line = null
|
||||
|
||||
@@ -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.value, page * 21)
|
||||
getString(R.string.historyApiUrl).format(Config.myHostApiUrl.random(), page * 21)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (MainActivity.member?.hasLogin != true) {
|
||||
|
||||
@@ -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.value, page * 21)
|
||||
getString(R.string.newestApiUrl).format(Config.myHostApiUrl.random(), page * 21)
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank
|
||||
|
||||
override fun getApiUrl() =
|
||||
getString(R.string.rankApiUrl).format(
|
||||
Config.myHostApiUrl.value,
|
||||
Config.myHostApiUrl.random(),
|
||||
page * 21,
|
||||
sortWay[sortValue],
|
||||
audienceWay[audience]
|
||||
|
||||
@@ -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.value, page * 21)
|
||||
getString(R.string.recommendApiUrl).format(Config.myHostApiUrl.random(), page * 21)
|
||||
}
|
||||
@@ -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.value, page * 21, query, type)
|
||||
getString(R.string.searchApiUrl).format(Config.myHostApiUrl.random(), page * 21, query, type)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -26,7 +26,7 @@ class ShelfFragment : InfoCardLoader(R.layout.fragment_shelf, R.id.action_nav_su
|
||||
|
||||
override fun getApiUrl() =
|
||||
getString(R.string.shelfApiUrl).format(
|
||||
Config.myHostApiUrl.value,
|
||||
Config.myHostApiUrl.random(),
|
||||
page * 21,
|
||||
sortWay[sortValue]
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ class SortFragment : StatusCardFlow(0, R.id.action_nav_sort_to_nav_book, R.layou
|
||||
|
||||
override fun getApiUrl() =
|
||||
getString(R.string.sortApiUrl).format(
|
||||
Config.myHostApiUrl.value,
|
||||
Config.myHostApiUrl.random(),
|
||||
page * 21,
|
||||
sortWay[sortValue],
|
||||
if(theme >= 0 && theme < (filter?.results?.theme?.size ?: 0)) (filter?.results?.theme?.get(theme)?.path_word ?: "") else "",
|
||||
@@ -42,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.value)) {
|
||||
PausableDownloader(getString(R.string.filterApiUrl).format(Config.myHostApiUrl.random())) {
|
||||
if(ad?.exit == true) return@PausableDownloader
|
||||
it.let {
|
||||
it.inputStream().use { i ->
|
||||
|
||||
@@ -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.value, arguments?.getString("path"), type, offset)
|
||||
getString(R.string.topicContentApiUrl).format(Config.myHostApiUrl.random(), arguments?.getString("path"), type, offset)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycleScope.launch {
|
||||
setProgress(5)
|
||||
PausableDownloader(getString(R.string.topicApiUrl).format(Config.myHostApiUrl.value, arguments?.getString("path"))) { data ->
|
||||
PausableDownloader(getString(R.string.topicApiUrl).format(Config.myHostApiUrl.random(), arguments?.getString("path"))) { data ->
|
||||
setProgress(10)
|
||||
withContext(Dispatchers.IO) {
|
||||
if(ad?.exit == true) return@withContext
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
package top.fumiama.copymanga.ui.download
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import kotlinx.android.synthetic.main.dialog_progress.view.*
|
||||
import kotlinx.android.synthetic.main.line_header.view.*
|
||||
import kotlinx.android.synthetic.main.line_lazybooklines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
|
||||
import top.fumiama.copymanga.MainActivity
|
||||
import top.fumiama.copymanga.api.Config.manga_dl_show_0m_manga
|
||||
import top.fumiama.copymanga.api.manga.Downloader
|
||||
import top.fumiama.copymanga.api.manga.Reader
|
||||
import top.fumiama.copymanga.view.template.MangaPagesFragmentTemplate
|
||||
import top.fumiama.copymanga.view.template.CardList
|
||||
import top.fumiama.copymanga.net.Client
|
||||
import top.fumiama.copymanga.storage.FileUtils
|
||||
import top.fumiama.copymanga.storage.FileUtils.compressToUserFile
|
||||
import top.fumiama.copymanga.view.interaction.Navigate
|
||||
import top.fumiama.copymanga.view.interaction.UITools
|
||||
import top.fumiama.copymanga.view.template.CardList
|
||||
import top.fumiama.copymanga.view.template.MangaPagesFragmentTemplate
|
||||
import top.fumiama.dmzj.copymanga.R
|
||||
import java.io.File
|
||||
import java.lang.ref.WeakReference
|
||||
@@ -27,25 +35,48 @@ import java.lang.ref.WeakReference
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownload, forceLoad = true) {
|
||||
private var sortedBookList: List<File>? = null
|
||||
private val oldDlCardName = mainWeakReference?.get()?.getString(R.string.old_download_card_name)!!
|
||||
private val extDir = mainWeakReference?.get()?.getExternalFilesDir("")
|
||||
private val oldDlCardName by lazy { getString(R.string.old_download_card_name) }
|
||||
private val extDir by lazy { activity?.getExternalFilesDir("") }
|
||||
private var isReverse = false
|
||||
private var isContentChanged = false
|
||||
private var exit = false
|
||||
private val showAll get() = manga_dl_show_0m_manga.value
|
||||
private var compressIntoFile: ActivityResultLauncher<String>? = null
|
||||
private var compressName = ""
|
||||
private var compressFile: File? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
wn = WeakReference(this)
|
||||
|
||||
compressIntoFile = FileUtils.registerZipExportLauncher(this@NewDownloadFragment) { uri ->
|
||||
val progressBar = layoutInflater.inflate(R.layout.dialog_progress, null, false)
|
||||
val progressHandler = object : Client.Progress{
|
||||
override fun notify(progressPercentage: Int) {
|
||||
progressBar?.dpp?.progress = progressPercentage
|
||||
}
|
||||
}
|
||||
val info = (activity as MainActivity).toolsBox.buildAlertWithView("压缩${compressName}.zip", progressBar!!, "隐藏")
|
||||
val f = compressFile!!
|
||||
Thread{
|
||||
uri?.let { context?.let { ctx -> compressToUserFile(ctx, f, it) {
|
||||
progressHandler.notify(it)
|
||||
if (it >= 100) activity?.runOnUiThread { info.dismiss() }
|
||||
} } }
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
exit = true
|
||||
wn = null
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
exit = false
|
||||
wn = WeakReference(this)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@@ -54,6 +85,7 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
||||
exit = true
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override suspend fun addPage(): Unit = withContext(Dispatchers.IO) {
|
||||
super.addPage()
|
||||
if(isRefresh) {
|
||||
@@ -105,8 +137,18 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
||||
cnt = 1
|
||||
}
|
||||
val size = sortedBookList?.size?:0
|
||||
sortedBookList?.let {
|
||||
for(i in it.listIterator(page)) {
|
||||
sortedBookList?.let { lst ->
|
||||
withContext(Dispatchers.Main) {
|
||||
mysp?.footerView?.lht?.text = "$page+"
|
||||
activity?.findViewById<Toolbar>(R.id.toolbar)?.let { appbar ->
|
||||
appbar.title.let { title ->
|
||||
if (!title.endsWith(")")) {
|
||||
appbar.title = "$title (${lst.size})"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i in lst.listIterator(page)) {
|
||||
if(cardList?.exitCardList != false) return@withContext
|
||||
page++ // page is actually count
|
||||
val chosenJson = File(i, "info.bin")
|
||||
@@ -120,14 +162,16 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
||||
chosenJson.exists() -> continue // unsupported old folder
|
||||
newJson.exists() -> {
|
||||
if(cardList?.exitCardList != false) return@withContext
|
||||
cardList?.addCard(i.name, bookSize)
|
||||
cardList?.addCard(i.name, "\n本地读至 ${activity?.let { a ->
|
||||
Reader.getLocalReadingProgress(a, i.name, Reader.getComicChapterNamesInFolder(i))
|
||||
}}$bookSize")
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
setProgress(80+20*(cnt-1)/size)
|
||||
if (cnt >= 21) break
|
||||
}
|
||||
if(page >= it.size) {
|
||||
if(page >= lst.size) {
|
||||
isEnd = true
|
||||
}
|
||||
}
|
||||
@@ -155,7 +199,7 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
||||
AlertDialog.Builder(context)
|
||||
.setIcon(R.drawable.ic_launcher_foreground)
|
||||
.setTitle(R.string.new_download_card_option_hint)
|
||||
.setItems(arrayOf("删除数据文件夹", "直接前往下载页")) { d, p ->
|
||||
.setItems(arrayOf("删除数据文件夹", "直接前往下载页", "导出压缩包")) { d, p ->
|
||||
d.cancel()
|
||||
when (p) {
|
||||
0 -> {
|
||||
@@ -174,10 +218,14 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
||||
.show()
|
||||
}
|
||||
1 -> callDownloadFragment(name)
|
||||
2 -> {
|
||||
compressName = name
|
||||
compressFile = chosenFile
|
||||
compressIntoFile?.launch("${name}.zip")
|
||||
}
|
||||
}
|
||||
}
|
||||
.show()
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -220,6 +268,10 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
||||
)
|
||||
}
|
||||
|
||||
fun importMangaFromZip() {
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
var wn: WeakReference<NewDownloadFragment>? = null
|
||||
}
|
||||
|
||||
@@ -298,7 +298,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.value, 0,
|
||||
PausableDownloader(getString(R.string.searchApiUrl).format(Config.myHostApiUrl.random(), 0,
|
||||
URLEncoder.encode(q.toString(), Charset.defaultCharset().name()), type)) {
|
||||
results = Gson().fromJson(it.decodeToString(), BookListStructure::class.java)
|
||||
count = results?.results?.total?:0
|
||||
|
||||
@@ -39,8 +39,9 @@ import top.fumiama.dmzj.copymanga.R
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadHandler(
|
||||
that.get()?.getString(R.string.mainPageApiUrl)!!.format(Config.myHostApiUrl.value),
|
||||
class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadHandler({
|
||||
that.get()?.getString(R.string.mainPageApiUrl)!!.format(Config.myHostApiUrl.random())
|
||||
},
|
||||
IndexStructure::class.java,
|
||||
that.get()
|
||||
) {
|
||||
|
||||
@@ -3,14 +3,19 @@ package top.fumiama.copymanga.ui.settings
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import top.fumiama.copymanga.api.Config.proxyUrl
|
||||
import top.fumiama.copymanga.view.interaction.UITools
|
||||
import top.fumiama.copymanga.view.AutoHideEditTextPreferenceDialogFragmentCompat
|
||||
import top.fumiama.dmzj.copymanga.R
|
||||
@@ -22,6 +27,21 @@ class SettingsFragment: PreferenceFragmentCompat() {
|
||||
delay(300)
|
||||
withContext(Dispatchers.Main) {
|
||||
setPreferencesFromResource(R.xml.pref_setting, rootKey)
|
||||
findPreference<ListPreference>(getString(R.string.darkModeKeyID))?.setOnPreferenceChangeListener { _, newValue ->
|
||||
when ((newValue as String).toInt()) {
|
||||
0 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
1 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||
2 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||
}
|
||||
true // 允许保存新值
|
||||
}
|
||||
context?.let { PreferenceManager.getDefaultSharedPreferences(it) }?.let {
|
||||
if (it.getString(getString(R.string.reverseProxyKeyID), "") != proxyUrl) {
|
||||
findPreference<SwitchPreferenceCompat>(getString(R.string.apiProxyKeyID))?.isVisible = false
|
||||
findPreference<SwitchPreferenceCompat>(getString(R.string.imgProxyKeyID))?.isVisible = false
|
||||
findPreference<EditTextPreference>(getString(R.string.imgProxyCodeKeyID))?.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, private val weeks: Array<String>) : AutoDownloadHandler(
|
||||
chapterUrl, Chapter2Return::class.java, activity
|
||||
{ chapterUrl }, Chapter2Return::class.java, activity
|
||||
) {
|
||||
var manga: Chapter2Return? = null
|
||||
private val wv = WeakReference(activity)
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package top.fumiama.copymanga.view.template
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.android.synthetic.main.line_header.view.*
|
||||
import kotlinx.android.synthetic.main.line_lazybooklines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -18,6 +22,7 @@ import top.fumiama.copymanga.json.TypeBookListStructure
|
||||
import top.fumiama.copymanga.net.template.PausableDownloader
|
||||
import top.fumiama.copymanga.strings.Chinese
|
||||
import top.fumiama.copymanga.view.interaction.Navigate
|
||||
import top.fumiama.dmzj.copymanga.R
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
@@ -26,6 +31,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
|
||||
private val subUrl get() = getApiUrl()
|
||||
var ad: PausableDownloader? = null
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override suspend fun addPage(): Unit = withContext(Dispatchers.IO) {
|
||||
super.addPage()
|
||||
setProgress(20)
|
||||
@@ -38,6 +44,16 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
|
||||
val bookList = Gson().fromJson(data.decodeToString(), TypeBookListStructure::class.java)
|
||||
bookList?.apply {
|
||||
Log.d("MyICL", "offset:${results.offset}, total:${results.total}")
|
||||
withContext(Dispatchers.Main) {
|
||||
mysp?.footerView?.lht?.text = "${results.offset}+"
|
||||
activity?.findViewById<Toolbar>(R.id.toolbar)?.let { appbar ->
|
||||
appbar.title.let {
|
||||
if (!it.endsWith(")")) {
|
||||
appbar.title = "$it (${results.total})"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(results.offset < results.total) {
|
||||
if(code == 200) {
|
||||
val size = results?.list?.size?:0
|
||||
@@ -60,6 +76,16 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
|
||||
val bookList = Gson().fromJson(data.decodeToString(), HistoryBookListStructure::class.java)
|
||||
bookList?.apply {
|
||||
Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
|
||||
withContext(Dispatchers.Main) {
|
||||
mysp?.footerView?.lht?.text = "${results.offset}+"
|
||||
activity?.findViewById<Toolbar>(R.id.toolbar)?.let { appbar ->
|
||||
appbar.title.let {
|
||||
if (!it.endsWith(")")) {
|
||||
appbar.title = "$it (${results.total})"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(results.offset < results.total) {
|
||||
if(code == 200) {
|
||||
val size = results?.list?.size?:0
|
||||
@@ -82,6 +108,16 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
|
||||
val bookList = Gson().fromJson(data.decodeToString(), ShelfStructure::class.java)
|
||||
bookList?.apply {
|
||||
Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
|
||||
withContext(Dispatchers.Main) {
|
||||
mysp?.footerView?.lht?.text = "${results.offset}+"
|
||||
activity?.findViewById<Toolbar>(R.id.toolbar)?.let { appbar ->
|
||||
appbar.title.let {
|
||||
if (!it.endsWith(")")) {
|
||||
appbar.title = "$it (${results.total})"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(results.offset < results.total) {
|
||||
if(code == 200) {
|
||||
val size = results?.list?.size?:0
|
||||
@@ -105,6 +141,16 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
|
||||
val bookList = Gson().fromJson(data.decodeToString(), BookListStructure::class.java)
|
||||
bookList?.apply {
|
||||
Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
|
||||
withContext(Dispatchers.Main) {
|
||||
mysp?.footerView?.lht?.text = "${results.offset}+"
|
||||
activity?.findViewById<Toolbar>(R.id.toolbar)?.let { appbar ->
|
||||
appbar.title.let {
|
||||
if (!it.endsWith(")")) {
|
||||
appbar.title = "$it (${results.total})"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(results.offset < results.total) {
|
||||
if(code == 200) {
|
||||
val size = results?.list?.size?:0
|
||||
|
||||
@@ -18,7 +18,7 @@ open class StatusCardFlow(private val api: Int, nav: Int, inflateRes: Int,
|
||||
|
||||
override fun getApiUrl() =
|
||||
getString(api).format(
|
||||
Config.myHostApiUrl.value,
|
||||
Config.myHostApiUrl.random(),
|
||||
page * 21,
|
||||
sortWay[sortValue]
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ open class ThemeCardFlow(private val api: Int, nav: Int) : StatusCardFlow(0, nav
|
||||
private var theme = ""
|
||||
override fun getApiUrl() =
|
||||
getString(api).format(
|
||||
Config.myHostApiUrl.value,
|
||||
Config.myHostApiUrl.random(),
|
||||
page * 21,
|
||||
sortWay[sortValue],
|
||||
theme
|
||||
|
||||
@@ -38,4 +38,14 @@
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
</string-array>
|
||||
<string-array name="themes">
|
||||
<item>跟随系统</item>
|
||||
<item>浅色</item>
|
||||
<item>深色</item>
|
||||
</string-array>
|
||||
<string-array name="themes_ids">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE resources [
|
||||
<!ENTITY hosturl "www.copy20.com">
|
||||
<!ENTITY appver "2.2.9">
|
||||
<!ENTITY hosturl "api.copy-manga.com">
|
||||
<!ENTITY appver "2.3.0">
|
||||
<!ENTITY proxyurl "copymanga.azurewebsites.net">
|
||||
]>
|
||||
<resources>
|
||||
<string name="app_name">拷贝漫画</string>
|
||||
@@ -66,6 +67,7 @@
|
||||
<string name="mainPageApiUrl">https://%1$s/api/v3/h5/homeIndex?platform=3</string>
|
||||
<string name="referUrl">https://%1$s</string>
|
||||
<string name="hostUrl">&hosturl;</string>
|
||||
<string name="proxyUrl">&proxyurl;</string>
|
||||
<string name="rankApiUrl">https://%1$s/api/v3/ranks?limit=21&offset=%2$d&date_type=%3$s&audience_type=%4$s&platform=3</string>
|
||||
<string name="searchApiUrl">https://%1$s/api/v3/search/comic?limit=21&offset=%2$d&q=%3$s&q_type=%4$s&platform=3</string>
|
||||
<string name="filterApiUrl">https://%1$s/api/v3/h5/filter/comic/tags?platform=3</string>
|
||||
@@ -87,11 +89,16 @@
|
||||
<string name="shelfApiUrl">https://%1$s/api/v3/member/collect/comics?limit=21&offset=%2$d&free_type=1&ordering=%3$s&platform=3</string>
|
||||
<string name="shelfOperateApiUrl">https://%1$s/api/v3/member/collect/comic</string>
|
||||
|
||||
<string name="imgProxyApiUrl">https://copymanga.azurewebsites.net/api/img?code=%1$s&url=%2$s</string>
|
||||
<string name="imgProxyKeyID">settings_cat_net_et_img_proxy_code</string>
|
||||
<string name="apiProxyApiUrl">https://copymanga.azurewebsites.net/api/api?code=%1$s&url=%2$s</string>
|
||||
<string name="imgProxyApiUrl">https://&proxyurl;/api/img?code=%1$s&url=%2$s</string>
|
||||
<string name="imgProxyCodeKeyID">settings_cat_net_et_img_proxy_code</string>
|
||||
<string name="apiProxyApiUrl">https://&proxyurl;/api/api?code=%1$s&url=%2$s</string>
|
||||
<string name="imgResolutionKeyID">settings_cat_net_sb_image_resolution</string>
|
||||
|
||||
<string name="darkModeKeyID">settings_cat_general_lp_dark_mode</string>
|
||||
<string name="reverseProxyKeyID">settings_cat_net_et_reverse_proxy</string>
|
||||
<string name="apiProxyKeyID">settings_cat_net_sw_use_api_proxy</string>
|
||||
<string name="imgProxyKeyID">settings_cat_net_sw_use_img_proxy</string>
|
||||
|
||||
<string name="comandy_version_url">https://gitea.seku.su/api/v1/repos/fumiama/%1$s/releases/tags/default</string>
|
||||
<string name="comandy_download_url">"https://gitea.seku.su/fumiama/%1$s/releases/download/default/%2$s_%3$s.gz"</string>
|
||||
|
||||
@@ -141,6 +148,8 @@
|
||||
<string name="caption">标签</string>
|
||||
|
||||
<string name="settings_cat_general">通用</string>
|
||||
<string name="settings_cat_general_lp_title_dark_mode">主题</string>
|
||||
<string name="settings_cat_general_lp_summary_dark_mode">默认跟随系统</string>
|
||||
<string name="settings_cat_general_sb_title_startup_menu">启动时显示</string>
|
||||
<string name="settings_cat_general_sb_summary_startup_menu">默认主页</string>
|
||||
<string name="settings_cat_general_et_title_app_version">拷贝版本号</string>
|
||||
@@ -163,14 +172,18 @@
|
||||
<string name="settings_cat_net_sm_use_comandy">使用经过优化的请求方法访问服务器</string>
|
||||
<string name="settings_cat_net_et_title_api_url">请求API网址</string>
|
||||
<string name="settings_cat_net_et_summary_api_url">一般无需更改,除非拷贝漫画官方更改网址,默认:&hosturl;</string>
|
||||
<string name="settings_cat_net_et_title_reverse_proxy">反向代理</string>
|
||||
<string name="settings_cat_net_et_summary_reverse_proxy">您可以自建反向代理并填写在此处以解决网络不畅</string>
|
||||
<string name="settings_cat_net_sw_use_api_proxy">使用API代理(需要密钥)</string>
|
||||
<string name="settings_cat_net_sm_use_api_proxy">作者自建的API代理,可缓解国内图书详情加载问题,但不保证100%解决,也不保证一直可用</string>
|
||||
<string name="settings_cat_net_sw_use_img_proxy">使用图床代理(需要密钥)</string>
|
||||
<string name="settings_cat_net_sm_use_img_proxy">作者自建的图床代理,可缓解国内图片无法加载问题,但不保证100%解决,也不保证一直可用</string>
|
||||
<string name="settings_cat_net_et_title_img_proxy">代理密钥</string>
|
||||
<string name="settings_cat_net_et_summary_img_proxy">为避免滥用,该密钥需加群(559748702)获得,且随时可能会刷新</string>
|
||||
<string name="settings_cat_net_et_summary_img_proxy">为避免滥用,该密钥随时可能会刷新</string>
|
||||
|
||||
<string name="settings_cat_vm">漫画浏览</string>
|
||||
<string name="settings_cat_vm_sw_inverse_chapters">倒序排列章节</string>
|
||||
<string name="settings_cat_vm_sm_inverse_chapters">打开后详情页将从后往前排列每一话</string>
|
||||
<string name="settings_cat_vm_sw_always_dark_bg">深色阅读背景</string>
|
||||
<string name="settings_cat_vm_sm_always_dark_bg">打开后阅览漫画的背景色永远为黑色</string>
|
||||
<string name="settings_cat_vm_sw_hide_info">隐藏底部时间栏</string>
|
||||
|
||||
@@ -4,6 +4,16 @@
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/settings_cat_general">
|
||||
<ListPreference
|
||||
android:max="2"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="@string/darkModeKeyID"
|
||||
app:selectable="true"
|
||||
app:summary="@string/settings_cat_general_lp_summary_dark_mode"
|
||||
app:title="@string/settings_cat_general_lp_title_dark_mode"
|
||||
app:entries="@array/themes"
|
||||
app:entryValues="@array/themes_ids"
|
||||
android:defaultValue="0"/>
|
||||
<ListPreference
|
||||
android:max="6"
|
||||
app:iconSpaceReserved="false"
|
||||
@@ -59,7 +69,7 @@
|
||||
<ListPreference
|
||||
android:max="1500"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="settings_cat_net_sb_image_resolution"
|
||||
app:key="@string/imgResolutionKeyID"
|
||||
app:selectable="true"
|
||||
app:summary="@string/settings_cat_net_sb_summary_image_resolution"
|
||||
app:title="@string/settings_cat_net_sb_title_image_resolution"
|
||||
@@ -89,15 +99,25 @@
|
||||
app:enableCopying="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="settings_cat_net_et_api_url" />
|
||||
<EditTextPreference
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:selectAllOnFocus="false"
|
||||
android:singleLine="true"
|
||||
android:title="@string/settings_cat_net_et_title_reverse_proxy"
|
||||
android:summary="@string/settings_cat_net_et_summary_reverse_proxy"
|
||||
app:enableCopying="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="@string/reverseProxyKeyID" />
|
||||
<SwitchPreferenceCompat
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="settings_cat_net_sw_use_api_proxy"
|
||||
app:key="@string/apiProxyKeyID"
|
||||
app:selectable="true"
|
||||
app:summary="@string/settings_cat_net_sm_use_api_proxy"
|
||||
app:title="@string/settings_cat_net_sw_use_api_proxy" />
|
||||
<SwitchPreferenceCompat
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="settings_cat_net_sw_use_img_proxy"
|
||||
app:key="@string/imgProxyKeyID"
|
||||
app:selectable="true"
|
||||
app:summary="@string/settings_cat_net_sm_use_img_proxy"
|
||||
app:title="@string/settings_cat_net_sw_use_img_proxy" />
|
||||
@@ -110,11 +130,17 @@
|
||||
android:summary="@string/settings_cat_net_et_summary_img_proxy"
|
||||
app:enableCopying="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="settings_cat_net_et_img_proxy_code" />
|
||||
app:key="@string/imgProxyCodeKeyID" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/settings_cat_vm">
|
||||
<SwitchPreferenceCompat
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="settings_cat_vm_sw_inverse_chapters"
|
||||
app:selectable="true"
|
||||
app:summary="@string/settings_cat_vm_sm_inverse_chapters"
|
||||
app:title="@string/settings_cat_vm_sw_inverse_chapters" />
|
||||
<SwitchPreferenceCompat
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="settings_cat_vm_sw_always_dark_bg"
|
||||
|
||||
Reference in New Issue
Block a user