mirror of
https://github.com/fumiama/copymanga.git
synced 2026-07-17 02:40:34 +08:00
v1.6.0
新增 1. 竖向翻页支持瀑布流 优化 1. 阅读器启动、章节图片流式收集与大章节下载稳定性 (#200) by @xuanli20 2. 同步到最新拷贝网址 3. 代码结构 修复 1. 高版本安卓下按返回键直接退出程序而非回退到上个页面 2. 音量键翻页无法切换到下一章节
This commit is contained in:
6
.idea/kotlinc.xml
generated
Normal file
6
.idea/kotlinc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.1.0" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -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"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
|
||||
@@ -2,15 +2,61 @@ javascript:
|
||||
if (typeof (loaded) == "undefined") {
|
||||
var loaded = true;
|
||||
function scanChapters(chapter) {
|
||||
var chapterList = chapter.getElementsByClassName("tab-pane fade show active")[0].getElementsByTagName("ul")[0].getElementsByTagName("a");
|
||||
var activeTab = chapter.getElementsByClassName("tab-pane fade show active")[0];
|
||||
var list = activeTab && activeTab.getElementsByTagName("ul")[0];
|
||||
var chapterList = list ? list.getElementsByTagName("a") : [];
|
||||
var chapterArr = Array();
|
||||
for (var i = 0; i < chapterList.length; i++) {
|
||||
chapterArr.push(JSON.constructor());
|
||||
chapterArr[i]["name"] = chapterList[i].title;
|
||||
chapterArr[i]["url"] = chapterList[i].href;
|
||||
var name = chapterList[i].title || chapterList[i].innerText.trim();
|
||||
var url = chapterList[i].href;
|
||||
if (name && url) chapterArr.push({"name": name, "url": url});
|
||||
}
|
||||
return chapterArr;
|
||||
}
|
||||
var detailScanVersion = 0;
|
||||
function collectChapterGroups() {
|
||||
var container = document.getElementsByClassName("upLoop")[0];
|
||||
if (!container || container.children.length < 2 || container.children.length % 2) return null;
|
||||
var groups = Array();
|
||||
for (var i = 0; i < container.children.length; i += 2) {
|
||||
var name = container.children[i].innerText.trim();
|
||||
var chapters = scanChapters(container.children[i + 1]);
|
||||
if (!name || !chapters.length) return null;
|
||||
groups.push({"name": name, "chapters": chapters});
|
||||
}
|
||||
return groups.length ? groups : null;
|
||||
}
|
||||
function waitForChapterGroups() {
|
||||
var version = ++detailScanVersion;
|
||||
var sourceUrl = location.href;
|
||||
var lastChapterCount = -1;
|
||||
var stableChecks = 0;
|
||||
var startedAt = Date.now();
|
||||
function scan() {
|
||||
if (version !== detailScanVersion || location.href !== sourceUrl) return;
|
||||
var groups = collectChapterGroups();
|
||||
if (groups) {
|
||||
var chapterCount = groups.reduce(function (count, group) {
|
||||
return count + group.chapters.length;
|
||||
}, 0);
|
||||
stableChecks = chapterCount === lastChapterCount ? stableChecks + 1 : 0;
|
||||
lastChapterCount = chapterCount;
|
||||
if (stableChecks >= 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();
|
||||
|
||||
@@ -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<ComicStructure>::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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
private lateinit var mBinding: ActivityDlistBinding
|
||||
private var nullZipDirStr = emptyArray<String>()
|
||||
private var handler: DlLHandler? = null
|
||||
private val fileExecutor = Executors.newSingleThreadExecutor()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -26,80 +26,98 @@ 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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDirectory(directory: File?, entries: List<String>) {
|
||||
mBinding.mylv.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, entries)
|
||||
mBinding.mylv.setOnItemClickListener { _, _, position, _ ->
|
||||
val chosenFile = File(cd, it[position])
|
||||
val chosenFile = File(directory, entries[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])
|
||||
Intent(this, DlListActivity::class.java)
|
||||
.putExtra("title", entries[position])
|
||||
)
|
||||
}
|
||||
chosenFile.name.endsWith(".zip") -> {
|
||||
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 = it[position]
|
||||
ViewMangaActivity.zipPosition = position
|
||||
ViewMangaActivity.zipList = it.toList().toTypedArray()
|
||||
ViewMangaActivity.cd = cd
|
||||
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(cd, it[position])
|
||||
val chosenFile = File(directory, entries[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
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun rmrf(f: File) {
|
||||
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"))
|
||||
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){
|
||||
DlActivity.json = jsonFile.readText()
|
||||
@@ -110,13 +128,16 @@ 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<String> {
|
||||
val invalidFiles = mutableListOf<String>()
|
||||
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 {
|
||||
return try {
|
||||
@@ -124,40 +145,47 @@ class DlListActivity: Activity() {
|
||||
if (!exist) true
|
||||
else {
|
||||
var re = true
|
||||
val zip = ZipInputStream(f.inputStream().buffered())
|
||||
ZipInputStream(f.inputStream().buffered()).use { zip ->
|
||||
var entry = zip.nextEntry
|
||||
while (entry != null) {
|
||||
if (!entry.isDirectory){
|
||||
if(zip.read() == -1 && entry.size == 0L){
|
||||
if (!entry.isDirectory && zip.read() == -1 && entry.size == 0L) {
|
||||
re = false
|
||||
break
|
||||
}
|
||||
}
|
||||
zip.closeEntry()
|
||||
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){_, _ ->}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
|
||||
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,13 +193,11 @@ 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()) {
|
||||
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 {
|
||||
|
||||
@@ -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() {
|
||||
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 super.onBackPressed()
|
||||
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<Uri>()
|
||||
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<Uri>? {
|
||||
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<Array<Uri>>) {
|
||||
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<String>()
|
||||
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<MainActivity>? = null
|
||||
var mh: MainHandler? = null
|
||||
}
|
||||
|
||||
@@ -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<String>()
|
||||
private var vpAdapter: RecyclerView.Adapter<ViewData>? = 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 {
|
||||
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<String>) {
|
||||
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
|
||||
return when (readerMode) {
|
||||
ReaderMode.SINGLE_PAGE, ReaderMode.CONTINUOUS -> currentItem
|
||||
ReaderMode.PAGED -> mBinding.vp.currentItem
|
||||
}
|
||||
|
||||
private fun logicalToPagerPosition(logicalPosition: Int): Int {
|
||||
return if (r2l) count - logicalPosition - 1 else logicalPosition
|
||||
}
|
||||
|
||||
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
|
||||
if (volTurnPage) {
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_VOLUME_UP -> pagesManager.goBackward()
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN -> pagesManager.goForward()
|
||||
else -> return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
||||
scrollForward()
|
||||
flag = true
|
||||
return true
|
||||
}
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
return if(flag) true else super.onKeyDown(keyCode, event)
|
||||
|
||||
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 } }
|
||||
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 {
|
||||
mBinding.vp.apply { post {
|
||||
visibility = View.VISIBLE
|
||||
vpAdapter = ViewData(this).RecyclerViewAdapter()
|
||||
adapter = vpAdapter
|
||||
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,54 +678,26 @@ 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 (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()
|
||||
}
|
||||
|
||||
inner class ViewData(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
inner class RecyclerViewAdapter :
|
||||
RecyclerView.Adapter<ViewData>() {
|
||||
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<ScaleImageView>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showSettings() {
|
||||
mBinding.oneinfo.infseek.visibility = View.VISIBLE
|
||||
mBinding.oneinfo.inftitle.isearch.visibility = View.VISIBLE
|
||||
@@ -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<ViewMangaActivity>? = null
|
||||
var imgUrls = arrayOf<String>()
|
||||
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<String>? = null
|
||||
var zipList: Array<File>? = null
|
||||
var cd: File? = null
|
||||
var pn = -1
|
||||
var pn = FIRST_PAGE
|
||||
var streamChapterUrl: String? = null
|
||||
}
|
||||
|
||||
private enum class ReaderMode {
|
||||
SINGLE_PAGE,
|
||||
PAGED,
|
||||
CONTINUOUS,
|
||||
}
|
||||
}
|
||||
@@ -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<PagedMangaAdapter.PageViewHolder>() {
|
||||
|
||||
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<ContinuousMangaAdapter.PageViewHolder>() {
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,12 +28,17 @@ class MainHandler(looper: Looper): Handler(looper) {
|
||||
}
|
||||
SET_LOADING_DIALOG_TEXT -> {
|
||||
val t = msg.obj as? String?:return
|
||||
dialog?.findViewById<TextView>(R.id.tunz)?.apply { post {
|
||||
text = t
|
||||
} }
|
||||
dialog?.findViewById<TextView>(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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ViewMangaActivity>) {
|
||||
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 {
|
||||
if (v?.dlZip2View == true) {
|
||||
switchZipChapter(goNext)
|
||||
} else {
|
||||
val chapterUrl = if(goNext) ViewMangaActivity.nextChapterUrl else ViewMangaActivity.previousChapterUrl
|
||||
if (chapterUrl != null) {
|
||||
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 "上"
|
||||
|
||||
@@ -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<ViewMangaActivity>? = 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
|
||||
//保存缩放的两个手指
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -51,10 +51,9 @@ class WebChromeClient:WebChromeClient() {
|
||||
filePathCallback: ValueCallback<Array<Uri>>?,
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,19 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/continuousPages"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/vp"
|
||||
android:layout_width="0dp"
|
||||
|
||||
7
app/src/main/res/layout/page_img_continuous.xml
Normal file
7
app/src/main/res/layout/page_img_continuous.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<top.fumiama.copymangaweb.view.ScaleImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/pageImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:contentDescription="@null" />
|
||||
@@ -1,8 +1,8 @@
|
||||
<resources>
|
||||
<string name="app_name">拷贝Lite</string>
|
||||
<string name="web_home">https://www.copy20.com</string>
|
||||
<string name="web_home_www">https://www.copy20.com</string>
|
||||
<string name="web_comic_detail_pc">https://www.copy20.com/comic</string>
|
||||
<string name="web_home">https://www.copy3000.com</string>
|
||||
<string name="web_home_www">https://www.copy3000.com</string>
|
||||
<string name="web_comic_detail_pc">https://www.copy3000.com/comic</string>
|
||||
|
||||
<string name="pc_ua">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</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user