mirror of
https://github.com/fumiama/copymanga.git
synced 2026-06-22 02:10:23 +08:00
v2.3.0
新增 1. 增强型数据访问选项 2. 漫画下载数量并发限制 修复 1. 同时下载过多漫画时失败 (fix #67) 2. 主页滑动横幅后刷新闪退 (fix #65) 优化 1. 网络不佳时自动打开增强型数据访问 2. 不再反复读取代理状态
This commit is contained in:
2
.idea/dictionaries/fumiama.xml
generated
2
.idea/dictionaries/fumiama.xml
generated
@@ -2,10 +2,12 @@
|
|||||||
<dictionary name="fumiama">
|
<dictionary name="fumiama">
|
||||||
<words>
|
<words>
|
||||||
<w>alphae</w>
|
<w>alphae</w>
|
||||||
|
<w>comandy</w>
|
||||||
<w>downloaders</w>
|
<w>downloaders</w>
|
||||||
<w>grps</w>
|
<w>grps</w>
|
||||||
<w>imgs</w>
|
<w>imgs</w>
|
||||||
<w>kohima</w>
|
<w>kohima</w>
|
||||||
|
<w>libcomandy</w>
|
||||||
<w>lowpan</w>
|
<w>lowpan</w>
|
||||||
<w>mangacopy</w>
|
<w>mangacopy</w>
|
||||||
<w>mangafuna</w>
|
<w>mangafuna</w>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
@@ -8,8 +9,8 @@ android {
|
|||||||
applicationId 'top.fumiama.copymanga'
|
applicationId 'top.fumiama.copymanga'
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode 57
|
versionCode 58
|
||||||
versionName '2.2.9'
|
versionName '2.3.0'
|
||||||
resourceConfigurations += ['zh', 'zh-rCN']
|
resourceConfigurations += ['zh', 'zh-rCN']
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
@@ -46,6 +47,11 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
jniLibs.srcDirs = ['libs']
|
||||||
|
}
|
||||||
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
@@ -78,7 +84,7 @@ dependencies {
|
|||||||
implementation 'com.github.yalantis:ucrop:2.2.6'
|
implementation 'com.github.yalantis:ucrop:2.2.6'
|
||||||
implementation 'com.to.aboomy:pager2banner:1.0.1'
|
implementation 'com.to.aboomy:pager2banner:1.0.1'
|
||||||
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
kapt 'com.github.bumptech.glide:compiler:4.16.0'
|
||||||
implementation 'com.google.code.gson:gson:2.10.1'
|
implementation 'com.google.code.gson:gson:2.10.1'
|
||||||
implementation 'com.github.vovaksenov99:OverscrollableScrollView:1.0'
|
implementation 'com.github.vovaksenov99:OverscrollableScrollView:1.0'
|
||||||
implementation 'com.liaoinstan.springview:library:1.7.0'
|
implementation 'com.liaoinstan.springview:library:1.7.0'
|
||||||
@@ -86,4 +92,5 @@ dependencies {
|
|||||||
implementation 'com.lapism:search:2.4.1@aar'
|
implementation 'com.lapism:search:2.4.1@aar'
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
|
||||||
implementation 'com.airbnb.android:lottie:6.4.0'
|
implementation 'com.airbnb.android:lottie:6.4.0'
|
||||||
|
implementation 'net.java.dev.jna:jna:5.14.0'
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
app/libs/arm64-v8a/libjnidispatch.so
Normal file
BIN
app/libs/arm64-v8a/libjnidispatch.so
Normal file
Binary file not shown.
BIN
app/libs/armeabi-v7a/libjnidispatch.so
Normal file
BIN
app/libs/armeabi-v7a/libjnidispatch.so
Normal file
Binary file not shown.
BIN
app/libs/x86/libjnidispatch.so
Normal file
BIN
app/libs/x86/libjnidispatch.so
Normal file
Binary file not shown.
BIN
app/libs/x86_64/libjnidispatch.so
Normal file
BIN
app/libs/x86_64/libjnidispatch.so
Normal file
Binary file not shown.
@@ -0,0 +1,11 @@
|
|||||||
|
package top.fumiama.copymanga.json;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class ComandyCapsule {
|
||||||
|
public int code;
|
||||||
|
public String method;
|
||||||
|
public String url;
|
||||||
|
public HashMap<String, Object> headers;
|
||||||
|
public String data;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package top.fumiama.copymanga.json;
|
||||||
|
|
||||||
|
public class ComandyVersion {
|
||||||
|
public String body;
|
||||||
|
}
|
||||||
@@ -10,9 +10,7 @@ import androidx.core.animation.doOnEnd
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.android.synthetic.main.content_main.*
|
import kotlinx.android.synthetic.main.content_main.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import top.fumiama.copymanga.MainActivity
|
import top.fumiama.copymanga.MainActivity
|
||||||
import top.fumiama.copymanga.tools.ui.UITools
|
import top.fumiama.copymanga.tools.ui.UITools
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
@@ -48,8 +46,8 @@ open class NoBackRefreshFragment(private val layoutToLoad: Int): Fragment() {
|
|||||||
isFirstInflate = true
|
isFirstInflate = true
|
||||||
Log.d("MyNBRF", "destroyed")
|
Log.d("MyNBRF", "destroyed")
|
||||||
}
|
}
|
||||||
suspend fun showKanban() = withContext(Dispatchers.Main) {
|
fun showKanban() {
|
||||||
if (disableAnimation) return@withContext
|
if (disableAnimation) return
|
||||||
(activity?:(MainActivity.mainWeakReference?.get()))?.apply {cmaini?.post {
|
(activity?:(MainActivity.mainWeakReference?.get()))?.apply {cmaini?.post {
|
||||||
if(cmaini?.visibility == View.GONE) {
|
if(cmaini?.visibility == View.GONE) {
|
||||||
Log.d("MyNBRF", "show: start, set h: ${window?.decorView?.height}")
|
Log.d("MyNBRF", "show: start, set h: ${window?.decorView?.height}")
|
||||||
@@ -62,8 +60,8 @@ open class NoBackRefreshFragment(private val layoutToLoad: Int): Fragment() {
|
|||||||
Log.d("MyNBRF", "show: end")
|
Log.d("MyNBRF", "show: end")
|
||||||
}
|
}
|
||||||
private var isHideRunning = AtomicBoolean()
|
private var isHideRunning = AtomicBoolean()
|
||||||
suspend fun hideKanban() = withContext(Dispatchers.Main) {
|
fun hideKanban() {
|
||||||
if (disableAnimation) return@withContext
|
if (disableAnimation) return
|
||||||
(activity?:(MainActivity.mainWeakReference?.get()))?.apply { cmaini?.post {
|
(activity?:(MainActivity.mainWeakReference?.get()))?.apply { cmaini?.post {
|
||||||
if(!isHideRunning.get() && cmaini?.visibility == View.VISIBLE) {
|
if(!isHideRunning.get() && cmaini?.visibility == View.VISIBLE) {
|
||||||
isHideRunning.set(true)
|
isHideRunning.set(true)
|
||||||
|
|||||||
117
app/src/main/java/top/fumiama/copymanga/tools/http/Comandy.kt
Normal file
117
app/src/main/java/top/fumiama/copymanga/tools/http/Comandy.kt
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package top.fumiama.copymanga.tools.http
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.sun.jna.Library
|
||||||
|
import com.sun.jna.Native
|
||||||
|
import top.fumiama.copymanga.MainActivity
|
||||||
|
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!!
|
||||||
|
MainActivity.mainWeakReference?.get()?.let {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
||||||
|
val b = getBoolean("settings_cat_net_sw_use_comandy", false)
|
||||||
|
Log.d("MyComandy", "use comandy: $b")
|
||||||
|
mUseComandy = b
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mUseComandy = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
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 = ma.getPreferences(AppCompatActivity.MODE_PRIVATE).getInt("comandy_version", 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) ma.getPreferences(AppCompatActivity.MODE_PRIVATE).edit {
|
||||||
|
putInt("comandy_version", remoteVersion)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package top.fumiama.copymanga.tools.http
|
||||||
|
|
||||||
|
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
|
||||||
|
import com.bumptech.glide.annotation.GlideModule
|
||||||
|
import com.bumptech.glide.load.DataSource
|
||||||
|
import com.bumptech.glide.load.Options
|
||||||
|
import com.bumptech.glide.load.data.DataFetcher
|
||||||
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
|
import com.bumptech.glide.load.model.ModelLoader
|
||||||
|
import com.bumptech.glide.load.model.ModelLoaderFactory
|
||||||
|
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 top.fumiama.copymanga.json.ComandyCapsule
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
@GlideModule
|
||||||
|
class ComandyGlideModule: AppGlideModule() {
|
||||||
|
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
||||||
|
super.registerComponents(context, glide, registry)
|
||||||
|
registry.prepend(GlideUrl::class.java, ByteBuffer::class.java, ComandyGlideUrlFactory())
|
||||||
|
registry.prepend(String::class.java, ByteBuffer::class.java, ComandyStringFactory())
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ComandyDataFetcher(private val model: GlideUrl): DataFetcher<ByteBuffer> {
|
||||||
|
constructor(model: String): this(GlideUrl(model))
|
||||||
|
override fun loadData(
|
||||||
|
priority: Priority,
|
||||||
|
callback: DataFetcher.DataCallback<in ByteBuffer>
|
||||||
|
) {
|
||||||
|
val capsule = ComandyCapsule()
|
||||||
|
capsule.url = model.toStringUrl()
|
||||||
|
capsule.method = "GET"
|
||||||
|
if (model.headers.isNotEmpty()) {
|
||||||
|
capsule.headers = hashMapOf()
|
||||||
|
model.headers.forEach { (k, v) -> capsule.headers[k] = v }
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val para = Gson().toJson(capsule)
|
||||||
|
MainActivity.mainWeakReference?.get()?.runOnUiThread {
|
||||||
|
Log.d("MyCGM", "request: $para")
|
||||||
|
}
|
||||||
|
Comandy.instance!!.request(para).let { result ->
|
||||||
|
Gson().fromJson(result, ComandyCapsule::class.java)!!.let {
|
||||||
|
if (it.code != 200) {
|
||||||
|
callback.onLoadFailed(IllegalArgumentException("HTTP${it.code} ${
|
||||||
|
it.data?.let { d -> Base64.decode(d, Base64.DEFAULT).decodeToString() }
|
||||||
|
}"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback.onDataReady(ByteBuffer.wrap(Base64.decode(it.data, Base64.DEFAULT)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
callback.onLoadFailed(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cleanup() { }
|
||||||
|
|
||||||
|
override fun cancel() { }
|
||||||
|
|
||||||
|
override fun getDataClass(): Class<ByteBuffer> {
|
||||||
|
return ByteBuffer::class.java
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDataSource(): DataSource {
|
||||||
|
return DataSource.REMOTE
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ComandyGlideUrlModelLoader: ModelLoader<GlideUrl, ByteBuffer> {
|
||||||
|
override fun buildLoadData(
|
||||||
|
model: GlideUrl,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
options: Options
|
||||||
|
): ModelLoader.LoadData<ByteBuffer> {
|
||||||
|
return ModelLoader.LoadData(ObjectKey(model), ComandyDataFetcher(model))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handles(model: GlideUrl): Boolean {
|
||||||
|
return Comandy.useComandy && Comandy.instance != null && model.toURL().let { it.protocol == "https" }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ComandyStringModelLoader: ModelLoader<String, ByteBuffer> {
|
||||||
|
override fun buildLoadData(
|
||||||
|
model: String,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
options: Options
|
||||||
|
): ModelLoader.LoadData<ByteBuffer> {
|
||||||
|
return ModelLoader.LoadData(ObjectKey(model), ComandyDataFetcher(model))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handles(model: String): Boolean {
|
||||||
|
return Comandy.useComandy && Comandy.instance != null && model.startsWith("https://")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ComandyGlideUrlFactory: ModelLoaderFactory<GlideUrl, ByteBuffer> {
|
||||||
|
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<GlideUrl, ByteBuffer> {
|
||||||
|
return ComandyGlideUrlModelLoader()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun teardown() { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ComandyStringFactory: ModelLoaderFactory<String, ByteBuffer> {
|
||||||
|
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<String, ByteBuffer> {
|
||||||
|
return ComandyStringModelLoader()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun teardown() { }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,21 @@
|
|||||||
package top.fumiama.copymanga.tools.http
|
package top.fumiama.copymanga.tools.http
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.google.gson.Gson
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import top.fumiama.copymanga.MainActivity
|
import top.fumiama.copymanga.MainActivity
|
||||||
|
import top.fumiama.copymanga.json.ComandyCapsule
|
||||||
import top.fumiama.dmzj.copymanga.R
|
import top.fumiama.dmzj.copymanga.R
|
||||||
|
import java.lang.IllegalArgumentException
|
||||||
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
|
||||||
import java.util.concurrent.FutureTask
|
import java.util.concurrent.FutureTask
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
object DownloadTools {
|
object DownloadTools {
|
||||||
val app_ver = MainActivity.mainWeakReference?.get()?.let { main ->
|
val app_ver = MainActivity.mainWeakReference?.get()?.let { main ->
|
||||||
@@ -20,6 +25,7 @@ object DownloadTools {
|
|||||||
}!!
|
}!!
|
||||||
val pc_ua = MainActivity.mainWeakReference?.get()!!.getString(R.string.pc_ua).format(app_ver)
|
val pc_ua = MainActivity.mainWeakReference?.get()!!.getString(R.string.pc_ua).format(app_ver)
|
||||||
val referer = MainActivity.mainWeakReference?.get()!!.getString(R.string.referer).format(app_ver)
|
val referer = MainActivity.mainWeakReference?.get()!!.getString(R.string.referer).format(app_ver)
|
||||||
|
val failTimes = AtomicInteger(0)
|
||||||
fun getApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null, timeout: Int = 20000) =
|
fun getApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null, timeout: Int = 20000) =
|
||||||
url.let {
|
url.let {
|
||||||
val connection = URL(url).openConnection() as HttpURLConnection
|
val connection = URL(url).openConnection() as HttpURLConnection
|
||||||
@@ -45,10 +51,37 @@ object DownloadTools {
|
|||||||
}
|
}
|
||||||
setRequestProperty("platform", "3")
|
setRequestProperty("platform", "3")
|
||||||
}
|
}
|
||||||
Log.d("Mydl", "getConnection: $url\n${connection.requestProperties.map { "${it.key}: ${it.value}" }.joinToString("\n")}")
|
Log.d("MyDT", "getConnection: $url\n${connection.requestProperties.map { "${it.key}: ${it.value}" }.joinToString("\n")}")
|
||||||
connection
|
connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getComandyApiConnection(url: String, method: String = "GET", refer: String? = null, ua: String? = null) =
|
||||||
|
run {
|
||||||
|
val capsule = ComandyCapsule()
|
||||||
|
capsule.url = url
|
||||||
|
capsule.method = method
|
||||||
|
capsule.headers = hashMapOf()
|
||||||
|
capsule.headers["host"] = url.substringAfter("://").substringBefore("/")
|
||||||
|
ua?.let { capsule.headers["user-agent"] = it }
|
||||||
|
refer?.let { capsule.headers["referer"] = it }
|
||||||
|
capsule.headers["source"] = "copyApp"
|
||||||
|
capsule.headers["webp"] = "1"
|
||||||
|
MainActivity.mainWeakReference?.get()?.let {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
||||||
|
capsule.headers["region"] = if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "1" else "0"
|
||||||
|
}
|
||||||
|
it.getPreferences(Context.MODE_PRIVATE).apply {
|
||||||
|
capsule.headers["version"] = app_ver
|
||||||
|
getString("token", "")?.let { tk ->
|
||||||
|
capsule.headers["authorization"] = "Token $tk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
capsule.headers["platform"] = "3"
|
||||||
|
Log.d("MyDT", "getComandyConnection: $url\n${capsule.headers.map { "${it.key}: ${it.value}" }.joinToString("\n")}")
|
||||||
|
capsule
|
||||||
|
}
|
||||||
|
|
||||||
private fun getNormalConnection(url: String, method: String = "GET", ua: String? = null, timeout: Int = 20000) =
|
private fun getNormalConnection(url: String, method: String = "GET", ua: String? = null, timeout: Int = 20000) =
|
||||||
url.let {
|
url.let {
|
||||||
val connection = URL(url).openConnection() as HttpURLConnection
|
val connection = URL(url).openConnection() as HttpURLConnection
|
||||||
@@ -61,20 +94,75 @@ object DownloadTools {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getComandyNormalConnection(url: String, method: String = "GET", ua: String? = null) =
|
||||||
|
run {
|
||||||
|
val capsule = ComandyCapsule()
|
||||||
|
capsule.url = url
|
||||||
|
capsule.method = method
|
||||||
|
capsule.headers = hashMapOf()
|
||||||
|
capsule.headers["host"] = url.substringAfter("://").substringBefore("/")
|
||||||
|
ua?.let { capsule.headers["user-agent"] = it }
|
||||||
|
capsule
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = null): ByteArray =
|
suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = null): ByteArray =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
if (Comandy.useComandy) {
|
||||||
|
getComandyApiConnection(u, "GET", refer, ua).let { capsule ->
|
||||||
|
val para = Gson().toJson(capsule)
|
||||||
|
//Log.d("MyDT", "comandy request: $para")
|
||||||
|
Comandy.instance?.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} ${
|
||||||
|
it.data?.let { d -> Base64.decode(d, Base64.DEFAULT).decodeToString() }
|
||||||
|
}")
|
||||||
|
Base64.decode(it.data, Base64.DEFAULT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.let { if(it?.isNotEmpty() == true ) return@withContext it }
|
||||||
|
}
|
||||||
|
failTimes.incrementAndGet()
|
||||||
getApiConnection(u, "GET", refer, ua).let {
|
getApiConnection(u, "GET", refer, ua).let {
|
||||||
val ret = it.inputStream.readBytes()
|
val ret = it.inputStream.readBytes()
|
||||||
it.disconnect()
|
it.disconnect()
|
||||||
Log.d("Mydl", "getHttpContent: ${ret.size} bytes")
|
Log.d("MyDT", "getHttpContent: ${ret.size} bytes")
|
||||||
|
failTimes.decrementAndGet()
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getHttpContent(u: String, readSize: Int): ByteArray? {
|
fun getHttpContent(u: String, readSize: Int): ByteArray? {
|
||||||
Log.d("Mydl", "getHttp: $u")
|
Log.d("MyDT", "getHttp: $u")
|
||||||
var ret: ByteArray? = null
|
val task = prepare(u, readSize)
|
||||||
val task = FutureTask(Callable {
|
Thread(task).start()
|
||||||
|
return try {
|
||||||
|
task.get()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
if (Comandy.useComandy) failTimes.incrementAndGet()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun prepare(u: String, readSize: Int = -1) = run {
|
||||||
|
Log.d("MyDT", "prepareHttp: $u")
|
||||||
|
FutureTask(if (Comandy.useComandy) Callable{
|
||||||
|
try {
|
||||||
|
Comandy.instance?.request(Gson().toJson(
|
||||||
|
getComandyNormalConnection(u, "GET"))
|
||||||
|
)?.let { result ->
|
||||||
|
Gson().fromJson(result, ComandyCapsule::class.java)?.let {
|
||||||
|
if (it.code != 200) null
|
||||||
|
else it.data?.let { d -> Base64.decode(d, Base64.DEFAULT) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} else Callable {
|
||||||
|
var ret: ByteArray? = null
|
||||||
try {
|
try {
|
||||||
val connection = getNormalConnection(u, "GET")
|
val connection = getNormalConnection(u, "GET")
|
||||||
val ci = connection.inputStream
|
val ci = connection.inputStream
|
||||||
@@ -87,57 +175,10 @@ object DownloadTools {
|
|||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
}
|
}
|
||||||
return@Callable ret
|
ret
|
||||||
})
|
})
|
||||||
Thread(task).start()
|
|
||||||
return try {
|
|
||||||
task.get()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
ex.printStackTrace()
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*fun touch(url: String?): FutureTask<ByteArray?>? =
|
|
||||||
url?.let {
|
|
||||||
Log.d("Mydl", "touchHttp: $it")
|
|
||||||
var ret: ByteArray? = null
|
|
||||||
val task = FutureTask(Callable {
|
|
||||||
try {
|
|
||||||
val connection = getNormalConnection(it, "GET")
|
|
||||||
|
|
||||||
val ci = connection?.inputStream
|
|
||||||
ret = ci?.readBytes()
|
|
||||||
ci?.close()
|
|
||||||
connection?.disconnect()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
ex.printStackTrace()
|
|
||||||
}
|
|
||||||
return@Callable ret
|
|
||||||
})
|
|
||||||
Thread(task).start()
|
|
||||||
task
|
|
||||||
}*/
|
|
||||||
|
|
||||||
fun prepare(url: String?): FutureTask<ByteArray?>? =
|
|
||||||
url?.let {
|
|
||||||
Log.d("Mydl", "prepareHttp: $it")
|
|
||||||
val task = FutureTask(Callable {
|
|
||||||
var ret: ByteArray? = null
|
|
||||||
try {
|
|
||||||
val connection = getNormalConnection(it, "GET")
|
|
||||||
connection.inputStream?.use { ci ->
|
|
||||||
ret = ci.readBytes()
|
|
||||||
}
|
|
||||||
connection.disconnect()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
ex.printStackTrace()
|
|
||||||
}
|
|
||||||
return@Callable ret
|
|
||||||
})
|
|
||||||
task
|
|
||||||
}
|
|
||||||
|
|
||||||
/*private fun replaceChineseCharacters(string: String?) : String? {
|
/*private fun replaceChineseCharacters(string: String?) : String? {
|
||||||
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.M) return string
|
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.M) return string
|
||||||
else return string?.replace(Regex("(?<=/)[\\w\\s\\d\\u4e00-\\u9fa5.-]+(?=/?)")) { match ->
|
else return string?.replace(Regex("(?<=/)[\\w\\s\\d\\u4e00-\\u9fa5.-]+(?=/?)")) { match ->
|
||||||
@@ -146,14 +187,31 @@ object DownloadTools {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
fun requestWithBody(url: String, method: String, body: ByteArray, refer: String? = null, ua: String? = null): ByteArray? {
|
fun requestWithBody(url: String, method: String, body: ByteArray, refer: String? = null, ua: String? = null): ByteArray? {
|
||||||
Log.d("Mydl", "$method Http: $url")
|
Log.d("MyDT", "$method Http: $url")
|
||||||
var ret: ByteArray? = null
|
var ret: ByteArray? = null
|
||||||
val task = FutureTask(Callable {
|
val task = FutureTask(if(Comandy.useComandy) Callable{
|
||||||
try {
|
try {
|
||||||
getApiConnection(url, method, refer, ua)?.apply {
|
val capsule = getComandyApiConnection(url, method, refer, ua)
|
||||||
|
capsule.data = body.decodeToString()
|
||||||
|
Comandy.instance?.request(Gson().toJson(capsule))?.let { result ->
|
||||||
|
Gson().fromJson(result, ComandyCapsule::class.java)?.let {
|
||||||
|
if (it.code != 200) null
|
||||||
|
else it.data?.let { d -> Base64.decode(d, Base64.DEFAULT) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else Callable {
|
||||||
|
failTimes.incrementAndGet()
|
||||||
|
try {
|
||||||
|
getApiConnection(url, method, refer, ua).apply {
|
||||||
outputStream.write(body)
|
outputStream.write(body)
|
||||||
ret = inputStream.readBytes()
|
ret = inputStream.readBytes()
|
||||||
disconnect()
|
disconnect()
|
||||||
|
failTimes.decrementAndGet()
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
|
|||||||
@@ -30,26 +30,34 @@ class Proxy(id: Int, private val apiRegex: Regex, keyID: Int? = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private var mUseImageProxy: Boolean? = null
|
||||||
val useImageProxy: Boolean
|
val useImageProxy: Boolean
|
||||||
get() {
|
get() {
|
||||||
|
if (mUseImageProxy != null) return mUseImageProxy!!
|
||||||
MainActivity.mainWeakReference?.get()?.let {
|
MainActivity.mainWeakReference?.get()?.let {
|
||||||
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
||||||
val b = getBoolean("settings_cat_net_sw_use_img_proxy", false)
|
val b = getBoolean("settings_cat_net_sw_use_img_proxy", false)
|
||||||
Log.d("MyProxy", "use image proxy: $b")
|
Log.d("MyProxy", "use image proxy: $b")
|
||||||
|
mUseImageProxy = b
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mUseImageProxy = false
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
private var mUseApiProxy: Boolean? = null
|
||||||
val useApiProxy: Boolean
|
val useApiProxy: Boolean
|
||||||
get() {
|
get() {
|
||||||
|
if (mUseApiProxy != null) return mUseApiProxy!!
|
||||||
MainActivity.mainWeakReference?.get()?.let {
|
MainActivity.mainWeakReference?.get()?.let {
|
||||||
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
PreferenceManager.getDefaultSharedPreferences(it).apply {
|
||||||
val b = getBoolean("settings_cat_net_sw_use_api_proxy", false)
|
val b = getBoolean("settings_cat_net_sw_use_api_proxy", false)
|
||||||
Log.d("MyProxy", "use api proxy: $b")
|
Log.d("MyProxy", "use api proxy: $b")
|
||||||
|
mUseApiProxy = b
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mUseApiProxy = false
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import android.view.ViewGroup
|
|||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import kotlinx.android.synthetic.main.button_tbutton.*
|
import kotlinx.android.synthetic.main.button_tbutton.*
|
||||||
import kotlinx.android.synthetic.main.button_tbutton.view.*
|
import kotlinx.android.synthetic.main.button_tbutton.view.*
|
||||||
@@ -21,6 +22,7 @@ import kotlinx.android.synthetic.main.line_chapter.view.*
|
|||||||
import kotlinx.android.synthetic.main.line_horizonal_empty.view.*
|
import kotlinx.android.synthetic.main.line_horizonal_empty.view.*
|
||||||
import kotlinx.android.synthetic.main.widget_downloadbar.*
|
import kotlinx.android.synthetic.main.widget_downloadbar.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
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.ChapterStructure
|
import top.fumiama.copymanga.json.ChapterStructure
|
||||||
@@ -38,6 +40,7 @@ import top.fumiama.copymanga.views.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
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragment>, private val vols: Array<VolumeStructure>, private val comicName: String, private val groupNames: Array<String>?):Handler(looper) {
|
class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragment>, private val vols: Array<VolumeStructure>, private val comicName: String, private val groupNames: Array<String>?):Handler(looper) {
|
||||||
constructor(looper: Looper, th: WeakReference<ComicDlFragment>, comicName: String) : this(looper, th, arrayOf(), comicName, null) {
|
constructor(looper: Looper, th: WeakReference<ComicDlFragment>, comicName: String) : this(looper, th, arrayOf(), comicName, null) {
|
||||||
@@ -63,6 +66,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
|
|||||||
var downloading = false
|
var downloading = false
|
||||||
private var urlArray = arrayOf<String>()
|
private var urlArray = arrayOf<String>()
|
||||||
private var uuidArray = arrayOf<String>()
|
private var uuidArray = arrayOf<String>()
|
||||||
|
private val maxBatch = that?.activity?.let { PreferenceManager.getDefaultSharedPreferences(it) }?.getInt("settings_cat_md_sb_max_batch", 16)?:16
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun handleMessage(msg: Message) {
|
override fun handleMessage(msg: Message) {
|
||||||
@@ -265,12 +269,15 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun downloadChapterPages() = withContext(Dispatchers.IO) {
|
private suspend fun downloadChapterPages() = withContext(Dispatchers.IO) {
|
||||||
|
val totalInDownload = AtomicInteger(0)
|
||||||
tbtnlist.forEach { i ->
|
tbtnlist.forEach { i ->
|
||||||
if(i.isChecked) {
|
if(i.isChecked) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
i.isEnabled = false
|
i.isEnabled = false
|
||||||
}
|
}
|
||||||
i.url?.let {
|
i.url?.let {
|
||||||
|
while (totalInDownload.get() >= maxBatch) delay(1000)
|
||||||
|
totalInDownload.incrementAndGet()
|
||||||
launch {
|
launch {
|
||||||
mangaDlTools.downloadChapterInVol(
|
mangaDlTools.downloadChapterInVol(
|
||||||
it,
|
it,
|
||||||
@@ -278,7 +285,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
|
|||||||
i.caption?:"null",
|
i.caption?:"null",
|
||||||
i.index
|
i.index
|
||||||
)
|
)
|
||||||
}
|
}.invokeOnCompletion { totalInDownload.decrementAndGet() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
|
|||||||
-1 -> {
|
-1 -> {
|
||||||
homeF?.apply {
|
homeF?.apply {
|
||||||
swiperefresh?.isRefreshing = msg.obj as Boolean
|
swiperefresh?.isRefreshing = msg.obj as Boolean
|
||||||
lifecycleScope.launch { if(msg.obj as Boolean) showKanban() else hideKanban() }
|
if(msg.obj as Boolean) showKanban() else hideKanban()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//0 -> setLayouts()
|
//0 -> setLayouts()
|
||||||
@@ -281,8 +281,8 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
|
|||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
homeF?.showKanban()
|
homeF?.showKanban()
|
||||||
fhib?.isAutoPlay = false
|
fhib?.isAutoPlay = false
|
||||||
fhib?.adapter?.notifyDataSetChanged()
|
|
||||||
index = null
|
index = null
|
||||||
|
fhib?.adapter?.notifyDataSetChanged()
|
||||||
fhib = null
|
fhib = null
|
||||||
indexLines = arrayOf()
|
indexLines = arrayOf()
|
||||||
this@HomeHandler.sendEmptyMessage(6) //removeAllViews
|
this@HomeHandler.sendEmptyMessage(6) //removeAllViews
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import android.util.Base64
|
|||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import top.fumiama.copymanga.json.ComandyCapsule
|
||||||
import top.fumiama.copymanga.json.LoginInfoStructure
|
import top.fumiama.copymanga.json.LoginInfoStructure
|
||||||
import top.fumiama.copymanga.tools.api.CMApi
|
import top.fumiama.copymanga.tools.api.CMApi
|
||||||
|
import top.fumiama.copymanga.tools.http.Comandy
|
||||||
import top.fumiama.copymanga.tools.http.DownloadTools
|
import top.fumiama.copymanga.tools.http.DownloadTools
|
||||||
import top.fumiama.dmzj.copymanga.R
|
import top.fumiama.dmzj.copymanga.R
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
@@ -16,29 +18,37 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
|
|||||||
val hasLogin: Boolean get() = pref.getString("token", "")?.isNotEmpty()?:false
|
val hasLogin: Boolean get() = pref.getString("token", "")?.isNotEmpty()?:false
|
||||||
suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure = withContext(Dispatchers.IO) {
|
suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure = withContext(Dispatchers.IO) {
|
||||||
var err = ""
|
var err = ""
|
||||||
getLoginConnection(username, pwd, salt).apply {
|
if (Comandy.useComandy) getComandyLoginConnection(username, pwd, salt).let { capsule ->
|
||||||
|
try {
|
||||||
|
Comandy.instance?.request(Gson().toJson(capsule))?.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() }
|
||||||
|
return@withContext l
|
||||||
|
}
|
||||||
|
Base64.decode(it.data, Base64.DEFAULT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
err = e.message.toString()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}?.let {
|
||||||
|
try {
|
||||||
|
saveInfo(it)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
err = e.message.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else getLoginConnection(username, pwd, salt).apply {
|
||||||
inputStream.use {
|
inputStream.use {
|
||||||
it?.readBytes()?.let { data ->
|
it?.readBytes()?.let { data ->
|
||||||
data.inputStream().use { dataIn ->
|
try {
|
||||||
try {
|
saveInfo(data)
|
||||||
Gson().fromJson<LoginInfoStructure>(
|
} catch (e: Exception) {
|
||||||
dataIn.reader(), LoginInfoStructure::class.java
|
err = e.message.toString()
|
||||||
)?.let { info ->
|
|
||||||
if(info.code == 200) {
|
|
||||||
pref.edit()?.apply {
|
|
||||||
putString("token", info.results?.token)
|
|
||||||
putString("user_id", info.results?.user_id)
|
|
||||||
putString("username", info.results?.username)
|
|
||||||
putString("nickname", info.results?.nickname)
|
|
||||||
apply()
|
|
||||||
return@withContext info()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@withContext info
|
|
||||||
}?: run { err = getString(R.string.login_parse_json_error) }
|
|
||||||
} catch (e: Exception) {
|
|
||||||
err = data.decodeToString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}?: run { err = getString(R.string.login_get_conn_failed) }
|
}?: run { err = getString(R.string.login_get_conn_failed) }
|
||||||
}
|
}
|
||||||
@@ -101,6 +111,26 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun saveInfo(data: ByteArray) = data.inputStream().use { dataIn ->
|
||||||
|
try {
|
||||||
|
Gson().fromJson(dataIn.reader(), LoginInfoStructure::class.java)?.let { l ->
|
||||||
|
if(l.code == 200) {
|
||||||
|
pref.edit()?.apply {
|
||||||
|
putString("token", l.results?.token)
|
||||||
|
putString("user_id", l.results?.user_id)
|
||||||
|
putString("username", l.results?.username)
|
||||||
|
putString("nickname", l.results?.nickname)
|
||||||
|
apply()
|
||||||
|
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) =
|
private fun getLoginConnection(username: String, pwd: String, salt: Int) =
|
||||||
getString(R.string.loginApiUrl).format(CMApi.myHostApiUrl).let {
|
getString(R.string.loginApiUrl).format(CMApi.myHostApiUrl).let {
|
||||||
CMApi.apiProxy?.wrap(it)?:it
|
CMApi.apiProxy?.wrap(it)?:it
|
||||||
@@ -117,4 +147,20 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getComandyLoginConnection(username: String, pwd: String, salt: Int) =
|
||||||
|
getString(R.string.loginApiUrl).format(CMApi.myHostApiUrl).let {
|
||||||
|
CMApi.apiProxy?.wrap(it)?:it
|
||||||
|
}.let {
|
||||||
|
DownloadTools.getComandyApiConnection(it, "POST").apply {
|
||||||
|
pref.apply {
|
||||||
|
headers["content-type"] = "application/x-www-form-urlencoded;charset=utf-8"
|
||||||
|
headers["platform"] = "3"
|
||||||
|
headers["accept"] = "application/json"
|
||||||
|
val r = if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "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=1.4.4&source=copyApp®ion=$r&webp=1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,9 @@
|
|||||||
<string name="apiProxyApiUrl">https://copymanga.azurewebsites.net/api/api?code=%1$s&url=%2$s</string>
|
<string name="apiProxyApiUrl">https://copymanga.azurewebsites.net/api/api?code=%1$s&url=%2$s</string>
|
||||||
<string name="imgResolutionKeyID">settings_cat_net_sb_image_resolution</string>
|
<string name="imgResolutionKeyID">settings_cat_net_sb_image_resolution</string>
|
||||||
|
|
||||||
|
<string name="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="complete">已完结</string>
|
<string name="complete">已完结</string>
|
||||||
|
|
||||||
<string name="TRANSPORT_WIFI">WIFI</string>
|
<string name="TRANSPORT_WIFI">WIFI</string>
|
||||||
@@ -151,6 +154,8 @@
|
|||||||
<string name="settings_cat_net_sm_use_cellar">打开后不再在开始阅读时提示</string>
|
<string name="settings_cat_net_sm_use_cellar">打开后不再在开始阅读时提示</string>
|
||||||
<string name="settings_cat_net_sw_use_foreign">使用海外线路</string>
|
<string name="settings_cat_net_sw_use_foreign">使用海外线路</string>
|
||||||
<string name="settings_cat_net_sm_use_foreign">不管使用什么线路, API访问均是海外, 只有图片CDN可能会变化(也可能不变), 请酌情选择使用</string>
|
<string name="settings_cat_net_sm_use_foreign">不管使用什么线路, API访问均是海外, 只有图片CDN可能会变化(也可能不变), 请酌情选择使用</string>
|
||||||
|
<string name="settings_cat_net_sw_use_comandy">增强型数据访问</string>
|
||||||
|
<string name="settings_cat_net_sm_use_comandy">使用经过优化的请求方法访问服务器</string>
|
||||||
<string name="settings_cat_net_et_title_api_url">请求API网址</string>
|
<string name="settings_cat_net_et_title_api_url">请求API网址</string>
|
||||||
<string name="settings_cat_net_et_summary_api_url">一般无需更改,除非拷贝漫画官方更改网址,默认:&hosturl;</string>
|
<string name="settings_cat_net_et_summary_api_url">一般无需更改,除非拷贝漫画官方更改网址,默认:&hosturl;</string>
|
||||||
<string name="settings_cat_net_sw_use_api_proxy">使用API代理(重启生效)</string>
|
<string name="settings_cat_net_sw_use_api_proxy">使用API代理(重启生效)</string>
|
||||||
@@ -175,6 +180,8 @@
|
|||||||
<string name="settings_cat_md">漫画下载</string>
|
<string name="settings_cat_md">漫画下载</string>
|
||||||
<string name="settings_cat_md_sw_show_0m_manga">显示未下载漫画</string>
|
<string name="settings_cat_md_sw_show_0m_manga">显示未下载漫画</string>
|
||||||
<string name="settings_cat_md_sm_show_0m_manga">打开后将在我的下载显示所有浏览过详情页的漫画,旧版下载永远全部显示</string>
|
<string name="settings_cat_md_sm_show_0m_manga">打开后将在我的下载显示所有浏览过详情页的漫画,旧版下载永远全部显示</string>
|
||||||
|
<string name="settings_cat_md_sb_max_batch">漫画下载并发限制</string>
|
||||||
|
<string name="settings_cat_md_sm_max_batch">默认为16</string>
|
||||||
|
|
||||||
<string name="login_null_username">用户名为空</string>
|
<string name="login_null_username">用户名为空</string>
|
||||||
<string name="login_null_pwd">密码为空</string>
|
<string name="login_null_pwd">密码为空</string>
|
||||||
|
|||||||
@@ -44,6 +44,12 @@
|
|||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:title="@string/settings_cat_net">
|
app:title="@string/settings_cat_net">
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:key="settings_cat_net_sw_use_comandy"
|
||||||
|
app:selectable="true"
|
||||||
|
app:summary="@string/settings_cat_net_sm_use_comandy"
|
||||||
|
app:title="@string/settings_cat_net_sw_use_comandy" />
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:max="1500"
|
android:max="1500"
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
@@ -146,5 +152,14 @@
|
|||||||
app:selectable="true"
|
app:selectable="true"
|
||||||
app:summary="@string/settings_cat_md_sm_show_0m_manga"
|
app:summary="@string/settings_cat_md_sm_show_0m_manga"
|
||||||
app:title="@string/settings_cat_md_sw_show_0m_manga" />
|
app:title="@string/settings_cat_md_sw_show_0m_manga" />
|
||||||
|
<SeekBarPreference
|
||||||
|
android:defaultValue="16"
|
||||||
|
android:max="32"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:key="settings_cat_md_sb_max_batch"
|
||||||
|
app:min="1"
|
||||||
|
app:showSeekBarValue="true"
|
||||||
|
app:summary="@string/settings_cat_md_sm_max_batch"
|
||||||
|
app:title="@string/settings_cat_md_sb_max_batch" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ buildscript {
|
|||||||
maven { url "https://jitpack.io" }
|
maven { url "https://jitpack.io" }
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.2.2'
|
classpath 'com.android.tools.build:gradle:8.3.2'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
|
||||||
|
|||||||
Reference in New Issue
Block a user