1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-23 11:00:24 +08:00
注意
> 由于大版本更新, 闪退问题可能增加. 如无 API 代理需求可以暂缓更新.
新增
1. 更安全的 API 代理, 旧版代理将无法使用
2. 组件下载提示
优化
1. 代码组织架构
This commit is contained in:
源文雨
2025-03-27 13:15:22 +09:00
parent b88fd93533
commit 0b5148d908
81 changed files with 681 additions and 491 deletions

View File

@@ -3,12 +3,14 @@
<words> <words>
<w>alphae</w> <w>alphae</w>
<w>azurewebsites</w> <w>azurewebsites</w>
<w>comancry</w>
<w>comandy</w> <w>comandy</w>
<w>downloaders</w> <w>downloaders</w>
<w>grps</w> <w>grps</w>
<w>hotmanga</w> <w>hotmanga</w>
<w>imgs</w> <w>imgs</w>
<w>kohima</w> <w>kohima</w>
<w>libcomancry</w>
<w>libcomandy</w> <w>libcomandy</w>
<w>lowpan</w> <w>lowpan</w>
<w>mangacopy</w> <w>mangacopy</w>

1
app/.gitignore vendored
View File

@@ -1,3 +1,4 @@
/build /build
/release /release
/debug /debug
signing.properties

View File

