1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-05 07:20:23 +08:00
新增
1. 从断点继续下载漫画
2. 封面加载动画
3. 长按下载漫画导航至对应文件夹
修复
1. 部分下载漫画时的闪退
2. 搜索退出后不保留关键词
优化
1. 漫画阅读器代码
2. 每次启动时如未登录则不再刷新用户信息
3. AppBar 不跟随滚动 (fix #40)
4. 标签、作者、已完结默认从新到旧显示
升级
1. 升级支持库版本
This commit is contained in:
源文雨
2024-01-13 01:55:56 +09:00
parent 279e08b06a
commit 2257e977d1
31 changed files with 417 additions and 224 deletions

View File

@@ -1,6 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="fumiama">
<words>
<w>imgs</w>
<w>lowpan</w>
<w>nisi</w>
</words>

View File

@@ -8,8 +8,8 @@ android {
applicationId 'top.fumiama.copymanga'
minSdkVersion 23
targetSdkVersion 34
versionCode 41
versionName '2.0.5'
versionCode 42
versionName '2.0.6'
resConfigs 'zh', 'zh-rCN'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -58,10 +58,10 @@ dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.google.android.material:material:1.10.0'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.7.5'
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.6'
implementation 'androidx.navigation:navigation-ui-ktx:2.7.6'
testImplementation 'junit:junit:4.13.2'
implementation "androidx.preference:preference-ktx:1.2.1"
implementation 'com.afollestad.material-dialogs:input:3.3.0'
@@ -74,5 +74,6 @@ dependencies {
implementation 'com.liaoinstan.springview:library:1.7.0'
implementation 'com.github.zawadz88.materialpopupmenu:material-popup-menu:4.0.1'
implementation 'com.lapism:search:2.4.1@aar'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
implementation 'com.airbnb.android:lottie:6.3.0'
}

View File

@@ -14,7 +14,7 @@
android:usesCleartextTraffic="true">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="top.fumiama.copymanga.fileprovider"
android:authorities="@string/file_provider_authority"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@@ -19,21 +19,26 @@ class MangaDlTools {
get() = pool?.wait
set(value) { if (value != null) { pool?.wait = value } }
fun downloadChapterInVol(url: CharSequence, chapterName: CharSequence, group: CharSequence, index: Int){
fun downloadChapterInVol(url: CharSequence, chapterName: CharSequence, group: CharSequence, index: Int) {
Log.d("MyMDT", "下载:$url, index$index")
AutoDownloadThread(url.toString()){ data ->
Gson().fromJson(data?.decodeToString(), Chapter2Return::class.java)?.let {
getChapterInfo(it, index, chapterName, group)
AutoDownloadThread(url.toString(), 1000) { data ->
try {
Gson().fromJson(data?.decodeToString(), Chapter2Return::class.java)?.let {
getChapterInfo(it, index, chapterName, group)
}
} catch (e: Exception) {
e.printStackTrace()
onDownloadedListener?.handleMessage(index, false, e.localizedMessage?:"Gson parsing error")
}
}.start()
}
@Synchronized private fun prepareDownloadListener() {
pool?.setOnDownloadListener { fileName: String, isSuccess: Boolean ->
indexMap[fileName]?.let { onDownloadedListener?.handleMessage(it, isSuccess) }
pool?.setOnDownloadListener { fileName: String, isSuccess: Boolean, message: String ->
indexMap[fileName]?.let { onDownloadedListener?.handleMessage(it, isSuccess, message) }
}
pool?.setOnPageDownloadListener { fileName: String, downloaded: Int, total: Int, isSuccess: Boolean ->
indexMap[fileName]?.let { onDownloadedListener?.handleMessage(it, downloaded, total, isSuccess) }
pool?.setOnPageDownloadListener { fileName: String, downloaded: Int, total: Int, isSuccess: Boolean, message: String ->
indexMap[fileName]?.let { onDownloadedListener?.handleMessage(it, downloaded, total, isSuccess, message) }
}
}
@@ -83,7 +88,7 @@ class MangaDlTools {
var onDownloadedListener: OnDownloadedListener? = null
interface OnDownloadedListener{
fun handleMessage(index: Int, isSuccess: Boolean)
fun handleMessage(index: Int, downloaded: Int, total: Int, isSuccess: Boolean)
fun handleMessage(index: Int, isSuccess: Boolean, message: String)
fun handleMessage(index: Int, downloaded: Int, total: Int, isSuccess: Boolean, message: String)
}
}

View File

@@ -4,14 +4,16 @@ import android.util.Log
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.dmzj.copymanga.R
import kotlin.random.Random
class AutoDownloadThread(private val url: String, private val whenFinish: (result: ByteArray?)->Unit): Thread() {
class AutoDownloadThread(private val url: String, private val waitMilliseconds: Long = 0, private val whenFinish: (result: ByteArray?)->Unit): Thread() {
var exit = false
override fun run() {
super.run()
var re: ByteArray? = null
var c = 0
while (!exit && re == null && c++ < 3){
while (!exit && re == null && c++ < 3) {
if (waitMilliseconds > 0) sleep(200+Random.nextLong(waitMilliseconds))
re = DownloadTools.getHttpContent(url,
mainWeakReference?.get()?.getString(R.string.referer)!!,
mainWeakReference?.get()?.getString(R.string.pc_ua)!!

View File

@@ -1,16 +1,23 @@
package top.fumiama.copymanga.template.ui
import android.annotation.SuppressLint
import android.graphics.drawable.Drawable
import android.net.Uri
import android.util.Log
import android.view.View
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import kotlinx.android.synthetic.main.card_book.*
import kotlinx.android.synthetic.main.card_book.view.*
import kotlinx.android.synthetic.main.line_horizonal_empty.view.*
import kotlinx.android.synthetic.main.line_lazybooklines.*
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
import top.fumiama.dmzj.copymanga.R
import java.io.File
import java.lang.ref.WeakReference
@@ -110,14 +117,22 @@ class CardList(
that?.context?.let { context ->
Glide.with(context).load(
GlideUrl(CMApi.proxy?.wrap(head)?:head, CMApi.myGlideHeaders)
).into(it.imic)
).addListener(GlideHideLottieViewListener(WeakReference(it.laic))).into(it.imic)
}
} else {
it.laic.pauseAnimation()
it.laic.visibility = View.GONE
it.imic.setImageResource(R.drawable.img_defmask)
}
} else {
val img = File(file, "head.jpg")
if(img.exists()) it.imic.setImageURI(Uri.fromFile(img))
it.laic.pauseAnimation()
it.laic.visibility = View.GONE
if(img.exists()) {
it.imic.setImageURI(Uri.fromFile(img))
} else {
it.imic.setImageResource(R.drawable.img_defmask)
}
}
if(card.isFinish) it.sgnic.visibility = View.VISIBLE
if(card.isNew) it.sgnnew.visibility = View.VISIBLE

View File

@@ -10,7 +10,7 @@ import top.fumiama.dmzj.copymanga.R
@ExperimentalStdlibApi
open class StatusCardFlow(private val api: Int, nav: Int) : InfoCardLoader(R.layout.fragment_statuscardflow, nav) {
val sortWay = listOf("datetime_updated", "-datetime_updated", "popular", "-popular")
val sortWay = listOf("-datetime_updated", "datetime_updated", "popular", "-popular")
var sortValue = 0
override fun getApiUrl() =

View File

@@ -3,11 +3,14 @@ package top.fumiama.copymanga.tools.http
import android.util.Log
import top.fumiama.copymanga.tools.api.CMApi
import java.io.File
import java.io.FileOutputStream
import java.lang.Thread.sleep
import java.util.zip.CRC32
import java.util.zip.CheckedOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
import kotlin.random.Random
class DownloadPool(folder: String) {
class Quest(val fileName: String, val imgUrl: Array<String>, val refer: String? = null)
@@ -22,9 +25,9 @@ class DownloadPool(folder: String) {
var wait = false
private val saveFolder = File(folder)
//fileName: String, isSuccess: Boolean
private var mOnDownloadListener: ((String, Boolean) -> Unit)? = null
private var mOnDownloadListener: ((String, Boolean, String) -> Unit)? = null
//fileName: String, downloaded: Int, total: Int, isSuccess: Boolean
private var mOnPageDownloadListener: ((String, Int, Int, Boolean) -> Unit)? = null
private var mOnPageDownloadListener: ((String, Int, Int, Boolean, String) -> Unit)? = null
init {
if(!saveFolder.exists()) saveFolder.mkdirs()
}
@@ -37,50 +40,76 @@ class DownloadPool(folder: String) {
Thread{
quests.forEach { quest ->
packZipFile(quest.fileName, quest.imgUrl, quest.refer)
sleep(1000)
}
}.start()
}
fun setOnDownloadListener(onDownloadListener: (String, Boolean) -> Unit) {
fun setOnDownloadListener(onDownloadListener: (String, Boolean, String) -> Unit) {
mOnDownloadListener = onDownloadListener
}
fun setOnPageDownloadListener(onPageDownloadListener: (String, Int, Int, Boolean) -> Unit) {
fun setOnPageDownloadListener(onPageDownloadListener: (String, Int, Int, Boolean, String) -> Unit) {
mOnPageDownloadListener = onPageDownloadListener
}
private fun packZipFile(fileName: String, imgUrls: Array<String>, refer: String?) {
Thread{
File(saveFolder, fileName).let { f ->
File(saveFolder, "$fileName.tmp").let { f ->
f.parentFile?.let { if(!it.exists()) it.mkdirs() }
if(f.exists()) f.delete()
f.createNewFile()
var start = 0
Log.d("MyDP", "Zip file: ${f.absolutePath}")
val zip = ZipOutputStream(CheckedOutputStream(f.outputStream(), CRC32()))
if(f.exists()) {
try {
val zipFile = ZipFile(f)
start = zipFile.size() - 1
zipFile.close()
Log.d("MyDP", "last downloaded index: $start")
if (start <= 0 || start >= imgUrls.size) { // error or re-download
f.delete()
f.createNewFile()
start = 0
}
} catch (e: Exception) {
e.printStackTrace()
f.delete()
f.createNewFile()
}
} else f.createNewFile()
val zip = ZipOutputStream(CheckedOutputStream(FileOutputStream(f, true), CRC32()))
zip.setLevel(9)
var succeed = true
for(index in imgUrls.indices) {
while (wait && !exit) sleep(1000)
if(exit) break
zip.putNextEntry(ZipEntry("$index.${if(imgUrls[index].contains(".webp")) "webp" else "jpg"}"))
var tryTimes = 3
var s = false
while (!s && tryTimes-- > 0){
val u = imgUrls[index]
s = (DownloadTools.getHttpContent(CMApi.proxy?.wrap(u)?:u, -1))?.let { zip.write(it); true }?:false
if (!s) sleep(2000)
var lastIndex = -8
try {
for(index in start until imgUrls.size) {
while (wait && !exit) sleep(100+Random.nextLong(1000))
if(exit) break
var tryTimes = 3
var s = false
while (!s && tryTimes-- > 0) {
val u = imgUrls[index]
s = (DownloadTools.getHttpContent(CMApi.proxy?.wrap(u)?:u, -1))?.let {
zip.putNextEntry(ZipEntry("$index.${if(imgUrls[index].contains(".webp")) "webp" else "jpg"}"))
zip.write(it)
zip.closeEntry()
true
}?:false
if (!s) sleep(2000)
}
if(!s && tryTimes <= 0) {
succeed = false
mOnPageDownloadListener?.let { it(fileName, index + 1, imgUrls.size, false, "超过最大重试次数") }
break
} else mOnPageDownloadListener?.let { it(fileName, index + 1, imgUrls.size, true, "") }
lastIndex = index
}
if(!s && tryTimes <= 0) {
succeed = false
mOnPageDownloadListener?.let { it(fileName, index + 1, imgUrls.size, false) }
break
} else mOnPageDownloadListener?.let { it(fileName, index + 1, imgUrls.size, true) }
//zip.flush()
zip.close()
if (succeed && lastIndex+1 >= imgUrls.size) f.renameTo(File(saveFolder, fileName))
mOnPageDownloadListener?.let { it(fileName, 0, 0, true, "") }
mOnDownloadListener?.let { it(fileName, succeed, "") }
} catch (e: Exception) {
e.printStackTrace()
mOnDownloadListener?.let { it(fileName, false, e.localizedMessage?:"packZipFile") }
}
zip.close()
mOnPageDownloadListener?.let { it(fileName, 0, 0, true) }
mOnDownloadListener?.let { it(fileName, succeed) }
}
}.start()
}

View File

@@ -0,0 +1,35 @@
package top.fumiama.copymanga.tools.ui
import android.graphics.drawable.Drawable
import android.view.View
import com.airbnb.lottie.LottieAnimationView
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import java.lang.ref.WeakReference
class GlideHideLottieViewListener(private val wla: WeakReference<LottieAnimationView>): RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
wla.get()?.apply {
pauseAnimation()
visibility = View.GONE
}
return false
}
}

View File

@@ -1,5 +1,6 @@
package top.fumiama.copymanga.tools.ui
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
@@ -42,6 +43,10 @@ class UITools(that: Context?, w: WeakReference<Activity>? = null) {
Toast.makeText(zis, s, Toast.LENGTH_SHORT).show()
if (willFinish) weak?.get()?.finish()
}
fun toastError(s: Int, willFinish: Boolean = true) {
Toast.makeText(zis, s, Toast.LENGTH_SHORT).show()
if (willFinish) weak?.get()?.finish()
}
fun buildInfo(
title: String,
msg: String,
@@ -84,7 +89,7 @@ class UITools(that: Context?, w: WeakReference<Activity>? = null) {
fun dp2px(dp:Int):Int?{
return zis?.resources?.displayMetrics?.density?.let { (dp * it + 0.5).toInt()}
}
fun px2dp(px:Int):Int?{
private fun px2dp(px:Int):Int?{
return zis?.resources?.displayMetrics?.density?.let { (px.toDouble() / it + 0.5).toInt()}
}
fun calcWidthFromDp(marginLeftDp:Int, widthDp:Int):List<Int>{
@@ -124,22 +129,24 @@ class UITools(that: Context?, w: WeakReference<Activity>? = null) {
}
toString()
}
@SuppressLint("DiscouragedApi", "InternalInsetResource")
fun getNavigationBarHeight(context: Context): Int {
val resources = context.resources
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return if (resourceId > 0) {
resources.getDimensionPixelSize(resourceId)
} else {
0
64
}
}
@SuppressLint("DiscouragedApi", "InternalInsetResource")
fun getStatusBarHeight(context: Context): Int {
val resources = context.resources
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
return if (resourceId > 0) {
resources.getDimensionPixelSize(resourceId)
} else {
0
64
}
}
}

View File

@@ -26,6 +26,7 @@ import java.lang.ref.WeakReference
class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
var fbibinfo: View? = null
var fbtinfo: View? = null
var isOnPause = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -67,14 +68,21 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
}
/*override fun onResume() {
override fun onResume() {
super.onResume()
mainWeakReference?.get()?.apply {
isOnPause = false
/*mainWeakReference?.get()?.apply {
toolbar.title = bookHandler?.book?.results?.comic?.name
}
setStartRead()
fbibinfo?.layoutParams?.height = ((fbibinfo?.width?:0) * 4.0 / 9.0 + 0.5).toInt()
}*/
*/
}
override fun onPause() {
super.onPause()
isOnPause = true
}
override fun onDestroy() {
super.onDestroy()

View File

@@ -33,6 +33,7 @@ import top.fumiama.copymanga.template.http.AutoDownloadHandler
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.GlideBlurTransformation
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.json
@@ -147,7 +148,7 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
book?.results?.comic?.cover?.let { cover ->
val load = Glide.with(this).load(
GlideUrl(CMApi.proxy?.wrap(cover)?:cover, CMApi.myGlideHeaders)
).timeout(10000)
).timeout(10000).addListener(GlideHideLottieViewListener(WeakReference(laic)))
load.into(imic)
context?.let { it1 -> GlideBlurTransformation(it1) }
?.let { it2 -> RequestOptions.bitmapTransform(it2) }
@@ -286,6 +287,10 @@ class BookHandler(private val th: WeakReference<BookFragment>, val path: String)
chapterNames += it.name
ViewMangaActivity.uuidArray += it.uuid
Log.d("MyBH", "i = $i, last=$last, add chapter ${it.name}, line is null: ${line == null}")
that?.isOnPause?.let { isOnPause ->
while (isOnPause && !exit) sleep(1000)
if (exit) return@Thread
}?:return@Thread
if(line == null) {
if(i == last) {
line = layoutInflater.inflate(R.layout.line_chapter, that!!.fbl, false)

View File

@@ -225,25 +225,27 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
return@setOnLongClickListener true
}
mangaDlTools.onDownloadedListener = object :MangaDlTools.OnDownloadedListener{
override fun handleMessage(index: Int, isSuccess: Boolean) {
override fun handleMessage(index: Int, isSuccess: Boolean, message: String) {
that?.activity?.runOnUiThread {
if(isSuccess) onZipDownloadFinish(index)
else onZipDownloadFailure(index)
else onZipDownloadFailure(index, message)
}
}
@SuppressLint("SetTextI18n")
override fun handleMessage(
index: Int,
downloaded: Int,
total: Int,
isSuccess: Boolean
isSuccess: Boolean,
message: String
) {
that?.activity?.runOnUiThread {
if(isSuccess) {
tbtnlist[index].text = if(downloaded == 0 && total == 0) tbtnlist[index].chapterName else "$downloaded/$total"
} else {
tbtnlist[index].text = "$downloaded/$total"
Toast.makeText(that?.context, "下载${tbtnlist[index].chapterName}的第${downloaded}页失败", Toast.LENGTH_SHORT).show()
Toast.makeText(that?.context, "下载${tbtnlist[index].chapterName}的第${downloaded}页失败: $message", Toast.LENGTH_SHORT).show()
}
}
}
@@ -283,6 +285,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
updateProgressBar()
that?.apply {
cdwn.postDelayed({
if (mangaDlTools?.exit != false) return@postDelayed
if (dldChapter == checkedChapter) {
checkedChapter = 0
setProgress2(0, 233)
@@ -296,9 +299,9 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
}
}
private fun onZipDownloadFailure(index: Int) {
private fun onZipDownloadFailure(index: Int, message: String) {
tbtnlist[index].setBackgroundResource(R.drawable.rndbg_error)
Toast.makeText(that?.context, "下载${tbtnlist[index].chapterName}失败", Toast.LENGTH_SHORT).show()
Toast.makeText(that?.context, "下载${tbtnlist[index].chapterName}失败: $message", Toast.LENGTH_SHORT).show()
updateProgressBar()
}

View File

@@ -1,9 +1,14 @@
package top.fumiama.copymanga.ui.download
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import android.provider.DocumentsContract
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
@@ -124,12 +129,12 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
AlertDialog.Builder(context)
.setIcon(R.drawable.ic_launcher_foreground)
.setTitle(R.string.new_download_card_option_hint)
.setItems(arrayOf("删除", "前往")) { d, p ->
.setItems(arrayOf("删除数据", "前往详情")) { d, p ->
d.cancel()
when (p) {
0 -> {
AlertDialog.Builder(context)
.setIcon(R.drawable.ic_launcher_foreground).setMessage("删除下载的漫画吗?")
.setIcon(R.drawable.ic_launcher_foreground).setMessage("删除下载的漫画${name}吗?")
.setTitle("提示").setPositiveButton(android.R.string.ok) { _, _ ->
if (chosenFile.exists()) Thread {
FileUtils.recursiveRemove(chosenFile)

View File

@@ -10,7 +10,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageButton
import androidx.appcompat.view.ContextThemeWrapper
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -29,8 +28,8 @@ import top.fumiama.copymanga.json.BookListStructure
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.dmzj.copymanga.R
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
@@ -72,13 +71,20 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
}
})
setTextHint(android.R.string.search_go)
setOnQueryTextListener(object : SearchLayout.OnQueryTextListener {
var lastChangeTime = 0L
var lastSearch: String = ""
override fun onQueryTextChange(newText: CharSequence): Boolean {
if (newText.contentEquals("__notice_focus_change__") || newText.contentEquals(lastSearch)) return true
postDelayed({
val diff = System.currentTimeMillis() - lastChangeTime
if(diff > 500) {
if (newText.isNotEmpty()) adapter.refresh(newText)
if (newText.isNotEmpty()) {
Log.d("MyHF", "new text: $newText")
lastSearch = newText.toString()
adapter.refresh(newText)
}
}
}, 1024)
lastChangeTime = System.currentTimeMillis()
@@ -90,6 +96,8 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
val key = query.toString()
Toast.makeText(context, key, Toast.LENGTH_SHORT).show()
}*/
Log.d("MyHF", "recover text: $lastSearch")
setTextQuery(lastSearch, false)
return true
}
})
@@ -112,9 +120,12 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
setOnFocusChangeListener(object : SearchLayout.OnFocusChangeListener {
override fun onFocusChange(hasFocus: Boolean) {
Log.d("MyHF", "fhs onFocusChange: $hasFocus")
navigationIconSupport = if (hasFocus) SearchLayout.NavigationIconSupport.ARROW
navigationIconSupport = if (hasFocus) {
setTextQuery("__notice_focus_change__", true)
SearchLayout.NavigationIconSupport.ARROW
}
else {
micView.postDelayed({ micView.visibility = View.VISIBLE }, 233)
micView.postDelayed({ micView?.visibility = View.VISIBLE }, 233)
SearchLayout.NavigationIconSupport.SEARCH
}
}
@@ -171,7 +182,7 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
//Log.d("MyHomeFVP", "Load img: $it")
Glide.with(this@HomeFragment).load(
GlideUrl(CMApi.proxy?.wrap(it)?:it, CMApi.myGlideHeaders)
).timeout(10000).into(holder.itemView.vpi)
).addListener(GlideHideLottieViewListener(WeakReference(holder.itemView.lai))).timeout(10000).into(holder.itemView.vpi)
}
holder.itemView.vpt.text = thisBanner?.brief
holder.itemView.vpc.setOnClickListener {
@@ -190,7 +201,7 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
RecyclerView.Adapter<ListViewHolder>() {
private var results: BookListStructure? = null
var type = ""
private var query: CharSequence? = null
private var query: String? = null
private var count = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
return ListViewHolder(
@@ -228,7 +239,10 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
}
holder.itemView.tb.text = popular.toString()
context?.let {
Glide.with(it).load(GlideUrl(CMApi.proxy?.wrap(cover)?:cover, CMApi.myGlideHeaders)).into(holder.itemView.imic)
Glide.with(it)
.load(GlideUrl(CMApi.proxy?.wrap(cover)?:cover, CMApi.myGlideHeaders))
.addListener(GlideHideLottieViewListener(WeakReference(holder.itemView.laic)))
.into(holder.itemView.imic)
}
holder.itemView.lwc.setOnClickListener {
val bundle = Bundle()
@@ -239,10 +253,10 @@ class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
}
}
override fun getItemCount() = (results?.results?.list?.size?:0)+1
override fun getItemCount() = (results?.results?.list?.size?:0) + if (query?.isNotEmpty() == true) 1 else 0
fun refresh(q: CharSequence) {
query = q
query = q.toString()
activity?.apply {
AutoDownloadThread(getString(R.string.searchApiUrl).format(CMApi.myHostApiUrl, 0, query, type)) {
results = Gson().fromJson(it?.decodeToString(), BookListStructure::class.java)

View File

@@ -21,13 +21,14 @@ import com.to.aboomy.pager2banner.ScaleInTransformer
import kotlinx.android.synthetic.main.card_book.view.*
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.line_1bookline.view.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.json.ComicStructure
import top.fumiama.copymanga.json.IndexStructure
import top.fumiama.copymanga.template.http.AutoDownloadHandler
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.dmzj.copymanga.R
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
@@ -310,7 +311,9 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
cv.tic.text = name
homeF?.let {
if(img.startsWith("http")) it.activity?.runOnUiThread {
Glide.with(it).load(GlideUrl(CMApi.proxy?.wrap(img)?:img, CMApi.myGlideHeaders)).timeout(20000).into(cv.imic)
Glide.with(it).load(GlideUrl(CMApi.proxy?.wrap(img)?:img, CMApi.myGlideHeaders))
.addListener(GlideHideLottieViewListener(WeakReference(cv.laic)))
.timeout(20000).into(cv.imic)
}
}
if (isFinal) cv.sgnic.visibility = View.VISIBLE

View File

@@ -4,12 +4,15 @@ import android.widget.Toast
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.comicName
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.position
import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference
class PagesManager(private val w: WeakReference<ViewMangaActivity>) {
val v get() = w.get()
private var isEndL = false
private var isEndR = false
private val canGoPrevious get() = (v?.pageNum ?: 0) > 1
private val canGoNext get() = (v?.pageNum ?: 0) < (v?.realCount ?: 0)
@ExperimentalStdlibApi
fun toPreviousPage(){
toPage(v?.r2l==true)
@@ -18,16 +21,14 @@ class PagesManager(private val w: WeakReference<ViewMangaActivity>) {
fun toNextPage(){
toPage(v?.r2l!=true)
}
private fun judgePrevious() = (v?.pageNum ?: 0) > 1
private fun judgeNext() = (v?.pageNum ?: 0) < (v?.realCount ?: 0)
@ExperimentalStdlibApi
fun toPage(goNext:Boolean) {
v?.let { v ->
if (v.clicked) {
v.hideObjs()
v.hideDrawer()
return
}
if (if(goNext)judgeNext() else judgePrevious()) {
if (if(goNext) canGoNext else canGoPrevious) {
if(goNext) {
v.scrollForward()
isEndR = false
@@ -38,33 +39,26 @@ class PagesManager(private val w: WeakReference<ViewMangaActivity>) {
return
}
val chapterPosition = position + if(goNext) 1 else -1
if (v.urlArray.isNotEmpty()) {
if(chapterPosition >= 0 && chapterPosition < v.urlArray.size) v.urlArray[chapterPosition].let {
if (if(goNext) isEndR else isEndL) {
//if(v.zipFirst) intent.putExtra("callFrom", "zipFirst")
v.tt.canDo = false
//ViewMangaActivity.dlhandler = null
comicName?.let { it1 -> Reader.viewMangaAt(it1, chapterPosition, v.urlArray, goNext) }
v.finish()
return
}
val hint = if(goNext) '下' else '上'
Toast.makeText(
v.applicationContext,
"再次按下加载${hint}一章",
Toast.LENGTH_SHORT
).show()
if(goNext) isEndR = true
else isEndL = true
} else Toast.makeText(
v.applicationContext,
"已经到头了~",
Toast.LENGTH_SHORT
).show()
if (v.urlArray.isEmpty()) return
if(chapterPosition < 0 || chapterPosition >= v.urlArray.size) {
Toast.makeText(v.applicationContext, R.string.end_of_chapter, Toast.LENGTH_SHORT).show()
return
}
if (if(goNext) isEndR else isEndL) {
//if(v.zipFirst) intent.putExtra("callFrom", "zipFirst")
v.tt.canDo = false
//ViewMangaActivity.dlhandler = null
comicName?.let { Reader.viewMangaAt(it, chapterPosition, v.urlArray, goNext) }
v.finish()
return
}
val hint = if(goNext) R.string.press_again_to_load_next_chapter else R.string.press_again_to_load_previous_chapter
Toast.makeText(v.applicationContext, hint, Toast.LENGTH_SHORT).show()
if(goNext) isEndR = true
else isEndL = true
}
}
fun manageInfo(){
if (v?.clicked == false) v?.showObjs() else v?.hideObjs()
fun toggleDrawer() {
if (v?.clicked == false) v?.showDrawer() else v?.hideDrawer()
}
}

View File

@@ -7,11 +7,11 @@ import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.View
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.google.gson.Gson
import kotlinx.android.synthetic.main.activity_viewmanga.*
import kotlinx.android.synthetic.main.widget_infodrawer.*
import kotlinx.android.synthetic.main.widget_infodrawer.view.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.json.Chapter2Return
import top.fumiama.copymanga.json.ChapterWithContent
import top.fumiama.copymanga.json.ComicStructure
@@ -21,21 +21,20 @@ import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.pn
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.position
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.uuidArray
import top.fumiama.copymanga.views.ScaleImageView
import top.fumiama.dmzj.copymanga.R
import java.io.File
import java.lang.Exception
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
url, Chapter2Return::class.java, Looper.myLooper()!!
) {
var manga: Chapter2Return? = null
private val wv = WeakReference(activity)
private val infcard = wv.get()?.infcard
private var infcShowed = false
private val drawer = wv.get()?.infcard
private val weeks = wv.get()?.getStringArray(R.array.weeks)
private var hasDrawerShown = false
val dl = activity.let {
val re = Dialog(it)
re.setContentView(R.layout.dialog_unzipping)
@@ -49,16 +48,9 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
private val week: String
get() {
val cal = Calendar.getInstance()
return when (cal[Calendar.DAY_OF_WEEK]) {
1 -> "周日"
2 -> "周一"
3 -> "周二"
4 -> "周三"
5 -> "周四"
6 -> "周五"
7 -> "周六"
else -> ""
}
val w = cal[Calendar.DAY_OF_WEEK]
if (w > 7 || w <= 0) return ""
return weeks?.get(w-1) ?: ""
}
private var remainingImageCount = 0
@@ -66,21 +58,22 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
HIDE_INFO_CARD -> if (infcShowed) {
hideInfCard(); infcShowed = false
HIDE_INFO_CARD -> if (hasDrawerShown) {
hideInfCard(); hasDrawerShown = false
}
SHOW_INFO_CARD -> if (!infcShowed) {
showInfCard(); infcShowed = true
SHOW_INFO_CARD -> if (!hasDrawerShown) {
showInfCard(); hasDrawerShown = true
}
TRIGGER_INFO_CARD -> infcShowed = if (infcShowed) {
TRIGGER_INFO_CARD -> hasDrawerShown = if (hasDrawerShown) {
hideInfCard(); false
} else {
showInfCard(); true
}
LOAD_IMG_ON -> {
val simg = msg.obj as ScaleImageView
wv.get()?.loadImgOn(simg, msg.arg1, msg.arg2)
//simg.setHeight2FitImgWidth()
val scaleImageView = msg.obj as ScaleImageView
// msg.arg2: isLast
wv.get()?.loadImgOn(scaleImageView, msg.arg1)
//scaleImageView.setHeight2FitImgWidth()
//if(msg.arg2 == 1) sendEmptyMessage(DELAYED_RESTORE_PAGE_NUMBER)
}
CLEAR_IMG_ON -> {
@@ -106,13 +99,13 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
Log.d("MyVMH", "Load page from $item")
}
DIALOG_HIDE -> dl.hide()
HIDE_INFO_CARD_FULL -> if (infcShowed) {
hideInfCardFull(); infcShowed = false
HIDE_INFO_CARD_FULL -> if (hasDrawerShown) {
hideInfCardFull(); hasDrawerShown = false
}
SHOW_INFO_CARD_FULL -> if (!infcShowed) {
showInfCardFull(); infcShowed = true
SHOW_INFO_CARD_FULL -> if (!hasDrawerShown) {
showInfCardFull(); hasDrawerShown = true
}
TRIGGER_INFO_CARD_FULL -> infcShowed = if (infcShowed) {
TRIGGER_INFO_CARD_FULL -> hasDrawerShown = if (hasDrawerShown) {
hideInfCardFull(); false
} else {
showInfCardFull(); true
@@ -147,7 +140,7 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
override fun onError() {
super.onError()
if(exit) return
wv.get()?.toolsBox?.toastError("下载章节信息失败")
wv.get()?.toolsBox?.toastError(R.string.download_chapter_info_failed)
}
override fun doWhenFinishDownload() {
@@ -181,7 +174,7 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
true
} catch (e: Exception){
e.printStackTrace()
//wv.get()?.toolsBox?.toastError("读取本地章节信息失败")
wv.get()?.toolsBox?.toastError(R.string.load_local_chapter_info_failed)
false
}
}
@@ -225,23 +218,23 @@ class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
private fun showInfCard() {
Log.d("MyVMH", "Read info drawer delta: $delta")
ObjectAnimator.ofFloat(infcard?.idc, "alpha", 0.3F, 0.8F).setDuration(233).start()
ObjectAnimator.ofFloat(infcard, "translationY", delta, 0F).setDuration(233).start()
ObjectAnimator.ofFloat(drawer?.idc, "alpha", 0.3F, 0.8F).setDuration(233).start()
ObjectAnimator.ofFloat(drawer, "translationY", delta, 0F).setDuration(233).start()
}
private fun showInfCardFull() {
Log.d("MyVMH", "Read info drawer delta: $delta")
ObjectAnimator.ofFloat(infcard?.idc, "alpha", 0.0F, 0.8F).setDuration(233).start()
ObjectAnimator.ofFloat(infcard, "translationY", delta, 0F).setDuration(233).start()
ObjectAnimator.ofFloat(drawer?.idc, "alpha", 0.0F, 0.8F).setDuration(233).start()
ObjectAnimator.ofFloat(drawer, "translationY", delta, 0F).setDuration(233).start()
}
private fun hideInfCard() {
ObjectAnimator.ofFloat(infcard?.idc, "alpha", 0.8F, 0.3F).setDuration(233).start()
ObjectAnimator.ofFloat(infcard, "translationY", 0F, delta).setDuration(233).start()
ObjectAnimator.ofFloat(drawer?.idc, "alpha", 0.8F, 0.3F).setDuration(233).start()
ObjectAnimator.ofFloat(drawer, "translationY", 0F, delta).setDuration(233).start()
}
private fun hideInfCardFull() {
ObjectAnimator.ofFloat(infcard?.idc, "alpha", 0.8F, 0.0F).setDuration(233).start()
ObjectAnimator.ofFloat(infcard, "translationY", 0F, delta).setDuration(233).start()
ObjectAnimator.ofFloat(drawer?.idc, "alpha", 0.8F, 0.0F).setDuration(233).start()
ObjectAnimator.ofFloat(drawer, "translationY", 0F, delta).setDuration(233).start()
}
companion object {

View File

@@ -8,6 +8,7 @@ import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.media.AudioManager
import android.os.Bundle
import android.os.Handler
@@ -20,10 +21,9 @@ import androidx.core.content.edit
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.liaoinstan.springview.widget.SpringView
import kotlinx.android.synthetic.main.activity_viewmanga.*
@@ -36,14 +36,14 @@ import kotlinx.android.synthetic.main.widget_titlebar.*
import kotlinx.android.synthetic.main.widget_titlebar.view.*
import kotlinx.android.synthetic.main.widget_viewmangainfo.*
import top.fumiama.copymanga.MainActivity
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.template.general.TitleActivityTemplate
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.Font
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.copymanga.tools.thread.TimeThread
import top.fumiama.copymanga.tools.ui.Font
import top.fumiama.copymanga.views.ScaleImageView
import top.fumiama.dmzj.copymanga.R
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
@@ -51,6 +51,7 @@ import java.io.InputStream
import java.lang.ref.WeakReference
import java.util.concurrent.FutureTask
import java.util.zip.ZipFile
import kotlin.math.abs
class ViewMangaActivity : TitleActivityTemplate() {
var count = 0
@@ -69,6 +70,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
private var notUseVP = true
private var isVertical = false
private var q = 100
private var tryWebpFirst = true
private val size get() = if(realCount / verticalLoadMaxCount > currentItem / verticalLoadMaxCount) verticalLoadMaxCount else realCount % verticalLoadMaxCount
var infoDrawerDelta = 0f
var pageNum: Int
@@ -130,10 +132,11 @@ class ViewMangaActivity : TitleActivityTemplate() {
} else prepareImgFromWeb()
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError("加载漫画错误")
toolsBox.toastError(R.string.load_manga_error)
}
}
@Suppress("DEPRECATION")
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R)
@@ -144,7 +147,6 @@ class ViewMangaActivity : TitleActivityTemplate() {
hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
}
@@ -183,7 +185,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
private fun preDownloadChapterPages() {
getImgUrlArray()?.let {
val mid = (if(pn in 1 until realCount) (if(cut) Math.abs(indexMap[pn]) else pn) else if(pn == -2 || pn >= realCount) it.size else 1) - 1
val mid = (if(pn in 1 until realCount) (if(cut) abs(indexMap[pn]) else pn) else if(pn == -2 || pn >= realCount) it.size else 1) - 1
val left = if(isVertical && mid > verticalLoadMaxCount) (mid / verticalLoadMaxCount) * verticalLoadMaxCount else (mid-1)
val right = if(isVertical) (mid / verticalLoadMaxCount + 1) * verticalLoadMaxCount else mid
tasks = arrayOfNulls(it.size)
@@ -277,7 +279,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
fun countZipEntries(doWhenFinish : (count: Int) -> Unit) = Thread{
if (zipFile != null) try {
Log.d("Myvm", "zip: $zipFile")
Log.d("MyVM", "zip: $zipFile")
val zip = ZipFile(zipFile)
count = zip.size()
if(cut) zip.entries().toList().sortedBy{it.name.substringBefore('.').toInt()}.forEachIndexed { i, it ->
@@ -285,13 +287,13 @@ class ViewMangaActivity : TitleActivityTemplate() {
isCut += useCut
indexMap += i + 1
if (useCut) indexMap += -(i + 1)
Log.d("Myvm", "[$i] 分析: ${it.name}, cut: $useCut")
Log.d("MyVM", "[$i] 分析: ${it.name}, cut: $useCut")
}
} catch (e: Exception) {
runOnUiThread { toolsBox.toastError("统计zip图片数错误!") }
runOnUiThread { toolsBox.toastError(R.string.count_zip_entries_error) }
}
runOnUiThread {
Log.d("Myvm", "开始加载控件")
Log.d("MyVM", "开始加载控件")
doWhenFinish(count)
}
}.start()
@@ -318,7 +320,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
loadOneImg()
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError("页数${currentItem}不合法")
toolsBox.toastError(getString(R.string.load_page_number_error).format(currentItem))
}
}
} else {
@@ -364,22 +366,21 @@ class ViewMangaActivity : TitleActivityTemplate() {
private fun cutBitmap(bitmap: Bitmap, isEnd: Boolean) = Bitmap.createBitmap(bitmap, if(!isEnd) 0 else (bitmap.width/2), 0, bitmap.width/2, bitmap.height)
private fun loadImg(imgView: ScaleImageView, bitmap: Bitmap, isLast: Int = 0, useCut: Boolean, isLeft: Boolean, isPlaceholder: Boolean = true) {
private fun loadImg(imgView: ScaleImageView, bitmap: Bitmap, useCut: Boolean, isLeft: Boolean, isPlaceholder: Boolean = true) {
val bitmap2load = if(!isPlaceholder && useCut) cutBitmap(bitmap, isLeft) else bitmap
runOnUiThread {
imgView.setImageBitmap(bitmap2load)
if(!isPlaceholder && isVertical) {
imgView.setHeight2FitImgWidth()
handler.sendEmptyMessage(VMHandler.DECREASE_IMAGE_COUNT_AND_RESTORE_PAGE_NUMBER_AT_ZERO)
//if (!isPlaceholder && isLast == 1) handler.sendEmptyMessageDelayed(VMHandler.RESTORE_PAGE_NUMBER, 233)
}
}
}
private fun loadImgUrlInto(imgView: ScaleImageView, url: String, isLast: Int = 0, useCut: Boolean, isLeft: Boolean){
private fun loadImgUrlInto(imgView: ScaleImageView, url: String, useCut: Boolean, isLeft: Boolean){
Log.d("MyVM", "Load from adt: $url")
AutoDownloadThread(CMApi.proxy?.wrap(url)?:url) {
it?.let { loadImg(imgView, BitmapFactory.decodeByteArray(it, 0, it.size), isLast, useCut, isLeft, false) }
AutoDownloadThread(CMApi.proxy?.wrap(url)?:url, 1000) {
it?.let { loadImg(imgView, BitmapFactory.decodeByteArray(it, 0, it.size), useCut, isLeft, false) }
}.start()
}
@@ -397,28 +398,28 @@ class ViewMangaActivity : TitleActivityTemplate() {
return loading
}
fun loadImgOn(imgView: ScaleImageView, position: Int, isLast: Int = 0){
fun loadImgOn(imgView: ScaleImageView, position: Int){
Log.d("MyVM", "Load img: $position")
val index2load = if(cut) Math.abs(indexMap[position]) -1 else position
val index2load = if(cut) abs(indexMap[position]) -1 else position
val useCut = cut && isCut[index2load]
val isLeft = cut && indexMap[position] > 0
if (zipFile?.exists() == true) getImgBitmap(index2load)?.let {
loadImg(imgView, it, isLast, useCut, isLeft, false)
loadImg(imgView, it, useCut, isLeft, false)
}
else {
loadImg(imgView, getLoadingBitmap(position), isLast, useCut, isLeft, true)
loadImg(imgView, getLoadingBitmap(position), useCut, isLeft, true)
val re = tasks?.get(index2load)
if (re != null) Thread{
val data = re.get()
if(data != null && data.isNotEmpty()) {
BitmapFactory.decodeByteArray(data, 0, data.size)?.let {
loadImg(imgView, it, isLast, useCut, isLeft, false)
loadImg(imgView, it, useCut, isLeft, false)
Log.d("MyVM", "Load position $position from task")
}?:Log.d("MyVM", "null bitmap at $position")
}
else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, isLast, useCut, isLeft) }
else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, useCut, isLeft) }
}.start()
else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, isLast, useCut, isLeft) }
else getImgUrl(index2load)?.let { loadImgUrlInto(imgView, it, useCut, isLeft) }
}
imgView.visibility = View.VISIBLE
}
@@ -445,27 +446,29 @@ class ViewMangaActivity : TitleActivityTemplate() {
if (position >= count || position < 0) null
else {
val zip = ZipFile(zipFile)
try {
if (q == 100) BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.webp")))
else {
val out = ByteArrayOutputStream()
BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.webp")))?.compress(Bitmap.CompressFormat.JPEG, q, out)
BitmapFactory.decodeStream(ByteArrayInputStream(out.toByteArray()))
}
} catch (e: Exception) {
try {
if (q == 100) BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.jpg")))
var bitmap: Bitmap? = null
for (i in 0..1) {
val ext = if((i == 0 && tryWebpFirst) || (i == 1 && !tryWebpFirst)) "webp" else "jpg"
bitmap = try {
if (q == 100) BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.$ext")))
else {
val out = ByteArrayOutputStream()
BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.jpg")))?.compress(Bitmap.CompressFormat.JPEG, q, out)
BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.$ext")))?.compress(Bitmap.CompressFormat.JPEG, q, out)
BitmapFactory.decodeStream(ByteArrayInputStream(out.toByteArray()))
}
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, "加载zip的第${position}项错误", Toast.LENGTH_SHORT).show()
if (i == 1) {
e.printStackTrace()
Toast.makeText(this, "加载zip的第${position}项错误", Toast.LENGTH_SHORT).show()
}
null
}
if (bitmap != null) {
tryWebpFirst = ext == "webp"
break
}
}
bitmap
}
private fun setIdPosition(position: Int) {
@@ -491,9 +494,10 @@ class ViewMangaActivity : TitleActivityTemplate() {
it["chapterId"] = hm.chapterId.toString()
it["name"] = inftitle.ttitle.text
}*/
}catch (e: Exception) {
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError("准备控件错误")
toolsBox.toastError(R.string.load_chapter_error)
finish()
}
}
@@ -512,7 +516,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
idtbcut.isChecked = cut
idtbcut.setOnClickListener {
pb["useCut"] = idtbcut.isChecked
Toast.makeText(this, "下次浏览生效", Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.take_effect_on_reload, Toast.LENGTH_SHORT).show()
}
}
@@ -520,7 +524,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
idtblr.isChecked = r2l
idtblr.setOnClickListener {
pb["r2l"] = idtblr.isChecked
Toast.makeText(this, "下次浏览生效", Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.take_effect_on_reload, Toast.LENGTH_SHORT).show()
}
}
@@ -528,7 +532,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
idtbvp.isChecked = notUseVP
idtbvp.setOnClickListener {
pb["noVP"] = idtbvp.isChecked
Toast.makeText(this, "下次浏览生效", Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.take_effect_on_reload, Toast.LENGTH_SHORT).show()
}
}
@@ -551,7 +555,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
}
fun updateSeekBar() {
if (!isInSeek) hideObjs()
if (!isInSeek) hideDrawer()
updateSeekText()
updateSeekProgress()
setProgress()
@@ -591,21 +595,22 @@ class ViewMangaActivity : TitleActivityTemplate() {
idtbvh.isChecked = isVertical
pm = PagesManager(WeakReference(this))
if (isVertical) {
val vsps = vsp as SpringView
vsps.footerView.lht.text = "更多"
vsps.headerView.lht.text = "更多"
vsps.setListener(object :SpringView.OnFreshListener{
override fun onLoadmore() {
//scrollForward()
pm?.toPage(true)
vsps.onFinishFreshAndLoad()
}
override fun onRefresh() {
//scrollBack()
pm?.toPage(false)
vsps.onFinishFreshAndLoad()
}
})
(vsp as SpringView).apply {
footerView.lht.setText(R.string.button_more)
headerView.lht.setText(R.string.button_more)
setListener(object :SpringView.OnFreshListener{
override fun onLoadmore() {
//scrollForward()
pm?.toPage(true)
onFinishFreshAndLoad()
}
override fun onRefresh() {
//scrollBack()
pm?.toPage(false)
onFinishFreshAndLoad()
}
})
}
vp.visibility = View.GONE
vsp.visibility = View.VISIBLE
initImgList()
@@ -623,7 +628,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
}
idtbvh.setOnClickListener {
pb["vertical"] = idtbvh.isChecked
Toast.makeText(this, "下次浏览生效", Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.take_effect_on_reload, Toast.LENGTH_SHORT).show()
}
}
@@ -633,7 +638,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
Log.d("MyVM", "Do scroll back, isVertical: $isVertical, pageNum: $pageNum")
handler.obtainMessage(VMHandler.LOAD_ITEM_SCROLL_MODE, currentItem - verticalLoadMaxCount, 0).sendToTarget() //loadImgsIntoLine(currentItem - verticalLoadMaxCount)
psivl.postDelayed({ pageNum-- }, 233)
}else pageNum--
} else pageNum--
}
fun scrollForward() {
@@ -674,7 +679,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
@SuppressLint("ClickableViewAccessibility", "SetTextI18n")
override fun onBindViewHolder(holder: ViewData, position: Int) {
val pos = if (r2l) realCount - position - 1 else position
val index2load = if(cut) Math.abs(indexMap[pos]) -1 else pos
val index2load = if(cut) abs(indexMap[pos]) -1 else pos
val useCut = cut && isCut[index2load]
val isLeft = cut && indexMap[pos] > 0
if (zipFile?.exists() == true) getImgBitmap(index2load)?.let {
@@ -684,15 +689,17 @@ class ViewMangaActivity : TitleActivityTemplate() {
else getImgUrl(index2load)?.let{
if(useCut){
val thisOneI = holder.itemView.onei
Glide.with(this@ViewMangaActivity)
Glide.with(this@ViewMangaActivity.applicationContext)
.asBitmap()
.load(GlideUrl(CMApi.proxy?.wrap(it)?:it, CMApi.myGlideHeaders))
.placeholder(BitmapDrawable(resources, getLoadingBitmap(pos)))
.into(object : SimpleTarget<Bitmap>() {
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
thisOneI.setImageBitmap(cutBitmap(resource, isLeft))
} })
} else Glide.with(this@ViewMangaActivity)
}
override fun onLoadCleared(placeholder: Drawable?) { }
})
} else Glide.with(this@ViewMangaActivity.applicationContext)
.load(GlideUrl(CMApi.proxy?.wrap(it)?:it, CMApi.myGlideHeaders))
.placeholder(BitmapDrawable(resources, getLoadingBitmap(pos)))
.into(holder.itemView.onei)
@@ -705,7 +712,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
}
}
fun showObjs() {
fun showDrawer() {
infseek.visibility = View.VISIBLE
isearch.visibility = View.VISIBLE
ObjectAnimator.ofFloat(
@@ -717,7 +724,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
clicked = true
}
fun hideObjs() {
fun hideDrawer() {
ObjectAnimator.ofFloat(
oneinfo,
"alpha",

View File

@@ -1,12 +1,9 @@
package top.fumiama.copymanga.user
import android.content.SharedPreferences
import android.widget.Toast
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.json.LoginInfoStructure
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.http.DownloadTools
@@ -47,6 +44,12 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
fun refreshAvatar() : LoginInfoStructure {
if (!pref.contains("token")) {
val l = LoginInfoStructure()
l.code = 400
l.message = getString(R.string.noLogin)
return l
}
try {
DownloadTools.getHttpContent(getString(R.string.memberInfoApiUrl).format(
CMApi.myHostApiUrl))?.decodeToString()?.let {
@@ -65,7 +68,7 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
}
val l = LoginInfoStructure()
l.code = 400
l.message = getString(R.string.login_get_avatar_failed)
l.message = getString(R.string.login_get_avatar_failed)
return l
}

View File

@@ -5,7 +5,7 @@ import android.util.AttributeSet
import androidx.cardview.widget.CardView
import java.io.File
class MangaCardView:CardView {
class MangaCardView :CardView {
constructor(context: Context): super(context)
constructor(context: Context, attrs: AttributeSet?): super (context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr)

View File

@@ -16,6 +16,7 @@ import android.view.MotionEvent
import android.widget.ImageView
import top.fumiama.copymanga.ui.vm.PagesManager
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference
import java.util.*
import kotlin.math.sqrt
@@ -563,7 +564,7 @@ class ScaleImageView : ImageView {
}
}catch (e:Exception){
e.printStackTrace()
ViewMangaActivity.va?.get()?.toolsBox?.toastError("图片加载错误,请尝试下载后使用较低图片质量查看", false)
ViewMangaActivity.va?.get()?.toolsBox?.toastError(R.string.show_image_error_try_lower_resolution, false)
}
}
////////////////////////////////有效性判断////////////////////////////////
@@ -688,7 +689,7 @@ class ScaleImageView : ImageView {
(event.x / width).let {
when {
it <= 1.0 / 3.0 -> pm?.toPreviousPage()
it <= 2.0 / 3.0 -> pm?.manageInfo()
it <= 2.0 / 3.0 -> pm?.toggleDrawer()
else -> pm?.toNextPage()
}
}

View File

@@ -16,16 +16,13 @@
android:id="@+id/coordiv"
android:layout_width="match_parent"
android:layout_height="16dp"
android:background="?attr/colorPrimarySurface"
android:visibility="visible"
app:layout_scrollFlags="scroll|enterAlways" />
android:background="?attr/colorPrimarySurface" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimarySurface"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>

View File

@@ -37,6 +37,19 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/laic"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:lottie_autoPlay="true"
app:lottie_loop="true"
app:lottie_rawRes="@raw/lottie_loading" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -36,6 +36,19 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/laic"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:lottie_autoPlay="true"
app:lottie_loop="true"
app:lottie_rawRes="@raw/lottie_loading" />
<include
android:id="@+id/sgnic"
layout="@layout/widget_finalmark"

View File

@@ -26,6 +26,19 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lai"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:lottie_autoPlay="true"
app:lottie_loop="true"
app:lottie_rawRes="@raw/lottie_loading" />
<ImageView
android:id="@+id/vpi"
android:layout_width="0dp"

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,8 @@
]>
<resources>
<string name="app_name">拷贝漫画</string>
<string name="file_provider_authority">top.fumiama.copymanga.fileprovider</string>
<string name="action_settings">设定</string>
<string name="action_info">关于</string>
<string name="action_download">下载</string>
@@ -40,6 +42,17 @@
<string name="web_error">网络错误</string>
<string name="download_cover_failed">保存封面失败</string>
<string name="download_cover_timeout">保存封面超时</string>
<string name="take_effect_on_reload">下次浏览生效</string>
<string name="end_of_chapter">已经到头了~</string>
<string name="press_again_to_load_previous_chapter">再次按下加载上一章</string>
<string name="press_again_to_load_next_chapter">再次按下加载下一章</string>
<string name="load_local_chapter_info_failed">读取本地章节信息失败</string>
<string name="download_chapter_info_failed">下载章节信息失败</string>
<string name="load_manga_error">加载漫画错误</string>
<string name="count_zip_entries_error">统计zip图片数错误</string>
<string name="load_page_number_error">第%1$d页加载异常</string>
<string name="load_chapter_error">加载章节错误</string>
<string name="show_image_error_try_lower_resolution">图片加载错误,请尝试下载后使用较低图片质量查看</string>
<string name="mainPageApiUrl">https://%1$s/api/v3/h5/homeIndex?platform=3</string>
<string name="referUrl">https://%1$s</string>
@@ -153,4 +166,5 @@
<string name="old_download_card_name">前往旧版下载</string>
<string name="new_download_card_option_hint">选择您的操作</string>
<string name="choose_how_download_folder">选择如何打开漫画下载目录</string>
</resources>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="weeks">
<item>周日</item>
<item>周一</item>
<item>周二</item>
<item>周三</item>
<item>周四</item>
<item>周五</item>
<item>周六</item>
</string-array>
</resources>

View File

@@ -23,4 +23,3 @@ android.enableR8.fullMode=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false
org.gradle.unsafe.configuration-cache=true