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