diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 0000000..1ccd6e4
--- /dev/null
+++ b/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/fumiama.xml b/.idea/dictionaries/fumiama.xml
index b79f8e3..9904d8f 100644
--- a/.idea/dictionaries/fumiama.xml
+++ b/.idea/dictionaries/fumiama.xml
@@ -4,6 +4,7 @@
imgs
lowpan
nisi
+ reilia
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 757f88e..0897082 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -4,10 +4,8 @@
+\ No newline at end of file
+Index: app/src/main/res/layout/card_book.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>\n\n\n \n\n \n\n \n\n \n\n \n\n \n\n \n \n\n \n\n \n\n \n \n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/layout/card_book.xml b/app/src/main/res/layout/card_book.xml
+--- a/app/src/main/res/layout/card_book.xml (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/res/layout/card_book.xml (date 1709904811979)
+@@ -78,7 +78,6 @@
+ android:layout_marginEnd="16dp"
+ android:layout_marginBottom="4dp"
+ android:gravity="center_horizontal"
+- android:text="获取标题失败"
+ android:textAppearance="@style/TextAppearance.AppCompat.Body1"
+ android:textColor="@android:color/white"
+ android:textSize="12sp"
+Index: app/src/main/java/top/fumiama/copymanga/LoginActivity.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga\n\nimport android.os.Bundle\nimport android.util.Log\nimport android.widget.Toast\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.lifecycle.lifecycleScope\nimport kotlinx.android.synthetic.main.activity_login.*\nimport kotlinx.coroutines.launch\nimport top.fumiama.dmzj.copymanga.R\nimport kotlin.random.Random\n\n\nclass LoginActivity : AppCompatActivity() {\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_login)\n val pref = MainActivity.mainWeakReference?.get()?.getPreferences(MODE_PRIVATE) ?: return\n val isLogout = pref.getString(\"token\", null) != null\n if (isLogout) {\n alblogin.setText(R.string.logout)\n }\n alblogin.setOnClickListener {\n lifecycleScope.launch {\n val salt = Random.nextInt(10000)\n val username = altusrnm.text?.toString() ?: run {\n Toast.makeText(\n this@LoginActivity,\n R.string.login_null_username,\n Toast.LENGTH_SHORT\n ).show()\n return@launch\n }\n val pwd = altpwd.text?.toString() ?: run {\n Toast.makeText(this@LoginActivity, R.string.login_null_pwd, Toast.LENGTH_SHORT)\n .show()\n return@launch\n }\n if (isLogout) {\n MainActivity.member?.logout()\n MainActivity.mainWeakReference?.get()?.refreshUserInfo()\n Toast.makeText(\n this@LoginActivity,\n R.string.login_restart_to_apply,\n Toast.LENGTH_SHORT\n ).show()\n finish()\n return@launch\n }\n val l = MainActivity.member?.login(username, pwd, salt)\n Log.d(\"MyLA\", \"login return code: ${l?.code}\")\n if (l?.code == 200) {\n MainActivity.mainWeakReference?.get()?.refreshUserInfo()\n finish()\n return@launch\n }\n Toast.makeText(this@LoginActivity, l?.message, Toast.LENGTH_SHORT).show()\n }\n }\n }\n}\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt b/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt
+--- a/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt (date 1709886202030)
+@@ -19,6 +19,7 @@
+ val isLogout = pref.getString("token", null) != null
+ if (isLogout) {
+ alblogin.setText(R.string.logout)
++ altusrnm.setText(pref.getString("username", "N/A"))
+ }
+ alblogin.setOnClickListener {
+ lifecycleScope.launch {
+Index: app/src/main/res/layout/line_chapter.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>\n\n\n \n\n \n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/layout/line_chapter.xml b/app/src/main/res/layout/line_chapter.xml
+--- a/app/src/main/res/layout/line_chapter.xml (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/res/layout/line_chapter.xml (date 1709899238798)
+@@ -15,7 +15,7 @@
+ android:layout_marginStart="@dimen/nav_header_vertical_spacing"
+ android:layout_marginTop="@dimen/nav_header_vertical_spacing"
+ android:layout_marginBottom="@dimen/nav_header_vertical_spacing"
+- android:background="@drawable/ic_list"
++ android:background="@drawable/ic_circle"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+Index: app/src/main/res/drawable-anydpi/ic_success.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/drawable-anydpi/ic_success.xml b/app/src/main/res/drawable-anydpi/ic_success.xml
+new file mode 100644
+--- /dev/null (date 1709898923840)
++++ b/app/src/main/res/drawable-anydpi/ic_success.xml (date 1709898923840)
+@@ -0,0 +1,10 @@
++
++
++
++
+\ No newline at end of file
+Index: app/src/main/java/top/fumiama/copymanga/MainActivity.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga\n\nimport android.Manifest\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.content.Intent\nimport android.content.pm.PackageManager\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Bundle\nimport android.provider.MediaStore\nimport android.util.Log\nimport android.view.Menu\nimport android.view.MenuItem\nimport android.view.View\nimport android.view.inputmethod.InputMethodManager\nimport android.widget.TextView\nimport android.widget.Toast\nimport androidx.appcompat.app.AlertDialog\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.app.ActivityCompat\nimport androidx.core.content.ContextCompat\nimport androidx.core.content.edit\nimport androidx.core.net.toUri\nimport androidx.drawerlayout.widget.DrawerLayout\nimport androidx.navigation.NavController\nimport androidx.navigation.findNavController\nimport androidx.navigation.ui.AppBarConfiguration\nimport androidx.navigation.ui.navigateUp\nimport androidx.navigation.ui.setupActionBarWithNavController\nimport androidx.navigation.ui.setupWithNavController\nimport com.afollestad.materialdialogs.MaterialDialog\nimport com.afollestad.materialdialogs.input.input\nimport com.bumptech.glide.Glide\nimport com.bumptech.glide.load.resource.bitmap.CircleCrop\nimport com.bumptech.glide.request.RequestOptions\nimport com.yalantis.ucrop.UCrop\nimport kotlinx.android.synthetic.main.activity_main.*\nimport kotlinx.android.synthetic.main.app_bar_main.*\nimport kotlinx.android.synthetic.main.nav_header_main.*\nimport top.fumiama.copymanga.manga.Shelf\nimport top.fumiama.copymanga.tools.ui.UITools\nimport top.fumiama.copymanga.ui.book.BookFragment.Companion.bookHandler\nimport top.fumiama.copymanga.ui.cardflow.rank.RankFragment\nimport top.fumiama.copymanga.ui.comicdl.ComicDlFragment\nimport top.fumiama.copymanga.ui.download.DownloadFragment\nimport top.fumiama.copymanga.ui.download.NewDownloadFragment\nimport top.fumiama.copymanga.update.Update\nimport top.fumiama.copymanga.user.Member\nimport top.fumiama.dmzj.copymanga.BuildConfig\nimport top.fumiama.dmzj.copymanga.R\nimport java.io.File\nimport java.io.FileInputStream\nimport java.lang.Thread.sleep\nimport java.lang.ref.WeakReference\n\nclass MainActivity : AppCompatActivity() {\n private var menuMain: Menu? = null\n private var navController: NavController? = null\n\n private lateinit var appBarConfiguration: AppBarConfiguration\n private lateinit var headPic: File\n lateinit var toolsBox: UITools\n\n private var latestDestination = 0\n private var isMenuWaiting = false\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(null)\n\n // must init before setContentView because HomeF need them to init\n mainWeakReference = WeakReference(this)\n toolsBox = UITools(this)\n\n setContentView(R.layout.activity_main)\n setSupportActionBar(toolbar)\n coordiv.layoutParams.height = UITools.getStatusBarHeight(this)\n\n navController = findNavController(R.id.nav_host_fragment)\n appBarConfiguration = AppBarConfiguration(\n setOf(\n R.id.nav_home,\n R.id.nav_sort,\n R.id.nav_rank,\n R.id.nav_sub,\n R.id.nav_history,\n R.id.nav_new_download,\n R.id.nav_settings\n ), drawer_layout\n )\n setupActionBarWithNavController(navController!!, appBarConfiguration)\n nav_view.setupWithNavController(navController!!)\n\n headPic = File(getExternalFilesDir(\"\"), \"headPic\")\n drawer_layout.addDrawerListener(object : DrawerLayout.DrawerListener {\n override fun onDrawerClosed(drawerView: View) {\n Log.d(\"MyMain\", \"onDrawerClosed\")\n isDrawerClosed = true\n }\n\n override fun onDrawerOpened(drawerView: View) {\n Log.d(\"MyMain\", \"onDrawerOpened\")\n isDrawerClosed = false\n DownloadFragment.currentDir = getExternalFilesDir(\"\")\n refreshUserInfo()\n }\n\n override fun onDrawerSlide(drawerView: View, slideOffset: Float) {}\n override fun onDrawerStateChanged(newState: Int) {}\n })\n checkUpdate(false)\n\n ime = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager\n\n navController!!.addOnDestinationChangedListener { _, destination, _ ->\n latestDestination = destination.id\n Log.d(\"MyMA\", \"latestDestination: $latestDestination\")\n if (isMenuWaiting) {\n return@addOnDestinationChangedListener\n }\n isMenuWaiting = true\n Log.d(\"MyMA\", \"start menu waiting\")\n Thread {\n sleep(1000)\n isMenuWaiting = false\n Log.d(\"MyMA\", \"finish menu waiting\")\n runOnUiThread {\n when (latestDestination) {\n R.id.nav_home -> {\n Log.d(\"MyMA\", \"enter home\")\n menuMain?.findItem(R.id.action_info)?.isVisible = true\n menuMain?.findItem(R.id.action_download)?.isVisible = false\n menuMain?.findItem(R.id.action_sort)?.isVisible = false\n }\n R.id.nav_book -> {\n Log.d(\"MyMA\", \"enter book\")\n menuMain?.findItem(R.id.action_info)?.isVisible = false\n menuMain?.findItem(R.id.action_download)?.isVisible = true\n menuMain?.findItem(R.id.action_sort)?.isVisible = false\n }\n R.id.nav_group -> {\n Log.d(\"MyMA\", \"enter group\")\n menuMain?.findItem(R.id.action_info)?.isVisible = false\n menuMain?.findItem(R.id.action_download)?.isVisible = false\n menuMain?.findItem(R.id.action_sort)?.isVisible = true\n }\n R.id.nav_new_download -> {\n Log.d(\"MyMA\", \"enter new_download\")\n menuMain?.findItem(R.id.action_info)?.isVisible = false\n menuMain?.findItem(R.id.action_download)?.isVisible = false\n menuMain?.findItem(R.id.action_sort)?.isVisible = true\n }\n R.id.nav_rank -> {\n Log.d(\"MyMA\", \"enter rank\")\n menuMain?.findItem(R.id.action_info)?.isVisible = false\n menuMain?.findItem(R.id.action_download)?.isVisible = false\n menuMain?.findItem(R.id.action_sort)?.isVisible = true\n }\n else -> {\n Log.d(\"MyMA\", \"enter others\")\n menuMain?.findItem(R.id.action_info)?.isVisible = false\n menuMain?.findItem(R.id.action_download)?.isVisible = false\n menuMain?.findItem(R.id.action_sort)?.isVisible = false\n }\n }\n }\n }.start()\n }\n }\n\n override fun onCreateOptionsMenu(menu: Menu): Boolean {\n // Inflate the menu; this adds items to the action bar if it is present.\n menuInflater.inflate(R.menu.main, menu)\n menuMain = menu\n return true\n }\n\n @OptIn(ExperimentalStdlibApi::class)\n override fun onOptionsItemSelected(item: MenuItem): Boolean {\n return when (item.itemId) {\n R.id.action_info -> {\n showAbout()\n true\n }\n R.id.action_download -> {\n bookHandler?.sendEmptyMessage(6)\n true\n }\n R.id.action_sort -> {\n ComicDlFragment.handler?.sendEmptyMessage(13)\n NewDownloadFragment.wn?.get()?.showReverseInfo(toolsBox)\n RankFragment.wr?.get()?.showSexInfo(toolsBox)\n true\n }\n else -> super.onOptionsItemSelected(item)\n }\n }\n\n override fun onSupportNavigateUp(): Boolean {\n val navController = findNavController(R.id.nav_host_fragment)\n\n checkHeadPicture()\n if (headPic.exists()) navhbg.setOnLongClickListener {\n if (headPic.exists()) {\n val dl = AlertDialog.Builder(this)\n dl.setMessage(R.string.clearHeadImgMsg)\n dl.setPositiveButton(android.R.string.ok) { _, _ ->\n if (headPic.exists()) headPic.delete()\n navhbg.setImageResource(R.drawable.illust_57793944_20190427_134853)\n }\n dl.show()\n }\n true\n }\n navtinfo.text = getPreferences(MODE_PRIVATE).getString(\"navTextInfo\", getString(R.string.navTextInfo))\n return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()\n }\n\n @Deprecated(\"Deprecated in Java\")\n override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n super.onActivityResult(requestCode, resultCode, data)\n if (resultCode == RESULT_OK) when (requestCode) {\n UCrop.REQUEST_CROP -> {\n val fi = headPic.inputStream()\n navhbg.setImageBitmap(BitmapFactory.decodeStream(fi))\n fi.close()\n }\n MSG_CROP_IMAGE -> {\n data?.data?.let {\n saveFile(it)\n cropImageUri()\n }\n }\n }\n }\n\n override fun onRequestPermissionsResult(\n requestCode: Int,\n permissions: Array,\n grantResults: IntArray\n ) {\n super.onRequestPermissionsResult(requestCode, permissions, grantResults)\n when (requestCode) {\n MSG_CROP_IMAGE -> {\n if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) pickPicture()\n else Toast.makeText(this, R.string.permissionDenied, Toast.LENGTH_SHORT).show()\n }\n }\n }\n\n fun refreshUserInfo() {\n getPreferences(MODE_PRIVATE)?.apply {\n val name = getString(\"nickname\", getString(\"username\", \"\"))\n val avatar = getString(\"avatar\", \"\")\n if(name != \"\") navttitle.text = name\n else navttitle.setText(R.string.noLogin)\n if(avatar != \"\")\n Glide.with(this@MainActivity).load(avatar)\n .apply(RequestOptions.bitmapTransform(CircleCrop()))\n .into(navhicon)\n }\n }\n\n private fun checkReadPermission(): Boolean {\n return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N && ContextCompat.checkSelfPermission(\n this,\n Manifest.permission.READ_EXTERNAL_STORAGE\n ) != PackageManager.PERMISSION_GRANTED\n ) {\n ActivityCompat.requestPermissions(\n this,\n arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), MSG_CROP_IMAGE\n )\n false\n } else true\n }\n\n @SuppressLint(\"IntentReset\")\n private fun pickPicture() {\n val i = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)\n i.type = \"image/*\"\n startActivityForResult(i, MSG_CROP_IMAGE)\n }\n\n private fun saveFile(uri: Uri) {\n //val f = File(getExternalFilesDir(\"\"), \"headPic\")\n val fd = contentResolver.openFileDescriptor(uri, \"r\")\n fd?.fileDescriptor?.let {\n val fi = FileInputStream(it)\n val fo = headPic.outputStream()\n fi.copyTo(fo)\n fi.close()\n fo.close()\n }\n fd?.close()\n }\n\n private fun checkHeadPicture() {\n //val hp = File(getExternalFilesDir(\"\"), \"headPic\")\n if (headPic.exists()) navhbg.setImageURI(headPic.toUri())\n }\n\n private fun cropImageUri() {\n val op = UCrop.Options()\n val r = navhbg.width.toFloat() / navhbg.height.toFloat()\n Log.d(\"MyMain\", \"Img info: (${navhbg.width}, ${navhbg.height})\")\n Log.d(\"MyMain\", \"Result code: ${UCrop.REQUEST_CROP}\")\n if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n op.setCompressionFormat(Bitmap.CompressFormat.WEBP_LOSSY)\n } else {\n op.setCompressionFormat(Bitmap.CompressFormat.WEBP)\n }\n op.setStatusBarColor(resources.getColor(R.color.colorPrimaryDark, theme))\n op.setToolbarColor(resources.getColor(R.color.colorPrimary, theme))\n op.setActiveControlsWidgetColor(resources.getColor(R.color.colorAccent, theme))\n UCrop.of(headPic.toUri(), headPic.toUri())\n .withAspectRatio(r, 1F)\n .withMaxResultSize(navhbg.width, navhbg.height)\n .withOptions(op)\n .start(this)\n }\n\n private fun checkUpdate(ignoreSkip: Boolean) {\n Thread{\n Update.checkUpdate(this, toolsBox, ignoreSkip)\n }.start()\n }\n\n private fun showAbout() {\n val dl = android.app.AlertDialog.Builder(this)\n dl.setMessage(R.string.app_description)\n dl.setTitle(\"${getString(R.string.action_info)} ${BuildConfig.VERSION_NAME}\")\n dl.setIcon(R.mipmap.ic_launcher)\n dl.setPositiveButton(android.R.string.ok) { _, _ -> }\n dl.setNeutralButton(R.string.check_update) {_, _ ->\n checkUpdate(true)\n }\n dl.show()\n }\n\n @SuppressLint(\"CheckResult\")\n fun onNavTInfoClicked(it: View) {\n MaterialDialog(this).show {\n input(prefill = (it as TextView).text) { _, charSequence ->\n it.text = charSequence\n getPreferences(MODE_PRIVATE).edit {\n putString(\"navTextInfo\", charSequence.toString())\n apply()\n }\n }\n positiveButton(android.R.string.ok)\n title(R.string.navTextInfoInputHint)\n }\n }\n\n fun onNavHBgClicked(v: View) {\n if (checkReadPermission()) pickPicture()\n }\n\n fun startLoginActivity(v: View){\n startActivity(Intent(this, LoginActivity::class.java))\n }\n\n companion object{\n var mainWeakReference: WeakReference? = null\n var isDrawerClosed = true\n var ime: InputMethodManager? = null\n const val MSG_CROP_IMAGE = 1\n var shelf: Shelf? = null\n get() {\n if (field != null) return field\n return mainWeakReference?.get()?.let {\n field = Shelf(\n it.getPreferences(Context.MODE_PRIVATE)\n .getString(\"token\", \"\")?:return@let null) { id ->\n return@Shelf it.getString(id)\n }\n field\n }\n }\n var member: Member? = null\n get() {\n if (field != null) return field\n return mainWeakReference?.get()?.let {\n it.getPreferences(MODE_PRIVATE)?.let { pref ->\n field = Member(pref) { id ->\n return@Member it.getString(id)\n }\n }\n field\n }\n }\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt
+--- a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt (date 1709887113979)
+@@ -2,6 +2,7 @@
+
+ import android.Manifest
+ import android.annotation.SuppressLint
++import android.app.Activity
+ import android.content.Context
+ import android.content.Intent
+ import android.content.pm.PackageManager
+@@ -18,6 +19,7 @@
+ import android.view.inputmethod.InputMethodManager
+ import android.widget.TextView
+ import android.widget.Toast
++import androidx.activity.result.contract.ActivityResultContracts
+ import androidx.appcompat.app.AlertDialog
+ import androidx.appcompat.app.AppCompatActivity
+ import androidx.core.app.ActivityCompat
+@@ -25,6 +27,7 @@
+ import androidx.core.content.edit
+ import androidx.core.net.toUri
+ import androidx.drawerlayout.widget.DrawerLayout
++import androidx.lifecycle.lifecycleScope
+ import androidx.navigation.NavController
+ import androidx.navigation.findNavController
+ import androidx.navigation.ui.AppBarConfiguration
+@@ -40,6 +43,9 @@
+ import kotlinx.android.synthetic.main.activity_main.*
+ import kotlinx.android.synthetic.main.app_bar_main.*
+ import kotlinx.android.synthetic.main.nav_header_main.*
++import kotlinx.coroutines.Dispatchers
++import kotlinx.coroutines.launch
++import kotlinx.coroutines.withContext
+ import top.fumiama.copymanga.manga.Shelf
+ import top.fumiama.copymanga.tools.ui.UITools
+ import top.fumiama.copymanga.ui.book.BookFragment.Companion.bookHandler
+@@ -64,7 +70,6 @@
+ private lateinit var headPic: File
+ lateinit var toolsBox: UITools
+
+- private var latestDestination = 0
+ private var isMenuWaiting = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+@@ -114,6 +119,7 @@
+
+ ime = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+
++ var latestDestination: Int
+ navController!!.addOnDestinationChangedListener { _, destination, _ ->
+ latestDestination = destination.id
+ Log.d("MyMA", "latestDestination: $latestDestination")
+@@ -122,51 +128,16 @@
+ }
+ isMenuWaiting = true
+ Log.d("MyMA", "start menu waiting")
+- Thread {
+- sleep(1000)
+- isMenuWaiting = false
+- Log.d("MyMA", "finish menu waiting")
+- runOnUiThread {
+- when (latestDestination) {
+- R.id.nav_home -> {
+- Log.d("MyMA", "enter home")
+- menuMain?.findItem(R.id.action_info)?.isVisible = true
+- menuMain?.findItem(R.id.action_download)?.isVisible = false
+- menuMain?.findItem(R.id.action_sort)?.isVisible = false
+- }
+- R.id.nav_book -> {
+- Log.d("MyMA", "enter book")
+- menuMain?.findItem(R.id.action_info)?.isVisible = false
+- menuMain?.findItem(R.id.action_download)?.isVisible = true
+- menuMain?.findItem(R.id.action_sort)?.isVisible = false
+- }
+- R.id.nav_group -> {
+- Log.d("MyMA", "enter group")
+- menuMain?.findItem(R.id.action_info)?.isVisible = false
+- menuMain?.findItem(R.id.action_download)?.isVisible = false
+- menuMain?.findItem(R.id.action_sort)?.isVisible = true
+- }
+- R.id.nav_new_download -> {
+- Log.d("MyMA", "enter new_download")
+- menuMain?.findItem(R.id.action_info)?.isVisible = false
+- menuMain?.findItem(R.id.action_download)?.isVisible = false
+- menuMain?.findItem(R.id.action_sort)?.isVisible = true
+- }
+- R.id.nav_rank -> {
+- Log.d("MyMA", "enter rank")
+- menuMain?.findItem(R.id.action_info)?.isVisible = false
+- menuMain?.findItem(R.id.action_download)?.isVisible = false
+- menuMain?.findItem(R.id.action_sort)?.isVisible = true
+- }
+- else -> {
+- Log.d("MyMA", "enter others")
+- menuMain?.findItem(R.id.action_info)?.isVisible = false
+- menuMain?.findItem(R.id.action_download)?.isVisible = false
+- menuMain?.findItem(R.id.action_sort)?.isVisible = false
+- }
+- }
+- }
+- }.start()
++ lifecycleScope.launch {
++ withContext(Dispatchers.IO) {
++ sleep(1000)
++ withContext(Dispatchers.Main) {
++ isMenuWaiting = false
++ Log.d("MyMA", "finish menu waiting")
++ changeMenuList(latestDestination)
++ }
++ }
++ }
+ }
+ }
+
+@@ -218,24 +189,6 @@
+ return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
+ }
+
+- @Deprecated("Deprecated in Java")
+- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+- super.onActivityResult(requestCode, resultCode, data)
+- if (resultCode == RESULT_OK) when (requestCode) {
+- UCrop.REQUEST_CROP -> {
+- val fi = headPic.inputStream()
+- navhbg.setImageBitmap(BitmapFactory.decodeStream(fi))
+- fi.close()
+- }
+- MSG_CROP_IMAGE -> {
+- data?.data?.let {
+- saveFile(it)
+- cropImageUri()
+- }
+- }
+- }
+- }
+-
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+@@ -263,6 +216,47 @@
+ }
+ }
+
++ private fun changeMenuList(latestDestination: Int) {
++ when (latestDestination) {
++ R.id.nav_home -> {
++ Log.d("MyMA", "enter home")
++ menuMain?.findItem(R.id.action_info)?.isVisible = true
++ menuMain?.findItem(R.id.action_download)?.isVisible = false
++ menuMain?.findItem(R.id.action_sort)?.isVisible = false
++ }
++ R.id.nav_book -> {
++ Log.d("MyMA", "enter book")
++ menuMain?.findItem(R.id.action_info)?.isVisible = false
++ menuMain?.findItem(R.id.action_download)?.isVisible = true
++ menuMain?.findItem(R.id.action_sort)?.isVisible = false
++ }
++ R.id.nav_group -> {
++ Log.d("MyMA", "enter group")
++ menuMain?.findItem(R.id.action_info)?.isVisible = false
++ menuMain?.findItem(R.id.action_download)?.isVisible = false
++ menuMain?.findItem(R.id.action_sort)?.isVisible = true
++ }
++ R.id.nav_new_download -> {
++ Log.d("MyMA", "enter new_download")
++ menuMain?.findItem(R.id.action_info)?.isVisible = false
++ menuMain?.findItem(R.id.action_download)?.isVisible = false
++ menuMain?.findItem(R.id.action_sort)?.isVisible = true
++ }
++ R.id.nav_rank -> {
++ Log.d("MyMA", "enter rank")
++ menuMain?.findItem(R.id.action_info)?.isVisible = false
++ menuMain?.findItem(R.id.action_download)?.isVisible = false
++ menuMain?.findItem(R.id.action_sort)?.isVisible = true
++ }
++ else -> {
++ Log.d("MyMA", "enter others")
++ menuMain?.findItem(R.id.action_info)?.isVisible = false
++ menuMain?.findItem(R.id.action_download)?.isVisible = false
++ menuMain?.findItem(R.id.action_sort)?.isVisible = false
++ }
++ }
++ }
++
+ private fun checkReadPermission(): Boolean {
+ return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N && ContextCompat.checkSelfPermission(
+ this,
+@@ -277,11 +271,18 @@
+ } else true
+ }
+
++ private var pickerLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
++ if (result.resultCode == Activity.RESULT_OK) result.data?.data?.let {
++ saveFile(it)
++ cropImageUri()
++ } else Toast.makeText(this, R.string.err_pick_img, Toast.LENGTH_SHORT).show()
++ }
++
+ @SuppressLint("IntentReset")
+ private fun pickPicture() {
+ val i = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
+ i.type = "image/*"
+- startActivityForResult(i, MSG_CROP_IMAGE)
++ pickerLauncher.launch(i)
+ }
+
+ private fun saveFile(uri: Uri) {
+@@ -302,11 +303,18 @@
+ if (headPic.exists()) navhbg.setImageURI(headPic.toUri())
+ }
+
++ private var cropLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
++ if (result.resultCode == Activity.RESULT_OK) {
++ val fi = headPic.inputStream()
++ navhbg.setImageBitmap(BitmapFactory.decodeStream(fi))
++ fi.close()
++ } else Toast.makeText(this, R.string.err_crop_img, Toast.LENGTH_SHORT).show()
++ }
++
+ private fun cropImageUri() {
+ val op = UCrop.Options()
+ val r = navhbg.width.toFloat() / navhbg.height.toFloat()
+ Log.d("MyMain", "Img info: (${navhbg.width}, ${navhbg.height})")
+- Log.d("MyMain", "Result code: ${UCrop.REQUEST_CROP}")
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ op.setCompressionFormat(Bitmap.CompressFormat.WEBP_LOSSY)
+ } else {
+@@ -315,17 +323,17 @@
+ op.setStatusBarColor(resources.getColor(R.color.colorPrimaryDark, theme))
+ op.setToolbarColor(resources.getColor(R.color.colorPrimary, theme))
+ op.setActiveControlsWidgetColor(resources.getColor(R.color.colorAccent, theme))
+- UCrop.of(headPic.toUri(), headPic.toUri())
++ cropLauncher.launch(UCrop.of(headPic.toUri(), headPic.toUri())
+ .withAspectRatio(r, 1F)
+ .withMaxResultSize(navhbg.width, navhbg.height)
+ .withOptions(op)
+- .start(this)
++ .getIntent(this))
+ }
+
+ private fun checkUpdate(ignoreSkip: Boolean) {
+- Thread{
+- Update.checkUpdate(this, toolsBox, ignoreSkip)
+- }.start()
++ lifecycleScope.launch {
++ Update.checkUpdate(this@MainActivity, toolsBox, ignoreSkip)
++ }
+ }
+
+ private fun showAbout() {
+Index: app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.ui.download\n\nimport android.app.AlertDialog\nimport android.content.Intent\nimport android.os.Bundle\nimport android.provider.DocumentsContract\nimport android.util.Log\nimport android.view.View\nimport android.widget.Toast\nimport androidx.core.content.FileProvider\nimport androidx.core.net.toUri\nimport androidx.fragment.app.Fragment\nimport androidx.navigation.fragment.findNavController\nimport androidx.preference.PreferenceManager\nimport kotlinx.android.synthetic.main.line_lazybooklines.*\nimport top.fumiama.copymanga.MainActivity\nimport top.fumiama.copymanga.manga.Reader\nimport top.fumiama.copymanga.template.general.MangaPagesFragmentTemplate\nimport top.fumiama.copymanga.template.ui.CardList\nimport top.fumiama.copymanga.tools.ui.Navigate\nimport top.fumiama.copymanga.tools.ui.UITools\nimport top.fumiama.copymanga.tools.file.FileUtils\nimport top.fumiama.copymanga.ui.comicdl.ComicDlFragment\nimport top.fumiama.dmzj.copymanga.R\nimport java.io.File\nimport java.lang.Thread.sleep\nimport java.lang.ref.WeakReference\n\n@OptIn(ExperimentalStdlibApi::class)\nclass NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownload, forceLoad = true) {\n private var sortedBookList: List? = null\n private val oldDlCardName = MainActivity.mainWeakReference?.get()?.getString(R.string.old_download_card_name)!!\n private val extDir = MainActivity.mainWeakReference?.get()?.getExternalFilesDir(\"\")\n private var isReverse = false\n private var isContentChanged = false\n private var exit = false\n private var showAll = false\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n wn = WeakReference(this)\n val settingsPref = MainActivity.mainWeakReference?.get()?.let { PreferenceManager.getDefaultSharedPreferences(it) }\n showAll = settingsPref?.getBoolean(\"settings_cat_md_sw_show_0m_manga\", false)?:false\n }\n\n override fun onPause() {\n super.onPause()\n exit = true\n }\n\n override fun onResume() {\n super.onResume()\n exit = false\n }\n\n override fun onDestroy() {\n super.onDestroy()\n wn = null\n exit = true\n }\n\n override fun addPage() {\n super.addPage()\n if(isRefresh){\n page = 0\n isRefresh = false\n }\n if(!isEnd) {\n if(sortedBookList == null || isContentChanged) {\n Log.d(\"MyNDF\", \"Sorting books...\")\n sortedBookList = extDir?.listFiles()?.sortedBy {\n return@sortedBy Reader.getComicPathWordInFile(it)\n }\n if (isReverse) {\n Log.d(\"MyNDF\", \"reversed...\")\n sortedBookList = sortedBookList?.asReversed()\n }\n if (!showAll) {\n sortedBookList = sortedBookList?.filter {\n return@filter FileUtils.sizeOf(it) / 1048576 > 0\n }\n }\n isContentChanged = false\n }\n Log.d(\"MyNDF\", \"Start drawing cards\")\n cardList?.addCard(oldDlCardName, path = oldDlCardName)\n var cnt = 1\n sortedBookList?.let {\n for(i in it.listIterator(page)) {\n if(cardList?.exitCardList != false) return\n page++ // page is actually count\n val chosenJson = File(i, \"info.bin\")\n val newJson = File(i, \"info.json\")\n val bookSize = (FileUtils.sizeOf(i)/1048576).toInt()\n when {\n chosenJson.exists() -> continue // unsupported old folder\n newJson.exists() -> {\n if(cardList?.exitCardList != false) return\n cardList?.addCard(i.name, \"\\n${bookSize}MB\")\n cnt++\n }\n }\n if (cnt >= 21) break\n }\n if(page >= it.size) {\n isEnd = true\n }\n }\n }\n onLoadFinish()\n }\n\n override fun initCardList(weakReference: WeakReference) {\n super.initCardList(weakReference)\n cardList = CardList(weakReference, cardWidth, cardHeight, cardPerRow)\n cardList?.initClickListeners = object : CardList.InitClickListeners {\n override fun prepareListeners(v: View, name: String, path: String?, chapterUUID: String?, pn: Int?) {\n v.setOnClickListener {\n if(name==oldDlCardName && path == oldDlCardName) {\n Navigate.safeNavigateTo(findNavController(), R.id.action_nav_new_download_to_nav_download)\n return@setOnClickListener\n }\n callDownloadFragment(name)\n }\n v.setOnLongClickListener {\n if (name == oldDlCardName && path == oldDlCardName) {\n return@setOnLongClickListener false\n }\n val chosenFile = File(extDir, name)\n AlertDialog.Builder(context)\n .setIcon(R.drawable.ic_launcher_foreground)\n .setTitle(R.string.new_download_card_option_hint)\n .setItems(arrayOf(\"删除数据\", \"前往详情\")) { d, p ->\n d.cancel()\n when (p) {\n 0 -> {\n AlertDialog.Builder(context)\n .setIcon(R.drawable.ic_launcher_foreground).setMessage(\"删除下载的漫画${name}吗?\")\n .setTitle(\"提示\").setPositiveButton(android.R.string.ok) { _, _ ->\n if (chosenFile.exists()) Thread {\n FileUtils.recursiveRemove(chosenFile)\n activity?.runOnUiThread {\n it.visibility = View.INVISIBLE\n }\n }.start()\n }.setNegativeButton(android.R.string.cancel) { _, _ -> }\n .show()\n }\n 1 -> {\n val bundle = Bundle()\n bundle.putBoolean(\"loadJson\", true)\n bundle.putString(\"name\", name)\n Navigate.safeNavigateTo(findNavController(), R.id.action_nav_new_download_to_nav_book, bundle)\n }\n }\n }\n .show()\n\n true\n }\n }\n }\n }\n\n private fun callDownloadFragment(name: String){\n val bundle = Bundle()\n Log.d(\"MyNDF\", \"Call dl and is new.\")\n bundle.putBoolean(\"loadJson\", true)\n bundle.putString(\"name\", name)\n ComicDlFragment.json = File(File(extDir, name), \"info.json\").readText()\n Log.d(\"MyNDF\", \"root view: $rootView\")\n Log.d(\"MyNDF\", \"action_nav_new_download_to_nav_group\")\n Navigate.safeNavigateTo(findNavController(), R.id.action_nav_new_download_to_nav_group, bundle)\n }\n\n fun showReverseInfo(toolsBox: UITools) {\n if (exit) return\n toolsBox.buildInfo(\"反转排序\", \"将按当前顺序的倒序显示下载的漫画\",\n \"确定\", null, \"取消\", {\n isReverse = !isReverse\n isContentChanged = true\n reset()\n Thread {\n sleep(600)\n addPage()\n }.start()\n }\n )\n }\n\n override fun onLoadFinish() {\n super.onLoadFinish()\n activity?.runOnUiThread {\n mypl.visibility = View.GONE\n }\n }\n\n companion object {\n var wn: WeakReference? = null\n }\n}\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt
+--- a/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt (date 1709909463888)
+@@ -10,9 +10,13 @@
+ import androidx.core.content.FileProvider
+ import androidx.core.net.toUri
+ import androidx.fragment.app.Fragment
++import androidx.lifecycle.lifecycleScope
+ import androidx.navigation.fragment.findNavController
+ import androidx.preference.PreferenceManager
+ import kotlinx.android.synthetic.main.line_lazybooklines.*
++import kotlinx.coroutines.Dispatchers
++import kotlinx.coroutines.launch
++import kotlinx.coroutines.withContext
+ import top.fumiama.copymanga.MainActivity
+ import top.fumiama.copymanga.manga.Reader
+ import top.fumiama.copymanga.template.general.MangaPagesFragmentTemplate
+@@ -64,49 +68,53 @@
+ page = 0
+ isRefresh = false
+ }
+- if(!isEnd) {
+- if(sortedBookList == null || isContentChanged) {
+- Log.d("MyNDF", "Sorting books...")
+- sortedBookList = extDir?.listFiles()?.sortedBy {
+- return@sortedBy Reader.getComicPathWordInFile(it)
+- }
+- if (isReverse) {
+- Log.d("MyNDF", "reversed...")
+- sortedBookList = sortedBookList?.asReversed()
+- }
+- if (!showAll) {
+- sortedBookList = sortedBookList?.filter {
+- return@filter FileUtils.sizeOf(it) / 1048576 > 0
+- }
+- }
+- isContentChanged = false
+- }
+- Log.d("MyNDF", "Start drawing cards")
+- cardList?.addCard(oldDlCardName, path = oldDlCardName)
+- var cnt = 1
+- sortedBookList?.let {
+- for(i in it.listIterator(page)) {
+- if(cardList?.exitCardList != false) return
+- page++ // page is actually count
+- val chosenJson = File(i, "info.bin")
+- val newJson = File(i, "info.json")
+- val bookSize = (FileUtils.sizeOf(i)/1048576).toInt()
+- when {
+- chosenJson.exists() -> continue // unsupported old folder
+- newJson.exists() -> {
+- if(cardList?.exitCardList != false) return
+- cardList?.addCard(i.name, "\n${bookSize}MB")
+- cnt++
+- }
+- }
+- if (cnt >= 21) break
+- }
+- if(page >= it.size) {
+- isEnd = true
+- }
+- }
+- }
+- onLoadFinish()
++ lifecycleScope.launch {
++ withContext(Dispatchers.IO) {
++ if(!isEnd) {
++ if(sortedBookList == null || isContentChanged) {
++ Log.d("MyNDF", "Sorting books...")
++ sortedBookList = extDir?.listFiles()?.sortedBy {
++ return@sortedBy Reader.getComicPathWordInFile(it)
++ }
++ if (isReverse) {
++ Log.d("MyNDF", "reversed...")
++ sortedBookList = sortedBookList?.asReversed()
++ }
++ if (!showAll) {
++ sortedBookList = sortedBookList?.filter {
++ return@filter FileUtils.sizeOf(it) / 1048576 > 0
++ }
++ }
++ isContentChanged = false
++ }
++ Log.d("MyNDF", "Start drawing cards")
++ cardList?.addCard(oldDlCardName, path = oldDlCardName)
++ var cnt = 1
++ sortedBookList?.let {
++ for(i in it.listIterator(page)) {
++ if(cardList?.exitCardList != false) return@withContext
++ page++ // page is actually count
++ val chosenJson = File(i, "info.bin")
++ val newJson = File(i, "info.json")
++ val bookSize = (FileUtils.sizeOf(i)/1048576).toInt()
++ when {
++ chosenJson.exists() -> continue // unsupported old folder
++ newJson.exists() -> {
++ if(cardList?.exitCardList != false) return@withContext
++ cardList?.addCard(i.name, "\n${bookSize}MB")
++ cnt++
++ }
++ }
++ if (cnt >= 21) break
++ }
++ if(page >= it.size) {
++ isEnd = true
++ }
++ }
++ }
++ onLoadFinish()
++ }
++ }
+ }
+
+ override fun initCardList(weakReference: WeakReference) {
+@@ -136,12 +144,14 @@
+ AlertDialog.Builder(context)
+ .setIcon(R.drawable.ic_launcher_foreground).setMessage("删除下载的漫画${name}吗?")
+ .setTitle("提示").setPositiveButton(android.R.string.ok) { _, _ ->
+- if (chosenFile.exists()) Thread {
+- FileUtils.recursiveRemove(chosenFile)
+- activity?.runOnUiThread {
+- it.visibility = View.INVISIBLE
++ if (chosenFile.exists()) lifecycleScope.launch {
++ withContext(Dispatchers.IO) {
++ FileUtils.recursiveRemove(chosenFile)
++ withContext(Dispatchers.Main) {
++ it.visibility = View.INVISIBLE
++ }
+ }
+- }.start()
++ }
+ }.setNegativeButton(android.R.string.cancel) { _, _ -> }
+ .show()
+ }
+Index: app/src/main/res/layout/line_bookinfo.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>\n\n\n \n\n \n\n \n\n \n \n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/layout/line_bookinfo.xml b/app/src/main/res/layout/line_bookinfo.xml
+--- a/app/src/main/res/layout/line_bookinfo.xml (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/res/layout/line_bookinfo.xml (date 1709897914592)
+@@ -27,22 +27,32 @@
+ layout="@layout/card_book"
+ android:layout_width="@dimen/book_card_width"
+ android:layout_height="0dp"
+- app:layout_constraintBottom_toBottomOf="parent"
++ app:layout_constraintBottom_toBottomOf="@+id/lbitb"
+ app:layout_constraintEnd_toStartOf="@+id/lbitb"
+ app:layout_constraintHorizontal_weight="1"
+ app:layout_constraintStart_toStartOf="parent"
+- app:layout_constraintTop_toTopOf="parent"
+- app:layout_constraintVertical_bias="0.499" />
++ app:layout_constraintTop_toTopOf="parent" />
+
+
++
++
+
+
+\ No newline at end of file
+Index: build.gradle
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n ext.kotlin_version = '1.7.10'\n repositories {\n google()\n jcenter()\n mavenCentral()\n mavenCentral()\n maven { url 'https://maven.google.com' }\n maven { url \"https://jitpack.io\" }\n }\n dependencies {\n classpath 'com.android.tools.build:gradle:8.1.1'\n classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n\n // NOTE: Do not place your application dependencies here; they belong\n // in the individual module build.gradle files\n }\n}\n\nallprojects {\n repositories {\n google()\n jcenter()\n mavenCentral()\n maven { url \"https://jitpack.io\" }\n }\n}\n\ntask clean(type: Delete) {\n delete rootProject.buildDir\n}\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/build.gradle b/build.gradle
+--- a/build.gradle (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/build.gradle (date 1709881875422)
+@@ -10,7 +10,7 @@
+ maven { url "https://jitpack.io" }
+ }
+ dependencies {
+- classpath 'com.android.tools.build:gradle:8.1.1'
++ classpath 'com.android.tools.build:gradle:8.2.2'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+@@ -27,6 +27,6 @@
+ }
+ }
+
+-task clean(type: Delete) {
++tasks.register('clean', Delete) {
+ delete rootProject.buildDir
+ }
+Index: app/src/main/res/drawable-anydpi/ic_circle.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/drawable-anydpi/ic_circle.xml b/app/src/main/res/drawable-anydpi/ic_circle.xml
+new file mode 100644
+--- /dev/null (date 1709899198517)
++++ b/app/src/main/res/drawable-anydpi/ic_circle.xml (date 1709899198517)
+@@ -0,0 +1,10 @@
++
++
++
++
+\ No newline at end of file
+Index: app/src/main/res/layout/page_nested_list.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/layout/page_nested_list.xml b/app/src/main/res/layout/page_nested_list.xml
+new file mode 100644
+--- /dev/null (date 1709903119912)
++++ b/app/src/main/res/layout/page_nested_list.xml (date 1709903119912)
+@@ -0,0 +1,13 @@
++
++
++
++
++
+\ No newline at end of file
+Index: gradle/wrapper/gradle-wrapper.properties
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>#Fri Sep 04 18:15:43 CST 2020\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.0-all.zip\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>ISO-8859-1
+===================================================================
+diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
+--- a/gradle/wrapper/gradle-wrapper.properties (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/gradle/wrapper/gradle-wrapper.properties (date 1709880931213)
+@@ -3,4 +3,4 @@
+ distributionPath=wrapper/dists
+ zipStoreBase=GRADLE_USER_HOME
+ zipStorePath=wrapper/dists
+-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
++distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip
+Index: app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.ui.book\n\nimport android.annotation.SuppressLint\nimport android.content.Context.MODE_PRIVATE\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.View\nimport android.widget.Toast\nimport androidx.navigation.fragment.findNavController\nimport com.google.gson.Gson\nimport kotlinx.android.synthetic.main.app_bar_main.*\nimport kotlinx.android.synthetic.main.fragment_book.*\nimport kotlinx.android.synthetic.main.line_bookinfo_text.*\nimport kotlinx.android.synthetic.main.line_booktandb.*\nimport top.fumiama.copymanga.MainActivity\nimport top.fumiama.copymanga.MainActivity.Companion.mainWeakReference\nimport top.fumiama.copymanga.json.VolumeStructure\nimport top.fumiama.copymanga.manga.Reader\nimport top.fumiama.copymanga.template.general.NoBackRefreshFragment\nimport top.fumiama.copymanga.tools.ui.Navigate\nimport top.fumiama.copymanga.ui.comicdl.ComicDlFragment\nimport top.fumiama.dmzj.copymanga.R\nimport java.io.File\nimport java.lang.Thread.sleep\nimport java.lang.ref.WeakReference\n\nclass BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {\n var isOnPause = false\n private var mBookHandler: BookHandler? = null\n\n override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n super.onViewCreated(view, savedInstanceState)\n\n ComicDlFragment.exit = false\n fbl?.setPadding(0, 0, 0, navBarHeight)\n\n if(isFirstInflate) {\n var path = \"\"\n arguments?.apply {\n if (getBoolean(\"loadJson\")) {\n getString(\"name\")?.let { name ->\n mainWeakReference?.get()?.getExternalFilesDir(\"\")?.let {\n Gson().fromJson(File(File(it, name), \"info.json\").readText(), Array::class.java)\n }?.apply {\n if (isEmpty() || get(0).results.list.isEmpty()) {\n findNavController().popBackStack()\n return\n }\n else {\n path = get(0).results.list[0].comic_path_word\n }\n }\n }\n } else getString(\"path\").let {\n if (it != null) path = it\n else {\n findNavController().popBackStack()\n return\n }\n }\n }\n mBookHandler = BookHandler(WeakReference(this), path)\n Log.d(\"MyBF\", \"read path: $path\")\n bookHandler = mBookHandler\n Thread {\n sleep(600)\n mBookHandler?.startLoad()\n }.start()\n } else {\n bookHandler = mBookHandler\n }\n }\n\n override fun onResume() {\n super.onResume()\n isOnPause = false\n bookHandler = mBookHandler\n mainWeakReference?.get()?.apply {\n toolbar.title = mBookHandler?.book?.results?.comic?.name\n }\n setStartRead()\n }\n\n override fun onPause() {\n super.onPause()\n isOnPause = true\n }\n\n override fun onDestroy() {\n super.onDestroy()\n mBookHandler?.destroy()\n mBookHandler?.ads?.forEach {\n it.exit = true\n }\n bookHandler = null\n }\n\n fun setStartRead() {\n if(mBookHandler?.chapterNames?.isNotEmpty() == true) mainWeakReference?.get()?.apply {\n mBookHandler?.book?.results?.comic?.let { comic ->\n getPreferences(MODE_PRIVATE).getInt(comic.name, -1).let { p ->\n this@BookFragment.lbbstart.apply {\n var i = 0\n if(p >= 0) {\n text = mBookHandler!!.chapterNames[p]\n i = p\n }\n setOnClickListener {\n mBookHandler?.urlArray?.let {\n Reader.viewMangaAt(comic.name, i, it)\n }\n }\n }\n }\n }\n }\n }\n\n @SuppressLint(\"SetTextI18n\")\n fun setAddToShelf() {\n if(mBookHandler?.chapterNames?.isNotEmpty() == true) {\n val b = MainActivity.shelf?.query(mBookHandler?.path!!)\n mBookHandler?.collect = b?.results?.collect?:-2\n Log.d(\"MyBF\", \"get collect of ${mBookHandler?.path} = ${mBookHandler?.collect}\")\n b?.results?.browse?.chapter_name?.let { name ->\n btsub.text = \"${btsub.text} ${getString(R.string.text_format_cloud_read_to).format(name)}\"\n }\n mBookHandler?.collect?.let { collect ->\n if (collect > 0) {\n this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)\n }\n }\n mBookHandler?.book?.results?.comic?.let { comic ->\n this@BookFragment.lbbsub.setOnClickListener {\n if (this@BookFragment.lbbsub.text != getString(R.string.button_sub)) {\n mBookHandler?.collect?.let { collect ->\n if (collect < 0) return@setOnClickListener\n Thread{\n val re = MainActivity.shelf?.del(collect)\n activity?.runOnUiThread {\n Toast.makeText(context, re, Toast.LENGTH_SHORT).show()\n if (re == \"请求成功\") {\n this@BookFragment.lbbsub.setText(R.string.button_sub)\n }\n }\n }.start()\n }\n return@setOnClickListener\n }\n Thread{\n val re = MainActivity.shelf?.add(comic.uuid)\n activity?.runOnUiThread {\n Toast.makeText(context, re, Toast.LENGTH_SHORT).show()\n if (re == \"修改成功\") {\n this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)\n }\n }\n }.start()\n }\n }\n }\n }\n\n fun navigate2dl(){\n val bundle = Bundle()\n bundle.putString(\"path\", arguments?.getString(\"path\")?:\"null\")\n bundle.putString(\"name\", mBookHandler!!.book?.results?.comic?.name)\n if(mBookHandler!!.vols != null) {\n bundle.putBoolean(\"loadJson\", true)\n }\n bundle.putStringArray(\"group\", mBookHandler!!.gpws)\n bundle.putStringArray(\"groupNames\", mBookHandler!!.keys)\n bundle.putIntArray(\"count\", mBookHandler!!.cnts)\n findNavController().let {\n Navigate.safeNavigateTo(it, R.id.action_nav_book_to_nav_group, bundle)\n }\n }\n\n companion object {\n var bookHandler: BookHandler? = null\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt
+--- a/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt (date 1709903835366)
+@@ -6,14 +6,18 @@
+ import android.util.Log
+ import android.view.View
+ import android.widget.Toast
++import androidx.lifecycle.lifecycleScope
+ import androidx.navigation.fragment.findNavController
+ import com.google.gson.Gson
+ import kotlinx.android.synthetic.main.app_bar_main.*
++import kotlinx.android.synthetic.main.card_book.*
+ import kotlinx.android.synthetic.main.fragment_book.*
+ import kotlinx.android.synthetic.main.line_bookinfo_text.*
+ import kotlinx.android.synthetic.main.line_booktandb.*
++import kotlinx.coroutines.Dispatchers
++import kotlinx.coroutines.launch
++import kotlinx.coroutines.withContext
+ import top.fumiama.copymanga.MainActivity
+-import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
+ import top.fumiama.copymanga.json.VolumeStructure
+ import top.fumiama.copymanga.manga.Reader
+ import top.fumiama.copymanga.template.general.NoBackRefreshFragment
+@@ -32,14 +36,14 @@
+ super.onViewCreated(view, savedInstanceState)
+
+ ComicDlFragment.exit = false
+- fbl?.setPadding(0, 0, 0, navBarHeight)
++ fbvp?.setPadding(0, 0, 0, navBarHeight)
+
+ if(isFirstInflate) {
+ var path = ""
+ arguments?.apply {
+ if (getBoolean("loadJson")) {
+ getString("name")?.let { name ->
+- mainWeakReference?.get()?.getExternalFilesDir("")?.let {
++ activity?.getExternalFilesDir("")?.let {
+ Gson().fromJson(File(File(it, name), "info.json").readText(), Array::class.java)
+ }?.apply {
+ if (isEmpty() || get(0).results.list.isEmpty()) {
+@@ -62,10 +66,12 @@
+ mBookHandler = BookHandler(WeakReference(this), path)
+ Log.d("MyBF", "read path: $path")
+ bookHandler = mBookHandler
+- Thread {
+- sleep(600)
+- mBookHandler?.startLoad()
+- }.start()
++ lifecycleScope.launch {
++ withContext(Dispatchers.IO) {
++ sleep(600)
++ mBookHandler?.startLoad()
++ }
++ }
+ } else {
+ bookHandler = mBookHandler
+ }
+@@ -75,7 +81,7 @@
+ super.onResume()
+ isOnPause = false
+ bookHandler = mBookHandler
+- mainWeakReference?.get()?.apply {
++ activity?.apply {
+ toolbar.title = mBookHandler?.book?.results?.comic?.name
+ }
+ setStartRead()
+@@ -96,7 +102,7 @@
+ }
+
+ fun setStartRead() {
+- if(mBookHandler?.chapterNames?.isNotEmpty() == true) mainWeakReference?.get()?.apply {
++ if(mBookHandler?.chapterNames?.isNotEmpty() == true) activity?.apply {
+ mBookHandler?.book?.results?.comic?.let { comic ->
+ getPreferences(MODE_PRIVATE).getInt(comic.name, -1).let { p ->
+ this@BookFragment.lbbstart.apply {
+@@ -118,12 +124,13 @@
+
+ @SuppressLint("SetTextI18n")
+ fun setAddToShelf() {
+- if(mBookHandler?.chapterNames?.isNotEmpty() == true) {
++ if(mBookHandler?.chapterNames?.isNotEmpty() != true) return
++ lifecycleScope.launch {
+ val b = MainActivity.shelf?.query(mBookHandler?.path!!)
+ mBookHandler?.collect = b?.results?.collect?:-2
+ Log.d("MyBF", "get collect of ${mBookHandler?.path} = ${mBookHandler?.collect}")
+- b?.results?.browse?.chapter_name?.let { name ->
+- btsub.text = "${btsub.text} ${getString(R.string.text_format_cloud_read_to).format(name)}"
++ tic.text = b?.results?.browse?.chapter_name?.let { name ->
++ getString(R.string.text_format_cloud_read_to).format(name)
+ }
+ mBookHandler?.collect?.let { collect ->
+ if (collect > 0) {
+@@ -132,30 +139,24 @@
+ }
+ mBookHandler?.book?.results?.comic?.let { comic ->
+ this@BookFragment.lbbsub.setOnClickListener {
+- if (this@BookFragment.lbbsub.text != getString(R.string.button_sub)) {
+- mBookHandler?.collect?.let { collect ->
+- if (collect < 0) return@setOnClickListener
+- Thread{
++ lifecycleScope.launch clickLaunch@ {
++ if (this@BookFragment.lbbsub.text != getString(R.string.button_sub)) {
++ mBookHandler?.collect?.let { collect ->
++ if (collect < 0) return@clickLaunch
+ val re = MainActivity.shelf?.del(collect)
+- activity?.runOnUiThread {
+- Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
+- if (re == "请求成功") {
+- this@BookFragment.lbbsub.setText(R.string.button_sub)
+- }
+- }
+- }.start()
++ Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
++ if (re == "请求成功") {
++ this@BookFragment.lbbsub.setText(R.string.button_sub)
++ }
++ }
++ return@clickLaunch
+ }
+- return@setOnClickListener
+- }
+- Thread{
+ val re = MainActivity.shelf?.add(comic.uuid)
+- activity?.runOnUiThread {
+- Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
+- if (re == "修改成功") {
+- this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)
+- }
+- }
+- }.start()
++ Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
++ if (re == "修改成功") {
++ this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)
++ }
++ }
+ }
+ }
+ }
+Index: app/src/main/java/top/fumiama/copymanga/update/SimpleKanban.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.update\n\nimport android.util.Log\n\nclass SimpleKanban(private val client: Client, private val pwd: String) { //must run in thread\n private val raw: ByteArray?\n get() {\n var times = 3\n var re: ByteArray\n var firstRecv: ByteArray\n do {\n re = byteArrayOf()\n if(client.initConnect()) {\n client.sendMessage(\"${pwd}catquit\")\n client.receiveRawMessage(33) //Welcome to simple kanban server.\n try {\n firstRecv = client.receiveRawMessage(4) //le\n val length = convert2Int(firstRecv)\n Log.d(\"MySK\", \"Msg len: $length\")\n if(firstRecv.size > 4) re += firstRecv.copyOfRange(4, firstRecv.size)\n re += client.receiveRawMessage(length - re.size, setProgress = true)\n break\n } catch (e: Exception) {\n e.printStackTrace()\n }\n client.closeConnect()\n }\n } while (times-- > 0)\n return if(re.isEmpty()) null else re\n }\n\n private fun convert2Int(buffer: ByteArray) =\n (buffer[3].toInt() and 0xff shl 24) or\n (buffer[2].toInt() and 0xff shl 16) or\n (buffer[1].toInt() and 0xff shl 8) or\n (buffer[0].toInt() and 0xff)\n\n fun fetchRaw(doOnLoadFailure: ()->Unit = {\n Log.d(\"MySD\", \"Fetch dict failed\")\n }, doOnLoadSuccess: (data: ByteArray)->Unit = {\n Log.d(\"MySD\", \"Fetch dict success\")\n }) {\n raw?.apply {\n doOnLoadSuccess(this)\n }?:doOnLoadFailure()\n }\n\n operator fun get(version: Int): String =\n if(client.initConnect()) {\n client.sendMessage(\"${pwd}get${version}quit\")\n client.receiveRawMessage(36) //Welcome to simple kanban server. get\n val r = try {\n val firstRecv = client.receiveRawMessage(4)\n if(firstRecv.decodeToString() == \"null\") \"null\"\n else {\n val length = convert2Int(firstRecv)\n Log.d(\"MySK\", \"Msg len: $length\")\n var re = byteArrayOf()\n if(firstRecv.size > 4) re += firstRecv.copyOfRange(4, firstRecv.size)\n re += client.receiveRawMessage(length - re.size)\n if(re.isNotEmpty()) re.decodeToString() else \"null\"\n }\n } catch (e: Exception){\n e.printStackTrace()\n \"null\"\n }\n client.closeConnect()\n r\n } else \"null\"\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/update/SimpleKanban.kt b/app/src/main/java/top/fumiama/copymanga/update/SimpleKanban.kt
+--- a/app/src/main/java/top/fumiama/copymanga/update/SimpleKanban.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/update/SimpleKanban.kt (date 1709886316131)
+@@ -7,17 +7,17 @@
+ get() {
+ var times = 3
+ var re: ByteArray
+- var firstRecv: ByteArray
++ var firstReceived: ByteArray
+ do {
+ re = byteArrayOf()
+ if(client.initConnect()) {
+ client.sendMessage("${pwd}catquit")
+ client.receiveRawMessage(33) //Welcome to simple kanban server.
+ try {
+- firstRecv = client.receiveRawMessage(4) //le
+- val length = convert2Int(firstRecv)
++ firstReceived = client.receiveRawMessage(4) //le
++ val length = convert2Int(firstReceived)
+ Log.d("MySK", "Msg len: $length")
+- if(firstRecv.size > 4) re += firstRecv.copyOfRange(4, firstRecv.size)
++ if(firstReceived.size > 4) re += firstReceived.copyOfRange(4, firstReceived.size)
+ re += client.receiveRawMessage(length - re.size, setProgress = true)
+ break
+ } catch (e: Exception) {
+@@ -35,9 +35,9 @@
+ (buffer[1].toInt() and 0xff shl 8) or
+ (buffer[0].toInt() and 0xff)
+
+- fun fetchRaw(doOnLoadFailure: ()->Unit = {
++ suspend fun fetchRaw(doOnLoadFailure: suspend ()->Unit = {
+ Log.d("MySD", "Fetch dict failed")
+- }, doOnLoadSuccess: (data: ByteArray)->Unit = {
++ }, doOnLoadSuccess: suspend (data: ByteArray)->Unit = {
+ Log.d("MySD", "Fetch dict success")
+ }) {
+ raw?.apply {
+Index: .idea/gradle.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/.idea/gradle.xml b/.idea/gradle.xml
+--- a/.idea/gradle.xml (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/.idea/gradle.xml (date 1709880886968)
+@@ -4,10 +4,8 @@
+
+
+
+-
+-
+
+-
++
+
+
+
+Index: app/src/main/res/layout/fragment_book.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>\n\n\n \n\n \n \n\n \n\n \n \n\n \n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/layout/fragment_book.xml b/app/src/main/res/layout/fragment_book.xml
+--- a/app/src/main/res/layout/fragment_book.xml (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/res/layout/fragment_book.xml (date 1709903119917)
+@@ -16,29 +16,30 @@
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/nav_header_vertical_spacing"
+- app:layout_constraintBottom_toTopOf="@id/fbov"
++ app:layout_constraintBottom_toTopOf="@id/fbvp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+- app:layout_scrollFlags="scroll|enterAlways" />
++ app:layout_scrollFlags="scroll|exitUntilCollapsed" />
++
++
+
+
+-
+-
+-
+-
++ app:layout_constraintTop_toBottomOf="@+id/fbiinf"
++ app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
+
+ package top.fumiama.copymanga.update\n\nimport android.app.Activity\nimport android.content.Context.MODE_PRIVATE\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport android.util.Log\nimport android.widget.Toast\nimport androidx.core.content.FileProvider\nimport androidx.core.content.edit\nimport kotlinx.android.synthetic.main.dialog_progress.view.*\nimport top.fumiama.copymanga.tools.ui.UITools\nimport top.fumiama.dmzj.copymanga.BuildConfig\nimport top.fumiama.dmzj.copymanga.R\nimport java.io.File\nimport java.security.MessageDigest\n\nobject Update {\n fun checkUpdate(activity: Activity, toolsBox: UITools, ignoreSkip: Boolean = false) = activity.apply{\n val client = Client(\"reilia.fumiama.top\", 13212)\n val progressBar = layoutInflater.inflate(R.layout.dialog_progress, null, false)\n val progressHandler = object : Client.Progress{\n override fun notify(progressPercentage: Int) {\n Log.d(\"MyUP\", \"Set progress: $progressPercentage\")\n progressBar.dpp.progress = progressPercentage\n }\n }\n val kanban = SimpleKanban(client, \"fumiama\")\n val msg = kanban[BuildConfig.VERSION_CODE]\n if(msg != \"null\") {\n val verNum = msg.substringBefore('\\n').toIntOrNull()\n val skipNum = getPreferences(MODE_PRIVATE).getInt(\"skipVersion\", 0)\n\n Log.d(\"MyUP\", \"Ver:$verNum, skip: $skipNum\")\n if(verNum != null) {\n if(msg.contains(\"md5:\")) {\n if(skipNum < verNum || ignoreSkip) runOnUiThread {\n toolsBox.buildInfo(\"看板\", msg.substringAfter('\\n').substringBeforeLast('\\n'), \"下载新版\", \"跳过该版\", \"取消\", {\n val info = toolsBox.buildAlertWithView(\"下载进度\", progressBar, \"隐藏\")\n client.progress = progressHandler\n Thread {\n kanban.fetchRaw({\n runOnUiThread {\n Toast.makeText(this, \"下载失败\", Toast.LENGTH_SHORT).show()\n client.progress = null\n }\n }) {\n val md5 = msg.substringAfterLast(\"md5:\")\n if (md5 == UITools.toHexStr(\n MessageDigest.getInstance(\"MD5\").digest(it)\n )\n ) {\n runOnUiThread {\n Toast.makeText(this, \"下载成功\", Toast.LENGTH_SHORT).show()\n info.dismiss()\n }\n val f = File(externalCacheDir, \"new.apk\")\n f.writeBytes(it)\n install(f, this)\n } else runOnUiThread {\n Toast.makeText(this, \"文件损坏\", Toast.LENGTH_SHORT).show()\n info.dismiss()\n }\n client.progress = null\n }\n }.start()\n }, {\n getPreferences(MODE_PRIVATE).edit {\n putInt(\"skipVersion\", verNum)\n apply()\n }\n })\n }\n } else runOnUiThread {\n toolsBox.buildInfo(\"看板\", msg.substringAfter('\\n'), \"知道了\")\n }\n }\n } else if(ignoreSkip) runOnUiThread {\n Toast.makeText(this, \"无更新\", Toast.LENGTH_SHORT).show()\n }\n }\n\n private fun install(apkFile: File, activity: Activity) = activity.apply{\n val intent = Intent(Intent.ACTION_VIEW)\n intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n val contentUri: Uri = FileProvider.getUriForFile(this, \"$packageName.fileprovider\", apkFile)\n intent.setDataAndType(contentUri, \"application/vnd.android.package-archive\")\n } else intent.setDataAndType(Uri.fromFile(apkFile), \"application/vnd.android.package-archive\")\n startActivity(intent)\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/update/Update.kt b/app/src/main/java/top/fumiama/copymanga/update/Update.kt
+--- a/app/src/main/java/top/fumiama/copymanga/update/Update.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/update/Update.kt (date 1709886688573)
+@@ -1,15 +1,21 @@
+ package top.fumiama.copymanga.update
+
+ import android.app.Activity
++import android.content.Context
+ import android.content.Context.MODE_PRIVATE
+ import android.content.Intent
+ import android.net.Uri
+ import android.os.Build
+ import android.util.Log
+ import android.widget.Toast
++import androidx.appcompat.app.AppCompatActivity
+ import androidx.core.content.FileProvider
+ import androidx.core.content.edit
++import androidx.lifecycle.lifecycleScope
+ import kotlinx.android.synthetic.main.dialog_progress.view.*
++import kotlinx.coroutines.Dispatchers
++import kotlinx.coroutines.launch
++import kotlinx.coroutines.withContext
+ import top.fumiama.copymanga.tools.ui.UITools
+ import top.fumiama.dmzj.copymanga.BuildConfig
+ import top.fumiama.dmzj.copymanga.R
+@@ -17,8 +23,10 @@
+ import java.security.MessageDigest
+
+ object Update {
+- fun checkUpdate(activity: Activity, toolsBox: UITools, ignoreSkip: Boolean = false) = activity.apply{
++ suspend fun checkUpdate(activity: AppCompatActivity, toolsBox: UITools, ignoreSkip: Boolean = false) = activity.apply{
+ val client = Client("reilia.fumiama.top", 13212)
++ val kanban = SimpleKanban(client, "fumiama")
++
+ val progressBar = layoutInflater.inflate(R.layout.dialog_progress, null, false)
+ val progressHandler = object : Client.Progress{
+ override fun notify(progressPercentage: Int) {
+@@ -26,69 +34,85 @@
+ progressBar.dpp.progress = progressPercentage
+ }
+ }
+- val kanban = SimpleKanban(client, "fumiama")
+- val msg = kanban[BuildConfig.VERSION_CODE]
+- if(msg != "null") {
+- val verNum = msg.substringBefore('\n').toIntOrNull()
+- val skipNum = getPreferences(MODE_PRIVATE).getInt("skipVersion", 0)
+-
+- Log.d("MyUP", "Ver:$verNum, skip: $skipNum")
+- if(verNum != null) {
+- if(msg.contains("md5:")) {
+- if(skipNum < verNum || ignoreSkip) runOnUiThread {
+- toolsBox.buildInfo("看板", msg.substringAfter('\n').substringBeforeLast('\n'), "下载新版", "跳过该版", "取消", {
+- val info = toolsBox.buildAlertWithView("下载进度", progressBar, "隐藏")
+- client.progress = progressHandler
+- Thread {
+- kanban.fetchRaw({
+- runOnUiThread {
+- Toast.makeText(this, "下载失败", Toast.LENGTH_SHORT).show()
+- client.progress = null
+- }
+- }) {
+- val md5 = msg.substringAfterLast("md5:")
+- if (md5 == UITools.toHexStr(
+- MessageDigest.getInstance("MD5").digest(it)
+- )
+- ) {
+- runOnUiThread {
+- Toast.makeText(this, "下载成功", Toast.LENGTH_SHORT).show()
+- info.dismiss()
+- }
+- val f = File(externalCacheDir, "new.apk")
+- f.writeBytes(it)
+- install(f, this)
+- } else runOnUiThread {
+- Toast.makeText(this, "文件损坏", Toast.LENGTH_SHORT).show()
+- info.dismiss()
+- }
+- client.progress = null
+- }
+- }.start()
+- }, {
+- getPreferences(MODE_PRIVATE).edit {
+- putInt("skipVersion", verNum)
+- apply()
+- }
+- })
+- }
+- } else runOnUiThread {
+- toolsBox.buildInfo("看板", msg.substringAfter('\n'), "知道了")
+- }
+- }
+- } else if(ignoreSkip) runOnUiThread {
+- Toast.makeText(this, "无更新", Toast.LENGTH_SHORT).show()
+- }
++
++ val msg = message(kanban)
++ if (msg == "null") {
++ if(ignoreSkip) withContext(Dispatchers.Main) {
++ Toast.makeText(this@apply, "无更新", Toast.LENGTH_SHORT).show()
++ }
++ return@apply
++ }
++
++ val verNum = msg.substringBefore('\n').toIntOrNull()
++ val skipNum = getPreferences(MODE_PRIVATE).getInt("skipVersion", 0)
++ Log.d("MyUP", "Ver:$verNum, skip: $skipNum")
++ if (verNum == null) return@apply
++
++ if(!msg.contains("md5:")) {
++ withContext(Dispatchers.Main) {
++ toolsBox.buildInfo("看板", msg.substringAfter('\n'), "知道了")
++ }
++ return@apply
++ }
++ if(skipNum < verNum || ignoreSkip) {
++ toolsBox.buildInfo("看板", msg.substringAfter('\n').substringBeforeLast('\n'), "下载新版", "跳过该版", "取消", {
++ val info = toolsBox.buildAlertWithView("下载进度", progressBar, "隐藏")
++ client.progress = progressHandler
++ lifecycleScope.launch {
++ fetch(client, kanban, this@apply) {
++ lifecycleScope.launch {
++ val md5 = msg.substringAfterLast("md5:")
++ if (md5 == UITools.toHexStr(
++ MessageDigest.getInstance("MD5").digest(it)
++ )
++ ) {
++ Toast.makeText(this@apply, "下载成功", Toast.LENGTH_SHORT).show()
++ info.dismiss()
++ install(it, this@apply)
++ } else runOnUiThread {
++ Toast.makeText(this@apply, "文件损坏", Toast.LENGTH_SHORT).show()
++ info.dismiss()
++ }
++ client.progress = null
++ }
++ }
++ }
++ }, {
++ getPreferences(MODE_PRIVATE).edit {
++ putInt("skipVersion", verNum)
++ apply()
++ }
++ })
++ }
++ }
++
++ private suspend fun message(kanban: SimpleKanban) = withContext(Dispatchers.IO) {
++ return@withContext kanban[BuildConfig.VERSION_CODE]
++ }
++
++ private suspend fun fetch(client: Client, kanban: SimpleKanban, context: Context, doOnLoadSuccess: (data: ByteArray) -> Unit) = withContext(Dispatchers.IO) {
++ return@withContext kanban.fetchRaw({ downloadFail(client, context) }, doOnLoadSuccess)
++ }
++
++ private suspend fun downloadFail(client: Client, context: Context) = withContext(Dispatchers.Main) {
++ Toast.makeText(context, R.string.download_apk_fail, Toast.LENGTH_SHORT).show()
++ client.progress = null
+ }
+
+- private fun install(apkFile: File, activity: Activity) = activity.apply{
+- val intent = Intent(Intent.ACTION_VIEW)
+- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+- val contentUri: Uri = FileProvider.getUriForFile(this, "$packageName.fileprovider", apkFile)
+- intent.setDataAndType(contentUri, "application/vnd.android.package-archive")
+- } else intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive")
+- startActivity(intent)
++ private suspend fun install(data: ByteArray, activity: Activity) = activity.apply{
++ withContext(Dispatchers.IO) {
++ val f = File(externalCacheDir, "new.apk")
++ f.writeBytes(data)
++ val intent = Intent(Intent.ACTION_VIEW)
++ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
++ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
++ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
++ val contentUri: Uri = FileProvider.getUriForFile(this@apply, "$packageName.fileprovider", f)
++ intent.setDataAndType(contentUri, "application/vnd.android.package-archive")
++ } else intent.setDataAndType(Uri.fromFile(f), "application/vnd.android.package-archive")
++ withContext(Dispatchers.Main) {
++ startActivity(intent)
++ }
++ }
+ }
+-}
+\ No newline at end of file
++}
+Index: app/src/main/res/values/strings.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>\n\n ]>\n\n 拷贝漫画\n\n 设定\n 关于\n 下载\n 整理\n\n 主页\n 分类\n 排行\n ©2022–2023源文雨\\n本应用为拷贝漫画的第三方客户端,数据均来源于网络,作者不对其中所呈现的任何内容负责。\n 浏览历史\n 我的订阅\n 我的下载\n 最近更新\n 图书详情\n 章节内容\n 漫画下载\n 专题系列\n 漫画搜索\n 检查更新\n 设置搜索类别\n\n illust: Hiten(490219)\n 请设定提示文字内容\n 清除设定的图片?\n\n 登录\n 注销\n\n 权限被拒绝\n 未登录\n 加载主页控件出错\n N/A\n 获取图书信息失败\n 网络错误\n 保存封面失败\n 保存封面超时\n 不支持在此模式下切换\n 已经到头了~\n 再次按下加载上一章\n 再次按下加载下一章\n 读取本地章节信息失败\n 下载章节信息失败\n 加载漫画错误\n 统计zip图片数错误\n 第%1$d页加载异常\n 加载章节错误\n 图片加载错误,请尝试下载后使用较低图片质量查看\n\n https://%1$s/api/v3/h5/homeIndex?platform=3\n https://%1$s\n &hosturl;\n https://%1$s/api/v3/ranks?limit=21&offset=%2$d&date_type=%3$s&audience_type=%4$s&platform=3\n https://%1$s/api/v3/search/comic?limit=21&offset=%2$d&q=%3$s&q_type=%4$s&platform=3\n https://%1$s/api/v3/h5/filter/comic/tags?platform=3\n https://%1$s/api/v3/comics?limit=21&offset=%2$d&ordering=%3$s&theme=%4$s&top=%5$s&platform=3\n https://%1$s/api/v3/comic2/%2$s?platform=3\n https://%1$s/api/v3/comic2/%2$s/query?platform=3\n https://%1$s/api/v3/comic/%2$s/group/%3$s/chapters?limit=100&offset=%4$d&platform=3\n https://%1$s/api/v3/comic/%2$s/chapter2/%3$s?platform=3\n https://%1$s/api/v3/topic/%2$s?platform=3\n https://%1$s/api/v3/topic/%2$s/contents?type=%3$d&limit=21&offset=%4$d&platform=3\n https://%1$s/api/v3/recs?pos=3200102&limit=21&offset=%2$d&platform=3\n https://%1$s/api/v3/update/newest?limit=21&offset=%2$d&platform=3\n https://%1$s/api/v3/comics?limit=21&offset=%2$d&ordering=%3$s&top=finish&platform=3\n https://%1$s/api/v3/comics?limit=21&offset=%2$d&ordering=%3$s&author=%4$s&platform=3\n https://%1$s/api/v3/comics?limit=21&offset=%2$d&ordering=%3$s&theme=%4$s&platform=3\n https://%1$s/api/v3/login?platform=3\n https://%1$s/api/v3/member/info?platform=3\n https://%1$s/api/v3/member/browse/comics?limit=21&offset=%2$d&platform=3\n https://%1$s/api/v3/member/collect/comics?limit=21&offset=%2$d&free_type=1&ordering=%3$s&platform=3\n https://%1$s/api/v3/member/collect/comic\n\n https://copymanga.azurewebsites.net/api/img?code=%1$s&url=%2$s\n https://hi77-overseas.mangafuna.xyz/\n settings_cat_net_et_img_proxy_code\n \n settings_cat_net_sb_image_resolution\n\n 已完结\n\n WIFI\n 移动网络\n 蓝牙\n 以太网\n LOWPAN\n 无网络\n 网络错误\n\n COPY/2.0.7\n 2.0.7\n com.copymanga.app-2.0.7\n\n 更新时间\n 热度\n 加入时间\n 阅读时间\n\n 加入书架\n 已加书架\n 开始阅读\n 更多\n\n 热度 %1$d\n 状态 %1$s\n 云端读到 %1$s\n\n 专题系列\n 漫画推荐\n 排行榜 ☟日周月 ☛冠亚季\n 热门更新\n 全新上架\n\n 上升最快\n 近七天\n 近三十天\n 总榜单\n\n 作者\n 标签\n\n 网络\n 图片分辨率\n 默认1500\n 总是使用流量观看\n 打开后不再在开始阅读时提示\n 使用海外线路\n 不管使用什么线路, API访问均是海外, 只有图片CDN可能会变化(也可能不变), 请酌情选择使用\n 请求API网址\n 一般无需更改,除非拷贝漫画官方更改网址,默认:&hosturl;\n 使用API代理(重启生效)\n 作者自建的API代理,可缓解国内图书详情加载问题,但不保证100%解决,也不保证一直可用\n 使用图床代理(重启生效)\n 作者自建的图床代理,可缓解国内图片无法加载问题,但不保证100%解决,也不保证一直可用\n 图床代理密钥(重启生效)\n 为避免滥用,代理密钥需加群(559748702)获得,且随时可能会刷新\n\n 漫画浏览\n 深色阅读背景\n 打开后阅览漫画的背景色永远为黑色\n 隐藏底部时间栏\n 打开后不再在底部显示时间和网络状态\n 音量键翻页\n 使用音量上下键前后翻页\n 竖向翻页一次加载页数\n 默认为20\n 图片质量\n 默认为100\n\n 漫画下载\n 显示未下载漫画\n 打开后将在我的下载显示所有浏览过详情页的漫画,旧版下载永远全部显示\n\n\n\n 用户名为空\n 密码为空\n 登录失败\n 刷新头像失败\n 重启应用以彻底退出登录\n\n 前往旧版下载\n 选择您的操作\n 选择如何打开漫画下载目录\n\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
+--- a/app/src/main/res/values/strings.xml (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/res/values/strings.xml (date 1709904762834)
+@@ -13,7 +13,7 @@
+ 主页
+ 分类
+ 排行
+- ©2022–2023源文雨\n本应用为拷贝漫画的第三方客户端,数据均来源于网络,作者不对其中所呈现的任何内容负责。
++ ©2022–2024源文雨\n本应用为拷贝漫画的第三方客户端,数据均来源于网络,作者不对其中所呈现的任何内容负责。
+ 浏览历史
+ 我的订阅
+ 我的下载
+@@ -29,6 +29,9 @@
+ illust: Hiten(490219)
+ 请设定提示文字内容
+ 清除设定的图片?
++ 选取图片失败
++ 裁剪图片失败
++ 下载更新失败
+
+ 登录
+ 注销
+@@ -112,7 +115,9 @@
+
+ 热度 %1$d
+ 状态 %1$s
+- 云端读到 %1$s
++ 区域 %1$s
++ 画幅 %1$s
++ 云读至%1$s
+
+ 专题系列
+ 漫画推荐
+Index: app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.manga\n\nimport com.google.gson.Gson\nimport top.fumiama.copymanga.json.BookQueryStructure\nimport top.fumiama.copymanga.json.ReturnBase\nimport top.fumiama.copymanga.tools.http.DownloadTools\nimport top.fumiama.dmzj.copymanga.R\n\nclass Shelf(private val token: String, getString: (Int) -> String) {\n private val hostUrl: String = getString(R.string.hostUrl)\n private val apiUrl: String = getString(R.string.shelfOperateApiUrl).format(hostUrl)\n private val queryApiUrl = getString(R.string.bookUserQueryApiUrl)\n private val referer: String = getString(R.string.referer)\n private val ua: String = getString(R.string.pc_ua)\n fun add(comicId: String): String {\n if (comicId.isEmpty()) {\n return \"空漫画ID\"\n }\n val body = buildString {\n append(\"comic_id=\")\n append(comicId)\n append(\"&is_collect=1&authorization=Token+\")\n append(\"\")\n append(token)\n }\n val re = DownloadTools.requestWithBody(\n \"$apiUrl?platform=3\", \"POST\", body.encodeToByteArray(), referer, ua\n )?.decodeToString() ?: return \"空回应\"\n return Gson().fromJson(re, ReturnBase::class.java).message\n }\n\n fun del(vararg bookIds: Int): String {\n if (bookIds.isEmpty()) {\n return \"空ID列表\"\n }\n val body = buildString {\n bookIds.forEach {\n append(\"ids=\")\n append(it)\n append(\"&\")\n }\n append(\"authorization=Token+\")\n append(token)\n }\n val re = DownloadTools.requestWithBody(\n \"${apiUrl}s?platform=3\", \"DELETE\", body.encodeToByteArray(), referer, ua\n )?.decodeToString() ?: return \"空回应\"\n return Gson().fromJson(re, ReturnBase::class.java).message\n }\n\n fun query(pathWord: String): BookQueryStructure {\n DownloadTools.getHttpContent(queryApiUrl.format(hostUrl, pathWord), referer, ua)?.let {\n return Gson().fromJson(it.decodeToString(), BookQueryStructure::class.java)\n }\n val b = BookQueryStructure()\n b.code = 400\n return b\n }\n}\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt b/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt
+--- a/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt (date 1709887808771)
+@@ -1,6 +1,8 @@
+ package top.fumiama.copymanga.manga
+
+ import com.google.gson.Gson
++import kotlinx.coroutines.Dispatchers
++import kotlinx.coroutines.withContext
+ import top.fumiama.copymanga.json.BookQueryStructure
+ import top.fumiama.copymanga.json.ReturnBase
+ import top.fumiama.copymanga.tools.http.DownloadTools
+@@ -12,9 +14,9 @@
+ private val queryApiUrl = getString(R.string.bookUserQueryApiUrl)
+ private val referer: String = getString(R.string.referer)
+ private val ua: String = getString(R.string.pc_ua)
+- fun add(comicId: String): String {
++ suspend fun add(comicId: String): String = withContext(Dispatchers.IO) {
+ if (comicId.isEmpty()) {
+- return "空漫画ID"
++ return@withContext "空漫画ID"
+ }
+ val body = buildString {
+ append("comic_id=")
+@@ -25,13 +27,13 @@
+ }
+ val re = DownloadTools.requestWithBody(
+ "$apiUrl?platform=3", "POST", body.encodeToByteArray(), referer, ua
+- )?.decodeToString() ?: return "空回应"
+- return Gson().fromJson(re, ReturnBase::class.java).message
++ )?.decodeToString() ?: return@withContext "空回应"
++ return@withContext Gson().fromJson(re, ReturnBase::class.java).message
+ }
+
+- fun del(vararg bookIds: Int): String {
++ suspend fun del(vararg bookIds: Int): String = withContext(Dispatchers.IO) {
+ if (bookIds.isEmpty()) {
+- return "空ID列表"
++ return@withContext "空ID列表"
+ }
+ val body = buildString {
+ bookIds.forEach {
+@@ -44,16 +46,13 @@
+ }
+ val re = DownloadTools.requestWithBody(
+ "${apiUrl}s?platform=3", "DELETE", body.encodeToByteArray(), referer, ua
+- )?.decodeToString() ?: return "空回应"
+- return Gson().fromJson(re, ReturnBase::class.java).message
++ )?.decodeToString() ?: return@withContext "空回应"
++ return@withContext Gson().fromJson(re, ReturnBase::class.java).message
+ }
+
+- fun query(pathWord: String): BookQueryStructure {
+- DownloadTools.getHttpContent(queryApiUrl.format(hostUrl, pathWord), referer, ua)?.let {
+- return Gson().fromJson(it.decodeToString(), BookQueryStructure::class.java)
++ suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
++ return@withContext DownloadTools.getHttpContent(queryApiUrl.format(hostUrl, pathWord), referer, ua)?.let {
++ Gson().fromJson(it.decodeToString(), BookQueryStructure::class.java)
+ }
+- val b = BookQueryStructure()
+- b.code = 400
+- return b
+ }
+ }
+Index: app/src/main/java/top/fumiama/copymanga/tools/ui/GlideHideLottieViewListener.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.tools.ui\n\nimport android.graphics.drawable.Drawable\nimport android.view.View\nimport com.airbnb.lottie.LottieAnimationView\nimport com.bumptech.glide.load.DataSource\nimport com.bumptech.glide.load.engine.GlideException\nimport com.bumptech.glide.request.RequestListener\nimport com.bumptech.glide.request.target.Target\nimport java.lang.ref.WeakReference\n\nclass GlideHideLottieViewListener(private val wla: WeakReference, private val runAfterLoad: (() -> Unit)? = null): RequestListener {\n override fun onLoadFailed(\n e: GlideException?,\n model: Any?,\n target: Target?,\n isFirstResource: Boolean\n ): Boolean {\n return false\n }\n\n override fun onResourceReady(\n resource: Drawable?,\n model: Any?,\n target: Target?,\n dataSource: DataSource?,\n isFirstResource: Boolean\n ): Boolean {\n wla.get()?.apply {\n pauseAnimation()\n visibility = View.GONE\n runAfterLoad?.let { it() }\n }\n return false\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/tools/ui/GlideHideLottieViewListener.kt b/app/src/main/java/top/fumiama/copymanga/tools/ui/GlideHideLottieViewListener.kt
+--- a/app/src/main/java/top/fumiama/copymanga/tools/ui/GlideHideLottieViewListener.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/tools/ui/GlideHideLottieViewListener.kt (date 1709881963313)
+@@ -13,17 +13,17 @@
+ override fun onLoadFailed(
+ e: GlideException?,
+ model: Any?,
+- target: Target?,
++ target: Target,
+ isFirstResource: Boolean
+ ): Boolean {
+ return false
+ }
+
+ override fun onResourceReady(
+- resource: Drawable?,
+- model: Any?,
++ resource: Drawable,
++ model: Any,
+ target: Target?,
+- dataSource: DataSource?,
++ dataSource: DataSource,
+ isFirstResource: Boolean
+ ): Boolean {
+ wla.get()?.apply {
+Index: app/src/main/java/top/fumiama/copymanga/user/Member.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.user\n\nimport android.content.SharedPreferences\nimport com.google.gson.Gson\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport top.fumiama.copymanga.json.LoginInfoStructure\nimport top.fumiama.copymanga.tools.api.CMApi\nimport top.fumiama.copymanga.tools.http.DownloadTools\nimport top.fumiama.dmzj.copymanga.R\n\nclass Member(private val pref: SharedPreferences, private val getString: (Int) -> String) {\n val hasLogin: Boolean get() = pref.getString(\"token\", \"\")?.isNotEmpty()?:false\n suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure = withContext(Dispatchers.IO) {\n try {\n CMApi.getLoginConnection(username, pwd, salt)?.apply {\n Gson().fromJson(inputStream.reader(), LoginInfoStructure::class.java)?.let { data ->\n disconnect()\n if(data.code == 200) {\n pref.edit()?.apply {\n putString(\"token\", data.results?.token)\n putString(\"user_id\", data.results?.user_id)\n putString(\"username\", data.results?.username)\n putString(\"nickname\", data.results?.nickname)\n apply()\n return@withContext refreshAvatar()\n }\n }\n return@withContext data\n }\n }\n val l = LoginInfoStructure()\n l.code = 400\n l.message = getString(R.string.login_get_conn_failed)\n return@withContext l\n } catch (e: Exception) {\n val l = LoginInfoStructure()\n l.code = 400\n l.message = e.localizedMessage\n return@withContext l\n }\n }\n\n\n\n fun refreshAvatar() : LoginInfoStructure {\n if (!pref.contains(\"token\")) {\n val l = LoginInfoStructure()\n l.code = 400\n l.message = getString(R.string.noLogin)\n return l\n }\n try {\n DownloadTools.getHttpContent(getString(R.string.memberInfoApiUrl).format(\n CMApi.myHostApiUrl))?.decodeToString()?.let {\n val l = Gson().fromJson(it, LoginInfoStructure::class.java)\n if(l.code == 200) pref.edit()?.apply {\n putString(\"avatar\", l.results.avatar)\n apply()\n }\n return l\n }\n } catch (e: Exception) {\n val l = LoginInfoStructure()\n l.code = 400\n l.message = \"${getString(R.string.login_get_avatar_failed)}: ${e.localizedMessage}\"\n return l\n }\n val l = LoginInfoStructure()\n l.code = 400\n l.message = getString(R.string.login_get_avatar_failed)\n return l\n }\n\n fun logout() {\n pref.edit()?.apply {\n remove(\"token\")\n remove(\"user_id\")\n remove(\"username\")\n remove(\"nickname\")\n remove(\"avatar\")\n apply()\n }\n }\n}\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/user/Member.kt b/app/src/main/java/top/fumiama/copymanga/user/Member.kt
+--- a/app/src/main/java/top/fumiama/copymanga/user/Member.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/user/Member.kt (date 1709886202034)
+@@ -11,7 +11,7 @@
+
+ class Member(private val pref: SharedPreferences, private val getString: (Int) -> String) {
+ val hasLogin: Boolean get() = pref.getString("token", "")?.isNotEmpty()?:false
+- suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure = withContext(Dispatchers.IO) {
++ suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure = withContext(Dispatchers.IO) {
+ try {
+ CMApi.getLoginConnection(username, pwd, salt)?.apply {
+ Gson().fromJson(inputStream.reader(), LoginInfoStructure::class.java)?.let { data ->
+Index: app/src/main/java/top/fumiama/copymanga/tools/http/Resolution.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.tools.http\n\nimport androidx.preference.PreferenceManager\nimport top.fumiama.copymanga.MainActivity\nimport top.fumiama.dmzj.copymanga.R\n\nclass Resolution(private val original: Regex) {\n private val imageResolution: Int\n get() {\n MainActivity.mainWeakReference?.get()?.apply {\n PreferenceManager.getDefaultSharedPreferences(this).apply {\n val b = getString(getString(R.string.imgResolutionKeyID), null)\n //Log.d(\"MyResolution\", \"use image resolution: $b\")\n return b?.toInt()?:1500\n }\n }\n return 1500\n }\n fun wrap(u: String) : String = u.replace(original, \".c${imageResolution}x.\")\n}\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/tools/http/Resolution.kt b/app/src/main/java/top/fumiama/copymanga/tools/http/Resolution.kt
+--- a/app/src/main/java/top/fumiama/copymanga/tools/http/Resolution.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/tools/http/Resolution.kt (date 1709890277658)
+@@ -16,5 +16,5 @@
+ }
+ return 1500
+ }
+- fun wrap(u: String) : String = u.replace(original, ".c${imageResolution}x.")
++ fun wrap(u: String) : String = u.replace(original, "c${imageResolution}x.")
+ }
+Index: app/src/main/res/navigation/mobile_navigation.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>\n\n\n \n \n \n \n \n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n \n\n \n \n \n \n \n\n \n \n\n \n \n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
+--- a/app/src/main/res/navigation/mobile_navigation.xml (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/res/navigation/mobile_navigation.xml (date 1709906072716)
+@@ -224,7 +224,7 @@
+
+
+ UTF-8
+===================================================================
+diff --git a/app/src/main/res/drawable-anydpi/ic_hot.xml b/app/src/main/res/drawable-anydpi/ic_hot.xml
+new file mode 100644
+--- /dev/null (date 1709889333999)
++++ b/app/src/main/res/drawable-anydpi/ic_hot.xml (date 1709889333999)
+@@ -0,0 +1,10 @@
++
++
++
++
+\ No newline at end of file
+Index: app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.ui.cardflow.rank\n\nimport android.os.Bundle\nimport com.google.android.material.tabs.TabLayout\nimport kotlinx.android.synthetic.main.fragment_rank.*\nimport kotlinx.android.synthetic.main.line_rank.view.*\nimport top.fumiama.copymanga.MainActivity\nimport top.fumiama.copymanga.template.ui.InfoCardLoader\nimport top.fumiama.copymanga.tools.api.CMApi\nimport top.fumiama.copymanga.tools.ui.UITools\nimport top.fumiama.dmzj.copymanga.R\nimport java.lang.Thread.sleep\nimport java.lang.ref.WeakReference\n\n@ExperimentalStdlibApi\nclass RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank_to_nav_book, true) {\n private val sortWay = listOf(\"day\", \"week\", \"month\", \"total\")\n private var sortValue = 0\n private val audienceWay = listOf(\"\", \"male\", \"female\")\n private var audience = 0 // 0 all 1 male 2 female\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n wr = WeakReference(this)\n }\n\n override fun onPause() {\n super.onPause()\n ad?.exit = true\n }\n\n override fun onResume() {\n super.onResume()\n ad?.exit = true\n }\n\n override fun onDestroy() {\n super.onDestroy()\n wr = null\n ad?.exit = true\n }\n\n override fun getApiUrl() =\n getString(R.string.rankApiUrl).format(\n CMApi.myHostApiUrl,\n page * 21,\n sortWay[sortValue],\n audienceWay[audience]\n )\n\n override fun setListeners() {\n super.setListeners()\n frlai.lrt.addOnTabSelectedListener(object: TabLayout.OnTabSelectedListener {\n override fun onTabReselected(tab: TabLayout.Tab?) {}\n\n override fun onTabSelected(tab: TabLayout.Tab?) {\n setSortValue(tab?.position?:0)\n }\n\n override fun onTabUnselected(tab: TabLayout.Tab?) {}\n })\n }\n\n private fun setSortValue(value: Int) {\n sortValue = value\n Thread{\n sleep(400)\n if(ad?.exit != true) activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n\n fun showSexInfo(toolsBox: UITools) {\n if (ad?.exit != false) return\n toolsBox.buildInfo(\"切换类型\", \"选择一种想筛选的漫画类型\",\n \"男频\", \"全部\", \"女频\", {\n audience = 1\n reset()\n Thread {\n sleep(600)\n addPage()\n }.start()\n }, {\n audience = 0\n reset()\n Thread {\n sleep(600)\n addPage()\n }.start()\n }, {\n audience = 2\n reset()\n Thread {\n sleep(600)\n addPage()\n }.start()\n })\n }\n\n companion object {\n var wr: WeakReference? = null\n }\n}\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt
+--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt (date 1709908862385)
+@@ -13,7 +13,7 @@
+ import java.lang.ref.WeakReference
+
+ @ExperimentalStdlibApi
+-class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank_to_nav_book, true) {
++class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank_to_nav_book, isTypeBook = true) {
+ private val sortWay = listOf("day", "week", "month", "total")
+ private var sortValue = 0
+ private val audienceWay = listOf("", "male", "female")
+Index: app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.template.ui\n\nimport android.annotation.SuppressLint\nimport android.net.Uri\nimport android.util.Log\nimport android.view.View\nimport androidx.fragment.app.Fragment\nimport com.bumptech.glide.Glide\nimport com.bumptech.glide.load.model.GlideUrl\nimport kotlinx.android.synthetic.main.card_book.*\nimport kotlinx.android.synthetic.main.card_book.view.*\nimport kotlinx.android.synthetic.main.line_horizonal_empty.view.*\nimport kotlinx.android.synthetic.main.line_lazybooklines.*\nimport top.fumiama.copymanga.tools.api.CMApi\nimport top.fumiama.copymanga.tools.ui.GlideHideLottieViewListener\nimport top.fumiama.dmzj.copymanga.R\nimport java.io.File\nimport java.lang.ref.WeakReference\nimport java.util.concurrent.atomic.AtomicInteger\n\nclass CardList(\n private val fragment: WeakReference,\n private val cardWidth: Int,\n private val cardHeight: Int,\n private val cardPerRow: Int\n) {\n private val that get() = fragment.get()\n private var rows:Array = arrayOfNulls(20)\n private var index = 0\n private var count = 0\n private var cardLoadingWaits = AtomicInteger()\n var initClickListeners: InitClickListeners? = null\n var exitCardList = false\n\n fun reset(){\n rows = arrayOfNulls(20)\n index = 0\n count = 0\n exitCardList = false\n }\n\n private fun manageRow() {\n if(!exitCardList && count++ % cardPerRow == 0) inflateRow()\n Log.d(\"MyCL\", \"index: $index, cardPR: $cardPerRow\")\n }\n\n private fun inflateRow(){\n that?.apply {\n layoutInflater.inflate(R.layout.line_horizonal_empty, mydll, false)?.let {\n if(exitCardList) return\n it.layoutParams.height = cardHeight + 16\n activity?.runOnUiThread {\n if(exitCardList) return@runOnUiThread\n mydll?.addView(it)\n }\n recycleOneRow(it)\n index++\n }\n }\n }\n private fun recycleOneRow(v:View?){\n val relativeIndex = index % 20\n if(rows[relativeIndex] == null) rows[relativeIndex] = v\n else {\n val victim = rows[relativeIndex]\n that?.apply {\n activity?.runOnUiThread {\n if(exitCardList) return@runOnUiThread\n mydll?.removeView(victim)\n mys?.scrollY = mys?.scrollY?.minus(cardHeight + 16)?:0\n }\n }\n rows[relativeIndex] = v\n }\n }\n\n @ExperimentalStdlibApi\n fun addCard(name: String, append: String? = null, head: String? = null, path: String? = null, chapterUUID: String? = null, pn: Int? = null, isFinish: Boolean = false, isNew: Boolean = false) {\n if (exitCardList) return\n manageRow()\n that?.apply {\n layoutInflater.inflate(R.layout.card_book, mydll?.ltbtn, false)?.let {\n val card = it.cic\n card.name = name\n card.append = append\n card.headImageUrl = head\n card.path = path\n card.index = index - 1\n card.chapterUUID = chapterUUID\n card.pageNumber = pn\n card.isFinish = isFinish\n card.isNew = isNew\n activity?.runOnUiThread {\n if (exitCardList) return@runOnUiThread\n addCard(it)\n }\n }\n }\n }\n\n @SuppressLint(\"SetTextI18n\")\n @ExperimentalStdlibApi\n fun addCard(cardFrame: View) {\n val card = cardFrame.cic\n if (card.index < 0) return\n val name = card.name + (card.append?:\"\")\n val head = card.headImageUrl\n val file = File(that?.context?.getExternalFilesDir(\"\"), card.name)\n if(exitCardList) return\n cardFrame.let {\n it.tic.text = name\n if(!file.exists()){\n if(head != null) {\n that?.context?.let { context ->\n val waitMillis = cardLoadingWaits.getAndIncrement().toLong()*200\n val g = Glide.with(context).load(\n GlideUrl(CMApi.proxy?.wrap(head)?:head, CMApi.myGlideHeaders)\n ).addListener(GlideHideLottieViewListener(WeakReference(it.laic)) {\n if (exitCardList) return@GlideHideLottieViewListener\n cardLoadingWaits.decrementAndGet()\n })\n if (waitMillis > 0) it.imic.postDelayed({\n if (exitCardList) return@postDelayed\n g.into(it.imic)\n }, waitMillis) else g.into(it.imic)\n }\n } else {\n it.laic.pauseAnimation()\n it.laic.visibility = View.GONE\n it.imic.setImageResource(R.drawable.img_defmask)\n }\n } else {\n val img = File(file, \"head.jpg\")\n it.laic.pauseAnimation()\n it.laic.visibility = View.GONE\n if(img.exists()) {\n it.imic.setImageURI(Uri.fromFile(img))\n } else {\n it.imic.setImageResource(R.drawable.img_defmask)\n }\n }\n if(card.isFinish) it.sgnic.visibility = View.VISIBLE\n if(card.isNew) it.sgnnew.visibility = View.VISIBLE\n initClickListeners?.prepareListeners(card, card.name, card.path, card.chapterUUID, card.pageNumber)\n rows[card.index % 20]?.ltbtn?.addView(it)\n it.layoutParams?.height = cardHeight\n it.layoutParams?.width = cardWidth\n }\n }\n interface InitClickListeners{\n fun prepareListeners(v: View, name: String, path: String?, chapterUUID: String?, pn: Int?)\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt b/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt
+--- a/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt (date 1709908106372)
+@@ -36,6 +36,7 @@
+ rows = arrayOfNulls(20)
+ index = 0
+ count = 0
++ cardLoadingWaits.set(0)
+ exitCardList = false
+ }
+
+Index: app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.ui.cardflow.sort\n\nimport android.animation.ObjectAnimator\nimport com.github.zawadz88.materialpopupmenu.popupMenu\nimport com.google.gson.Gson\nimport kotlinx.android.synthetic.main.anchor_popular.view.*\nimport kotlinx.android.synthetic.main.line_sort.*\nimport top.fumiama.copymanga.MainActivity.Companion.mainWeakReference\nimport top.fumiama.copymanga.json.FilterStructure\nimport top.fumiama.copymanga.template.http.AutoDownloadThread\nimport top.fumiama.copymanga.template.ui.InfoCardLoader\nimport top.fumiama.copymanga.tools.api.CMApi\nimport top.fumiama.dmzj.copymanga.R\nimport java.lang.Thread.sleep\n\n@ExperimentalStdlibApi\nclass SortFragment : InfoCardLoader(R.layout.fragment_sort, R.id.action_nav_sort_to_nav_book) {\n private val sortWay = listOf(\"-datetime_updated\", \"datetime_updated\", \"-popular\", \"popular\")\n private var theme = -1\n private var region = -1\n private var sortValue = 0\n private var filter: FilterStructure? = null\n\n override fun getApiUrl() =\n getString(R.string.sortApiUrl).format(\n CMApi.myHostApiUrl,\n page * 21,\n sortWay[sortValue],\n if(theme >= 0) (filter?.results?.theme?.get(theme)?.path_word ?: \"\") else \"\",\n if(region >= 0) (filter?.results?.top?.get(region)?.path_word ?: \"\") else \"\",\n )\n\n override fun setListeners() {\n super.setListeners()\n setUpdate()\n setHot()\n AutoDownloadThread(getString(R.string.filterApiUrl).format(CMApi.myHostApiUrl)) {\n if(ad?.exit == true) return@AutoDownloadThread\n it?.let {\n filter = Gson().fromJson(it.inputStream().reader(), FilterStructure::class.java)\n if(ad?.exit == true) return@AutoDownloadThread\n activity?.runOnUiThread{\n if(ad?.exit != true) setClasses()\n }\n }\n }.start()\n }\n\n private fun setUpdate(){\n if(ad?.exit == true) return\n line_sort_time.apt.setText(R.string.menu_update_time)\n line_sort_time.setOnClickListener {\n sortValue = if(it.apim.rotation == 0f) {\n ObjectAnimator.ofFloat(it.apim, \"rotation\", 0f, 180f).setDuration(233).start()\n 1\n }else{\n ObjectAnimator.ofFloat(it.apim, \"rotation\", 180f, 0f).setDuration(233).start()\n 0\n }\n Thread{\n sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n\n private fun setClasses(){\n filter?.results?.top?.let { items ->\n if(ad?.exit == true) return@let\n line_sort_region.apt.text = \"全部\"\n line_sort_region.setOnClickListener {\n val popupMenu = popupMenu {\n style = R.style.Widget_MPM_Menu_Dark_CustomBackground\n section {\n item {\n label = \"全部\"\n labelColor = it.apt.currentTextColor\n callback = {\n region = -1\n it.apt.text = \"全部\"\n Thread{\n sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n for(i in items.indices) item {\n label = items[i].name\n labelColor = it.apt.currentTextColor\n callback = { //optional\n it.apt.text = label\n region = i\n Thread{\n sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n }\n }\n this.context?.let { it1 -> popupMenu.show(it1, it) }\n }\n }\n filter?.results?.theme?.let { items ->\n if(ad?.exit == true) return@let\n line_sort_class.apt.text = \"全部\"\n line_sort_class.setOnClickListener {\n val popupMenu = popupMenu {\n style = R.style.Widget_MPM_Menu_Dark_CustomBackground\n section {\n item {\n label = \"全部\"\n labelColor = it.apt.currentTextColor\n callback = {\n theme = -1\n it.apt.text = \"全部\"\n Thread{\n sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n for(i in items.indices) item {\n label = items[i].name\n labelColor = it.apt.currentTextColor\n callback = { //optional\n it.apt.text = label\n theme = i\n Thread{\n sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n }\n }\n this.context?.let { it1 -> popupMenu.show(it1, it) }\n }\n }\n }\n\n private fun setHot() {\n if(ad?.exit == true) return\n line_sort_hot.apt.setText(R.string.menu_hot)\n line_sort_hot.setOnClickListener {\n sortValue = if (it.apim.rotation == 0f) {\n ObjectAnimator.ofFloat(it.apim, \"rotation\", 0f, 180f).setDuration(233).start()\n 3\n } else {\n ObjectAnimator.ofFloat(it.apim, \"rotation\", 180f, 0f).setDuration(233).start()\n 2\n }\n Thread {\n sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt
+--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt (date 1709908962128)
+@@ -1,24 +1,22 @@
+ package top.fumiama.copymanga.ui.cardflow.sort
+
+-import android.animation.ObjectAnimator
++import android.os.Bundle
++import android.view.View
+ import com.github.zawadz88.materialpopupmenu.popupMenu
+ import com.google.gson.Gson
+ import kotlinx.android.synthetic.main.anchor_popular.view.*
+ import kotlinx.android.synthetic.main.line_sort.*
+-import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
+ import top.fumiama.copymanga.json.FilterStructure
+ import top.fumiama.copymanga.template.http.AutoDownloadThread
+-import top.fumiama.copymanga.template.ui.InfoCardLoader
++import top.fumiama.copymanga.template.ui.StatusCardFlow
+ import top.fumiama.copymanga.tools.api.CMApi
+ import top.fumiama.dmzj.copymanga.R
+ import java.lang.Thread.sleep
+
+ @ExperimentalStdlibApi
+-class SortFragment : InfoCardLoader(R.layout.fragment_sort, R.id.action_nav_sort_to_nav_book) {
+- private val sortWay = listOf("-datetime_updated", "datetime_updated", "-popular", "popular")
++class SortFragment : StatusCardFlow(0, R.id.action_nav_sort_to_nav_book, R.layout.fragment_sort) {
+ private var theme = -1
+ private var region = -1
+- private var sortValue = 0
+ private var filter: FilterStructure? = null
+
+ override fun getApiUrl() =
+@@ -30,10 +28,14 @@
+ if(region >= 0) (filter?.results?.top?.get(region)?.path_word ?: "") else "",
+ )
+
++ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
++ super.onViewCreated(view, savedInstanceState)
++ lineUpdate = line_sort_time
++ lineHot = line_sort_hot
++ }
++
+ override fun setListeners() {
+ super.setListeners()
+- setUpdate()
+- setHot()
+ AutoDownloadThread(getString(R.string.filterApiUrl).format(CMApi.myHostApiUrl)) {
+ if(ad?.exit == true) return@AutoDownloadThread
+ it?.let {
+@@ -45,27 +47,6 @@
+ }
+ }.start()
+ }
+-
+- private fun setUpdate(){
+- if(ad?.exit == true) return
+- line_sort_time.apt.setText(R.string.menu_update_time)
+- line_sort_time.setOnClickListener {
+- sortValue = if(it.apim.rotation == 0f) {
+- ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
+- 1
+- }else{
+- ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
+- 0
+- }
+- Thread{
+- sleep(400)
+- activity?.runOnUiThread {
+- reset()
+- addPage()
+- }
+- }.start()
+- }
+- }
+
+ private fun setClasses(){
+ filter?.results?.top?.let { items ->
+@@ -153,25 +134,4 @@
+ }
+ }
+ }
+-
+- private fun setHot() {
+- if(ad?.exit == true) return
+- line_sort_hot.apt.setText(R.string.menu_hot)
+- line_sort_hot.setOnClickListener {
+- sortValue = if (it.apim.rotation == 0f) {
+- ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
+- 3
+- } else {
+- ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
+- 2
+- }
+- Thread {
+- sleep(400)
+- activity?.runOnUiThread {
+- reset()
+- addPage()
+- }
+- }.start()
+- }
+- }
+ }
+\ No newline at end of file
+Index: app/src/main/java/top/fumiama/copymanga/template/ui/ThemeCardFlow.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.template.ui\n\nimport android.os.Bundle\nimport kotlinx.android.synthetic.main.app_bar_main.*\nimport top.fumiama.copymanga.MainActivity.Companion.mainWeakReference\nimport top.fumiama.copymanga.tools.api.CMApi\n\n@ExperimentalStdlibApi\nopen class ThemeCardFlow(private val api: Int, nav: Int) : StatusCardFlow(0, nav) {\n private var theme = \"\"\n override fun getApiUrl() =\n getString(api).format(\n CMApi.myHostApiUrl,\n page * 21,\n sortWay[sortValue],\n theme\n )\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n arguments?.apply {\n getString(\"path\")?.apply { theme = this }\n getString(\"name\")?.apply {\n mainWeakReference?.get()?.toolbar?.title = this\n }\n }\n }\n\n override fun onResume() {\n super.onResume()\n arguments?.getString(\"name\")?.apply {\n mainWeakReference?.get()?.toolbar?.title = this\n }\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/template/ui/ThemeCardFlow.kt b/app/src/main/java/top/fumiama/copymanga/template/ui/ThemeCardFlow.kt
+--- a/app/src/main/java/top/fumiama/copymanga/template/ui/ThemeCardFlow.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/template/ui/ThemeCardFlow.kt (date 1709907861458)
+@@ -1,12 +1,17 @@
+ package top.fumiama.copymanga.template.ui
+
+ import android.os.Bundle
++import android.view.LayoutInflater
++import android.view.View
++import android.view.ViewGroup
+ import kotlinx.android.synthetic.main.app_bar_main.*
++import kotlinx.android.synthetic.main.line_finish.*
+ import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
+ import top.fumiama.copymanga.tools.api.CMApi
++import top.fumiama.dmzj.copymanga.R
+
+ @ExperimentalStdlibApi
+-open class ThemeCardFlow(private val api: Int, nav: Int) : StatusCardFlow(0, nav) {
++open class ThemeCardFlow(private val api: Int, nav: Int) : StatusCardFlow(0, nav, R.layout.fragment_statuscardflow) {
+ private var theme = ""
+ override fun getApiUrl() =
+ getString(api).format(
+@@ -26,6 +31,12 @@
+ }
+ }
+
++ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
++ super.onViewCreated(view, savedInstanceState)
++ lineUpdate = line_finish_time
++ lineHot = line_finish_pop
++ }
++
+ override fun onResume() {
+ super.onResume()
+ arguments?.getString("name")?.apply {
+Index: app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.tools.api\n\nimport android.util.Base64\nimport androidx.preference.PreferenceManager\nimport com.bumptech.glide.load.model.LazyHeaders\nimport top.fumiama.dmzj.copymanga.R\nimport top.fumiama.copymanga.MainActivity\nimport top.fumiama.copymanga.tools.http.DownloadTools\nimport top.fumiama.copymanga.tools.http.Proxy\nimport top.fumiama.copymanga.tools.http.Resolution\nimport java.io.File\nimport java.net.URLEncoder\n\nobject CMApi {\n var proxy = if(Proxy.useImageProxy) Proxy(R.string.imgProxyApiUrl, R.string.imgProxyApiPrefix, R.string.imgProxyKeyID) else null\n var resolution = Resolution(Regex(\"\\\\.c\\\\d+x\\\\.\"))\n var myGlideHeaders: LazyHeaders? = null\n get() {\n MainActivity.mainWeakReference?.get()?.let {\n PreferenceManager.getDefaultSharedPreferences(it).apply {\n if(field === null)\n field = LazyHeaders.Builder()\n .addHeader(\"referer\", MainActivity.mainWeakReference?.get()?.getString(R.string.referer)!!)\n .addHeader(\"User-Agent\", MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)!!)\n .addHeader(\"source\", \"copyApp\")\n .addHeader(\"webp\", \"1\")\n .addHeader(\"version\", MainActivity.mainWeakReference?.get()?.getString(R.string.app_ver)!!)\n .addHeader(\"region\", if(!getBoolean(\"settings_cat_net\", false)) \"1\" else \"0\")\n .addHeader(\"platform\", \"3\")\n .build()\n }\n }\n return field\n }\n var myHostApiUrl: String = \"\"\n get() {\n if(field != \"\") return field\n MainActivity.mainWeakReference?.get()?.let {\n PreferenceManager.getDefaultSharedPreferences(it).apply {\n getString(\"settings_cat_net_et_api_url\", \"\")?.let { host ->\n if(host != \"\") {\n field = host\n return host\n }\n }\n }\n field = it.getString(R.string.hostUrl)\n }\n return field\n }\n\n fun getZipFile(exDir: File?, manga: String, caption: CharSequence, name: CharSequence) = File(exDir, \"$manga/$caption/$name.zip\")\n fun getChapterInfoApiUrl(arg1: String?, arg2: String?) =\n MainActivity.mainWeakReference?.get()?.getString(R.string.chapterInfoApiUrl)?.format(myHostApiUrl, arg1, arg2)\n fun getGroupInfoApiUrl(arg1: String?, arg2: String?, arg3: Int? = 0) =\n MainActivity.mainWeakReference?.get()?.getString(R.string.groupInfoApiUrl)?.format(myHostApiUrl, arg1, arg2, arg3)\n fun getLoginConnection(username: String, pwd: String, salt: Int) =\n MainActivity.mainWeakReference?.get()?.getString(R.string.loginApiUrl)?.format(myHostApiUrl)?.let {\n DownloadTools.getConnection(it, \"POST\")?.apply {\n MainActivity.mainWeakReference?.get()?.let {\n PreferenceManager.getDefaultSharedPreferences(it).apply {\n doOutput = true\n setRequestProperty(\"content-type\", \"application/x-www-form-urlencoded;charset=utf-8\")\n setRequestProperty(\"platform\", \"3\")\n setRequestProperty(\"accept\", \"application/json\")\n val r = if(!getBoolean(\"settings_cat_net_sw_use_foreign\", false)) \"1\" else \"0\"\n val pwdb64 = Base64.encode(\"$pwd-$salt\".toByteArray(), Base64.DEFAULT).decodeToString()\n outputStream.write(\"username=${URLEncoder.encode(username)}&password=$pwdb64&salt=$salt&platform=3&authorization=Token+&version=1.4.4&source=copyApp®ion=$r&webp=1\".toByteArray())\n }\n }\n }\n }\n }\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt b/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt
+--- a/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt (date 1709890277655)
+@@ -13,7 +13,7 @@
+
+ object CMApi {
+ var proxy = if(Proxy.useImageProxy) Proxy(R.string.imgProxyApiUrl, R.string.imgProxyApiPrefix, R.string.imgProxyKeyID) else null
+- var resolution = Resolution(Regex("\\.c\\d+x\\."))
++ var resolution = Resolution(Regex("c\\d+x\\."))
+ var myGlideHeaders: LazyHeaders? = null
+ get() {
+ MainActivity.mainWeakReference?.get()?.let {
+Index: app/build.gradle
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\n\nandroid {\n defaultConfig {\n compileSdk 34\n applicationId 'top.fumiama.copymanga'\n minSdkVersion 23\n targetSdkVersion 34\n versionCode 45\n versionName '2.1.2'\n resConfigs 'zh', 'zh-rCN'\n\n testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n }\n\n aaptOptions {\n cruncherEnabled = false\n }\n\n buildTypes {\n release {\n minifyEnabled true\n shrinkResources true\n proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n }\n /*winrelease {\n minifyEnabled true\n shrinkResources true\n proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n }\n debug{\n minifyEnabled true\n shrinkResources true\n proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n }*/\n }\n compileOptions {\n sourceCompatibility JavaVersion.VERSION_11\n targetCompatibility JavaVersion.VERSION_11\n }\n kotlinOptions {\n jvmTarget = '11'\n }\n bundle{\n density{\n enableSplit = true\n }\n language{\n enableSplit = false\n }\n }\n namespace 'top.fumiama.dmzj.copymanga'\n}\n\ndependencies {\n implementation 'androidx.core:core-ktx:1.12.0'\n implementation 'androidx.appcompat:appcompat:1.6.1'\n implementation 'androidx.legacy:legacy-support-v4:1.0.0'\n implementation 'com.google.android.material:material:1.11.0'\n implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n implementation 'androidx.navigation:navigation-fragment-ktx:2.7.6'\n implementation 'androidx.navigation:navigation-ui-ktx:2.7.6'\n testImplementation 'junit:junit:4.13.2'\n implementation \"androidx.preference:preference-ktx:1.2.1\"\n implementation 'com.afollestad.material-dialogs:input:3.3.0'\n implementation 'com.github.yalantis:ucrop:2.2.6'\n implementation 'com.to.aboomy:pager2banner:1.0.1'\n implementation 'com.github.bumptech.glide:glide:4.14.2'\n annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'\n implementation 'com.google.code.gson:gson:2.10.1'\n implementation 'com.github.vovaksenov99:OverscrollableScrollView:1.0'\n implementation 'com.liaoinstan.springview:library:1.7.0'\n implementation 'com.github.zawadz88.materialpopupmenu:material-popup-menu:4.0.1'\n implementation 'com.lapism:search:2.4.1@aar'\n implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'\n implementation 'com.airbnb.android:lottie:6.3.0'\n}\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/build.gradle b/app/build.gradle
+--- a/app/build.gradle (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/build.gradle (date 1709911026401)
+@@ -8,16 +8,13 @@
+ applicationId 'top.fumiama.copymanga'
+ minSdkVersion 23
+ targetSdkVersion 34
+- versionCode 45
+- versionName '2.1.2'
+- resConfigs 'zh', 'zh-rCN'
++ versionCode 46
++ versionName '2.1.3'
++ resourceConfigurations += ['zh', 'zh-rCN']
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+- aaptOptions {
+- cruncherEnabled = false
+- }
+
+ buildTypes {
+ release {
+@@ -60,20 +57,20 @@
+ implementation 'androidx.legacy:legacy-support-v4:1.0.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.6'
+- implementation 'androidx.navigation:navigation-ui-ktx:2.7.6'
++ implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7'
++ implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
+ testImplementation 'junit:junit:4.13.2'
+ implementation "androidx.preference:preference-ktx:1.2.1"
+ implementation 'com.afollestad.material-dialogs:input:3.3.0'
+ implementation 'com.github.yalantis:ucrop:2.2.6'
+ implementation 'com.to.aboomy:pager2banner:1.0.1'
+- implementation 'com.github.bumptech.glide:glide:4.14.2'
+- annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
++ implementation 'com.github.bumptech.glide:glide:4.16.0'
++ annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
+ implementation 'com.google.code.gson:gson:2.10.1'
+ implementation 'com.github.vovaksenov99:OverscrollableScrollView:1.0'
+ 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.7.0'
+- implementation 'com.airbnb.android:lottie:6.3.0'
++ implementation 'com.airbnb.android:lottie:6.4.0'
+ }
+Index: app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.template.ui\n\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.View\nimport androidx.fragment.app.Fragment\nimport androidx.navigation.fragment.findNavController\nimport com.google.gson.Gson\nimport kotlinx.android.synthetic.main.line_lazybooklines.*\nimport top.fumiama.copymanga.MainActivity\nimport top.fumiama.copymanga.json.BookListStructure\nimport top.fumiama.copymanga.json.HistoryBookListStructure\nimport top.fumiama.copymanga.json.ShelfStructure\nimport top.fumiama.copymanga.json.TypeBookListStructure\nimport top.fumiama.copymanga.template.general.MangaPagesFragmentTemplate\nimport top.fumiama.copymanga.template.http.AutoDownloadThread\nimport top.fumiama.copymanga.tools.ui.Navigate\nimport java.lang.ref.WeakReference\n\n@ExperimentalStdlibApi\nopen class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isTypeBook: Boolean = false, private val isHistoryBook: Boolean = false, private val isShelfBook: Boolean = false): MangaPagesFragmentTemplate(inflateRes) {\n var offset = 0\n private val subUrl get() = getApiUrl()\n var ad: AutoDownloadThread? = null\n\n override fun addPage(){\n super.addPage()\n ad = AutoDownloadThread(subUrl) {\n if (it == null) {\n activity?.runOnUiThread {\n findNavController().popBackStack()\n }\n return@AutoDownloadThread\n }\n if(isRefresh){\n page = 0\n isRefresh = false\n }\n if(isTypeBook) {\n val bookList = Gson().fromJson(it?.decodeToString(), TypeBookListStructure::class.java)\n bookList?.apply {\n Log.d(\"MyICL\", \"offset:${results.offset}, total:${results.total}\")\n if(results.offset < results.total) {\n if(code == 200) {\n results.list.forEach { book ->\n if(ad?.exit == true) return@AutoDownloadThread\n cardList?.addCard(\n book?.comic?.name?:\"null\", null, book?.comic?.cover,\n book?.comic?.path_word, null, null,\n isFinish = false, isNew = false\n )\n }\n offset += results.list.size\n }\n }\n page++\n }\n } else if(isHistoryBook) {\n val bookList = Gson().fromJson(it?.decodeToString(), HistoryBookListStructure::class.java)\n bookList?.apply {\n Log.d(\"MyICL\", \"offset:${results?.offset}, total:${results?.total}\")\n if(results.offset < results.total) {\n if(code == 200) {\n results?.list?.forEach{ book ->\n if(ad?.exit == true) return@AutoDownloadThread\n cardList?.addCard(\n book?.comic?.name?:\"null\", \"\\n最新${book?.last_chapter_name}\", book?.comic?.cover,\n book?.comic?.path_word, null, null,\n book?.comic?.status==1\n )\n }\n offset += results.list.size\n }\n }\n page++\n }\n } else if (isShelfBook) {\n val bookList = Gson().fromJson(it?.decodeToString(), ShelfStructure::class.java)\n bookList?.apply {\n Log.d(\"MyICL\", \"offset:${results?.offset}, total:${results?.total}\")\n if(results.offset < results.total) {\n if(code == 200) {\n results?.list?.forEach{ book ->\n if(ad?.exit == true) return@AutoDownloadThread\n cardList?.addCard(\n book?.comic?.name?:\"null\", \"\\n读到${book?.last_browse?.last_browse_name}\", book?.comic?.cover,\n book?.comic?.path_word, null, null,\n book?.comic?.status==1,\n book.comic?.browse?.chapter_uuid != book.comic?.last_chapter_id\n )\n }\n offset += results.list.size\n }\n }\n page++\n }\n } else {\n val bookList = Gson().fromJson(it?.decodeToString(), BookListStructure::class.java)\n bookList?.apply {\n Log.d(\"MyICL\", \"offset:${results?.offset}, total:${results?.total}\")\n if(results.offset < results.total) {\n if(code == 200) {\n results?.list?.forEach{ book ->\n if(ad?.exit == true) return@AutoDownloadThread\n cardList?.addCard(book?.name?:\"null\", null, book?.cover, book?.path_word, null, null, false)\n }\n offset += results.list.size\n }\n }\n page++\n }\n }\n onLoadFinish()\n }\n ad?.start()\n }\n override fun initCardList(weakReference: WeakReference) {\n super.initCardList(weakReference)\n cardList = CardList(weakReference, cardWidth, cardHeight, cardPerRow)\n cardList?.initClickListeners = object : CardList.InitClickListeners {\n override fun prepareListeners(v: View, name: String, path: String?, chapterUUID: String?, pn: Int?) {\n v.setOnClickListener {\n val bundle = Bundle()\n bundle.putString(\"path\", path)\n Navigate.safeNavigateTo(findNavController(), navId, bundle)\n }\n }\n }\n }\n\n open fun getApiUrl(): String{\n return \"\"\n }\n\n override fun onLoadFinish() {\n super.onLoadFinish()\n activity?.runOnUiThread {\n if(ad?.exit != true) mypl.visibility = View.GONE\n }\n }\n\n override fun reset() {\n super.reset()\n offset = 0\n }\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n ad?.exit = false\n }\n\n override fun onResume() {\n super.onResume()\n ad?.exit = false\n }\n\n override fun onDestroy() {\n super.onDestroy()\n ad?.exit = true\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt b/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt
+--- a/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt (date 1709904313295)
+@@ -25,8 +25,8 @@
+
+ override fun addPage(){
+ super.addPage()
+- ad = AutoDownloadThread(subUrl) {
+- if (it == null) {
++ ad = AutoDownloadThread(subUrl) { data ->
++ if (data == null) {
+ activity?.runOnUiThread {
+ findNavController().popBackStack()
+ }
+@@ -37,7 +37,7 @@
+ isRefresh = false
+ }
+ if(isTypeBook) {
+- val bookList = Gson().fromJson(it?.decodeToString(), TypeBookListStructure::class.java)
++ val bookList = Gson().fromJson(data?.decodeToString(), TypeBookListStructure::class.java)
+ bookList?.apply {
+ Log.d("MyICL", "offset:${results.offset}, total:${results.total}")
+ if(results.offset < results.total) {
+@@ -56,7 +56,7 @@
+ page++
+ }
+ } else if(isHistoryBook) {
+- val bookList = Gson().fromJson(it?.decodeToString(), HistoryBookListStructure::class.java)
++ val bookList = Gson().fromJson(data?.decodeToString(), HistoryBookListStructure::class.java)
+ bookList?.apply {
+ Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
+ if(results.offset < results.total) {
+@@ -75,7 +75,7 @@
+ page++
+ }
+ } else if (isShelfBook) {
+- val bookList = Gson().fromJson(it?.decodeToString(), ShelfStructure::class.java)
++ val bookList = Gson().fromJson(data?.decodeToString(), ShelfStructure::class.java)
+ bookList?.apply {
+ Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
+ if(results.offset < results.total) {
+@@ -83,7 +83,7 @@
+ results?.list?.forEach{ book ->
+ if(ad?.exit == true) return@AutoDownloadThread
+ cardList?.addCard(
+- book?.comic?.name?:"null", "\n读到${book?.last_browse?.last_browse_name}", book?.comic?.cover,
++ book?.comic?.name?:"null", "\n${book?.last_browse?.last_browse_name?.let { "读到$it" }?:"未读"}", book?.comic?.cover,
+ book?.comic?.path_word, null, null,
+ book?.comic?.status==1,
+ book.comic?.browse?.chapter_uuid != book.comic?.last_chapter_id
+@@ -95,7 +95,7 @@
+ page++
+ }
+ } else {
+- val bookList = Gson().fromJson(it?.decodeToString(), BookListStructure::class.java)
++ val bookList = Gson().fromJson(data?.decodeToString(), BookListStructure::class.java)
+ bookList?.apply {
+ Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
+ if(results.offset < results.total) {
+Index: app/src/main/java/top/fumiama/copymanga/template/ui/StatusCardFlow.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.template.ui\n\nimport android.animation.ObjectAnimator\nimport android.view.View\nimport kotlinx.android.synthetic.main.anchor_popular.view.*\nimport kotlinx.android.synthetic.main.line_finish.*\nimport top.fumiama.copymanga.MainActivity.Companion.mainWeakReference\nimport top.fumiama.copymanga.tools.api.CMApi\nimport top.fumiama.dmzj.copymanga.R\n\n@ExperimentalStdlibApi\nopen class StatusCardFlow(private val api: Int, nav: Int) : InfoCardLoader(R.layout.fragment_statuscardflow, nav) {\n val sortWay = listOf(\"-datetime_updated\", \"datetime_updated\", \"popular\", \"-popular\")\n var sortValue = 0\n\n override fun getApiUrl() =\n getString(api).format(\n CMApi.myHostApiUrl,\n page * 21,\n sortWay[sortValue]\n )\n\n override fun setListeners() {\n super.setListeners()\n setUpdate(line_finish_time)\n setHot(line_finish_pop)\n }\n\n open fun setUpdate(that: View) {\n that.apply {\n apt.setText(R.string.menu_update_time)\n setOnClickListener {\n sortValue = if(apim.rotation == 0f) {\n ObjectAnimator.ofFloat(apim, \"rotation\", 0f, 180f).setDuration(233).start()\n 1\n }else{\n ObjectAnimator.ofFloat(apim, \"rotation\", 180f, 0f).setDuration(233).start()\n 0\n }\n Thread{\n Thread.sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n }\n\n open fun setHot(that: View) {\n that.apply {\n apt.setText(R.string.menu_hot)\n setOnClickListener {\n sortValue = if (apim.rotation == 0f) {\n ObjectAnimator.ofFloat(apim, \"rotation\", 0f, 180f).setDuration(233).start()\n 3\n } else {\n ObjectAnimator.ofFloat(apim, \"rotation\", 180f, 0f).setDuration(233).start()\n 2\n }\n Thread {\n Thread.sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/template/ui/StatusCardFlow.kt b/app/src/main/java/top/fumiama/copymanga/template/ui/StatusCardFlow.kt
+--- a/app/src/main/java/top/fumiama/copymanga/template/ui/StatusCardFlow.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/template/ui/StatusCardFlow.kt (date 1709908862388)
+@@ -3,15 +3,18 @@
+ import android.animation.ObjectAnimator
+ import android.view.View
+ import kotlinx.android.synthetic.main.anchor_popular.view.*
+-import kotlinx.android.synthetic.main.line_finish.*
+-import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
+ import top.fumiama.copymanga.tools.api.CMApi
+ 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")
++open class StatusCardFlow(private val api: Int, nav: Int, inflateRes: Int,
++ isTypeBook: Boolean = false,
++ isHistoryBook: Boolean = false,
++ isShelfBook: Boolean = false) : InfoCardLoader(inflateRes, nav, isTypeBook, isHistoryBook, isShelfBook) {
++ val sortWay = listOf("-datetime_updated", "datetime_updated", "-popular", "popular")
+ var sortValue = 0
++ var lineUpdate: View? = null
++ var lineHot: View? = null
+
+ override fun getApiUrl() =
+ getString(api).format(
+@@ -22,21 +25,17 @@
+
+ override fun setListeners() {
+ super.setListeners()
+- setUpdate(line_finish_time)
+- setHot(line_finish_pop)
++ lineUpdate?.let { setUpdate(it) }
++ lineHot?.let { setHot(it) }
++ lineUpdate?.alpha = 1f
++ lineHot?.alpha = 0.5f
+ }
+
+ open fun setUpdate(that: View) {
+ that.apply {
+ apt.setText(R.string.menu_update_time)
+ setOnClickListener {
+- sortValue = if(apim.rotation == 0f) {
+- ObjectAnimator.ofFloat(apim, "rotation", 0f, 180f).setDuration(233).start()
+- 1
+- }else{
+- ObjectAnimator.ofFloat(apim, "rotation", 180f, 0f).setDuration(233).start()
+- 0
+- }
++ sortValue = triggerLine(false)
+ Thread{
+ Thread.sleep(400)
+ activity?.runOnUiThread {
+@@ -52,13 +51,7 @@
+ that.apply {
+ apt.setText(R.string.menu_hot)
+ setOnClickListener {
+- sortValue = if (apim.rotation == 0f) {
+- ObjectAnimator.ofFloat(apim, "rotation", 0f, 180f).setDuration(233).start()
+- 3
+- } else {
+- ObjectAnimator.ofFloat(apim, "rotation", 180f, 0f).setDuration(233).start()
+- 2
+- }
++ sortValue = triggerLine(true)
+ Thread {
+ Thread.sleep(400)
+ activity?.runOnUiThread {
+@@ -69,4 +62,46 @@
+ }
+ }
+ }
++
++ open fun triggerLine(isHot: Boolean): Int {
++ val hot = lineHot?:return 0
++ val update = lineUpdate?:return 0
++ if(sortValue >= 2) {
++ if (isHot) {
++ return if (hot.apim.rotation == 0f) {
++ ObjectAnimator.ofFloat(hot.apim, "rotation", 0f, 180f).setDuration(233).start()
++ 3
++ } else {
++ ObjectAnimator.ofFloat(hot.apim, "rotation", 180f, 0f).setDuration(233).start()
++ 2
++ }
++ } else {
++ update.alpha = 1f
++ hot.alpha = 0.5f
++ return if(update.apim.rotation == 0f) {
++ 0
++ } else {
++ 1
++ }
++ }
++ } else {
++ if (!isHot) {
++ return if(update.apim.rotation == 0f) {
++ ObjectAnimator.ofFloat(update.apim, "rotation", 0f, 180f).setDuration(233).start()
++ 1
++ }else{
++ ObjectAnimator.ofFloat(update.apim, "rotation", 180f, 0f).setDuration(233).start()
++ 0
++ }
++ } else {
++ hot.alpha = 1f
++ update.alpha = 0.5f
++ return if (hot.apim.rotation == 0f) {
++ 2
++ } else {
++ 3
++ }
++ }
++ }
++ }
+ }
+\ No newline at end of file
+Index: app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.ui.cardflow.shelf\n\nimport android.animation.ObjectAnimator\nimport android.os.Bundle\nimport androidx.navigation.fragment.findNavController\nimport kotlinx.android.synthetic.main.anchor_popular.view.*\nimport kotlinx.android.synthetic.main.line_shelf.*\nimport top.fumiama.copymanga.MainActivity\nimport top.fumiama.copymanga.template.ui.InfoCardLoader\nimport top.fumiama.copymanga.tools.api.CMApi\nimport top.fumiama.dmzj.copymanga.R\nimport java.lang.Thread.sleep\n\n@ExperimentalStdlibApi\nclass ShelfFragment : InfoCardLoader(R.layout.fragment_shelf, R.id.action_nav_sub_to_nav_book, isShelfBook = true) {\n private val sortWay = listOf(\n \"-datetime_updated\",\n \"datetime_updated\",\n \"-datetime_modifier\",\n \"datetime_modifier\",\n \"-datetime_browse\",\n \"datetime_browse\"\n )\n private var sortValue = 0\n\n override fun getApiUrl() =\n getString(R.string.shelfApiUrl).format(\n CMApi.myHostApiUrl,\n page * 21,\n sortWay[sortValue]\n )\n\n override fun onCreate(savedInstanceState: Bundle?) {\n if (MainActivity.member?.hasLogin != true) findNavController().popBackStack()\n super.onCreate(savedInstanceState)\n }\n\n override fun setListeners() {\n super.setListeners()\n setUpdate()\n setModify()\n setBrowse()\n }\n\n private fun setUpdate() {\n if (ad?.exit == true) return\n line_shelf_updated.apt.setText(R.string.menu_update_time)\n line_shelf_updated.setOnClickListener {\n sortValue = if (it.apim.rotation == 0f) {\n ObjectAnimator.ofFloat(it.apim, \"rotation\", 0f, 180f).setDuration(233).start()\n 1\n } else {\n ObjectAnimator.ofFloat(it.apim, \"rotation\", 180f, 0f).setDuration(233).start()\n 0\n }\n Thread {\n sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n\n private fun setModify() {\n if (ad?.exit == true) return\n line_shelf_modifier.apt.setText(R.string.menu_add_time)\n line_shelf_modifier.setOnClickListener {\n sortValue = if (it.apim.rotation == 0f) {\n ObjectAnimator.ofFloat(it.apim, \"rotation\", 0f, 180f).setDuration(233).start()\n 3\n } else {\n ObjectAnimator.ofFloat(it.apim, \"rotation\", 180f, 0f).setDuration(233).start()\n 2\n }\n Thread {\n sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n\n private fun setBrowse() {\n if (ad?.exit == true) return\n line_shelf_browse.apt.setText(R.string.menu_read_time)\n line_shelf_browse.setOnClickListener {\n sortValue = if (it.apim.rotation == 0f) {\n ObjectAnimator.ofFloat(it.apim, \"rotation\", 0f, 180f).setDuration(233).start()\n 5\n } else {\n ObjectAnimator.ofFloat(it.apim, \"rotation\", 180f, 0f).setDuration(233).start()\n 4\n }\n Thread {\n sleep(400)\n activity?.runOnUiThread {\n reset()\n addPage()\n }\n }.start()\n }\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt
+--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt (date 1709910426026)
+@@ -2,9 +2,14 @@
+
+ import android.animation.ObjectAnimator
+ import android.os.Bundle
++import android.view.View
++import androidx.lifecycle.lifecycleScope
+ import androidx.navigation.fragment.findNavController
+ import kotlinx.android.synthetic.main.anchor_popular.view.*
+ import kotlinx.android.synthetic.main.line_shelf.*
++import kotlinx.coroutines.Dispatchers
++import kotlinx.coroutines.launch
++import kotlinx.coroutines.withContext
+ import top.fumiama.copymanga.MainActivity
+ import top.fumiama.copymanga.template.ui.InfoCardLoader
+ import top.fumiama.copymanga.tools.api.CMApi
+@@ -37,6 +42,7 @@
+
+ override fun setListeners() {
+ super.setListeners()
++ fade()
+ setUpdate()
+ setModify()
+ setBrowse()
+@@ -46,20 +52,10 @@
+ if (ad?.exit == true) return
+ line_shelf_updated.apt.setText(R.string.menu_update_time)
+ line_shelf_updated.setOnClickListener {
+- sortValue = if (it.apim.rotation == 0f) {
+- ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
+- 1
+- } else {
+- ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
+- 0
+- }
+- Thread {
+- sleep(400)
+- activity?.runOnUiThread {
+- reset()
+- addPage()
+- }
+- }.start()
++ val same = sortValue in 0..1
++ sortValue = rotate(it.apim, same, 0)
++ if (!same) fade()
++ resetDelayed()
+ }
+ }
+
+@@ -67,20 +63,10 @@
+ if (ad?.exit == true) return
+ line_shelf_modifier.apt.setText(R.string.menu_add_time)
+ line_shelf_modifier.setOnClickListener {
+- sortValue = if (it.apim.rotation == 0f) {
+- ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
+- 3
+- } else {
+- ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
+- 2
+- }
+- Thread {
+- sleep(400)
+- activity?.runOnUiThread {
+- reset()
+- addPage()
+- }
+- }.start()
++ val same = sortValue in 2..3
++ sortValue = rotate(it.apim, same, 2)
++ if (!same) fade()
++ resetDelayed()
+ }
+ }
+
+@@ -88,20 +74,60 @@
+ if (ad?.exit == true) return
+ line_shelf_browse.apt.setText(R.string.menu_read_time)
+ line_shelf_browse.setOnClickListener {
+- sortValue = if (it.apim.rotation == 0f) {
+- ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
+- 5
++ val same = sortValue>=4
++ sortValue = rotate(it.apim, same, 4)
++ if (!same) fade()
++ resetDelayed()
++ }
++ }
++
++ private fun rotate(img: View, isSameSlot: Boolean, offset: Int): Int {
++ return if (isSameSlot) {
++ if (img.rotation == 0f) {
++ ObjectAnimator.ofFloat(img, "rotation", 0f, 180f).setDuration(233).start()
++ offset+1
+ } else {
+- ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
+- 4
++ ObjectAnimator.ofFloat(img, "rotation", 180f, 0f).setDuration(233).start()
++ offset
+ }
+- Thread {
++ } else {
++ if (img.rotation == 0f) {
++ offset
++ } else {
++ offset+1
++ }
++ }
++ }
++
++ private fun fade() {
++ when(sortValue) {
++ 0, 1 -> {
++ line_shelf_updated.alpha = 1f
++ line_shelf_modifier.alpha = 0.5f
++ line_shelf_browse.alpha = 0.5f
++ }
++ 2, 3 -> {
++ line_shelf_updated.alpha = 0.5f
++ line_shelf_modifier.alpha = 1f
++ line_shelf_browse.alpha = 0.5f
++ }
++ 4, 5 -> {
++ line_shelf_updated.alpha = 0.5f
++ line_shelf_modifier.alpha = 0.5f
++ line_shelf_browse.alpha = 1f
++ }
++ }
++ }
++
++ private fun resetDelayed() {
++ lifecycleScope.launch {
++ withContext(Dispatchers.IO) {
+ sleep(400)
+- activity?.runOnUiThread {
++ withContext(Dispatchers.Main) {
+ reset()
+ addPage()
+ }
+- }.start()
++ }
+ }
+ }
+ }
+\ No newline at end of file
+Index: app/src/main/java/top/fumiama/copymanga/json/ComicStructure.java
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.json;\n\npublic class ComicStructure {\n public String uuid;\n public String name;\n public String alias;\n public ValueDisplayPair status;\n public ThemeStructure[] theme;\n public String path_word;\n public ThemeStructure[] author;\n public int img_type;\n public String brief;\n public String datetime_updated;\n public String cover;\n public int popular;\n}\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/json/ComicStructure.java b/app/src/main/java/top/fumiama/copymanga/json/ComicStructure.java
+--- a/app/src/main/java/top/fumiama/copymanga/json/ComicStructure.java (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/json/ComicStructure.java (date 1709888745347)
+@@ -4,11 +4,12 @@
+ public String uuid;
+ public String name;
+ public String alias;
++ public int img_type;
++ public ValueDisplayPair region;
+ public ValueDisplayPair status;
+ public ThemeStructure[] theme;
+ public String path_word;
+ public ThemeStructure[] author;
+- public int img_type;
+ public String brief;
+ public String datetime_updated;
+ public String cover;
+Index: app/src/main/res/drawable-anydpi/ic_image.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/drawable-anydpi/ic_image.xml b/app/src/main/res/drawable-anydpi/ic_image.xml
+new file mode 100644
+--- /dev/null (date 1709889633320)
++++ b/app/src/main/res/drawable-anydpi/ic_image.xml (date 1709889633320)
+@@ -0,0 +1,10 @@
++
++
++
++
+\ No newline at end of file
+Index: app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>package top.fumiama.copymanga.ui.cardflow.topic\n\nimport android.os.Bundle\nimport com.google.gson.Gson\nimport kotlinx.android.synthetic.main.app_bar_main.*\nimport kotlinx.android.synthetic.main.fragment_topic.*\nimport top.fumiama.copymanga.MainActivity.Companion.mainWeakReference\nimport top.fumiama.copymanga.json.TopicStructure\nimport top.fumiama.copymanga.template.http.AutoDownloadThread\nimport top.fumiama.copymanga.template.ui.InfoCardLoader\nimport top.fumiama.copymanga.tools.api.CMApi\nimport top.fumiama.dmzj.copymanga.R\n\n@ExperimentalStdlibApi\nclass TopicFragment : InfoCardLoader(R.layout.fragment_topic, R.id.action_nav_topic_to_nav_book) {\n private var type = 1\n override fun getApiUrl() =\n getString(R.string.topicContentApiUrl).format(CMApi.myHostApiUrl, arguments?.getString(\"path\"), type, offset)\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n AutoDownloadThread(getString(R.string.topicApiUrl).format(CMApi.myHostApiUrl, arguments?.getString(\"path\"))) { data ->\n if(ad?.exit == true) return@AutoDownloadThread\n data?.apply {\n val r = inputStream().reader()\n val topic = Gson().fromJson(r, TopicStructure::class.java)\n topic?.apply {\n if(ad?.exit != false) return@AutoDownloadThread\n activity?.let {\n it.runOnUiThread {\n if(ad?.exit != false) return@runOnUiThread\n it.toolbar.title = results.title\n ftttime.text = results.datetime_created\n fttintro.text = results.intro\n type = results.type\n }\n }\n }\n }\n }.start()\n }\n}
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt
+--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt (revision 229d5b2088d8ccb7c11e4a5760a6c133ec95b101)
++++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt (date 1709906325403)
+@@ -23,12 +23,11 @@
+ if(ad?.exit == true) return@AutoDownloadThread
+ data?.apply {
+ val r = inputStream().reader()
+- val topic = Gson().fromJson(r, TopicStructure::class.java)
+- topic?.apply {
+- if(ad?.exit != false) return@AutoDownloadThread
++ Gson().fromJson(r, TopicStructure::class.java)?.apply {
++ if(ad?.exit == true) return@AutoDownloadThread
+ activity?.let {
+ it.runOnUiThread {
+- if(ad?.exit != false) return@runOnUiThread
++ if(ad?.exit == true) return@runOnUiThread
+ it.toolbar.title = results.title
+ ftttime.text = results.datetime_created
+ fttintro.text = results.intro
+Index: app/src/main/res/drawable-anydpi/ic_data.xml
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+diff --git a/app/src/main/res/drawable-anydpi/ic_data.xml b/app/src/main/res/drawable-anydpi/ic_data.xml
+new file mode 100644
+--- /dev/null (date 1709889542283)
++++ b/app/src/main/res/drawable-anydpi/ic_data.xml (date 1709889542283)
+@@ -0,0 +1,10 @@
++
++
++
++
+\ No newline at end of file
diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_2024_3_9__12_23_AM__Changes_.xml b/.idea/shelf/Uncommitted_changes_before_Update_at_2024_3_9__12_23_AM__Changes_.xml
new file mode 100644
index 0000000..007eed3
--- /dev/null
+++ b/.idea/shelf/Uncommitted_changes_before_Update_at_2024_3_9__12_23_AM__Changes_.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 289a7dd..4c9f419 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,16 +8,13 @@ android {
applicationId 'top.fumiama.copymanga'
minSdkVersion 23
targetSdkVersion 34
- versionCode 45
- versionName '2.1.2'
- resConfigs 'zh', 'zh-rCN'
+ versionCode 46
+ versionName '2.1.3'
+ resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
- aaptOptions {
- cruncherEnabled = false
- }
buildTypes {
release {
@@ -60,20 +57,20 @@ dependencies {
implementation 'androidx.legacy:legacy-support-v4:1.0.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.6'
- implementation 'androidx.navigation:navigation-ui-ktx:2.7.6'
+ implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7'
+ implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
testImplementation 'junit:junit:4.13.2'
implementation "androidx.preference:preference-ktx:1.2.1"
implementation 'com.afollestad.material-dialogs:input:3.3.0'
implementation 'com.github.yalantis:ucrop:2.2.6'
implementation 'com.to.aboomy:pager2banner:1.0.1'
- implementation 'com.github.bumptech.glide:glide:4.14.2'
- annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
+ implementation 'com.github.bumptech.glide:glide:4.16.0'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.github.vovaksenov99:OverscrollableScrollView:1.0'
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.7.0'
- implementation 'com.airbnb.android:lottie:6.3.0'
+ implementation 'com.airbnb.android:lottie:6.4.0'
}
diff --git a/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt b/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt
index ee29040..8f6cc38 100644
--- a/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt
+++ b/app/src/main/java/top/fumiama/copymanga/LoginActivity.kt
@@ -19,6 +19,7 @@ class LoginActivity : AppCompatActivity() {
val isLogout = pref.getString("token", null) != null
if (isLogout) {
alblogin.setText(R.string.logout)
+ altusrnm.setText(pref.getString("username", "N/A"))
}
alblogin.setOnClickListener {
lifecycleScope.launch {
diff --git a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt
index 7cee5bf..07b4942 100644
--- a/app/src/main/java/top/fumiama/copymanga/MainActivity.kt
+++ b/app/src/main/java/top/fumiama/copymanga/MainActivity.kt
@@ -2,6 +2,7 @@ package top.fumiama.copymanga
import android.Manifest
import android.annotation.SuppressLint
+import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -18,6 +19,7 @@ import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.TextView
import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
@@ -25,6 +27,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.drawerlayout.widget.DrawerLayout
+import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
@@ -40,6 +43,9 @@ import com.yalantis.ucrop.UCrop
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.nav_header_main.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import top.fumiama.copymanga.manga.Shelf
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.copymanga.ui.book.BookFragment.Companion.bookHandler
@@ -64,7 +70,6 @@ class MainActivity : AppCompatActivity() {
private lateinit var headPic: File
lateinit var toolsBox: UITools
- private var latestDestination = 0
private var isMenuWaiting = false
override fun onCreate(savedInstanceState: Bundle?) {
@@ -114,6 +119,7 @@ class MainActivity : AppCompatActivity() {
ime = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ var latestDestination: Int
navController!!.addOnDestinationChangedListener { _, destination, _ ->
latestDestination = destination.id
Log.d("MyMA", "latestDestination: $latestDestination")
@@ -122,51 +128,16 @@ class MainActivity : AppCompatActivity() {
}
isMenuWaiting = true
Log.d("MyMA", "start menu waiting")
- Thread {
- sleep(1000)
- isMenuWaiting = false
- Log.d("MyMA", "finish menu waiting")
- runOnUiThread {
- when (latestDestination) {
- R.id.nav_home -> {
- Log.d("MyMA", "enter home")
- menuMain?.findItem(R.id.action_info)?.isVisible = true
- menuMain?.findItem(R.id.action_download)?.isVisible = false
- menuMain?.findItem(R.id.action_sort)?.isVisible = false
- }
- R.id.nav_book -> {
- Log.d("MyMA", "enter book")
- menuMain?.findItem(R.id.action_info)?.isVisible = false
- menuMain?.findItem(R.id.action_download)?.isVisible = true
- menuMain?.findItem(R.id.action_sort)?.isVisible = false
- }
- R.id.nav_group -> {
- Log.d("MyMA", "enter group")
- menuMain?.findItem(R.id.action_info)?.isVisible = false
- menuMain?.findItem(R.id.action_download)?.isVisible = false
- menuMain?.findItem(R.id.action_sort)?.isVisible = true
- }
- R.id.nav_new_download -> {
- Log.d("MyMA", "enter new_download")
- menuMain?.findItem(R.id.action_info)?.isVisible = false
- menuMain?.findItem(R.id.action_download)?.isVisible = false
- menuMain?.findItem(R.id.action_sort)?.isVisible = true
- }
- R.id.nav_rank -> {
- Log.d("MyMA", "enter rank")
- menuMain?.findItem(R.id.action_info)?.isVisible = false
- menuMain?.findItem(R.id.action_download)?.isVisible = false
- menuMain?.findItem(R.id.action_sort)?.isVisible = true
- }
- else -> {
- Log.d("MyMA", "enter others")
- menuMain?.findItem(R.id.action_info)?.isVisible = false
- menuMain?.findItem(R.id.action_download)?.isVisible = false
- menuMain?.findItem(R.id.action_sort)?.isVisible = false
- }
+ lifecycleScope.launch {
+ withContext(Dispatchers.IO) {
+ sleep(1000)
+ withContext(Dispatchers.Main) {
+ isMenuWaiting = false
+ Log.d("MyMA", "finish menu waiting")
+ changeMenuList(latestDestination)
}
}
- }.start()
+ }
}
}
@@ -218,24 +189,6 @@ class MainActivity : AppCompatActivity() {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
- @Deprecated("Deprecated in Java")
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- if (resultCode == RESULT_OK) when (requestCode) {
- UCrop.REQUEST_CROP -> {
- val fi = headPic.inputStream()
- navhbg.setImageBitmap(BitmapFactory.decodeStream(fi))
- fi.close()
- }
- MSG_CROP_IMAGE -> {
- data?.data?.let {
- saveFile(it)
- cropImageUri()
- }
- }
- }
- }
-
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array,
@@ -263,6 +216,47 @@ class MainActivity : AppCompatActivity() {
}
}
+ private fun changeMenuList(latestDestination: Int) {
+ when (latestDestination) {
+ R.id.nav_home -> {
+ Log.d("MyMA", "enter home")
+ menuMain?.findItem(R.id.action_info)?.isVisible = true
+ menuMain?.findItem(R.id.action_download)?.isVisible = false
+ menuMain?.findItem(R.id.action_sort)?.isVisible = false
+ }
+ R.id.nav_book -> {
+ Log.d("MyMA", "enter book")
+ menuMain?.findItem(R.id.action_info)?.isVisible = false
+ menuMain?.findItem(R.id.action_download)?.isVisible = true
+ menuMain?.findItem(R.id.action_sort)?.isVisible = false
+ }
+ R.id.nav_group -> {
+ Log.d("MyMA", "enter group")
+ menuMain?.findItem(R.id.action_info)?.isVisible = false
+ menuMain?.findItem(R.id.action_download)?.isVisible = false
+ menuMain?.findItem(R.id.action_sort)?.isVisible = true
+ }
+ R.id.nav_new_download -> {
+ Log.d("MyMA", "enter new_download")
+ menuMain?.findItem(R.id.action_info)?.isVisible = false
+ menuMain?.findItem(R.id.action_download)?.isVisible = false
+ menuMain?.findItem(R.id.action_sort)?.isVisible = true
+ }
+ R.id.nav_rank -> {
+ Log.d("MyMA", "enter rank")
+ menuMain?.findItem(R.id.action_info)?.isVisible = false
+ menuMain?.findItem(R.id.action_download)?.isVisible = false
+ menuMain?.findItem(R.id.action_sort)?.isVisible = true
+ }
+ else -> {
+ Log.d("MyMA", "enter others")
+ menuMain?.findItem(R.id.action_info)?.isVisible = false
+ menuMain?.findItem(R.id.action_download)?.isVisible = false
+ menuMain?.findItem(R.id.action_sort)?.isVisible = false
+ }
+ }
+ }
+
private fun checkReadPermission(): Boolean {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N && ContextCompat.checkSelfPermission(
this,
@@ -277,11 +271,18 @@ class MainActivity : AppCompatActivity() {
} else true
}
+ private var pickerLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (result.resultCode == Activity.RESULT_OK) result.data?.data?.let {
+ saveFile(it)
+ cropImageUri()
+ } else Toast.makeText(this, R.string.err_pick_img, Toast.LENGTH_SHORT).show()
+ }
+
@SuppressLint("IntentReset")
private fun pickPicture() {
val i = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
i.type = "image/*"
- startActivityForResult(i, MSG_CROP_IMAGE)
+ pickerLauncher.launch(i)
}
private fun saveFile(uri: Uri) {
@@ -302,11 +303,18 @@ class MainActivity : AppCompatActivity() {
if (headPic.exists()) navhbg.setImageURI(headPic.toUri())
}
+ private var cropLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (result.resultCode == Activity.RESULT_OK) {
+ val fi = headPic.inputStream()
+ navhbg.setImageBitmap(BitmapFactory.decodeStream(fi))
+ fi.close()
+ } else Toast.makeText(this, R.string.err_crop_img, Toast.LENGTH_SHORT).show()
+ }
+
private fun cropImageUri() {
val op = UCrop.Options()
val r = navhbg.width.toFloat() / navhbg.height.toFloat()
Log.d("MyMain", "Img info: (${navhbg.width}, ${navhbg.height})")
- Log.d("MyMain", "Result code: ${UCrop.REQUEST_CROP}")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
op.setCompressionFormat(Bitmap.CompressFormat.WEBP_LOSSY)
} else {
@@ -315,17 +323,17 @@ class MainActivity : AppCompatActivity() {
op.setStatusBarColor(resources.getColor(R.color.colorPrimaryDark, theme))
op.setToolbarColor(resources.getColor(R.color.colorPrimary, theme))
op.setActiveControlsWidgetColor(resources.getColor(R.color.colorAccent, theme))
- UCrop.of(headPic.toUri(), headPic.toUri())
+ cropLauncher.launch(UCrop.of(headPic.toUri(), headPic.toUri())
.withAspectRatio(r, 1F)
.withMaxResultSize(navhbg.width, navhbg.height)
.withOptions(op)
- .start(this)
+ .getIntent(this))
}
private fun checkUpdate(ignoreSkip: Boolean) {
- Thread{
- Update.checkUpdate(this, toolsBox, ignoreSkip)
- }.start()
+ lifecycleScope.launch {
+ Update.checkUpdate(this@MainActivity, toolsBox, ignoreSkip)
+ }
}
private fun showAbout() {
diff --git a/app/src/main/java/top/fumiama/copymanga/json/ComicStructure.java b/app/src/main/java/top/fumiama/copymanga/json/ComicStructure.java
index a0cd262..db97b86 100644
--- a/app/src/main/java/top/fumiama/copymanga/json/ComicStructure.java
+++ b/app/src/main/java/top/fumiama/copymanga/json/ComicStructure.java
@@ -3,12 +3,13 @@ package top.fumiama.copymanga.json;
public class ComicStructure {
public String uuid;
public String name;
- public String alias;
+ //public String alias;
+ public int img_type;
+ public ValueDisplayPair region;
public ValueDisplayPair status;
public ThemeStructure[] theme;
public String path_word;
public ThemeStructure[] author;
- public int img_type;
public String brief;
public String datetime_updated;
public String cover;
diff --git a/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt b/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt
index 818278e..8974803 100644
--- a/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt
+++ b/app/src/main/java/top/fumiama/copymanga/manga/Shelf.kt
@@ -1,6 +1,8 @@
package top.fumiama.copymanga.manga
import com.google.gson.Gson
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
import top.fumiama.copymanga.json.BookQueryStructure
import top.fumiama.copymanga.json.ReturnBase
import top.fumiama.copymanga.tools.http.DownloadTools
@@ -12,9 +14,9 @@ class Shelf(private val token: String, getString: (Int) -> String) {
private val queryApiUrl = getString(R.string.bookUserQueryApiUrl)
private val referer: String = getString(R.string.referer)
private val ua: String = getString(R.string.pc_ua)
- fun add(comicId: String): String {
+ suspend fun add(comicId: String): String = withContext(Dispatchers.IO) {
if (comicId.isEmpty()) {
- return "空漫画ID"
+ return@withContext "空漫画ID"
}
val body = buildString {
append("comic_id=")
@@ -25,13 +27,13 @@ class Shelf(private val token: String, getString: (Int) -> String) {
}
val re = DownloadTools.requestWithBody(
"$apiUrl?platform=3", "POST", body.encodeToByteArray(), referer, ua
- )?.decodeToString() ?: return "空回应"
- return Gson().fromJson(re, ReturnBase::class.java).message
+ )?.decodeToString() ?: return@withContext "空回应"
+ return@withContext Gson().fromJson(re, ReturnBase::class.java).message
}
- fun del(vararg bookIds: Int): String {
+ suspend fun del(vararg bookIds: Int): String = withContext(Dispatchers.IO) {
if (bookIds.isEmpty()) {
- return "空ID列表"
+ return@withContext "空ID列表"
}
val body = buildString {
bookIds.forEach {
@@ -44,16 +46,13 @@ class Shelf(private val token: String, getString: (Int) -> String) {
}
val re = DownloadTools.requestWithBody(
"${apiUrl}s?platform=3", "DELETE", body.encodeToByteArray(), referer, ua
- )?.decodeToString() ?: return "空回应"
- return Gson().fromJson(re, ReturnBase::class.java).message
+ )?.decodeToString() ?: return@withContext "空回应"
+ return@withContext Gson().fromJson(re, ReturnBase::class.java).message
}
- fun query(pathWord: String): BookQueryStructure {
- DownloadTools.getHttpContent(queryApiUrl.format(hostUrl, pathWord), referer, ua)?.let {
- return Gson().fromJson(it.decodeToString(), BookQueryStructure::class.java)
+ suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
+ return@withContext DownloadTools.getHttpContent(queryApiUrl.format(hostUrl, pathWord), referer, ua)?.let {
+ Gson().fromJson(it.decodeToString(), BookQueryStructure::class.java)
}
- val b = BookQueryStructure()
- b.code = 400
- return b
}
}
diff --git a/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt b/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt
index 811b934..7f9b742 100644
--- a/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt
+++ b/app/src/main/java/top/fumiama/copymanga/template/ui/CardList.kt
@@ -7,7 +7,6 @@ import android.view.View
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
-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.*
@@ -36,6 +35,7 @@ class CardList(
rows = arrayOfNulls(20)
index = 0
count = 0
+ cardLoadingWaits.set(0)
exitCardList = false
}
diff --git a/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt b/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt
index 286d0b3..1d8c55e 100644
--- a/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt
+++ b/app/src/main/java/top/fumiama/copymanga/template/ui/InfoCardLoader.kt
@@ -7,7 +7,6 @@ import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.google.gson.Gson
import kotlinx.android.synthetic.main.line_lazybooklines.*
-import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.json.BookListStructure
import top.fumiama.copymanga.json.HistoryBookListStructure
import top.fumiama.copymanga.json.ShelfStructure
@@ -25,8 +24,8 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
override fun addPage(){
super.addPage()
- ad = AutoDownloadThread(subUrl) {
- if (it == null) {
+ ad = AutoDownloadThread(subUrl) { data ->
+ if (data == null) {
activity?.runOnUiThread {
findNavController().popBackStack()
}
@@ -37,7 +36,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
isRefresh = false
}
if(isTypeBook) {
- val bookList = Gson().fromJson(it?.decodeToString(), TypeBookListStructure::class.java)
+ val bookList = Gson().fromJson(data.decodeToString(), TypeBookListStructure::class.java)
bookList?.apply {
Log.d("MyICL", "offset:${results.offset}, total:${results.total}")
if(results.offset < results.total) {
@@ -56,7 +55,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
page++
}
} else if(isHistoryBook) {
- val bookList = Gson().fromJson(it?.decodeToString(), HistoryBookListStructure::class.java)
+ val bookList = Gson().fromJson(data.decodeToString(), HistoryBookListStructure::class.java)
bookList?.apply {
Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
if(results.offset < results.total) {
@@ -75,7 +74,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
page++
}
} else if (isShelfBook) {
- val bookList = Gson().fromJson(it?.decodeToString(), ShelfStructure::class.java)
+ val bookList = Gson().fromJson(data.decodeToString(), ShelfStructure::class.java)
bookList?.apply {
Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
if(results.offset < results.total) {
@@ -83,7 +82,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
results?.list?.forEach{ book ->
if(ad?.exit == true) return@AutoDownloadThread
cardList?.addCard(
- book?.comic?.name?:"null", "\n读到${book?.last_browse?.last_browse_name}", book?.comic?.cover,
+ book?.comic?.name?:"null", "\n${book?.last_browse?.last_browse_name?.let { "读到$it" }?:"未读"}", book?.comic?.cover,
book?.comic?.path_word, null, null,
book?.comic?.status==1,
book.comic?.browse?.chapter_uuid != book.comic?.last_chapter_id
@@ -95,7 +94,7 @@ open class InfoCardLoader(inflateRes:Int, private val navId:Int, private val isT
page++
}
} else {
- val bookList = Gson().fromJson(it?.decodeToString(), BookListStructure::class.java)
+ val bookList = Gson().fromJson(data.decodeToString(), BookListStructure::class.java)
bookList?.apply {
Log.d("MyICL", "offset:${results?.offset}, total:${results?.total}")
if(results.offset < results.total) {
diff --git a/app/src/main/java/top/fumiama/copymanga/template/ui/StatusCardFlow.kt b/app/src/main/java/top/fumiama/copymanga/template/ui/StatusCardFlow.kt
index f66b858..16f78bc 100644
--- a/app/src/main/java/top/fumiama/copymanga/template/ui/StatusCardFlow.kt
+++ b/app/src/main/java/top/fumiama/copymanga/template/ui/StatusCardFlow.kt
@@ -3,15 +3,18 @@ package top.fumiama.copymanga.template.ui
import android.animation.ObjectAnimator
import android.view.View
import kotlinx.android.synthetic.main.anchor_popular.view.*
-import kotlinx.android.synthetic.main.line_finish.*
-import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.tools.api.CMApi
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")
+open class StatusCardFlow(private val api: Int, nav: Int, inflateRes: Int,
+ isTypeBook: Boolean = false,
+ isHistoryBook: Boolean = false,
+ isShelfBook: Boolean = false) : InfoCardLoader(inflateRes, nav, isTypeBook, isHistoryBook, isShelfBook) {
+ val sortWay = listOf("-datetime_updated", "datetime_updated", "-popular", "popular")
var sortValue = 0
+ var lineUpdate: View? = null
+ var lineHot: View? = null
override fun getApiUrl() =
getString(api).format(
@@ -22,21 +25,17 @@ open class StatusCardFlow(private val api: Int, nav: Int) : InfoCardLoader(R.lay
override fun setListeners() {
super.setListeners()
- setUpdate(line_finish_time)
- setHot(line_finish_pop)
+ lineUpdate?.let { setUpdate(it) }
+ lineHot?.let { setHot(it) }
+ lineUpdate?.alpha = 1f
+ lineHot?.alpha = 0.5f
}
open fun setUpdate(that: View) {
that.apply {
apt.setText(R.string.menu_update_time)
setOnClickListener {
- sortValue = if(apim.rotation == 0f) {
- ObjectAnimator.ofFloat(apim, "rotation", 0f, 180f).setDuration(233).start()
- 1
- }else{
- ObjectAnimator.ofFloat(apim, "rotation", 180f, 0f).setDuration(233).start()
- 0
- }
+ sortValue = triggerLine(false)
Thread{
Thread.sleep(400)
activity?.runOnUiThread {
@@ -52,13 +51,7 @@ open class StatusCardFlow(private val api: Int, nav: Int) : InfoCardLoader(R.lay
that.apply {
apt.setText(R.string.menu_hot)
setOnClickListener {
- sortValue = if (apim.rotation == 0f) {
- ObjectAnimator.ofFloat(apim, "rotation", 0f, 180f).setDuration(233).start()
- 3
- } else {
- ObjectAnimator.ofFloat(apim, "rotation", 180f, 0f).setDuration(233).start()
- 2
- }
+ sortValue = triggerLine(true)
Thread {
Thread.sleep(400)
activity?.runOnUiThread {
@@ -69,4 +62,46 @@ open class StatusCardFlow(private val api: Int, nav: Int) : InfoCardLoader(R.lay
}
}
}
+
+ open fun triggerLine(isHot: Boolean): Int {
+ val hot = lineHot?:return 0
+ val update = lineUpdate?:return 0
+ if(sortValue >= 2) {
+ if (isHot) {
+ return if (hot.apim.rotation == 0f) {
+ ObjectAnimator.ofFloat(hot.apim, "rotation", 0f, 180f).setDuration(233).start()
+ 3
+ } else {
+ ObjectAnimator.ofFloat(hot.apim, "rotation", 180f, 0f).setDuration(233).start()
+ 2
+ }
+ } else {
+ update.alpha = 1f
+ hot.alpha = 0.5f
+ return if(update.apim.rotation == 0f) {
+ 0
+ } else {
+ 1
+ }
+ }
+ } else {
+ if (!isHot) {
+ return if(update.apim.rotation == 0f) {
+ ObjectAnimator.ofFloat(update.apim, "rotation", 0f, 180f).setDuration(233).start()
+ 1
+ }else{
+ ObjectAnimator.ofFloat(update.apim, "rotation", 180f, 0f).setDuration(233).start()
+ 0
+ }
+ } else {
+ hot.alpha = 1f
+ update.alpha = 0.5f
+ return if (hot.apim.rotation == 0f) {
+ 2
+ } else {
+ 3
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/top/fumiama/copymanga/template/ui/ThemeCardFlow.kt b/app/src/main/java/top/fumiama/copymanga/template/ui/ThemeCardFlow.kt
index 0d91cca..e26a2f5 100644
--- a/app/src/main/java/top/fumiama/copymanga/template/ui/ThemeCardFlow.kt
+++ b/app/src/main/java/top/fumiama/copymanga/template/ui/ThemeCardFlow.kt
@@ -1,12 +1,15 @@
package top.fumiama.copymanga.template.ui
import android.os.Bundle
+import android.view.View
import kotlinx.android.synthetic.main.app_bar_main.*
+import kotlinx.android.synthetic.main.line_finish.*
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.tools.api.CMApi
+import top.fumiama.dmzj.copymanga.R
@ExperimentalStdlibApi
-open class ThemeCardFlow(private val api: Int, nav: Int) : StatusCardFlow(0, nav) {
+open class ThemeCardFlow(private val api: Int, nav: Int) : StatusCardFlow(0, nav, R.layout.fragment_statuscardflow) {
private var theme = ""
override fun getApiUrl() =
getString(api).format(
@@ -26,6 +29,12 @@ open class ThemeCardFlow(private val api: Int, nav: Int) : StatusCardFlow(0, nav
}
}
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ lineUpdate = line_finish_time
+ lineHot = line_finish_pop
+ }
+
override fun onResume() {
super.onResume()
arguments?.getString("name")?.apply {
diff --git a/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt b/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt
index fb41924..904e2ab 100644
--- a/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt
+++ b/app/src/main/java/top/fumiama/copymanga/tools/api/CMApi.kt
@@ -13,7 +13,7 @@ import java.net.URLEncoder
object CMApi {
var proxy = if(Proxy.useImageProxy) Proxy(R.string.imgProxyApiUrl, R.string.imgProxyApiPrefix, R.string.imgProxyKeyID) else null
- var resolution = Resolution(Regex("\\.c\\d+x\\."))
+ var resolution = Resolution(Regex("c\\d+x\\."))
var myGlideHeaders: LazyHeaders? = null
get() {
MainActivity.mainWeakReference?.get()?.let {
diff --git a/app/src/main/java/top/fumiama/copymanga/tools/http/Resolution.kt b/app/src/main/java/top/fumiama/copymanga/tools/http/Resolution.kt
index 3bfd27c..ed9a82d 100644
--- a/app/src/main/java/top/fumiama/copymanga/tools/http/Resolution.kt
+++ b/app/src/main/java/top/fumiama/copymanga/tools/http/Resolution.kt
@@ -16,5 +16,5 @@ class Resolution(private val original: Regex) {
}
return 1500
}
- fun wrap(u: String) : String = u.replace(original, ".c${imageResolution}x.")
+ fun wrap(u: String) : String = u.replace(original, "c${imageResolution}x.")
}
diff --git a/app/src/main/java/top/fumiama/copymanga/tools/ui/GlideHideLottieViewListener.kt b/app/src/main/java/top/fumiama/copymanga/tools/ui/GlideHideLottieViewListener.kt
index 647eaad..33b9c9d 100644
--- a/app/src/main/java/top/fumiama/copymanga/tools/ui/GlideHideLottieViewListener.kt
+++ b/app/src/main/java/top/fumiama/copymanga/tools/ui/GlideHideLottieViewListener.kt
@@ -13,17 +13,17 @@ class GlideHideLottieViewListener(private val wla: WeakReference?,
+ target: Target,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
- resource: Drawable?,
- model: Any?,
+ resource: Drawable,
+ model: Any,
target: Target?,
- dataSource: DataSource?,
+ dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
wla.get()?.apply {
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt
index d10b449..b73fa00 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/book/BookFragment.kt
@@ -6,14 +6,18 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
+import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.gson.Gson
import kotlinx.android.synthetic.main.app_bar_main.*
+import kotlinx.android.synthetic.main.card_book.*
import kotlinx.android.synthetic.main.fragment_book.*
import kotlinx.android.synthetic.main.line_bookinfo_text.*
import kotlinx.android.synthetic.main.line_booktandb.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
-import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.template.general.NoBackRefreshFragment
@@ -32,14 +36,14 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
super.onViewCreated(view, savedInstanceState)
ComicDlFragment.exit = false
- fbl?.setPadding(0, 0, 0, navBarHeight)
+ fbvp?.setPadding(0, 0, 0, navBarHeight)
if(isFirstInflate) {
var path = ""
arguments?.apply {
if (getBoolean("loadJson")) {
getString("name")?.let { name ->
- mainWeakReference?.get()?.getExternalFilesDir("")?.let {
+ activity?.getExternalFilesDir("")?.let {
Gson().fromJson(File(File(it, name), "info.json").readText(), Array::class.java)
}?.apply {
if (isEmpty() || get(0).results.list.isEmpty()) {
@@ -62,10 +66,12 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
mBookHandler = BookHandler(WeakReference(this), path)
Log.d("MyBF", "read path: $path")
bookHandler = mBookHandler
- Thread {
- sleep(600)
- mBookHandler?.startLoad()
- }.start()
+ lifecycleScope.launch {
+ withContext(Dispatchers.IO) {
+ sleep(600)
+ mBookHandler?.startLoad()
+ }
+ }
} else {
bookHandler = mBookHandler
}
@@ -75,7 +81,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
super.onResume()
isOnPause = false
bookHandler = mBookHandler
- mainWeakReference?.get()?.apply {
+ activity?.apply {
toolbar.title = mBookHandler?.book?.results?.comic?.name
}
setStartRead()
@@ -96,7 +102,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
fun setStartRead() {
- if(mBookHandler?.chapterNames?.isNotEmpty() == true) mainWeakReference?.get()?.apply {
+ if(mBookHandler?.chapterNames?.isNotEmpty() == true) activity?.apply {
mBookHandler?.book?.results?.comic?.let { comic ->
getPreferences(MODE_PRIVATE).getInt(comic.name, -1).let { p ->
this@BookFragment.lbbstart.apply {
@@ -118,12 +124,13 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
@SuppressLint("SetTextI18n")
fun setAddToShelf() {
- if(mBookHandler?.chapterNames?.isNotEmpty() == true) {
+ if(mBookHandler?.chapterNames?.isNotEmpty() != true) return
+ lifecycleScope.launch {
val b = MainActivity.shelf?.query(mBookHandler?.path!!)
mBookHandler?.collect = b?.results?.collect?:-2
Log.d("MyBF", "get collect of ${mBookHandler?.path} = ${mBookHandler?.collect}")
- b?.results?.browse?.chapter_name?.let { name ->
- btsub.text = "${btsub.text} ${getString(R.string.text_format_cloud_read_to).format(name)}"
+ tic.text = b?.results?.browse?.chapter_name?.let { name ->
+ getString(R.string.text_format_cloud_read_to).format(name)
}
mBookHandler?.collect?.let { collect ->
if (collect > 0) {
@@ -132,30 +139,24 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
}
mBookHandler?.book?.results?.comic?.let { comic ->
this@BookFragment.lbbsub.setOnClickListener {
- if (this@BookFragment.lbbsub.text != getString(R.string.button_sub)) {
- mBookHandler?.collect?.let { collect ->
- if (collect < 0) return@setOnClickListener
- Thread{
+ lifecycleScope.launch clickLaunch@ {
+ if (this@BookFragment.lbbsub.text != getString(R.string.button_sub)) {
+ mBookHandler?.collect?.let { collect ->
+ if (collect < 0) return@clickLaunch
val re = MainActivity.shelf?.del(collect)
- activity?.runOnUiThread {
- Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
- if (re == "请求成功") {
- this@BookFragment.lbbsub.setText(R.string.button_sub)
- }
+ Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
+ if (re == "请求成功") {
+ this@BookFragment.lbbsub.setText(R.string.button_sub)
}
- }.start()
- }
- return@setOnClickListener
- }
- Thread{
- val re = MainActivity.shelf?.add(comic.uuid)
- activity?.runOnUiThread {
- Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
- if (re == "修改成功") {
- this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)
}
+ return@clickLaunch
}
- }.start()
+ val re = MainActivity.shelf?.add(comic.uuid)
+ Toast.makeText(context, re, Toast.LENGTH_SHORT).show()
+ if (re == "修改成功") {
+ this@BookFragment.lbbsub.setText(R.string.button_sub_subscribed)
+ }
+ }
}
}
}
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/book/BookHandler.kt b/app/src/main/java/top/fumiama/copymanga/ui/book/BookHandler.kt
index 80bddef..5317835 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/book/BookHandler.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/book/BookHandler.kt
@@ -6,13 +6,18 @@ import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.core.graphics.drawable.toBitmap
+import androidx.core.widget.NestedScrollView
import androidx.navigation.fragment.findNavController
+import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.RequestOptions
+import com.google.android.material.tabs.TabLayoutMediator
import com.google.gson.Gson
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.card_book.*
@@ -22,6 +27,7 @@ import kotlinx.android.synthetic.main.line_bookinfo.*
import kotlinx.android.synthetic.main.line_bookinfo_text.*
import kotlinx.android.synthetic.main.line_caption.view.*
import kotlinx.android.synthetic.main.line_chapter.view.*
+import kotlinx.android.synthetic.main.page_nested_list.view.*
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.BookInfoStructure
import top.fumiama.copymanga.json.ChapterStructure
@@ -62,7 +68,7 @@ class BookHandler(private val th: WeakReference, val path: String)
var vols: Array? = null
var chapterNames = arrayOf()
var collect: Int = -1
- private val divider get() = that?.layoutInflater?.inflate(R.layout.div_h, that?.fbl, false)
+ private val divider get() = that?.layoutInflater?.inflate(R.layout.div_h, that?.lbl, false)
var urlArray = arrayOf()
@@ -73,10 +79,7 @@ class BookHandler(private val th: WeakReference, val path: String)
1 -> setCover()
2 -> setTexts()
3 -> setAuthorsAndTags()
- //4 -> setOverScale()
6 -> if(complete) that?.navigate2dl()
- 7 -> setVolumes()
- 8 -> that?.apply { fbl?.addView(msg.obj as View) }
9 -> endSetLayouts()
}
}
@@ -127,11 +130,6 @@ class BookHandler(private val th: WeakReference, val path: String)
Log.d("MyBH", "Set complete: true")
}
- /*private fun setOverScale() {
- if (exit) return
- that?.fbov?.setScaleView(that!!.fbapp)
- }*/
-
private fun setCover() {
if (exit) return
that?.apply {
@@ -144,63 +142,69 @@ class BookHandler(private val th: WeakReference, val path: String)
?.let { it2 -> RequestOptions.bitmapTransform(it2) }
?.let { it3 -> load.apply(it3).into(lbibg) }
}
- imf?.visibility = View.GONE
+ //imf?.visibility = View.GONE
//fbl?.addView(divider)
}
}
- private fun getThemeSeq(authors: Array): CharSequence{
+ /*private fun getThemeSeq(authors: Array): CharSequence{
var re = ""
for(author in authors) re += author.name + ' '
return re
- }
+ }*/
- private fun setTexts(){
+ private fun setTexts() {
if (exit) return
//that?.tic?.text = book?.name
- that?.tic?.visibility = View.GONE
+ //that?.tic?.visibility = View.GONE
mainWeakReference?.get()?.toolbar?.title = book?.results?.comic?.name
- that?.btauth?.text = book?.results?.comic?.author?.let { getThemeSeq(it) }
- that?.bttag?.text = book?.results?.comic?.theme?.let { getThemeSeq(it) }
- that?.bthit?.text = that?.getString(R.string.text_format_hit)?.let { String.format(
- it,
+ that?.btauth?.text = that?.getString(R.string.text_format_region)?.format(
+ book?.results?.comic?.region?.display
+ )
+ that?.bttag?.text = that?.getString(R.string.text_format_img_type)?.format(when(book?.results?.comic?.img_type) {
+ 1 -> "条漫"
+ 2 -> "普通"
+ else -> "未知类型${book?.results?.comic?.img_type}"
+ })
+ that?.bthit?.text = that?.getString(R.string.text_format_hit)?.format(
book?.results?.comic?.popular
- ) }?:""
- that?.btsub?.text = that?.getString(R.string.text_format_stat)?.let { String.format(
- it,
+ )
+ that?.btsub?.text = that?.getString(R.string.text_format_stat)?.format(
book?.results?.comic?.status?.display
- ) }?:""
+ )
that?.bttime?.text = book?.results?.comic?.datetime_updated
- val v = that?.layoutInflater?.inflate(R.layout.line_text_info, that?.fbl, false)
+ val v = that?.layoutInflater?.inflate(R.layout.line_text_info, that?.lbl, false)
(v as TextView).text = book?.results?.comic?.brief
- that?.fbl?.addView(v)
- that?.fbl?.addView(divider)
+ that?.lbl?.addView(v)
+ that?.lbl?.addView(divider)
}
private fun setTheme(caption: String, themeStructure: Array, nav: Int) {
that?.apply {
- val t = layoutInflater.inflate(R.layout.line_caption, fbl, false)
+ val t = layoutInflater.inflate(R.layout.line_caption, lbl, false)
t.tcptn.text = caption
- fbl.addView(t)
- fbl.addView(layoutInflater.inflate(R.layout.div_h, fbl, false))
+ lbl.addView(t)
+ lbl.addView(layoutInflater.inflate(R.layout.div_h, lbl, false))
}
var line: View? = null
val last = themeStructure.size - 1
themeStructure.onEachIndexed { i, it ->
if(line == null) {
if(i == last) {
- line = that?.layoutInflater?.inflate(R.layout.line_chapter, that!!.fbl, false)
+ line = that?.layoutInflater?.inflate(R.layout.line_chapter, that!!.lbl, false)
line?.lcc?.apply {
lct.text = it.name
+ lci.setBackgroundResource(R.drawable.ic_list)
setOnClickListener { _ ->
loadVolume(it.name, it.path_word, nav)
}
}
- that?.fbl?.addView(line)
+ that?.lbl?.addView(line)
} else {
- line = that?.layoutInflater?.inflate(R.layout.line_2chapters, that!!.fbl, false)
+ line = that?.layoutInflater?.inflate(R.layout.line_2chapters, that!!.lbl, false)
line?.l2cl?.apply {
lct.text = it.name
+ lci.setBackgroundResource(R.drawable.ic_list)
setOnClickListener { _ ->
loadVolume(it.name, it.path_word, nav)
}
@@ -208,10 +212,11 @@ class BookHandler(private val th: WeakReference, val path: String)
}
} else line?.l2cr?.apply {
lct.text = it.name
+ lci.setBackgroundResource(R.drawable.ic_list)
setOnClickListener { _ ->
loadVolume(it.name, it.path_word, nav)
}
- that?.fbl?.addView(line)
+ that?.lbl?.addView(line)
line = null
}
}
@@ -228,7 +233,7 @@ class BookHandler(private val th: WeakReference, val path: String)
R.id.action_nav_book_to_nav_author
)
}
- fbl.addView(layoutInflater.inflate(R.layout.div_h, fbl, false))
+ lbl.addView(layoutInflater.inflate(R.layout.div_h, lbl, false))
theme?.let {
setTheme(
getString(R.string.caption),
@@ -240,11 +245,68 @@ class BookHandler(private val th: WeakReference, val path: String)
}
}
- private fun addVolumesView(v: View) {
- obtainMessage(8, v).sendToTarget()
+ private fun addVolumesView(l: LinearLayout, v: View) {
+ that?.activity?.runOnUiThread {
+ l.addView(v)
+ }
}
- private fun setVolumes() = Thread {
+ private fun setVolume(fbl: LinearLayout, p: Int) = Thread {
+ if (exit) return@Thread
+ that?.apply {
+ book?.results?.apply {
+ var i = 0
+ for (j in 0 until p) {
+ i += vols?.get(j)?.results?.list?.size?:0
+ }
+ var last = i-1
+ vols?.get(p)?.let { v ->
+ if(exit) return@Thread
+ var line: View? = null
+ last += v.results.list.size
+ v.results.list.forEach {
+ val f = CMApi.getZipFile(context?.getExternalFilesDir(""), comic.name, keys[p], it.name)
+ 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, fbl, false)
+ line?.lcc?.apply {
+ lct.text = it.name
+ if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
+ Log.d("MyBH", "add last single chapter ${it.name}")
+ val index = i
+ setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) }
+ }
+ line?.let { l -> addVolumesView(fbl, l) }
+ } else {
+ line = layoutInflater.inflate(R.layout.line_2chapters, fbl, false)
+ line?.l2cl?.apply {
+ lct.text = it.name
+ if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
+ val index = i
+ setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) }
+ }
+ }
+ } else line?.l2cr?.apply {
+ lct.text = it.name
+ if (f.exists()) lci.setBackgroundResource(R.drawable.ic_success)
+ val index = i
+ setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) }
+ line?.let { l -> addVolumesView(fbl, l) }
+ line = null
+ }
+ i++
+ }
+ }
+ }
+ }
+ }.start()
+
+ private fun setViewManga() = Thread {
if (exit) return@Thread
that?.apply {
book?.results?.apply {
@@ -255,58 +317,26 @@ class BookHandler(private val th: WeakReference, val path: String)
var last = -1
vols?.forEachIndexed { groupIndex, v ->
if(exit) return@Thread
- addVolumesView(layoutInflater.inflate(R.layout.div_h, fbl, false))
- val t = layoutInflater.inflate(R.layout.line_caption, fbl, false)
- t.tcptn.text = keys[groupIndex]
- addVolumesView(t)
- addVolumesView(layoutInflater.inflate(R.layout.div_h, fbl, false))
- var line: View? = null
last += v.results.list.size
v.results.list.forEach {
urlArray += CMApi.getChapterInfoApiUrl(
comic.path_word,
it.uuid
)?:""
- ViewMangaActivity.fileArray += CMApi.getZipFile(context?.getExternalFilesDir(""), comic.name, keys[groupIndex], it.name)
+ val f = CMApi.getZipFile(context?.getExternalFilesDir(""), comic.name, keys[groupIndex], it.name)
+ ViewMangaActivity.fileArray += f
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)
- line?.lcc?.apply {
- lct.text = it.name
- Log.d("MyBH", "add last single chapter ${it.name}")
- val index = i
- setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) }
- }
- line?.let { l -> addVolumesView(l) }
-
- } else {
- line = layoutInflater.inflate(R.layout.line_2chapters, that!!.fbl, false)
- line?.l2cl?.apply {
- lct.text = it.name
- val index = i
- setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) }
- }
- }
- } else line?.l2cr?.apply {
- lct.text = it.name
- val index = i
- setOnClickListener { Reader.viewMangaAt(comic.name, index, urlArray) }
- line?.let { l -> addVolumesView(l) }
- line = null
- }
i++
}
}
- sendEmptyMessage(9) // end set layout
}
}
+ sendEmptyMessage(9) // end set layout
}.start()
private fun loadVolume(name: String, path: String, nav: Int){
@@ -375,10 +405,18 @@ class BookHandler(private val th: WeakReference, val path: String)
c++
}
if (volumes.size == gpws.size) {
- that?.activity?.runOnUiThread {
- saveVolumes(volumes)
- sendEmptyMessage(7)
+ saveVolumes(volumes)
+ that?.fbtab?.let { tab ->
+ that?.fbvp?.let { vp ->
+ that?.activity?.runOnUiThread {
+ vp.adapter = ViewData(vp).RecyclerViewAdapter()
+ TabLayoutMediator(tab, vp) { t, p ->
+ t.text = keys[p]
+ }.attach()
+ }
+ }
}
+ setViewManga()
}
}.start()
@@ -417,4 +455,18 @@ class BookHandler(private val th: WeakReference, val path: String)
}
vols = volumes
}
+
+ inner class ViewData(itemView: View): RecyclerView.ViewHolder(itemView) {
+ inner class RecyclerViewAdapter: RecyclerView.Adapter() {
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewData {
+ return ViewData(that?.layoutInflater?.inflate(R.layout.page_nested_list, parent, false) as NestedScrollView)
+ }
+
+ override fun onBindViewHolder(holder: ViewData, position: Int) {
+ setVolume(holder.itemView.fbl, position)
+ }
+
+ override fun getItemCount(): Int = keys.size
+ }
+ }
}
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/finish/FinishFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/finish/FinishFragment.kt
index 1b77baf..a7e58d9 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/finish/FinishFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/finish/FinishFragment.kt
@@ -1,7 +1,17 @@
package top.fumiama.copymanga.ui.cardflow.finish
+import android.os.Bundle
+import android.view.View
import top.fumiama.copymanga.template.ui.StatusCardFlow
import top.fumiama.dmzj.copymanga.R
+import kotlinx.android.synthetic.main.line_finish.*
@ExperimentalStdlibApi
-class FinishFragment : StatusCardFlow(R.string.finishApiUrl, R.id.action_nav_finish_to_nav_book)
\ No newline at end of file
+class FinishFragment : StatusCardFlow(
+ R.string.finishApiUrl, R.id.action_nav_finish_to_nav_book, R.layout.fragment_statuscardflow) {
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ lineUpdate = line_finish_time
+ lineHot = line_finish_pop
+ }
+}
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/newest/NewestFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/newest/NewestFragment.kt
index f87a4e4..f67ecf6 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/newest/NewestFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/newest/NewestFragment.kt
@@ -1,8 +1,5 @@
package top.fumiama.copymanga.ui.cardflow.newest
-import android.view.View
-import kotlinx.android.synthetic.main.line_lazybooklines.*
-import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.template.ui.InfoCardLoader
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.dmzj.copymanga.R
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt
index cac0a6e..cbc24f4 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/rank/RankFragment.kt
@@ -4,7 +4,6 @@ import android.os.Bundle
import com.google.android.material.tabs.TabLayout
import kotlinx.android.synthetic.main.fragment_rank.*
import kotlinx.android.synthetic.main.line_rank.view.*
-import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.template.ui.InfoCardLoader
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.ui.UITools
@@ -13,7 +12,7 @@ import java.lang.Thread.sleep
import java.lang.ref.WeakReference
@ExperimentalStdlibApi
-class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank_to_nav_book, true) {
+class RankFragment : InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank_to_nav_book, isTypeBook = true) {
private val sortWay = listOf("day", "week", "month", "total")
private var sortValue = 0
private val audienceWay = listOf("", "male", "female")
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt
index 078c149..e6820a1 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/shelf/ShelfFragment.kt
@@ -2,9 +2,14 @@ package top.fumiama.copymanga.ui.cardflow.shelf
import android.animation.ObjectAnimator
import android.os.Bundle
+import android.view.View
+import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.anchor_popular.view.*
import kotlinx.android.synthetic.main.line_shelf.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.template.ui.InfoCardLoader
import top.fumiama.copymanga.tools.api.CMApi
@@ -37,6 +42,7 @@ class ShelfFragment : InfoCardLoader(R.layout.fragment_shelf, R.id.action_nav_su
override fun setListeners() {
super.setListeners()
+ fade()
setUpdate()
setModify()
setBrowse()
@@ -46,20 +52,10 @@ class ShelfFragment : InfoCardLoader(R.layout.fragment_shelf, R.id.action_nav_su
if (ad?.exit == true) return
line_shelf_updated.apt.setText(R.string.menu_update_time)
line_shelf_updated.setOnClickListener {
- sortValue = if (it.apim.rotation == 0f) {
- ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
- 1
- } else {
- ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
- 0
- }
- Thread {
- sleep(400)
- activity?.runOnUiThread {
- reset()
- addPage()
- }
- }.start()
+ val same = sortValue in 0..1
+ sortValue = rotate(it.apim, same, 0)
+ if (!same) fade()
+ resetDelayed()
}
}
@@ -67,20 +63,10 @@ class ShelfFragment : InfoCardLoader(R.layout.fragment_shelf, R.id.action_nav_su
if (ad?.exit == true) return
line_shelf_modifier.apt.setText(R.string.menu_add_time)
line_shelf_modifier.setOnClickListener {
- sortValue = if (it.apim.rotation == 0f) {
- ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
- 3
- } else {
- ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
- 2
- }
- Thread {
- sleep(400)
- activity?.runOnUiThread {
- reset()
- addPage()
- }
- }.start()
+ val same = sortValue in 2..3
+ sortValue = rotate(it.apim, same, 2)
+ if (!same) fade()
+ resetDelayed()
}
}
@@ -88,20 +74,60 @@ class ShelfFragment : InfoCardLoader(R.layout.fragment_shelf, R.id.action_nav_su
if (ad?.exit == true) return
line_shelf_browse.apt.setText(R.string.menu_read_time)
line_shelf_browse.setOnClickListener {
- sortValue = if (it.apim.rotation == 0f) {
- ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
- 5
+ val same = sortValue>=4
+ sortValue = rotate(it.apim, same, 4)
+ if (!same) fade()
+ resetDelayed()
+ }
+ }
+
+ private fun rotate(img: View, isSameSlot: Boolean, offset: Int): Int {
+ return if (isSameSlot) {
+ if (img.rotation == 0f) {
+ ObjectAnimator.ofFloat(img, "rotation", 0f, 180f).setDuration(233).start()
+ offset+1
} else {
- ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
- 4
+ ObjectAnimator.ofFloat(img, "rotation", 180f, 0f).setDuration(233).start()
+ offset
}
- Thread {
+ } else {
+ if (img.rotation == 0f) {
+ offset
+ } else {
+ offset+1
+ }
+ }
+ }
+
+ private fun fade() {
+ when(sortValue) {
+ 0, 1 -> {
+ line_shelf_updated.alpha = 1f
+ line_shelf_modifier.alpha = 0.5f
+ line_shelf_browse.alpha = 0.5f
+ }
+ 2, 3 -> {
+ line_shelf_updated.alpha = 0.5f
+ line_shelf_modifier.alpha = 1f
+ line_shelf_browse.alpha = 0.5f
+ }
+ 4, 5 -> {
+ line_shelf_updated.alpha = 0.5f
+ line_shelf_modifier.alpha = 0.5f
+ line_shelf_browse.alpha = 1f
+ }
+ }
+ }
+
+ private fun resetDelayed() {
+ lifecycleScope.launch {
+ withContext(Dispatchers.IO) {
sleep(400)
- activity?.runOnUiThread {
+ withContext(Dispatchers.Main) {
reset()
addPage()
}
- }.start()
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt
index 9adbf95..426346c 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/sort/SortFragment.kt
@@ -1,24 +1,22 @@
package top.fumiama.copymanga.ui.cardflow.sort
-import android.animation.ObjectAnimator
+import android.os.Bundle
+import android.view.View
import com.github.zawadz88.materialpopupmenu.popupMenu
import com.google.gson.Gson
import kotlinx.android.synthetic.main.anchor_popular.view.*
import kotlinx.android.synthetic.main.line_sort.*
-import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.FilterStructure
import top.fumiama.copymanga.template.http.AutoDownloadThread
-import top.fumiama.copymanga.template.ui.InfoCardLoader
+import top.fumiama.copymanga.template.ui.StatusCardFlow
import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.dmzj.copymanga.R
import java.lang.Thread.sleep
@ExperimentalStdlibApi
-class SortFragment : InfoCardLoader(R.layout.fragment_sort, R.id.action_nav_sort_to_nav_book) {
- private val sortWay = listOf("-datetime_updated", "datetime_updated", "-popular", "popular")
+class SortFragment : StatusCardFlow(0, R.id.action_nav_sort_to_nav_book, R.layout.fragment_sort) {
private var theme = -1
private var region = -1
- private var sortValue = 0
private var filter: FilterStructure? = null
override fun getApiUrl() =
@@ -30,10 +28,14 @@ class SortFragment : InfoCardLoader(R.layout.fragment_sort, R.id.action_nav_sort
if(region >= 0) (filter?.results?.top?.get(region)?.path_word ?: "") else "",
)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ lineUpdate = line_sort_time
+ lineHot = line_sort_hot
+ }
+
override fun setListeners() {
super.setListeners()
- setUpdate()
- setHot()
AutoDownloadThread(getString(R.string.filterApiUrl).format(CMApi.myHostApiUrl)) {
if(ad?.exit == true) return@AutoDownloadThread
it?.let {
@@ -46,27 +48,6 @@ class SortFragment : InfoCardLoader(R.layout.fragment_sort, R.id.action_nav_sort
}.start()
}
- private fun setUpdate(){
- if(ad?.exit == true) return
- line_sort_time.apt.setText(R.string.menu_update_time)
- line_sort_time.setOnClickListener {
- sortValue = if(it.apim.rotation == 0f) {
- ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
- 1
- }else{
- ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
- 0
- }
- Thread{
- sleep(400)
- activity?.runOnUiThread {
- reset()
- addPage()
- }
- }.start()
- }
- }
-
private fun setClasses(){
filter?.results?.top?.let { items ->
if(ad?.exit == true) return@let
@@ -153,25 +134,4 @@ class SortFragment : InfoCardLoader(R.layout.fragment_sort, R.id.action_nav_sort
}
}
}
-
- private fun setHot() {
- if(ad?.exit == true) return
- line_sort_hot.apt.setText(R.string.menu_hot)
- line_sort_hot.setOnClickListener {
- sortValue = if (it.apim.rotation == 0f) {
- ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
- 3
- } else {
- ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
- 2
- }
- Thread {
- sleep(400)
- activity?.runOnUiThread {
- reset()
- addPage()
- }
- }.start()
- }
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt
index 86c91ad..0e5bcad 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/cardflow/topic/TopicFragment.kt
@@ -4,7 +4,6 @@ import android.os.Bundle
import com.google.gson.Gson
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.fragment_topic.*
-import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.TopicStructure
import top.fumiama.copymanga.template.http.AutoDownloadThread
import top.fumiama.copymanga.template.ui.InfoCardLoader
@@ -23,12 +22,11 @@ class TopicFragment : InfoCardLoader(R.layout.fragment_topic, R.id.action_nav_to
if(ad?.exit == true) return@AutoDownloadThread
data?.apply {
val r = inputStream().reader()
- val topic = Gson().fromJson(r, TopicStructure::class.java)
- topic?.apply {
- if(ad?.exit != false) return@AutoDownloadThread
+ Gson().fromJson(r, TopicStructure::class.java)?.apply {
+ if(ad?.exit == true) return@AutoDownloadThread
activity?.let {
it.runOnUiThread {
- if(ad?.exit != false) return@runOnUiThread
+ if(ad?.exit == true) return@runOnUiThread
it.toolbar.title = results.title
ftttime.text = results.datetime_created
fttintro.text = results.intro
diff --git a/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt b/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt
index dd4b90f..75ee719 100644
--- a/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt
+++ b/app/src/main/java/top/fumiama/copymanga/ui/download/NewDownloadFragment.kt
@@ -1,25 +1,24 @@
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.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
import kotlinx.android.synthetic.main.line_lazybooklines.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.manga.Reader
import top.fumiama.copymanga.template.general.MangaPagesFragmentTemplate
import top.fumiama.copymanga.template.ui.CardList
+import top.fumiama.copymanga.tools.file.FileUtils
import top.fumiama.copymanga.tools.ui.Navigate
import top.fumiama.copymanga.tools.ui.UITools
-import top.fumiama.copymanga.tools.file.FileUtils
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
import top.fumiama.dmzj.copymanga.R
import java.io.File
@@ -64,49 +63,53 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
page = 0
isRefresh = false
}
- if(!isEnd) {
- if(sortedBookList == null || isContentChanged) {
- Log.d("MyNDF", "Sorting books...")
- sortedBookList = extDir?.listFiles()?.sortedBy {
- return@sortedBy Reader.getComicPathWordInFile(it)
- }
- if (isReverse) {
- Log.d("MyNDF", "reversed...")
- sortedBookList = sortedBookList?.asReversed()
- }
- if (!showAll) {
- sortedBookList = sortedBookList?.filter {
- return@filter FileUtils.sizeOf(it) / 1048576 > 0
+ lifecycleScope.launch {
+ withContext(Dispatchers.IO) {
+ if(!isEnd) {
+ if(sortedBookList == null || isContentChanged) {
+ Log.d("MyNDF", "Sorting books...")
+ sortedBookList = extDir?.listFiles()?.sortedBy {
+ return@sortedBy Reader.getComicPathWordInFile(it)
+ }
+ if (isReverse) {
+ Log.d("MyNDF", "reversed...")
+ sortedBookList = sortedBookList?.asReversed()
+ }
+ if (!showAll) {
+ sortedBookList = sortedBookList?.filter {
+ return@filter FileUtils.sizeOf(it) / 1048576 > 0
+ }
+ }
+ isContentChanged = false
}
- }
- isContentChanged = false
- }
- Log.d("MyNDF", "Start drawing cards")
- cardList?.addCard(oldDlCardName, path = oldDlCardName)
- var cnt = 1
- sortedBookList?.let {
- for(i in it.listIterator(page)) {
- if(cardList?.exitCardList != false) return
- page++ // page is actually count
- val chosenJson = File(i, "info.bin")
- val newJson = File(i, "info.json")
- val bookSize = (FileUtils.sizeOf(i)/1048576).toInt()
- when {
- chosenJson.exists() -> continue // unsupported old folder
- newJson.exists() -> {
- if(cardList?.exitCardList != false) return
- cardList?.addCard(i.name, "\n${bookSize}MB")
- cnt++
+ Log.d("MyNDF", "Start drawing cards")
+ cardList?.addCard(oldDlCardName, path = oldDlCardName)
+ var cnt = 1
+ sortedBookList?.let {
+ for(i in it.listIterator(page)) {
+ if(cardList?.exitCardList != false) return@withContext
+ page++ // page is actually count
+ val chosenJson = File(i, "info.bin")
+ val newJson = File(i, "info.json")
+ val bookSize = (FileUtils.sizeOf(i)/1048576).toInt()
+ when {
+ chosenJson.exists() -> continue // unsupported old folder
+ newJson.exists() -> {
+ if(cardList?.exitCardList != false) return@withContext
+ cardList?.addCard(i.name, "\n${bookSize}MB")
+ cnt++
+ }
+ }
+ if (cnt >= 21) break
+ }
+ if(page >= it.size) {
+ isEnd = true
}
}
- if (cnt >= 21) break
- }
- if(page >= it.size) {
- isEnd = true
}
+ onLoadFinish()
}
}
- onLoadFinish()
}
override fun initCardList(weakReference: WeakReference) {
@@ -136,12 +139,14 @@ class NewDownloadFragment: MangaPagesFragmentTemplate(R.layout.fragment_newdownl
AlertDialog.Builder(context)
.setIcon(R.drawable.ic_launcher_foreground).setMessage("删除下载的漫画${name}吗?")
.setTitle("提示").setPositiveButton(android.R.string.ok) { _, _ ->
- if (chosenFile.exists()) Thread {
- FileUtils.recursiveRemove(chosenFile)
- activity?.runOnUiThread {
- it.visibility = View.INVISIBLE
+ if (chosenFile.exists()) lifecycleScope.launch {
+ withContext(Dispatchers.IO) {
+ FileUtils.recursiveRemove(chosenFile)
+ withContext(Dispatchers.Main) {
+ it.visibility = View.INVISIBLE
+ }
}
- }.start()
+ }
}.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
diff --git a/app/src/main/java/top/fumiama/copymanga/update/SimpleKanban.kt b/app/src/main/java/top/fumiama/copymanga/update/SimpleKanban.kt
index 68f0fba..a03c9e4 100644
--- a/app/src/main/java/top/fumiama/copymanga/update/SimpleKanban.kt
+++ b/app/src/main/java/top/fumiama/copymanga/update/SimpleKanban.kt
@@ -7,17 +7,17 @@ class SimpleKanban(private val client: Client, private val pwd: String) { //mu
get() {
var times = 3
var re: ByteArray
- var firstRecv: ByteArray
+ var firstReceived: ByteArray
do {
re = byteArrayOf()
if(client.initConnect()) {
client.sendMessage("${pwd}catquit")
client.receiveRawMessage(33) //Welcome to simple kanban server.
try {
- firstRecv = client.receiveRawMessage(4) //le
- val length = convert2Int(firstRecv)
+ firstReceived = client.receiveRawMessage(4) //le
+ val length = convert2Int(firstReceived)
Log.d("MySK", "Msg len: $length")
- if(firstRecv.size > 4) re += firstRecv.copyOfRange(4, firstRecv.size)
+ if(firstReceived.size > 4) re += firstReceived.copyOfRange(4, firstReceived.size)
re += client.receiveRawMessage(length - re.size, setProgress = true)
break
} catch (e: Exception) {
@@ -35,9 +35,9 @@ class SimpleKanban(private val client: Client, private val pwd: String) { //mu
(buffer[1].toInt() and 0xff shl 8) or
(buffer[0].toInt() and 0xff)
- fun fetchRaw(doOnLoadFailure: ()->Unit = {
+ suspend fun fetchRaw(doOnLoadFailure: suspend ()->Unit = {
Log.d("MySD", "Fetch dict failed")
- }, doOnLoadSuccess: (data: ByteArray)->Unit = {
+ }, doOnLoadSuccess: suspend (data: ByteArray)->Unit = {
Log.d("MySD", "Fetch dict success")
}) {
raw?.apply {
diff --git a/app/src/main/java/top/fumiama/copymanga/update/Update.kt b/app/src/main/java/top/fumiama/copymanga/update/Update.kt
index f1f6f79..31376ec 100644
--- a/app/src/main/java/top/fumiama/copymanga/update/Update.kt
+++ b/app/src/main/java/top/fumiama/copymanga/update/Update.kt
@@ -1,15 +1,21 @@
package top.fumiama.copymanga.update
import android.app.Activity
+import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.util.Log
import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import androidx.core.content.edit
+import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.dialog_progress.view.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import top.fumiama.copymanga.tools.ui.UITools
import top.fumiama.dmzj.copymanga.BuildConfig
import top.fumiama.dmzj.copymanga.R
@@ -17,8 +23,10 @@ import java.io.File
import java.security.MessageDigest
object Update {
- fun checkUpdate(activity: Activity, toolsBox: UITools, ignoreSkip: Boolean = false) = activity.apply{
+ suspend fun checkUpdate(activity: AppCompatActivity, toolsBox: UITools, ignoreSkip: Boolean = false) = activity.apply{
val client = Client("reilia.fumiama.top", 13212)
+ val kanban = SimpleKanban(client, "fumiama")
+
val progressBar = layoutInflater.inflate(R.layout.dialog_progress, null, false)
val progressHandler = object : Client.Progress{
override fun notify(progressPercentage: Int) {
@@ -26,69 +34,85 @@ object Update {
progressBar.dpp.progress = progressPercentage
}
}
- val kanban = SimpleKanban(client, "fumiama")
- val msg = kanban[BuildConfig.VERSION_CODE]
- if(msg != "null") {
- val verNum = msg.substringBefore('\n').toIntOrNull()
- val skipNum = getPreferences(MODE_PRIVATE).getInt("skipVersion", 0)
- Log.d("MyUP", "Ver:$verNum, skip: $skipNum")
- if(verNum != null) {
- if(msg.contains("md5:")) {
- if(skipNum < verNum || ignoreSkip) runOnUiThread {
- toolsBox.buildInfo("看板", msg.substringAfter('\n').substringBeforeLast('\n'), "下载新版", "跳过该版", "取消", {
- val info = toolsBox.buildAlertWithView("下载进度", progressBar, "隐藏")
- client.progress = progressHandler
- Thread {
- kanban.fetchRaw({
- runOnUiThread {
- Toast.makeText(this, "下载失败", Toast.LENGTH_SHORT).show()
- client.progress = null
- }
- }) {
- val md5 = msg.substringAfterLast("md5:")
- if (md5 == UITools.toHexStr(
- MessageDigest.getInstance("MD5").digest(it)
- )
- ) {
- runOnUiThread {
- Toast.makeText(this, "下载成功", Toast.LENGTH_SHORT).show()
- info.dismiss()
- }
- val f = File(externalCacheDir, "new.apk")
- f.writeBytes(it)
- install(f, this)
- } else runOnUiThread {
- Toast.makeText(this, "文件损坏", Toast.LENGTH_SHORT).show()
- info.dismiss()
- }
- client.progress = null
- }
- }.start()
- }, {
- getPreferences(MODE_PRIVATE).edit {
- putInt("skipVersion", verNum)
- apply()
- }
- })
- }
- } else runOnUiThread {
- toolsBox.buildInfo("看板", msg.substringAfter('\n'), "知道了")
- }
+ val msg = message(kanban)
+ if (msg == "null") {
+ if(ignoreSkip) withContext(Dispatchers.Main) {
+ Toast.makeText(this@apply, "无更新", Toast.LENGTH_SHORT).show()
}
- } else if(ignoreSkip) runOnUiThread {
- Toast.makeText(this, "无更新", Toast.LENGTH_SHORT).show()
+ return@apply
+ }
+
+ val verNum = msg.substringBefore('\n').toIntOrNull()
+ val skipNum = getPreferences(MODE_PRIVATE).getInt("skipVersion", 0)
+ Log.d("MyUP", "Ver:$verNum, skip: $skipNum")
+ if (verNum == null) return@apply
+
+ if(!msg.contains("md5:")) {
+ withContext(Dispatchers.Main) {
+ toolsBox.buildInfo("看板", msg.substringAfter('\n'), "知道了")
+ }
+ return@apply
+ }
+ if(skipNum < verNum || ignoreSkip) {
+ toolsBox.buildInfo("看板", msg.substringAfter('\n').substringBeforeLast('\n'), "下载新版", "跳过该版", "取消", {
+ val info = toolsBox.buildAlertWithView("下载进度", progressBar, "隐藏")
+ client.progress = progressHandler
+ lifecycleScope.launch {
+ fetch(client, kanban, this@apply) {
+ lifecycleScope.launch {
+ val md5 = msg.substringAfterLast("md5:")
+ if (md5 == UITools.toHexStr(
+ MessageDigest.getInstance("MD5").digest(it)
+ )
+ ) {
+ Toast.makeText(this@apply, "下载成功", Toast.LENGTH_SHORT).show()
+ info.dismiss()
+ install(it, this@apply)
+ } else runOnUiThread {
+ Toast.makeText(this@apply, "文件损坏", Toast.LENGTH_SHORT).show()
+ info.dismiss()
+ }
+ client.progress = null
+ }
+ }
+ }
+ }, {
+ getPreferences(MODE_PRIVATE).edit {
+ putInt("skipVersion", verNum)
+ apply()
+ }
+ })
}
}
- private fun install(apkFile: File, activity: Activity) = activity.apply{
- val intent = Intent(Intent.ACTION_VIEW)
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- val contentUri: Uri = FileProvider.getUriForFile(this, "$packageName.fileprovider", apkFile)
- intent.setDataAndType(contentUri, "application/vnd.android.package-archive")
- } else intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive")
- startActivity(intent)
+ private suspend fun message(kanban: SimpleKanban) = withContext(Dispatchers.IO) {
+ return@withContext kanban[BuildConfig.VERSION_CODE]
}
-}
\ No newline at end of file
+
+ private suspend fun fetch(client: Client, kanban: SimpleKanban, context: Context, doOnLoadSuccess: (data: ByteArray) -> Unit) = withContext(Dispatchers.IO) {
+ return@withContext kanban.fetchRaw({ downloadFail(client, context) }, doOnLoadSuccess)
+ }
+
+ private suspend fun downloadFail(client: Client, context: Context) = withContext(Dispatchers.Main) {
+ Toast.makeText(context, R.string.download_apk_fail, Toast.LENGTH_SHORT).show()
+ client.progress = null
+ }
+
+ private suspend fun install(data: ByteArray, activity: Activity) = activity.apply{
+ withContext(Dispatchers.IO) {
+ val f = File(externalCacheDir, "new.apk")
+ f.writeBytes(data)
+ val intent = Intent(Intent.ACTION_VIEW)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ val contentUri: Uri = FileProvider.getUriForFile(this@apply, "$packageName.fileprovider", f)
+ intent.setDataAndType(contentUri, "application/vnd.android.package-archive")
+ } else intent.setDataAndType(Uri.fromFile(f), "application/vnd.android.package-archive")
+ withContext(Dispatchers.Main) {
+ startActivity(intent)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/top/fumiama/copymanga/user/Member.kt b/app/src/main/java/top/fumiama/copymanga/user/Member.kt
index c958b7a..28abaef 100644
--- a/app/src/main/java/top/fumiama/copymanga/user/Member.kt
+++ b/app/src/main/java/top/fumiama/copymanga/user/Member.kt
@@ -11,7 +11,7 @@ import top.fumiama.dmzj.copymanga.R
class Member(private val pref: SharedPreferences, private val getString: (Int) -> String) {
val hasLogin: Boolean get() = pref.getString("token", "")?.isNotEmpty()?:false
- suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure = withContext(Dispatchers.IO) {
+ suspend fun login(username: String, pwd: String, salt: Int): LoginInfoStructure = withContext(Dispatchers.IO) {
try {
CMApi.getLoginConnection(username, pwd, salt)?.apply {
Gson().fromJson(inputStream.reader(), LoginInfoStructure::class.java)?.let { data ->
diff --git a/app/src/main/res/drawable-anydpi/ic_circle.xml b/app/src/main/res/drawable-anydpi/ic_circle.xml
new file mode 100644
index 0000000..385d7cf
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_circle.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi/ic_data.xml b/app/src/main/res/drawable-anydpi/ic_data.xml
new file mode 100644
index 0000000..a354f86
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_data.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi/ic_hot.xml b/app/src/main/res/drawable-anydpi/ic_hot.xml
new file mode 100644
index 0000000..a46da1b
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_hot.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi/ic_image.xml b/app/src/main/res/drawable-anydpi/ic_image.xml
new file mode 100644
index 0000000..5d37c4a
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_image.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi/ic_success.xml b/app/src/main/res/drawable-anydpi/ic_success.xml
new file mode 100644
index 0000000..e929ff2
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_success.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/card_book.xml b/app/src/main/res/layout/card_book.xml
index e4018e6..3dbad67 100644
--- a/app/src/main/res/layout/card_book.xml
+++ b/app/src/main/res/layout/card_book.xml
@@ -78,7 +78,6 @@
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:gravity="center_horizontal"
- android:text="获取标题失败"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="@android:color/white"
android:textSize="12sp"
diff --git a/app/src/main/res/layout/fragment_book.xml b/app/src/main/res/layout/fragment_book.xml
index a772adb..c45f5c2 100644
--- a/app/src/main/res/layout/fragment_book.xml
+++ b/app/src/main/res/layout/fragment_book.xml
@@ -16,29 +16,30 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/nav_header_vertical_spacing"
- app:layout_constraintBottom_toTopOf="@id/fbov"
+ app:layout_constraintBottom_toTopOf="@id/fbvp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
- app:layout_scrollFlags="scroll|enterAlways" />
+ app:layout_scrollFlags="scroll|exitUntilCollapsed" />
+
+
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@+id/fbiinf"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
+ app:layout_constraintTop_toTopOf="parent" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/line_bookinfo_text.xml b/app/src/main/res/layout/line_bookinfo_text.xml
index b6d0869..5826b69 100644
--- a/app/src/main/res/layout/line_bookinfo_text.xml
+++ b/app/src/main/res/layout/line_bookinfo_text.xml
@@ -20,7 +20,7 @@
android:layout_width="@dimen/icon_size_small"
android:layout_height="@dimen/icon_size_small"
android:layout_marginBottom="@dimen/nav_header_vertical_spacing"
- android:background="@drawable/ic_list"
+ android:background="@drawable/ic_data"
app:layout_constraintBottom_toTopOf="@+id/bitime"
app:layout_constraintEnd_toStartOf="@+id/btsub"
app:layout_constraintStart_toStartOf="parent"
@@ -61,7 +61,7 @@
android:layout_width="@dimen/icon_size_small"
android:layout_height="@dimen/icon_size_small"
android:layout_marginBottom="@dimen/nav_header_vertical_spacing"
- android:background="@drawable/ic_list"
+ android:background="@drawable/ic_image"
app:layout_constraintBottom_toTopOf="@+id/bihit"
app:layout_constraintEnd_toStartOf="@+id/bttag"
app:layout_constraintStart_toStartOf="parent"
@@ -82,7 +82,7 @@
android:layout_width="@dimen/icon_size_small"
android:layout_height="@dimen/icon_size_small"
android:layout_marginBottom="@dimen/nav_header_vertical_spacing"
- android:background="@drawable/ic_list"
+ android:background="@drawable/ic_hot"
app:layout_constraintBottom_toTopOf="@+id/bisub"
app:layout_constraintEnd_toStartOf="@+id/bthit"
app:layout_constraintStart_toStartOf="parent"
@@ -103,7 +103,7 @@
android:layout_width="@dimen/icon_size_small"
android:layout_height="@dimen/icon_size_small"
android:layout_marginBottom="@dimen/nav_header_vertical_spacing"
- android:background="@drawable/ic_author"
+ android:background="@drawable/ic_locate"
app:layout_constraintBottom_toTopOf="@+id/bitag"
app:layout_constraintEnd_toStartOf="@+id/btauth"
app:layout_constraintStart_toStartOf="parent"
diff --git a/app/src/main/res/layout/line_chapter.xml b/app/src/main/res/layout/line_chapter.xml
index b020a48..60f3e8c 100644
--- a/app/src/main/res/layout/line_chapter.xml
+++ b/app/src/main/res/layout/line_chapter.xml
@@ -15,7 +15,7 @@
android:layout_marginStart="@dimen/nav_header_vertical_spacing"
android:layout_marginTop="@dimen/nav_header_vertical_spacing"
android:layout_marginBottom="@dimen/nav_header_vertical_spacing"
- android:background="@drawable/ic_list"
+ android:background="@drawable/ic_circle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
diff --git a/app/src/main/res/layout/page_nested_list.xml b/app/src/main/res/layout/page_nested_list.xml
new file mode 100644
index 0000000..a307512
--- /dev/null
+++ b/app/src/main/res/layout/page_nested_list.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
index e4fb17c..56156d7 100644
--- a/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/src/main/res/navigation/mobile_navigation.xml
@@ -224,7 +224,7 @@
主页
分类
排行
- ©2022–2023源文雨\n本应用为拷贝漫画的第三方客户端,数据均来源于网络,作者不对其中所呈现的任何内容负责。
+ ©2022–2024源文雨\n本应用为拷贝漫画的第三方客户端,数据均来源于网络,作者不对其中所呈现的任何内容负责。
浏览历史
我的订阅
我的下载
@@ -29,6 +29,9 @@
illust: Hiten(490219)
请设定提示文字内容
清除设定的图片?
+ 选取图片失败
+ 裁剪图片失败
+ 下载更新失败
登录
注销
@@ -112,7 +115,9 @@
热度 %1$d
状态 %1$s
- 云端读到 %1$s
+ 区域 %1$s
+ 画幅 %1$s
+ 云读至%1$s
专题系列
漫画推荐
diff --git a/build.gradle b/build.gradle
index 1af899f..5f5470d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,7 +10,7 @@ buildscript {
maven { url "https://jitpack.io" }
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.1.1'
+ classpath 'com.android.tools.build:gradle:8.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
@@ -27,6 +27,6 @@ allprojects {
}
}
-task clean(type: Delete) {
+tasks.register('clean', Delete) {
delete rootProject.buildDir
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index bcbaca3..5563ca9 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip