mirror of
https://github.com/fumiama/copymanga.git
synced 2026-07-17 10:56:24 +08:00
optimize: improve chapter reader streaming and download reliability (#200)
This commit is contained in:
@@ -12,44 +12,142 @@ if (typeof (loaded) == "undefined") {
|
||||
return chapterArr;
|
||||
}
|
||||
function smoothLoadChapter(speed, interval) {
|
||||
let prevHeight = document.body.scrollHeight;
|
||||
let lastTime = 0;
|
||||
let ticking = false;
|
||||
let lastHeight = 0;
|
||||
let lastImageCount = 0;
|
||||
let stableCount = 0;
|
||||
let finished = false;
|
||||
let declaredCount = 0;
|
||||
let lastProgressNotify = 0;
|
||||
let lastPushNotify = 0;
|
||||
const pushed = Object.create(null);
|
||||
const startTime = Date.now();
|
||||
|
||||
function getTextByClass(name) {
|
||||
const item = document.getElementsByClassName(name)[0];
|
||||
return item ? item.innerText : "0";
|
||||
}
|
||||
|
||||
function getImages() {
|
||||
const content = document.getElementsByClassName("container-fluid comicContent")[0];
|
||||
return content ? content.getElementsByTagName("li") : [];
|
||||
}
|
||||
|
||||
function safeHref(className, index) {
|
||||
try {
|
||||
const items = document.getElementsByClassName(className);
|
||||
const links = items[index].getElementsByTagName("a");
|
||||
return links[0].href;
|
||||
} catch(e) {
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
|
||||
function getChapterHeader() {
|
||||
var nextChapter = safeHref("comicContent-next", 0);
|
||||
var prevChapter = safeHref("comicContent-prev", 1);
|
||||
if(nextChapter == location.href) nextChapter = "null";
|
||||
if(prevChapter == location.href) prevChapter = "null";
|
||||
return document.title.split(" - ")[1] + " " + location.href.substring(location.href.lastIndexOf("/")+1) + "\n" + nextChapter + "\n" + prevChapter;
|
||||
}
|
||||
|
||||
function notifyMeta() {
|
||||
try { GM.setChapterMeta(getChapterHeader()); } catch(e) {}
|
||||
}
|
||||
|
||||
function imageSrcOf(li) {
|
||||
const img = li.getElementsByTagName("img")[0];
|
||||
if (!img) return "";
|
||||
return img.dataset.src || img.getAttribute("data-src") || img.src || "";
|
||||
}
|
||||
|
||||
function notifyCount(count) {
|
||||
if (count > 0 && count !== declaredCount) {
|
||||
declaredCount = count;
|
||||
try { GM.setChapterCount(count.toString()); } catch(e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function pushNewImages(force) {
|
||||
const images = getImages();
|
||||
const batch = [];
|
||||
for(var i = 0; i < images.length; i++) {
|
||||
const src = imageSrcOf(images[i]);
|
||||
if (src && !pushed[src]) {
|
||||
pushed[src] = true;
|
||||
batch.push(src);
|
||||
}
|
||||
}
|
||||
if (batch.length > 0) {
|
||||
try { GM.appendChapterImages(batch.join("\n")); } catch(e) {}
|
||||
} else if (force) {
|
||||
try { GM.appendChapterImages(""); } catch(e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function finish() {
|
||||
if (finished) return;
|
||||
finished = true;
|
||||
notifyMeta();
|
||||
pushNewImages(true);
|
||||
var images = getImages();
|
||||
var result = getChapterHeader();
|
||||
for(var i = 0; i < images.length; i++) {
|
||||
var src = imageSrcOf(images[i]);
|
||||
if (src) result += "\n" + src;
|
||||
}
|
||||
try { GM.finishStreamingChapter(); } catch(e) {}
|
||||
GM.setLoadingDialog(false);
|
||||
GM.loadChapter(result);
|
||||
}
|
||||
|
||||
function requestTick() {
|
||||
if (!ticking) {
|
||||
if (!ticking && !finished) {
|
||||
ticking = true;
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
}
|
||||
|
||||
function step(timestamp) {
|
||||
if (!lastTime) lastTime = timestamp;
|
||||
const elapsed = timestamp - lastTime;
|
||||
if (elapsed >= interval) {
|
||||
const index = document.getElementsByClassName("comicIndex")[0].innerText;
|
||||
const count = document.getElementsByClassName("comicCount")[0].innerText;
|
||||
GM.setLoadingDialogProgress(index, count);
|
||||
const index = parseInt(getTextByClass("comicIndex")) || 0;
|
||||
const count = parseInt(getTextByClass("comicCount")) || 0;
|
||||
const images = getImages();
|
||||
notifyCount(count);
|
||||
if (timestamp - lastProgressNotify >= 200) {
|
||||
lastProgressNotify = timestamp;
|
||||
try { GM.setLoadingDialogProgress(index.toString(), count.toString()); } catch(e) {}
|
||||
}
|
||||
if (timestamp - lastPushNotify >= 80) {
|
||||
lastPushNotify = timestamp;
|
||||
pushNewImages(false);
|
||||
}
|
||||
window.scrollBy(0, speed);
|
||||
lastTime = timestamp;
|
||||
const currentHeight = document.body.scrollHeight;
|
||||
if (Math.round(window.innerHeight+window.scrollY+0.5) >= currentHeight) { /*避免小数不符无法触发*/
|
||||
if (currentHeight === prevHeight) {
|
||||
var images = document.getElementsByClassName("container-fluid comicContent")[0].getElementsByTagName("li");
|
||||
var nextChapter = document.getElementsByClassName("comicContent-next")[0].getElementsByTagName("a")[0].href;
|
||||
var prevChapter = document.getElementsByClassName("comicContent-prev")[1].getElementsByTagName("a")[0].href;
|
||||
if(nextChapter == location.href) nextChapter = "null";
|
||||
if(prevChapter == location.href) prevChapter = "null";
|
||||
var result = document.title.split(" - ")[1] + " " + location.href.substring(location.href.lastIndexOf("/")+1) + "\n" + nextChapter + "\n" + prevChapter;
|
||||
for(var i = 0; i < images.length; i++) result += "\n" + images[i].getElementsByTagName("img")[0].dataset.src;
|
||||
GM.setLoadingDialog(false);
|
||||
GM.loadChapter(result);
|
||||
|
||||
const height = document.body.scrollHeight;
|
||||
const imageCount = images.length;
|
||||
const atBottom = Math.round(window.innerHeight + window.scrollY + 1) >= height;
|
||||
if (height === lastHeight && imageCount === lastImageCount) stableCount++;
|
||||
else stableCount = 0;
|
||||
lastHeight = height;
|
||||
lastImageCount = imageCount;
|
||||
|
||||
if ((count > 0 && imageCount >= count && (index >= count || atBottom) && stableCount >= 2) ||
|
||||
(atBottom && stableCount >= 10) ||
|
||||
(Date.now() - startTime > 180000)) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
prevHeight = currentHeight;
|
||||
}
|
||||
}
|
||||
ticking = false;
|
||||
requestTick();
|
||||
}
|
||||
notifyMeta();
|
||||
pushNewImages(false);
|
||||
requestTick();
|
||||
}
|
||||
function modify() {
|
||||
|
||||
@@ -19,6 +19,7 @@ import top.fumiama.copymangaweb.activity.template.ToolsBoxActivity
|
||||
import top.fumiama.copymangaweb.data.ComicStructure
|
||||
import top.fumiama.copymangaweb.databinding.ActivityDlBinding
|
||||
import top.fumiama.copymangaweb.handler.DlHandler
|
||||
import top.fumiama.copymangaweb.tool.InsetsTools
|
||||
import top.fumiama.copymangaweb.tool.MangaDlTools
|
||||
import top.fumiama.copymangaweb.tool.MangaDlTools.Companion.wmdlt
|
||||
import top.fumiama.copymangaweb.view.ChapterToggleButton
|
||||
@@ -51,13 +52,14 @@ class DlActivity : ToolsBoxActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
mBinding = ActivityDlBinding.inflate(layoutInflater)
|
||||
setContentView(mBinding.root)
|
||||
InsetsTools.applySafeContentInsets(this, mBinding.root)
|
||||
wm?.get()?.saveUrlsOnly = true
|
||||
mangaDlTools = MangaDlTools(this)
|
||||
mBinding.dwh.apply { post {
|
||||
settings.userAgentString = getString(R.string.pc_ua)
|
||||
webChromeClient = WebChromeClient()
|
||||
setWebViewClient("h.js")
|
||||
loadJSInterface(JSHidden())
|
||||
loadJSInterface(JSHidden(onLoadChapter = { onHiddenChapterLoaded(it) }))
|
||||
} }
|
||||
handler.sendEmptyMessage(-2) //setLayouts
|
||||
}
|
||||
@@ -91,12 +93,17 @@ class DlActivity : ToolsBoxActivity() {
|
||||
).start()
|
||||
}
|
||||
|
||||
private fun fillChapters() {
|
||||
private fun fillChapters(): Boolean {
|
||||
mangaDlTools.allocateChapterUrls(checkedChapter)
|
||||
var ok = true
|
||||
for (i in tbtnlist) {
|
||||
if (i.isChecked) mangaDlTools.dlChapterUrl(i.url.toString())
|
||||
if (i.isChecked && !mangaDlTools.dlChapterUrl(i.url.toString())) {
|
||||
ok = false
|
||||
handler.obtainMessage(-1, i.index, 0).sendToTarget()
|
||||
}
|
||||
}
|
||||
return ok && mangaDlTools.waitChapterUrlsReady()
|
||||
}
|
||||
|
||||
private fun dlThread(dlMethod: (i: ChapterToggleButton) -> Unit) {
|
||||
sleep(10000)
|
||||
@@ -146,8 +153,13 @@ class DlActivity : ToolsBoxActivity() {
|
||||
handler.sendEmptyMessage(9) //set dl card color to red
|
||||
Toast.makeText(this@DlActivity, "请耐心等待加载...", Toast.LENGTH_SHORT).show()
|
||||
Thread {
|
||||
fillChapters()
|
||||
if (fillChapters()) {
|
||||
dlThread { downloadChapterPages(it) }
|
||||
} else {
|
||||
canDl = false
|
||||
haveDlStarted = false
|
||||
handler.sendEmptyMessage(8)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
@@ -200,6 +212,14 @@ class DlActivity : ToolsBoxActivity() {
|
||||
if(!(jsonFile.exists() && intent.getBooleanExtra("callFromDlList", false))) json?.let { jsonFile.writeText(it) }
|
||||
}
|
||||
|
||||
private fun onHiddenChapterLoaded(content: String) {
|
||||
val listChapter = content.split('\n')
|
||||
val hash = listChapter.getOrNull(0)?.substringAfterLast(' ')
|
||||
if (hash.isNullOrBlank()) return
|
||||
val imgs = listChapter.drop(3).filter { it.isNotBlank() }.toTypedArray()
|
||||
mangaDlTools.setChapterImages(hash, imgs)
|
||||
}
|
||||
|
||||
private fun downloadChapterPages(i: ChapterToggleButton) {
|
||||
mangaDlTools.onDownloadedListener =
|
||||
object : MangaDlTools.OnDownloadedListener {
|
||||
|
||||
@@ -10,6 +10,7 @@ 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.regex.Pattern
|
||||
import java.util.zip.ZipInputStream
|
||||
@@ -23,6 +24,7 @@ class DlListActivity: Activity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
mBinding = ActivityDlistBinding.inflate(layoutInflater)
|
||||
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
|
||||
|
||||
@@ -20,6 +20,7 @@ import top.fumiama.copymangaweb.activity.template.ToolsBoxActivity
|
||||
import top.fumiama.copymangaweb.activity.viewmodel.MainViewModel
|
||||
import top.fumiama.copymangaweb.databinding.ActivityMainBinding
|
||||
import top.fumiama.copymangaweb.handler.MainHandler
|
||||
import top.fumiama.copymangaweb.tool.InsetsTools
|
||||
import top.fumiama.copymangaweb.tool.MangaDlTools.Companion.wmdlt
|
||||
import top.fumiama.copymangaweb.tool.SetDraggable
|
||||
import top.fumiama.copymangaweb.tool.Updater
|
||||
@@ -41,6 +42,7 @@ class MainActivity: ToolsBoxActivity() {
|
||||
mBinding.mainViewModel = mViewModel
|
||||
mBinding.lifecycleOwner = this
|
||||
setContentView(mBinding.root)
|
||||
InsetsTools.applySafeContentInsets(this, mBinding.root)
|
||||
|
||||
wm = WeakReference(this)
|
||||
mh = MainHandler(Looper.myLooper()!!)
|
||||
@@ -125,6 +127,16 @@ class MainActivity: ToolsBoxActivity() {
|
||||
lifecycleScope.launch { mViewModel.updateLoadProgress(p) }
|
||||
}
|
||||
|
||||
fun openStreamingManga(url: String) {
|
||||
ViewMangaActivity.streamChapterUrl = url
|
||||
ViewMangaActivity.titleText = "加载中..."
|
||||
ViewMangaActivity.nextChapterUrl = null
|
||||
ViewMangaActivity.previousChapterUrl = null
|
||||
ViewMangaActivity.imgUrls = arrayOf()
|
||||
ViewMangaActivity.zipFile = null
|
||||
startActivity(Intent(this, ViewMangaActivity::class.java))
|
||||
}
|
||||
|
||||
fun setFab(content: String) {
|
||||
json = content
|
||||
lifecycleScope.launch {
|
||||
|
||||
@@ -28,10 +28,14 @@ import top.fumiama.copymangaweb.handler.TimeThread
|
||||
import top.fumiama.copymangaweb.tool.PropertiesTools
|
||||
import top.fumiama.copymangaweb.tool.ToolsBox
|
||||
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 kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
@@ -52,6 +56,12 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
private var notUseVP = true
|
||||
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 val streamSeenUrls = LinkedHashSet<String>()
|
||||
private var vpAdapter: RecyclerView.Adapter<ViewData>? = null
|
||||
private val volTurnPage get() = p["volturn"] == "true"
|
||||
var pageNum = 1
|
||||
get() {
|
||||
@@ -91,8 +101,9 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
show()
|
||||
}
|
||||
mBinding.oneinfo.inftitle.ttitle.apply { post { text = titleText } }
|
||||
Log.d("MyVM", "dlZip2View: $dlZip2View, mangaZip: $mangaZip")
|
||||
Log.d("MyVM", "dlZip2View: $dlZip2View, mangaZip: $mangaZip, streamUrl: $streamUrl")
|
||||
if(dlZip2View && mangaZip?.exists() != true) toolsBox.toastError("已经到头了~")
|
||||
else if(!dlZip2View && !streamUrl.isNullOrBlank()) startStreamingCollector(streamUrl!!)
|
||||
else Thread {
|
||||
try {
|
||||
count = if (dlZip2View) countZipItems() else imgUrls.size
|
||||
@@ -100,8 +111,120 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
e.printStackTrace()
|
||||
runOnUiThread { toolsBox.toastError("分析图片url错误") }
|
||||
}
|
||||
runOnUiThread { prepareReaderIfNeeded() }
|
||||
}.start()
|
||||
}
|
||||
|
||||
|
||||
private fun startStreamingCollector(url: String) {
|
||||
Log.d("CopymangaDL", "reader collector load url=$url")
|
||||
mBinding.wcollector.apply { post {
|
||||
settings.userAgentString = getString(R.string.pc_ua)
|
||||
webChromeClient = WebChromeClient()
|
||||
setWebViewClient("h.js")
|
||||
loadJSInterface(
|
||||
JSHidden(
|
||||
onLoadChapter = { onStreamingChapterFinished(it) },
|
||||
onChapterMeta = { onStreamingMeta(it) },
|
||||
onAppendImages = { appendStreamingImages(it) },
|
||||
onFinishStreaming = { onStreamingFinished() },
|
||||
onChapterCount = { onStreamingCount(it) },
|
||||
enableLoadingDialog = false
|
||||
)
|
||||
)
|
||||
loadUrl(url)
|
||||
} }
|
||||
}
|
||||
|
||||
private fun onStreamingMeta(headerString: String) {
|
||||
val lines = headerString.split('\n')
|
||||
if (lines.size < 3) return
|
||||
titleText = lines[0].substringBeforeLast(' ')
|
||||
nextChapterUrl = lines[1].let { if(it == "null") null else it }
|
||||
previousChapterUrl = lines[2].let { if(it == "null") null else it }
|
||||
runOnUiThread { mBinding.oneinfo.inftitle.ttitle.text = titleText }
|
||||
}
|
||||
private fun setEarlyTapLayerEnabled(enabled: Boolean) {
|
||||
mBinding.onec.apply { post {
|
||||
isClickable = enabled
|
||||
isFocusable = false
|
||||
setOnClickListener(if (enabled) View.OnClickListener {
|
||||
if (clicked) hideSettings() else showSettings()
|
||||
} else null)
|
||||
} }
|
||||
}
|
||||
|
||||
private fun seekProgressToPage(progress: Int): Int {
|
||||
if (count <= 1) return 1
|
||||
return ((progress.coerceIn(0, 100) * (count - 1) + 50) / 100 + 1).coerceIn(1, count)
|
||||
}
|
||||
|
||||
private fun onStreamingCount(total: Int) {
|
||||
if (total <= 0) 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)
|
||||
}
|
||||
syncSeekBarOnly()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onStreamingChapterFinished(content: String) {
|
||||
val listChapter = content.split('\n')
|
||||
if (listChapter.size >= 3) onStreamingMeta(listChapter.take(3).joinToString("\n"))
|
||||
appendStreamingImages(listChapter.drop(3).filter { it.isNotBlank() }.toTypedArray())
|
||||
onStreamingFinished()
|
||||
}
|
||||
|
||||
private fun onStreamingFinished() {
|
||||
streamFinished = true
|
||||
Log.d("CopymangaDL", "reader stream finished pages=$count")
|
||||
runOnUiThread {
|
||||
dialog?.dismiss()
|
||||
dialog = null
|
||||
syncSeekBarOnly()
|
||||
}
|
||||
}
|
||||
|
||||
private fun appendStreamingImages(urls: Array<String>) {
|
||||
runOnUiThread {
|
||||
val oldCount = count
|
||||
val oldImageCount = imgUrls.size
|
||||
var added = 0
|
||||
for (url in urls) {
|
||||
if (streamSeenUrls.add(url)) {
|
||||
imgUrls += url
|
||||
added++
|
||||
}
|
||||
}
|
||||
if (added <= 0) return@runOnUiThread
|
||||
count = max(max(streamDeclaredCount, imgUrls.size), count)
|
||||
Log.d("CopymangaDL", "reader appended images added=$added total=$count images=${imgUrls.size}")
|
||||
if (!readerPrepared) prepareReaderIfNeeded()
|
||||
else {
|
||||
if (notUseVP) {
|
||||
if (currentItem in oldImageCount until imgUrls.size) loadOneImg(hidePanel = false)
|
||||
else syncSeekBarOnly()
|
||||
} else {
|
||||
if (count > oldCount) vpAdapter?.notifyItemRangeInserted(oldCount, count - oldCount)
|
||||
notifyVisiblePagesIfArrived(oldImageCount, imgUrls.size)
|
||||
syncSeekBarOnly()
|
||||
}
|
||||
preloadAround(getLogicalCurrentItem())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareReaderIfNeeded() {
|
||||
try {
|
||||
if (count <= 0) return
|
||||
prepareItems()
|
||||
if(pn > 0) {
|
||||
pageNum = pn
|
||||
@@ -114,11 +237,46 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
e.printStackTrace()
|
||||
toolsBox.toastError("准备控件错误")
|
||||
} finally {
|
||||
if (streamUrl == null || count > 0) {
|
||||
dialog?.dismiss()
|
||||
dialog = null
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun preloadAround(position: Int) {
|
||||
if (dlZip2View || position < 0 || imgUrls.isEmpty()) return
|
||||
val end = min(imgUrls.size - 1, position + 4)
|
||||
for (i in position + 1..end) {
|
||||
imgUrls.getOrNull(i)?.let { url ->
|
||||
Glide.with(this@ViewMangaActivity)
|
||||
.load(toolsBox.resolution.wrap(url))
|
||||
.preload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
private fun notifyVisiblePagesIfArrived(oldImageCount: Int, newImageCount: Int) {
|
||||
if (notUseVP || 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
@@ -155,20 +313,47 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
|
||||
private fun getImgBitmap(position: Int): Bitmap? {
|
||||
if (position >= count || position < 0) return null
|
||||
else {
|
||||
val zip = ZipFile(mangaZip)
|
||||
return BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.webp")))
|
||||
val zipPath = mangaZip ?: return null
|
||||
return try {
|
||||
ZipFile(zipPath).use { zip ->
|
||||
// Older downloaded zips use 0.webp, 1.webp...
|
||||
// The large-chapter fix may use 000.webp, 001.webp... for stable sorting.
|
||||
// Support both formats and fall back to the sorted entry list.
|
||||
val entry = zip.getEntry("${position}.webp")
|
||||
?: zip.getEntry("%03d.webp".format(position))
|
||||
?: zip.entries().asSequence()
|
||||
.filter { !it.isDirectory }
|
||||
.sortedBy { it.name }
|
||||
.elementAtOrNull(position)
|
||||
?: run {
|
||||
Log.e("CopymangaDL", "zip entry missing position=$position file=$zipPath")
|
||||
return null
|
||||
}
|
||||
zip.getInputStream(entry).use { input ->
|
||||
BitmapFactory.decodeStream(input)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("CopymangaDL", "decode zip image failed position=$position file=$zipPath", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadOneImg() {
|
||||
private fun loadOneImg(hidePanel: Boolean = true) {
|
||||
if(dlZip2View) mBinding.vone.onei.apply { post { setImageBitmap(getImgBitmap(currentItem)) } }
|
||||
else Glide.with(this@ViewMangaActivity)
|
||||
.load(toolsBox.resolution.wrap(imgUrls[currentItem]))
|
||||
else {
|
||||
val url = imgUrls.getOrNull(currentItem)
|
||||
if (url.isNullOrBlank()) {
|
||||
mBinding.vone.onei.apply { post { setImageResource(R.drawable.ic_dl) } }
|
||||
} else {
|
||||
Glide.with(this@ViewMangaActivity)
|
||||
.load(toolsBox.resolution.wrap(url))
|
||||
.placeholder(R.drawable.ic_dl)
|
||||
.dontAnimate()
|
||||
.into(mBinding.vone.onei)
|
||||
updateSeekBar()
|
||||
}
|
||||
}
|
||||
updateSeekBar(hidePanel)
|
||||
}
|
||||
|
||||
private fun setIdPosition(position: Int) {
|
||||
@@ -178,6 +363,7 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun prepareItems() {
|
||||
if (count <= 0) return
|
||||
prepareVP()
|
||||
prepareInfoBar(count)
|
||||
if (notUseVP) loadOneImg() else prepareIdBtVH()
|
||||
@@ -185,6 +371,7 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
prepareIdBtVolTurn()
|
||||
prepareIdBtVP()
|
||||
prepareIdBtLR()
|
||||
readerPrepared = true
|
||||
}
|
||||
|
||||
private fun prepareIdBtLR() {
|
||||
@@ -216,9 +403,12 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
} else {
|
||||
mBinding.vp.apply { post {
|
||||
visibility = View.VISIBLE
|
||||
adapter = ViewData(this).RecyclerViewAdapter()
|
||||
vpAdapter = ViewData(this).RecyclerViewAdapter()
|
||||
adapter = vpAdapter
|
||||
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
val pos = if (r2l) count - position - 1 else position
|
||||
preloadAround(pos)
|
||||
updateSeekBar()
|
||||
super.onPageSelected(position)
|
||||
}
|
||||
@@ -229,8 +419,12 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSeekBar() {
|
||||
if (!isInSeek) hideSettings()
|
||||
private fun updateSeekBar(hidePanel: Boolean = true) {
|
||||
if (hidePanel && !isInSeek) hideSettings()
|
||||
syncSeekBarOnly()
|
||||
}
|
||||
|
||||
private fun syncSeekBarOnly() {
|
||||
updateSeekText()
|
||||
updateSeekProgress()
|
||||
}
|
||||
@@ -242,9 +436,10 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
visibility = View.INVISIBLE
|
||||
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(p0: SeekBar?, p1: Int, isHuman: Boolean) {
|
||||
if (isHuman) {
|
||||
if (p1 >= (pageNum + 1) * 100 / size) scrollForward()
|
||||
else if (p1 < (pageNum - 1) * 100 / size) scrollBack()
|
||||
if (isHuman && count > 0) {
|
||||
val targetPage = seekProgressToPage(p1)
|
||||
if (targetPage != pageNum) pageNum = targetPage
|
||||
updateSeekText()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +448,12 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(p0: SeekBar?) {
|
||||
p0?.let {
|
||||
val targetPage = seekProgressToPage(it.progress)
|
||||
if (targetPage != pageNum) pageNum = targetPage
|
||||
}
|
||||
isInSeek = false
|
||||
updateSeekBar()
|
||||
}
|
||||
})
|
||||
} }
|
||||
@@ -312,11 +512,12 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
}
|
||||
|
||||
fun scrollBack() {
|
||||
pageNum--
|
||||
if (pageNum > 1) pageNum--
|
||||
}
|
||||
|
||||
fun scrollForward() {
|
||||
pageNum++
|
||||
if (pageNum < count) pageNum++
|
||||
else if (!streamFinished && streamUrl != null) Toast.makeText(this, "后续页面仍在后台加载", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@@ -325,7 +526,10 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
}
|
||||
|
||||
private fun updateSeekProgress() {
|
||||
mBinding.oneinfo.infseek.apply { post { progress = pageNum * 100 / count } }
|
||||
if (isInSeek || count <= 0) return
|
||||
mBinding.oneinfo.infseek.apply { post {
|
||||
progress = if (count <= 1) 0 else ((pageNum - 1) * 100 / (count - 1)).coerceIn(0, 100)
|
||||
} }
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
@@ -338,6 +542,7 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
override fun onDestroy() {
|
||||
tt.canDo = false
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
if (streamUrl != null) streamChapterUrl = null
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@@ -359,10 +564,13 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
//Glide.with(this@ViewMangaActivity).load(it).placeholder(R.drawable.bg_comment).into(holder.itemView.onei)
|
||||
oneImage.setImageBitmap(it)
|
||||
}
|
||||
else Glide.with(this@ViewMangaActivity)
|
||||
.load(toolsBox.resolution.wrap(imgUrls[pos])).placeholder(R.drawable.ic_dl)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,5 +674,6 @@ class ViewMangaActivity : ToolsBoxActivity() {
|
||||
var zipList: Array<String>? = null
|
||||
var cd: File? = null
|
||||
var pn = -1
|
||||
var streamChapterUrl: String? = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package top.fumiama.copymangaweb.tool
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import kotlin.math.max
|
||||
|
||||
object InsetsTools {
|
||||
fun applySafeContentInsets(activity: Activity, root: View) {
|
||||
WindowCompat.setDecorFitsSystemWindows(activity.window, false)
|
||||
|
||||
val startLeft = root.paddingLeft
|
||||
val startTop = root.paddingTop
|
||||
val startRight = root.paddingRight
|
||||
val startBottom = root.paddingBottom
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(root) { view, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val displayCutout = insets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
val captionBar = insets.getInsets(WindowInsetsCompat.Type.captionBar())
|
||||
|
||||
view.updatePadding(
|
||||
left = startLeft + max(systemBars.left, displayCutout.left),
|
||||
top = startTop + maxOf(systemBars.top, displayCutout.top, captionBar.top),
|
||||
right = startRight + max(systemBars.right, displayCutout.right),
|
||||
bottom = startBottom + max(systemBars.bottom, displayCutout.bottom)
|
||||
)
|
||||
insets
|
||||
}
|
||||
|
||||
if (ViewCompat.isAttachedToWindow(root)) {
|
||||
ViewCompat.requestApplyInsets(root)
|
||||
} else {
|
||||
root.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(v: View) {
|
||||
v.removeOnAttachStateChangeListener(this)
|
||||
ViewCompat.requestApplyInsets(v)
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(v: View) = Unit
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package top.fumiama.copymangaweb.tool
|
||||
|
||||
import android.util.Log
|
||||
import top.fumiama.copymangaweb.R
|
||||
import top.fumiama.copymangaweb.activity.DlActivity
|
||||
import java.io.File
|
||||
import java.lang.Thread.sleep
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.Semaphore
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.zip.CRC32
|
||||
import java.util.zip.CheckedOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
@@ -25,7 +27,7 @@ class MangaDlTools(activity: DlActivity) {
|
||||
}
|
||||
|
||||
fun getImgsCountByHash(hash: String): Int?{
|
||||
return imgUrlsList?.get(p[hash].toInt())?.size
|
||||
return p[hash].toIntOrNull()?.let { imgUrlsList?.getOrNull(it)?.size }
|
||||
}
|
||||
|
||||
fun allocateChapterUrls(count: Int){
|
||||
@@ -33,50 +35,118 @@ class MangaDlTools(activity: DlActivity) {
|
||||
chaptersCount = 0
|
||||
}
|
||||
|
||||
fun dlChapterUrl(url: String){
|
||||
sem.acquire()
|
||||
fun dlChapterUrl(url: String): Boolean {
|
||||
Log.d("CopymangaDL", "waiting chapter semaphore url=$url")
|
||||
if (!sem.tryAcquire(3, TimeUnit.MINUTES)) {
|
||||
Log.e("CopymangaDL", "timeout waiting previous chapter url collection")
|
||||
return false
|
||||
}
|
||||
var started = false
|
||||
da.get()?.apply {
|
||||
p[url.substringAfterLast("/")] = (chaptersCount++).toString()
|
||||
Log.d("CopymangaDL", "load chapter url=$url hash=${url.substringAfterLast("/")}")
|
||||
runOnUiThread { mBinding.dwh.apply { post { loadUrl(url) } } }
|
||||
started = true
|
||||
}
|
||||
if (!started) sem.release()
|
||||
return started
|
||||
}
|
||||
|
||||
|
||||
fun waitChapterUrlsReady(): Boolean {
|
||||
Log.d("CopymangaDL", "waiting final chapter url collection")
|
||||
return if (sem.tryAcquire(5, TimeUnit.MINUTES)) {
|
||||
sem.release()
|
||||
Log.d("CopymangaDL", "all chapter urls collected")
|
||||
true
|
||||
} else {
|
||||
Log.e("CopymangaDL", "timeout waiting final chapter url collection")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun setChapterImages(hash: String, imgUrls: Array<String>){
|
||||
imgUrlsList?.set(p[hash].toInt(), imgUrls)
|
||||
val index = p[hash].toIntOrNull()
|
||||
if (index == null) {
|
||||
Log.e("CopymangaDL", "missing chapter hash=$hash, images=${imgUrls.size}")
|
||||
sem.release()
|
||||
return
|
||||
}
|
||||
Log.d("CopymangaDL", "chapter images collected hash=$hash index=$index images=${imgUrls.size}")
|
||||
imgUrlsList?.set(index, imgUrls)
|
||||
sem.release()
|
||||
}
|
||||
|
||||
fun dlChapterAndPackIntoZip(zipf: File, hash: String){
|
||||
imgUrlsList?.get(p[hash].toInt())?.let { images ->
|
||||
val idx = p[hash].toIntOrNull()
|
||||
val images = idx?.let { imgUrlsList?.getOrNull(it) }
|
||||
if (images.isNullOrEmpty()) {
|
||||
Log.e("CopymangaDL", "no images to zip hash=$hash idx=$idx")
|
||||
onDownloadedListener?.handleMessage(false)
|
||||
return
|
||||
}
|
||||
|
||||
val dl = DownloadTools()
|
||||
zipf.parentFile?.let { if (!it.exists()) it.mkdirs() }
|
||||
if (zipf.exists()) zipf.delete()
|
||||
zipf.createNewFile()
|
||||
val zip = ZipOutputStream(CheckedOutputStream(zipf.outputStream(), CRC32()))
|
||||
zip.setLevel(9)
|
||||
val tmpZip = File(zipf.absolutePath + ".tmp")
|
||||
if (tmpZip.exists()) tmpZip.delete()
|
||||
|
||||
Log.d("CopymangaDL", "start zip file=${zipf.absolutePath} pages=${images.size}")
|
||||
var succeed = true
|
||||
try {
|
||||
ZipOutputStream(CheckedOutputStream(tmpZip.outputStream().buffered(), CRC32())).use { zip ->
|
||||
zip.setLevel(9)
|
||||
for (i in images.indices) {
|
||||
zip.putNextEntry(ZipEntry("$i.webp"))
|
||||
if (i % 10 == 0) Log.d("CopymangaDL", "zipping page ${i + 1}/${images.size}")
|
||||
var tryTimes = 3
|
||||
var s = false
|
||||
while (!s && tryTimes-- > 0){
|
||||
s = d?.toolsBox?.resolution?.wrap(images[i])?.let { u ->
|
||||
dl.getHttpContent(u, d?.getString(R.string.web_home_www),
|
||||
var data: ByteArray? = null
|
||||
while (data == null && tryTimes-- > 0) {
|
||||
data = d?.toolsBox?.resolution?.wrap(images[i])?.let { u ->
|
||||
dl.getHttpContent(
|
||||
u,
|
||||
d?.getString(R.string.web_home_www),
|
||||
d?.getString(R.string.pc_ua)
|
||||
)?.let { zip.write(it); true } ?: false
|
||||
} ?: false
|
||||
if (!s) {
|
||||
)
|
||||
}
|
||||
if (data == null) {
|
||||
Log.w("CopymangaDL", "retry page=${i + 1} left=$tryTimes url=${images[i]}")
|
||||
onDownloadedListener?.handleMessage(i + 1)
|
||||
sleep(2000)
|
||||
}
|
||||
}
|
||||
if(!s && tryTimes <= 0) succeed = false
|
||||
onDownloadedListener?.handleMessage(s, i + 1)
|
||||
zip.flush()
|
||||
if (exit) break
|
||||
|
||||
if (data == null) {
|
||||
succeed = false
|
||||
Log.e("CopymangaDL", "download failed page=${i + 1} url=${images[i]}")
|
||||
onDownloadedListener?.handleMessage(false, i + 1)
|
||||
} else {
|
||||
zip.putNextEntry(ZipEntry("%03d.webp".format(i)))
|
||||
zip.write(data)
|
||||
zip.closeEntry()
|
||||
onDownloadedListener?.handleMessage(true, i + 1)
|
||||
}
|
||||
zip.close()
|
||||
zip.flush()
|
||||
if (exit) {
|
||||
succeed = false
|
||||
Log.w("CopymangaDL", "zip canceled")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (succeed) {
|
||||
if (zipf.exists()) zipf.delete()
|
||||
if (!tmpZip.renameTo(zipf)) {
|
||||
tmpZip.copyTo(zipf, overwrite = true)
|
||||
tmpZip.delete()
|
||||
}
|
||||
} else tmpZip.delete()
|
||||
Log.d("CopymangaDL", "zip finished succeed=$succeed file=${zipf.absolutePath}")
|
||||
onDownloadedListener?.handleMessage(succeed)
|
||||
} catch (e: Exception) {
|
||||
Log.e("CopymangaDL", "zip exception", e)
|
||||
tmpZip.delete()
|
||||
onDownloadedListener?.handleMessage(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ class JS {
|
||||
else -> ""
|
||||
}
|
||||
Log.d("MyJS", "Load comic: $u")
|
||||
wm?.get()?.loadHiddenUrl(u)
|
||||
if (u.contains("/chapter/")) wm?.get()?.openStreamingManga(u)
|
||||
else wm?.get()?.loadHiddenUrl(u)
|
||||
}
|
||||
@JavascriptInterface
|
||||
fun hideFab() {
|
||||
|
||||
@@ -7,11 +7,58 @@ import top.fumiama.copymangaweb.activity.MainActivity.Companion.mh
|
||||
import top.fumiama.copymangaweb.activity.MainActivity.Companion.wm
|
||||
import top.fumiama.copymangaweb.handler.MainHandler
|
||||
|
||||
class JSHidden {
|
||||
class JSHidden(
|
||||
private val onLoadChapter: ((String) -> Unit)? = null,
|
||||
private val onChapterMeta: ((String) -> Unit)? = null,
|
||||
private val onAppendImages: ((Array<String>) -> Unit)? = null,
|
||||
private val onFinishStreaming: (() -> Unit)? = null,
|
||||
private val onChapterCount: ((Int) -> Unit)? = null,
|
||||
private val enableLoadingDialog: Boolean = true,
|
||||
) {
|
||||
@JavascriptInterface
|
||||
fun loadChapter(listString: String){
|
||||
wm?.get()?.callViewManga(listString)
|
||||
Log.d("CopymangaDL", "JS loadChapter called, length=${listString.length}, lines=${listString.count { it == '\n' } + 1}")
|
||||
val callback = onLoadChapter
|
||||
if (callback != null) callback(listString)
|
||||
else {
|
||||
val main = wm?.get()
|
||||
if (main == null) Log.e("CopymangaDL", "MainActivity reference lost, dropped chapter result")
|
||||
else main.callViewManga(listString)
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun setChapterMeta(headerString: String) {
|
||||
Log.d("CopymangaDL", "JS setChapterMeta called, length=${headerString.length}")
|
||||
onChapterMeta?.invoke(headerString)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun appendChapterImages(urlsString: String) {
|
||||
val urls = urlsString
|
||||
.split('\n')
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
.toTypedArray()
|
||||
Log.d("CopymangaDL", "JS appendChapterImages called, images=${urls.size}")
|
||||
if (urls.isNotEmpty()) onAppendImages?.invoke(urls)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun setChapterCount(countString: String) {
|
||||
val c = countString.toIntOrNull() ?: return
|
||||
if (c > 0) {
|
||||
Log.d("CopymangaDL", "JS setChapterCount called, count=$c")
|
||||
onChapterCount?.invoke(c)
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun finishStreamingChapter() {
|
||||
Log.d("CopymangaDL", "JS finishStreamingChapter called")
|
||||
onFinishStreaming?.invoke()
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun setTitle(title:String){
|
||||
Log.d("MyJSH", "Set title: $title")
|
||||
@@ -23,10 +70,10 @@ class JSHidden {
|
||||
}
|
||||
@JavascriptInterface
|
||||
fun setLoadingDialog(display: Boolean) {
|
||||
mh?.sendEmptyMessage(if (display) MainHandler.SHOW_LOADING_DIALOG else MainHandler.HIDE_LOADING_DIALOG)
|
||||
if (enableLoadingDialog) mh?.sendEmptyMessage(if (display) MainHandler.SHOW_LOADING_DIALOG else MainHandler.HIDE_LOADING_DIALOG)
|
||||
}
|
||||
@JavascriptInterface
|
||||
fun setLoadingDialogProgress(index: String, count: String) {
|
||||
mh?.obtainMessage(MainHandler.SET_LOADING_DIALOG_TEXT, "$index/$count")?.sendToTarget()
|
||||
if (enableLoadingDialog) mh?.obtainMessage(MainHandler.SET_LOADING_DIALOG_TEXT, "$index/$count")?.sendToTarget()
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,15 @@ package top.fumiama.copymangaweb.web
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import top.fumiama.copymangaweb.R
|
||||
import top.fumiama.copymangaweb.activity.MainActivity.Companion.wm
|
||||
|
||||
class WebViewClient(private val context: Context, jsFileName: String):WebViewClient() {
|
||||
private val js = context.assets.open(jsFileName).readBytes().decodeToString()
|
||||
@@ -30,17 +26,12 @@ class WebViewClient(private val context: Context, jsFileName: String):WebViewCli
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
wm?.get()?.lifecycleScope?.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
delay(500)
|
||||
withContext(Dispatchers.Main) {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
view?.loadUrl(js)
|
||||
Log.d("MyWC", "Inject JS into: $url")
|
||||
}, 500)
|
||||
super.onPageFinished(view, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldInterceptRequest(
|
||||
view: WebView?,
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<top.fumiama.copymangaweb.view.JSWebView
|
||||
android:id="@+id/wcollector"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="1dp"
|
||||
android:alpha="0.01"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<include
|
||||
android:id="@+id/vone"
|
||||
layout="@layout/page_imgview"
|
||||
|
||||
Reference in New Issue
Block a user