1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-07 08:10:23 +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>
<w>alphae</w>
<w>azurewebsites</w>
<w>comancry</w>
<w>comandy</w>
<w>downloaders</w>
<w>grps</w>
<w>hotmanga</w>
<w>imgs</w>
<w>kohima</w>
<w>libcomancry</w>
<w>libcomandy</w>
<w>lowpan</w>
<w>mangacopy</w>

3
app/.gitignore vendored
View File

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

View File

@@ -11,8 +11,8 @@ android {
applicationId 'top.fumiama.copymanga'
minSdkVersion 23
targetSdkVersion 34
versionCode 66
versionName '2.3.8'
versionCode 67
versionName '2.4.0'
resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -31,6 +31,12 @@ android {
enableV3Signing true
enableV4Signing true
}
debug {
enableV1Signing true
enableV2Signing true
enableV3Signing true
enableV4Signing true
}
}
buildTypes {
@@ -45,12 +51,14 @@ android {
minifyEnabled true
shrinkResources true
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 {
sourceCompatibility JavaVersion.VERSION_11
@@ -70,6 +78,21 @@ android {
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 {
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.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.core.view.WindowCompat
import androidx.drawerlayout.widget.DrawerLayout
@@ -51,16 +50,16 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.manga.Shelf
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.copymanga.api.manga.Shelf
import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.copymanga.ui.book.BookFragment.Companion.bookHandler
import top.fumiama.copymanga.ui.book.BookHandler
import top.fumiama.copymanga.ui.cardflow.rank.RankFragment
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
import top.fumiama.copymanga.ui.download.DownloadFragment
import top.fumiama.copymanga.ui.download.NewDownloadFragment
import top.fumiama.copymanga.update.Update
import top.fumiama.copymanga.user.Member
import top.fumiama.copymanga.api.update.Update
import top.fumiama.copymanga.api.user.Member
import top.fumiama.dmzj.copymanga.BuildConfig
import top.fumiama.dmzj.copymanga.R
import java.io.File
@@ -236,7 +235,7 @@ class MainActivity : AppCompatActivity() {
}
true
}
navtinfo.text = getPreferences(MODE_PRIVATE).getString("navTextInfo", getString(R.string.navTextInfo))
navtinfo.text = Config.navTextInfo.value
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
@@ -421,10 +420,7 @@ class MainActivity : AppCompatActivity() {
MaterialDialog(this).show {
input(prefill = (it as TextView).text) { _, charSequence ->
it.text = charSequence
getPreferences(MODE_PRIVATE).edit {
putString("navTextInfo", charSequence.toString())
apply()
}
Config.navTextInfo.value = charSequence.toString()
}
positiveButton(android.R.string.ok)
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.UserPreferenceInt
import top.fumiama.copymanga.tools.file.UserPreferenceString
import top.fumiama.copymanga.tools.http.Proxy
import top.fumiama.copymanga.tools.http.Resolution
import top.fumiama.copymanga.net.Proxy
import top.fumiama.copymanga.net.Resolution
import top.fumiama.dmzj.copymanga.R
import java.io.File
@@ -54,12 +54,14 @@ object Config {
}
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 app_ver = PreferenceString("settings_cat_general_et_app_version", R.string.app_ver)
val token = UserPreferenceString("token", "", null)
val pc_ua get() = MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)?.format(app_ver.value)?:""
val referer get() = MainActivity.mainWeakReference?.get()?.getString(R.string.referer)?.format(app_ver.value)?:""
val comandy_version = UserPreferenceInt("comandy_version", 0)
val comancry_version = UserPreferenceInt("comancry_version", 0)
val user_id = UserPreferenceString("user_id")
val username = UserPreferenceString("username")
val nickname = UserPreferenceString("nickname")
@@ -76,6 +78,7 @@ object Config {
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)
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_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 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.VolumeStructure
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 java.io.File
class Book(val path: String, private val getString: (Int) -> String, private val exDir: File, private val loadCache: Boolean = false, private val mPassName: String? = null) {
private val mBookApiUrl = getString(R.string.bookInfoApiUrl).format(Config.myHostApiUrl.value, path).let {
Config.apiProxy?.wrap(it)?:it
}
private val mBookApiUrl = getString(R.string.bookInfoApiUrl).format(Config.myHostApiUrl.value, path)
private val mUserAgent = getString(R.string.pc_ua).format(Config.app_ver.value)
private var mBook: BookInfoStructure? = null
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) {
name?.let { loadInfo(it) } ?: run {
isDownload = true
DownloadTools.getHttpContent(mBookApiUrl, null, mUserAgent)
Config.apiProxy?.comancry(mBookApiUrl) { url ->
DownloadTools.getHttpContent(url, null, mUserAgent)
}
}
} else {
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 {
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 com.google.gson.Gson
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.Chapter2Return
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.tools.http.DownloadPool
import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.net.DownloadPool
import java.io.File
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.Intent

View File

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

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.manga
package top.fumiama.copymanga.api.manga
import android.util.Log
import com.google.gson.Gson
@@ -6,7 +6,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.ChapterStructure
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.dmzj.copymanga.R

View File

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

View File

@@ -1,6 +1,7 @@
package top.fumiama.copymanga.update
package top.fumiama.copymanga.api.update
import android.util.Log
import top.fumiama.copymanga.net.Client
class SimpleKanban(private val client: Client, private val pwd: String) { //must run in thread
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.content.Context
@@ -16,7 +16,8 @@ import kotlinx.android.synthetic.main.dialog_progress.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
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.R
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
//Client.kt
import android.util.Log
import top.fumiama.copymanga.api.update.ByteArrayQueue
import java.io.*
import java.lang.Thread.sleep
import java.net.Socket
@@ -75,12 +76,17 @@ class Client(private val ip: String, private val port: Int) {
try {
if (isConnect) {
Log.d("MyC", "开始接收服务端信息")
var prevP = 0
while(totalSize > buffer.size) {
val count = din?.read(receiveBuffer)?:0
if(count > 0) {
buffer += receiveBuffer.copyOfRange(0, 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 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.util.Base64
import android.util.Log
import com.bumptech.glide.Glide
import com.bumptech.glide.Priority
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.signature.ObjectKey
import com.google.gson.Gson
import top.fumiama.copymanga.MainActivity
import kotlinx.coroutines.runBlocking
import top.fumiama.copymanga.json.ComandyCapsule
import top.fumiama.copymanga.lib.Comandy
import java.nio.ByteBuffer
@GlideModule
@@ -44,7 +44,7 @@ class ComandyGlideModule: AppGlideModule() {
}
try {
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 {
if (it.code != 200) {
callback.onLoadFailed(IllegalArgumentException("HTTP${it.code} ${
@@ -85,7 +85,7 @@ class ComandyGlideModule: AppGlideModule() {
}
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"
}
}
@@ -103,7 +103,7 @@ class ComandyGlideModule: AppGlideModule() {
}
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 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.Log
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.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.URL
import java.util.concurrent.Callable
@@ -85,13 +90,39 @@ object DownloadTools {
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) {
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 ->
val para = Gson().toJson(capsule)
//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")
Gson().fromJson(result, ComandyCapsule::class.java)!!.let {
if (it.code != 200) throw IllegalArgumentException("HTTP${it.code} ${
@@ -104,7 +135,12 @@ object DownloadTools {
}
failTimes.incrementAndGet()
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()
Log.d("MyDT", "getHttpContent: ${ret.size} bytes")
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")
val task = prepare(u, readSize)
val task = prepare(u, readSize, p)
Thread(task).start()
return try {
task.get()
} catch (ex: Exception) {
ex.printStackTrace()
if (Comandy.useComandy) failTimes.incrementAndGet()
if (Comandy.instance.enabled) failTimes.incrementAndGet()
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")
FutureTask(if (!u.startsWith("https://copymanga.azurewebsites.net") && Comandy.useComandy) Callable{
FutureTask(if (!u.startsWith("https://copymanga.azurewebsites.net") && Comandy.instance.enabled) Callable{
try {
Comandy.instance?.request(Gson().toJson(
runBlocking { Comandy.instance.getInstance() }?.request(Gson().toJson(
getComandyNormalConnection(u, "GET", Config.pc_ua))
)?.let { result ->
Gson().fromJson(result, ComandyCapsule::class.java)?.let {
@@ -146,10 +183,15 @@ object DownloadTools {
try {
val connection = getNormalConnection(u, "GET", Config.pc_ua)
val ci = connection.inputStream
val sz = connection.getHeaderFieldInt("Content-Length", 0)
if(readSize > 0) {
ret = ByteArray(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()
connection.disconnect()
} 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? {
Log.d("MyDT", "$method Http: $url")
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 {
val capsule = getComandyApiConnection(url, method, refer, ua)
contentType?.let { capsule.headers["content-type"] = it }
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 {
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.Looper
@@ -14,7 +14,7 @@ import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.ReturnBase
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.copymanga.net.DownloadTools
import java.io.File
import java.security.MessageDigest
@@ -77,7 +77,13 @@ open class AutoDownloadHandler(
var cnt = 0
while (cnt++ <= 3) {
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
val fi = data.inputStream()
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.copymanga.net.DownloadTools
import kotlin.random.Random
class PausableDownloader(private val url: String, private val waitMilliseconds: Long = 0, private val isApi: Boolean = true, private val whenFinish: (suspend (result: ByteArray)->Unit)? = null) {
@@ -14,10 +14,13 @@ class PausableDownloader(private val url: String, private val waitMilliseconds:
var c = 0
while (!exit && c++ < 3) {
try {
val data = (DownloadTools.getHttpContent(
(if(isApi) Config.apiProxy?.wrap(url) else null)?:url,
Config.referer
))
val data = if (isApi) Config.apiProxy?.comancry(url) {
DownloadTools.getHttpContent(it, Config.referer)
} else DownloadTools.getHttpContent(url, Config.referer)
if (data == null) {
delay(3000)
continue
}
whenFinish?.let { it(data) }
return@withContext true
} 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.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.manga.Book
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.api.manga.Book
import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.view.template.NoBackRefreshFragment
import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference

View File

@@ -38,11 +38,11 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
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.tools.ui.GlideBlurTransformation
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.view.operation.GlideBlurTransformation
import top.fumiama.copymanga.view.operation.GlideHideLottieViewListener
import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference

View File

@@ -1,6 +1,6 @@
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
@ExperimentalStdlibApi

View File

@@ -1,6 +1,6 @@
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
@ExperimentalStdlibApi

View File

@@ -2,7 +2,7 @@ package top.fumiama.copymanga.ui.cardflow.finish
import android.os.Bundle
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 kotlinx.android.synthetic.main.line_finish.*

View File

@@ -4,7 +4,7 @@ import android.os.Bundle
import android.widget.Toast
import androidx.navigation.fragment.findNavController
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.dmzj.copymanga.R

View File

@@ -1,6 +1,6 @@
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.dmzj.copymanga.R

View File

@@ -4,9 +4,9 @@ import android.os.Bundle
import com.google.android.material.tabs.TabLayout
import kotlinx.android.synthetic.main.fragment_rank.*
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.tools.ui.UITools
import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference

View File

@@ -1,6 +1,6 @@
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.dmzj.copymanga.R

View File

@@ -2,7 +2,7 @@ package top.fumiama.copymanga.ui.cardflow.search
import android.os.Bundle
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.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.line_shelf.*
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.dmzj.copymanga.R

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
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 java.io.File
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.ComicStructureOld
import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.manga.Downloader
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.copymanga.api.manga.Downloader
import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.exit
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.json
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
import top.fumiama.copymanga.views.ChapterToggleButton
import top.fumiama.copymanga.views.LazyScrollView
import top.fumiama.copymanga.view.ChapterToggleButton
import top.fumiama.copymanga.view.LazyScrollView
import top.fumiama.dmzj.copymanga.R
import java.io.File
import java.lang.ref.WeakReference

View File

@@ -14,11 +14,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.manga.Downloader
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
import top.fumiama.copymanga.api.manga.Downloader
import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.view.template.NoBackRefreshFragment
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 java.io.File
import java.lang.ref.WeakReference

View File

@@ -13,13 +13,13 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.api.Config.manga_dl_show_0m_manga
import top.fumiama.copymanga.manga.Downloader
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.template.general.MangaPagesFragmentTemplate
import top.fumiama.copymanga.template.ui.CardList
import top.fumiama.copymanga.api.manga.Downloader
import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.copymanga.view.template.MangaPagesFragmentTemplate
import top.fumiama.copymanga.view.template.CardList
import top.fumiama.copymanga.tools.file.FileUtils
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.dmzj.copymanga.R
import java.io.File
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.Companion.ime
import top.fumiama.copymanga.json.BookListStructure
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.view.template.NoBackRefreshFragment
import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.api.Config
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.view.operation.GlideHideLottieViewListener
import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference

View File

@@ -30,11 +30,11 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.ComicStructure
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.tools.ui.GlideHideLottieViewListener
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.copymanga.view.operation.GlideHideLottieViewListener
import top.fumiama.copymanga.view.interaction.Navigate
import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicInteger

View File

@@ -11,8 +11,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.copymanga.views.AutoHideEditTextPreferenceDialogFragmentCompat
import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.copymanga.view.AutoHideEditTextPreferenceDialogFragmentCompat
import top.fumiama.dmzj.copymanga.R
class SettingsFragment: PreferenceFragmentCompat() {

View File

@@ -1,7 +1,7 @@
package top.fumiama.copymanga.ui.vm
import android.widget.Toast
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.api.manga.Reader
import top.fumiama.dmzj.copymanga.R
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.ChapterWithContent
import top.fumiama.copymanga.json.ComicStructure
import top.fumiama.copymanga.template.http.AutoDownloadHandler
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.views.ScaleImageView
import top.fumiama.copymanga.net.template.AutoDownloadHandler
import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.view.ScaleImageView
import top.fumiama.dmzj.copymanga.R
import java.io.File
import java.lang.ref.WeakReference

View File

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

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.views
package top.fumiama.copymanga.view
import android.content.Context
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 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.content.Context

View File

@@ -1,9 +1,8 @@
package top.fumiama.copymanga.views
package top.fumiama.copymanga.view
import android.content.Context
import android.util.AttributeSet
import androidx.cardview.widget.CardView
import java.io.File
class MangaCardView :CardView {
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.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.AnimatorUpdateListener

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.views
package top.fumiama.copymanga.view
import android.content.Context
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 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.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.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.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.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.copymanga.view.interaction.UITools
open class ActivityTemplate: AppCompatActivity() {
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.net.Uri
@@ -14,7 +14,7 @@ import kotlinx.android.synthetic.main.line_lazybooklines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
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 java.io.File
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.util.Log
@@ -15,9 +15,8 @@ import top.fumiama.copymanga.json.BookListStructure
import top.fumiama.copymanga.json.HistoryBookListStructure
import top.fumiama.copymanga.json.ShelfStructure
import top.fumiama.copymanga.json.TypeBookListStructure
import top.fumiama.copymanga.template.general.MangaPagesFragmentTemplate
import top.fumiama.copymanga.template.http.PausableDownloader
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.net.template.PausableDownloader
import top.fumiama.copymanga.view.interaction.Navigate
import java.lang.ref.WeakReference
@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.annotation.SuppressLint
@@ -19,8 +19,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.template.ui.CardList
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.copymanga.view.interaction.UITools
import top.fumiama.dmzj.copymanga.R
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.os.Bundle
@@ -11,7 +11,7 @@ import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.content_main.*
import top.fumiama.copymanga.MainActivity
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
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.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.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 kotlinx.android.synthetic.main.widget_titlebar.*

View File

@@ -1,5 +1,5 @@
<?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"
android:id="@+id/tbtn"
android:layout_width="64dp"

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<top.fumiama.copymanga.views.ScaleImageView
<top.fumiama.copymanga.view.ScaleImageView
android:id="@+id/onei"
android:layout_width="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="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_download_url">"https://gitea.seku.su/fumiama/comandy/releases/download/default/%1$s_libcomandy.so.gz"</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="complete">已完结</string>