diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..bb44937
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 5398dde..7d1c6c9 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,8 +10,8 @@ android {
applicationId "top.fumiama.copymangaweb"
minSdkVersion 23
targetSdkVersion 36
- versionCode 16
- versionName '1.5.3'
+ versionCode 17
+ versionName '1.6.0'
resConfigs "zh", "zh-rCN"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 15dafde..7d81ed5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
= 4) {
+ var titleElement = document.getElementsByTagName("h6")[0];
+ var comicTitle = titleElement && titleElement.title
+ ? titleElement.title
+ : document.title;
+ GM.setFab(JSON.stringify(groups), sourceUrl, comicTitle);
+ return;
+ }
+ } else {
+ stableChecks = 0;
+ lastChapterCount = -1;
+ }
+ if (Date.now() - startedAt < 120000) setTimeout(scan, 250);
+ }
+ scan();
+ }
function smoothLoadChapter(speed, interval) {
let lastTime = 0;
let ticking = false;
@@ -156,22 +202,7 @@ if (typeof (loaded) == "undefined") {
GM.setLoadingDialog(true);
smoothLoadChapter(320, 16);
} else {
- var json = Array();
- var chapters = document.getElementsByClassName("upLoop")[0].children;
- var newObj = null;
- for(var i = 0; i < chapters.length; i++) {
- if(i % 2) {
- newObj["chapters"] = scanChapters(chapters[i]);
- json.push(newObj);
- newObj = null;
- }
- else {
- newObj = JSON.constructor();
- newObj["name"] = chapters[i].innerText;
- }
- }
- GM.setTitle(document.getElementsByTagName("h6")[0].title);
- GM.setFab(JSON.stringify(json));
+ waitForChapterGroups();
}
}
modify();
diff --git a/app/src/main/java/top/fumiama/copymangaweb/activity/DlActivity.kt b/app/src/main/java/top/fumiama/copymangaweb/activity/DlActivity.kt
index 837f071..04a717a 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/activity/DlActivity.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/activity/DlActivity.kt
@@ -61,13 +61,14 @@ class DlActivity : ToolsBoxActivity() {
setWebViewClient("h.js")
loadJSInterface(JSHidden(onLoadChapter = { onHiddenChapterLoaded(it) }))
} }
- handler.sendEmptyMessage(-2) //setLayouts
+ handler.sendEmptyMessage(DlHandler.INITIALIZE_LAYOUTS)
}
override fun onDestroy() {
wm?.get()?.saveUrlsOnly = false
wmdlt?.get()?.exit = true
handler.removeCallbacksAndMessages(null)
+ mBinding.dwh.destroy()
super.onDestroy()
}
@@ -99,7 +100,7 @@ class DlActivity : ToolsBoxActivity() {
for (i in tbtnlist) {
if (i.isChecked && !mangaDlTools.dlChapterUrl(i.url.toString())) {
ok = false
- handler.obtainMessage(-1, i.index, 0).sendToTarget()
+ handler.obtainMessage(DlHandler.CHAPTER_DOWNLOAD_FAILED, i.index, 0).sendToTarget()
}
}
return ok && mangaDlTools.waitChapterUrlsReady()
@@ -119,7 +120,7 @@ class DlActivity : ToolsBoxActivity() {
haveDlStarted = false
canDl = false
}
- handler.sendEmptyMessage(8) //set dl card color to blue
+ handler.sendEmptyMessage(DlHandler.SET_DOWNLOAD_CARD_BLUE)
}
@SuppressLint("SetTextI18n")
@@ -150,7 +151,7 @@ class DlActivity : ToolsBoxActivity() {
else {
haveDlStarted = true
canDl = true
- handler.sendEmptyMessage(9) //set dl card color to red
+ handler.sendEmptyMessage(DlHandler.SET_DOWNLOAD_CARD_RED)
Toast.makeText(this@DlActivity, "请耐心等待加载...", Toast.LENGTH_SHORT).show()
Thread {
if (fillChapters()) {
@@ -158,14 +159,14 @@ class DlActivity : ToolsBoxActivity() {
} else {
canDl = false
haveDlStarted = false
- handler.sendEmptyMessage(8)
+ handler.sendEmptyMessage(DlHandler.SET_DOWNLOAD_CARD_BLUE)
}
}.start()
}
}
}
it.setOnLongClickListener {
- handler.sendEmptyMessage(4)
+ handler.sendEmptyMessage(DlHandler.TOGGLE_SELECT_ALL)
return@setOnLongClickListener true
}
} }
@@ -181,7 +182,7 @@ class DlActivity : ToolsBoxActivity() {
}
private fun analyzeStructure() {
- ViewMangaActivity.zipList = arrayOf()
+ ViewMangaActivity.zipList = emptyArray()
Gson().fromJson(json?.reader(), Array::class.java)?.let {
for (group in it) {
val tc = layoutInflater.inflate(R.layout.line_caption, mBinding.ldwn, false)
@@ -224,12 +225,17 @@ class DlActivity : ToolsBoxActivity() {
mangaDlTools.onDownloadedListener =
object : MangaDlTools.OnDownloadedListener {
override fun handleMessage(succeed: Boolean) {
- handler.obtainMessage(if (succeed) 1 else -1, i.index, 0)
+ handler.obtainMessage(
+ if (succeed) DlHandler.CHAPTER_DOWNLOAD_SUCCEEDED
+ else DlHandler.CHAPTER_DOWNLOAD_FAILED,
+ i.index,
+ 0
+ )
.sendToTarget()
}
override fun handleMessage(succeed: Boolean, pageNow: Int) {
handler.obtainMessage(
- 5,
+ DlHandler.PAGE_DOWNLOAD_FINISHED,
i.index,
pageNow,
succeed
@@ -237,7 +243,7 @@ class DlActivity : ToolsBoxActivity() {
}
override fun handleMessage(pageNow: Int){
handler.obtainMessage(
- 10,
+ DlHandler.PAGE_DOWNLOAD_RETRYING,
i.index,
pageNow
).sendToTarget()
@@ -268,14 +274,19 @@ class DlActivity : ToolsBoxActivity() {
tbtnlist += tbvTbtn
tbvTbtn.url = url
tbtncnt++
- val zipPosition = ViewMangaActivity.zipList?.size
- ViewMangaActivity.zipList = ViewMangaActivity.zipList?.plus("$title.zip")
tbvTbtn.textOff = title
tbvTbtn.textOn = title
tbvTbtn.text = title
tbvTbtn.hint = caption
tbvTbtn.layoutParams.width = btnw
val zipFile = File("${getExternalFilesDir("")}/$comicName/$caption/$title.zip")
+ val zipPosition = if (zipFile.exists()) {
+ ViewMangaActivity.zipList.orEmpty().size.also {
+ ViewMangaActivity.zipList = (
+ ViewMangaActivity.zipList?.toList().orEmpty() + zipFile
+ ).toTypedArray()
+ }
+ } else null
if (zipFile.exists()) {
tbvTbtn.setBackgroundResource(R.drawable.rndbg_checked)
tbvTbtn.isChecked = false
@@ -301,9 +312,7 @@ class DlActivity : ToolsBoxActivity() {
} else if(tbtn.isChecked) {
tbtn.apply { post {
isChecked = false
- zipPosition?.let { Thread {
- callVM(title, zipFile, it)
- }.start() }
+ zipPosition?.let { callVM(title, zipFile, it) }
} }
}
}
@@ -321,7 +330,7 @@ class DlActivity : ToolsBoxActivity() {
text = "$dldChapter/${++checkedChapter}"
} }
}
- handler.sendEmptyMessage(7)
+ handler.sendEmptyMessage(DlHandler.DELETE_SELECTED_CHAPTERS)
})
}
true
@@ -338,15 +347,16 @@ class DlActivity : ToolsBoxActivity() {
}
}
}
- handler.sendEmptyMessage(6)
+ handler.sendEmptyMessage(DlHandler.UPDATE_CHAPTER_PROGRESS)
}
private fun callVM(titleText: String, zipFile: File, zipPosition:Int) {
ViewMangaActivity.titleText = titleText
ViewMangaActivity.zipFile = zipFile
- //ViewMangaActivity.zipList = zipArrayList
ViewMangaActivity.zipPosition = zipPosition
ViewMangaActivity.cd = zipFile.parentFile
+ ViewMangaActivity.nextChapterUrl = null
+ ViewMangaActivity.previousChapterUrl = null
startActivity(Intent(this@DlActivity, ViewMangaActivity::class.java))
}
diff --git a/app/src/main/java/top/fumiama/copymangaweb/activity/DlListActivity.kt b/app/src/main/java/top/fumiama/copymangaweb/activity/DlListActivity.kt
index b090aab..7b8066e 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/activity/DlListActivity.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/activity/DlListActivity.kt
@@ -4,21 +4,21 @@ import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
-import android.os.Looper
+import android.util.Log
import android.widget.ArrayAdapter
import android.widget.Toast
import top.fumiama.copymangaweb.R
import top.fumiama.copymangaweb.databinding.ActivityDlistBinding
-import top.fumiama.copymangaweb.handler.DlLHandler
import top.fumiama.copymangaweb.tool.InsetsTools
import java.io.File
+import java.util.concurrent.Executors
import java.util.regex.Pattern
import java.util.zip.ZipInputStream
-class DlListActivity: Activity() {
+class DlListActivity : Activity() {
private lateinit var mBinding: ActivityDlistBinding
private var nullZipDirStr = emptyArray()
- private var handler: DlLHandler? = null
+ private val fileExecutor = Executors.newSingleThreadExecutor()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -26,79 +26,97 @@ class DlListActivity: Activity() {
setContentView(mBinding.root)
InsetsTools.applySafeContentInsets(this, mBinding.root)
mBinding.myt.ttitle.text = intent.getStringExtra("title")
- handler = DlLHandler(Looper.myLooper()!!, this)
- handler?.obtainMessage(3, currentDir)?.sendToTarget() //call scanFile
+ loadDirectory(currentDir)
}
override fun onDestroy() {
+ fileExecutor.shutdownNow()
super.onDestroy()
- handler?.removeCallbacksAndMessages(null)
- handler = null
}
- fun scanFile(cd: File?){
- val isRoot = cd == getExternalFilesDir("")
- val jsonFile = File(cd, "info.bin")
- if(isRoot || !jsonFile.exists()) cd?.list()?.sortedArrayWith { o1, o2 ->
- if(o1.endsWith(".zip") && o2.endsWith(".zip")) (10000*getFloat(o1) - 10000*getFloat(o2) + 0.5).toInt()
- else o1[0] - o2[0]
- }?.let {
- mBinding.mylv.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, it)
- mBinding.mylv.setOnItemClickListener { _, _, position, _ ->
- val chosenFile = File(cd, it[position])
- val chosenJson = File(chosenFile, "info.bin")
- //Toast.makeText(this, "进入$chosenFile", Toast.LENGTH_SHORT).show()
- when {
- chosenJson.exists() -> callDownloadActivity(chosenJson)
- chosenFile.isDirectory -> {
- currentDir = chosenFile
- startActivity(
- Intent(
- this,
- DlListActivity::class.java
- ).putExtra("title", it[position])
- )
- }
- chosenFile.name.endsWith(".zip") -> {
- Toast.makeText(this, "加载中...", Toast.LENGTH_SHORT).show()
- ViewMangaActivity.zipFile = chosenFile
- ViewMangaActivity.titleText = it[position]
- ViewMangaActivity.zipPosition = position
- ViewMangaActivity.zipList = it.toList().toTypedArray()
- ViewMangaActivity.cd = cd
- startActivity(Intent(this, ViewMangaActivity::class.java))
- }
+ private fun loadDirectory(directory: File?) {
+ fileExecutor.execute {
+ val isRoot = directory == getExternalFilesDir("")
+ val jsonFile = File(directory, "info.bin")
+ val entries = if (isRoot || !jsonFile.exists()) {
+ directory?.list()?.sortedWith(::compareFileNames)
+ } else null
+ runOnUiThread {
+ if (!isFinishing && !isDestroyed && entries != null) {
+ showDirectory(directory, entries)
}
}
- mBinding.mylv.setOnItemLongClickListener { _, _, position, _ ->
- val chosenFile = File(cd, it[position])
- AlertDialog.Builder(this)
- .setIcon(R.drawable.ic_launcher_foreground).setMessage("在此执行删除/查错?")
- .setTitle("提示").setPositiveButton("删除"){ _, _ ->
- if(chosenFile.exists()) handler?.obtainMessage(2, chosenFile)?.sendToTarget() //call rmrf
- handler?.obtainMessage(3, cd)?.sendToTarget() //call scanFile
- }.setNegativeButton(android.R.string.cancel){_, _ ->}
- .setNeutralButton("查错"){_, _ -> handler?.obtainMessage(1, chosenFile)?.sendToTarget()} //call checkDir
- .show()
- true
- }
}
}
- fun rmrf(f: File) {
+ private fun showDirectory(directory: File?, entries: List) {
+ mBinding.mylv.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, entries)
+ mBinding.mylv.setOnItemClickListener { _, _, position, _ ->
+ val chosenFile = File(directory, entries[position])
+ val chosenJson = File(chosenFile, "info.bin")
+ when {
+ chosenJson.exists() -> callDownloadActivity(chosenJson)
+ chosenFile.isDirectory -> {
+ currentDir = chosenFile
+ startActivity(
+ Intent(this, DlListActivity::class.java)
+ .putExtra("title", entries[position])
+ )
+ }
+ chosenFile.name.endsWith(".zip", ignoreCase = true) -> {
+ Toast.makeText(this, "加载中...", Toast.LENGTH_SHORT).show()
+ val zipFiles = entries
+ .map { File(directory, it) }
+ .filter { it.isFile && it.extension.equals("zip", ignoreCase = true) }
+ ViewMangaActivity.zipFile = chosenFile
+ ViewMangaActivity.titleText = entries[position]
+ ViewMangaActivity.zipPosition = zipFiles.indexOf(chosenFile)
+ ViewMangaActivity.zipList = zipFiles.toTypedArray()
+ ViewMangaActivity.cd = directory
+ ViewMangaActivity.nextChapterUrl = null
+ ViewMangaActivity.previousChapterUrl = null
+ startActivity(Intent(this, ViewMangaActivity::class.java))
+ }
+ }
+ }
+ mBinding.mylv.setOnItemLongClickListener { _, _, position, _ ->
+ val chosenFile = File(directory, entries[position])
+ AlertDialog.Builder(this)
+ .setIcon(R.drawable.ic_launcher_foreground)
+ .setMessage("在此执行删除/查错?")
+ .setTitle("提示")
+ .setPositiveButton("删除") { _, _ ->
+ fileExecutor.execute {
+ if (chosenFile.exists()) deleteRecursively(chosenFile)
+ loadDirectory(directory)
+ }
+ }
+ .setNegativeButton(android.R.string.cancel, null)
+ .setNeutralButton("查错") { _, _ -> checkDirectory(chosenFile) }
+ .show()
+ true
+ }
+ }
+
+ private fun deleteRecursively(f: File) {
if (f.isDirectory) f.listFiles()?.let {
for (i in it)
- if (i.isDirectory) rmrf(i)
+ if (i.isDirectory) deleteRecursively(i)
else i.delete()
}
f.delete()
}
- fun checkDir(f: File){
- nullZipDirStr = emptyArray()
- findNullWebpZipFileInDir(f)
- if(nullZipDirStr.isNotEmpty()) showErrorZip(nullZipDirStr.joinToString("\n"))
- else Toast.makeText(this, "未发现错误", Toast.LENGTH_SHORT).show()
+ private fun checkDirectory(directory: File) {
+ fileExecutor.execute {
+ val invalidFiles = findInvalidZipFiles(directory)
+ runOnUiThread {
+ if (isFinishing || isDestroyed) return@runOnUiThread
+ nullZipDirStr = invalidFiles.toTypedArray()
+ if (invalidFiles.isNotEmpty()) showErrorZip(invalidFiles.joinToString("\n"))
+ else Toast.makeText(this, "未发现错误", Toast.LENGTH_SHORT).show()
+ }
+ }
}
private fun callDownloadActivity(jsonFile: File){
@@ -110,54 +128,64 @@ class DlListActivity: Activity() {
)
}
- private fun findNullWebpZipFileInDir(f: File){
- if (f.isDirectory) f.listFiles()?.let {
- for (i in it)
- if (i.isDirectory) findNullWebpZipFileInDir(i)
- else if(!checkZip(i)) nullZipDirStr += i.path.substringAfterLast(getExternalFilesDir("").toString())
+ private fun findInvalidZipFiles(file: File): List {
+ val invalidFiles = mutableListOf()
+ if (file.isDirectory) file.listFiles()?.forEach { child ->
+ if (child.isDirectory) invalidFiles += findInvalidZipFiles(child)
+ else if (child.extension.equals("zip", ignoreCase = true) && !checkZip(child)) {
+ invalidFiles += child.path.substringAfterLast(getExternalFilesDir("").toString())
+ }
}
+ return invalidFiles
}
- private fun checkZip(f: File): Boolean{
+ private fun checkZip(f: File): Boolean {
return try {
val exist = f.exists()
if (!exist) true
else {
var re = true
- val zip = ZipInputStream(f.inputStream().buffered())
- var entry = zip.nextEntry
- while (entry != null) {
- if (!entry.isDirectory){
- if(zip.read() == -1 && entry.size == 0L){
+ ZipInputStream(f.inputStream().buffered()).use { zip ->
+ var entry = zip.nextEntry
+ while (entry != null) {
+ if (!entry.isDirectory && zip.read() == -1 && entry.size == 0L) {
re = false
break
}
+ zip.closeEntry()
+ entry = zip.nextEntry
}
- entry = zip.nextEntry
}
- zip.closeEntry()
- zip.close()
re
}
} catch (e: Exception) {
- Toast.makeText(this, "读取${f.name}错误!", Toast.LENGTH_SHORT).show()
- true
+ Log.e("DlListActivity", "读取 ${f.name} 失败", e)
+ false
}
}
+ private fun compareFileNames(first: String, second: String): Int {
+ return if (first.endsWith(".zip") && second.endsWith(".zip")) {
+ (10000 * getFloat(first) - 10000 * getFloat(second) + 0.5).toInt()
+ } else first.compareTo(second)
+ }
+
private fun showErrorZip(msg: CharSequence) = AlertDialog.Builder(this)
.setIcon(R.drawable.ic_launcher_foreground)
.setTitle("找到以下错误文件,是否删除?")
.setMessage(msg)
- .setPositiveButton(android.R.string.ok){_, _ -> deleteErrorZip()}
- .setNegativeButton(android.R.string.cancel){_, _ ->}
+ .setPositiveButton(android.R.string.ok) { _, _ -> deleteErrorZip() }
+ .setNegativeButton(android.R.string.cancel, null)
.show()
- private fun deleteErrorZip(){
+ private fun deleteErrorZip() {
val exf = getExternalFilesDir("")
- for(i in nullZipDirStr){
- val f = File(exf, i)
- if(f.exists()) f.delete()
+ fileExecutor.execute {
+ for (path in nullZipDirStr) {
+ val file = File(exf, path)
+ if (file.exists()) file.delete()
+ }
+ loadDirectory(currentDir)
}
}
@@ -165,16 +193,14 @@ class DlListActivity: Activity() {
val newString = StringBuffer()
var matcher = Pattern.compile("\\d+.+\\d+").matcher(oldString)
while (matcher.find()) newString.append(matcher.group())
- //Log.d("MyDLL1", newString.toString())
- if(newString.isEmpty()){
+ if (newString.isEmpty()) {
matcher = Pattern.compile("\\d").matcher(oldString)
while (matcher.find()) newString.append(matcher.group())
}
- //Log.d("MyDLL2", newString.toString().toFloat().toString())
- return if(newString.isEmpty()) 0f else newString.toString().toFloat()
+ return newString.toString().toFloatOrNull() ?: 0f
}
- companion object{
+ companion object {
var currentDir: File? = null
}
}
diff --git a/app/src/main/java/top/fumiama/copymangaweb/activity/MainActivity.kt b/app/src/main/java/top/fumiama/copymangaweb/activity/MainActivity.kt
index 384bfcf..4f0ca22 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/activity/MainActivity.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/activity/MainActivity.kt
@@ -3,12 +3,14 @@ package top.fumiama.copymangaweb.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
+import android.os.Build
import android.os.Bundle
import android.os.Looper
import android.view.View
-import android.view.WindowManager
import android.webkit.ValueCallback
import android.webkit.WebView
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -34,6 +36,9 @@ class MainActivity: ToolsBoxActivity() {
var saveUrlsOnly = false
lateinit var mBinding: ActivityMainBinding
private val mViewModel = MainViewModel()
+ private var backInvokedCallback: OnBackInvokedCallback? = null
+ @Volatile
+ private var requestedDetailsUrl: String? = null
@SuppressLint("JavascriptInterface")
override fun onCreate(savedInstanceState: Bundle?) {
@@ -43,6 +48,7 @@ class MainActivity: ToolsBoxActivity() {
mBinding.lifecycleOwner = this
setContentView(mBinding.root)
InsetsTools.applySafeContentInsets(this, mBinding.root)
+ registerBackCallback()
wm = WeakReference(this)
mh = MainHandler(Looper.myLooper()!!)
@@ -58,7 +64,7 @@ class MainActivity: ToolsBoxActivity() {
}
}
- WebView.setWebContentsDebuggingEnabled(true)
+ WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
mBinding.w.apply { post {
setWebViewClient("i.js")
webChromeClient = WebChromeClient()
@@ -78,36 +84,38 @@ class MainActivity: ToolsBoxActivity() {
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
- if(mBinding.w.canGoBack()) mBinding.w.goBack()
- else super.onBackPressed()
+ navigateBack()
+ }
+
+ private fun registerBackCallback() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
+ backInvokedCallback = OnBackInvokedCallback(::navigateBack).also {
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ it
+ )
+ }
+ }
+
+ private fun navigateBack() {
+ if (mBinding.w.canGoBack()) mBinding.w.goBack()
+ else finishAfterTransition()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
- if (requestCode == FILE_CHOOSER_RESULT_CODE) { //处理返回的图片,并进行上传
- if (uploadMessageAboveL == null || resultCode != RESULT_OK) return
- data?.let {
- onActivityResultAboveL(requestCode, resultCode, it)
- }
- }
+ if (requestCode != FILE_CHOOSER_RESULT_CODE) return
+
+ val callback = uploadMessageAboveL ?: return
+ uploadMessageAboveL = null
+ callback.onReceiveValue(if (resultCode == RESULT_OK) data?.selectedUris() else null)
}
- private fun onActivityResultAboveL(requestCode: Int, resultCode: Int, intent: Intent) {
- if (requestCode != FILE_CHOOSER_RESULT_CODE ||
- uploadMessageAboveL == null ||
- resultCode != RESULT_OK
- ) return
- intent.clipData?.let { clipData ->
- var results = arrayOf()
- for (i in 0..clipData.itemCount) {
- val item = clipData.getItemAt(i)
- results += item.uri
- }
- if (intent.dataString != null) {
- uploadMessageAboveL?.onReceiveValue(results)
- uploadMessageAboveL = null
- }
- }
+ private fun Intent.selectedUris(): Array? {
+ val uris = clipData?.let { clips ->
+ Array(clips.itemCount) { index -> clips.getItemAt(index).uri }
+ } ?: data?.let { arrayOf(it) }
+ return uris?.takeIf { it.isNotEmpty() }
}
private suspend fun goCheckUpdate(ignoreSkip: Boolean) {
@@ -120,6 +128,7 @@ class MainActivity: ToolsBoxActivity() {
}
fun loadHiddenUrl(u: String) {
+ requestedDetailsUrl = u
mBinding.wh.apply { post { loadUrl(u) } }
}
@@ -137,7 +146,9 @@ class MainActivity: ToolsBoxActivity() {
startActivity(Intent(this, ViewMangaActivity::class.java))
}
- fun setFab(content: String) {
+ fun setFab(content: String, sourceUrl: String, comicTitle: String) {
+ if (content.isBlank() || content == "[]" || !isRequestedDetailsPage(sourceUrl)) return
+ DlActivity.comicName = comicTitle
json = content
lifecycleScope.launch {
withContext(Dispatchers.Main) {
@@ -148,6 +159,7 @@ class MainActivity: ToolsBoxActivity() {
}
fun setFab2DlList() {
+ requestedDetailsUrl = null
lifecycleScope.launch {
withContext(Dispatchers.Main) {
mViewModel.showDlList.value = true
@@ -157,9 +169,20 @@ class MainActivity: ToolsBoxActivity() {
}
fun hideFab() {
+ requestedDetailsUrl = null
lifecycleScope.launch { mViewModel.setFabVisibility(false) }
}
+ private fun isRequestedDetailsPage(sourceUrl: String): Boolean {
+ val requestedPath = requestedDetailsUrl
+ ?.let(Uri::parse)
+ ?.path
+ ?.trimEnd('/')
+ ?: return false
+ val sourcePath = Uri.parse(sourceUrl).path?.trimEnd('/') ?: return false
+ return requestedPath == sourcePath
+ }
+
fun onFabClicked(v: View) {
DlListActivity.currentDir = getExternalFilesDir("")
startActivity(
@@ -168,13 +191,16 @@ class MainActivity: ToolsBoxActivity() {
)
}
- fun openImageChooserActivity() {
- // 调用自己的图库
+ fun openImageChooserActivity(callback: ValueCallback>) {
+ uploadMessageAboveL?.onReceiveValue(null)
+ uploadMessageAboveL = callback
startActivityForResult(
Intent.createChooser(
Intent(Intent.ACTION_GET_CONTENT)
.addCategory(Intent.CATEGORY_OPENABLE)
- .setType("image/*"), "Image Chooser"
+ .setType("image/*")
+ .putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false),
+ "Image Chooser"
), FILE_CHOOSER_RESULT_CODE
)
}
@@ -182,25 +208,40 @@ class MainActivity: ToolsBoxActivity() {
fun callViewManga(content: String) {
lifecycleScope.launch { withContext(Dispatchers.IO) {
val listChapter = content.split('\n')
+ if (listChapter.size < CHAPTER_METADATA_LINE_COUNT) return@withContext
+ val images = listChapter.drop(CHAPTER_METADATA_LINE_COUNT).toTypedArray()
if(!saveUrlsOnly) {
ViewMangaActivity.titleText = listChapter[0].substringBeforeLast(' ')
ViewMangaActivity.nextChapterUrl = listChapter[1].let { if(it == "null") null else it }
ViewMangaActivity.previousChapterUrl = listChapter[2].let { if(it == "null") null else it }
- ViewMangaActivity.imgUrls = arrayOf()
- for(i in 3 until listChapter.size) ViewMangaActivity.imgUrls += listChapter[i]
+ ViewMangaActivity.imgUrls = images
withContext(Dispatchers.Main) {
startActivity(Intent(this@MainActivity, ViewMangaActivity::class.java))
}
} else {
- var imgs = arrayOf()
- for(i in 3 until listChapter.size) imgs += listChapter[i]
- wmdlt?.get()?.setChapterImages(listChapter[0].substringAfterLast(' '), imgs)
+ wmdlt?.get()?.setChapterImages(listChapter[0].substringAfterLast(' '), images)
}
} }
}
+ override fun onDestroy() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ backInvokedCallback?.let(onBackInvokedDispatcher::unregisterOnBackInvokedCallback)
+ }
+ backInvokedCallback = null
+ uploadMessageAboveL?.onReceiveValue(null)
+ uploadMessageAboveL = null
+ mBinding.w.destroy()
+ mBinding.wh.destroy()
+ mh?.dispose()
+ mh = null
+ if (wm?.get() === this) wm = null
+ super.onDestroy()
+ }
+
companion object {
- const val FILE_CHOOSER_RESULT_CODE = 1
+ private const val FILE_CHOOSER_RESULT_CODE = 1
+ private const val CHAPTER_METADATA_LINE_COUNT = 3
var wm: WeakReference? = null
var mh: MainHandler? = null
}
diff --git a/app/src/main/java/top/fumiama/copymangaweb/activity/ViewMangaActivity.kt b/app/src/main/java/top/fumiama/copymangaweb/activity/ViewMangaActivity.kt
index 91b5f64..11dad33 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/activity/ViewMangaActivity.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/activity/ViewMangaActivity.kt
@@ -7,41 +7,37 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Build
import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
-import android.os.Message
import android.util.Log
import android.view.KeyEvent
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import android.widget.SeekBar
import android.widget.Toast
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.bumptech.glide.Glide
import top.fumiama.copymangaweb.R
+import top.fumiama.copymangaweb.activity.reader.ContinuousMangaAdapter
+import top.fumiama.copymangaweb.activity.reader.PagedMangaAdapter
+import top.fumiama.copymangaweb.activity.reader.ReaderOverlayController
import top.fumiama.copymangaweb.activity.MainActivity.Companion.wm
import top.fumiama.copymangaweb.activity.template.ToolsBoxActivity
import top.fumiama.copymangaweb.databinding.ActivityViewmangaBinding
-import top.fumiama.copymangaweb.handler.TimeThread
import top.fumiama.copymangaweb.tool.PropertiesTools
-import top.fumiama.copymangaweb.tool.ToolsBox
+import top.fumiama.copymangaweb.tool.PagesManager
import top.fumiama.copymangaweb.view.ScaleImageView
import top.fumiama.copymangaweb.web.JSHidden
import top.fumiama.copymangaweb.web.WebChromeClient
import java.io.File
import java.lang.ref.WeakReference
-import java.text.SimpleDateFormat
-import java.util.Date
+import java.util.concurrent.Executors
import kotlin.math.max
import kotlin.math.min
import java.util.zip.ZipFile
-import java.util.zip.ZipInputStream
class ViewMangaActivity : ToolsBoxActivity() {
- lateinit var handler: Handler
- lateinit var tt: TimeThread
lateinit var mBinding: ActivityViewmangaBinding
var count = 0
@@ -54,32 +50,40 @@ class ViewMangaActivity : ToolsBoxActivity() {
private var isInSeek = false
private var currentItem = 0
private var notUseVP = true
+ private var verticalReading = false
private var mangaZip = zipFile
val dlZip2View = mangaZip != null
private var streamUrl = streamChapterUrl
private var readerPrepared = false
private var streamFinished = false
private var streamDeclaredCount = 0
+ private var userRequestedExit = false
+ private var backInvokedCallback: OnBackInvokedCallback? = null
private val streamSeenUrls = LinkedHashSet()
- private var vpAdapter: RecyclerView.Adapter? = null
+ private var pagedAdapter: PagedMangaAdapter? = null
+ private var continuousAdapter: ContinuousMangaAdapter? = null
+ private val imageExecutor = Executors.newFixedThreadPool(2)
+ private lateinit var overlayController: ReaderOverlayController
+ private val pagesManager by lazy { PagesManager(WeakReference(this)) }
private val volTurnPage get() = p["volturn"] == "true"
- var pageNum = 1
- get() {
- field = getPageNumber()
- return field
+ private val readerMode: ReaderMode
+ get() = when {
+ verticalReading -> ReaderMode.CONTINUOUS
+ notUseVP -> ReaderMode.SINGLE_PAGE
+ else -> ReaderMode.PAGED
}
+ var pageNum: Int
+ get() = getPageNumber()
set(value) {
- setPageNumber(value)
- if (notUseVP) {
- //currentItem += delta
+ setPageNumber(value, smoothScroll = true)
+ if (readerMode == ReaderMode.SINGLE_PAGE) {
try {
loadOneImg()
} catch (e: java.lang.Exception) {
e.printStackTrace()
toolsBox.toastError("页数${currentItem}不合法")
}
- }// else vp.currentItem += delta
- field = getPageNumber()
+ }
}
@SuppressLint("SetTextI18n")
@@ -87,14 +91,18 @@ class ViewMangaActivity : ToolsBoxActivity() {
super.onCreate(savedInstanceState)
mBinding = ActivityViewmangaBinding.inflate(layoutInflater)
setContentView(mBinding.root)
+ registerBackCallback()
va = WeakReference(this)
p = PropertiesTools(File("$filesDir/settings.properties"))
r2l = p["r2l"] == "true"
notUseVP = p["noAnimation"] == "true"
- handler = MyHandler(toolsBox)
- tt = TimeThread(handler, 22)
- tt.canDo = true
- tt.start()
+ verticalReading = p["vertical"] == "true"
+ overlayController = ReaderOverlayController(
+ activity = this,
+ binding = mBinding,
+ toolsBox = toolsBox,
+ drawerOffset = { infoDrawerDelta }
+ ).also { it.start() }
dialog = Dialog(this)
dialog?.apply {
setContentView(R.layout.dialog_unzipping)
@@ -105,13 +113,18 @@ class ViewMangaActivity : ToolsBoxActivity() {
if(dlZip2View && mangaZip?.exists() != true) toolsBox.toastError("已经到头了~")
else if(!dlZip2View && !streamUrl.isNullOrBlank()) startStreamingCollector(streamUrl!!)
else Thread {
- try {
- count = if (dlZip2View) countZipItems() else imgUrls.size
+ val initialCount = try {
+ if (dlZip2View) countZipItems() else imgUrls.size
} catch (e: Exception) {
e.printStackTrace()
- runOnUiThread { toolsBox.toastError("分析图片url错误") }
+ 0
+ }
+ runOnUiThread {
+ if (isFinishing || isDestroyed) return@runOnUiThread
+ count = initialCount
+ if (initialCount == 0) toolsBox.toastError("分析图片url错误")
+ prepareReaderIfNeeded()
}
- runOnUiThread { prepareReaderIfNeeded() }
}.start()
}
@@ -162,14 +175,15 @@ class ViewMangaActivity : ToolsBoxActivity() {
private fun onStreamingCount(total: Int) {
if (total <= 0) return
runOnUiThread {
+ if (isFinishing || isDestroyed) return@runOnUiThread
val oldCount = count
streamDeclaredCount = max(streamDeclaredCount, total)
count = max(count, streamDeclaredCount)
if (!readerPrepared) {
prepareReaderIfNeeded()
} else {
- if (!notUseVP && count > oldCount) {
- vpAdapter?.notifyItemRangeInserted(oldCount, count - oldCount)
+ if (readerMode != ReaderMode.SINGLE_PAGE && count > oldCount) {
+ notifyPagesInserted(oldCount, count - oldCount)
}
syncSeekBarOnly()
}
@@ -184,17 +198,25 @@ class ViewMangaActivity : ToolsBoxActivity() {
}
private fun onStreamingFinished() {
- streamFinished = true
- Log.d("CopymangaDL", "reader stream finished pages=$count")
runOnUiThread {
- dialog?.dismiss()
- dialog = null
- syncSeekBarOnly()
+ if (isFinishing || isDestroyed) return@runOnUiThread
+ streamFinished = true
+ Log.d(
+ "CopymangaDL",
+ "reader stream finished declared=$streamDeclaredCount received=${imgUrls.size}"
+ )
+ prepareReaderIfNeeded()
+ if (readerPrepared) {
+ dialog?.dismiss()
+ dialog = null
+ syncSeekBarOnly()
+ }
}
}
private fun appendStreamingImages(urls: Array) {
runOnUiThread {
+ if (isFinishing || isDestroyed) return@runOnUiThread
val oldCount = count
val oldImageCount = imgUrls.size
var added = 0
@@ -209,12 +231,16 @@ class ViewMangaActivity : ToolsBoxActivity() {
Log.d("CopymangaDL", "reader appended images added=$added total=$count images=${imgUrls.size}")
if (!readerPrepared) prepareReaderIfNeeded()
else {
- if (notUseVP) {
+ if (readerMode == ReaderMode.SINGLE_PAGE) {
if (currentItem in oldImageCount until imgUrls.size) loadOneImg(hidePanel = false)
else syncSeekBarOnly()
} else {
- if (count > oldCount) vpAdapter?.notifyItemRangeInserted(oldCount, count - oldCount)
+ if (count > oldCount) notifyPagesInserted(oldCount, count - oldCount)
notifyVisiblePagesIfArrived(oldImageCount, imgUrls.size)
+ continuousAdapter?.notifyItemRangeChanged(
+ oldImageCount,
+ imgUrls.size - oldImageCount
+ )
syncSeekBarOnly()
}
preloadAround(getLogicalCurrentItem())
@@ -223,17 +249,21 @@ class ViewMangaActivity : ToolsBoxActivity() {
}
private fun prepareReaderIfNeeded() {
+ if (
+ readerPrepared ||
+ count <= 0 ||
+ isFinishing ||
+ isDestroyed ||
+ shouldWaitForLastStreamingPage()
+ ) return
+ readerPrepared = true
try {
- if (count <= 0) return
prepareItems()
- if(pn > 0) {
- pageNum = pn
- pn = -1
- } else if(pn == -2){
- pageNum = count
- pn = -1
- }
+ setPageNumber(consumeRequestedPage(), smoothScroll = false)
+ if (readerMode == ReaderMode.SINGLE_PAGE) loadOneImg()
+ else syncSeekBarOnly()
} catch (e: Exception) {
+ readerPrepared = false
e.printStackTrace()
toolsBox.toastError("准备控件错误")
} finally {
@@ -244,6 +274,23 @@ class ViewMangaActivity : ToolsBoxActivity() {
}
}
+ private fun shouldWaitForLastStreamingPage(): Boolean {
+ if (streamUrl == null || pn != LAST_PAGE) return false
+ val allDeclaredPagesReceived =
+ streamDeclaredCount <= 0 || imgUrls.size >= streamDeclaredCount
+ return !streamFinished || !allDeclaredPagesReceived
+ }
+
+ private fun consumeRequestedPage(): Int {
+ val requestedPage = when {
+ pn == LAST_PAGE -> count
+ pn > 0 -> pn.coerceAtMost(count)
+ else -> 1
+ }
+ pn = FIRST_PAGE
+ return requestedPage
+ }
+
private fun preloadAround(position: Int) {
if (dlZip2View || position < 0 || imgUrls.isEmpty()) return
val end = min(imgUrls.size - 1, position + 4)
@@ -257,24 +304,20 @@ class ViewMangaActivity : ToolsBoxActivity() {
}
private fun getLogicalCurrentItem(): Int {
- return if (notUseVP) currentItem
- else if (r2l) count - mBinding.vp.currentItem - 1
- else mBinding.vp.currentItem
- }
-
- private fun logicalToPagerPosition(logicalPosition: Int): Int {
- return if (r2l) count - logicalPosition - 1 else logicalPosition
+ return when (readerMode) {
+ ReaderMode.SINGLE_PAGE, ReaderMode.CONTINUOUS -> currentItem
+ ReaderMode.PAGED -> mBinding.vp.currentItem
+ }
}
private fun notifyVisiblePagesIfArrived(oldImageCount: Int, newImageCount: Int) {
- if (notUseVP || oldImageCount >= newImageCount || count <= 0) return
+ if (readerMode != ReaderMode.PAGED || oldImageCount >= newImageCount || count <= 0) return
val center = getLogicalCurrentItem().coerceIn(0, count - 1)
val from = max(0, center - 1)
val to = min(newImageCount - 1, center + 1)
for (logicalPosition in from..to) {
if (logicalPosition >= oldImageCount) {
- val pagerPosition = logicalToPagerPosition(logicalPosition)
- if (pagerPosition in 0 until count) vpAdapter?.notifyItemChanged(pagerPosition)
+ pagedAdapter?.notifyItemChanged(logicalPosition)
}
}
}
@@ -287,28 +330,57 @@ class ViewMangaActivity : ToolsBoxActivity() {
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
- var flag = false
- if(volTurnPage) when(keyCode) {
- KeyEvent.KEYCODE_VOLUME_UP -> {
- scrollBack()
- flag = true
- }
- KeyEvent.KEYCODE_VOLUME_DOWN -> {
- scrollForward()
- flag = true
+ if (volTurnPage) {
+ when (keyCode) {
+ KeyEvent.KEYCODE_VOLUME_UP -> pagesManager.goBackward()
+ KeyEvent.KEYCODE_VOLUME_DOWN -> pagesManager.goForward()
+ else -> return super.onKeyDown(keyCode, event)
}
+ return true
}
- return if(flag) true else super.onKeyDown(keyCode, event)
+ return super.onKeyDown(keyCode, event)
+ }
+
+ @Deprecated("Deprecated in Java")
+ override fun onBackPressed() {
+ exitByUserRequest()
+ }
+
+ private fun registerBackCallback() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
+ backInvokedCallback = OnBackInvokedCallback(::exitByUserRequest).also { callback ->
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ callback
+ )
+ }
+ }
+
+ private fun exitByUserRequest() {
+ userRequestedExit = true
+ finishAfterTransition()
}
private fun getPageNumber(): Int {
- return if (r2l && !notUseVP) count - mBinding.vp.currentItem
- else (if (notUseVP) currentItem else mBinding.vp.currentItem) + 1
+ return when (readerMode) {
+ ReaderMode.SINGLE_PAGE, ReaderMode.CONTINUOUS -> currentItem + 1
+ ReaderMode.PAGED -> mBinding.vp.currentItem + 1
+ }
}
- private fun setPageNumber(num: Int) {
- if (r2l && !notUseVP) mBinding.vp.apply { post { currentItem = count - num } }
- else if (notUseVP) currentItem = num - 1 else mBinding.vp.currentItem = num - 1
+ private fun setPageNumber(num: Int, smoothScroll: Boolean) {
+ val target = (num - 1).coerceIn(0, (count - 1).coerceAtLeast(0))
+ when (readerMode) {
+ ReaderMode.SINGLE_PAGE -> currentItem = target
+ ReaderMode.CONTINUOUS -> {
+ currentItem = target
+ mBinding.continuousPages.scrollToPosition(target)
+ }
+ ReaderMode.PAGED -> mBinding.vp.setCurrentItem(
+ target,
+ smoothScroll
+ )
+ }
}
private fun getImgBitmap(position: Int): Bitmap? {
@@ -340,6 +412,7 @@ class ViewMangaActivity : ToolsBoxActivity() {
}
private fun loadOneImg(hidePanel: Boolean = true) {
+ mBinding.vone.onei.resetImageTransform()
if(dlZip2View) mBinding.vone.onei.apply { post { setImageBitmap(getImgBitmap(currentItem)) } }
else {
val url = imgUrls.getOrNull(currentItem)
@@ -364,14 +437,14 @@ class ViewMangaActivity : ToolsBoxActivity() {
@SuppressLint("SetTextI18n")
private fun prepareItems() {
if (count <= 0) return
+ mBinding.vone.onei.setOnTapRegionListener(::onImageTapped)
prepareVP()
prepareInfoBar(count)
- if (notUseVP) loadOneImg() else prepareIdBtVH()
toolsBox.dp2px(67)?.let { setIdPosition(it) }
prepareIdBtVolTurn()
+ prepareIdBtVH()
prepareIdBtVP()
prepareIdBtLR()
- readerPrepared = true
}
private fun prepareIdBtLR() {
@@ -388,6 +461,7 @@ class ViewMangaActivity : ToolsBoxActivity() {
private fun prepareIdBtVP() {
mBinding.infcard.idtbvp.apply { post {
isChecked = notUseVP
+ isEnabled = !verticalReading
setOnClickListener {
if (mBinding.infcard.idtbvp.isChecked) p["noAnimation"] = "true"
else p["noAnimation"] = "false"
@@ -397,25 +471,107 @@ class ViewMangaActivity : ToolsBoxActivity() {
}
private fun prepareVP() {
- if (notUseVP) {
- mBinding.vp.apply { post { visibility = View.INVISIBLE } }
- mBinding.vone.root.apply { post { visibility = View.VISIBLE } }
- } else {
- mBinding.vp.apply { post {
- visibility = View.VISIBLE
- vpAdapter = ViewData(this).RecyclerViewAdapter()
- adapter = vpAdapter
+ mBinding.vone.root.visibility = if (readerMode == ReaderMode.SINGLE_PAGE) View.VISIBLE else View.GONE
+ mBinding.vp.visibility = if (readerMode == ReaderMode.PAGED) View.VISIBLE else View.GONE
+ mBinding.continuousPages.visibility = if (readerMode == ReaderMode.CONTINUOUS) View.VISIBLE else View.GONE
+
+ when (readerMode) {
+ ReaderMode.SINGLE_PAGE -> Unit
+ ReaderMode.PAGED -> mBinding.vp.apply {
+ layoutDirection = if (r2l) {
+ View.LAYOUT_DIRECTION_RTL
+ } else {
+ View.LAYOUT_DIRECTION_LTR
+ }
+ pagedAdapter = PagedMangaAdapter(
+ itemCountProvider = { count },
+ bindImage = ::bindPageImage
+ )
+ adapter = pagedAdapter
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
- val pos = if (r2l) count - position - 1 else position
- preloadAround(pos)
+ preloadAround(position)
updateSeekBar()
super.onPageSelected(position)
}
})
- if (r2l) currentItem = count - 1
- } }
- mBinding.vone.root.apply { post { visibility = View.INVISIBLE } }
+ }
+ ReaderMode.CONTINUOUS -> prepareContinuousReader()
+ }
+ }
+
+ private fun prepareContinuousReader() {
+ continuousAdapter = ContinuousMangaAdapter(
+ itemCountProvider = { count },
+ bindImage = ::bindPageImage
+ )
+ mBinding.continuousPages.apply {
+ adapter = continuousAdapter
+ itemAnimator = null
+ addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ val manager = recyclerView.layoutManager as? LinearLayoutManager ?: return
+ val firstVisible = manager.findFirstVisibleItemPosition()
+ if (firstVisible != RecyclerView.NO_POSITION) {
+ currentItem = firstVisible
+ syncSeekBarOnly()
+ preloadAround(firstVisible)
+ }
+ }
+ })
+ }
+ }
+
+ private fun notifyPagesInserted(positionStart: Int, itemCount: Int) {
+ if (itemCount <= 0) return
+ pagedAdapter?.notifyItemRangeInserted(positionStart, itemCount)
+ continuousAdapter?.notifyItemRangeInserted(positionStart, itemCount)
+ }
+
+ private fun bindPageImage(imageView: ScaleImageView, position: Int) {
+ imageView.tag = position
+ imageView.setOnTapRegionListener(::onImageTapped)
+ if (dlZip2View) loadZipImage(imageView, position)
+ else loadNetworkImage(imageView, position)
+ }
+
+ private fun onImageTapped(region: ScaleImageView.TapRegion) {
+ when (region) {
+ ScaleImageView.TapRegion.PREVIOUS -> {
+ if (readerMode == ReaderMode.CONTINUOUS) pagesManager.goBackward()
+ else pagesManager.toPreviousPage()
+ }
+ ScaleImageView.TapRegion.CENTER -> pagesManager.manageInfo()
+ ScaleImageView.TapRegion.NEXT -> {
+ if (readerMode == ReaderMode.CONTINUOUS) pagesManager.goForward()
+ else pagesManager.toNextPage()
+ }
+ }
+ }
+
+ private fun loadNetworkImage(imageView: ScaleImageView, position: Int) {
+ val url = imgUrls.getOrNull(position)
+ if (url.isNullOrBlank()) {
+ imageView.setImageResource(R.drawable.ic_dl)
+ return
+ }
+ Glide.with(this)
+ .load(toolsBox.resolution.wrap(url))
+ .placeholder(R.drawable.ic_dl)
+ .dontAnimate()
+ .into(imageView)
+ preloadAround(position)
+ }
+
+ private fun loadZipImage(imageView: ScaleImageView, position: Int) {
+ imageView.setImageResource(R.drawable.ic_dl)
+ imageExecutor.execute {
+ val bitmap = getImgBitmap(position)
+ imageView.post {
+ if (!isFinishing && !isDestroyed && imageView.tag == position) {
+ imageView.setImageBitmap(bitmap)
+ }
+ }
}
}
@@ -459,28 +615,18 @@ class ViewMangaActivity : ToolsBoxActivity() {
} }
mBinding.oneinfo.inftitle.isearch.apply { post {
visibility = View.INVISIBLE
- setOnClickListener {
- this@ViewMangaActivity.handler.sendEmptyMessage(3)
- }
+ setOnClickListener { overlayController.toggleDrawer() }
} }
mBinding.oneinfo.inftxtprogress.apply { post { text = "$pageNum/$size" } }
}
private fun prepareIdBtVH() {
mBinding.infcard.idtbvh.apply { post {
- isChecked = p["vertical"] == "true"
+ isChecked = verticalReading
setOnClickListener {
- if (mBinding.infcard.idtbvh.isChecked) {
- mBinding.vp.apply { post { orientation = ViewPager2.ORIENTATION_VERTICAL } }
- p["vertical"] = "true"
- } else {
- mBinding.vp.apply { post { orientation = ViewPager2.ORIENTATION_HORIZONTAL } }
- p["vertical"] = "false"
- }
+ p["vertical"] = isChecked.toString()
+ Toast.makeText(this@ViewMangaActivity, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
- if (isChecked) mBinding.vp.apply { post {
- orientation = ViewPager2.ORIENTATION_VERTICAL
- } }
} }
}
@@ -532,52 +678,24 @@ class ViewMangaActivity : ToolsBoxActivity() {
} }
}
- @Deprecated("Deprecated in Java")
- override fun onBackPressed() {
- tt.canDo = false
- wm?.get()?.mBinding?.w?.goBack()
- super.onBackPressed()
- }
-
override fun onDestroy() {
- tt.canDo = false
- handler.removeCallbacksAndMessages(null)
- if (streamUrl != null) streamChapterUrl = null
- super.onDestroy()
- }
-
- inner class ViewData(itemView: View) : RecyclerView.ViewHolder(itemView) {
- inner class RecyclerViewAdapter :
- RecyclerView.Adapter() {
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewData {
- return ViewData(
- LayoutInflater.from(parent.context)
- .inflate(R.layout.page_imgview, parent, false)
- )
- }
-
- @SuppressLint("ClickableViewAccessibility", "SetTextI18n")
- override fun onBindViewHolder(holder: ViewData, position: Int) {
- val pos = if (r2l) count - position - 1 else position
- holder.itemView.findViewById(R.id.onei)?.let { oneImage ->
- if(dlZip2View) getImgBitmap(pos)?.let {
- //Glide.with(this@ViewMangaActivity).load(it).placeholder(R.drawable.bg_comment).into(holder.itemView.onei)
- oneImage.setImageBitmap(it)
- }
- else imgUrls.getOrNull(pos)?.let { url ->
- Glide.with(this@ViewMangaActivity)
- .load(toolsBox.resolution.wrap(url)).placeholder(R.drawable.ic_dl)
- .dontAnimate().timeout(10000)
- .into(oneImage)
- preloadAround(pos)
- } ?: oneImage.setImageResource(R.drawable.ic_dl)
- }
- }
-
- override fun getItemCount(): Int {
- return count
- }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ backInvokedCallback?.let(onBackInvokedDispatcher::unregisterOnBackInvokedCallback)
}
+ backInvokedCallback = null
+ overlayController.close()
+ imageExecutor.shutdownNow()
+ pagedAdapter = null
+ continuousAdapter = null
+ mBinding.vp.adapter = null
+ mBinding.continuousPages.adapter = null
+ dialog?.dismiss()
+ dialog = null
+ mBinding.wcollector.destroy()
+ if (userRequestedExit && !dlZip2View) wm?.get()?.mBinding?.w?.goBack()
+ if (streamUrl != null) streamChapterUrl = null
+ if (va?.get() === this) va = null
+ super.onDestroy()
}
fun showSettings() {
@@ -603,62 +721,17 @@ class ViewMangaActivity : ToolsBoxActivity() {
).setDuration(233).start()
clicked = false
mBinding.oneinfo.infseek.postDelayed({
+ if (isFinishing || isDestroyed) return@postDelayed
mBinding.oneinfo.infseek.visibility = View.INVISIBLE
mBinding.oneinfo.inftitle.isearch.visibility = View.INVISIBLE
}, 300)
- handler.sendEmptyMessage(1)
- }
-
- class MyHandler(
- private val toolsBox: ToolsBox
- ) : Handler(Looper.myLooper()!!) {
- private var infoShown = false
- private var delta = -1f
- get() {
- if (field < 0) field = va?.get()?.infoDrawerDelta ?: 0f
- return field
- }
-
- @SuppressLint("SimpleDateFormat", "SetTextI18n")
- override fun handleMessage(msg: Message) {
- super.handleMessage(msg)
- when (msg.what) {
- 1 -> if (infoShown) {
- hideInfCard(); infoShown = false
- }
- 2 -> if (!infoShown) {
- showInfCard(); infoShown = true
- }
- 3 -> infoShown = if (infoShown) {
- hideInfCard(); false
- } else {
- showInfCard(); true
- }
- 22 -> (toolsBox.zis as? ViewMangaActivity)?.mBinding?.infcard?.idtime?.apply { post {
- text = SimpleDateFormat("HH:mm")
- .format(Date()) + toolsBox.week + toolsBox.netInfo
- } }
- }
- }
-
- private fun showInfCard() {
- Log.d("MyVM", "showInfCard delta $delta")
- va?.get()?.mBinding?.infcard?.apply {
- ObjectAnimator.ofFloat(idc, "alpha", 0.3F, 0.8F).setDuration(233).start()
- ObjectAnimator.ofFloat(root, "translationY", delta, 0F).setDuration(233).start()
- }
- }
-
- private fun hideInfCard() {
- Log.d("MyVM", "hideInfCard delta $delta")
- va?.get()?.mBinding?.infcard?.apply {
- ObjectAnimator.ofFloat(idc, "alpha", 0.8F, 0.3F).setDuration(233).start()
- ObjectAnimator.ofFloat(root, "translationY", 0F, delta).setDuration(233).start()
- }
- }
+ overlayController.hideDrawer()
}
companion object {
+ const val FIRST_PAGE = -1
+ const val LAST_PAGE = -2
+
var va: WeakReference? = null
var imgUrls = arrayOf()
var zipFile: File? = null
@@ -671,9 +744,15 @@ class ViewMangaActivity : ToolsBoxActivity() {
var nextChapterUrl: String? = null
var previousChapterUrl: String? = null
var zipPosition = 0
- var zipList: Array? = null
+ var zipList: Array? = null
var cd: File? = null
- var pn = -1
+ var pn = FIRST_PAGE
var streamChapterUrl: String? = null
}
+
+ private enum class ReaderMode {
+ SINGLE_PAGE,
+ PAGED,
+ CONTINUOUS,
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/top/fumiama/copymangaweb/activity/reader/MangaPageAdapters.kt b/app/src/main/java/top/fumiama/copymangaweb/activity/reader/MangaPageAdapters.kt
new file mode 100644
index 0000000..040517e
--- /dev/null
+++ b/app/src/main/java/top/fumiama/copymangaweb/activity/reader/MangaPageAdapters.kt
@@ -0,0 +1,66 @@
+package top.fumiama.copymangaweb.activity.reader
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import top.fumiama.copymangaweb.R
+import top.fumiama.copymangaweb.view.ScaleImageView
+
+class PagedMangaAdapter(
+ private val itemCountProvider: () -> Int,
+ private val bindImage: (ScaleImageView, Int) -> Unit,
+) : RecyclerView.Adapter() {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PageViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.page_imgview, parent, false)
+ return PageViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: PageViewHolder, position: Int) {
+ holder.image.resetImageTransform()
+ bindImage(holder.image, position)
+ }
+
+ override fun onViewRecycled(holder: PageViewHolder) {
+ holder.image.setOnTapRegionListener(null)
+ holder.image.setImageDrawable(null)
+ super.onViewRecycled(holder)
+ }
+
+ override fun getItemCount(): Int = itemCountProvider()
+
+ class PageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val image: ScaleImageView = itemView.findViewById(R.id.onei)
+ }
+}
+
+class ContinuousMangaAdapter(
+ private val itemCountProvider: () -> Int,
+ private val bindImage: (ScaleImageView, Int) -> Unit,
+) : RecyclerView.Adapter() {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PageViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.page_img_continuous, parent, false)
+ return PageViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: PageViewHolder, position: Int) {
+ holder.image.resetImageTransform()
+ bindImage(holder.image, position)
+ }
+
+ override fun onViewRecycled(holder: PageViewHolder) {
+ holder.image.setOnTapRegionListener(null)
+ holder.image.setImageDrawable(null)
+ super.onViewRecycled(holder)
+ }
+
+ override fun getItemCount(): Int = itemCountProvider()
+
+ class PageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val image: ScaleImageView = itemView.findViewById(R.id.pageImage)
+ }
+}
diff --git a/app/src/main/java/top/fumiama/copymangaweb/activity/reader/ReaderOverlayController.kt b/app/src/main/java/top/fumiama/copymangaweb/activity/reader/ReaderOverlayController.kt
new file mode 100644
index 0000000..7daeff2
--- /dev/null
+++ b/app/src/main/java/top/fumiama/copymangaweb/activity/reader/ReaderOverlayController.kt
@@ -0,0 +1,70 @@
+package top.fumiama.copymangaweb.activity.reader
+
+import android.animation.ObjectAnimator
+import android.annotation.SuppressLint
+import android.os.Handler
+import android.os.Looper
+import android.text.format.DateFormat
+import android.util.Log
+import top.fumiama.copymangaweb.activity.ViewMangaActivity
+import top.fumiama.copymangaweb.databinding.ActivityViewmangaBinding
+import top.fumiama.copymangaweb.tool.ToolsBox
+import java.util.Date
+
+class ReaderOverlayController(
+ private val activity: ViewMangaActivity,
+ private val binding: ActivityViewmangaBinding,
+ private val toolsBox: ToolsBox,
+ private val drawerOffset: () -> Float,
+) {
+ private val handler = Handler(Looper.getMainLooper())
+ private val clockFormat = DateFormat.getTimeFormat(activity)
+ private var drawerVisible = false
+
+ private val updateStatus = object : Runnable {
+ @SuppressLint("SetTextI18n")
+ override fun run() {
+ if (activity.isFinishing || activity.isDestroyed) return
+ binding.infcard.idtime.text = "${clockFormat.format(Date())}${toolsBox.week}${toolsBox.netInfo}"
+ handler.postDelayed(this, STATUS_UPDATE_INTERVAL_MS)
+ }
+ }
+
+ fun start() {
+ handler.post(updateStatus)
+ }
+
+ fun toggleDrawer() {
+ if (drawerVisible) hideDrawer() else showDrawer()
+ }
+
+ fun hideDrawer() {
+ if (!drawerVisible) return
+ val offset = drawerOffset()
+ Log.d("ReaderOverlay", "hide drawer offset=$offset")
+ binding.infcard.apply {
+ ObjectAnimator.ofFloat(idc, "alpha", idc.alpha, 0.3f).setDuration(ANIMATION_DURATION_MS).start()
+ ObjectAnimator.ofFloat(root, "translationY", root.translationY, offset).setDuration(ANIMATION_DURATION_MS).start()
+ }
+ drawerVisible = false
+ }
+
+ private fun showDrawer() {
+ val offset = drawerOffset()
+ Log.d("ReaderOverlay", "show drawer offset=$offset")
+ binding.infcard.apply {
+ ObjectAnimator.ofFloat(idc, "alpha", idc.alpha, 0.8f).setDuration(ANIMATION_DURATION_MS).start()
+ ObjectAnimator.ofFloat(root, "translationY", root.translationY, 0f).setDuration(ANIMATION_DURATION_MS).start()
+ }
+ drawerVisible = true
+ }
+
+ fun close() {
+ handler.removeCallbacksAndMessages(null)
+ }
+
+ companion object {
+ private const val STATUS_UPDATE_INTERVAL_MS = 3_000L
+ private const val ANIMATION_DURATION_MS = 233L
+ }
+}
diff --git a/app/src/main/java/top/fumiama/copymangaweb/activity/template/ToolsBoxActivity.kt b/app/src/main/java/top/fumiama/copymangaweb/activity/template/ToolsBoxActivity.kt
index 2725f57..12ccd8b 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/activity/template/ToolsBoxActivity.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/activity/template/ToolsBoxActivity.kt
@@ -16,7 +16,6 @@ open class ToolsBoxActivity: Activity(), LifecycleOwner {
super.onCreate(savedInstanceState)
lifecycleRegistry = LifecycleRegistry(this)
lifecycleRegistry.currentState = Lifecycle.State.CREATED
- lifecycleRegistry.currentState = Lifecycle.State.STARTED
toolsBox = ToolsBox(WeakReference(this))
}
diff --git a/app/src/main/java/top/fumiama/copymangaweb/handler/DlHandler.kt b/app/src/main/java/top/fumiama/copymangaweb/handler/DlHandler.kt
index a5688ec..06dd4f9 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/handler/DlHandler.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/handler/DlHandler.kt
@@ -20,10 +20,12 @@ class DlHandler(activity: DlActivity, looper: Looper) : Handler(looper) {
@SuppressLint("SetTextI18n")
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
+ val chapterIndex = msg.arg1
+ val pageNumber = msg.arg2
when (msg.what) {
- -2 -> d?.setLayouts()
- 1 -> {
- d?.tbtnlist?.get(msg.arg1)?.apply { post {
+ INITIALIZE_LAYOUTS -> d?.setLayouts()
+ CHAPTER_DOWNLOAD_SUCCEEDED -> {
+ d?.tbtnlist?.get(chapterIndex)?.apply { post {
setBackgroundResource(R.drawable.rndbg_checked)
isChecked = false
d?.updateProgressBar()
@@ -39,15 +41,15 @@ class DlHandler(activity: DlActivity, looper: Looper) : Handler(looper) {
}
} }
}
- -1 -> {
- d?.tbtnlist?.get(msg.arg1)?.apply { post {
+ CHAPTER_DOWNLOAD_FAILED -> {
+ d?.tbtnlist?.get(chapterIndex)?.apply { post {
setBackgroundResource(R.drawable.rndbg_error)
d!!.dldChapter--
- Toast.makeText(d, "下载${d?.tbtnlist?.get(msg.arg1)?.textOn}失败", Toast.LENGTH_SHORT).show()
+ Toast.makeText(d, "下载${d?.tbtnlist?.get(chapterIndex)?.textOn}失败", Toast.LENGTH_SHORT).show()
d?.updateProgressBar()
} }
}
- 4 -> {
+ TOGGLE_SELECT_ALL -> {
d?.mBinding?.dldlbar?.pdwn?.apply { post { progress = 0 } }
val selectDownloaded = d?.multiSelect?:false
if (d?.haveSElectAll == true) {
@@ -81,27 +83,27 @@ class DlHandler(activity: DlActivity, looper: Looper) : Handler(looper) {
text = "${d?.dldChapter}/${d?.checkedChapter}"
} }
}
- 5 -> {
- setSize(msg.arg2, msg.arg1)
- d?.updateProgressBar(msg.arg2, size)
- if (!(msg.obj as Boolean)) {
- Toast.makeText(d, "下载${d?.tbtnlist?.get(msg.arg1)?.textOn}的第${msg.arg2}页失败", Toast.LENGTH_SHORT).show()
+ PAGE_DOWNLOAD_FINISHED -> {
+ setSize(pageNumber, chapterIndex)
+ d?.updateProgressBar(pageNumber, size)
+ if (msg.obj != true) {
+ Toast.makeText(d, "下载${d?.tbtnlist?.get(chapterIndex)?.textOn}的第${pageNumber}页失败", Toast.LENGTH_SHORT).show()
}else{
val progressTxt = d?.mBinding?.dldlbar?.tdwn?.text.toString()
d?.mBinding?.dldlbar?.tdwn?.apply { post {
- text = "${progressTxt.substringBefore(' ')} 的 ${msg.arg2}/${size} 页"
+ text = "${progressTxt.substringBefore(' ')} 的 ${pageNumber}/${size} 页"
} }
}
}
- 6 -> d?.mBinding?.dldlbar?.tdwn?.apply { post { text = "${d?.dldChapter}/${d?.checkedChapter}" } }
- 7 -> d?.deleteChapters()
- 8 -> d?.resources?.getColor(R.color.colorBlue)?.let { d?.mBinding?.dldlbar?.cdwn?.apply { post {
+ UPDATE_CHAPTER_PROGRESS -> d?.mBinding?.dldlbar?.tdwn?.apply { post { text = "${d?.dldChapter}/${d?.checkedChapter}" } }
+ DELETE_SELECTED_CHAPTERS -> d?.deleteChapters()
+ SET_DOWNLOAD_CARD_BLUE -> d?.resources?.getColor(R.color.colorBlue)?.let { d?.mBinding?.dldlbar?.cdwn?.apply { post {
setCardBackgroundColor(it)
} } }
- 9 -> d?.resources?.getColor(R.color.colorRed)?.let { d?.mBinding?.dldlbar?.cdwn?.apply { post {
+ SET_DOWNLOAD_CARD_RED -> d?.resources?.getColor(R.color.colorRed)?.let { d?.mBinding?.dldlbar?.cdwn?.apply { post {
setCardBackgroundColor(it)
} } }
- 10 -> Toast.makeText(d, "下载${d?.tbtnlist?.get(msg.arg1)?.textOn}的第${msg.arg2}页失败,尝试重新下载...", Toast.LENGTH_SHORT).show()
+ PAGE_DOWNLOAD_RETRYING -> Toast.makeText(d, "下载${d?.tbtnlist?.get(chapterIndex)?.textOn}的第${pageNumber}页失败,尝试重新下载...", Toast.LENGTH_SHORT).show()
}
}
private fun setSize(pageNow: Int, tbtnNo: Int){
@@ -110,4 +112,17 @@ class DlHandler(activity: DlActivity, looper: Looper) : Handler(looper) {
refreshSize = false
}else if(pageNow == size) refreshSize = true
}
+
+ companion object {
+ const val INITIALIZE_LAYOUTS = -2
+ const val CHAPTER_DOWNLOAD_FAILED = -1
+ const val CHAPTER_DOWNLOAD_SUCCEEDED = 1
+ const val TOGGLE_SELECT_ALL = 4
+ const val PAGE_DOWNLOAD_FINISHED = 5
+ const val UPDATE_CHAPTER_PROGRESS = 6
+ const val DELETE_SELECTED_CHAPTERS = 7
+ const val SET_DOWNLOAD_CARD_BLUE = 8
+ const val SET_DOWNLOAD_CARD_RED = 9
+ const val PAGE_DOWNLOAD_RETRYING = 10
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/top/fumiama/copymangaweb/handler/DlLHandler.kt b/app/src/main/java/top/fumiama/copymangaweb/handler/DlLHandler.kt
deleted file mode 100644
index 9aae211..0000000
--- a/app/src/main/java/top/fumiama/copymangaweb/handler/DlLHandler.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package top.fumiama.copymangaweb.handler
-
-import android.os.Handler
-import android.os.Looper
-import android.os.Message
-import top.fumiama.copymangaweb.activity.DlListActivity
-import java.io.File
-import java.lang.ref.WeakReference
-
-
-class DlLHandler(looper: Looper, activity: DlListActivity): Handler(looper) {
- private val dll = WeakReference(activity)
- override fun handleMessage(msg: Message) {
- super.handleMessage(msg)
- when(msg.what){
- 1 -> dll.get()?.checkDir(msg.obj as File)
- 2 -> dll.get()?.rmrf(msg.obj as File)
- 3 -> dll.get()?.scanFile(msg.obj as File)
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/top/fumiama/copymangaweb/handler/MainHandler.kt b/app/src/main/java/top/fumiama/copymangaweb/handler/MainHandler.kt
index b0590b1..c1c6fdc 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/handler/MainHandler.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/handler/MainHandler.kt
@@ -28,12 +28,17 @@ class MainHandler(looper: Looper): Handler(looper) {
}
SET_LOADING_DIALOG_TEXT -> {
val t = msg.obj as? String?:return
- dialog?.findViewById(R.id.tunz)?.apply { post {
- text = t
- } }
+ dialog?.findViewById(R.id.tunz)?.text = t
}
}
}
+
+ fun dispose() {
+ removeCallbacksAndMessages(null)
+ dialog?.dismiss()
+ dialog = null
+ }
+
companion object {
const val SHOW_LOADING_DIALOG = 7
const val HIDE_LOADING_DIALOG = 8
diff --git a/app/src/main/java/top/fumiama/copymangaweb/handler/TimeThread.kt b/app/src/main/java/top/fumiama/copymangaweb/handler/TimeThread.kt
deleted file mode 100644
index 19dce96..0000000
--- a/app/src/main/java/top/fumiama/copymangaweb/handler/TimeThread.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package top.fumiama.copymangaweb.handler
-
-import android.os.Handler
-
-class TimeThread(private val handler: Handler, private val msg: Int) : Thread() {
- var canDo = false
- override fun run() {
- while (canDo) {
- try {
- handler.sendEmptyMessage(msg)
- sleep(3000)
- } catch (e: InterruptedException) {
- e.printStackTrace()
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/top/fumiama/copymangaweb/tool/DownloadTools.kt b/app/src/main/java/top/fumiama/copymangaweb/tool/DownloadTools.kt
index e16dd9f..30d90d2 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/tool/DownloadTools.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/tool/DownloadTools.kt
@@ -3,35 +3,31 @@ package top.fumiama.copymangaweb.tool
import android.util.Log
import java.net.HttpURLConnection
import java.net.URL
-import java.util.concurrent.Callable
-import java.util.concurrent.FutureTask
class DownloadTools {
fun getHttpContent(u: String, refer: String? = null, ua: String? = null): ByteArray? {
Log.d("Mydl", "getHttp: $u")
- var ret: ByteArray? = null
- val task = FutureTask(Callable {
- try {
- val connection = URL(u).openConnection() as HttpURLConnection
- connection.requestMethod = "GET"
- connection.connectTimeout = 10000
- connection.readTimeout = 10000
- refer?.let { connection.setRequestProperty("referer", it) }
- ua?.let { connection.setRequestProperty("User-agent", it) }
+ val connection = try {
+ URL(u).openConnection() as HttpURLConnection
+ } catch (e: Exception) {
+ Log.e("Mydl", "Unable to open connection: $u", e)
+ return null
+ }
- ret = connection.inputStream.readBytes()
- connection.disconnect()
- } catch (ex: Exception) {
- ex.printStackTrace()
- }
- return@Callable ret
- })
- Thread(task).start()
return try {
- task.get()
- } catch (ex: Exception) {
- ex.printStackTrace()
+ connection.run {
+ requestMethod = "GET"
+ connectTimeout = 10000
+ readTimeout = 10000
+ refer?.let { setRequestProperty("referer", it) }
+ ua?.let { setRequestProperty("User-agent", it) }
+ inputStream.use { it.readBytes() }
+ }
+ } catch (e: Exception) {
+ Log.e("Mydl", "Download failed: $u", e)
null
+ } finally {
+ connection.disconnect()
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/top/fumiama/copymangaweb/tool/PagesManager.kt b/app/src/main/java/top/fumiama/copymangaweb/tool/PagesManager.kt
index dcb02b2..0c508f4 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/tool/PagesManager.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/tool/PagesManager.kt
@@ -4,63 +4,86 @@ import android.content.Intent
import android.widget.Toast
import top.fumiama.copymangaweb.activity.MainActivity.Companion.wm
import top.fumiama.copymangaweb.activity.ViewMangaActivity
-import java.io.File
import java.lang.ref.WeakReference
class PagesManager(w: WeakReference) {
- val v = w.get()
+ private val activity = w
+ private val v get() = activity.get()
private var isEndL = false
private var isEndR = false
fun toPreviousPage(){ toPage(v?.r2l==true) }
fun toNextPage(){ toPage(v?.r2l!=true) }
+ fun goBackward() = toPage(false)
+ fun goForward() = toPage(true)
private fun judgePrevious() = (v?.pageNum ?: 0) > 1
private fun judgeNext() = (v?.pageNum ?: 0) < (v?.count ?: 0)
private fun toPage(goNext:Boolean){
if (v?.clicked == false) {
if (if(goNext)judgeNext() else judgePrevious()) {
if(goNext) {
- v.scrollForward()
+ v?.scrollForward()
isEndR = false
} else {
- v.scrollBack()
+ v?.scrollBack()
isEndL = false
}
} else {
- val chapterUrl = if(goNext) ViewMangaActivity.nextChapterUrl else ViewMangaActivity.previousChapterUrl
- if (chapterUrl != null) {
+ if (v?.dlZip2View == true) {
+ switchZipChapter(goNext)
+ } else {
+ val chapterUrl = if(goNext) ViewMangaActivity.nextChapterUrl else ViewMangaActivity.previousChapterUrl
+ if (chapterUrl == null) {
+ showReachedEnd()
+ return
+ }
if (if(goNext)isEndR else isEndL) {
- if(!goNext) ViewMangaActivity.pn = -2
+ setChapterStartPage(goNext)
wm?.get()?.mBinding?.w?.apply { post {
loadUrl("javascript:invoke.clickClass(\"comicControlBottomTopClick\",${if(goNext)1 else 0});")
} }
- v.tt.canDo = false
- v.finish()
+ v?.finish()
} else doubleTapToast(goNext)
- } else {
- val newZipPosition = ViewMangaActivity.zipPosition + (if(goNext) 1 else -1)
- if(v.dlZip2View && newZipPosition >= 0 && newZipPosition <
- (ViewMangaActivity.zipList?.size ?: 0)){
- if (if(goNext)isEndR else isEndL){
- if(!goNext) ViewMangaActivity.pn = -2
- ViewMangaActivity.zipPosition = newZipPosition
- ViewMangaActivity.titleText = ViewMangaActivity.zipList?.get(newZipPosition) ?: "null"
- ViewMangaActivity.zipFile = File(ViewMangaActivity.cd, ViewMangaActivity.titleText)
- v.startActivity(Intent(v, ViewMangaActivity::class.java))
- v.tt.canDo = false
- v.finish()
- }else doubleTapToast(goNext)
- }
- else Toast.makeText(
- v.applicationContext,
- "已经到头了~",
- Toast.LENGTH_SHORT
- ).show()
}
}
} else v?.hideSettings()
}
+
+ private fun switchZipChapter(goNext: Boolean) {
+ val chapters = ViewMangaActivity.zipList.orEmpty()
+ val newPosition = ViewMangaActivity.zipPosition + if (goNext) 1 else -1
+ val chapter = chapters.getOrNull(newPosition)
+ if (chapter == null) {
+ showReachedEnd()
+ return
+ }
+ if (!(if (goNext) isEndR else isEndL)) {
+ doubleTapToast(goNext)
+ return
+ }
+
+ val reader = v ?: return
+ setChapterStartPage(goNext)
+ ViewMangaActivity.zipPosition = newPosition
+ ViewMangaActivity.titleText = chapter.nameWithoutExtension
+ ViewMangaActivity.zipFile = chapter
+ reader.startActivity(Intent(reader, ViewMangaActivity::class.java))
+ reader.finish()
+ }
+
+ private fun showReachedEnd() {
+ Toast.makeText(v?.applicationContext, "已经到头了~", Toast.LENGTH_SHORT).show()
+ }
+
+ private fun setChapterStartPage(goNext: Boolean) {
+ ViewMangaActivity.pn = if (goNext) {
+ ViewMangaActivity.FIRST_PAGE
+ } else {
+ ViewMangaActivity.LAST_PAGE
+ }
+ }
+
fun manageInfo(){
- if (v?.clicked == false) v.showSettings() else v?.hideSettings()
+ if (v?.clicked == false) v?.showSettings() else v?.hideSettings()
}
private fun doubleTapToast(goNext: Boolean){
val hint = if(goNext) "下" else "上"
diff --git a/app/src/main/java/top/fumiama/copymangaweb/view/ScaleImageView.kt b/app/src/main/java/top/fumiama/copymangaweb/view/ScaleImageView.kt
index c63bd86..4f36648 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/view/ScaleImageView.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/view/ScaleImageView.kt
@@ -15,8 +15,6 @@ import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.widget.ImageView
import top.fumiama.copymangaweb.activity.ViewMangaActivity
-import top.fumiama.copymangaweb.tool.PagesManager
-import java.lang.ref.WeakReference
import java.util.*
import kotlin.math.sqrt
@@ -26,6 +24,28 @@ import kotlin.math.sqrt
* @author clifford
*/
class ScaleImageView : ImageView {
+ enum class TapRegion {
+ PREVIOUS,
+ CENTER,
+ NEXT,
+ }
+
+ private var onTapRegionListener: ((TapRegion) -> Unit)? = null
+
+ fun setOnTapRegionListener(listener: ((TapRegion) -> Unit)?) {
+ onTapRegionListener = listener
+ }
+
+ fun resetImageTransform() {
+ cancelAllAnimator()
+ mOuterMatrix.reset()
+ pinchMode = PINCH_MODE_FREE
+ mLastMovePoint.set(0f, 0f)
+ mScaleCenter.set(0f, 0f)
+ mScaleBase = 0f
+ invalidate()
+ }
+
////////////////////////////////监听器////////////////////////////////
/**
* 外界点击事件
@@ -672,24 +692,14 @@ class ScaleImageView : ImageView {
return true
}
- var v :WeakReference? = null
- var pm:PagesManager? = null
override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
- if(v == null) {
- v = ViewMangaActivity.va
- v?.let { pm = PagesManager(it) }
- }
- //触发点击
- if (mOnClickListener != null) {
- mOnClickListener!!.onClick(this@ScaleImageView)
- }
- (event.x / width).let {
- when {
- it <= 1.0 / 3.0 -> pm?.toPreviousPage()
- it <= 2.0 / 3.0 -> pm?.manageInfo()
- else -> pm?.toNextPage()
- }
+ mOnClickListener?.onClick(this@ScaleImageView)
+ val region = when {
+ width <= 0 || event.x <= width / 3f -> TapRegion.PREVIOUS
+ event.x <= width * 2f / 3f -> TapRegion.CENTER
+ else -> TapRegion.NEXT
}
+ onTapRegionListener?.invoke(region)
return true
}
})
@@ -739,6 +749,7 @@ class ScaleImageView : ImageView {
} else if (action == MotionEvent.ACTION_POINTER_DOWN) {
//停止所有动画
cancelAllAnimator()
+ parent.requestDisallowInterceptTouchEvent(true)
//切换到缩放模式
pinchMode = PINCH_MODE_SCALE
//保存缩放的两个手指
diff --git a/app/src/main/java/top/fumiama/copymangaweb/web/JSHidden.kt b/app/src/main/java/top/fumiama/copymangaweb/web/JSHidden.kt
index 881227d..fc474e4 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/web/JSHidden.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/web/JSHidden.kt
@@ -2,7 +2,6 @@ package top.fumiama.copymangaweb.web
import android.util.Log
import android.webkit.JavascriptInterface
-import top.fumiama.copymangaweb.activity.DlActivity
import top.fumiama.copymangaweb.activity.MainActivity.Companion.mh
import top.fumiama.copymangaweb.activity.MainActivity.Companion.wm
import top.fumiama.copymangaweb.handler.MainHandler
@@ -60,13 +59,8 @@ class JSHidden(
}
@JavascriptInterface
- fun setTitle(title:String){
- Log.d("MyJSH", "Set title: $title")
- DlActivity.comicName = title
- }
- @JavascriptInterface
- fun setFab(content: String){
- wm?.get()?.setFab(content)
+ fun setFab(content: String, sourceUrl: String, comicTitle: String){
+ wm?.get()?.setFab(content, sourceUrl, comicTitle)
}
@JavascriptInterface
fun setLoadingDialog(display: Boolean) {
diff --git a/app/src/main/java/top/fumiama/copymangaweb/web/WebChromeClient.kt b/app/src/main/java/top/fumiama/copymangaweb/web/WebChromeClient.kt
index 4d6be83..4920505 100644
--- a/app/src/main/java/top/fumiama/copymangaweb/web/WebChromeClient.kt
+++ b/app/src/main/java/top/fumiama/copymangaweb/web/WebChromeClient.kt
@@ -51,10 +51,9 @@ class WebChromeClient:WebChromeClient() {
filePathCallback: ValueCallback>?,
fileChooserParams: FileChooserParams?
): Boolean {
- wm?.get()?.apply {
- uploadMessageAboveL = filePathCallback
- openImageChooserActivity()
- }
+ val callback = filePathCallback ?: return false
+ val activity = wm?.get() ?: return false
+ activity.openImageChooserActivity(callback)
return true
}
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_viewmanga.xml b/app/src/main/res/layout/activity_viewmanga.xml
index 4946aea..cbc7b7b 100644
--- a/app/src/main/res/layout/activity_viewmanga.xml
+++ b/app/src/main/res/layout/activity_viewmanga.xml
@@ -25,6 +25,19 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 547bc4c..1df1ed3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,8 +1,8 @@
拷贝Lite
- https://www.copy20.com
- https://www.copy20.com
- https://www.copy20.com/comic
+ https://www.copy3000.com
+ https://www.copy3000.com
+ https://www.copy3000.com/comic
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36 Edg/86.0.622.38