@@ -11,8 +11,8 @@ android {
applicationId 'top.fumiama.copymanga' applicationId 'top.fumiama.copymanga'
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 34 targetSdkVersion 34
versionCode 66 versionCode 67
versionName '2.3.8' versionName '2.4.0'
resourceConfigurations += ['zh', 'zh-rCN'] resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -31,6 +31,12 @@ android {
enableV3Signing true enableV3Signing true
enableV4Signing true enableV4Signing true
} }
debug {
enableV1Signing true
enableV2Signing true
enableV3Signing true
enableV4Signing true
}
} }
buildTypes { buildTypes {
@@ -45,12 +51,14 @@ android {
minifyEnabled true minifyEnabled true
shrinkResources true shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug{
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}*/ }*/
debug{
//minifyEnabled true
//shrinkResources true
//vcsInfo.include false
//proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
}
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_11
@@ -70,6 +78,21 @@ android {
namespace 'top.fumiama.dmzj.copymanga' namespace 'top.fumiama.dmzj.copymanga'
} }
Properties props = new Properties()
def propFile = file('signing.properties')
if (propFile.canRead()){
props.load(new FileInputStream(propFile))
if (props!=null && props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&
props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
android.signingConfigs.debug.storeFile = file(props['STORE_FILE'])
android.signingConfigs.debug.storePassword = props['STORE_PASSWORD']
android.signingConfigs.debug.keyAlias = props['KEY_ALIAS']
android.signingConfigs.debug.keyPassword = props['KEY_PASSWORD']
}
}
dependencies { dependencies {
api fileTree(include: ['*.aar'], dir: 'libs') // https://stackoverflow.com/a/63029110/28801553 api fileTree(include: ['*.aar'], dir: 'libs') // https://stackoverflow.com/a/63029110/28801553

View File

@@ -25,7 +25,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
@@ -51,16 +50,16 @@ 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 import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.manga.Shelf import top.fumiama.copymanga.api.manga.Shelf
import top.fumiama.copymanga.tools.ui.UITools import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.copymanga.ui.book.BookFragment.Companion.bookHandler import top.fumiama.copymanga.ui.book.BookFragment.Companion.bookHandler
import top.fumiama.copymanga.ui.book.BookHandler import top.fumiama.copymanga.ui.book.BookHandler
import top.fumiama.copymanga.ui.cardflow.rank.RankFragment import top.fumiama.copymanga.ui.cardflow.rank.RankFragment
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
import top.fumiama.copymanga.ui.download.DownloadFragment import top.fumiama.copymanga.ui.download.DownloadFragment
import top.fumiama.copymanga.ui.download.NewDownloadFragment import top.fumiama.copymanga.ui.download.NewDownloadFragment
import top.fumiama.copymanga.update.Update import top.fumiama.copymanga.api.update.Update
import top.fumiama.copymanga.user.Member import top.fumiama.copymanga.api.user.Member
import top.fumiama.dmzj.copymanga.BuildConfig import top.fumiama.dmzj.copymanga.BuildConfig
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.io.File import java.io.File
@@ -236,7 +235,7 @@ class MainActivity : AppCompatActivity() {
} }
true true
} }
navtinfo.text = getPreferences(MODE_PRIVATE).getString("navTextInfo", getString(R.string.navTextInfo)) navtinfo.text = Config.navTextInfo.value
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
} }
@@ -421,10 +420,7 @@ class MainActivity : AppCompatActivity() {
MaterialDialog(this).show { MaterialDialog(this).show {
input(prefill = (it as TextView).text) { _, charSequence -> input(prefill = (it as TextView).text) { _, charSequence ->
it.text = charSequence it.text = charSequence
getPreferences(MODE_PRIVATE).edit { Config.navTextInfo.value = charSequence.toString()
putString("navTextInfo", charSequence.toString())
apply()
}
} }
positiveButton(android.R.string.ok) positiveButton(android.R.string.ok)
title(R.string.navTextInfoInputHint) title(R.string.navTextInfoInputHint)

View File

@@ -7,8 +7,8 @@ import top.fumiama.copymanga.tools.file.PreferenceInt
import top.fumiama.copymanga.tools.file.PreferenceString import top.fumiama.copymanga.tools.file.PreferenceString
import top.fumiama.copymanga.tools.file.UserPreferenceInt import top.fumiama.copymanga.tools.file.UserPreferenceInt
import top.fumiama.copymanga.tools.file.UserPreferenceString import top.fumiama.copymanga.tools.file.UserPreferenceString
import top.fumiama.copymanga.tools.http.Proxy import top.fumiama.copymanga.net.Proxy
import top.fumiama.copymanga.tools.http.Resolution 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
@@ -54,12 +54,14 @@ object Config {
} }
val myHostApiUrl = PreferenceString("settings_cat_net_et_api_url", R.string.hostUrl) val myHostApiUrl = PreferenceString("settings_cat_net_et_api_url", R.string.hostUrl)
val navTextInfo = UserPreferenceString("navTextInfo", R.string.navTextInfo)
val proxy_key = PreferenceString(R.string.imgProxyKeyID) val proxy_key = PreferenceString(R.string.imgProxyKeyID)
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)?:""
val referer get() = MainActivity.mainWeakReference?.get()?.getString(R.string.referer)?.format(app_ver.value)?:"" val referer get() = MainActivity.mainWeakReference?.get()?.getString(R.string.referer)?.format(app_ver.value)?:""
val comandy_version = UserPreferenceInt("comandy_version", 0) val comandy_version = UserPreferenceInt("comandy_version", 0)
val comancry_version = UserPreferenceInt("comancry_version", 0)
val user_id = UserPreferenceString("user_id") val user_id = UserPreferenceString("user_id")
val username = UserPreferenceString("username") val username = UserPreferenceString("username")
val nickname = UserPreferenceString("nickname") val nickname = UserPreferenceString("nickname")
@@ -76,6 +78,7 @@ object Config {
val net_use_foreign = PreferenceBoolean("settings_cat_net_sw_use_foreign", false) val net_use_foreign = PreferenceBoolean("settings_cat_net_sw_use_foreign", false)
private val net_use_img_proxy = PreferenceBoolean("settings_cat_net_sw_use_img_proxy", false) private val net_use_img_proxy = PreferenceBoolean("settings_cat_net_sw_use_img_proxy", false)
val net_use_api_proxy = PreferenceBoolean("settings_cat_net_sw_use_api_proxy", false) val net_use_api_proxy = PreferenceBoolean("settings_cat_net_sw_use_api_proxy", false)
val net_img_resolution = PreferenceString(R.string.imgResolutionKeyID)
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)

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.manga package top.fumiama.copymanga.api.manga
import android.util.Log import android.util.Log
import com.google.gson.Gson import com.google.gson.Gson
@@ -10,14 +10,12 @@ import top.fumiama.copymanga.json.BookInfoStructure
import top.fumiama.copymanga.json.ThemeStructure import top.fumiama.copymanga.json.ThemeStructure
import top.fumiama.copymanga.json.VolumeStructure import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.http.DownloadTools import top.fumiama.copymanga.net.DownloadTools
import top.fumiama.dmzj.copymanga.R 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).let { private val mBookApiUrl = getString(R.string.bookInfoApiUrl).format(Config.myHostApiUrl.value, path)
Config.apiProxy?.wrap(it)?:it
}
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>()
@@ -68,12 +66,16 @@ class Book(val path: String, private val getString: (Int) -> String, private val
val data: ByteArray = if (loadCache) { val data: ByteArray = if (loadCache) {
name?.let { loadInfo(it) } ?: run { name?.let { loadInfo(it) } ?: run {
isDownload = true isDownload = true
DownloadTools.getHttpContent(mBookApiUrl, null, mUserAgent) Config.apiProxy?.comancry(mBookApiUrl) { url ->
DownloadTools.getHttpContent(url, null, mUserAgent)
}
} }
} else { } else {
isDownload = true isDownload = true
DownloadTools.getHttpContent(mBookApiUrl, null, mUserAgent) Config.apiProxy?.comancry(mBookApiUrl) { url ->
} DownloadTools.getHttpContent(url, null, mUserAgent)
}
}?:DownloadTools.getHttpContent(mBookApiUrl, null, mUserAgent)
mBook = data.inputStream().use { mBook = data.inputStream().use {
Gson().fromJson(it.reader(), BookInfoStructure::class.java) Gson().fromJson(it.reader(), BookInfoStructure::class.java)
} }

View File

@@ -1,11 +1,11 @@
package top.fumiama.copymanga.manga package top.fumiama.copymanga.api.manga
import android.util.Log import android.util.Log
import com.google.gson.Gson import com.google.gson.Gson
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.Chapter2Return import top.fumiama.copymanga.json.Chapter2Return
import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.tools.http.DownloadPool import top.fumiama.copymanga.net.DownloadPool
import java.io.File import java.io.File
class Downloader { class Downloader {

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.manga package top.fumiama.copymanga.api.manga
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.manga package top.fumiama.copymanga.api.manga
import com.google.gson.Gson import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -6,14 +6,14 @@ import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.BookQueryStructure import top.fumiama.copymanga.json.BookQueryStructure
import top.fumiama.copymanga.json.ReturnBase import top.fumiama.copymanga.json.ReturnBase
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.http.DownloadTools 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.value)
private val queryApiUrlTemplate = getString(R.string.bookUserQueryApiUrl) private val queryApiUrlTemplate = getString(R.string.bookUserQueryApiUrl)
private val addApiUrl get() = "$apiUrl?platform=3".let { Config.apiProxy?.wrap(it)?:it } private val addApiUrl get() = "$apiUrl?platform=3"
private val delApiUrl get() = "${apiUrl}s?platform=3".let { Config.apiProxy?.wrap(it)?:it } private val delApiUrl get() = "${apiUrl}s?platform=3"
suspend fun add(comicId: String): String = withContext(Dispatchers.IO) { suspend fun add(comicId: String): String = withContext(Dispatchers.IO) {
if (comicId.isEmpty()) { if (comicId.isEmpty()) {
return@withContext "空漫画ID" return@withContext "空漫画ID"
@@ -25,9 +25,11 @@ class Shelf(private val getString: (Int) -> String) {
append("") append("")
append(Config.token.value) append(Config.token.value)
} }
val re = DownloadTools.requestWithBody( val re = Config.apiProxy?.comancry(addApiUrl) { url ->
addApiUrl, "POST", body.encodeToByteArray() DownloadTools.requestWithBody(
)?.decodeToString() ?: return@withContext "空回应" url, "POST", body.encodeToByteArray()
)
}?.decodeToString() ?: return@withContext "空回应"
return@withContext try { return@withContext try {
Gson().fromJson(re, ReturnBase::class.java).message Gson().fromJson(re, ReturnBase::class.java).message
} catch (e: Exception) { } catch (e: Exception) {
@@ -48,9 +50,11 @@ class Shelf(private val getString: (Int) -> String) {
append("authorization=Token+") append("authorization=Token+")
append(Config.token.value) append(Config.token.value)
} }
val re = DownloadTools.requestWithBody( val re = Config.apiProxy?.comancry(delApiUrl) { url ->
delApiUrl, "DELETE", body.encodeToByteArray() DownloadTools.requestWithBody(
)?.decodeToString() ?: return@withContext "空回应" url, "DELETE", body.encodeToByteArray()
)
}?.decodeToString() ?: return@withContext "空回应"
return@withContext try { return@withContext try {
Gson().fromJson(re, ReturnBase::class.java).message Gson().fromJson(re, ReturnBase::class.java).message
} catch (e: Exception) { } catch (e: Exception) {
@@ -60,11 +64,11 @@ 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 {
Gson().fromJson(DownloadTools.getHttpContent( Config.apiProxy?.comancry(queryApiUrlTemplate.format(Config.myHostApiUrl.value, pathWord)) { url ->
queryApiUrlTemplate.format(Config.myHostApiUrl.value, pathWord).let { DownloadTools.getHttpContent(url, Config.referer)
Config.apiProxy?.wrap(it)?:it }?.let {
}, Config.referer Gson().fromJson(it.decodeToString(), BookQueryStructure::class.java)
).decodeToString(), BookQueryStructure::class.java) }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
null null

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.manga package top.fumiama.copymanga.api.manga
import android.util.Log import android.util.Log
import com.google.gson.Gson import com.google.gson.Gson
@@ -6,7 +6,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.ChapterStructure import top.fumiama.copymanga.json.ChapterStructure
import top.fumiama.copymanga.json.VolumeStructure import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.update package top.fumiama.copymanga.api.update
//Fumiama 20210601 //Fumiama 20210601
//ByteArrayQueue.kt //ByteArrayQueue.kt
//FIFO队列 //FIFO队列

View File

@@ -1,6 +1,7 @@
package top.fumiama.copymanga.update package top.fumiama.copymanga.api.update
import android.util.Log import android.util.Log
import top.fumiama.copymanga.net.Client
class SimpleKanban(private val client: Client, private val pwd: String) { //must run in thread class SimpleKanban(private val client: Client, private val pwd: String) { //must run in thread
private val raw: ByteArray? private val raw: ByteArray?

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.update package top.fumiama.copymanga.api.update
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
@@ -16,7 +16,8 @@ import kotlinx.android.synthetic.main.dialog_progress.view.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.fumiama.copymanga.tools.ui.UITools import top.fumiama.copymanga.net.Client
import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.dmzj.copymanga.BuildConfig import top.fumiama.dmzj.copymanga.BuildConfig
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.io.File import java.io.File

View File

@@ -0,0 +1,159 @@
package top.fumiama.copymanga.api.user
import android.util.Base64
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.json.ComandyCapsule
import top.fumiama.copymanga.json.LoginInfoStructure
import top.fumiama.copymanga.lib.Comandy
import top.fumiama.copymanga.net.DownloadTools
import top.fumiama.dmzj.copymanga.R
import java.net.URLEncoder
import java.nio.charset.Charset
class Member(private val getString: (Int) -> String) {
val hasLogin: Boolean get() = Config.token.value?.isNotEmpty() ?: false
suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure =
withContext(Dispatchers.IO) {
var err = ""
(if (!Config.net_use_api_proxy.value && Comandy.instance.enabled)
postComandyLogin(username, pwd, salt)
else postLogin(username, pwd, salt))?.let { data ->
try {
return@withContext saveInfo(data)
} catch (e: Exception) {
err = e.message.toString()
}
} ?: run { err = getString(R.string.login_get_conn_failed) }
val l = LoginInfoStructure()
l.code = 400
l.message = err
return@withContext l
}
/**
* 获得登录信息并更新头像
* @return 登录态
* - **code**: 449: 未登录, 450: 有 Exception
* - **message**: 可以 toast 的信息
*/
suspend fun info(): LoginInfoStructure = withContext(Dispatchers.IO) {
if (!hasLogin) {
val l = LoginInfoStructure()
l.code = 449
l.message = getString(R.string.noLogin)
return@withContext l
}
try {
val data = Config.apiProxy?.comancry(
getString(R.string.memberInfoApiUrl)
.format(Config.myHostApiUrl.value)
) {
DownloadTools.getHttpContent(it)
}?.decodeToString()
try {
val l = Gson().fromJson(data, LoginInfoStructure::class.java)
if (l.code == 200) Config.avatar.value = l.results.avatar
l
} catch (e: Exception) {
val l = LoginInfoStructure()
l.code = 450
l.message = "${getString(R.string.login_get_avatar_failed)}: $data"
l
}
} catch (e: Exception) {
val l = LoginInfoStructure()
l.code = 450
l.message = "${getString(R.string.login_get_avatar_failed)}: $e"
l
}
}
suspend fun logout() = withContext(Dispatchers.IO) {
Config.token.value = ""
Config.user_id.value = null
Config.username.value = null
Config.nickname.value = null
Config.avatar.value = null
}
private suspend fun saveInfo(data: ByteArray) = data.inputStream().use { dataIn ->
try {
Gson().fromJson(dataIn.reader(), LoginInfoStructure::class.java)?.let { l ->
if (l.code == 200) {
Config.token.value = l.results?.token
Config.user_id.value = l.results?.user_id
Config.username.value = l.results?.username
Config.nickname.value = l.results.nickname
return@use info()
}
return@use l
} ?: throw Exception(getString(R.string.login_parse_json_error))
} catch (e: Exception) {
throw Exception(data.decodeToString(), e)
}
}
private suspend fun postLogin(username: String, pwd: String, salt: Int): ByteArray? =
Config.apiProxy?.comancry(getString(R.string.loginApiUrl).format(Config.myHostApiUrl.value)) {
DownloadTools.getApiConnection(it, "POST").let { c ->
c.doOutput = true
c.setRequestProperty(
"content-type",
"application/x-www-form-urlencoded;charset=utf-8"
)
c.setRequestProperty("platform", "3")
c.setRequestProperty("accept", "application/json")
val r = if (!Config.net_use_foreign.value) "1" else "0"
val pwdEncoded =
Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
c.outputStream.write(
"username=${
URLEncoder.encode(
username,
Charset.defaultCharset().name()
)
}&password=$pwdEncoded&salt=$salt&platform=3&authorization=Token+&version=${Config.app_ver.value}&source=copyApp&region=$r&webp=1".toByteArray()
)
c.outputStream.close()
val b = c.inputStream.readBytes()
c.inputStream.close()
b
}
}
private suspend fun postComandyLogin(username: String, pwd: String, salt: Int) =
Config.apiProxy?.comancry(getString(R.string.loginApiUrl).format(Config.myHostApiUrl.value)) {
DownloadTools.getComandyApiConnection(it, "POST", null, Config.pc_ua).apply {
headers["content-type"] = "application/x-www-form-urlencoded;charset=utf-8"
headers["platform"] = "3"
headers["accept"] = "application/json"
val r = if (!Config.net_use_foreign.value) "1" else "0"
val pwdEncoded =
Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
data = "username=${
URLEncoder.encode(
username,
Charset.defaultCharset().name()
)
}&password=$pwdEncoded&salt=$salt&platform=3&authorization=Token+&version=${Config.app_ver.value}&source=copyApp&region=$r&webp=1"
}.let { capsule ->
try {
val para = Gson().toJson(capsule)
Comandy.instance.getInstance()?.request(para)?.let { result ->
Gson().fromJson(result, ComandyCapsule::class.java)!!.let {
if (it.code != 200) null
else Base64.decode(it.data, Base64.DEFAULT)
}
}
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}
}

View File

@@ -0,0 +1,22 @@
package top.fumiama.copymanga.lib
import android.util.Log
import com.sun.jna.Memory
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.lib.template.LazyLibrary
class Comancry: LazyLibrary<ComancryMethods>(
ComancryMethods::class.java, "libcomancry.so", "API代理",
Config.net_use_api_proxy, Config.comancry_version
) {
suspend fun decrypt(sd: String, data: ByteArray): String? {
// 将 ByteArray 转换为 char*
val nativeMemory = Memory(data.size.toLong())
nativeMemory.write(0, data, 0, data.size) // 将 byteArray 写入内存
Log.d("MyComancry", "get data len ${data.size}")
return getInstance()?.decrypt(sd, nativeMemory, data.size)
}
companion object {
val instance = Comancry()
}
}

View File

@@ -0,0 +1,8 @@
package top.fumiama.copymanga.lib
import com.sun.jna.Library
import com.sun.jna.Pointer
interface ComancryMethods : Library {
fun decrypt(sd: String, data: Pointer, len: Int): String?
}

View File

@@ -0,0 +1,13 @@
package top.fumiama.copymanga.lib
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.lib.template.LazyLibrary
class Comandy: LazyLibrary<ComandyMethods>(
ComandyMethods::class.java, "libcomandy.so", "网络增强",
Config.net_use_comandy, Config.comandy_version
) {
companion object {
val instance = Comandy()
}
}

View File

@@ -0,0 +1,9 @@
package top.fumiama.copymanga.lib
import com.sun.jna.Library
interface ComandyMethods : Library {
// fun add_dns(para: String?, is_ipv6: Int): String?
fun request(para: String): String?
}

View File

@@ -0,0 +1,136 @@
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.tools.file.PreferenceBoolean
import top.fumiama.copymanga.tools.file.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
import java.util.concurrent.atomic.AtomicBoolean
import java.util.zip.GZIPInputStream
open class LazyLibrary<T: Library>(
private val clazz: Class<T>,
private val name: String,
private val functionName: String,
private val isInUse: PreferenceBoolean,
private val version: UserPreferenceInt
) {
private val repoName = name.substring(3).substringBeforeLast(".")
private var isInInit = AtomicBoolean(false)
private var mInstance: T? = null
suspend fun getInstance(): T? {
//Log.d("MyLazyLibrary", "get instance @$field")
if (mInstance != null) return mInstance
mInstance = libraryFile()?.absolutePath?.let { Native.load(it, clazz) }?:return null
//Log.d("MyLazyLibrary", "init instance @$field")
return mInstance
}
private var mEnabled: Boolean? = null
val enabled: Boolean
get() {
if (isInInit.get()) {
Log.d("MyLazyLibrary", "$name block enabled for isInInit")
return false
}
if (mEnabled != true && DownloadTools.failTimes.get() >= 2) {
mEnabled = true
return true
}
if (mEnabled != null) return mEnabled!!
val v = isInUse.value
mEnabled = v
return v
}
private var mLibraryFile: File? = null
private suspend fun libraryFile(): File? {
if (isInInit.get()) return null
mLibraryFile?.let { return it }
isInInit.set(true)
Log.d("MyLazyLibrary", "start to download/check $name")
val prefix = when (Build.SUPPORTED_ABIS[0]) {
"arm64-v8a" -> "aarch64"
"armeabi-v7a" -> "armv7a"
"x86_64" -> "x86_64"
"x86" -> "i686"
else -> null
}?: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@{
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 {
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)
info.dismiss()
} catch (e: Exception) {
e.printStackTrace()
if(f.exists()) f.delete()
}
}
}
}
mLibraryFile = if(f.exists()) f else null
return@withContext
}
}.join()
}
return mLibraryFile
}
}

View File

@@ -1,7 +1,8 @@
package top.fumiama.copymanga.update package top.fumiama.copymanga.net
//Fumiama 20210601 //Fumiama 20210601
//Client.kt //Client.kt
import android.util.Log import android.util.Log
import top.fumiama.copymanga.api.update.ByteArrayQueue
import java.io.* import java.io.*
import java.lang.Thread.sleep import java.lang.Thread.sleep
import java.net.Socket import java.net.Socket
@@ -75,12 +76,17 @@ class Client(private val ip: String, private val port: Int) {
try { try {
if (isConnect) { if (isConnect) {
Log.d("MyC", "开始接收服务端信息") Log.d("MyC", "开始接收服务端信息")
var prevP = 0
while(totalSize > buffer.size) { while(totalSize > buffer.size) {
val count = din?.read(receiveBuffer)?:0 val count = din?.read(receiveBuffer)?:0
if(count > 0) { if(count > 0) {
buffer += receiveBuffer.copyOfRange(0, count) buffer += receiveBuffer.copyOfRange(0, count)
Log.d("MyC", "reply length:$count") Log.d("MyC", "reply length:$count")
if(setProgress && totalSize > 0) progress?.notify(100 * buffer.size / totalSize) val p = 100 * buffer.size / totalSize
if(setProgress && totalSize > 0 && prevP != p) {
progress?.notify(p)
prevP = p
}
} else sleep(10) } else sleep(10)
} }
} else Log.d("MyC", "no connect to receive message") } else Log.d("MyC", "no connect to receive message")

View File

@@ -1,8 +1,7 @@
package top.fumiama.copymanga.tools.http package top.fumiama.copymanga.net
import android.content.Context import android.content.Context
import android.util.Base64 import android.util.Base64
import android.util.Log
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.Priority import com.bumptech.glide.Priority
import com.bumptech.glide.Registry import com.bumptech.glide.Registry
@@ -17,8 +16,9 @@ import com.bumptech.glide.load.model.MultiModelLoaderFactory
import com.bumptech.glide.module.AppGlideModule 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 top.fumiama.copymanga.MainActivity import kotlinx.coroutines.runBlocking
import top.fumiama.copymanga.json.ComandyCapsule import top.fumiama.copymanga.json.ComandyCapsule
import top.fumiama.copymanga.lib.Comandy
import java.nio.ByteBuffer import java.nio.ByteBuffer
@GlideModule @GlideModule
@@ -44,7 +44,7 @@ class ComandyGlideModule: AppGlideModule() {
} }
try { try {
val para = Gson().toJson(capsule) val para = Gson().toJson(capsule)
Comandy.instance!!.request(para).let { result -> runBlocking { Comandy.instance.getInstance() }!!.request(para).let { result ->
Gson().fromJson(result, ComandyCapsule::class.java)!!.let { Gson().fromJson(result, ComandyCapsule::class.java)!!.let {
if (it.code != 200) { if (it.code != 200) {
callback.onLoadFailed(IllegalArgumentException("HTTP${it.code} ${ callback.onLoadFailed(IllegalArgumentException("HTTP${it.code} ${
@@ -85,7 +85,7 @@ class ComandyGlideModule: AppGlideModule() {
} }
override fun handles(model: GlideUrl): Boolean { override fun handles(model: GlideUrl): Boolean {
return Comandy.useComandy && Comandy.instance != null && model.toURL().let { return Comandy.instance.enabled && runBlocking { Comandy.instance.getInstance() } != null && model.toURL().let {
it.protocol == "https" && it.host != "copymanga.azurewebsites.net" it.protocol == "https" && it.host != "copymanga.azurewebsites.net"
} }
} }
@@ -103,7 +103,7 @@ class ComandyGlideModule: AppGlideModule() {
} }
override fun handles(model: String): Boolean { override fun handles(model: String): Boolean {
return Comandy.useComandy && Comandy.instance != null && model.startsWith("https://") return Comandy.instance.enabled && runBlocking { Comandy.instance.getInstance() } != null && model.startsWith("https://")
} }
} }

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.tools.http package top.fumiama.copymanga.net
import android.util.Log import android.util.Log
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config

View File

@@ -1,13 +1,18 @@
package top.fumiama.copymanga.tools.http package top.fumiama.copymanga.net
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
import com.google.gson.Gson import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
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.json.ComandyCapsule import top.fumiama.copymanga.json.ComandyCapsule
import top.fumiama.copymanga.lib.Comandy
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.io.OutputStream
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
import java.util.concurrent.Callable import java.util.concurrent.Callable
@@ -85,13 +90,39 @@ object DownloadTools {
capsule capsule
} }
suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = Config.pc_ua): ByteArray = private fun InputStream.copyToWithProgress(out: OutputStream, sz: Int, p: Client.Progress, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
var bytesCopied: Long = 0
val buffer = ByteArray(bufferSize)
var bytes = read(buffer)
var prevP = 0
p.notify(0)
while (bytes >= 0) {
val progress = (100*bytesCopied/sz).toInt()
if (prevP != progress) {
p.notify(progress)
prevP = progress
}
out.write(buffer, 0, bytes)
bytesCopied += bytes
bytes = read(buffer)
}
p.notify(100)
return bytesCopied
}
private fun InputStream.readBytesWithProgress(sz: Int, p: Client.Progress): ByteArray {
val buffer = ByteArrayOutputStream(maxOf(DEFAULT_BUFFER_SIZE, this.available()))
copyToWithProgress(buffer, sz, p)
return buffer.toByteArray()
}
suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = Config.pc_ua, p: Client.Progress? = null): ByteArray =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
if (!u.startsWith("https://copymanga.azurewebsites.net") && Comandy.useComandy) { if (!u.startsWith("https://copymanga.azurewebsites.net") && 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")
Comandy.instance?.request(para)?.let { result -> Comandy.instance.getInstance()?.request(para)?.let { result ->
//Log.d("MyDT", "comandy reply: $result") //Log.d("MyDT", "comandy reply: $result")
Gson().fromJson(result, ComandyCapsule::class.java)!!.let { Gson().fromJson(result, ComandyCapsule::class.java)!!.let {
if (it.code != 200) throw IllegalArgumentException("HTTP${it.code} ${ if (it.code != 200) throw IllegalArgumentException("HTTP${it.code} ${
@@ -104,7 +135,12 @@ object DownloadTools {
} }
failTimes.incrementAndGet() failTimes.incrementAndGet()
getApiConnection(u, "GET", refer, ua).let { getApiConnection(u, "GET", refer, ua).let {
val ret = it.inputStream.readBytes() val sz = it.getHeaderFieldInt("Content-Length", 0)
val ret = if (sz > 0 && p != null) {
it.inputStream.readBytesWithProgress(sz, p)
} else {
it.inputStream.readBytes()
}
it.disconnect() it.disconnect()
Log.d("MyDT", "getHttpContent: ${ret.size} bytes") Log.d("MyDT", "getHttpContent: ${ret.size} bytes")
failTimes.decrementAndGet() failTimes.decrementAndGet()
@@ -112,24 +148,25 @@ object DownloadTools {
} }
} }
fun getHttpContent(u: String, readSize: Int): ByteArray? { fun getHttpContent(u: String, readSize: Int, p: Client.Progress? = null): ByteArray? {
Log.d("MyDT", "getHttp: $u") Log.d("MyDT", "getHttp: $u")
val task = prepare(u, readSize) val task = prepare(u, readSize, p)
Thread(task).start() Thread(task).start()
return try { return try {
task.get() task.get()
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
if (Comandy.useComandy) failTimes.incrementAndGet() if (Comandy.instance.enabled) failTimes.incrementAndGet()
null null
} }
} }
fun prepare(u: String, readSize: Int = -1) = run { // 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") Log.d("MyDT", "prepareHttp: $u")
FutureTask(if (!u.startsWith("https://copymanga.azurewebsites.net") && Comandy.useComandy) Callable{ FutureTask(if (!u.startsWith("https://copymanga.azurewebsites.net") && Comandy.instance.enabled) Callable{
try { try {
Comandy.instance?.request(Gson().toJson( runBlocking { Comandy.instance.getInstance() }?.request(Gson().toJson(
getComandyNormalConnection(u, "GET", Config.pc_ua)) getComandyNormalConnection(u, "GET", Config.pc_ua))
)?.let { result -> )?.let { result ->
Gson().fromJson(result, ComandyCapsule::class.java)?.let { Gson().fromJson(result, ComandyCapsule::class.java)?.let {
@@ -146,10 +183,15 @@ object DownloadTools {
try { try {
val connection = getNormalConnection(u, "GET", Config.pc_ua) val connection = getNormalConnection(u, "GET", Config.pc_ua)
val ci = connection.inputStream val ci = connection.inputStream
val sz = connection.getHeaderFieldInt("Content-Length", 0)
if(readSize > 0) { if(readSize > 0) {
ret = ByteArray(readSize) ret = ByteArray(readSize)
ci?.read(ret, 0, readSize) ci?.read(ret, 0, readSize)
} else ret = ci?.readBytes() } else ret = if (sz > 0 && p != null) {
ci.readBytesWithProgress(sz, p)
} else {
ci.readBytes()
}
ci?.close() ci?.close()
connection.disconnect() connection.disconnect()
} catch (ex: Exception) { } catch (ex: Exception) {
@@ -169,12 +211,12 @@ 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.useComandy) Callable{ val task = FutureTask(if(!url.startsWith("https://copymanga.azurewebsites.net") && 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 }
capsule.data = body.decodeToString() capsule.data = body.decodeToString()
Comandy.instance?.request(Gson().toJson(capsule))?.let { result -> runBlocking { Comandy.instance.getInstance() }?.request(Gson().toJson(capsule))?.let { result ->
Gson().fromJson(result, ComandyCapsule::class.java)?.let { Gson().fromJson(result, ComandyCapsule::class.java)?.let {
it.data?.let { d -> Base64.decode(d, Base64.DEFAULT) }?:"empty comandy data".encodeToByteArray() it.data?.let { d -> Base64.decode(d, Base64.DEFAULT) }?:"empty comandy data".encodeToByteArray()
} }

View File

@@ -0,0 +1,53 @@
package top.fumiama.copymanga.net
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.api.Config.proxy_key
import top.fumiama.copymanga.lib.Comancry
import java.net.URLEncoder
import java.nio.charset.Charset
class Proxy(id: Int, private val apiRegex: Regex) {
private val code get() = proxy_key.value
private val proxyApiUrl = MainActivity.mainWeakReference?.get()?.getString(id)
private val sourceDir = MainActivity.mainWeakReference?.get()?.applicationInfo?.sourceDir?.substringBeforeLast("/")?:""
private val enabled: Boolean get() = code.isNotEmpty() and sourceDir.isNotEmpty() and !proxyApiUrl.isNullOrEmpty()
fun wrap(u: String): String {
if(!apiRegex.containsMatchIn(u)) {
Log.d("MyP", "[N] wrap: $u")
return u
}
if(enabled) {
val wu = proxyApiUrl?.format(code, URLEncoder.encode(u, Charset.defaultCharset().name()))
?:u
Log.d("MyP", "[M] wrap: $wu")
return wu
}
Log.d("MyP", "[C] wrap: $u")
//return proxyApiUrl?.format(URLEncoder.encode(u, Charset.defaultCharset().name()))?:u
return u
}
suspend fun comancry(u: String, use: suspend (String) -> ByteArray?): ByteArray? {
if(!apiRegex.containsMatchIn(u)) {
Log.d("MyP", "[N] comancry: $u")
return use(u)
}
if(enabled) {
val wu = proxyApiUrl?.format(code,
withContext(Dispatchers.IO) {
URLEncoder.encode(u, Charset.defaultCharset().name())
})
?:u
Log.d("MyP", "[M] comancry: $wu, sd: $sourceDir")
return use(wu)?.let { data ->
Comancry.instance.decrypt(sourceDir, data)?.encodeToByteArray()
}
}
Log.d("MyP", "[C] comancry: $u")
return use(u)
}
}

View File

@@ -0,0 +1,11 @@
package top.fumiama.copymanga.net
import top.fumiama.copymanga.api.Config
class Resolution(private val original: Regex) {
private val imageResolution: Int
get() = Config.net_img_resolution.value.let {
if (it.isNotEmpty()) it.toInt() else 1500
}
fun wrap(u: String) : String = u.replace(original, "c${imageResolution}x.")
}

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.template.http package top.fumiama.copymanga.net.template
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@@ -14,7 +14,7 @@ import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.ReturnBase import top.fumiama.copymanga.json.ReturnBase
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.http.DownloadTools import top.fumiama.copymanga.net.DownloadTools
import java.io.File import java.io.File
import java.security.MessageDigest import java.security.MessageDigest
@@ -77,7 +77,13 @@ open class AutoDownloadHandler(
var cnt = 0 var cnt = 0
while (cnt++ <= 3) { while (cnt++ <= 3) {
try { try {
val data = DownloadTools.getHttpContent(Config.apiProxy?.wrap(url)?:url) val data = Config.apiProxy?.comancry(url) {
DownloadTools.getHttpContent(it)
}
if (data == null) {
delay(2000)
continue
}
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))

View File

@@ -1,11 +1,11 @@
package top.fumiama.copymanga.template.http package top.fumiama.copymanga.net.template
import android.util.Log import android.util.Log
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.http.DownloadTools import top.fumiama.copymanga.net.DownloadTools
import kotlin.random.Random import kotlin.random.Random
class PausableDownloader(private val url: String, private val waitMilliseconds: Long = 0, private val isApi: Boolean = true, private val whenFinish: (suspend (result: ByteArray)->Unit)? = null) { class PausableDownloader(private val url: String, private val waitMilliseconds: Long = 0, private val isApi: Boolean = true, private val whenFinish: (suspend (result: ByteArray)->Unit)? = null) {
@@ -14,10 +14,13 @@ class PausableDownloader(private val url: String, private val waitMilliseconds:
var c = 0 var c = 0
while (!exit && c++ < 3) { while (!exit && c++ < 3) {
try { try {
val data = (DownloadTools.getHttpContent( val data = if (isApi) Config.apiProxy?.comancry(url) {
(if(isApi) Config.apiProxy?.wrap(url) else null)?:url, DownloadTools.getHttpContent(it, Config.referer)
Config.referer } else DownloadTools.getHttpContent(url, Config.referer)
)) if (data == null) {
delay(3000)
continue
}
whenFinish?.let { it(data) } whenFinish?.let { it(data) }
return@withContext true return@withContext true
} catch (e: Exception) { } catch (e: Exception) {

View File

@@ -1,105 +0,0 @@
package top.fumiama.copymanga.tools.http
import android.os.Build
import android.util.Log
import com.google.gson.Gson
import com.sun.jna.Library
import com.sun.jna.Native
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.json.ComandyVersion
import top.fumiama.dmzj.copymanga.R
import java.io.ByteArrayInputStream
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean
import java.util.zip.GZIPInputStream
interface Comandy : Library {
// fun add_dns(para: String?, is_ipv6: Int): String?
fun request(para: String): String?
companion object {
private var isInInit = AtomicBoolean(false)
var instance: Comandy? = null
get() {
//Log.d("MyComandy", "get instance @$field")
if (field != null) return field
field = libraryFile?.absolutePath?.let { Native.load(it, Comandy::class.java) }?:return null
//Log.d("MyComandy", "init instance @$field")
return field
}
private var mUseComandy: Boolean? = null
val useComandy: Boolean
get() {
if (isInInit.get()) {
Log.d("MyComandy", "block useComandy for isInInit")
return false
}
if (mUseComandy != true && DownloadTools.failTimes.get() >= 2) {
mUseComandy = true
return true
}
if (mUseComandy != null) return mUseComandy!!
val v = Config.net_use_comandy.value
mUseComandy = v
return v
}
private val libraryFile: File?
get() {
if (isInInit.get()) return null
isInInit.set(true)
Log.d("MyComandy", "start to download/check lib")
val prefix = when (Build.SUPPORTED_ABIS[0]) {
"arm64-v8a" -> "aarch64"
"armeabi-v7a" -> "armv7a"
"x86_64" -> "x86_64"
"x86" -> "i686"
else -> null
}?:return null
Log.d("MyComandy", "arch: $prefix")
return MainActivity.mainWeakReference?.get()?.let { ma ->
var f = File(ma.filesDir, "libs")
if (!f.exists()) f.mkdirs()
f = File(f, "libcomandy.so")
var remoteVersion = 0
if (f.exists()) {
DownloadTools.getHttpContent(ma.getString(R.string.comandy_version_url), -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 = Config.comandy_version.value?:0
if (myVersion >= remoteVersion) {
Log.d("MyComandy", "lib version $myVersion is latest")
isInInit.set(false)
return@let f
}
Log.d("MyComandy", "lib version $myVersion <= latest $remoteVersion, update...")
}
}
DownloadTools.getHttpContent(ma.getString(R.string.comandy_download_url).format(prefix), -1)?.let {
if(f.exists()) f.delete()
try {
GZIPInputStream(ByteArrayInputStream(it)).use { dataIn ->
f.outputStream().use { dataOut ->
dataIn.copyTo(dataOut)
}
}
if (remoteVersion > 0) Config.comandy_version.value = remoteVersion
Log.d("MyComandy", "update success")
isInInit.set(false)
} catch (e: Exception) {
e.printStackTrace()
if(f.exists()) f.delete()
}
}
return@let if(f.exists()) f else null
}
}
}
}

View File

@@ -1,28 +0,0 @@
package top.fumiama.copymanga.tools.http
import android.util.Log
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.api.Config.proxy_key
import java.net.URLEncoder
import java.nio.charset.Charset
class Proxy(id: Int, private val apiRegex: Regex) {
private val code get() = proxy_key.value
private val proxyApiUrl = MainActivity.mainWeakReference?.get()?.getString(id)
fun wrap(u: String): String {
if(!apiRegex.containsMatchIn(u)) {
Log.d("MyP", "[N] wrap: $u")
return u
}
if(code.isNotEmpty() and !proxyApiUrl.isNullOrEmpty()) {
val wu = proxyApiUrl?.format(code, URLEncoder.encode(u, Charset.defaultCharset().name()))
?:u
Log.d("MyP", "[M] wrap: $wu")
return wu
}
Log.d("MyP", "[C] wrap: $u")
//return proxyApiUrl?.format(URLEncoder.encode(u, Charset.defaultCharset().name()))?:u
return u
}
}

View File

@@ -1,20 +0,0 @@
package top.fumiama.copymanga.tools.http
import androidx.preference.PreferenceManager
import top.fumiama.copymanga.MainActivity
import top.fumiama.dmzj.copymanga.R
class Resolution(private val original: Regex) {
private val imageResolution: Int
get() {
MainActivity.mainWeakReference?.get()?.apply {
PreferenceManager.getDefaultSharedPreferences(this).apply {
val b = getString(getString(R.string.imgResolutionKeyID), null)
//Log.d("MyResolution", "use image resolution: $b")
return b?.toInt()?:1500
}
}
return 1500
}
fun wrap(u: String) : String = u.replace(original, "c${imageResolution}x.")
}

View File

@@ -17,10 +17,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch 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.manga.Book import top.fumiama.copymanga.api.manga.Book
import top.fumiama.copymanga.manga.Reader import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.template.general.NoBackRefreshFragment import top.fumiama.copymanga.view.template.NoBackRefreshFragment
import top.fumiama.copymanga.tools.ui.Navigate import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference import java.lang.ref.WeakReference

View File

@@ -38,11 +38,11 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.ThemeStructure import top.fumiama.copymanga.json.ThemeStructure
import top.fumiama.copymanga.manga.Reader import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.ui.GlideBlurTransformation import top.fumiama.copymanga.view.operation.GlideBlurTransformation
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener import top.fumiama.copymanga.view.operation.GlideHideLottieViewListener
import top.fumiama.copymanga.tools.ui.Navigate import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference import java.lang.ref.WeakReference

View File

@@ -1,6 +1,6 @@
package top.fumiama.copymanga.ui.cardflow.author package top.fumiama.copymanga.ui.cardflow.author
import top.fumiama.copymanga.template.ui.ThemeCardFlow import top.fumiama.copymanga.view.template.ThemeCardFlow
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
@ExperimentalStdlibApi @ExperimentalStdlibApi

View File

@@ -1,6 +1,6 @@
package top.fumiama.copymanga.ui.cardflow.caption package top.fumiama.copymanga.ui.cardflow.caption
import top.fumiama.copymanga.template.ui.ThemeCardFlow import top.fumiama.copymanga.view.template.ThemeCardFlow
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
@ExperimentalStdlibApi @ExperimentalStdlibApi

View File

@@ -2,7 +2,7 @@ package top.fumiama.copymanga.ui.cardflow.finish
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import top.fumiama.copymanga.template.ui.StatusCardFlow import top.fumiama.copymanga.view.template.StatusCardFlow
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import kotlinx.android.synthetic.main.line_finish.* import kotlinx.android.synthetic.main.line_finish.*

View File

@@ -4,7 +4,7 @@ import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import top.fumiama.copymanga.MainActivity import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.template.ui.InfoCardLoader import top.fumiama.copymanga.view.template.InfoCardLoader
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R

View File

@@ -1,6 +1,6 @@
package top.fumiama.copymanga.ui.cardflow.newest package top.fumiama.copymanga.ui.cardflow.newest
import top.fumiama.copymanga.template.ui.InfoCardLoader import top.fumiama.copymanga.view.template.InfoCardLoader
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R

View File

@@ -4,9 +4,9 @@ import android.os.Bundle
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import kotlinx.android.synthetic.main.fragment_rank.* import kotlinx.android.synthetic.main.fragment_rank.*
import kotlinx.android.synthetic.main.line_rank.view.* import kotlinx.android.synthetic.main.line_rank.view.*
import top.fumiama.copymanga.template.ui.InfoCardLoader import top.fumiama.copymanga.view.template.InfoCardLoader
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.ui.UITools import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference import java.lang.ref.WeakReference

View File

@@ -1,6 +1,6 @@
package top.fumiama.copymanga.ui.cardflow.recommend package top.fumiama.copymanga.ui.cardflow.recommend
import top.fumiama.copymanga.template.ui.InfoCardLoader import top.fumiama.copymanga.view.template.InfoCardLoader
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R

View File

@@ -2,7 +2,7 @@ package top.fumiama.copymanga.ui.cardflow.search
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import top.fumiama.copymanga.template.ui.InfoCardLoader import top.fumiama.copymanga.view.template.InfoCardLoader
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R

View File

@@ -8,7 +8,7 @@ import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.anchor_popular.view.* import kotlinx.android.synthetic.main.anchor_popular.view.*
import kotlinx.android.synthetic.main.line_shelf.* import kotlinx.android.synthetic.main.line_shelf.*
import top.fumiama.copymanga.MainActivity import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.template.ui.InfoCardLoader import top.fumiama.copymanga.view.template.InfoCardLoader
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R

View File

@@ -12,8 +12,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.FilterStructure import top.fumiama.copymanga.json.FilterStructure
import top.fumiama.copymanga.json.ThemeStructure import top.fumiama.copymanga.json.ThemeStructure
import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.template.ui.StatusCardFlow import top.fumiama.copymanga.view.template.StatusCardFlow
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R

View File

@@ -9,8 +9,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.TopicStructure import top.fumiama.copymanga.json.TopicStructure
import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.template.ui.InfoCardLoader import top.fumiama.copymanga.view.template.InfoCardLoader
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R

View File

@@ -14,7 +14,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.VolumeStructure import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.template.general.NoBackRefreshFragment import top.fumiama.copymanga.view.template.NoBackRefreshFragment
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

View File

@@ -28,14 +28,14 @@ import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.json.ChapterStructure import top.fumiama.copymanga.json.ChapterStructure
import top.fumiama.copymanga.json.ComicStructureOld import top.fumiama.copymanga.json.ComicStructureOld
import top.fumiama.copymanga.json.VolumeStructure import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.manga.Downloader import top.fumiama.copymanga.api.manga.Downloader
import top.fumiama.copymanga.manga.Reader import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.tools.ui.UITools import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.exit import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.exit
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.json import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.json
import top.fumiama.copymanga.ui.vm.ViewMangaActivity import top.fumiama.copymanga.ui.vm.ViewMangaActivity
import top.fumiama.copymanga.views.ChapterToggleButton import top.fumiama.copymanga.view.ChapterToggleButton
import top.fumiama.copymanga.views.LazyScrollView import top.fumiama.copymanga.view.LazyScrollView
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

View File

@@ -14,11 +14,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch 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.manga.Downloader import top.fumiama.copymanga.api.manga.Downloader
import top.fumiama.copymanga.manga.Reader import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.template.general.NoBackRefreshFragment import top.fumiama.copymanga.view.template.NoBackRefreshFragment
import top.fumiama.copymanga.tools.file.FileUtils import top.fumiama.copymanga.tools.file.FileUtils
import top.fumiama.copymanga.tools.ui.Navigate import top.fumiama.copymanga.view.interaction.Navigate
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

View File

@@ -13,13 +13,13 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
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.manga.Downloader import top.fumiama.copymanga.api.manga.Downloader
import top.fumiama.copymanga.manga.Reader import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.template.general.MangaPagesFragmentTemplate import top.fumiama.copymanga.view.template.MangaPagesFragmentTemplate
import top.fumiama.copymanga.template.ui.CardList import top.fumiama.copymanga.view.template.CardList
import top.fumiama.copymanga.tools.file.FileUtils import top.fumiama.copymanga.tools.file.FileUtils
import top.fumiama.copymanga.tools.ui.Navigate import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.copymanga.tools.ui.UITools import top.fumiama.copymanga.view.interaction.UITools
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

View File

@@ -30,11 +30,11 @@ import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.MainActivity.Companion.ime import top.fumiama.copymanga.MainActivity.Companion.ime
import top.fumiama.copymanga.json.BookListStructure import top.fumiama.copymanga.json.BookListStructure
import top.fumiama.copymanga.template.general.NoBackRefreshFragment import top.fumiama.copymanga.view.template.NoBackRefreshFragment
import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener import top.fumiama.copymanga.view.operation.GlideHideLottieViewListener
import top.fumiama.copymanga.tools.ui.Navigate import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference import java.lang.ref.WeakReference

View File

@@ -30,11 +30,11 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.ComicStructure import top.fumiama.copymanga.json.ComicStructure
import top.fumiama.copymanga.json.IndexStructure import top.fumiama.copymanga.json.IndexStructure
import top.fumiama.copymanga.template.http.AutoDownloadHandler import top.fumiama.copymanga.net.template.AutoDownloadHandler
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener import top.fumiama.copymanga.view.operation.GlideHideLottieViewListener
import top.fumiama.copymanga.tools.ui.Navigate import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.copymanga.tools.ui.UITools import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.dmzj.copymanga.R 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

View File

@@ -11,8 +11,8 @@ 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.tools.ui.UITools import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.copymanga.views.AutoHideEditTextPreferenceDialogFragmentCompat import top.fumiama.copymanga.view.AutoHideEditTextPreferenceDialogFragmentCompat
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
class SettingsFragment: PreferenceFragmentCompat() { class SettingsFragment: PreferenceFragmentCompat() {

View File

@@ -1,7 +1,7 @@
package top.fumiama.copymanga.ui.vm package top.fumiama.copymanga.ui.vm
import android.widget.Toast import android.widget.Toast
import top.fumiama.copymanga.manga.Reader import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference import java.lang.ref.WeakReference

View File

@@ -20,9 +20,9 @@ import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.json.Chapter2Return import top.fumiama.copymanga.json.Chapter2Return
import top.fumiama.copymanga.json.ChapterWithContent import top.fumiama.copymanga.json.ChapterWithContent
import top.fumiama.copymanga.json.ComicStructure import top.fumiama.copymanga.json.ComicStructure
import top.fumiama.copymanga.template.http.AutoDownloadHandler import top.fumiama.copymanga.net.template.AutoDownloadHandler
import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.views.ScaleImageView import top.fumiama.copymanga.view.ScaleImageView
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

View File

@@ -54,12 +54,12 @@ 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 import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.template.general.TitleActivityTemplate import top.fumiama.copymanga.view.template.TitleActivityTemplate
import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.tools.http.DownloadTools import top.fumiama.copymanga.net.DownloadTools
import top.fumiama.copymanga.tools.thread.TimeThread import top.fumiama.copymanga.tools.thread.TimeThread
import top.fumiama.copymanga.tools.ui.Font import top.fumiama.copymanga.view.Font
import top.fumiama.copymanga.views.ScaleImageView import top.fumiama.copymanga.view.ScaleImageView
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream

View File

@@ -1,154 +0,0 @@
package top.fumiama.copymanga.user
import android.util.Base64
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.json.ComandyCapsule
import top.fumiama.copymanga.json.LoginInfoStructure
import top.fumiama.copymanga.tools.http.Comandy
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.dmzj.copymanga.R
import java.net.URLEncoder
import java.nio.charset.Charset
class Member(private val getString: (Int) -> String) {
val hasLogin: Boolean get() = Config.token.value?.isNotEmpty()?:false
suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure = withContext(Dispatchers.IO) {
var err = ""
if (!Config.net_use_api_proxy.value && Comandy.useComandy)
getComandyLoginConnection(username, pwd, salt).let { capsule ->
try {
val para = Gson().toJson(capsule)
Comandy.instance?.request(para)?.let { result ->
Gson().fromJson(result, ComandyCapsule::class.java)!!.let {
if (it.code != 200) {
val l = LoginInfoStructure()
l.code = it.code
l.message = it.data?.let { d -> Base64.decode(d, Base64.DEFAULT).decodeToString() }?:"HTTP ${it.code}"
return@withContext l
}
Base64.decode(it.data, Base64.DEFAULT)
}
}
} catch (e: Exception) {
err = e.message.toString()
null
}
}?.let {
try {
return@withContext saveInfo(it)
} catch (e: Exception) {
err = e.message.toString()
}
}
else getLoginConnection(username, pwd, salt).apply {
inputStream.use {
it?.readBytes()?.let { data ->
try {
return@withContext saveInfo(data)
} catch (e: Exception) {
err = e.message.toString()
}
}?: run { err = getString(R.string.login_get_conn_failed) }
}
}
val l = LoginInfoStructure()
l.code = 400
l.message = err
return@withContext l
}
/**
* 获得登录信息并更新头像
* @return 登录态
* - **code**: 449: 未登录, 450: 有 Exception
* - **message**: 可以 toast 的信息
*/
suspend fun info() : LoginInfoStructure = withContext(Dispatchers.IO) {
if (!hasLogin) {
val l = LoginInfoStructure()
l.code = 449
l.message = getString(R.string.noLogin)
return@withContext l
}
try {
val data = DownloadTools.getHttpContent(
getString(R.string.memberInfoApiUrl).format(Config.myHostApiUrl.value).let {
Config.apiProxy?.wrap(it)?:it
}
).decodeToString()
try {
val l = Gson().fromJson(data, LoginInfoStructure::class.java)
if(l.code == 200) Config.avatar.value = l.results.avatar
l
} catch (e : Exception) {
val l = LoginInfoStructure()
l.code = 450
l.message = "${getString(R.string.login_get_avatar_failed)}: $data"
l
}
} catch (e: Exception) {
val l = LoginInfoStructure()
l.code = 450
l.message = "${getString(R.string.login_get_avatar_failed)}: $e"
l
}
}
suspend fun logout() = withContext(Dispatchers.IO) {
Config.token.value = ""
Config.user_id.value = null
Config.username.value = null
Config.nickname.value = null
Config.avatar.value = null
}
private suspend fun saveInfo(data: ByteArray) = data.inputStream().use { dataIn ->
try {
Gson().fromJson(dataIn.reader(), LoginInfoStructure::class.java)?.let { l ->
if(l.code == 200) {
Config.token.value = l.results?.token
Config.user_id.value = l.results?.user_id
Config.username.value = l.results?.username
Config.nickname.value = l.results.nickname
return@use info()
}
return@use l
}?: throw Exception(getString(R.string.login_parse_json_error))
} catch (e: Exception) {
throw Exception(data.decodeToString(), e)
}
}
private fun getLoginConnection(username: String, pwd: String, salt: Int) =
getString(R.string.loginApiUrl).format(Config.myHostApiUrl.value).let {
Config.apiProxy?.wrap(it)?:it
}.let {
DownloadTools.getApiConnection(it, "POST").apply {
doOutput = true
setRequestProperty("content-type", "application/x-www-form-urlencoded;charset=utf-8")
setRequestProperty("platform", "3")
setRequestProperty("accept", "application/json")
val r = if(!Config.net_use_foreign.value) "1" else "0"
val pwdEncoded = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
outputStream.write("username=${URLEncoder.encode(username, Charset.defaultCharset().name())}&password=$pwdEncoded&salt=$salt&platform=3&authorization=Token+&version=${Config.app_ver.value}&source=copyApp&region=$r&webp=1".toByteArray())
}
}
private fun getComandyLoginConnection(username: String, pwd: String, salt: Int) =
getString(R.string.loginApiUrl).format(Config.myHostApiUrl.value).let {
Config.apiProxy?.wrap(it)?:it
}.let {
DownloadTools.getComandyApiConnection(it, "POST", null, Config.pc_ua).apply {
headers["content-type"] = "application/x-www-form-urlencoded;charset=utf-8"
headers["platform"] = "3"
headers["accept"] = "application/json"
val r = if(!Config.net_use_foreign.value) "1" else "0"
val pwdEncoded = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
data = "username=${URLEncoder.encode(username, Charset.defaultCharset().name())}&password=$pwdEncoded&salt=$salt&platform=3&authorization=Token+&version=${Config.app_ver.value}&source=copyApp&region=$r&webp=1"
}
}
}

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.views package top.fumiama.copymanga.view
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.graphics.Rect import android.graphics.Rect

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.views package top.fumiama.copymanga.view
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.tools.ui package top.fumiama.copymanga.view
import android.graphics.Typeface import android.graphics.Typeface
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.views package top.fumiama.copymanga.view
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context

View File

@@ -1,9 +1,8 @@
package top.fumiama.copymanga.views package top.fumiama.copymanga.view
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import java.io.File
class MangaCardView :CardView { class MangaCardView :CardView {
constructor(context: Context): super(context) constructor(context: Context): super(context)

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.views package top.fumiama.copymanga.view
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.views package top.fumiama.copymanga.view
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener import android.animation.ValueAnimator.AnimatorUpdateListener

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.views package top.fumiama.copymanga.view
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.tools.ui package top.fumiama.copymanga.view.interaction
import android.os.Bundle import android.os.Bundle
import androidx.navigation.NavController import androidx.navigation.NavController

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.tools.ui package top.fumiama.copymanga.view.interaction
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.tools.ui package top.fumiama.copymanga.view.operation
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.tools.ui package top.fumiama.copymanga.view.operation
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.view.View import android.view.View

View File

@@ -1,11 +1,10 @@
package top.fumiama.copymanga.template.general package top.fumiama.copymanga.view.template
import android.app.Activity
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit import androidx.core.content.edit
import top.fumiama.copymanga.tools.ui.UITools import top.fumiama.copymanga.view.interaction.UITools
open class ActivityTemplate: AppCompatActivity() { open class ActivityTemplate: AppCompatActivity() {
lateinit var toolsBox: UITools lateinit var toolsBox: UITools

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.template.ui package top.fumiama.copymanga.view.template
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.net.Uri import android.net.Uri
@@ -14,7 +14,7 @@ import kotlinx.android.synthetic.main.line_lazybooklines.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.fumiama.copymanga.api.Config import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener import top.fumiama.copymanga.view.operation.GlideHideLottieViewListener
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

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.template.ui package top.fumiama.copymanga.view.template
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@@ -15,9 +15,8 @@ import top.fumiama.copymanga.json.BookListStructure
import top.fumiama.copymanga.json.HistoryBookListStructure import top.fumiama.copymanga.json.HistoryBookListStructure
import top.fumiama.copymanga.json.ShelfStructure import top.fumiama.copymanga.json.ShelfStructure
import top.fumiama.copymanga.json.TypeBookListStructure import top.fumiama.copymanga.json.TypeBookListStructure
import top.fumiama.copymanga.template.general.MangaPagesFragmentTemplate import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.template.http.PausableDownloader import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.copymanga.tools.ui.Navigate
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
@ExperimentalStdlibApi @ExperimentalStdlibApi

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.template.general package top.fumiama.copymanga.view.template
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
@@ -19,8 +19,7 @@ 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.template.ui.CardList import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference import java.lang.ref.WeakReference

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.template.general package top.fumiama.copymanga.view.template
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.os.Bundle import android.os.Bundle
@@ -11,7 +11,7 @@ import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.content_main.* import kotlinx.android.synthetic.main.content_main.*
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.tools.ui.UITools import top.fumiama.copymanga.view.interaction.UITools
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
open class NoBackRefreshFragment(private val layoutToLoad: Int): Fragment() { open class NoBackRefreshFragment(private val layoutToLoad: Int): Fragment() {

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.template.ui package top.fumiama.copymanga.view.template
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.view.View import android.view.View

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.template.ui package top.fumiama.copymanga.view.template
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.template.general package top.fumiama.copymanga.view.template
import android.os.Bundle import android.os.Bundle
import kotlinx.android.synthetic.main.widget_titlebar.* import kotlinx.android.synthetic.main.widget_titlebar.*

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<top.fumiama.copymanga.views.ChapterToggleButton xmlns:android="http://schemas.android.com/apk/res/android" <top.fumiama.copymanga.view.ChapterToggleButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tbtn" android:id="@+id/tbtn"
android:layout_width="64dp" android:layout_width="64dp"

View File

@@ -5,7 +5,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<top.fumiama.copymanga.views.MangaCardView <top.fumiama.copymanga.view.MangaCardView
android:id="@+id/cic" android:id="@+id/cic"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
@@ -116,5 +116,5 @@
tools:visibility="visible" /> tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</top.fumiama.copymanga.views.MangaCardView> </top.fumiama.copymanga.view.MangaCardView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -4,7 +4,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<top.fumiama.copymanga.views.MangaCardView <top.fumiama.copymanga.view.MangaCardView
android:id="@+id/cic" android:id="@+id/cic"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
@@ -62,5 +62,5 @@
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</top.fumiama.copymanga.views.MangaCardView> </top.fumiama.copymanga.view.MangaCardView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -5,7 +5,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<top.fumiama.copymanga.views.LazyScrollView <top.fumiama.copymanga.view.LazyScrollView
android:id="@+id/dllazys" android:id="@+id/dllazys"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@@ -15,7 +15,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"/> android:orientation="vertical"/>
</top.fumiama.copymanga.views.LazyScrollView> </top.fumiama.copymanga.view.LazyScrollView>
<include <include
android:id="@+id/dlsdwn" android:id="@+id/dlsdwn"

View File

@@ -15,7 +15,7 @@
android:layout_marginTop="@dimen/search_layout_padding" android:layout_marginTop="@dimen/search_layout_padding"
android:isScrollContainer="true"> android:isScrollContainer="true">
<top.fumiama.copymanga.views.ScrollRefreshView <top.fumiama.copymanga.view.ScrollRefreshView
android:id="@+id/fhov" android:id="@+id/fhov"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -32,7 +32,7 @@
android:orientation="vertical"> android:orientation="vertical">
</LinearLayout> </LinearLayout>
</top.fumiama.copymanga.views.ScrollRefreshView> </top.fumiama.copymanga.view.ScrollRefreshView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.lapism.search.widget.MaterialSearchView <com.lapism.search.widget.MaterialSearchView

View File

@@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<top.fumiama.copymanga.views.ScaleImageView <top.fumiama.copymanga.view.ScaleImageView
android:id="@+id/onei" android:id="@+id/onei"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />

View File

@@ -92,8 +92,8 @@
<string name="apiProxyApiUrl">https://copymanga.azurewebsites.net/api/api?code=%1$s&amp;url=%2$s</string> <string name="apiProxyApiUrl">https://copymanga.azurewebsites.net/api/api?code=%1$s&amp;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="comandy_version_url">https://gitea.seku.su/api/v1/repos/fumiama/comandy/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/comandy/releases/download/default/%1$s_libcomandy.so.gz"</string> <string name="comandy_download_url">"https://gitea.seku.su/fumiama/%1$s/releases/download/default/%2$s_%3$s.gz"</string>
<string name="complete">已完结</string> <string name="complete">已完结</string>