mirror of
https://github.com/fumiama/copymanga.git
synced 2026-06-23 11:00: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'
|
applicationId 'top.fumiama.copymanga'
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode 70
|
versionCode 71
|
||||||
versionName '2.4.3'
|
versionName '2.5.0'
|
||||||
resourceConfigurations += ['zh', 'zh-rCN']
|
resourceConfigurations += ['zh', 'zh-rCN']
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.widget.Toast
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
@@ -81,6 +82,19 @@ class MainActivity : AppCompatActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(null)
|
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
|
// must init before setContentView because HomeF need them to init
|
||||||
mainWeakReference = WeakReference(this)
|
mainWeakReference = WeakReference(this)
|
||||||
toolsBox = UITools(this)
|
toolsBox = UITools(this)
|
||||||
@@ -172,13 +186,13 @@ class MainActivity : AppCompatActivity() {
|
|||||||
)[it])
|
)[it])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Config.general_enable_transparent_system_bar.value) {
|
}
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
if (Config.general_enable_transparent_system_bar.value) {
|
||||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||||
window.statusBarColor = 0
|
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||||
window.navigationBarColor = 0
|
window.statusBarColor = 0
|
||||||
}
|
window.navigationBarColor = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +211,11 @@ class MainActivity : AppCompatActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_download -> {
|
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
|
true
|
||||||
}
|
}
|
||||||
R.id.action_sort -> {
|
R.id.action_sort -> {
|
||||||
@@ -412,7 +430,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
dl.setMessage("${getString(R.string.app_description)}\n" +
|
dl.setMessage("${getString(R.string.app_description)}\n" +
|
||||||
"\n$comandy\n" +
|
"\n$comandy\n" +
|
||||||
"$comancry\n\n"+ File("/proc/self/cmdline").readText() + "\n" +
|
"$comancry\n\n"+ File("/proc/self/cmdline").readText() + "\n" +
|
||||||
"安装位置: ${applicationInfo.sourceDir}")
|
"当前API: ${Config.myHostApiUrl.joinToString(", ")}")
|
||||||
dl.setTitle("${getString(R.string.action_info)} ${BuildConfig.VERSION_NAME}")
|
dl.setTitle("${getString(R.string.action_info)} ${BuildConfig.VERSION_NAME}")
|
||||||
dl.setIcon(R.mipmap.ic_launcher)
|
dl.setIcon(R.mipmap.ic_launcher)
|
||||||
dl.setPositiveButton(android.R.string.ok) { _, _ -> }
|
dl.setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
package top.fumiama.copymanga.api
|
package top.fumiama.copymanga.api
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import com.bumptech.glide.load.model.LazyHeaders
|
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.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.PreferenceBoolean
|
||||||
import top.fumiama.copymanga.storage.PreferenceInt
|
import top.fumiama.copymanga.storage.PreferenceInt
|
||||||
import top.fumiama.copymanga.storage.PreferenceString
|
import top.fumiama.copymanga.storage.PreferenceString
|
||||||
import top.fumiama.copymanga.storage.UserPreferenceInt
|
import top.fumiama.copymanga.storage.UserPreferenceInt
|
||||||
import top.fumiama.copymanga.storage.UserPreferenceString
|
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 top.fumiama.dmzj.copymanga.R
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -53,9 +60,52 @@ object Config {
|
|||||||
return field
|
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 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 app_ver = PreferenceString("settings_cat_general_et_app_version", R.string.app_ver)
|
||||||
val token = UserPreferenceString("token", "", null)
|
val token = UserPreferenceString("token", "", null)
|
||||||
val pc_ua get() = MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)?.format(app_ver.value)?:""
|
val pc_ua get() = MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)?.format(app_ver.value)?:""
|
||||||
@@ -80,6 +130,7 @@ object Config {
|
|||||||
val net_use_api_proxy = PreferenceBoolean("settings_cat_net_sw_use_api_proxy", false)
|
val net_use_api_proxy = PreferenceBoolean("settings_cat_net_sw_use_api_proxy", false)
|
||||||
val net_img_resolution = PreferenceString(R.string.imgResolutionKeyID)
|
val net_img_resolution = PreferenceString(R.string.imgResolutionKeyID)
|
||||||
|
|
||||||
|
val 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_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_vertical_max = PreferenceInt("settings_cat_vm_sb_vertical_max", 20)
|
||||||
val view_manga_quality = PreferenceInt("settings_cat_vm_sb_quality", 100)
|
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) =
|
fun getChapterInfoApiUrl(path: String?, uuid: String?, version: Int) =
|
||||||
MainActivity.mainWeakReference?.get()?.getString(R.string.chapterInfoApiUrl)
|
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
|
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) {
|
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 val mUserAgent = getString(R.string.pc_ua).format(Config.app_ver.value)
|
||||||
private var mBook: BookInfoStructure? = null
|
private var mBook: BookInfoStructure? = null
|
||||||
private var mGroupPathWords = arrayOf<String>()
|
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 author: Array<ThemeStructure>? get() = mBook?.results?.comic?.author
|
||||||
val theme: Array<ThemeStructure>? get() = mBook?.results?.comic?.theme
|
val theme: Array<ThemeStructure>? get() = mBook?.results?.comic?.theme
|
||||||
val keys get() = mKeys
|
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 popular get() = mBook?.results?.comic?.popular?:0
|
||||||
val status get() = mBook?.results?.comic?.status?.display?:"未知"
|
val status get() = mBook?.results?.comic?.status?.display?:"未知"
|
||||||
val updateTime get() = mBook?.results?.comic?.datetime_updated?:"未知"
|
val updateTime get() = mBook?.results?.comic?.datetime_updated?:"未知"
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package top.fumiama.copymanga.api.manga
|
package top.fumiama.copymanga.api.manga
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Context.MODE_PRIVATE
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import kotlinx.android.synthetic.main.button_tbutton.view.*
|
import kotlinx.android.synthetic.main.button_tbutton.view.*
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
|
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
|
||||||
|
import top.fumiama.copymanga.api.Config
|
||||||
import top.fumiama.copymanga.json.VolumeStructure
|
import top.fumiama.copymanga.json.VolumeStructure
|
||||||
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
|
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -81,4 +85,37 @@ object Reader {
|
|||||||
}
|
}
|
||||||
return "N/A:null_gson"
|
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
|
import top.fumiama.dmzj.copymanga.R
|
||||||
|
|
||||||
class Shelf(private val getString: (Int) -> String) {
|
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 queryApiUrlTemplate = getString(R.string.bookUserQueryApiUrl)
|
||||||
private val addApiUrl get() = "$apiUrl?platform=3"
|
private val addApiUrl get() = "$apiUrl?platform=3"
|
||||||
private val delApiUrl get() = "${apiUrl}s?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) {
|
suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val queryUrl = queryApiUrlTemplate.format(Config.myHostApiUrl.value, pathWord)
|
val queryUrl = queryApiUrlTemplate.format(Config.myHostApiUrl.random(), pathWord)
|
||||||
(Config.apiProxy?.comancry(queryUrl) { url ->
|
(Config.apiProxy?.comancry(queryUrl) { url ->
|
||||||
DownloadTools.getHttpContent(url, Config.referer)
|
DownloadTools.getHttpContent(url, Config.referer)
|
||||||
}?:DownloadTools.getHttpContent(queryUrl, Config.referer)).let {
|
}?:DownloadTools.getHttpContent(queryUrl, Config.referer)).let {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class Volume(private val path: String, private val groupPathWord: String, getStr
|
|||||||
return@withContext mVolume
|
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) {
|
private suspend fun download(re: Array<VolumeStructure?>, offset: Int, c: Int) = withContext(Dispatchers.IO) {
|
||||||
Log.d("MyV", "下载偏移: $offset")
|
Log.d("MyV", "下载偏移: $offset")
|
||||||
getApiUrl(offset).let {
|
getApiUrl(offset).let {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class Member(private val getString: (Int) -> String) {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
val u = getString(R.string.memberInfoApiUrl)
|
val u = getString(R.string.memberInfoApiUrl)
|
||||||
.format(Config.myHostApiUrl.value)
|
.format(Config.myHostApiUrl.random())
|
||||||
val data = (Config.apiProxy?.comancry(u) {
|
val data = (Config.apiProxy?.comancry(u) {
|
||||||
DownloadTools.getHttpContent(it)
|
DownloadTools.getHttpContent(it)
|
||||||
}?:DownloadTools.getHttpContent(u)).decodeToString()
|
}?: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? =
|
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 ->
|
val use: suspend (String) -> ByteArray? = { it: String ->
|
||||||
DownloadTools.getApiConnection(it, "POST").let { c ->
|
DownloadTools.getApiConnection(it, "POST").let { c ->
|
||||||
c.doOutput = true
|
c.doOutput = true
|
||||||
@@ -129,7 +129,7 @@ class Member(private val getString: (Int) -> String) {
|
|||||||
|
|
||||||
|
|
||||||
private suspend fun postComandyLogin(username: String, pwd: String, salt: Int) =
|
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 ->
|
val use: suspend (String) -> ByteArray? = { it: String ->
|
||||||
DownloadTools.getComandyApiConnection(it, "POST", null, Config.pc_ua).apply {
|
DownloadTools.getComandyApiConnection(it, "POST", null, Config.pc_ua).apply {
|
||||||
headers["content-type"] = "application/x-www-form-urlencoded;charset=utf-8"
|
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")
|
Log.d("MyComandy", "$name block enabled for isInInit")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (mEnabled != true && DownloadTools.failTimes.get() >= 3) {
|
if (mEnabled != true && DownloadTools.failTimes.get() >= 16) {
|
||||||
mEnabled = true
|
mEnabled = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,18 @@ package top.fumiama.copymanga.lib.template
|
|||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.sun.jna.Library
|
import com.sun.jna.Library
|
||||||
import com.sun.jna.Native
|
import com.sun.jna.Native
|
||||||
import kotlinx.android.synthetic.main.dialog_progress.view.*
|
import kotlinx.android.synthetic.main.dialog_progress.view.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import top.fumiama.copymanga.MainActivity
|
import top.fumiama.copymanga.MainActivity
|
||||||
import top.fumiama.copymanga.json.ComandyVersion
|
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.PreferenceBoolean
|
||||||
import top.fumiama.copymanga.storage.UserPreferenceInt
|
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 top.fumiama.dmzj.copymanga.R
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -54,68 +52,66 @@ open class LazyLibrary<T: Library>(
|
|||||||
}?:return null
|
}?:return null
|
||||||
Log.d("MyLazyLibrary", "$name arch: $prefix")
|
Log.d("MyLazyLibrary", "$name arch: $prefix")
|
||||||
MainActivity.mainWeakReference?.get()?.let { ma ->
|
MainActivity.mainWeakReference?.get()?.let { ma ->
|
||||||
ma.lifecycleScope.launch {
|
withContext(Dispatchers.IO) {
|
||||||
withContext(Dispatchers.IO) {
|
Log.d("MyLazyLibrary", "$name launched")
|
||||||
Log.d("MyLazyLibrary", "$name launched")
|
var f = File(ma.filesDir, "libs")
|
||||||
var f = File(ma.filesDir, "libs")
|
if (!f.exists()) f.mkdirs()
|
||||||
if (!f.exists()) f.mkdirs()
|
f = File(f, name)
|
||||||
f = File(f, name)
|
var remoteVersion = 0
|
||||||
var remoteVersion = 0
|
if (f.exists()) {
|
||||||
if (f.exists()) {
|
DownloadTools.getHttpContent(ma.getString(R.string.comandy_version_url).format(repoName), -1)?.let dataLet@{
|
||||||
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 {
|
try {
|
||||||
val body = Gson().fromJson(it.decodeToString(), ComandyVersion::class.java)?.body
|
GZIPInputStream(ByteArrayInputStream(it)).use { dataIn ->
|
||||||
if (body?.startsWith("Version: ") == true) {
|
f.outputStream().use { dataOut ->
|
||||||
remoteVersion = body.substring(9).toInt()
|
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) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
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()
|
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
|
return mLibraryFile
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import com.bumptech.glide.module.AppGlideModule
|
|||||||
import com.bumptech.glide.signature.ObjectKey
|
import com.bumptech.glide.signature.ObjectKey
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import top.fumiama.copymanga.api.Config.proxyUrl
|
||||||
import top.fumiama.copymanga.json.ComandyCapsule
|
import top.fumiama.copymanga.json.ComandyCapsule
|
||||||
import top.fumiama.copymanga.lib.Comandy
|
import top.fumiama.copymanga.lib.Comandy
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
@@ -86,7 +87,7 @@ class ComandyGlideModule: AppGlideModule() {
|
|||||||
|
|
||||||
override fun handles(model: GlideUrl): Boolean {
|
override fun handles(model: GlideUrl): Boolean {
|
||||||
return Comandy.instance.enabled && model.toURL().let {
|
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 {
|
override fun handles(model: String): Boolean {
|
||||||
return Comandy.instance.enabled &&
|
return Comandy.instance.enabled &&
|
||||||
model.startsWith("https://") &&
|
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 kotlinx.coroutines.withContext
|
||||||
import top.fumiama.copymanga.MainActivity
|
import top.fumiama.copymanga.MainActivity
|
||||||
import top.fumiama.copymanga.api.Config
|
import top.fumiama.copymanga.api.Config
|
||||||
|
import top.fumiama.copymanga.api.Config.proxyUrl
|
||||||
import top.fumiama.copymanga.json.ComandyCapsule
|
import top.fumiama.copymanga.json.ComandyCapsule
|
||||||
import top.fumiama.copymanga.lib.Comandy
|
import top.fumiama.copymanga.lib.Comandy
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
@@ -16,34 +17,40 @@ import java.io.OutputStream
|
|||||||
import java.lang.Thread.sleep
|
import java.lang.Thread.sleep
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
import java.util.concurrent.FutureTask
|
import java.util.concurrent.FutureTask
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
object DownloadTools {
|
object DownloadTools {
|
||||||
val failTimes = AtomicInteger(0)
|
val failTimes = AtomicInteger(0)
|
||||||
fun getApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null, timeout: Int = 20000) =
|
fun getApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null, timeout: Int = 20000): HttpURLConnection {
|
||||||
url.let {
|
val connection = URL(url).openConnection() as HttpURLConnection
|
||||||
val connection = URL(url).openConnection() as HttpURLConnection
|
connection.requestMethod = method
|
||||||
connection.requestMethod = method
|
connection.connectTimeout = timeout
|
||||||
connection.connectTimeout = timeout
|
connection.readTimeout = timeout
|
||||||
connection.readTimeout = timeout
|
connection.apply {
|
||||||
connection.apply {
|
/*setRequestProperty("host", if (url.startsWith("https://copymanga.azurewebsites.net")) {
|
||||||
setRequestProperty("host", url.substringAfter("://").substringBefore("/"))
|
Uri.parse(url).getQueryParameter("url")?.substringAfter("://")?.substringBefore("/")?:""
|
||||||
ua?.let { setRequestProperty("user-agent", it) }
|
} else {
|
||||||
refer?.let { setRequestProperty("referer", it) }
|
url.substringAfter("://").substringBefore("/")
|
||||||
setRequestProperty("source", "copyApp")
|
})*/
|
||||||
setRequestProperty("webp", "1")
|
ua?.let { setRequestProperty("user-agent", it) }
|
||||||
setRequestProperty("region", if(!Config.net_use_foreign.value) "1" else "0")
|
refer?.let { setRequestProperty("referer", it) }
|
||||||
setRequestProperty("version", Config.app_ver.value)
|
setRequestProperty("source", "copyApp")
|
||||||
Config.token.value?.let { tk ->
|
setRequestProperty("webp", "1")
|
||||||
setRequestProperty("authorization", "Token $tk")
|
setRequestProperty("region", if(!Config.net_use_foreign.value) "1" else "0")
|
||||||
}
|
setRequestProperty("version", Config.app_ver.value)
|
||||||
setRequestProperty("platform", "3")
|
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")}")
|
setRequestProperty("platform", "3")
|
||||||
connection
|
|
||||||
}
|
}
|
||||||
|
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) =
|
fun getComandyApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null) =
|
||||||
run {
|
run {
|
||||||
@@ -51,7 +58,11 @@ object DownloadTools {
|
|||||||
capsule.url = url
|
capsule.url = url
|
||||||
capsule.method = method
|
capsule.method = method
|
||||||
capsule.headers = hashMapOf()
|
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 }
|
ua?.let { capsule.headers["user-agent"] = it }
|
||||||
refer?.let { capsule.headers["referer"] = it }
|
refer?.let { capsule.headers["referer"] = it }
|
||||||
capsule.headers["source"] = "copyApp"
|
capsule.headers["source"] = "copyApp"
|
||||||
@@ -64,6 +75,7 @@ object DownloadTools {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
capsule.headers["platform"] = "3"
|
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")}")
|
Log.d("MyDT", "getComandyConnection: $url\n${capsule.headers.map { "${it.key}: ${it.value}" }.joinToString("\n")}")
|
||||||
capsule
|
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 =
|
suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = Config.pc_ua, p: Client.Progress? = null): ByteArray =
|
||||||
withContext(Dispatchers.IO) {
|
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 ->
|
getComandyApiConnection(u, "GET", refer, ua).let { capsule ->
|
||||||
val para = Gson().toJson(capsule)
|
val para = Gson().toJson(capsule)
|
||||||
//Log.d("MyDT", "comandy request: $para")
|
//Log.d("MyDT", "comandy request: $para")
|
||||||
@@ -188,7 +200,7 @@ object DownloadTools {
|
|||||||
// prepare p only take effect when readSize is -1
|
// prepare p only take effect when readSize is -1
|
||||||
fun prepare(u: String, readSize: Int = -1, p: Client.Progress? = null) = run {
|
fun prepare(u: String, readSize: Int = -1, p: Client.Progress? = null) = run {
|
||||||
Log.d("MyDT", "prepareHttp: $u")
|
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 {
|
try {
|
||||||
runBlocking { Comandy.instance.getInstance() }?.let { ins ->
|
runBlocking { Comandy.instance.getInstance() }?.let { ins ->
|
||||||
runBlocking {
|
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? {
|
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")
|
Log.d("MyDT", "$method Http: $url")
|
||||||
var ret: ByteArray? = null
|
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 {
|
try {
|
||||||
val capsule = getComandyApiConnection(url, method, refer, ua)
|
val capsule = getComandyApiConnection(url, method, refer, ua)
|
||||||
contentType?.let { capsule.headers["content-type"] = it }
|
contentType?.let { capsule.headers["content-type"] = it }
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class Proxy(id: Int, private val apiRegex: Regex) {
|
|||||||
?:u
|
?:u
|
||||||
Log.d("MyP", "[M] comancry: $wu, sd: $sourceDir")
|
Log.d("MyP", "[M] comancry: $wu, sd: $sourceDir")
|
||||||
return use(wu)?.let { data ->
|
return use(wu)?.let { data ->
|
||||||
|
Log.d("MyP", "[M] comancry: decrypt ${data.size} bytes data")
|
||||||
Comancry.instance.decrypt(sourceDir, data)?.encodeToByteArray()
|
Comancry.instance.decrypt(sourceDir, data)?.encodeToByteArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import java.io.File
|
|||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
|
||||||
open class AutoDownloadHandler(
|
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 context: LifecycleOwner?,
|
||||||
private val loadFromCache: Boolean = false,
|
private val loadFromCache: Boolean = false,
|
||||||
private val customCacheFile: File? = null): Handler(Looper.myLooper()!!) {
|
private val customCacheFile: File? = null): Handler(Looper.myLooper()!!) {
|
||||||
@@ -57,7 +57,7 @@ open class AutoDownloadHandler(
|
|||||||
toString()
|
toString()
|
||||||
}
|
}
|
||||||
private suspend fun downloadCoroutine() = withContext(Dispatchers.IO) {
|
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) })
|
val cacheFile = customCacheFile?:(mainWeakReference?.get()?.externalCacheDir?.let { File(it, cacheName) })
|
||||||
if(loadFromCache) {
|
if(loadFromCache) {
|
||||||
cacheFile?.let {
|
cacheFile?.let {
|
||||||
@@ -77,9 +77,9 @@ open class AutoDownloadHandler(
|
|||||||
var cnt = 0
|
var cnt = 0
|
||||||
while (cnt++ <= 3) {
|
while (cnt++ <= 3) {
|
||||||
try {
|
try {
|
||||||
val data = Config.apiProxy?.comancry(url) {
|
val data = Config.apiProxy?.comancry(url()) {
|
||||||
DownloadTools.getHttpContent(it)
|
DownloadTools.getHttpContent(it)
|
||||||
}?:DownloadTools.getHttpContent(url)
|
}?:DownloadTools.getHttpContent(url())
|
||||||
if(exit) return@withContext
|
if(exit) return@withContext
|
||||||
val fi = data.inputStream()
|
val fi = data.inputStream()
|
||||||
val pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass))
|
val pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass))
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
package top.fumiama.copymanga.storage
|
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.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
object FileUtils {
|
object FileUtils {
|
||||||
fun recursiveRemove(f: File) {
|
fun recursiveRemove(f: File) {
|
||||||
@@ -19,4 +28,52 @@ object FileUtils {
|
|||||||
}
|
}
|
||||||
return size
|
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() {
|
fun setReadTo() {
|
||||||
var chapter = "未读"
|
var chapter = "未读"
|
||||||
if(!mBookHandler?.chapterNames.isNullOrEmpty()) {
|
if(!mBookHandler?.chapterNames.isNullOrEmpty()) {
|
||||||
activity?.apply {
|
chapter = "读至 ${activity?.let { a ->
|
||||||
book?.name?.let { name ->
|
book?.name?.let { name ->
|
||||||
getPreferences(MODE_PRIVATE).getInt(name, -1).let { p ->
|
Reader.getLocalReadingProgress(a, name, mBookHandler!!.chapterNames)
|
||||||
if(p >= 0) mBookHandler!!.chapterNames.let {
|
} }}"
|
||||||
chapter = it[if (p >= it.size) it.size-1 else p]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
chapter = "读至 $chapter"
|
|
||||||
this@BookFragment.bttag.text = chapter
|
this@BookFragment.bttag.text = chapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -218,13 +218,16 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
|
|||||||
}
|
}
|
||||||
var last = i-1
|
var last = i-1
|
||||||
val comicName = name?:return@withContext
|
val comicName = name?:return@withContext
|
||||||
|
val init = i
|
||||||
|
Log.d("MyBH", "i = $i, last=$last, add comic $comicName, volume $p")
|
||||||
volumes[p].let { v ->
|
volumes[p].let { v ->
|
||||||
if(exit) return@withContext
|
if(exit) return@withContext
|
||||||
var line: View? = null
|
var line: View? = null
|
||||||
last += v.results.list.size
|
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)
|
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 ->
|
that?.isOnPause?.let { isOnPause ->
|
||||||
while (isOnPause && !exit) delay(500)
|
while (isOnPause && !exit) delay(500)
|
||||||
if (exit) return@withContext
|
if (exit) return@withContext
|
||||||
@@ -236,7 +239,7 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
|
|||||||
lct.text = it.name
|
lct.text = it.name
|
||||||
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
||||||
Log.d("MyBH", "add last single chapter ${it.name}")
|
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) }
|
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray, uuidArray) }
|
||||||
}
|
}
|
||||||
line?.let { l -> addVolumesView(fbl, l) }
|
line?.let { l -> addVolumesView(fbl, l) }
|
||||||
@@ -245,14 +248,14 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
|
|||||||
line?.l2cl?.apply {
|
line?.l2cl?.apply {
|
||||||
lct.text = it.name
|
lct.text = it.name
|
||||||
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
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) }
|
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray, uuidArray) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else line?.l2cr?.apply {
|
} else line?.l2cr?.apply {
|
||||||
lct.text = it.name
|
lct.text = it.name
|
||||||
if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
|
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) }
|
setOnClickListener { Reader.start2viewManga(comicName, index, urlArray, uuidArray) }
|
||||||
line?.let { l -> addVolumesView(fbl, l) }
|
line?.let { l -> addVolumesView(fbl, l) }
|
||||||
line = null
|
line = null
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import top.fumiama.dmzj.copymanga.R
|
|||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
class HistoryFragment : InfoCardLoader(R.layout.fragment_history, R.id.action_nav_history_to_nav_book, isHistoryBook = true) {
|
class HistoryFragment : InfoCardLoader(R.layout.fragment_history, R.id.action_nav_history_to_nav_book, isHistoryBook = true) {
|
||||||
override fun getApiUrl() =
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
if (MainActivity.member?.hasLogin != true) {
|
if (MainActivity.member?.hasLogin != true) {
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ import top.fumiama.dmzj.copymanga.R
|
|||||||
@ExperimentalStdlibApi
|
@ExperimentalStdlibApi
|
||||||
class NewestFragment : InfoCardLoader(R.layout.fragment_newest, R.id.action_nav_newest_to_nav_book, true) {
|
class NewestFragment : InfoCardLoader(R.layout.fragment_newest, R.id.action_nav_newest_to_nav_book, true) {
|
||||||
override fun getApiUrl() =
|
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() =
|
override fun getApiUrl() =
|
||||||
getString(R.string.rankApiUrl).format(
|
getString(R.string.rankApiUrl).format(
|
||||||
Config.myHostApiUrl.value,
|
Config.myHostApiUrl.random(),
|
||||||
page * 21,
|
page * 21,
|
||||||
sortWay[sortValue],
|
sortWay[sortValue],
|
||||||
audienceWay[audience]
|
audienceWay[audience]
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ import top.fumiama.dmzj.copymanga.R
|
|||||||
@ExperimentalStdlibApi
|
@ExperimentalStdlibApi
|
||||||
class RecFragment : InfoCardLoader(R.layout.fragment_recommend, R.id.action_nav_recommend_to_nav_book, true) {
|
class RecFragment : InfoCardLoader(R.layout.fragment_recommend, R.id.action_nav_recommend_to_nav_book, true) {
|
||||||
override fun getApiUrl() =
|
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 query: String? = null
|
||||||
private var type: String? = null
|
private var type: String? = null
|
||||||
override fun getApiUrl() =
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class ShelfFragment : InfoCardLoader(R.layout.fragment_shelf, R.id.action_nav_su
|
|||||||
|
|
||||||
override fun getApiUrl() =
|
override fun getApiUrl() =
|
||||||
getString(R.string.shelfApiUrl).format(
|
getString(R.string.shelfApiUrl).format(
|
||||||
Config.myHostApiUrl.value,
|
Config.myHostApiUrl.random(),
|
||||||
page * 21,
|
page * 21,
|
||||||
sortWay[sortValue]
|
sortWay[sortValue]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class SortFragment : StatusCardFlow(0, R.id.action_nav_sort_to_nav_book, R.layou
|
|||||||
|
|
||||||
override fun getApiUrl() =
|
override fun getApiUrl() =
|
||||||
getString(R.string.sortApiUrl).format(
|
getString(R.string.sortApiUrl).format(
|
||||||
Config.myHostApiUrl.value,
|
Config.myHostApiUrl.random(),
|
||||||
page * 21,
|
page * 21,
|
||||||
sortWay[sortValue],
|
sortWay[sortValue],
|
||||||
if(theme >= 0 && theme < (filter?.results?.theme?.size ?: 0)) (filter?.results?.theme?.get(theme)?.path_word ?: "") else "",
|
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()
|
super.setListeners()
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
setProgress(5)
|
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
|
if(ad?.exit == true) return@PausableDownloader
|
||||||
it.let {
|
it.let {
|
||||||
it.inputStream().use { i ->
|
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) {
|
class TopicFragment : InfoCardLoader(R.layout.fragment_topic, R.id.action_nav_topic_to_nav_book) {
|
||||||
private var type = 1
|
private var type = 1
|
||||||
override fun getApiUrl() =
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
setProgress(5)
|
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)
|
setProgress(10)
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
if(ad?.exit == true) return@withContext
|
if(ad?.exit == true) return@withContext
|
||||||
|
|||||||
@@ -1,25 +1,33 @@
|
|||||||
package top.fumiama.copymanga.ui.download
|
package top.fumiama.copymanga.ui.download
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
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.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
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.Config.manga_dl_show_0m_manga
|
||||||
import top.fumiama.copymanga.api.manga.Downloader
|
import top.fumiama.copymanga.api.manga.Downloader
|
||||||
import top.fumiama.copymanga.api.manga.Reader
|
import top.fumiama.copymanga.api.manga.Reader
|
||||||
import top.fumiama.copymanga.view.template.MangaPagesFragmentTemplate
|
import top.fumiama.copymanga.net.Client
|
||||||
import top.fumiama.copymanga.view.template.CardList
|
|
||||||
import top.fumiama.copymanga.storage.FileUtils
|
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.Navigate
|
||||||
import top.fumiama.copymanga.view.interaction.UITools
|
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 top.fumiama.dmzj.copymanga.R
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
@@ -27,25 +35,48 @@ import java.lang.ref.WeakReference
|
|||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownload, forceLoad = true) {
|
class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownload, forceLoad = true) {
|
||||||
private var sortedBookList: List<File>? = null
|
private var sortedBookList: List<File>? = null
|
||||||
private val oldDlCardName = mainWeakReference?.get()?.getString(R.string.old_download_card_name)!!
|
private val oldDlCardName by lazy { getString(R.string.old_download_card_name) }
|
||||||
private val extDir = mainWeakReference?.get()?.getExternalFilesDir("")
|
private val extDir by lazy { activity?.getExternalFilesDir("") }
|
||||||
private var isReverse = false
|
private var isReverse = false
|
||||||
private var isContentChanged = false
|
private var isContentChanged = false
|
||||||
private var exit = false
|
private var exit = false
|
||||||
private val showAll get() = manga_dl_show_0m_manga.value
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
wn = WeakReference(this)
|
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() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
exit = true
|
exit = true
|
||||||
|
wn = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
exit = false
|
exit = false
|
||||||
|
wn = WeakReference(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
@@ -54,6 +85,7 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
|||||||
exit = true
|
exit = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
override suspend fun addPage(): Unit = withContext(Dispatchers.IO) {
|
override suspend fun addPage(): Unit = withContext(Dispatchers.IO) {
|
||||||
super.addPage()
|
super.addPage()
|
||||||
if(isRefresh) {
|
if(isRefresh) {
|
||||||
@@ -105,8 +137,18 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
|||||||
cnt = 1
|
cnt = 1
|
||||||
}
|
}
|
||||||
val size = sortedBookList?.size?:0
|
val size = sortedBookList?.size?:0
|
||||||
sortedBookList?.let {
|
sortedBookList?.let { lst ->
|
||||||
for(i in it.listIterator(page)) {
|
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
|
if(cardList?.exitCardList != false) return@withContext
|
||||||
page++ // page is actually count
|
page++ // page is actually count
|
||||||
val chosenJson = File(i, "info.bin")
|
val chosenJson = File(i, "info.bin")
|
||||||
@@ -120,14 +162,16 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
|||||||
chosenJson.exists() -> continue // unsupported old folder
|
chosenJson.exists() -> continue // unsupported old folder
|
||||||
newJson.exists() -> {
|
newJson.exists() -> {
|
||||||
if(cardList?.exitCardList != false) return@withContext
|
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++
|
cnt++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setProgress(80+20*(cnt-1)/size)
|
setProgress(80+20*(cnt-1)/size)
|
||||||
if (cnt >= 21) break
|
if (cnt >= 21) break
|
||||||
}
|
}
|
||||||
if(page >= it.size) {
|
if(page >= lst.size) {
|
||||||
isEnd = true
|
isEnd = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +199,7 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
|||||||
AlertDialog.Builder(context)
|
AlertDialog.Builder(context)
|
||||||
.setIcon(R.drawable.ic_launcher_foreground)
|
.setIcon(R.drawable.ic_launcher_foreground)
|
||||||
.setTitle(R.string.new_download_card_option_hint)
|
.setTitle(R.string.new_download_card_option_hint)
|
||||||
.setItems(arrayOf("删除数据文件夹", "直接前往下载页")) { d, p ->
|
.setItems(arrayOf("删除数据文件夹", "直接前往下载页", "导出压缩包")) { d, p ->
|
||||||
d.cancel()
|
d.cancel()
|
||||||
when (p) {
|
when (p) {
|
||||||
0 -> {
|
0 -> {
|
||||||
@@ -174,10 +218,14 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
1 -> callDownloadFragment(name)
|
1 -> callDownloadFragment(name)
|
||||||
|
2 -> {
|
||||||
|
compressName = name
|
||||||
|
compressFile = chosenFile
|
||||||
|
compressIntoFile?.launch("${name}.zip")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,6 +268,10 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun importMangaFromZip() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var wn: WeakReference<NewDownloadFragment>? = null
|
var wn: WeakReference<NewDownloadFragment>? = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
|
|||||||
suspend fun refresh(q: CharSequence) = withContext(Dispatchers.IO) {
|
suspend fun refresh(q: CharSequence) = withContext(Dispatchers.IO) {
|
||||||
query = q.toString()
|
query = q.toString()
|
||||||
activity?.apply {
|
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)) {
|
URLEncoder.encode(q.toString(), Charset.defaultCharset().name()), type)) {
|
||||||
results = Gson().fromJson(it.decodeToString(), BookListStructure::class.java)
|
results = Gson().fromJson(it.decodeToString(), BookListStructure::class.java)
|
||||||
count = results?.results?.total?:0
|
count = results?.results?.total?:0
|
||||||
|
|||||||
@@ -39,8 +39,9 @@ import top.fumiama.dmzj.copymanga.R
|
|||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadHandler(
|
class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadHandler({
|
||||||
that.get()?.getString(R.string.mainPageApiUrl)!!.format(Config.myHostApiUrl.value),
|
that.get()?.getString(R.string.mainPageApiUrl)!!.format(Config.myHostApiUrl.random())
|
||||||
|
},
|
||||||
IndexStructure::class.java,
|
IndexStructure::class.java,
|
||||||
that.get()
|
that.get()
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -3,14 +3,19 @@ package top.fumiama.copymanga.ui.settings
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import top.fumiama.copymanga.api.Config.proxyUrl
|
||||||
import top.fumiama.copymanga.view.interaction.UITools
|
import top.fumiama.copymanga.view.interaction.UITools
|
||||||
import top.fumiama.copymanga.view.AutoHideEditTextPreferenceDialogFragmentCompat
|
import top.fumiama.copymanga.view.AutoHideEditTextPreferenceDialogFragmentCompat
|
||||||
import top.fumiama.dmzj.copymanga.R
|
import top.fumiama.dmzj.copymanga.R
|
||||||
@@ -22,6 +27,21 @@ class SettingsFragment: PreferenceFragmentCompat() {
|
|||||||
delay(300)
|
delay(300)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
setPreferencesFromResource(R.xml.pref_setting, rootKey)
|
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.*
|
import java.util.*
|
||||||
|
|
||||||
class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, private val weeks: Array<String>) : AutoDownloadHandler(
|
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
|
var manga: Chapter2Return? = null
|
||||||
private val wv = WeakReference(activity)
|
private val wv = WeakReference(activity)
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package top.fumiama.copymanga.view.template
|
package top.fumiama.copymanga.view.template
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.gson.Gson
|
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.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
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.net.template.PausableDownloader
|
||||||
import top.fumiama.copymanga.strings.Chinese
|
import top.fumiama.copymanga.strings.Chinese
|
||||||
import top.fumiama.copymanga.view.interaction.Navigate
|
import top.fumiama.copymanga.view.interaction.Navigate
|
||||||
|
import top.fumiama.dmzj.copymanga.R
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
@ExperimentalStdlibApi
|
@ExperimentalStdlibApi
|
||||||
@@ -26,6 +31,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
|
|||||||
private val subUrl get() = getApiUrl()
|
private val subUrl get() = getApiUrl()
|
||||||
var ad: PausableDownloader? = null
|
var ad: PausableDownloader? = null
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
override suspend fun addPage(): Unit = withContext(Dispatchers.IO) {
|
override suspend fun addPage(): Unit = withContext(Dispatchers.IO) {
|
||||||
super.addPage()
|
super.addPage()
|
||||||
setProgress(20)
|
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)
|
val bookList = Gson().fromJson(data.decodeToString(), TypeBookListStructure::class.java)
|
||||||
bookList?.apply {
|
bookList?.apply {
|
||||||
Log.d("MyICL", "offset:${results.offset}, total:${results.total}")
|
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(results.offset < results.total) {
|
||||||
if(code == 200) {
|
if(code == 200) {
|
||||||
val size = results?.list?.size?:0
|
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)
|
val bookList = Gson().fromJson(data.decodeToString(), HistoryBookListStructure::class.java)
|
||||||
bookList?.apply {
|
bookList?.apply {
|
||||||
Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
|
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(results.offset < results.total) {
|
||||||
if(code == 200) {
|
if(code == 200) {
|
||||||
val size = results?.list?.size?:0
|
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)
|
val bookList = Gson().fromJson(data.decodeToString(), ShelfStructure::class.java)
|
||||||
bookList?.apply {
|
bookList?.apply {
|
||||||
Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
|
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(results.offset < results.total) {
|
||||||
if(code == 200) {
|
if(code == 200) {
|
||||||
val size = results?.list?.size?:0
|
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)
|
val bookList = Gson().fromJson(data.decodeToString(), BookListStructure::class.java)
|
||||||
bookList?.apply {
|
bookList?.apply {
|
||||||
Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
|
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(results.offset < results.total) {
|
||||||
if(code == 200) {
|
if(code == 200) {
|
||||||
val size = results?.list?.size?:0
|
val size = results?.list?.size?:0
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ open class StatusCardFlow(private val api: Int, nav: Int, inflateRes: Int,
|
|||||||
|
|
||||||
override fun getApiUrl() =
|
override fun getApiUrl() =
|
||||||
getString(api).format(
|
getString(api).format(
|
||||||
Config.myHostApiUrl.value,
|
Config.myHostApiUrl.random(),
|
||||||
page * 21,
|
page * 21,
|
||||||
sortWay[sortValue]
|
sortWay[sortValue]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ open class ThemeCardFlow(private val api: Int, nav: Int) : StatusCardFlow(0, nav
|
|||||||
private var theme = ""
|
private var theme = ""
|
||||||
override fun getApiUrl() =
|
override fun getApiUrl() =
|
||||||
getString(api).format(
|
getString(api).format(
|
||||||
Config.myHostApiUrl.value,
|
Config.myHostApiUrl.random(),
|
||||||
page * 21,
|
page * 21,
|
||||||
sortWay[sortValue],
|
sortWay[sortValue],
|
||||||
theme
|
theme
|
||||||
|
|||||||
@@ -38,4 +38,14 @@
|
|||||||
<item>5</item>
|
<item>5</item>
|
||||||
<item>6</item>
|
<item>6</item>
|
||||||
</string-array>
|
</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>
|
</resources>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE resources [
|
<!DOCTYPE resources [
|
||||||
<!ENTITY hosturl "www.copy20.com">
|
<!ENTITY hosturl "api.copy-manga.com">
|
||||||
<!ENTITY appver "2.2.9">
|
<!ENTITY appver "2.3.0">
|
||||||
|
<!ENTITY proxyurl "copymanga.azurewebsites.net">
|
||||||
]>
|
]>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">拷贝漫画</string>
|
<string name="app_name">拷贝漫画</string>
|
||||||
@@ -66,6 +67,7 @@
|
|||||||
<string name="mainPageApiUrl">https://%1$s/api/v3/h5/homeIndex?platform=3</string>
|
<string name="mainPageApiUrl">https://%1$s/api/v3/h5/homeIndex?platform=3</string>
|
||||||
<string name="referUrl">https://%1$s</string>
|
<string name="referUrl">https://%1$s</string>
|
||||||
<string name="hostUrl">&hosturl;</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="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="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>
|
<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="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="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="imgProxyApiUrl">https://&proxyurl;/api/img?code=%1$s&url=%2$s</string>
|
||||||
<string name="imgProxyKeyID">settings_cat_net_et_img_proxy_code</string>
|
<string name="imgProxyCodeKeyID">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="apiProxyApiUrl">https://&proxyurl;/api/api?code=%1$s&url=%2$s</string>
|
||||||
<string name="imgResolutionKeyID">settings_cat_net_sb_image_resolution</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_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>
|
<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="caption">标签</string>
|
||||||
|
|
||||||
<string name="settings_cat_general">通用</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_title_startup_menu">启动时显示</string>
|
||||||
<string name="settings_cat_general_sb_summary_startup_menu">默认主页</string>
|
<string name="settings_cat_general_sb_summary_startup_menu">默认主页</string>
|
||||||
<string name="settings_cat_general_et_title_app_version">拷贝版本号</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_sm_use_comandy">使用经过优化的请求方法访问服务器</string>
|
||||||
<string name="settings_cat_net_et_title_api_url">请求API网址</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_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_sw_use_api_proxy">使用API代理(需要密钥)</string>
|
||||||
<string name="settings_cat_net_sm_use_api_proxy">作者自建的API代理,可缓解国内图书详情加载问题,但不保证100%解决,也不保证一直可用</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_sw_use_img_proxy">使用图床代理(需要密钥)</string>
|
||||||
<string name="settings_cat_net_sm_use_img_proxy">作者自建的图床代理,可缓解国内图片无法加载问题,但不保证100%解决,也不保证一直可用</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_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">漫画浏览</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_sw_always_dark_bg">深色阅读背景</string>
|
||||||
<string name="settings_cat_vm_sm_always_dark_bg">打开后阅览漫画的背景色永远为黑色</string>
|
<string name="settings_cat_vm_sm_always_dark_bg">打开后阅览漫画的背景色永远为黑色</string>
|
||||||
<string name="settings_cat_vm_sw_hide_info">隐藏底部时间栏</string>
|
<string name="settings_cat_vm_sw_hide_info">隐藏底部时间栏</string>
|
||||||
|
|||||||
@@ -4,6 +4,16 @@
|
|||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:title="@string/settings_cat_general">
|
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
|
<ListPreference
|
||||||
android:max="6"
|
android:max="6"
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
@@ -59,7 +69,7 @@
|
|||||||
<ListPreference
|
<ListPreference
|
||||||
android:max="1500"
|
android:max="1500"
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="settings_cat_net_sb_image_resolution"
|
app:key="@string/imgResolutionKeyID"
|
||||||
app:selectable="true"
|
app:selectable="true"
|
||||||
app:summary="@string/settings_cat_net_sb_summary_image_resolution"
|
app:summary="@string/settings_cat_net_sb_summary_image_resolution"
|
||||||
app:title="@string/settings_cat_net_sb_title_image_resolution"
|
app:title="@string/settings_cat_net_sb_title_image_resolution"
|
||||||
@@ -89,15 +99,25 @@
|
|||||||
app:enableCopying="true"
|
app:enableCopying="true"
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="settings_cat_net_et_api_url" />
|
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
|
<SwitchPreferenceCompat
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="settings_cat_net_sw_use_api_proxy"
|
app:key="@string/apiProxyKeyID"
|
||||||
app:selectable="true"
|
app:selectable="true"
|
||||||
app:summary="@string/settings_cat_net_sm_use_api_proxy"
|
app:summary="@string/settings_cat_net_sm_use_api_proxy"
|
||||||
app:title="@string/settings_cat_net_sw_use_api_proxy" />
|
app:title="@string/settings_cat_net_sw_use_api_proxy" />
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="settings_cat_net_sw_use_img_proxy"
|
app:key="@string/imgProxyKeyID"
|
||||||
app:selectable="true"
|
app:selectable="true"
|
||||||
app:summary="@string/settings_cat_net_sm_use_img_proxy"
|
app:summary="@string/settings_cat_net_sm_use_img_proxy"
|
||||||
app:title="@string/settings_cat_net_sw_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"
|
android:summary="@string/settings_cat_net_et_summary_img_proxy"
|
||||||
app:enableCopying="true"
|
app:enableCopying="true"
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="settings_cat_net_et_img_proxy_code" />
|
app:key="@string/imgProxyCodeKeyID" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:title="@string/settings_cat_vm">
|
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
|
<SwitchPreferenceCompat
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="settings_cat_vm_sw_always_dark_bg"
|
app:key="settings_cat_vm_sw_always_dark_bg"
|
||||||
|
|||||||
Reference in New Issue
Block a user