From d1c2f7dc278ebb271043ce12fb86480b5156fc5f Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 12:47:43 +0800 Subject: [PATCH 01/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/welcome/WelcomeActivity.kt | 31 ++------ app/src/main/res/layout/activity_welcome.xml | 72 ++++++++++++++++--- 2 files changed, 71 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt index 86b326ee1..44054f192 100644 --- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt @@ -1,7 +1,5 @@ package io.legado.app.ui.welcome -import android.animation.Animator -import android.animation.ValueAnimator import android.content.Intent import android.os.Bundle import io.legado.app.R @@ -16,34 +14,19 @@ import org.jetbrains.anko.startActivity open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { override fun onActivityCreated(savedInstanceState: Bundle?) { - iv_bg.setColorFilter(accentColor) + iv_book.setColorFilter(accentColor) // 避免从桌面启动程序后,会重新实例化入口类的activity if (intent.flags and Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT != 0) { finish() return } - val welAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(800) - welAnimator.startDelay = 100 - welAnimator.addUpdateListener { animation -> - val alpha = animation.animatedValue as Float - iv_bg.alpha = alpha - } - welAnimator.addListener(object : Animator.AnimatorListener { - override fun onAnimationStart(animation: Animator) { - startActivity() - if (getPrefBoolean(getString(R.string.pk_default_read))) { - startActivity() - } - finish() + root_view.postDelayed({ + startActivity() + if (getPrefBoolean(getString(R.string.pk_default_read))) { + startActivity() } - - override fun onAnimationEnd(animation: Animator) = Unit - - override fun onAnimationCancel(animation: Animator) = Unit - - override fun onAnimationRepeat(animation: Animator) = Unit - }) - welAnimator.start() + finish() + }, 200) } } diff --git a/app/src/main/res/layout/activity_welcome.xml b/app/src/main/res/layout/activity_welcome.xml index e8e320279..6013c90b1 100644 --- a/app/src/main/res/layout/activity_welcome.xml +++ b/app/src/main/res/layout/activity_welcome.xml @@ -1,15 +1,71 @@ - + android:layout_height="match_parent" + android:gravity="center_horizontal"> - + + + + + + + android:contentDescription="@string/welcome" + app:layout_constraintBottom_toTopOf="@+id/tv_gzh" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" /> + + - \ No newline at end of file + \ No newline at end of file From 823bfe7685a3f723d4ae776e7fa958637666caae Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 13:16:24 +0800 Subject: [PATCH 02/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/ui/main/MainActivity.kt | 11 ----------- .../java/io/legado/app/ui/main/MainViewModel.kt | 8 -------- .../io/legado/app/ui/welcome/WelcomeActivity.kt | 16 ++++++++++++++++ 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 38569115b..8b4705ae0 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -7,7 +7,6 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentStatePagerAdapter import androidx.viewpager.widget.ViewPager -import com.github.houbb.opencc4j.util.ZhConverterUtil import com.google.android.material.bottomnavigation.BottomNavigationView import io.legado.app.App import io.legado.app.BuildConfig @@ -16,7 +15,6 @@ import io.legado.app.base.VMBaseActivity import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey import io.legado.app.help.AppConfig -import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.storage.Backup import io.legado.app.lib.theme.ATH import io.legado.app.service.BaseReadAloudService @@ -53,21 +51,12 @@ class MainActivity : VMBaseActivity(R.layout.activity_main), override fun onPostCreate(savedInstanceState: Bundle?) { super.onPostCreate(savedInstanceState) upVersion() - //初始化简繁转换引擎 - when (AppConfig.chineseConverterType) { - 1 -> Coroutine.async { ZhConverterUtil.toSimple("初始化") } - 2 -> Coroutine.async { ZhConverterUtil.toTraditional("初始化") } - } //自动更新书籍 if (AppConfig.autoRefreshBook) { view_pager_main.postDelayed({ viewModel.upChapterList() }, 1000) } - //清楚过期数据 - view_pager_main.postDelayed({ - viewModel.clearExpiredData() - }, 3000) } override fun onNavigationItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt index 1a0652bae..4fe57d758 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt @@ -13,7 +13,6 @@ import io.legado.app.utils.fromJsonObject import io.legado.app.utils.postEvent import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.delay -import java.util.concurrent.TimeUnit class MainViewModel(application: Application) : BaseViewModel(application) { val updateList = hashSetOf() @@ -54,13 +53,6 @@ class MainViewModel(application: Application) : BaseViewModel(application) { } } - fun clearExpiredData() { - execute { - App.db.searchBookDao() - .clearExpired(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)) - } - } - fun initRss() { execute { val url = "https://gitee.com/alanskycn/yuedu/raw/master/JS/RSS/rssSource" diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt index 44054f192..22e694b0f 100644 --- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt @@ -2,14 +2,19 @@ package io.legado.app.ui.welcome import android.content.Intent import android.os.Bundle +import com.github.houbb.opencc4j.util.ZhConverterUtil +import io.legado.app.App import io.legado.app.R import io.legado.app.base.BaseActivity +import io.legado.app.help.AppConfig +import io.legado.app.help.coroutine.Coroutine import io.legado.app.lib.theme.accentColor import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.main.MainActivity import io.legado.app.utils.getPrefBoolean import kotlinx.android.synthetic.main.activity_welcome.* import org.jetbrains.anko.startActivity +import java.util.concurrent.TimeUnit open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { @@ -20,6 +25,17 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { finish() return } + Coroutine.async { + //清楚过期数据 + App.db.searchBookDao() + .clearExpired(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)) + //初始化简繁转换引擎 + when (AppConfig.chineseConverterType) { + 1 -> ZhConverterUtil.toSimple("初始化") + 2 -> ZhConverterUtil.toTraditional("初始化") + else -> null + } + } root_view.postDelayed({ startActivity() if (getPrefBoolean(getString(R.string.pk_default_read))) { From 83fbe6b93bf513991a2a635553b4c01ee48e1e71 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 13:18:02 +0800 Subject: [PATCH 03/91] =?UTF-8?q?feat:=20=E5=88=A0=E9=99=A4=E6=AC=A2?= =?UTF-8?q?=E8=BF=8E=E7=95=8C=E9=9D=A2=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/drawable/image_welcome.xml | 75 --------------------- 1 file changed, 75 deletions(-) delete mode 100644 app/src/main/res/drawable/image_welcome.xml diff --git a/app/src/main/res/drawable/image_welcome.xml b/app/src/main/res/drawable/image_welcome.xml deleted file mode 100644 index d9d1c13a9..000000000 --- a/app/src/main/res/drawable/image_welcome.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 9f8744c65b41f85bcfb2d739f1c3f330bc04e00f Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 13:23:45 +0800 Subject: [PATCH 04/91] =?UTF-8?q?feat:=20=E5=88=A0=E9=99=A4=E6=AC=A2?= =?UTF-8?q?=E8=BF=8E=E7=95=8C=E9=9D=A2=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/ui/welcome/WelcomeActivity.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt index 22e694b0f..0f1cd57dd 100644 --- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt @@ -23,8 +23,12 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { // 避免从桌面启动程序后,会重新实例化入口类的activity if (intent.flags and Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT != 0) { finish() - return + } else { + init() } + } + + private fun init() { Coroutine.async { //清楚过期数据 App.db.searchBookDao() From 3659000cb97c9be659e40c8b3196e76e331c9b11 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 14:05:09 +0800 Subject: [PATCH 05/91] =?UTF-8?q?feat:=20=E5=88=A0=E9=99=A4=E6=AC=A2?= =?UTF-8?q?=E8=BF=8E=E7=95=8C=E9=9D=A2=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/welcome/WelcomeActivity.kt | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt index 0f1cd57dd..f024dba3d 100644 --- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt @@ -1,5 +1,7 @@ package io.legado.app.ui.welcome +import android.animation.Animator +import android.animation.ValueAnimator import android.content.Intent import android.os.Bundle import com.github.houbb.opencc4j.util.ZhConverterUtil @@ -16,7 +18,7 @@ import kotlinx.android.synthetic.main.activity_welcome.* import org.jetbrains.anko.startActivity import java.util.concurrent.TimeUnit -open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { +open class WelcomeActivity : BaseActivity(R.layout.activity_welcome), Animator.AnimatorListener { override fun onActivityCreated(savedInstanceState: Bundle?) { iv_book.setColorFilter(accentColor) @@ -40,15 +42,33 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { else -> null } } - root_view.postDelayed({ - startActivity() - if (getPrefBoolean(getString(R.string.pk_default_read))) { - startActivity() - } - finish() - }, 200) + val welAnimator = ValueAnimator.ofFloat(1f, 0.3f).setDuration(300) + welAnimator.addUpdateListener { animation -> + root_view.alpha = animation.animatedValue as Float + } + welAnimator.addListener(this) + welAnimator.start() + } + + private fun startMainActivity() { + startActivity() + if (getPrefBoolean(getString(R.string.pk_default_read))) { + startActivity() + } + finish() } + override fun onAnimationStart(animation: Animator) { + + } + + override fun onAnimationEnd(animation: Animator) { + startMainActivity() + } + + override fun onAnimationCancel(animation: Animator) = Unit + + override fun onAnimationRepeat(animation: Animator) = Unit } class Launcher1 : WelcomeActivity() From 5f2f5495d0c93c989d07fcb5994c92e65258e878 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 14:14:10 +0800 Subject: [PATCH 06/91] =?UTF-8?q?feat:=20=E5=88=A0=E9=99=A4=E6=AC=A2?= =?UTF-8?q?=E8=BF=8E=E7=95=8C=E9=9D=A2=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/welcome/WelcomeActivity.kt | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt index f024dba3d..5255d38b9 100644 --- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt @@ -1,6 +1,5 @@ package io.legado.app.ui.welcome -import android.animation.Animator import android.animation.ValueAnimator import android.content.Intent import android.os.Bundle @@ -18,7 +17,7 @@ import kotlinx.android.synthetic.main.activity_welcome.* import org.jetbrains.anko.startActivity import java.util.concurrent.TimeUnit -open class WelcomeActivity : BaseActivity(R.layout.activity_welcome), Animator.AnimatorListener { +open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { override fun onActivityCreated(savedInstanceState: Bundle?) { iv_book.setColorFilter(accentColor) @@ -42,11 +41,15 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome), Animator.A else -> null } } - val welAnimator = ValueAnimator.ofFloat(1f, 0.3f).setDuration(300) + val welAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(800) + welAnimator.startDelay = 100 welAnimator.addUpdateListener { animation -> - root_view.alpha = animation.animatedValue as Float + val alpha = animation.animatedValue as Float + root_view.alpha = alpha + if (alpha < 0.6) { + startMainActivity() + } } - welAnimator.addListener(this) welAnimator.start() } @@ -58,17 +61,6 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome), Animator.A finish() } - override fun onAnimationStart(animation: Animator) { - - } - - override fun onAnimationEnd(animation: Animator) { - startMainActivity() - } - - override fun onAnimationCancel(animation: Animator) = Unit - - override fun onAnimationRepeat(animation: Animator) = Unit } class Launcher1 : WelcomeActivity() From 30ddfe495258dfb8fe03de6e92e00250d2516f8d Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 14:32:04 +0800 Subject: [PATCH 07/91] =?UTF-8?q?feat:=20=E5=88=A0=E9=99=A4=E6=AC=A2?= =?UTF-8?q?=E8=BF=8E=E7=95=8C=E9=9D=A2=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/ui/welcome/WelcomeActivity.kt | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt index 5255d38b9..3a5924780 100644 --- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt @@ -1,6 +1,5 @@ package io.legado.app.ui.welcome -import android.animation.ValueAnimator import android.content.Intent import android.os.Bundle import com.github.houbb.opencc4j.util.ZhConverterUtil @@ -41,16 +40,7 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { else -> null } } - val welAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(800) - welAnimator.startDelay = 100 - welAnimator.addUpdateListener { animation -> - val alpha = animation.animatedValue as Float - root_view.alpha = alpha - if (alpha < 0.6) { - startMainActivity() - } - } - welAnimator.start() + root_view.postDelayed({ startMainActivity() }, 200) } private fun startMainActivity() { From 82b7e6feb4d8cbefad9c8b96e3432a9ffeeb6ec8 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 16:10:42 +0800 Subject: [PATCH 08/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/help/storage/Backup.kt | 5 +- .../legado/app/help/storage/ImportOldData.kt | 4 +- .../book/source/manage/BookSourceActivity.kt | 43 +++- .../book/source/manage/BookSourceViewModel.kt | 21 +- .../legado/app/ui/config/BackupRestoreUi.kt | 93 +-------- .../app/ui/config/OtherConfigFragment.kt | 39 +--- .../legado/app/ui/filechooser/FilePicker.kt | 183 ++++++++++++++++++ .../app/ui/filechooser/adapter/FileAdapter.kt | 2 +- .../app/ui/filechooser/adapter/PathAdapter.kt | 2 +- .../{ => utils}/FilePickerIcon.java | 2 +- app/src/main/res/values/strings.xml | 1 + 11 files changed, 255 insertions(+), 140 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt rename app/src/main/java/io/legado/app/ui/filechooser/{ => utils}/FilePickerIcon.java (99%) diff --git a/app/src/main/java/io/legado/app/help/storage/Backup.kt b/app/src/main/java/io/legado/app/help/storage/Backup.kt index 921910f34..8c4fa8fc2 100644 --- a/app/src/main/java/io/legado/app/help/storage/Backup.kt +++ b/app/src/main/java/io/legado/app/help/storage/Backup.kt @@ -101,9 +101,8 @@ object Backup { val file = File(backupPath + File.separator + fileName) if (file.exists()) { treeDoc.findFile(fileName)?.delete() - treeDoc.createFile("", fileName)?.let { - DocumentUtils.writeBytes(context, file.readBytes(), it.uri) - } + treeDoc.createFile("", fileName) + ?.writeBytes(context, file.readBytes()) } } } diff --git a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt index 7329705e9..91618a704 100644 --- a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt +++ b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt @@ -115,13 +115,13 @@ object ImportOldData { } } - fun importOldBookshelf(json: String): Int { + private fun importOldBookshelf(json: String): Int { val books = OldBook.toNewBook(json) App.db.bookDao().insert(*books.toTypedArray()) return books.size } - fun importOldSource(json: String): Int { + private fun importOldSource(json: String): Int { val bookSources = mutableListOf() val items: List> = Restore.jsonPath.parse(json).read("$") for (item in items) { diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index c94b59d7c..69f8133f7 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -9,6 +9,7 @@ import android.view.MenuItem import android.view.SubMenu import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView +import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import androidx.recyclerview.widget.DiffUtil @@ -28,6 +29,7 @@ import io.legado.app.lib.theme.primaryTextColor import io.legado.app.service.help.CheckSource import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.qrcode.QrCodeActivity import io.legado.app.ui.widget.SelectActionBar import io.legado.app.ui.widget.recycler.VerticalDivider @@ -39,6 +41,7 @@ import kotlinx.android.synthetic.main.view_search.* import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivityForResult import org.jetbrains.anko.toast +import java.io.File import java.io.FileNotFoundException class BookSourceActivity : VMBaseActivity(R.layout.activity_book_source), @@ -51,6 +54,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity private val importRecordKey = "bookSourceRecordKey" private val qrRequestCode = 101 private val importSource = 132 + private val exportRequestCode = 65 private lateinit var adapter: BookSourceAdapter private var bookSourceLiveDate: LiveData>? = null private var groups = linkedSetOf() @@ -187,7 +191,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity R.id.menu_disable_selection -> viewModel.disableSelection(adapter.getSelection()) R.id.menu_enable_explore -> viewModel.enableSelectExplore(adapter.getSelection()) R.id.menu_disable_explore -> viewModel.disableSelectExplore(adapter.getSelection()) - R.id.menu_export_selection -> viewModel.exportSelection(adapter.getSelection()) + R.id.menu_export_selection -> FilePicker.selectFolder(this, exportRequestCode) R.id.menu_check_source -> CheckSource.start(this, adapter.getSelection()) } return true @@ -267,15 +271,6 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity select_action_bar.upCountView(adapter.getSelection().size, adapter.getActualItemCount()) } - override fun onFilePicked(requestCode: Int, currentPath: String) { - if (requestCode == importSource) { - Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() - viewModel.importSourceFromFilePath(currentPath) { msg -> - title_bar.snackbar(msg) - } - } - } - override fun onQueryTextChange(newText: String?): Boolean { newText?.let { initLiveDataBookSource(it) @@ -307,6 +302,21 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity viewModel.topSource(bookSource) } + override fun onFilePicked(requestCode: Int, currentPath: String) { + when (requestCode) { + exportRequestCode -> viewModel.exportSelection( + adapter.getSelection(), + File(currentPath) + ) + importSource -> { + Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() + viewModel.importSourceFromFilePath(currentPath) { msg -> + title_bar.snackbar(msg) + } + } + } + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { @@ -344,6 +354,19 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity } } } + exportRequestCode -> { + data?.data?.let { uri -> + if (uri.toString().isContentPath()) { + DocumentFile.fromTreeUri(this, uri)?.let { + viewModel.exportSelection(adapter.getSelection(), it) + } + } else { + uri.path?.let { + viewModel.exportSelection(adapter.getSelection(), File(it)) + } + } + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt index ec5c50020..85e3239bc 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.book.source.manage import android.app.Application import android.text.TextUtils +import androidx.documentfile.provider.DocumentFile import com.jayway.jsonpath.JsonPath import io.legado.app.App import io.legado.app.base.BaseViewModel @@ -89,12 +90,24 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application) } } - fun exportSelection(sources: LinkedHashSet) { + fun exportSelection(sources: LinkedHashSet, file: File) { execute { val json = GSON.toJson(sources) - val file = - FileUtils.createFileIfNotExist(Backup.exportPath + File.separator + "exportBookSource.json") - file.writeText(json) + FileUtils.createFileIfNotExist(file, "exportBookSource.json") + .writeText(json) + }.onSuccess { + context.toast("成功导出至\n${Backup.exportPath}") + }.onError { + context.toast("导出失败\n${it.localizedMessage}") + } + } + + fun exportSelection(sources: LinkedHashSet, doc: DocumentFile) { + execute { + val json = GSON.toJson(sources) + doc.findFile("exportBookSource.json")?.delete() + doc.createFile("", "exportBookSource.json") + ?.writeText(context, json) }.onSuccess { context.toast("成功导出至\n${Backup.exportPath}") }.onError { diff --git a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt index f314b5f1b..7f094ebc4 100644 --- a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt +++ b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt @@ -17,8 +17,7 @@ import io.legado.app.help.storage.Backup import io.legado.app.help.storage.ImportOldData import io.legado.app.help.storage.Restore import io.legado.app.help.storage.WebDavHelp -import io.legado.app.lib.dialogs.alert -import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.utils.getPrefString import io.legado.app.utils.isContentPath import io.legado.app.utils.toast @@ -79,31 +78,9 @@ object BackupRestoreUi { } fun selectBackupFolder(fragment: Fragment, requestCode: Int = selectFolderRequestCode) { - fragment.alert { - titleResource = R.string.select_folder - items(fragment.resources.getStringArray(R.array.select_folder).toList()) { _, index -> - when (index) { - 0 -> backupUsePermission(fragment, requestCode = requestCode) - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - fragment.startActivityForResult(intent, requestCode) - } catch (e: java.lang.Exception) { - e.printStackTrace() - fragment.toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> { - FileChooserDialog.show( - fragment.childFragmentManager, - requestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - } - } - }.show() + FilePicker.selectFolder(fragment, requestCode) { + backupUsePermission(fragment, requestCode = requestCode) + } } fun restore(fragment: Fragment) { @@ -148,65 +125,15 @@ object BackupRestoreUi { } private fun selectRestoreFolder(fragment: Fragment) { - fragment.alert { - titleResource = R.string.select_folder - items(fragment.resources.getStringArray(R.array.select_folder).toList()) { _, index -> - when (index) { - 0 -> restoreUsePermission(fragment) - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - fragment.startActivityForResult(intent, restoreSelectRequestCode) - } catch (e: java.lang.Exception) { - e.printStackTrace() - fragment.toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> { - FileChooserDialog.show( - fragment.childFragmentManager, - restoreSelectRequestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - } - } - }.show() + FilePicker.selectFolder(fragment, restoreSelectRequestCode) { + restoreUsePermission(fragment) + } } fun importOldData(fragment: Fragment) { - fragment.alert { - titleResource = R.string.select_folder - items(fragment.resources.getStringArray(R.array.select_folder).toList()) { _, index -> - when (index) { - 0 -> importOldUsePermission(fragment) - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - fragment.startActivityForResult(intent, oldDataRequestCode) - } catch (e: java.lang.Exception) { - e.printStackTrace() - fragment.toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> { - PermissionsCompat.Builder(fragment) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - fragment.childFragmentManager, - oldDataRequestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - .request() - } - } - } - }.show() + FilePicker.selectFolder(fragment, oldDataRequestCode) { + importOldUsePermission(fragment) + } } private fun importOldUsePermission(fragment: Fragment) { diff --git a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt index 31c3d709f..e8ea335c0 100644 --- a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt @@ -16,12 +16,10 @@ import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey import io.legado.app.help.AppConfig import io.legado.app.help.BookHelp -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat -import io.legado.app.lib.dialogs.alert import io.legado.app.lib.theme.ATH import io.legado.app.receiver.SharedReceiverActivity import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.widget.number.NumberPickerDialog import io.legado.app.utils.* @@ -124,38 +122,9 @@ class OtherConfigFragment : PreferenceFragmentCompat(), } private fun selectDownloadPath() { - alert { - titleResource = R.string.select_folder - items(resources.getStringArray(R.array.select_folder).toList()) { _, i -> - when (i) { - 0 -> { - removePref(PreferKey.downloadPath) - } - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - startActivityForResult(intent, requestCodeDownloadPath) - } catch (e: Exception) { - e.printStackTrace() - toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> PermissionsCompat.Builder(this@OtherConfigFragment) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - childFragmentManager, - requestCodeDownloadPath, - mode = FileChooserDialog.DIRECTORY, - initPath = BookHelp.downloadPath - ) - } - .request() - } - } - }.show() + FilePicker.selectFolder(this, requestCodeDownloadPath) { + removePref(PreferKey.downloadPath) + } } override fun onFilePicked(requestCode: Int, currentPath: String) { diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt new file mode 100644 index 000000000..0f383b49e --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt @@ -0,0 +1,183 @@ +package io.legado.app.ui.filechooser + +import android.content.Intent +import androidx.fragment.app.Fragment +import io.legado.app.R +import io.legado.app.base.BaseActivity +import io.legado.app.help.permission.Permissions +import io.legado.app.help.permission.PermissionsCompat +import io.legado.app.lib.dialogs.alert +import io.legado.app.utils.toast +import org.jetbrains.anko.toast + +object FilePicker { + + fun selectFolder(activity: BaseActivity, requestCode: Int, default: (() -> Unit)? = null) { + activity.alert(titleResource = R.string.select_folder) { + val selectList = + activity.resources.getStringArray(R.array.select_folder).toMutableList() + default ?: let { + selectList.removeAt(0) + } + items(selectList) { _, index -> + when (index) { + 0 -> default?.invoke() + 1 -> { + try { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + activity.startActivityForResult(intent, requestCode) + } catch (e: java.lang.Exception) { + e.printStackTrace() + activity.toast(e.localizedMessage ?: "ERROR") + } + } + 2 -> { + PermissionsCompat.Builder(activity) + .addPermissions(*Permissions.Group.STORAGE) + .rationale(R.string.tip_perm_request_storage) + .onGranted { + FileChooserDialog.show( + activity.supportFragmentManager, + requestCode, + mode = FileChooserDialog.DIRECTORY + ) + } + .request() + } + } + } + }.show() + } + + fun selectFolder(fragment: Fragment, requestCode: Int, default: (() -> Unit)? = null) { + fragment.requireContext() + .alert(titleResource = R.string.select_folder) { + val selectList = + fragment.resources.getStringArray(R.array.select_folder).toMutableList() + default ?: let { + selectList.removeAt(0) + } + items(selectList) { _, index -> + when (index) { + 0 -> default?.invoke() + 1 -> { + try { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + fragment.startActivityForResult(intent, requestCode) + } catch (e: java.lang.Exception) { + e.printStackTrace() + fragment.toast(e.localizedMessage ?: "ERROR") + } + } + 2 -> { + PermissionsCompat.Builder(fragment) + .addPermissions(*Permissions.Group.STORAGE) + .rationale(R.string.tip_perm_request_storage) + .onGranted { + FileChooserDialog.show( + fragment.childFragmentManager, + requestCode, + mode = FileChooserDialog.DIRECTORY + ) + } + .request() + } + } + } + }.show() + } + + fun selectFile( + activity: BaseActivity, + requestCode: Int, + type: String, + default: (() -> Unit)? = null + ) { + activity.alert(titleResource = R.string.select_file) { + val selectList = + activity.resources.getStringArray(R.array.select_folder).toMutableList() + default ?: let { + selectList.removeAt(0) + } + items(selectList) { _, index -> + when (index) { + 0 -> default?.invoke() + 1 -> { + try { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.addCategory(Intent.CATEGORY_OPENABLE) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + intent.type = type//设置类型 + activity.startActivityForResult(intent, requestCode) + } catch (e: java.lang.Exception) { + e.printStackTrace() + activity.toast(e.localizedMessage ?: "ERROR") + } + } + 2 -> { + PermissionsCompat.Builder(activity) + .addPermissions(*Permissions.Group.STORAGE) + .rationale(R.string.tip_perm_request_storage) + .onGranted { + FileChooserDialog.show( + activity.supportFragmentManager, + requestCode, + mode = FileChooserDialog.FILE + ) + } + .request() + } + } + } + }.show() + } + + fun selectFile( + fragment: Fragment, + requestCode: Int, + type: String, + default: (() -> Unit)? = null + ) { + fragment.requireContext() + .alert(titleResource = R.string.select_file) { + val selectList = + fragment.resources.getStringArray(R.array.select_folder).toMutableList() + default ?: let { + selectList.removeAt(0) + } + items(selectList) { _, index -> + when (index) { + 0 -> default?.invoke() + 1 -> { + try { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.addCategory(Intent.CATEGORY_OPENABLE) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + intent.type = type//设置类型 + fragment.startActivityForResult(intent, requestCode) + } catch (e: java.lang.Exception) { + e.printStackTrace() + fragment.toast(e.localizedMessage ?: "ERROR") + } + } + 2 -> { + PermissionsCompat.Builder(fragment) + .addPermissions(*Permissions.Group.STORAGE) + .rationale(R.string.tip_perm_request_storage) + .onGranted { + FileChooserDialog.show( + fragment.childFragmentManager, + requestCode, + mode = FileChooserDialog.FILE + ) + } + .request() + } + } + } + }.show() + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt b/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt index 4ade6da7f..0fbaafe33 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt @@ -6,9 +6,9 @@ import android.graphics.drawable.Drawable import io.legado.app.R import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.SimpleRecyclerAdapter -import io.legado.app.ui.filechooser.FilePickerIcon import io.legado.app.ui.filechooser.entity.FileItem import io.legado.app.ui.filechooser.utils.ConvertUtils +import io.legado.app.ui.filechooser.utils.FilePickerIcon import io.legado.app.ui.filechooser.utils.FileUtils import kotlinx.android.synthetic.main.item_path_filepicker.view.* import org.jetbrains.anko.sdk27.listeners.onClick diff --git a/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt b/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt index 3d2323495..86ccf67c0 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt @@ -5,8 +5,8 @@ import android.os.Environment import io.legado.app.R import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.SimpleRecyclerAdapter -import io.legado.app.ui.filechooser.FilePickerIcon import io.legado.app.ui.filechooser.utils.ConvertUtils +import io.legado.app.ui.filechooser.utils.FilePickerIcon import kotlinx.android.synthetic.main.item_path_filepicker.view.* import org.jetbrains.anko.sdk27.listeners.onClick import java.util.* diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePickerIcon.java b/app/src/main/java/io/legado/app/ui/filechooser/utils/FilePickerIcon.java similarity index 99% rename from app/src/main/java/io/legado/app/ui/filechooser/FilePickerIcon.java rename to app/src/main/java/io/legado/app/ui/filechooser/utils/FilePickerIcon.java index 4ab481938..6732626e4 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FilePickerIcon.java +++ b/app/src/main/java/io/legado/app/ui/filechooser/utils/FilePickerIcon.java @@ -1,4 +1,4 @@ -package io.legado.app.ui.filechooser; +package io.legado.app.ui.filechooser.utils; /** * Generated by https://github.com/gzu-liyujiang/Image2ByteVar diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3f54ea194..7903cadfb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -488,6 +488,7 @@ 三字符缩进 四字符缩进 选择文件夹 + 选择文件 没有发现,可以在书源里添加。 恢复默认 自定义缓存路径需要存储权限 From 7d3c7d23b35b4c11b7bed73a929f90653c1129a1 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 16:18:35 +0800 Subject: [PATCH 09/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/download/DownloadActivity.kt | 48 ++++--------------- .../legado/app/ui/filechooser/FilePicker.kt | 3 +- 2 files changed, 10 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt b/app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt index ef1d87e68..cdcbf9746 100644 --- a/app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt +++ b/app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt @@ -16,17 +16,15 @@ import io.legado.app.constant.EventBus import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.help.BookHelp -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat import io.legado.app.service.help.Download import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.utils.* import kotlinx.android.synthetic.main.activity_download.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.jetbrains.anko.alert import org.jetbrains.anko.toast @@ -126,44 +124,14 @@ class DownloadActivity : VMBaseActivity(R.layout.activity_dow override fun export(position: Int) { exportPosition = position - alert { - titleResource = R.string.select_folder - items(resources.getStringArray(R.array.select_folder).toList()) { _, index -> - when (index) { - 0 -> { - val path = ACache.get(this@DownloadActivity).getAsString(exportBookPathKey) - if (path.isNullOrEmpty()) { - toast("没有默认路径") - } else { - startExport(path) - } - } - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - startActivityForResult(intent, exportRequestCode) - } catch (e: java.lang.Exception) { - e.printStackTrace() - toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> { - PermissionsCompat.Builder(this@DownloadActivity) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - supportFragmentManager, - exportRequestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - .request() - } - } + FilePicker.selectFolder(this, exportRequestCode) { + val path = ACache.get(this@DownloadActivity).getAsString(exportBookPathKey) + if (path.isNullOrEmpty()) { + toast("没有默认路径") + } else { + startExport(path) } - }.show() + } } private fun startExport(path: String) { diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt index 0f383b49e..aa3db53c3 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt @@ -1,6 +1,7 @@ package io.legado.app.ui.filechooser import android.content.Intent +import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import io.legado.app.R import io.legado.app.base.BaseActivity @@ -12,7 +13,7 @@ import org.jetbrains.anko.toast object FilePicker { - fun selectFolder(activity: BaseActivity, requestCode: Int, default: (() -> Unit)? = null) { + fun selectFolder(activity: AppCompatActivity, requestCode: Int, default: (() -> Unit)? = null) { activity.alert(titleResource = R.string.select_folder) { val selectList = activity.resources.getStringArray(R.array.select_folder).toMutableList() From 909f31a5f2d73c9c429673ee0a3abca6ea231035 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 16:49:52 +0800 Subject: [PATCH 10/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/source/manage/BookSourceActivity.kt | 45 +----------------- .../app/ui/filechooser/FileChooserDialog.kt | 4 +- .../legado/app/ui/filechooser/FilePicker.kt | 8 +++- .../app/ui/filechooser/adapter/FileAdapter.kt | 2 +- .../app/ui/filechooser/utils/FileUtils.kt | 16 ++++--- .../app/ui/replacerule/ReplaceRuleActivity.kt | 46 ++----------------- .../ui/rss/source/manage/RssSourceActivity.kt | 45 ++---------------- .../app/ui/widget/font/FontSelectDialog.kt | 42 +++-------------- 8 files changed, 33 insertions(+), 175 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index 69f8133f7..49405ecbf 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -21,8 +21,6 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.data.entities.BookSource import io.legado.app.help.ItemTouchCallback -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat import io.legado.app.lib.dialogs.* import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor @@ -42,7 +40,6 @@ import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivityForResult import org.jetbrains.anko.toast import java.io.File -import java.io.FileNotFoundException class BookSourceActivity : VMBaseActivity(R.layout.activity_book_source), PopupMenu.OnMenuItemClickListener, @@ -86,7 +83,8 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity R.id.menu_import_source_qr -> startActivityForResult(qrRequestCode) R.id.menu_group_manage -> GroupManageDialog().show(supportFragmentManager, "groupManage") - R.id.menu_import_source_local -> selectFileSys() + R.id.menu_import_source_local -> FilePicker + .selectFile(this, importSource, "text/*", arrayOf("txt", "json")) R.id.menu_import_source_onLine -> showImportDialog() } if (item.groupId == R.id.source_group) { @@ -239,34 +237,6 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity }.show().applyTint() } - private fun selectFileSys() { - try { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - intent.type = "text/*"//设置类型 - startActivityForResult(intent, importSource) - } catch (e: Exception) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFile() - } - .request() - } - } - - private fun selectFile() { - FileChooserDialog.show( - supportFragmentManager, importSource, - allowExtensions = arrayOf("txt", "json") - ) - } - override fun upCountView() { select_action_bar.upCountView(adapter.getSelection().size, adapter.getActualItemCount()) } @@ -338,17 +308,6 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity title_bar.snackbar(msg) } } - } catch (e: FileNotFoundException) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFileSys() - } - .request() } catch (e: Exception) { toast(e.localizedMessage ?: "ERROR") } diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt b/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt index af8cf50f4..f1e6a7bc9 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt @@ -42,7 +42,7 @@ class FileChooserDialog : DialogFragment(), isShowHomeDir: Boolean = false, isShowUpDir: Boolean = true, isShowHideDir: Boolean = false, - allowExtensions: Array? = null, + allowExtensions: Array? = null, menus: Array? = null ) { FileChooserDialog().apply { @@ -61,7 +61,7 @@ class FileChooserDialog : DialogFragment(), } } - override var allowExtensions: Array? = null + override var allowExtensions: Array? = null override val isOnlyListDir: Boolean get() = mode == DIRECTORY override var isShowHomeDir: Boolean = false diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt index aa3db53c3..0b81368a1 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt @@ -94,6 +94,7 @@ object FilePicker { activity: BaseActivity, requestCode: Int, type: String, + allowExtensions: Array?, default: (() -> Unit)? = null ) { activity.alert(titleResource = R.string.select_file) { @@ -125,7 +126,8 @@ object FilePicker { FileChooserDialog.show( activity.supportFragmentManager, requestCode, - mode = FileChooserDialog.FILE + mode = FileChooserDialog.FILE, + allowExtensions = allowExtensions ) } .request() @@ -139,6 +141,7 @@ object FilePicker { fragment: Fragment, requestCode: Int, type: String, + allowExtensions: Array, default: (() -> Unit)? = null ) { fragment.requireContext() @@ -171,7 +174,8 @@ object FilePicker { FileChooserDialog.show( fragment.childFragmentManager, requestCode, - mode = FileChooserDialog.FILE + mode = FileChooserDialog.FILE, + allowExtensions = allowExtensions ) } .request() diff --git a/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt b/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt index 0fbaafe33..42e34dfb1 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt @@ -122,7 +122,7 @@ class FileAdapter(context: Context, val callBack: CallBack) : interface CallBack { fun onFileClick(position: Int) //允许的扩展名 - var allowExtensions: Array? + var allowExtensions: Array? /** * 是否仅仅读取目录 */ diff --git a/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt b/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt index 309dee0a2..e7e2a4805 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt @@ -60,7 +60,7 @@ object FileUtils { @JvmOverloads fun listDirs( startDirPath: String, - excludeDirs: Array? = null, @SortType sortType: Int = BY_NAME_ASC + excludeDirs: Array? = null, @SortType sortType: Int = BY_NAME_ASC ): Array { var excludeDirs1 = excludeDirs val dirList = ArrayList() @@ -76,7 +76,7 @@ object FileUtils { }) ?: return arrayOfNulls(0) if (excludeDirs1 == null) { - excludeDirs1 = arrayOfNulls(0) + excludeDirs1 = arrayOf() } for (dir in dirs) { val file = dir.absoluteFile @@ -115,7 +115,7 @@ object FileUtils { @JvmOverloads fun listDirsAndFiles( startDirPath: String, - allowExtensions: Array? = null + allowExtensions: Array? = null ): Array? { val dirs: Array? val files: Array? = if (allowExtensions == null) { @@ -189,12 +189,13 @@ object FileUtils { /** * 列出指定目录下的所有文件 */ - fun listFiles(startDirPath: String, allowExtensions: Array): Array? { + fun listFiles(startDirPath: String, allowExtensions: Array?): Array? { val file = File(startDirPath) return file.listFiles { _, name -> //返回当前目录所有以某些扩展名结尾的文件 val extension = getExtension(name) - allowExtensions.contentDeepToString().contains(extension) + allowExtensions?.contentDeepToString()?.contains(extension) == true + || allowExtensions == null } } @@ -202,7 +203,10 @@ object FileUtils { * 列出指定目录下的所有文件 */ fun listFiles(startDirPath: String, allowExtension: String?): Array? { - return listFiles(startDirPath, arrayOf(allowExtension)) + return if (allowExtension == null) + listFiles(startDirPath, allowExtension = null) + else + listFiles(startDirPath, arrayOf(allowExtension)) } /** diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt index 5044e2667..940173bd9 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt @@ -20,12 +20,11 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.data.entities.ReplaceRule import io.legado.app.help.ItemTouchCallback -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat import io.legado.app.lib.dialogs.* import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.replacerule.edit.ReplaceEditDialog import io.legado.app.ui.widget.SelectActionBar import io.legado.app.ui.widget.recycler.VerticalDivider @@ -36,7 +35,6 @@ import kotlinx.android.synthetic.main.dialog_edit_text.view.* import kotlinx.android.synthetic.main.view_search.* import org.jetbrains.anko.toast import java.io.File -import java.io.FileNotFoundException class ReplaceRuleActivity : VMBaseActivity(R.layout.activity_replace_rule), @@ -177,7 +175,8 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi R.id.menu_del_selection -> viewModel.delSelection(adapter.getSelection()) R.id.menu_import_source_onLine -> showImportDialog() - R.id.menu_import_source_local -> selectFileSys() + R.id.menu_import_source_local -> FilePicker + .selectFile(this, importSource, "text/*", arrayOf("txt", "json")) } return super.onCompatOptionsItemSelected(item) } @@ -233,34 +232,6 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi }.show().applyTint() } - private fun selectFileSys() { - try { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - intent.type = "text/*"//设置类型 - startActivityForResult(intent, importSource) - } catch (e: Exception) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFile() - } - .request() - } - } - - private fun selectFile() { - FileChooserDialog.show( - supportFragmentManager, importSource, - allowExtensions = arrayOf("txt", "json") - ) - } - override fun onFilePicked(requestCode: Int, currentPath: String) { if (requestCode == importSource) { Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() @@ -292,17 +263,6 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi title_bar.snackbar(msg) } } - } catch (e: FileNotFoundException) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFileSys() - } - .request() } catch (e: Exception) { toast(e.localizedMessage ?: "ERROR") } diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt index 22277476e..b26cfe8f7 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt @@ -20,12 +20,11 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.data.entities.RssSource import io.legado.app.help.ItemTouchCallback -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat import io.legado.app.lib.dialogs.* import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.qrcode.QrCodeActivity import io.legado.app.ui.rss.source.edit.RssSourceEditActivity import io.legado.app.ui.widget.SelectActionBar @@ -38,7 +37,6 @@ import kotlinx.android.synthetic.main.view_search.* import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivityForResult import org.jetbrains.anko.toast -import java.io.FileNotFoundException class RssSourceActivity : VMBaseActivity(R.layout.activity_rss_source), @@ -79,7 +77,8 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.menu_add -> startActivity() - R.id.menu_import_source_local -> selectFileSys() + R.id.menu_import_source_local -> FilePicker + .selectFile(this, importSource, "text/*", arrayOf("txt", "json")) R.id.menu_import_source_onLine -> showImportDialog() R.id.menu_import_source_qr -> startActivityForResult(qrRequestCode) R.id.menu_group_manage -> GroupManageDialog() @@ -248,33 +247,6 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r }.show().applyTint() } - private fun selectFileSys() { - try { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.type = "text/*"//设置类型 - startActivityForResult(intent, importSource) - } catch (e: Exception) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFile() - } - .request() - } - } - - private fun selectFile() { - FileChooserDialog.show( - supportFragmentManager, importSource, - allowExtensions = arrayOf("txt", "json") - ) - } - override fun onFilePicked(requestCode: Int, currentPath: String) { if (requestCode == importSource) { Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() @@ -297,17 +269,6 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r title_bar.snackbar(msg) } } - } catch (e: FileNotFoundException) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFileSys() - } - .request() } catch (e: Exception) { toast(e.localizedMessage ?: "ERROR") } diff --git a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt index 87d87e8d6..bbeb7f04e 100644 --- a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt +++ b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt @@ -23,13 +23,13 @@ import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.PermissionsCompat import io.legado.app.lib.dialogs.alert import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.utils.* import kotlinx.android.synthetic.main.dialog_font_select.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.jetbrains.anko.toast import java.io.File class FontSelectDialog : BaseDialogFragment(), @@ -107,41 +107,11 @@ class FontSelectDialog : BaseDialogFragment(), private fun openFolder() { launch(Main) { - alert { - titleResource = R.string.select_folder - items(resources.getStringArray(R.array.select_folder).toList()) { _, index -> - when (index) { - 0 -> { - val path = "${FileUtils.getSdCardPath()}${File.separator}Fonts" - putPrefString(PreferKey.fontFolder, path) - getFontFilesByPermission(path) - } - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - startActivityForResult(intent, fontFolderRequestCode) - } catch (e: java.lang.Exception) { - e.printStackTrace() - requireContext().toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> { - PermissionsCompat.Builder(this@FontSelectDialog) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - childFragmentManager, - fontFolderRequestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - .request() - } - } - } - }.show() + FilePicker.selectFolder(this@FontSelectDialog, fontFolderRequestCode) { + val path = "${FileUtils.getSdCardPath()}${File.separator}Fonts" + putPrefString(PreferKey.fontFolder, path) + getFontFilesByPermission(path) + } } } From a7b574af4d9f389fb6fc6533bb7aa37503d39998 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 17:42:08 +0800 Subject: [PATCH 11/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/source/manage/BookSourceActivity.kt | 8 ++-- .../ui/rss/source/manage/RssSourceActivity.kt | 38 +++++++++++++++---- .../rss/source/manage/RssSourceViewModel.kt | 21 ++++++++-- .../java/io/legado/app/utils/UriExtensions.kt | 27 ++++--------- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index 49405ecbf..c05294965 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -50,7 +50,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity get() = getViewModel(BookSourceViewModel::class.java) private val importRecordKey = "bookSourceRecordKey" private val qrRequestCode = 101 - private val importSource = 132 + private val importRequestCode = 132 private val exportRequestCode = 65 private lateinit var adapter: BookSourceAdapter private var bookSourceLiveDate: LiveData>? = null @@ -84,7 +84,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity R.id.menu_group_manage -> GroupManageDialog().show(supportFragmentManager, "groupManage") R.id.menu_import_source_local -> FilePicker - .selectFile(this, importSource, "text/*", arrayOf("txt", "json")) + .selectFile(this, importRequestCode, "text/*", arrayOf("txt", "json")) R.id.menu_import_source_onLine -> showImportDialog() } if (item.groupId == R.id.source_group) { @@ -278,7 +278,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity adapter.getSelection(), File(currentPath) ) - importSource -> { + importRequestCode -> { Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() viewModel.importSourceFromFilePath(currentPath) { msg -> title_bar.snackbar(msg) @@ -298,7 +298,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity } } } - importSource -> if (resultCode == Activity.RESULT_OK) { + importRequestCode -> if (resultCode == Activity.RESULT_OK) { data?.data?.let { uri -> try { uri.readText(this)?.let { diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt index b26cfe8f7..c3dd008c8 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt @@ -9,6 +9,7 @@ import android.view.MenuItem import android.view.SubMenu import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView +import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import androidx.recyclerview.widget.DiffUtil @@ -37,6 +38,7 @@ import kotlinx.android.synthetic.main.view_search.* import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivityForResult import org.jetbrains.anko.toast +import java.io.File class RssSourceActivity : VMBaseActivity(R.layout.activity_rss_source), @@ -48,7 +50,8 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r get() = getViewModel(RssSourceViewModel::class.java) private val importRecordKey = "rssSourceRecordKey" private val qrRequestCode = 101 - private val importSource = 124 + private val importRequestCode = 124 + private val exportRequestCode = 65 private lateinit var adapter: RssSourceAdapter private var sourceLiveData: LiveData>? = null private var groups = hashSetOf() @@ -78,7 +81,7 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r when (item.itemId) { R.id.menu_add -> startActivity() R.id.menu_import_source_local -> FilePicker - .selectFile(this, importSource, "text/*", arrayOf("txt", "json")) + .selectFile(this, importRequestCode, "text/*", arrayOf("txt", "json")) R.id.menu_import_source_onLine -> showImportDialog() R.id.menu_import_source_qr -> startActivityForResult(qrRequestCode) R.id.menu_group_manage -> GroupManageDialog() @@ -95,7 +98,7 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r R.id.menu_enable_selection -> viewModel.enableSelection(adapter.getSelection()) R.id.menu_disable_selection -> viewModel.disableSelection(adapter.getSelection()) R.id.menu_del_selection -> viewModel.delSelection(adapter.getSelection()) - R.id.menu_export_selection -> viewModel.exportSelection(adapter.getSelection()) + R.id.menu_export_selection -> FilePicker.selectFolder(this, exportRequestCode) R.id.menu_check_source -> { } } @@ -248,18 +251,24 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r } override fun onFilePicked(requestCode: Int, currentPath: String) { - if (requestCode == importSource) { - Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() - viewModel.importSourceFromFilePath(currentPath) { msg -> - title_bar.snackbar(msg) + when (requestCode) { + importRequestCode -> { + Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() + viewModel.importSourceFromFilePath(currentPath) { msg -> + title_bar.snackbar(msg) + } } + exportRequestCode -> viewModel.exportSelection( + adapter.getSelection(), + File(currentPath) + ) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { - importSource -> if (resultCode == Activity.RESULT_OK) { + importRequestCode -> if (resultCode == Activity.RESULT_OK) { data?.data?.let { uri -> try { uri.readText(this)?.let { @@ -282,6 +291,19 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r } } } + exportRequestCode -> if (resultCode == RESULT_OK) { + data?.data?.let { uri -> + if (uri.toString().isContentPath()) { + DocumentFile.fromTreeUri(this, uri)?.let { + viewModel.exportSelection(adapter.getSelection(), it) + } + } else { + uri.path?.let { + viewModel.exportSelection(adapter.getSelection(), File(it)) + } + } + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt index bb9e5f219..7a64ea382 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.rss.source.manage import android.app.Application import android.text.TextUtils +import androidx.documentfile.provider.DocumentFile import com.jayway.jsonpath.JsonPath import io.legado.app.App import io.legado.app.R @@ -67,12 +68,24 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application) } } - fun exportSelection(sources: LinkedHashSet) { + fun exportSelection(sources: LinkedHashSet, file: File) { execute { val json = GSON.toJson(sources) - val file = - FileUtils.createFileIfNotExist(Backup.exportPath + File.separator + "exportRssSource.json") - file.writeText(json) + FileUtils.createFileIfNotExist(file, "exportRssSource.json") + .writeText(json) + }.onSuccess { + context.toast("成功导出至\n${Backup.exportPath}") + }.onError { + context.toast("导出失败\n${it.localizedMessage}") + } + } + + fun exportSelection(sources: LinkedHashSet, doc: DocumentFile) { + execute { + val json = GSON.toJson(sources) + doc.findFile("exportBookSource.json")?.delete() + doc.createFile("", "exportBookSource.json") + ?.writeText(context, json) }.onSuccess { context.toast("成功导出至\n${Backup.exportPath}") }.onError { diff --git a/app/src/main/java/io/legado/app/utils/UriExtensions.kt b/app/src/main/java/io/legado/app/utils/UriExtensions.kt index 672259dc8..583f699f7 100644 --- a/app/src/main/java/io/legado/app/utils/UriExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/UriExtensions.kt @@ -19,19 +19,17 @@ fun Uri.readBytes(context: Context): ByteArray? { @Throws(Exception::class) fun Uri.readText(context: Context): String? { - if (this.toString().isContentPath()) { - return DocumentUtils.readText(context, this) - } else { - val path = RealPathUtil.getPath(context, this) - if (path?.isNotEmpty() == true) { - return File(path).readText() - } + readBytes(context)?.let { + return String(it) } return null } @Throws(Exception::class) -fun Uri.writeBytes(context: Context, byteArray: ByteArray): Boolean { +fun Uri.writeBytes( + context: Context, + byteArray: ByteArray +): Boolean { if (this.toString().isContentPath()) { return DocumentUtils.writeBytes(context, byteArray, this) } else { @@ -46,14 +44,5 @@ fun Uri.writeBytes(context: Context, byteArray: ByteArray): Boolean { @Throws(Exception::class) fun Uri.writeText(context: Context, text: String): Boolean { - if (this.toString().isContentPath()) { - return DocumentUtils.writeText(context, text, this) - } else { - val path = RealPathUtil.getPath(context, this) - if (path?.isNotEmpty() == true) { - File(path).writeText(text) - return true - } - } - return false -} \ No newline at end of file + return writeBytes(context, text.toByteArray()) +} From 91cd8cd3562ab46f4a45566e04921afb443e692c Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 18:19:58 +0800 Subject: [PATCH 12/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/replacerule/ReplaceRuleActivity.kt | 47 ++++++++++++++----- .../ui/replacerule/ReplaceRuleViewModel.kt | 28 +++++++---- .../rss/source/manage/RssSourceViewModel.kt | 4 +- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt index 940173bd9..7910b9cb8 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt @@ -9,6 +9,7 @@ import android.view.MenuItem import android.view.SubMenu import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView +import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import androidx.recyclerview.widget.DiffUtil @@ -45,7 +46,8 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi override val viewModel: ReplaceRuleViewModel get() = getViewModel(ReplaceRuleViewModel::class.java) private val importRecordKey = "replaceRuleRecordKey" - private val importSource = 132 + private val importRequestCode = 132 + private val exportRequestCode = 65 private lateinit var adapter: ReplaceRuleAdapter private var groups = hashSetOf() private var groupMenu: SubMenu? = null @@ -176,7 +178,7 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi R.id.menu_del_selection -> viewModel.delSelection(adapter.getSelection()) R.id.menu_import_source_onLine -> showImportDialog() R.id.menu_import_source_local -> FilePicker - .selectFile(this, importSource, "text/*", arrayOf("txt", "json")) + .selectFile(this, importRequestCode, "text/*", arrayOf("txt", "json")) } return super.onCompatOptionsItemSelected(item) } @@ -185,7 +187,7 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi when (item?.itemId) { R.id.menu_enable_selection -> viewModel.enableSelection(adapter.getSelection()) R.id.menu_disable_selection -> viewModel.disableSelection(adapter.getSelection()) - R.id.menu_export_selection -> viewModel.exportSelection(adapter.getSelection()) + R.id.menu_export_selection -> FilePicker.selectFolder(this, exportRequestCode) } return false } @@ -232,15 +234,6 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi }.show().applyTint() } - override fun onFilePicked(requestCode: Int, currentPath: String) { - if (requestCode == importSource) { - Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() - viewModel.importSource(File(currentPath).readText()) { msg -> - title_bar.snackbar(msg) - } - } - } - override fun onQueryTextChange(newText: String?): Boolean { observeReplaceRuleData("%$newText%") return false @@ -250,10 +243,25 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi return false } + override fun onFilePicked(requestCode: Int, currentPath: String) { + when (requestCode) { + importRequestCode -> { + Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() + viewModel.importSource(File(currentPath).readText()) { msg -> + title_bar.snackbar(msg) + } + } + exportRequestCode -> viewModel.exportSelection( + adapter.getSelection(), + File(currentPath) + ) + } + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { - importSource -> if (resultCode == Activity.RESULT_OK) { + importRequestCode -> if (resultCode == Activity.RESULT_OK) { data?.data?.let { uri -> try { uri.readText(this)?.let { @@ -268,6 +276,19 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi } } } + exportRequestCode -> if (resultCode == RESULT_OK) { + data?.data?.let { uri -> + if (uri.toString().isContentPath()) { + DocumentFile.fromTreeUri(this, uri)?.let { + viewModel.exportSelection(adapter.getSelection(), it) + } + } else { + uri.path?.let { + viewModel.exportSelection(adapter.getSelection(), File(it)) + } + } + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt index 42217ec18..a63d7ea38 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.replacerule import android.app.Application import android.text.TextUtils +import androidx.documentfile.provider.DocumentFile import io.legado.app.App import io.legado.app.R import io.legado.app.base.BaseViewModel @@ -9,10 +10,7 @@ import io.legado.app.data.entities.ReplaceRule import io.legado.app.help.http.HttpHelper import io.legado.app.help.storage.Backup import io.legado.app.help.storage.ImportOldData -import io.legado.app.utils.FileUtils -import io.legado.app.utils.GSON -import io.legado.app.utils.isAbsUrl -import io.legado.app.utils.splitNotBlank +import io.legado.app.utils.* import org.jetbrains.anko.toast import java.io.File @@ -89,12 +87,24 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application } } - fun exportSelection(rules: LinkedHashSet) { + fun exportSelection(sources: LinkedHashSet, file: File) { execute { - val json = GSON.toJson(rules) - val file = - FileUtils.createFileIfNotExist(Backup.exportPath + File.separator + "exportReplaceRule.json") - file.writeText(json) + val json = GSON.toJson(sources) + FileUtils.createFileIfNotExist(file, "exportReplaceRule.json") + .writeText(json) + }.onSuccess { + context.toast("成功导出至\n${Backup.exportPath}") + }.onError { + context.toast("导出失败\n${it.localizedMessage}") + } + } + + fun exportSelection(sources: LinkedHashSet, doc: DocumentFile) { + execute { + val json = GSON.toJson(sources) + doc.findFile("exportReplaceRule.json")?.delete() + doc.createFile("", "exportReplaceRule.json") + ?.writeText(context, json) }.onSuccess { context.toast("成功导出至\n${Backup.exportPath}") }.onError { diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt index 7a64ea382..9160155b3 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt @@ -83,8 +83,8 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application) fun exportSelection(sources: LinkedHashSet, doc: DocumentFile) { execute { val json = GSON.toJson(sources) - doc.findFile("exportBookSource.json")?.delete() - doc.createFile("", "exportBookSource.json") + doc.findFile("exportRssSource.json")?.delete() + doc.createFile("", "exportRssSource.json") ?.writeText(context, json) }.onSuccess { context.toast("成功导出至\n${Backup.exportPath}") From 6ce554173d55ccb9381f393b8137059fe54ae3eb Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 20:42:22 +0800 Subject: [PATCH 13/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/model/webBook/BookChapterList.kt | 88 +++++++++++++------ .../legado/app/ui/filechooser/FilePicker.kt | 8 +- 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt index aa0b7de74..d5c4cbb66 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt @@ -95,35 +95,21 @@ object BookChapterList { } Debug.log(bookSource.bookSourceUrl, "◇目录总页数:${nextUrlList.size}") for (item in chapterDataList) { - Coroutine.async(scope = coroutineScope) { - val nextBody = AnalyzeUrl( - ruleUrl = item.nextUrl, - book = book, - headerMapF = bookSource.getHeaderMap() - ).getResponseAwait().body - val nextChapterData = analyzeChapterList( - analyzeRule.setContent(nextBody, item.nextUrl), - book.bookUrl, item.nextUrl, tocRule, listRule, bookSource, - false - ) - synchronized(chapterDataList) { - val isFinished = addChapterListIsFinish( - chapterDataList, - item, - nextChapterData.chapterList - ) - if (isFinished) { - chapterDataList.forEach { item -> - item.chapterList?.let { - chapterList.addAll(it) - } - } - block.resume(finish(book, chapterList, reverse)) - } - } - }.onError { - block.resumeWithException(it) - } + downloadToc( + coroutineScope, + item, + book, + bookSource, + analyzeRule, + tocRule, + listRule, + chapterList, + chapterDataList, + { + block.resume(finish(book, chapterList, reverse)) + }, { + block.cancel(it) + }) } } } @@ -132,6 +118,50 @@ object BookChapterList { } } + private fun downloadToc( + coroutineScope: CoroutineScope, + chapterData: ChapterData, + book: Book, + bookSource: BookSource, + analyzeRule: AnalyzeRule, + tocRule: TocRule, + listRule: String, + chapterList: ArrayList, + chapterDataList: ArrayList>, + onFinish: () -> Unit, + onError: (e: Throwable) -> Unit + ) { + Coroutine.async(scope = coroutineScope) { + val nextBody = AnalyzeUrl( + ruleUrl = chapterData.nextUrl, + book = book, + headerMapF = bookSource.getHeaderMap() + ).getResponseAwait().body + val nextChapterData = analyzeChapterList( + analyzeRule.setContent(nextBody, chapterData.nextUrl), + book.bookUrl, chapterData.nextUrl, tocRule, listRule, bookSource, + false + ) + synchronized(chapterDataList) { + val isFinished = addChapterListIsFinish( + chapterDataList, + chapterData, + nextChapterData.chapterList + ) + if (isFinished) { + chapterDataList.forEach { item -> + item.chapterList?.let { + chapterList.addAll(it) + } + } + onFinish() + } + } + }.onError { + onError(it) + } + } + private fun addChapterListIsFinish( chapterDataList: ArrayList>, chapterData: ChapterData, diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt index 0b81368a1..772add3e0 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt @@ -21,7 +21,7 @@ object FilePicker { selectList.removeAt(0) } items(selectList) { _, index -> - when (index) { + when (if (default == null) index - 1 else index) { 0 -> default?.invoke() 1 -> { try { @@ -60,7 +60,7 @@ object FilePicker { selectList.removeAt(0) } items(selectList) { _, index -> - when (index) { + when (if (default == null) index - 1 else index) { 0 -> default?.invoke() 1 -> { try { @@ -104,7 +104,7 @@ object FilePicker { selectList.removeAt(0) } items(selectList) { _, index -> - when (index) { + when (if (default == null) index - 1 else index) { 0 -> default?.invoke() 1 -> { try { @@ -152,7 +152,7 @@ object FilePicker { selectList.removeAt(0) } items(selectList) { _, index -> - when (index) { + when (if (default == null) index - 1 else index) { 0 -> default?.invoke() 1 -> { try { From e30fdbbe9fb258e81da7cdb90bf9d4da7ee4a8c4 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 20:44:51 +0800 Subject: [PATCH 14/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/ui/filechooser/FilePicker.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt index 772add3e0..affb430ad 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt @@ -21,7 +21,7 @@ object FilePicker { selectList.removeAt(0) } items(selectList) { _, index -> - when (if (default == null) index - 1 else index) { + when (if (default == null) index + 1 else index) { 0 -> default?.invoke() 1 -> { try { @@ -60,7 +60,7 @@ object FilePicker { selectList.removeAt(0) } items(selectList) { _, index -> - when (if (default == null) index - 1 else index) { + when (if (default == null) index + 1 else index) { 0 -> default?.invoke() 1 -> { try { @@ -104,7 +104,7 @@ object FilePicker { selectList.removeAt(0) } items(selectList) { _, index -> - when (if (default == null) index - 1 else index) { + when (if (default == null) index + 1 else index) { 0 -> default?.invoke() 1 -> { try { @@ -152,7 +152,7 @@ object FilePicker { selectList.removeAt(0) } items(selectList) { _, index -> - when (if (default == null) index - 1 else index) { + when (if (default == null) index + 1 else index) { 0 -> default?.invoke() 1 -> { try { From fe7c41a5698673f538f0aa81608cd3e425db8a44 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 21:24:14 +0800 Subject: [PATCH 15/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/model/webBook/BookChapterList.kt | 23 ++++++++----------- .../legado/app/model/webBook/BookContent.kt | 16 ++++++------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt index d5c4cbb66..e476eea28 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt @@ -31,7 +31,7 @@ object BookChapterList { App.INSTANCE.getString(R.string.error_get_web_content, baseUrl) ) Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") - val analyzeRule = AnalyzeRule(book) + val tocRule = bookSource.getTocRule() val nextUrlList = arrayListOf(baseUrl) var reverse = false @@ -45,8 +45,7 @@ object BookChapterList { } var chapterData = analyzeChapterList( - analyzeRule.setContent(body, baseUrl), - book.bookUrl, baseUrl, tocRule, listRule, bookSource, log = true + book, baseUrl, body, tocRule, listRule, bookSource, log = true ) chapterData.chapterList?.let { chapterList.addAll(it) @@ -67,8 +66,7 @@ object BookChapterList { ).getResponseAwait() .body?.let { nextBody -> chapterData = analyzeChapterList( - analyzeRule.setContent(nextBody, nextUrl), - book.bookUrl, nextUrl, tocRule, listRule, bookSource + book, nextUrl, nextBody, tocRule, listRule, bookSource ) nextUrl = if (chapterData.nextUrl.isNotEmpty()) { chapterData.nextUrl[0] @@ -100,7 +98,6 @@ object BookChapterList { item, book, bookSource, - analyzeRule, tocRule, listRule, chapterList, @@ -123,7 +120,6 @@ object BookChapterList { chapterData: ChapterData, book: Book, bookSource: BookSource, - analyzeRule: AnalyzeRule, tocRule: TocRule, listRule: String, chapterList: ArrayList, @@ -136,10 +132,9 @@ object BookChapterList { ruleUrl = chapterData.nextUrl, book = book, headerMapF = bookSource.getHeaderMap() - ).getResponseAwait().body + ).getResponseAwait().body ?: throw Exception("${chapterData.nextUrl}, 下载失败") val nextChapterData = analyzeChapterList( - analyzeRule.setContent(nextBody, chapterData.nextUrl), - book.bookUrl, chapterData.nextUrl, tocRule, listRule, bookSource, + book, chapterData.nextUrl, nextBody, tocRule, listRule, bookSource, false ) synchronized(chapterDataList) { @@ -203,15 +198,17 @@ object BookChapterList { } private fun analyzeChapterList( - analyzeRule: AnalyzeRule, - bookUrl: String, + book: Book, baseUrl: String, + body: String, tocRule: TocRule, listRule: String, bookSource: BookSource, getNextUrl: Boolean = true, log: Boolean = false ): ChapterData> { + val analyzeRule = AnalyzeRule(book) + analyzeRule.setContent(body, baseUrl) val chapterList = arrayListOf() val nextUrlList = arrayListOf() val nextTocRule = tocRule.nextTocUrl @@ -242,7 +239,7 @@ object BookChapterList { var isVip: String? for (item in elements) { analyzeRule.setContent(item) - val bookChapter = BookChapter(bookUrl = bookUrl) + val bookChapter = BookChapter(bookUrl = book.bookUrl) analyzeRule.chapter = bookChapter bookChapter.title = analyzeRule.getString(nameRule) bookChapter.url = analyzeRule.getString(urlRule, true) diff --git a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt index d8e874195..8ca67a6c5 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt @@ -33,13 +33,11 @@ object BookContent { ) ) Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") - val analyzeRule = AnalyzeRule(book) val content = StringBuilder() val nextUrlList = arrayListOf(baseUrl) val contentRule = bookSource.getContentRule() var contentData = analyzeContent( - analyzeRule.setContent(body, baseUrl), - contentRule, bookChapter, bookSource + book, baseUrl, body, contentRule, bookChapter, bookSource ) content.append(contentData.content.replace(bookChapter.title, "")) if (contentData.nextUrl.size == 1) { @@ -62,8 +60,7 @@ object BookContent { .body?.let { nextBody -> contentData = analyzeContent( - analyzeRule.setContent(nextBody, nextUrl), - contentRule, bookChapter, bookSource, false + book, nextUrl, nextBody, contentRule, bookChapter, bookSource, false ) nextUrl = if (contentData.nextUrl.isNotEmpty()) contentData.nextUrl[0] else "" @@ -87,8 +84,7 @@ object BookContent { .body?.let { contentData = analyzeContent( - analyzeRule.setContent(it, item.nextUrl), - contentRule, bookChapter, bookSource, false + book, item.nextUrl, it, contentRule, bookChapter, bookSource, false ) item.content = contentData.content } @@ -108,12 +104,16 @@ object BookContent { @Throws(Exception::class) private fun analyzeContent( - analyzeRule: AnalyzeRule, + book: Book, + baseUrl: String, + body: String, contentRule: ContentRule, chapter: BookChapter, bookSource: BookSource, printLog: Boolean = true ): ContentData> { + val analyzeRule = AnalyzeRule(book) + analyzeRule.setContent(body, baseUrl) val nextUrlList = arrayListOf() analyzeRule.chapter = chapter val nextUrlRule = contentRule.nextContentUrl From 3cd92830b53d0c2ce1c00c80f556cd5d8cc51bb5 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 21:31:15 +0800 Subject: [PATCH 16/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index aabd6c581..b984a2937 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -2,6 +2,9 @@ * 旧版数据导入教程: * 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +**2020/02/30** +* 修复书源解析的一个bug + **2020/02/29** * 添加书源一键导入 * 修复主题模式跟随系统 From 3bdb7bff7a127fa374eda75e738eb71a1678d4b2 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 21:44:20 +0800 Subject: [PATCH 17/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt | 2 ++ app/src/main/res/layout/activity_main.xml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt b/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt index 3aab86a50..079abc480 100644 --- a/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt +++ b/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt @@ -9,6 +9,7 @@ import androidx.annotation.MenuRes import androidx.annotation.StringRes import androidx.appcompat.widget.PopupMenu import io.legado.app.R +import io.legado.app.utils.dp import io.legado.app.utils.visible import kotlinx.android.synthetic.main.view_select_action_bar.view.* import org.jetbrains.anko.sdk27.listeners.onClick @@ -19,6 +20,7 @@ class SelectActionBar(context: Context, attrs: AttributeSet?) : FrameLayout(cont init { setBackgroundResource(R.color.background_menu) + elevation = 10.dp.toFloat() View.inflate(context, R.layout.view_select_action_bar, this) cb_selected_all.setOnCheckedChangeListener { buttonView, isChecked -> if (buttonView.isPressed) { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index a21e6de1c..9f2d2044e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ android:id="@+id/bottom_navigation_view" android:layout_width="match_parent" android:layout_height="wrap_content" - android:elevation="6dp" + android:elevation="10dp" android:background="@color/background" app:labelVisibilityMode="labeled" app:menu="@menu/main_bnv" From ce85b42e7b881b1d4af0132c7fb218ab1ead4bb2 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 22:43:52 +0800 Subject: [PATCH 18/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/App.kt | 22 +++++++++---------- .../main/java/io/legado/app/lib/theme/ATH.kt | 2 +- .../app/lib/theme/MaterialValueHelper.kt | 6 +++++ .../io/legado/app/lib/theme/ThemeStore.kt | 14 ++++++++++++ .../app/lib/theme/ThemeStoreInterface.kt | 4 ++++ .../app/lib/theme/ThemeStorePrefKeys.kt | 1 + .../app/ui/config/ThemeConfigFragment.kt | 10 +++++++-- .../legado/app/ui/widget/SelectActionBar.kt | 3 ++- app/src/main/res/xml/pref_config_theme.xml | 16 ++++++++++++++ 9 files changed, 62 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt index cf0f4cf43..95f29cd64 100644 --- a/app/src/main/java/io/legado/app/App.kt +++ b/app/src/main/java/io/legado/app/App.kt @@ -78,26 +78,24 @@ class App : Application() { ThemeStore.editTheme(this) .primaryColor( getPrefInt("colorPrimaryNight", getCompatColor(R.color.md_blue_grey_600)) - ) - .accentColor( + ).accentColor( getPrefInt("colorAccentNight", getCompatColor(R.color.md_brown_800)) - ) - .backgroundColor( + ).backgroundColor( getPrefInt("colorBackgroundNight", getCompatColor(R.color.shine_color)) - ) - .apply() + ).bottomBackground( + getPrefInt("colorBottomBackgroundNight", getCompatColor(R.color.md_grey_850)) + ).apply() } else { ThemeStore.editTheme(this) .primaryColor( getPrefInt("colorPrimary", getCompatColor(R.color.md_indigo_800)) - ) - .accentColor( + ).accentColor( getPrefInt("colorAccent", getCompatColor(R.color.md_red_600)) - ) - .backgroundColor( + ).backgroundColor( getPrefInt("colorBackground", getCompatColor(R.color.md_grey_100)) - ) - .apply() + ).bottomBackground( + getPrefInt("colorBottomBackground", getCompatColor(R.color.md_grey_200)) + ).apply() } } diff --git a/app/src/main/java/io/legado/app/lib/theme/ATH.kt b/app/src/main/java/io/legado/app/lib/theme/ATH.kt index e20741987..0bcbb114b 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ATH.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ATH.kt @@ -205,7 +205,7 @@ object ATH { .setSelectedColor(ThemeStore.accentColor(bottom_navigation_view.context)).create() itemIconTintList = colorStateList itemTextColor = colorStateList - itemBackgroundResource = R.color.background_menu + setBackgroundColor(ThemeStore.bottomBackground(context)) } } diff --git a/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt index 1618884b5..fab853c6b 100644 --- a/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt +++ b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt @@ -54,6 +54,9 @@ val Context.accentColor: Int val Context.backgroundColor: Int get() = ThemeStore.backgroundColor(this) +val Context.bottomBackground: Int + get() = ThemeStore.bottomBackground(this) + val Context.primaryTextColor: Int get() = getPrimaryTextColor(isDarkTheme) @@ -78,6 +81,9 @@ val Fragment.accentColor: Int val Fragment.backgroundColor: Int get() = ThemeStore.backgroundColor(requireContext()) +val Fragment.bottomBackground: Int + get() = ThemeStore.bottomBackground(requireContext()) + val Fragment.primaryTextColor: Int get() = requireContext().getPrimaryTextColor(isDarkTheme) diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt index 5335a6830..927fc2fb0 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt @@ -147,6 +147,11 @@ private constructor(private val mContext: Context) : ThemeStoreInterface { return this } + override fun bottomBackground(color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_BOTTOM_BACKGROUND, color) + return this + } + override fun coloredStatusBar(colored: Boolean): ThemeStore { mEditor.putBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARYDARK_STATUSBAR, colored) return this @@ -279,6 +284,15 @@ private constructor(private val mContext: Context) : ThemeStoreInterface { ) } + @CheckResult + @ColorInt + fun bottomBackground(context: Context): Int { + return prefs(context).getInt( + ThemeStorePrefKeys.KEY_BOTTOM_BACKGROUND, + ATHUtils.resolveColor(context, android.R.attr.colorBackground) + ) + } + @CheckResult fun coloredStatusBar(context: Context): Boolean { return prefs(context).getBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARYDARK_STATUSBAR, true) diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt index 55584b633..521b9d237 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt @@ -78,8 +78,12 @@ internal interface ThemeStoreInterface { fun textColorSecondaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore + // Background + fun backgroundColor(@ColorInt color: Int): ThemeStore + fun bottomBackground(@ColorInt color: Int): ThemeStore + // Toggle configurations fun coloredStatusBar(colored: Boolean): ThemeStore diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt index c934ef176..a2ab19d60 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt @@ -22,6 +22,7 @@ object ThemeStorePrefKeys { const val KEY_TEXT_COLOR_SECONDARY_INVERSE = "text_color_secondary_inverse" const val KEY_BACKGROUND_COLOR = "backgroundColor" + const val KEY_BOTTOM_BACKGROUND = "bottomBackground" const val KEY_APPLY_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar" const val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar" diff --git a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt index 1a1851ca1..0652350c5 100644 --- a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt @@ -52,7 +52,10 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar "transparentStatusBar" -> { recreateActivities() } - "colorPrimary", "colorAccent", "colorBackground" -> { + "colorPrimary", + "colorAccent", + "colorBackground", + "colorBottomBackground" -> { if (backgroundIsDark(sharedPreferences)) { alert { title = "白天背景太暗" @@ -73,7 +76,10 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar upTheme(false) } } - "colorPrimaryNight", "colorAccentNight", "colorBackgroundNight" -> { + "colorPrimaryNight", + "colorAccentNight", + "colorBackgroundNight", + "colorBottomBackgroundNight" -> { if (backgroundIsLight(sharedPreferences)) { alert { title = "夜间背景太亮" diff --git a/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt b/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt index 079abc480..dc37085b0 100644 --- a/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt +++ b/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt @@ -9,6 +9,7 @@ import androidx.annotation.MenuRes import androidx.annotation.StringRes import androidx.appcompat.widget.PopupMenu import io.legado.app.R +import io.legado.app.lib.theme.bottomBackground import io.legado.app.utils.dp import io.legado.app.utils.visible import kotlinx.android.synthetic.main.view_select_action_bar.view.* @@ -19,7 +20,7 @@ class SelectActionBar(context: Context, attrs: AttributeSet?) : FrameLayout(cont private var selMenu: PopupMenu? = null init { - setBackgroundResource(R.color.background_menu) + setBackgroundColor(context.bottomBackground) elevation = 10.dp.toFloat() View.inflate(context, R.layout.view_select_action_bar, this) cb_selected_all.setOnCheckedChangeListener { buttonView, isChecked -> diff --git a/app/src/main/res/xml/pref_config_theme.xml b/app/src/main/res/xml/pref_config_theme.xml index 3380e4669..fe0d24ca2 100644 --- a/app/src/main/res/xml/pref_config_theme.xml +++ b/app/src/main/res/xml/pref_config_theme.xml @@ -60,6 +60,14 @@ app:cpv_dialogType="preset" app:iconSpaceReserved="false" /> + + + + \ No newline at end of file From 15516c7683d872c1a40e99e4c7dab269c96a413d Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 22:44:40 +0800 Subject: [PATCH 19/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index b984a2937..ef873cb12 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -4,6 +4,7 @@ **2020/02/30** * 修复书源解析的一个bug +* 添加底部操作栏颜色配置 **2020/02/29** * 添加书源一键导入 From 4d621485d395b49087cc40f8dc7482fa5ad5ae2a Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 23:17:04 +0800 Subject: [PATCH 20/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values/colors.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 054222265..d6999a7ae 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -15,7 +15,7 @@ @color/md_grey_50 @color/md_grey_100 - @color/md_grey_300 + @color/md_grey_200 #00000000 #30000000 From 66b88fab2897d7f122715b4fe9532f1cdfdf27e6 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 29 Feb 2020 23:29:05 +0800 Subject: [PATCH 21/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/page/ContentTextView.kt | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt index d17e55a67..c6f719c9d 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt @@ -163,24 +163,20 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at } pageOffset += offset - if (pageOffset > 0) { - if (!pageFactory.hasPrev()) { - pageOffset = 0f - } else { - pageFactory.moveToPrev() - textPage = pageFactory.currentPage - pageOffset -= textPage.height - upView?.invoke(textPage) - } + if (!pageFactory.hasPrev() && pageOffset > 0) { + pageOffset = 0f + } else if (!pageFactory.hasNext() && pageOffset < 0) { + pageOffset = 0f + } else if (pageOffset > 0) { + pageFactory.moveToPrev() + textPage = pageFactory.currentPage + pageOffset -= textPage.height + upView?.invoke(textPage) } else if (pageOffset < -textPage.height) { - if (!pageFactory.hasNext()) { - pageOffset = -textPage.height.toFloat() - } else { - pageOffset += textPage.height - pageFactory.moveToNext() - textPage = pageFactory.currentPage - upView?.invoke(textPage) - } + pageOffset += textPage.height + pageFactory.moveToNext() + textPage = pageFactory.currentPage + upView?.invoke(textPage) } invalidate() } From 3bf94f8cc0476efd34fcc52053fde34caa5a5181 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 10:30:43 +0800 Subject: [PATCH 22/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/ui/book/read/page/TextPageFactory.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt b/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt index fe18f1b26..8d08a45ca 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt @@ -68,6 +68,9 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource ?: TextPage(title = it.title).format() } } + if (!hasNextChapter()) { + return@with TextPage(text = "") + } nextChapter?.let { return@with it.page(0)?.removePageAloudSpan() ?: TextPage(title = it.title).format() @@ -83,6 +86,9 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource ?: TextPage(title = it.title).format() } } + if (!hasPrevChapter()) { + return@with TextPage(text = "") + } prevChapter?.let { return@with it.lastPage()?.removePageAloudSpan() ?: TextPage(title = it.title).format() @@ -97,6 +103,9 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource return@with it.page(pageIndex + 2)?.removePageAloudSpan() ?: TextPage(title = it.title).format() } + if (!hasNextChapter()) { + TextPage(text = "") + } nextChapter?.let { nc -> if (pageIndex < it.pageSize() - 1) { return@with nc.page(0)?.removePageAloudSpan() From cacbf2f8a4e6bbdf2b726e8c63c533918f098d10 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 10:37:06 +0800 Subject: [PATCH 23/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/page/delegate/ScrollPageDelegate.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt index 368a9ad8a..a3434aeed 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.book.read.page.delegate import android.view.MotionEvent import android.view.VelocityTracker +import io.legado.app.ui.book.read.page.ChapterProvider import io.legado.app.ui.book.read.page.PageView import kotlin.math.abs @@ -60,10 +61,10 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) { } override fun nextPageByAnim() { - + startScroll(0, 0, 0, -ChapterProvider.visibleHeight) } override fun prevPageByAnim() { - + startScroll(0, 0, 0, ChapterProvider.visibleHeight) } } \ No newline at end of file From f461806b0d1718c34681bb448954e0c65e34959b Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 10:57:19 +0800 Subject: [PATCH 24/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index ef873cb12..44fae7557 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -1,6 +1,7 @@ ## 更新日志 * 旧版数据导入教程: * 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +* 请关注[开源阅读软件]()支持我,同时可关注合作公众号[小说拾遗](),阅读公众号小编 **2020/02/30** * 修复书源解析的一个bug From f1caa4abb14ab8f763fac5a241d5036158389ab6 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 11:01:14 +0800 Subject: [PATCH 25/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/ui/config/BackupRestoreUi.kt | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt index 7f094ebc4..36715bcf8 100644 --- a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt +++ b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt @@ -78,9 +78,7 @@ object BackupRestoreUi { } fun selectBackupFolder(fragment: Fragment, requestCode: Int = selectFolderRequestCode) { - FilePicker.selectFolder(fragment, requestCode) { - backupUsePermission(fragment, requestCode = requestCode) - } + FilePicker.selectFolder(fragment, requestCode) } fun restore(fragment: Fragment) { @@ -97,13 +95,13 @@ object BackupRestoreUi { Restore.restore(fragment.requireContext(), backupPath) fragment.toast(R.string.restore_success) } else { - selectRestoreFolder(fragment) + selectBackupFolder(fragment, restoreSelectRequestCode) } } else { restoreUsePermission(fragment, backupPath) } } else { - selectRestoreFolder(fragment) + selectBackupFolder(fragment, restoreSelectRequestCode) } } } @@ -124,12 +122,6 @@ object BackupRestoreUi { .request() } - private fun selectRestoreFolder(fragment: Fragment) { - FilePicker.selectFolder(fragment, restoreSelectRequestCode) { - restoreUsePermission(fragment) - } - } - fun importOldData(fragment: Fragment) { FilePicker.selectFolder(fragment, oldDataRequestCode) { importOldUsePermission(fragment) From 51db1e5cfb67da847f6d87c16a325b6fbe372dc3 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 11:03:13 +0800 Subject: [PATCH 26/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 44fae7557..454201f18 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -1,7 +1,7 @@ ## 更新日志 * 旧版数据导入教程: * 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 -* 请关注[开源阅读软件]()支持我,同时可关注合作公众号[小说拾遗](),阅读公众号小编 +* 请关注[开源阅读软件]()支持我,同时可关注合作公众号[小说拾遗](),阅读公众号小编。 **2020/02/30** * 修复书源解析的一个bug From b442ae137354e67b85ec046407a8187c79c40c85 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 11:03:47 +0800 Subject: [PATCH 27/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 454201f18..4314ba246 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -6,6 +6,7 @@ **2020/02/30** * 修复书源解析的一个bug * 添加底部操作栏颜色配置 +* 修复滚动点击翻页 **2020/02/29** * 添加书源一键导入 From baf41265053ed93ce09b27d0fbad2e172b99bd3a Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 11:05:34 +0800 Subject: [PATCH 28/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 4314ba246..ce72eff69 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -1,12 +1,13 @@ ## 更新日志 * 旧版数据导入教程: * 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 -* 请关注[开源阅读软件]()支持我,同时可关注合作公众号[小说拾遗](),阅读公众号小编。 +* 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 **2020/02/30** * 修复书源解析的一个bug * 添加底部操作栏颜色配置 * 修复滚动点击翻页 +* 去除备份恢复默认路径 **2020/02/29** * 添加书源一键导入 From 8c1aa1e5e284f0997d2f21dbadb0f9cfceef9562 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 11:07:08 +0800 Subject: [PATCH 29/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index ce72eff69..7044f0163 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -6,7 +6,7 @@ **2020/02/30** * 修复书源解析的一个bug * 添加底部操作栏颜色配置 -* 修复滚动点击翻页 +* 修复滚动点击翻页,修复滚动最后一页显示加载中 * 去除备份恢复默认路径 **2020/02/29** From 3d2e03cbd644ad6693eb36617db64fa29acd5eac Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 11:08:26 +0800 Subject: [PATCH 30/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 7044f0163..27ad880e6 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -8,6 +8,7 @@ * 添加底部操作栏颜色配置 * 修复滚动点击翻页,修复滚动最后一页显示加载中 * 去除备份恢复默认路径 +* 翻页还有些bug不用反馈了,我已经知道,会修复的 **2020/02/29** * 添加书源一键导入 From 1df26fd14909d86848c0d61e4fda8eceb2baea60 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 11:20:54 +0800 Subject: [PATCH 31/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + .../java/io/legado/app/ui/rss/read/ReadRssActivity.kt | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 27ad880e6..d73409a48 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -8,6 +8,7 @@ * 添加底部操作栏颜色配置 * 修复滚动点击翻页,修复滚动最后一页显示加载中 * 去除备份恢复默认路径 +* 尝试修复部分手机一键导入书源报错 * 翻页还有些bug不用反馈了,我已经知道,会修复的 **2020/02/29** diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index 255505178..9ad7a9cf0 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -78,6 +78,17 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r } return true } + + @Suppress("DEPRECATION") + override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { + if (url?.startsWith("http", true) == true) { + return false + } + url?.let { + openUrl(it) + } + return true + } } webView.settings.apply { mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW From 8f93096ae18baad6a883d337eea8ccc2feac6783 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 12:12:47 +0800 Subject: [PATCH 32/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt index 3a5924780..a347b0c05 100644 --- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt @@ -40,7 +40,7 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { else -> null } } - root_view.postDelayed({ startMainActivity() }, 200) + root_view.postDelayed({ startMainActivity() }, 300) } private fun startMainActivity() { From 975ff99538c0c41d8ed7c6be348359ca0de99e9a Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 12:36:31 +0800 Subject: [PATCH 33/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index d73409a48..abe12be04 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -3,7 +3,7 @@ * 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 -**2020/02/30** +**2020/01/01** * 修复书源解析的一个bug * 添加底部操作栏颜色配置 * 修复滚动点击翻页,修复滚动最后一页显示加载中 From 263b3e28437f93648dd40f7740ffde38ab930b47 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 12:48:01 +0800 Subject: [PATCH 34/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index abe12be04..2de034c99 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -1,6 +1,5 @@ ## 更新日志 -* 旧版数据导入教程: -* 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 **2020/01/01** From 78781ff7574b9d0fb883c1c7c3702181093f3780 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 13:09:54 +0800 Subject: [PATCH 35/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/help/http/AjaxWebView.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt b/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt index 81d0347b8..946b43f09 100644 --- a/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt +++ b/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt @@ -9,7 +9,11 @@ import android.os.Message import android.text.TextUtils import android.webkit.* import io.legado.app.App +import io.legado.app.R import io.legado.app.constant.AppConst +import io.legado.app.lib.dialogs.alert +import io.legado.app.lib.dialogs.cancelButton +import io.legado.app.lib.dialogs.okButton import org.apache.commons.text.StringEscapeUtils import java.lang.ref.WeakReference @@ -169,7 +173,11 @@ class AjaxWebView { } override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { - handler.proceed() + App.INSTANCE.alert(titleResource = R.string.sure) { + message = "${view.url}证书不匹配,是否继续访问" + okButton { handler.proceed() } + cancelButton { handler.cancel() } + }.show() } } @@ -243,7 +251,11 @@ class AjaxWebView { } override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { - handler.proceed() + App.INSTANCE.alert(titleResource = R.string.sure) { + message = "${view.url}证书不匹配,是否继续访问" + okButton { handler.proceed() } + cancelButton { handler.cancel() } + }.show() } override fun onPageFinished(view: WebView, url: String) { From 0155f20451a07b1a3fab4a7a051a194e07324b5c Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 13:14:59 +0800 Subject: [PATCH 36/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/help/http/AjaxWebView.kt | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt b/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt index 946b43f09..f501fcb71 100644 --- a/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt +++ b/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt @@ -1,7 +1,6 @@ package io.legado.app.help.http import android.annotation.SuppressLint -import android.net.http.SslError import android.os.Build import android.os.Handler import android.os.Looper @@ -9,11 +8,7 @@ import android.os.Message import android.text.TextUtils import android.webkit.* import io.legado.app.App -import io.legado.app.R import io.legado.app.constant.AppConst -import io.legado.app.lib.dialogs.alert -import io.legado.app.lib.dialogs.cancelButton -import io.legado.app.lib.dialogs.okButton import org.apache.commons.text.StringEscapeUtils import java.lang.ref.WeakReference @@ -171,14 +166,6 @@ class AjaxWebView { ).sendToTarget() } } - - override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { - App.INSTANCE.alert(titleResource = R.string.sure) { - message = "${view.url}证书不匹配,是否继续访问" - okButton { handler.proceed() } - cancelButton { handler.cancel() } - }.show() - } } private class EvalJsRunnable( @@ -250,14 +237,6 @@ class AjaxWebView { } } - override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { - App.INSTANCE.alert(titleResource = R.string.sure) { - message = "${view.url}证书不匹配,是否继续访问" - okButton { handler.proceed() } - cancelButton { handler.cancel() } - }.show() - } - override fun onPageFinished(view: WebView, url: String) { params.setCookie(url) if (params.hasJavaScript()) { From 5e59b73c175dfc5c75a17672617a30dad5a7118d Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 14:14:10 +0800 Subject: [PATCH 37/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../source/debug/BookSourceDebugActivity.kt | 2 +- .../io/legado/app/ui/login/SourceLogin.kt | 67 +++++++++++++++++++ .../legado/app/ui/rss/read/ReadRssActivity.kt | 34 +++++++--- app/src/main/res/layout/activity_rss_read.xml | 2 +- .../main/res/layout/activity_source_login.xml | 6 +- app/src/main/res/menu/source_debug.xml | 2 +- app/src/main/res/menu/source_login.xml | 11 +++ 7 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 app/src/main/res/menu/source_login.xml diff --git a/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt index dafeaa218..75ac065e0 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt @@ -83,7 +83,7 @@ class BookSourceDebugActivity : override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.action_scan -> { + R.id.menu_scan -> { startActivityForResult(qrRequestCode) } } diff --git a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt b/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt index d4e9c2e4e..05be53788 100644 --- a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt +++ b/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt @@ -1,14 +1,81 @@ package io.legado.app.ui.login +import android.annotation.SuppressLint +import android.graphics.Bitmap import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.webkit.CookieManager +import android.webkit.WebView +import android.webkit.WebViewClient +import io.legado.app.App import io.legado.app.R import io.legado.app.base.BaseActivity +import io.legado.app.data.entities.Cookie +import io.legado.app.utils.snackbar +import kotlinx.android.synthetic.main.activity_source_login.* class SourceLogin : BaseActivity(R.layout.activity_source_login) { + var sourceUrl: String? = null + var loginUrl: String? = null + var checking = false + override fun onActivityCreated(savedInstanceState: Bundle?) { + sourceUrl = intent.getStringExtra("sourceUrl") + loginUrl = intent.getStringExtra("loginUrl") + title = getString(R.string.login_source, sourceUrl) + initWebView() + } + + @SuppressLint("SetJavaScriptEnabled") + private fun initWebView() { + val settings = web_view.settings + settings.setSupportZoom(true) + settings.builtInZoomControls = true + settings.javaScriptEnabled = true + val cookieManager = CookieManager.getInstance() + web_view.webViewClient = object : WebViewClient() { + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + val cookie = cookieManager.getCookie(url) + saveCookie(cookie) + super.onPageStarted(view, url, favicon) + } + + override fun onPageFinished(view: WebView?, url: String?) { + val cookie = cookieManager.getCookie(url) + saveCookie(cookie) + if (checking) { + finish() + } + super.onPageFinished(view, url) + } + } + web_view.loadUrl(loginUrl) + } + + override fun onCompatCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.source_login, menu) + return super.onCompatCreateOptionsMenu(menu) + } + override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.menu_success -> { + if (!checking) { + checking = true + title_bar.snackbar(R.string.check_host_cookie) + web_view.loadUrl(sourceUrl) + } + } + } + return super.onCompatOptionsItemSelected(item) } + private fun saveCookie(cookie: String) { + sourceUrl?.let { + App.db.cookieDao().insert(Cookie(it, cookie)) + } + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index 9ad7a9cf0..10504efd7 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -65,7 +65,7 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r } private fun initWebView() { - webView.webViewClient = object : WebViewClient() { + web_view.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading( view: WebView?, request: WebResourceRequest? @@ -90,7 +90,7 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r return true } } - webView.settings.apply { + web_view.settings.apply { mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW domStorageEnabled = true allowContentAccess = true @@ -105,23 +105,35 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r val url = NetworkUtils.getAbsoluteURL(it.origin, it.link) val html = viewModel.clHtml(content) if (viewModel.rssSource?.loadWithBaseUrl == true) { - webView.loadDataWithBaseURL(url, html, "text/html", "utf-8", url)//不想用baseUrl进else + web_view.loadDataWithBaseURL( + url, + html, + "text/html", + "utf-8", + url + )//不想用baseUrl进else } else { //webView.loadData(html, "text/html;charset=utf-8", "utf-8")//经测试可以解决中文乱码 - webView.loadDataWithBaseURL(null, html, "text/html;charset=utf-8", "utf-8", url) + web_view.loadDataWithBaseURL( + null, + html, + "text/html;charset=utf-8", + "utf-8", + url + ) } } }) viewModel.urlLiveData.observe(this, Observer { upJavaScriptEnable() - webView.loadUrl(it.url, it.headerMap) + web_view.loadUrl(it.url, it.headerMap) }) } @SuppressLint("SetJavaScriptEnabled") private fun upJavaScriptEnable() { if (viewModel.rssSource?.enableJs == true) { - webView.settings.javaScriptEnabled = true + web_view.settings.javaScriptEnabled = true } } @@ -162,9 +174,9 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { event?.let { when (keyCode) { - KeyEvent.KEYCODE_BACK -> if (event.isTracking && !event.isCanceled && webView.canGoBack()) { - if (webView.copyBackForwardList().size > 1) { - webView.goBack() + KeyEvent.KEYCODE_BACK -> if (event.isTracking && !event.isCanceled && web_view.canGoBack()) { + if (web_view.copyBackForwardList().size > 1) { + web_view.goBack() return true } } @@ -179,8 +191,8 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r viewModel.textToSpeech.stop() upTtsMenu(false) } else { - webView.settings.javaScriptEnabled = true - webView.evaluateJavascript("document.documentElement.outerHTML") { + web_view.settings.javaScriptEnabled = true + web_view.evaluateJavascript("document.documentElement.outerHTML") { val html = StringEscapeUtils.unescapeJson(it) val text = Jsoup.clean(html, Whitelist.none()) .replace(Regex("""&\w+;"""), "") diff --git a/app/src/main/res/layout/activity_rss_read.xml b/app/src/main/res/layout/activity_rss_read.xml index e2b27076c..3c1fd1a3c 100644 --- a/app/src/main/res/layout/activity_rss_read.xml +++ b/app/src/main/res/layout/activity_rss_read.xml @@ -10,7 +10,7 @@ android:layout_height="wrap_content" /> diff --git a/app/src/main/res/layout/activity_source_login.xml b/app/src/main/res/layout/activity_source_login.xml index e2b27076c..ed20ab520 100644 --- a/app/src/main/res/layout/activity_source_login.xml +++ b/app/src/main/res/layout/activity_source_login.xml @@ -2,15 +2,17 @@ + android:layout_height="wrap_content" + app:title="@string/login" /> diff --git a/app/src/main/res/menu/source_debug.xml b/app/src/main/res/menu/source_debug.xml index 5b8b467f2..a55aecf0f 100644 --- a/app/src/main/res/menu/source_debug.xml +++ b/app/src/main/res/menu/source_debug.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> diff --git a/app/src/main/res/menu/source_login.xml b/app/src/main/res/menu/source_login.xml new file mode 100644 index 000000000..c58ada14b --- /dev/null +++ b/app/src/main/res/menu/source_login.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file From 2ddf83e9c4392ed50ae89838f0320a085c30d445 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 14:23:18 +0800 Subject: [PATCH 38/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../source/edit/BookSourceEditActivity.kt | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt index c0eab1b32..46f3eb49f 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt @@ -24,6 +24,7 @@ import io.legado.app.data.entities.rule.* import io.legado.app.lib.dialogs.alert import io.legado.app.lib.theme.ATH import io.legado.app.ui.book.source.debug.BookSourceDebugActivity +import io.legado.app.ui.login.SourceLogin import io.legado.app.ui.widget.KeyboardToolPop import io.legado.app.utils.GSON import io.legado.app.utils.applyTint @@ -67,22 +68,20 @@ class BookSourceEditActivity : override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.menu_save -> { - val source = getSource() + R.id.menu_save -> getSource().let { source -> if (checkSource(source)) { viewModel.save(source) { setResult(Activity.RESULT_OK); finish() } } } - R.id.menu_debug_source -> { - val source = getSource() + R.id.menu_debug_source -> getSource().let { source -> if (checkSource(source)) { viewModel.save(source) { startActivity(Pair("key", source.bookSourceUrl)) } } } - R.id.menu_copy_source -> { - GSON.toJson(getSource())?.let { sourceStr -> + R.id.menu_copy_source -> getSource().let { source -> + GSON.toJson(source)?.let { sourceStr -> val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager clipboard?.setPrimaryClip(ClipData.newPlainText(null, sourceStr)) } @@ -101,6 +100,18 @@ class BookSourceEditActivity : toast(R.string.can_not_open) } } + R.id.menu_login -> getSource().let { + if (checkSource(it)) { + if (it.loginUrl.isNullOrEmpty()) { + toast(R.string.source_no_login) + } else { + startActivity( + Pair("sourceUrl", it.bookSourceUrl), + Pair("loginUrl", it.loginUrl) + ) + } + } + } } return super.onCompatOptionsItemSelected(item) } From aa110cc9c52d48739aeff3111fd612ef3fa66d5a Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 14:30:02 +0800 Subject: [PATCH 39/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/ui/book/read/ReadBookActivity.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 48bdd6cf6..f57a6d067 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -40,6 +40,7 @@ import io.legado.app.ui.book.read.page.delegate.PageDelegate import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.changesource.ChangeSourceDialog import io.legado.app.ui.chapterlist.ChapterListActivity +import io.legado.app.ui.login.SourceLogin import io.legado.app.ui.replacerule.ReplaceRuleActivity import io.legado.app.ui.replacerule.edit.ReplaceEditDialog import io.legado.app.ui.widget.dialog.TextDialog @@ -219,6 +220,12 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo supportFragmentManager, ReadBook.book?.tocUrl ) + R.id.menu_login -> ReadBook.webBook?.bookSource?.let { + startActivity( + Pair("sourceUrl", it.bookSourceUrl), + Pair("loginUrl", it.loginUrl) + ) + } } return super.onCompatOptionsItemSelected(item) } From 8de2bd73d001c5bcd15e63337410574ccd445ff2 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 14:43:21 +0800 Subject: [PATCH 40/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/help/storage/ImportOldData.kt | 11 ++++------ .../legado/app/ui/config/BackupRestoreUi.kt | 22 +++++-------------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt index 91618a704..9087c3e9f 100644 --- a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt +++ b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt @@ -17,15 +17,12 @@ import org.jetbrains.anko.toast import java.io.File object ImportOldData { - val yueDuPath by lazy { - FileUtils.getSdCardPath() + File.separator + "YueDu" - } - fun import(context: Context) { + fun import(context: Context, file: File) { GlobalScope.launch(Dispatchers.IO) { try {// 导入书架 val shelfFile = - FileUtils.createFileIfNotExist(yueDuPath + File.separator + "myBookShelf.json") + FileUtils.createFileIfNotExist(file, "myBookShelf.json") val json = shelfFile.readText() val importCount = importOldBookshelf(json) withContext(Dispatchers.Main) { @@ -39,7 +36,7 @@ object ImportOldData { try {// Book source val sourceFile = - FileUtils.createFileIfNotExist(yueDuPath + File.separator + "myBookSource.json") + FileUtils.createFileIfNotExist(file, "myBookSource.json") val json = sourceFile.readText() val importCount = importOldSource(json) withContext(Dispatchers.Main) { @@ -53,7 +50,7 @@ object ImportOldData { try {// Replace rules val ruleFile = - FileUtils.createFileIfNotExist(yueDuPath + File.separator + "myBookReplaceRule.json") + FileUtils.createFileIfNotExist(file, "myBookReplaceRule.json") val json = ruleFile.readText() val importCount = importOldReplaceRule(json) withContext(Dispatchers.Main) { diff --git a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt index 36715bcf8..1bec0a184 100644 --- a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt +++ b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt @@ -3,7 +3,6 @@ package io.legado.app.ui.config import android.app.Activity.RESULT_OK import android.content.Intent import android.net.Uri -import android.os.Build import androidx.documentfile.provider.DocumentFile import androidx.fragment.app.Fragment import io.legado.app.App @@ -23,6 +22,7 @@ import io.legado.app.utils.isContentPath import io.legado.app.utils.toast import kotlinx.coroutines.Dispatchers.Main import org.jetbrains.anko.toast +import java.io.File object BackupRestoreUi { private const val selectFolderRequestCode = 21 @@ -123,22 +123,7 @@ object BackupRestoreUi { } fun importOldData(fragment: Fragment) { - FilePicker.selectFolder(fragment, oldDataRequestCode) { - importOldUsePermission(fragment) - } - } - - private fun importOldUsePermission(fragment: Fragment) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { - fragment.toast(R.string.a10_permission_toast) - } - PermissionsCompat.Builder(fragment) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - ImportOldData.import(fragment.requireContext()) - } - .request() + FilePicker.selectFolder(fragment, oldDataRequestCode) } fun onFilePicked(requestCode: Int, currentPath: String) { @@ -162,6 +147,9 @@ object BackupRestoreUi { selectFolderRequestCode -> { AppConfig.backupPath = currentPath } + oldDataRequestCode -> { + ImportOldData.import(App.INSTANCE, File(currentPath)) + } } } From cb0e81748c76140a751d7597571abdb63b706eea Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 15:17:51 +0800 Subject: [PATCH 41/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 2 +- .../legado/app/model/analyzeRule/AnalyzeUrl.kt | 18 ++++++++++++------ .../java/io/legado/app/ui/login/SourceLogin.kt | 8 ++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 2de034c99..e10b8d9be 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -2,7 +2,7 @@ * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 -**2020/01/01** +**2020/03/01** * 修复书源解析的一个bug * 添加底部操作栏颜色配置 * 修复滚动点击翻页,修复滚动最后一页显示加载中 diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index f1c92e798..9ac402267 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -8,10 +8,7 @@ import io.legado.app.constant.Pattern.EXP_PATTERN import io.legado.app.constant.Pattern.JS_PATTERN import io.legado.app.data.entities.BaseBook import io.legado.app.help.JsExtensions -import io.legado.app.help.http.AjaxWebView -import io.legado.app.help.http.HttpHelper -import io.legado.app.help.http.RequestMethod -import io.legado.app.help.http.Res +import io.legado.app.help.http.* import io.legado.app.help.http.api.HttpGetApi import io.legado.app.help.http.api.HttpPostApi import io.legado.app.utils.* @@ -243,7 +240,11 @@ class AnalyzeUrl( } @Throws(Exception::class) - fun getResponse(): Call { + fun getResponse(tag: String): Call { + val cookie = CookieStore.getCookie(tag) + if (cookie.isNotEmpty()) { + headerMap["Cookie"] = cookie + } return when { method == RequestMethod.POST -> { if (fieldMap.isNotEmpty()) { @@ -267,7 +268,7 @@ class AnalyzeUrl( @Throws(Exception::class) suspend fun getResponseAwait( - tag: String? = null, + tag: String, jsStr: String? = null, sourceRegex: String? = null ): Res { @@ -278,8 +279,13 @@ class AnalyzeUrl( params.javaScript = jsStr params.sourceRegex = sourceRegex params.postData = bodyTxt?.toByteArray() + params.tag = tag return HttpHelper.ajax(params) } + val cookie = CookieStore.getCookie(tag) + if (cookie.isNotEmpty()) { + headerMap["Cookie"] = cookie + } val res = when { method == RequestMethod.POST -> { if (fieldMap.isNotEmpty()) { diff --git a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt b/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt index 05be53788..a0703aef3 100644 --- a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt +++ b/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt @@ -14,6 +14,8 @@ import io.legado.app.base.BaseActivity import io.legado.app.data.entities.Cookie import io.legado.app.utils.snackbar import kotlinx.android.synthetic.main.activity_source_login.* +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.launch class SourceLogin : BaseActivity(R.layout.activity_source_login) { @@ -74,8 +76,10 @@ class SourceLogin : BaseActivity(R.layout.activity_source_login) { } private fun saveCookie(cookie: String) { - sourceUrl?.let { - App.db.cookieDao().insert(Cookie(it, cookie)) + launch(IO) { + sourceUrl?.let { + App.db.cookieDao().insert(Cookie(it, cookie)) + } } } } \ No newline at end of file From cd859406d58e6095e49c6682a49877dff2dcc7f0 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 15:42:42 +0800 Subject: [PATCH 42/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/help/http/CookieStore.kt | 4 ++-- .../io/legado/app/ui/login/SourceLogin.kt | 20 +++++++------------ 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/http/CookieStore.kt b/app/src/main/java/io/legado/app/help/http/CookieStore.kt index d01eb465c..d82a4b175 100644 --- a/app/src/main/java/io/legado/app/help/http/CookieStore.kt +++ b/app/src/main/java/io/legado/app/help/http/CookieStore.kt @@ -3,15 +3,15 @@ package io.legado.app.help.http import android.text.TextUtils import io.legado.app.App import io.legado.app.data.entities.Cookie +import io.legado.app.help.coroutine.Coroutine object CookieStore { fun setCookie(url: String, cookie: String?) { - try { + Coroutine.async { val cookieBean = Cookie(url, cookie ?: "") App.db.cookieDao().insert(cookieBean) - } catch (ignore: Exception) { } } diff --git a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt b/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt index a0703aef3..35a62200c 100644 --- a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt +++ b/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt @@ -8,14 +8,11 @@ import android.view.MenuItem import android.webkit.CookieManager import android.webkit.WebView import android.webkit.WebViewClient -import io.legado.app.App import io.legado.app.R import io.legado.app.base.BaseActivity -import io.legado.app.data.entities.Cookie +import io.legado.app.help.http.CookieStore import io.legado.app.utils.snackbar import kotlinx.android.synthetic.main.activity_source_login.* -import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.launch class SourceLogin : BaseActivity(R.layout.activity_source_login) { @@ -41,13 +38,17 @@ class SourceLogin : BaseActivity(R.layout.activity_source_login) { web_view.webViewClient = object : WebViewClient() { override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { val cookie = cookieManager.getCookie(url) - saveCookie(cookie) + sourceUrl?.let { + CookieStore.setCookie(it, cookie) + } super.onPageStarted(view, url, favicon) } override fun onPageFinished(view: WebView?, url: String?) { val cookie = cookieManager.getCookie(url) - saveCookie(cookie) + sourceUrl?.let { + CookieStore.setCookie(it, cookie) + } if (checking) { finish() } @@ -75,11 +76,4 @@ class SourceLogin : BaseActivity(R.layout.activity_source_login) { return super.onCompatOptionsItemSelected(item) } - private fun saveCookie(cookie: String) { - launch(IO) { - sourceUrl?.let { - App.db.cookieDao().insert(Cookie(it, cookie)) - } - } - } } \ No newline at end of file From 62236b4fd738602e0437a8f226b665465f202ba5 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 15:51:31 +0800 Subject: [PATCH 43/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/filechooser/FilePicker.kt | 122 +++++++++--------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt index affb430ad..392286479 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt @@ -25,26 +25,19 @@ object FilePicker { 0 -> default?.invoke() 1 -> { try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + val intent = getSelectDirIntent() activity.startActivityForResult(intent, requestCode) } catch (e: java.lang.Exception) { e.printStackTrace() activity.toast(e.localizedMessage ?: "ERROR") } } - 2 -> { - PermissionsCompat.Builder(activity) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - activity.supportFragmentManager, - requestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - .request() + 2 -> checkPermissions(activity) { + FileChooserDialog.show( + activity.supportFragmentManager, + requestCode, + mode = FileChooserDialog.DIRECTORY + ) } } } @@ -64,26 +57,19 @@ object FilePicker { 0 -> default?.invoke() 1 -> { try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + val intent = getSelectDirIntent() fragment.startActivityForResult(intent, requestCode) } catch (e: java.lang.Exception) { e.printStackTrace() fragment.toast(e.localizedMessage ?: "ERROR") } } - 2 -> { - PermissionsCompat.Builder(fragment) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - fragment.childFragmentManager, - requestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - .request() + 2 -> checkPermissions(fragment) { + FileChooserDialog.show( + fragment.childFragmentManager, + requestCode, + mode = FileChooserDialog.DIRECTORY + ) } } } @@ -108,9 +94,7 @@ object FilePicker { 0 -> default?.invoke() 1 -> { try { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + val intent = getSelectFileIntent() intent.type = type//设置类型 activity.startActivityForResult(intent, requestCode) } catch (e: java.lang.Exception) { @@ -118,19 +102,13 @@ object FilePicker { activity.toast(e.localizedMessage ?: "ERROR") } } - 2 -> { - PermissionsCompat.Builder(activity) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - activity.supportFragmentManager, - requestCode, - mode = FileChooserDialog.FILE, - allowExtensions = allowExtensions - ) - } - .request() + 2 -> checkPermissions(activity) { + FileChooserDialog.show( + activity.supportFragmentManager, + requestCode, + mode = FileChooserDialog.FILE, + allowExtensions = allowExtensions + ) } } } @@ -156,9 +134,7 @@ object FilePicker { 0 -> default?.invoke() 1 -> { try { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + val intent = getSelectFileIntent() intent.type = type//设置类型 fragment.startActivityForResult(intent, requestCode) } catch (e: java.lang.Exception) { @@ -166,23 +142,49 @@ object FilePicker { fragment.toast(e.localizedMessage ?: "ERROR") } } - 2 -> { - PermissionsCompat.Builder(fragment) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - fragment.childFragmentManager, - requestCode, - mode = FileChooserDialog.FILE, - allowExtensions = allowExtensions - ) - } - .request() + 2 -> checkPermissions(fragment) { + FileChooserDialog.show( + fragment.childFragmentManager, + requestCode, + mode = FileChooserDialog.FILE, + allowExtensions = allowExtensions + ) } } } }.show() } + private fun getSelectFileIntent(): Intent { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.addCategory(Intent.CATEGORY_OPENABLE) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + return intent + } + + private fun getSelectDirIntent(): Intent { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + return intent + } + + private fun checkPermissions(fragment: Fragment, success: (() -> Unit)? = null) { + PermissionsCompat.Builder(fragment) + .addPermissions(*Permissions.Group.STORAGE) + .rationale(R.string.tip_perm_request_storage) + .onGranted { + success?.invoke() + } + .request() + } + + private fun checkPermissions(activity: AppCompatActivity, success: (() -> Unit)? = null) { + PermissionsCompat.Builder(activity) + .addPermissions(*Permissions.Group.STORAGE) + .rationale(R.string.tip_perm_request_storage) + .onGranted { + success?.invoke() + } + .request() + } } \ No newline at end of file From f1ed9e6e45350354ad8e511ed8de31968e72a928 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 15:52:50 +0800 Subject: [PATCH 44/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt index 392286479..9260fc8bd 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt @@ -11,6 +11,7 @@ import io.legado.app.lib.dialogs.alert import io.legado.app.utils.toast import org.jetbrains.anko.toast +@Suppress("unused") object FilePicker { fun selectFolder(activity: AppCompatActivity, requestCode: Int, default: (() -> Unit)? = null) { From 6984c6998cc7964930445d6f590e183cda31d1da Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 16:18:50 +0800 Subject: [PATCH 45/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/help/BookHelp.kt | 10 +--- .../java/io/legado/app/help/JsExtensions.kt | 2 +- .../java/io/legado/app/help/ReadBookConfig.kt | 51 ++++++++++++------- .../io/legado/app/help/storage/Restore.kt | 1 + app/src/main/java/io/legado/app/model/Rss.kt | 2 +- .../main/java/io/legado/app/model/WebBook.kt | 8 +-- .../app/model/analyzeRule/AnalyzeRule.kt | 2 +- .../app/model/webBook/BookChapterList.kt | 5 +- .../legado/app/model/webBook/BookContent.kt | 4 +- .../ui/book/read/config/ReadStyleDialog.kt | 3 +- .../app/ui/book/read/page/ChapterProvider.kt | 5 +- 11 files changed, 50 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt index c7560b819..97c71f04c 100644 --- a/app/src/main/java/io/legado/app/help/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/BookHelp.kt @@ -237,13 +237,7 @@ object BookHelp { private var bookName: String? = null private var bookOrigin: String? = null private var replaceRules: List = arrayListOf() - var bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2) - set(value) { - field = value - App.INSTANCE.putPrefInt(PreferKey.bodyIndent, value) - bodyIndent = " ".repeat(value) - } - var bodyIndent = " ".repeat(bodyIndentCount) + fun disposeContent( @@ -281,6 +275,6 @@ object BookHelp { 1 -> c = ZhConvertBootstrap.newInstance().toSimple(c) 2 -> c = ZhConvertBootstrap.newInstance().toTraditional(c) } - return c.replace("\\s*\\n+\\s*".toRegex(), "\n$bodyIndent") + return c.replace("\\s*\\n+\\s*".toRegex(), "\n${ReadBookConfig.bodyIndent}") } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index 0160000f7..32da4bb06 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -17,7 +17,7 @@ interface JsExtensions { fun ajax(urlStr: String): String? { return try { val analyzeUrl = AnalyzeUrl(urlStr, null, null, null, null, null) - val call = analyzeUrl.getResponse() + val call = analyzeUrl.getResponse(urlStr) val response = call.execute() response.body() } catch (e: Exception) { diff --git a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt index 229d5d7e6..a3a6de521 100644 --- a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt +++ b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt @@ -26,24 +26,6 @@ object ReadBookConfig { } val durConfig get() = getConfig(styleSelect) private val shareConfig get() = getConfig(5) - var styleSelect = App.INSTANCE.getPrefInt(PreferKey.readStyleSelect) - set(value) { - field = value - App.INSTANCE.putPrefInt(PreferKey.readStyleSelect, value) - } - var shareLayout = App.INSTANCE.getPrefBoolean(PreferKey.shareLayout) - set(value) { - field = value - App.INSTANCE.putPrefBoolean(PreferKey.shareLayout, value) - } - var pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim) - set(value) { - field = value - isScroll = value == 3 - App.INSTANCE.putPrefInt(PreferKey.pageAnim, value) - } - var isScroll = pageAnim == 3 - val clickTurnPage get() = App.INSTANCE.getPrefBoolean(PreferKey.clickTurnPage, true) var bg: Drawable? = null init { @@ -113,6 +95,39 @@ object ReadBookConfig { } //配置写入读取 + var styleSelect = App.INSTANCE.getPrefInt(PreferKey.readStyleSelect) + set(value) { + field = value + if (App.INSTANCE.getPrefInt(PreferKey.readStyleSelect) != value) { + App.INSTANCE.putPrefInt(PreferKey.readStyleSelect, value) + } + } + var shareLayout = App.INSTANCE.getPrefBoolean(PreferKey.shareLayout) + set(value) { + field = value + if (App.INSTANCE.getPrefBoolean(PreferKey.shareLayout) != value) { + App.INSTANCE.putPrefBoolean(PreferKey.shareLayout, value) + } + } + var pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim) + set(value) { + field = value + isScroll = value == 3 + if (App.INSTANCE.getPrefInt(PreferKey.pageAnim) != value) { + App.INSTANCE.putPrefInt(PreferKey.pageAnim, value) + } + } + var isScroll = pageAnim == 3 + val clickTurnPage get() = App.INSTANCE.getPrefBoolean(PreferKey.clickTurnPage, true) + var bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2) + set(value) { + field = value + bodyIndent = " ".repeat(value) + if (App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2) != value) { + App.INSTANCE.putPrefInt(PreferKey.bodyIndent, value) + } + } + var bodyIndent = " ".repeat(bodyIndentCount) var hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar) var hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar) var textBold: Boolean diff --git a/app/src/main/java/io/legado/app/help/storage/Restore.kt b/app/src/main/java/io/legado/app/help/storage/Restore.kt index dd48d6472..d9be6e9bd 100644 --- a/app/src/main/java/io/legado/app/help/storage/Restore.kt +++ b/app/src/main/java/io/legado/app/help/storage/Restore.kt @@ -114,6 +114,7 @@ object Restore { pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim) hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar) hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar) + bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2) } ChapterProvider.upStyle() ReadBook.loadContent() diff --git a/app/src/main/java/io/legado/app/model/Rss.kt b/app/src/main/java/io/legado/app/model/Rss.kt index c2568f458..b3622b40b 100644 --- a/app/src/main/java/io/legado/app/model/Rss.kt +++ b/app/src/main/java/io/legado/app/model/Rss.kt @@ -35,7 +35,7 @@ object Rss { ): Coroutine { return Coroutine.async(scope, context) { val body = AnalyzeUrl(rssArticle.link, baseUrl = rssArticle.origin) - .getResponseAwait() + .getResponseAwait(rssArticle.origin) .body val analyzeRule = AnalyzeRule() analyzeRule.setContent( diff --git a/app/src/main/java/io/legado/app/model/WebBook.kt b/app/src/main/java/io/legado/app/model/WebBook.kt index bb3f80b8d..5a51b486c 100644 --- a/app/src/main/java/io/legado/app/model/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/WebBook.kt @@ -37,7 +37,7 @@ class WebBook(val bookSource: BookSource) { baseUrl = sourceUrl, headerMapF = bookSource.getHeaderMap() ) - val res = analyzeUrl.getResponseAwait() + val res = analyzeUrl.getResponseAwait(bookSource.bookSourceUrl) BookList.analyzeBookList( res.body, bookSource, @@ -65,7 +65,7 @@ class WebBook(val bookSource: BookSource) { baseUrl = sourceUrl, headerMapF = bookSource.getHeaderMap() ) - val res = analyzeUrl.getResponseAwait() + val res = analyzeUrl.getResponseAwait(bookSource.bookSourceUrl) BookList.analyzeBookList( res.body, bookSource, @@ -96,7 +96,7 @@ class WebBook(val bookSource: BookSource) { baseUrl = sourceUrl, headerMapF = bookSource.getHeaderMap() ) - analyzeUrl.getResponseAwait().body + analyzeUrl.getResponseAwait(bookSource.bookSourceUrl).body } BookInfo.analyzeBookInfo(book, body, bookSource, book.bookUrl) book @@ -122,7 +122,7 @@ class WebBook(val bookSource: BookSource) { ruleUrl = book.tocUrl, baseUrl = book.bookUrl, headerMapF = bookSource.getHeaderMap() - ).getResponseAwait().body + ).getResponseAwait(bookSource.bookSourceUrl).body } BookChapterList.analyzeChapterList(this, book, body, bookSource, book.tocUrl) } diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index f9dbba8ee..f88a5ee85 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -610,7 +610,7 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions { override fun ajax(urlStr: String): String? { return try { val analyzeUrl = AnalyzeUrl(urlStr, null, null, null, baseUrl, book) - val call = analyzeUrl.getResponse() + val call = analyzeUrl.getResponse(urlStr) val response = call.execute() response.body() } catch (e: Exception) { diff --git a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt index e476eea28..8559351f4 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt @@ -63,7 +63,7 @@ object BookChapterList { ruleUrl = nextUrl, book = book, headerMapF = bookSource.getHeaderMap() - ).getResponseAwait() + ).getResponseAwait(bookSource.bookSourceUrl) .body?.let { nextBody -> chapterData = analyzeChapterList( book, nextUrl, nextBody, tocRule, listRule, bookSource @@ -132,7 +132,8 @@ object BookChapterList { ruleUrl = chapterData.nextUrl, book = book, headerMapF = bookSource.getHeaderMap() - ).getResponseAwait().body ?: throw Exception("${chapterData.nextUrl}, 下载失败") + ).getResponseAwait(bookSource.bookSourceUrl).body + ?: throw Exception("${chapterData.nextUrl}, 下载失败") val nextChapterData = analyzeChapterList( book, chapterData.nextUrl, nextBody, tocRule, listRule, bookSource, false diff --git a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt index 8ca67a6c5..14ea49b25 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt @@ -56,7 +56,7 @@ object BookContent { ruleUrl = nextUrl, book = book, headerMapF = bookSource.getHeaderMap() - ).getResponseAwait() + ).getResponseAwait(bookSource.bookSourceUrl) .body?.let { nextBody -> contentData = analyzeContent( @@ -80,7 +80,7 @@ object BookContent { ruleUrl = item.nextUrl, book = book, headerMapF = bookSource.getHeaderMap() - ).getResponseAwait() + ).getResponseAwait(bookSource.bookSourceUrl) .body?.let { contentData = analyzeContent( diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt index 40c3c9e94..996eaa25c 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt @@ -11,7 +11,6 @@ import androidx.fragment.app.DialogFragment import io.legado.app.R import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey -import io.legado.app.help.BookHelp import io.legado.app.help.ReadBookConfig import io.legado.app.lib.dialogs.selector import io.legado.app.lib.theme.accentColor @@ -118,7 +117,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { title = getString(R.string.text_indent), items = resources.getStringArray(R.array.indent).toList() ) { _, index -> - BookHelp.bodyIndentCount = index + ReadBookConfig.bodyIndentCount = index postEvent(EventBus.UP_CONFIG, true) } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt index 2c3996fd8..33ece04d2 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt @@ -9,7 +9,6 @@ import io.legado.app.App import io.legado.app.constant.PreferKey import io.legado.app.data.entities.BookChapter import io.legado.app.help.AppConfig -import io.legado.app.help.BookHelp import io.legado.app.help.ReadBookConfig import io.legado.app.ui.book.read.page.entities.TextChapter import io.legado.app.ui.book.read.page.entities.TextLine @@ -34,7 +33,6 @@ object ChapterProvider { var typeface: Typeface = Typeface.SANS_SERIF var titlePaint = TextPaint() var contentPaint = TextPaint() - private var bodyIndent = BookHelp.bodyIndent init { upStyle() @@ -227,6 +225,7 @@ object ChapterProvider { desiredWidth: Float ) { var x = 0f + val bodyIndent = ReadBookConfig.bodyIndent val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length for (i in 0..bodyIndent.lastIndex) { val x1 = x + icw @@ -348,8 +347,6 @@ object ChapterProvider { titlePaint.textSize = (ReadBookConfig.textSize + 2).dp.toFloat() contentPaint.textSize = ReadBookConfig.textSize.dp.toFloat() - bodyIndent = BookHelp.bodyIndent - upSize() } From ebc1d8d03bd8988c6cbd6e4c4fdf51a60e2c983b Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 17:04:10 +0800 Subject: [PATCH 46/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/help/BookHelp.kt | 50 ++++++++++++------- .../app/ui/book/read/ReadBookActivity.kt | 7 +++ .../app/ui/replacerule/ReplaceRuleActivity.kt | 7 +++ .../ui/replacerule/edit/ReplaceEditDialog.kt | 7 +++ 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt index 97c71f04c..7df2540c5 100644 --- a/app/src/main/java/io/legado/app/help/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/BookHelp.kt @@ -238,7 +238,29 @@ object BookHelp { private var bookOrigin: String? = null private var replaceRules: List = arrayListOf() - + @Synchronized + fun upReplaceRules(name: String? = null, origin: String? = null) { + if (name != null) { + if (bookName != name || bookOrigin != origin) { + replaceRules = if (origin.isNullOrEmpty()) { + App.db.replaceRuleDao().findEnabledByScope(name) + } else { + App.db.replaceRuleDao().findEnabledByScope(name, origin) + } + bookName = name + bookOrigin = origin + } + } else { + val o = bookOrigin + bookName?.let { + replaceRules = if (o.isNullOrEmpty()) { + App.db.replaceRuleDao().findEnabledByScope(it) + } else { + App.db.replaceRuleDao().findEnabledByScope(it, o) + } + } + } + } fun disposeContent( title: String, @@ -247,23 +269,17 @@ object BookHelp { content: String, enableReplace: Boolean ): String { - synchronized(this) { - if (enableReplace && (bookName != name || bookOrigin != origin)) { - replaceRules = if (origin.isNullOrEmpty()) { - App.db.replaceRuleDao().findEnabledByScope(name) - } else { - App.db.replaceRuleDao().findEnabledByScope(name, origin) - } - } - } var c = content - for (item in replaceRules) { - item.pattern.let { - if (it.isNotEmpty()) { - c = if (item.isRegex) { - c.replace(it.toRegex(), item.replacement) - } else { - c.replace(it, item.replacement) + if (enableReplace) { + upReplaceRules(name, origin) + for (item in replaceRules) { + item.pattern.let { + if (it.isNotEmpty()) { + c = if (item.isRegex) { + c.replace(it.toRegex(), item.replacement) + } else { + c.replace(it, item.replacement) + } } } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index f57a6d067..9b03a4991 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -20,7 +20,9 @@ import io.legado.app.constant.PreferKey import io.legado.app.constant.Status import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter +import io.legado.app.help.BookHelp import io.legado.app.help.ReadBookConfig +import io.legado.app.help.coroutine.Coroutine import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.noButton import io.legado.app.lib.dialogs.okButton @@ -64,6 +66,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo ChangeSourceDialog.CallBack, ReadBook.CallBack, TocRegexDialog.CallBack, + ReplaceEditDialog.CallBack, ColorPickerDialogListener { private val requestCodeChapterList = 568 private val requestCodeEditSource = 111 @@ -551,6 +554,10 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo } } + override fun onReplaceRuleSave() { + Coroutine.async { BookHelp.upReplaceRules() } + } + override fun showReadStyle() { ReadStyleDialog().show(supportFragmentManager, "readStyle") } diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt index 7910b9cb8..e5be539c5 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt @@ -20,7 +20,9 @@ import io.legado.app.App import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.data.entities.ReplaceRule +import io.legado.app.help.BookHelp import io.legado.app.help.ItemTouchCallback +import io.legado.app.help.coroutine.Coroutine import io.legado.app.lib.dialogs.* import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor @@ -292,6 +294,11 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi } } + override fun onDestroy() { + super.onDestroy() + Coroutine.async { BookHelp.upReplaceRules() } + } + override fun upCountView() { select_action_bar.upCountView(adapter.getSelection().size, adapter.getActualItemCount()) } diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt index de5002931..71026502e 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt @@ -68,6 +68,7 @@ class ReplaceEditDialog : DialogFragment(), when (item?.itemId) { R.id.menu_save -> { viewModel.save(getReplaceRule()) { + callBack?.onReplaceRuleSave() dismiss() } } @@ -94,4 +95,10 @@ class ReplaceEditDialog : DialogFragment(), replaceRule.scope = et_scope.text.toString() return replaceRule } + + val callBack = activity as? CallBack + + interface CallBack { + fun onReplaceRuleSave() + } } From b60ce32c11b0c78b14fbc45c269179651f852360 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 17:32:16 +0800 Subject: [PATCH 47/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/ui/book/read/ReadBookActivity.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 9b03a4991..6740f3169 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -554,18 +554,31 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo } } + /** + * 替换规则变化 + */ override fun onReplaceRuleSave() { Coroutine.async { BookHelp.upReplaceRules() } + ReadBook.loadContent() } + /** + * 显示阅读样式配置 + */ override fun showReadStyle() { ReadStyleDialog().show(supportFragmentManager, "readStyle") } + /** + * 显示更多设置 + */ override fun showMoreSetting() { MoreConfigDialog().show(supportFragmentManager, "moreConfig") } + /** + * 更新状态栏,导航栏 + */ override fun upSystemUiVisibility() { Help.upSystemUiVisibility(this, !read_menu.isVisible) } From afc99e13d373994e9c3fc290b4a10cad3cdc9681 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 17:41:39 +0800 Subject: [PATCH 48/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/replacerule/edit/ReplaceEditDialog.kt | 8 +++++++- .../app/ui/replacerule/edit/ReplaceEditViewModel.kt | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt index 71026502e..f4226a64e 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt @@ -23,11 +23,17 @@ class ReplaceEditDialog : DialogFragment(), companion object { - fun show(fragmentManager: FragmentManager, id: Long = -1, pattern: String? = null) { + fun show( + fragmentManager: FragmentManager, + id: Long = -1, + pattern: String? = null, + isRegex: Boolean = false + ) { val dialog = ReplaceEditDialog() val bundle = Bundle() bundle.putLong("id", id) bundle.putString("pattern", pattern) + bundle.putBoolean("isRegex", isRegex) dialog.arguments = bundle dialog.show(fragmentManager, "editReplace") } diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt index f326c88c3..722deb738 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt @@ -21,8 +21,9 @@ class ReplaceEditViewModel(application: Application) : BaseViewModel(application } } else { bundle.getString("pattern")?.let { pattern -> + val isRegex = bundle.getBoolean("isRegex") replaceRuleData.postValue( - ReplaceRule(pattern = pattern) + ReplaceRule(pattern = pattern, isRegex = isRegex) ) } } From ae09c555cdc4eb9971c535c4742f9e74d5cfc7c6 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 17:57:16 +0800 Subject: [PATCH 49/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/ui/book/read/ReadBookActivity.kt | 8 +++++--- .../java/io/legado/app/ui/book/read/TextActionMenu.kt | 2 +- .../legado/app/ui/replacerule/edit/ReplaceEditDialog.kt | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 6740f3169..09d29a7f3 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -558,8 +558,10 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo * 替换规则变化 */ override fun onReplaceRuleSave() { - Coroutine.async { BookHelp.upReplaceRules() } - ReadBook.loadContent() + Coroutine.async { + BookHelp.upReplaceRules() + ReadBook.loadContent() + } } /** @@ -637,7 +639,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo viewModel.openChapter(index) } } - requestCodeReplace -> ReadBook.loadContent() + requestCodeReplace -> onReplaceRuleSave() } } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt index 4dc9ba7da..7e90c7ad1 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt @@ -95,6 +95,7 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac onMenuItemSelected(it) } } + callBack.onMenuActionFinally() } } } @@ -121,7 +122,6 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac } } } - callBack.onMenuActionFinally() } @RequiresApi(Build.VERSION_CODES.M) diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt index f4226a64e..d125674c5 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt @@ -102,7 +102,7 @@ class ReplaceEditDialog : DialogFragment(), return replaceRule } - val callBack = activity as? CallBack + val callBack get() = activity as? CallBack interface CallBack { fun onReplaceRuleSave() From 24a2235fc8750abd83552c2c4abf9aafe0de3732 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 17:58:54 +0800 Subject: [PATCH 50/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index e10b8d9be..38ab363a9 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -2,6 +2,10 @@ * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 +**2020/03/01** +* 添加书源登录 +* 替换规则实时生效 + **2020/03/01** * 修复书源解析的一个bug * 添加底部操作栏颜色配置 From cffb082d8bb6ed5ff69110e04849bd4ded7ef07c Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 18:20:45 +0800 Subject: [PATCH 51/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/ui/book/read/page/ContentTextView.kt | 2 ++ .../io/legado/app/ui/book/read/page/PageFactory.kt | 1 + .../io/legado/app/ui/book/read/page/TextPageFactory.kt | 10 ++++------ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt index c6f719c9d..e8beebf6a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt @@ -92,6 +92,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at } if (!ReadBookConfig.isScroll) return //滚动翻页 + if (!pageFactory.hasNext()) return val nextPage = relativePage(1) relativeOffset = relativeOffset(1) nextPage.textLines.forEach { textLine -> @@ -108,6 +109,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at textLine.isReadAloud ) } + if (!pageFactory.hasNextPlus()) return relativeOffset = relativeOffset(2) if (relativeOffset < ChapterProvider.visibleHeight) { relativePage(2).textLines.forEach { textLine -> diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt index 85cdecbd5..7a04ae5ec 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt @@ -22,4 +22,5 @@ abstract class PageFactory(protected val dataSource: DataSource) { abstract fun hasPrev(): Boolean + abstract fun hasNextPlus(): Boolean } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt b/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt index 8d08a45ca..571f0d68f 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt @@ -13,6 +13,10 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource return hasNextChapter() || currentChapter?.isLastIndex(pageIndex) != true } + override fun hasNextPlus(): Boolean = with(dataSource) { + return hasNextChapter() || pageIndex < (currentChapter?.pageSize() ?: 1) - 2 + } + override fun moveToFirst() { ReadBook.setPageIndex(0) } @@ -86,9 +90,6 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource ?: TextPage(title = it.title).format() } } - if (!hasPrevChapter()) { - return@with TextPage(text = "") - } prevChapter?.let { return@with it.lastPage()?.removePageAloudSpan() ?: TextPage(title = it.title).format() @@ -103,9 +104,6 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource return@with it.page(pageIndex + 2)?.removePageAloudSpan() ?: TextPage(title = it.title).format() } - if (!hasNextChapter()) { - TextPage(text = "") - } nextChapter?.let { nc -> if (pageIndex < it.pageSize() - 1) { return@with nc.page(0)?.removePageAloudSpan() From 906244a3b0782096087e2f79b040258c34439be6 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 19:36:13 +0800 Subject: [PATCH 52/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/page/ChapterProvider.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt index 33ece04d2..430db0e53 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt @@ -120,10 +120,12 @@ object ChapterProvider { ) for (lineIndex in 0 until layout.lineCount) { textPages.last().height = durY - durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) val textLine = TextLine(isTitle = true) - if (durY < visibleHeight) { + if (durY + layout.getLineBaseline(lineIndex) - layout.getLineTop(lineIndex) + + contentPaint.fontMetrics.descent < visibleHeight + ) { textPages.last().textLines.add(textLine) + durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) } else { textPages.last().text = stringBuilder.toString() stringBuilder.clear() @@ -138,7 +140,7 @@ object ChapterProvider { (layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex))).toFloat() textLine.lineBase = (paddingTop + durY - (layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat() - textLine.lineBottom = textLine.lineBase + titlePaint.fontMetrics.descent + textLine.lineBottom = textLine.lineBase + contentPaint.fontMetrics.descent val words = title.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) stringBuilder.append(words) @@ -176,10 +178,12 @@ object ChapterProvider { ) for (lineIndex in 0 until layout.lineCount) { textPages.last().height = durY - durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) val textLine = TextLine() - if (durY < visibleHeight) { + if (durY + layout.getLineBaseline(lineIndex) - layout.getLineTop(lineIndex) + + contentPaint.fontMetrics.descent < visibleHeight + ) { textPages.last().textLines.add(textLine) + durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) } else { textPages.last().text = stringBuilder.toString() stringBuilder.clear() From 8db445c6efd4b88d6ccf462d4d52c3a467225e8b Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 19:37:37 +0800 Subject: [PATCH 53/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 38ab363a9..4908b5aa2 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -5,6 +5,7 @@ **2020/03/01** * 添加书源登录 * 替换规则实时生效 +* 页面最后一行计算是否能放下时不计算行距 **2020/03/01** * 修复书源解析的一个bug From 4d4e3a09d431248821fb31f5de7e8ebb4374fa85 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 19:37:57 +0800 Subject: [PATCH 54/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 4908b5aa2..b78537486 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -2,7 +2,7 @@ * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 -**2020/03/01** +**2020/03/02** * 添加书源登录 * 替换规则实时生效 * 页面最后一行计算是否能放下时不计算行距 From afa23f662c93b40f22cfbb9b57b0db847d030eac Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 19:58:32 +0800 Subject: [PATCH 55/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt | 2 ++ app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt | 1 + app/src/main/res/layout/view_read_menu.xml | 1 + 3 files changed, 4 insertions(+) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt index 6dec43d12..7ea2cff28 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt @@ -12,6 +12,7 @@ import io.legado.app.App import io.legado.app.R import io.legado.app.help.AppConfig import io.legado.app.help.ReadBookConfig +import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.buttonDisabledColor import io.legado.app.service.help.ReadBook @@ -48,6 +49,7 @@ class ReadMenu : FrameLayout { fabNightTheme.setImageResource(R.drawable.ic_brightness) } initAnimation() + ATH.applyBackgroundTint(fl_bottom_bg) vw_bg.onClick { } vwNavigationBar.onClick { } seek_brightness.progress = context.getPrefInt("brightness", 100) diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt index a347b0c05..7e535f009 100644 --- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt @@ -20,6 +20,7 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { override fun onActivityCreated(savedInstanceState: Bundle?) { iv_book.setColorFilter(accentColor) + vw_title_line.setBackgroundColor(accentColor) // 避免从桌面启动程序后,会重新实例化入口类的activity if (intent.flags and Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT != 0) { finish() diff --git a/app/src/main/res/layout/view_read_menu.xml b/app/src/main/res/layout/view_read_menu.xml index d23bc2248..9344b90f2 100644 --- a/app/src/main/res/layout/view_read_menu.xml +++ b/app/src/main/res/layout/view_read_menu.xml @@ -147,6 +147,7 @@ From e05c9cc561276c4a3419180ae54cc9cd848b0ee2 Mon Sep 17 00:00:00 2001 From: fisher Date: Sun, 1 Mar 2020 20:03:52 +0800 Subject: [PATCH 56/91] update full-width characters --- app/src/main/assets/txtTocRule.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/assets/txtTocRule.json b/app/src/main/assets/txtTocRule.json index 1304059d0..d99d765a2 100644 --- a/app/src/main/assets/txtTocRule.json +++ b/app/src/main/assets/txtTocRule.json @@ -2,61 +2,61 @@ { "enable": true, "name": "目录", - "rule": "^[ \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", + "rule": "^[  \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", "serialNumber": 0 }, { "enable": false, "name": "目录(不匹配行前空白)", - "rule": "^(?<=\\s)(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", + "rule": "^(?<= |\\s)(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", "serialNumber": 1 }, { "enable": false, "name": "目录(去简介)", - "rule": "^(?<=\\s)(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", + "rule": "^(?<= |\\s)(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$", "serialNumber": 2 }, { "enable": false, "name": "目录(古典、轻小说备用)", - "rule": "^[ \\t]{0,4}(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$", + "rule": "^[  \\t]{0,4}(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$", "serialNumber": 3 }, { "enable": true, "name": "数字 分隔符 标题名称", - "rule": "^[ \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$", + "rule": "^[  \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$", "serialNumber": 4 }, { "enable": true, "name": "正文 标题/序号", - "rule": "^[ \\t]{0,4}正文\\s{1,4}.{0,20}$", + "rule": "^[  \\t]{0,4}正文\\s{1,4}.{0,20}$", "serialNumber": 5 }, { "enable": true, - "name": "Chapter/Section/Part 序号 标题", - "rule": "^[ \\t]{0,4}(?:[Cc]hapter|[Ss]ection|[Pp]art)\\s{0,4}\\d{1,4}.{0,30}$", + "name": "Chapter/Section/Part/Episode 序号 标题", + "rule": "^[  \\t]{0,4}(?:[Cc]hapter|[Ss]ection|[Pp]art|PART|[Ee]pisode)\\s{0,4}\\d{1,4}.{0,30}$", "serialNumber": 6 }, { "enable": true, "name": "特殊符号 序号 标题", - "rule": "^[ \\t]{0,4}[〈〖〔【][第卷][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节][\\.:: \f\t].{0,30}$", + "rule": "^[  \\t]{0,4}[〈〖〔【][第卷][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节][\\.:: \f\t].{0,30}$", "serialNumber": 7 }, { "enable": true, "name": "特殊符号 标题", - "rule": "^[ \\t]{0,4}[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", + "rule": "^[  \\t]{0,4}[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", "serialNumber": 8 }, { "enable":false, "name": "特殊符号 标题(不匹配空白字符)", - "rule": "^(?<=\\s)[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", + "rule": "^(?<= |\\s)[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", "serialNumber": 9 }, { From 441bd5f276d49dcb2425febb3645cd6e4bc8938b Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 20:47:16 +0800 Subject: [PATCH 57/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt | 4 ++-- app/src/main/res/layout/view_read_menu.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt index 7ea2cff28..254d037ad 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt @@ -12,8 +12,8 @@ import io.legado.app.App import io.legado.app.R import io.legado.app.help.AppConfig import io.legado.app.help.ReadBookConfig -import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.accentColor +import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.buttonDisabledColor import io.legado.app.service.help.ReadBook import io.legado.app.utils.* @@ -49,7 +49,7 @@ class ReadMenu : FrameLayout { fabNightTheme.setImageResource(R.drawable.ic_brightness) } initAnimation() - ATH.applyBackgroundTint(fl_bottom_bg) + ll_bottom_bg.setBackgroundColor(context.bottomBackground) vw_bg.onClick { } vwNavigationBar.onClick { } seek_brightness.progress = context.getPrefInt("brightness", 100) diff --git a/app/src/main/res/layout/view_read_menu.xml b/app/src/main/res/layout/view_read_menu.xml index 9344b90f2..4d4f8f18b 100644 --- a/app/src/main/res/layout/view_read_menu.xml +++ b/app/src/main/res/layout/view_read_menu.xml @@ -147,7 +147,6 @@ @@ -157,6 +156,7 @@ android:layout_height="match_parent" /> Date: Sun, 1 Mar 2020 21:21:14 +0800 Subject: [PATCH 58/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt | 3 ++- .../java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt | 2 ++ .../java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt | 2 ++ app/src/main/res/layout/dialog_read_aloud.xml | 1 + app/src/main/res/layout/dialog_read_book_style.xml | 1 + 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt index 9b975e7e8..9d8ea8ba8 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt @@ -16,6 +16,7 @@ import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey import io.legado.app.help.ReadBookConfig import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.bottomBackground import io.legado.app.ui.book.read.Help import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.postEvent @@ -47,7 +48,7 @@ class MoreConfigDialog : DialogFragment() { savedInstanceState: Bundle? ): View? { val view = LinearLayout(context) - view.setBackgroundResource(R.color.background) + view.setBackgroundColor(requireContext().bottomBackground) view.id = R.id.tag1 container?.addView(view) return view diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt index 6040c0880..8cce7d4d2 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt @@ -11,6 +11,7 @@ import androidx.fragment.app.DialogFragment import io.legado.app.R import io.legado.app.constant.EventBus import io.legado.app.help.AppConfig +import io.legado.app.lib.theme.bottomBackground import io.legado.app.service.BaseReadAloudService import io.legado.app.service.help.ReadAloud import io.legado.app.service.help.ReadBook @@ -54,6 +55,7 @@ class ReadAloudDialog : DialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + ll_bottom_bg.setBackgroundColor(requireContext().bottomBackground) initData() initOnChange() initOnClick() diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt index 996eaa25c..f7355534a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt @@ -14,6 +14,7 @@ import io.legado.app.constant.PreferKey import io.legado.app.help.ReadBookConfig import io.legado.app.lib.dialogs.selector import io.legado.app.lib.theme.accentColor +import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.primaryColor import io.legado.app.ui.book.read.Help import io.legado.app.ui.book.read.ReadBookActivity @@ -71,6 +72,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { } private fun initView() { + root_view.setBackgroundColor(requireContext().bottomBackground) dsb_text_size.valueFormat = { (it + 5).toString() } diff --git a/app/src/main/res/layout/dialog_read_aloud.xml b/app/src/main/res/layout/dialog_read_aloud.xml index ea04696d4..627bd9414 100644 --- a/app/src/main/res/layout/dialog_read_aloud.xml +++ b/app/src/main/res/layout/dialog_read_aloud.xml @@ -60,6 +60,7 @@ Date: Sun, 1 Mar 2020 21:54:00 +0800 Subject: [PATCH 59/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/config/MoreConfigDialog.kt | 2 +- .../legado/app/ui/book/read/config/ReadStyleDialog.kt | 2 +- app/src/main/res/layout/view_read_menu.xml | 11 +++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt index 9d8ea8ba8..6f472f270 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt @@ -33,7 +33,7 @@ class MoreConfigDialog : DialogFragment() { } dialog?.window?.let { it.setBackgroundDrawableResource(R.color.background) - it.decorView.setPadding(0, 5, 0, 0) + it.decorView.setPadding(0, 0, 0, 0) val attr = it.attributes attr.dimAmount = 0.0f attr.gravity = Gravity.BOTTOM diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt index f7355534a..b51d8c088 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt @@ -42,7 +42,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { } dialog?.window?.let { it.setBackgroundDrawableResource(R.color.background) - it.decorView.setPadding(0, 5, 0, 0) + it.decorView.setPadding(0, 0, 0, 0) val attr = it.attributes attr.dimAmount = 0.0f attr.gravity = Gravity.BOTTOM diff --git a/app/src/main/res/layout/view_read_menu.xml b/app/src/main/res/layout/view_read_menu.xml index 4d4f8f18b..17e2c89f8 100644 --- a/app/src/main/res/layout/view_read_menu.xml +++ b/app/src/main/res/layout/view_read_menu.xml @@ -81,8 +81,9 @@ @@ -90,7 +91,7 @@ android:id="@+id/fabAutoPage" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_margin="10dp" + android:layout_margin="16dp" android:contentDescription="@string/auto_next_page" android:src="@drawable/ic_auto_page" android:tint="@color/tv_text_default" @@ -110,7 +111,7 @@ android:id="@+id/fabReplaceRule" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_margin="10dp" + android:layout_margin="16dp" android:contentDescription="@string/replace_rule_title" android:src="@drawable/ic_find_replace" android:tint="@color/tv_text_default" @@ -130,7 +131,7 @@ android:id="@+id/fabNightTheme" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_margin="10dp" + android:layout_margin="16dp" android:contentDescription="@string/dark_theme" android:src="@drawable/ic_brightness" android:tint="@color/tv_text_default" @@ -143,8 +144,6 @@ - - Date: Sun, 1 Mar 2020 22:11:06 +0800 Subject: [PATCH 60/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 6403 -> 3587 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 3306 -> 1957 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 10383 -> 5435 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 20908 -> 10087 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 34213 -> 15918 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index 80723f59b4d0528559ad47fdc3bd7f96b653b0dc..a7e087bc8b52c9eeeefb6e6a96cfeda3778d9904 100644 GIT binary patch literal 3587 zcma)0Du$#00*M5 z%?5h&pzt&|Hn`zUo$0s7+<+Qk;uHb^3_bsC&_G}n{N_j*YHDdn`I8jHKp_w~`QQ`) z=$1_lbYaN(oxceoE(2kSpQpl~i~>r6AWxyzlq@>41yM6)v4m#0Tn4LiUQO%pew>#> z#X%NYl(G7PEy6_lRj_TcKz1w9_Cl}{Zdz(-4l_t@HZc58@$O(jZ}w<%*4b|@=UY?jMOu{~tI!Q)+p}+y` zIDL><(sHb1jYm%$&)sc46NQPtdzlTrBXs2C1&<`7_I3%>_cii-kcycYEl8Xvlc$Hi zG;>M2oQ1EVfv6-WskfCBYdmHu!sv^kBKe>LD;vm;OpFsu_aS}%V)yax&g1G>8fCWW z2H=28($}crV*9&HVQWDj8v@2m=bx?nB=8O*&?+{JkJ`RZKM8PT!r})|YOybsvOBGFnxQKZiMdUu%NUDc+$2ZRIbJ3 zsxjsG;VuDM#xLF7b_^`de;YYIGS$-skuBx1Uz2?y_)llcJ1;`jhernbhuCG^sUUkt z#`gQhj7LnNg<|q8;v}|Tu)^1;o9bF^g`~U&%lkD_okxBGLshznH>wzgj_+cjA^(z;k1S2sFi%Rl zsYDNdg#5LW$_rI_vE{@o0&U0SXfJ*W6<>`NJ8WGwtKNT72!Y3eGD@fP`} zD$aADl#bqZx_|B-e;pl8Hr_PeQ2*-!j? zuD*1qxf?{qwc_92c8c%hPXjTISS@n>m^=GN0HIov0Fp{U5A26LU?{R6WuopnDP97myGPJfPV5~V+=5x z=mnPxk*DvCCAQDa^MKrCf9$&jx^*`xa=6K92pXQPRtFjRN~0#0_=r61QQ+`53@EC4 z1WEvkO?I3UMDuFqujafg6iAj=6}0n~y}L5=&cOyMoG}o7@==7FL3tiNRYB!HPtsA> z&J!2s`zn7C8^8V+KAv5ZTid#^BZw$XC`*)JIB#jLuU6`e#{my9axzCXq&3l!vnNTa z7j}SgEc8l;(qF?9wHoelP}m7g|2J`yw8>8=HcPJk!Uw@^n54efOX^D2JK=ZVXeL_< zYq4&IEOK_xNU;vp{DM`iuw7Le;Gx%mVw>2&NPG3rNM0-YX|C_GS_1G_xDoZ*Q5eue zy5;QHBE6X9)hvmP9kI%ESv$o_Nwax}gcought`)WCA@rbu3~6In>e1a|7V}N|9y(+ zEl_7@jKuh(t!{&gEZ}Uzz9^ zJG3b_9%shVRAie&)@?I4BU8S+ixBHPEOY7o%O()f1Az+eywQ8T?2v2`UcSO|;=S8X zn)ZlAS`cTw5+6`ZU!9k0%IBy2_thcYr)dq4&Hdht`^U!;fiF8+LdEqRb3pE%@2Ni3 zm{XRc?q#|VdK&W(T}%uVOyw(@`Dmh3sdX)?D>d`Oia%ow?kLxB4(I-Id~Ta&nbX6; zEoJAKX|Q3Y-(dCZ=;QCDNJ*4^$mQ`NiSjwlj!rWOqvR<`zRuu+!R8 zx_ULks3}P~p)h@zwBvu9S{KuG_1}}Cazd-m>7|b8Kdg1`s!dvl zxi{{oN-o>fO7`3p%ZP>!1xAObPk!i|inu<=X_3!L>*{=tZ-eZ3seXOtmME3FOd!2A zV;~jQL1_Dxz80XVb~|^qUOrU02_6#1n&Qm}7UVCrel}hSDg4K6pEYcajzZw{>;vx;)VK%7%t#i?&$6=7Q~HT8dC10*|JezRXz^9EmCNfPzmWu@xDbo0#9 z@EAtiT0V|@b^}(Fw#C>Y!B(!q(sqeTd){17;^~)8j%mNpkn2OQj#hA8P*#HLn#l{j z=f|$Eh8WEli(iun3g+$bWY08&2Yf$5vGy$c1$gS#nY|05DoIlt*}Y5s_C zHkiG;;^3=U`(EbaQYOw~G}nf@AuIqDs5Kw1A6#CIg2{Pr2|;VDTv>X3k-S<;xX%LI;tmIKJx4pbSDS>W4GV)j_=QTP#x~W`VtY|WYjMBz>+xTH zMiE-K@af1U+#jU#!rRmaZtrwTu1i#rE_XHP$Jk17#M z(}RwGw;W+9Ad25a+r5?>=9wP5kFdYm#rxXO_= z5k}k5TA8oS>6reb;Qc4!mpv(mzB2Sr^SnbZrwqC~hqz_d*A2psdK8ds8;U>okVx~C z&SS}!C5P3)8ANHi^z|Uu5wt;)vGA?UQgA9axOA2G#uQ%OnI} zp1fi&ih}cL26&VY$v4S@m}Y`g-8$-KxK5zg{#_tu%IkE#eXECq-S&%Gns{x2R@CP9 zr0nP6l+()JhUBR@Nym3;{Ei9+`iK~}!jIYuWBqvc>y2XNYnOl?XZj_=@B(Aw;hL=9 z}*J+rhos2ru+qS;&jm`dFPVoER5OF2or}ct4*6s)A zP5k*Q66P2R_Xzcdcm;dk005BlN{V2`2^H|H3Ybnou|W~sq^K})yRJ_0CS~bNk&|N< hsYL!q84w76;1luxP{yv5U~ZIvso@=iT0QsJ{{TT$rE>rP literal 6403 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 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index afdcc42eb4b7508e4c9b796aede8c9af7bf96d6d..371e045cdeff6018a3152f988c2578f02a165c17 100644 GIT binary patch literal 5435 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�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 O0000|MoxK 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 literal 34213 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 J);T3K0RY!AHlqLl From b6cb7776e665fd067d36ef10629291bcee74d19c Mon Sep 17 00:00:00 2001 From: fisher Date: Sun, 1 Mar 2020 22:22:51 +0800 Subject: [PATCH 61/91] update special format type --- app/src/main/assets/txtTocRule.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/assets/txtTocRule.json b/app/src/main/assets/txtTocRule.json index d99d765a2..fc5469653 100644 --- a/app/src/main/assets/txtTocRule.json +++ b/app/src/main/assets/txtTocRule.json @@ -59,10 +59,16 @@ "rule": "^(?<= |\\s)[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", "serialNumber": 9 }, + { + "enable": true, + "name": "章/卷 序号 标题", + "rule": "^[  \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|[卷章][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,8})\\s{0,4}.{0,30}$", + "serialNumber": 10 + }, { "enable":false, "name": "顶格标题", "rule": "^\\S.{1,20}$", - "serialNumber": 10 + "serialNumber": 11 } ] From 7c575ca762de47fc0186f3a2f1acc070297dbef3 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 22:27:33 +0800 Subject: [PATCH 62/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5cb5d5179..ce09ff149 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { maven { url 'https://plugins.gradle.org/m2/' } } dependencies { - classpath 'com.android.tools.build:gradle:3.6.0' + classpath 'com.android.tools.build:gradle:3.6.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.3' classpath 'com.google.gms:google-services:4.3.3' From 3d4934adeb2aeefa1a812cca26392298af1c846a Mon Sep 17 00:00:00 2001 From: kunfei Date: Sun, 1 Mar 2020 22:47:58 +0800 Subject: [PATCH 63/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 1b0c7d4b0..b2c3924d8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,8 +75,11 @@ android { } compileOptions { - sourceCompatibility = '1.8' - targetCompatibility = '1.8' + // Flag to enable support for the new language APIs + //coreLibraryDesugaringEnabled true + // Sets Java compatibility to Java 8 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { From 5d2d73895911c26f097590290336df05baed3a30 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 11:45:28 +0800 Subject: [PATCH 64/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/book/read/page/PageView.kt | 5 +-- .../read/page/delegate/CoverPageDelegate.kt | 33 +++++++++---------- .../page/delegate/HorizontalPageDelegate.kt | 8 ++--- .../book/read/page/delegate/PageDelegate.kt | 17 ++-------- .../page/delegate/SimulationPageDelegate.kt | 3 +- .../read/page/delegate/SlidePageDelegate.kt | 27 +++++---------- 6 files changed, 34 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt index c6839b214..ee098e22c 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt @@ -26,12 +26,12 @@ class PageView(context: Context, attrs: AttributeSet) : init { callBack = activity as CallBack - prevPage = ContentView(context) - addView(prevPage) nextPage = ContentView(context) addView(nextPage) curPage = ContentView(context) addView(curPage) + prevPage = ContentView(context) + addView(prevPage) upBg() setWillNotDraw(false) pageFactory = TextPageFactory(this) @@ -40,6 +40,7 @@ class PageView(context: Context, attrs: AttributeSet) : override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) + prevPage.x = -w.toFloat() pageDelegate?.setViewSize(w, h) if (oldw != 0 && oldh != 0) { ReadBook.loadContent() diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt index 21ffcee77..c7d342213 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt @@ -1,14 +1,12 @@ package io.legado.app.ui.book.read.page.delegate import android.graphics.Canvas -import android.graphics.Matrix import android.graphics.drawable.GradientDrawable import io.legado.app.ui.book.read.page.PageView class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { private val shadowDrawableR: GradientDrawable - private val bitmapMatrix = Matrix() init { val shadowColors = intArrayOf(0x66111111, 0x00000000) @@ -38,17 +36,9 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { viewWidth - (touchX - startX) } } - startScroll(touchX.toInt(), 0, distanceX.toInt(), 0) } - override fun onAnimStop() { - curPage.x = 0.toFloat() - if (!isCancel) { - pageView.fillPage(mDirection) - } - } - override fun onDraw(canvas: Canvas) { val offsetX = touchX - startX @@ -57,15 +47,13 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { ) return val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth - bitmap?.let { - if (mDirection == Direction.PREV) { - bitmapMatrix.setTranslate(distanceX, 0.toFloat()) - canvas.drawBitmap(it, bitmapMatrix, null) - } else if (mDirection == Direction.NEXT) { - curPage.translationX = offsetX - } - addShadow(distanceX.toInt(), canvas) + if (!isMoved) return + if (mDirection == Direction.PREV) { + prevPage.translationX = offsetX - viewWidth + } else if (mDirection == Direction.NEXT) { + curPage.translationX = offsetX } + addShadow(distanceX.toInt(), canvas) } private fun addShadow(left: Int, canvas: Canvas) { @@ -77,4 +65,13 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { shadowDrawableR.draw(canvas) } } + + override fun onAnimStop() { + curPage.x = 0.toFloat() + prevPage.x = -viewWidth.toFloat() + if (!isCancel) { + pageView.fillPage(mDirection) + } + } + } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt index 700728b14..b42d04738 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt @@ -8,6 +8,9 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie override fun onTouch(event: MotionEvent) { when (event.action) { + MotionEvent.ACTION_DOWN -> { + abort() + } MotionEvent.ACTION_MOVE -> { if (isTextSelected) { selectText(event) @@ -32,7 +35,6 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie return } setDirection(Direction.PREV) - setBitmap() } else { //如果不存在表示没有下一页了 if (!hasNext()) { @@ -40,7 +42,6 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie return } setDirection(Direction.NEXT) - setBitmap() } } } @@ -55,7 +56,6 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie override fun nextPageByAnim() { if (!hasNext()) return setDirection(Direction.NEXT) - setBitmap() setTouchPoint(viewWidth.toFloat(), 0f) onAnimStart() } @@ -63,8 +63,8 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie override fun prevPageByAnim() { if (!hasPrev()) return setDirection(Direction.PREV) - setBitmap() setTouchPoint(0f, 0f) onAnimStart() } + } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt index 76b40fb78..3ad9c31ce 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt @@ -1,7 +1,6 @@ package io.legado.app.ui.book.read.page.delegate import android.content.Context -import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.RectF import android.view.GestureDetector @@ -15,7 +14,6 @@ import io.legado.app.help.AppConfig import io.legado.app.help.ReadBookConfig import io.legado.app.ui.book.read.page.ContentView import io.legado.app.ui.book.read.page.PageView -import io.legado.app.utils.screenshot import kotlin.math.abs abstract class PageDelegate(protected val pageView: PageView) : @@ -40,8 +38,6 @@ abstract class PageDelegate(protected val pageView: PageView) : protected val curPage: ContentView get() = pageView.curPage protected val prevPage: ContentView get() = pageView.prevPage - protected var bitmap: Bitmap? = null - protected var viewWidth: Int = pageView.width protected var viewHeight: Int = pageView.height @@ -126,11 +122,10 @@ abstract class PageDelegate(protected val pageView: PageView) : } private fun stopScroll() { + isMoved = false isRunning = false isStarted = false pageView.invalidate() - bitmap?.recycle() - bitmap = null } open fun setViewSize(width: Int, height: Int) { @@ -175,14 +170,6 @@ abstract class PageDelegate(protected val pageView: PageView) : mDirection = direction } - open fun setBitmap() { - bitmap = when (mDirection) { - Direction.NEXT -> nextPage.screenshot() - Direction.PREV -> prevPage.screenshot() - else -> null - } - } - /** * 触摸事件处理 */ @@ -321,7 +308,7 @@ abstract class PageDelegate(protected val pageView: PageView) : } open fun onDestroy() { - bitmap?.recycle() + } enum class Direction { diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt index 6d7dd7bf8..2d587ad2b 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt @@ -142,6 +142,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi override fun setDirection(direction: Direction) { super.setDirection(direction) + setBitmap() when (direction) { Direction.PREV -> //上一页滑动不出现对角 @@ -158,7 +159,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi } } - override fun setBitmap() { + fun setBitmap() { when (mDirection) { Direction.PREV -> { prevBitmap = prevPage.screenshot() diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt index 0308d965e..097ef41d9 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt @@ -1,13 +1,10 @@ package io.legado.app.ui.book.read.page.delegate import android.graphics.Canvas -import android.graphics.Matrix import io.legado.app.ui.book.read.page.PageView class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { - private val bitmapMatrix = Matrix() - override fun onAnimStart() { val distanceX: Float when (mDirection) { @@ -28,7 +25,6 @@ class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { viewWidth - (touchX - startX) } } - startScroll(touchX.toInt(), 0, distanceX.toInt(), 0) } @@ -39,26 +35,19 @@ class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { || (mDirection == Direction.PREV && offsetX < 0) ) return - val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth - bitmap?.let { - bitmapMatrix.setTranslate(distanceX, 0.toFloat()) - canvas.drawBitmap(it, bitmapMatrix, null) + if (!isMoved) return + if (mDirection == Direction.PREV) { + prevPage.translationX = offsetX - viewWidth + curPage.translationX = offsetX + } else if (mDirection == Direction.NEXT) { + curPage.translationX = offsetX + nextPage.translationX = curPage.x + viewWidth } } - override fun onScroll() { - val offsetX = touchX - startX - - if ((mDirection == Direction.NEXT && offsetX > 0) - || (mDirection == Direction.PREV && offsetX < 0) - ) return - - curPage.translationX = offsetX - } - override fun onAnimStop() { curPage.x = 0.toFloat() - + prevPage.x = -viewWidth.toFloat() if (!isCancel) { pageView.fillPage(mDirection) } From 320c30da56566ec104933118bdd7341a90fea0f4 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 12:19:24 +0800 Subject: [PATCH 65/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../page/delegate/SimulationPageDelegate.kt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt index 2d587ad2b..54daf98c1 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt @@ -118,6 +118,16 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi mMaxLength = hypot(viewWidth.toDouble(), viewHeight.toDouble()).toFloat() } + override fun onDestroy() { + super.onDestroy() + prevBitmap?.recycle() + prevBitmap = null + curBitmap?.recycle() + curBitmap = null + nextBitmap?.recycle() + nextBitmap = null + } + override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) { super.setStartPoint(x, y, invalidate) calcCornerXY(x, y) @@ -159,7 +169,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi } } - fun setBitmap() { + private fun setBitmap() { when (mDirection) { Direction.PREV -> { prevBitmap = prevPage.screenshot() @@ -210,12 +220,6 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi if (!isCancel) { pageView.fillPage(mDirection) } - prevBitmap?.recycle() - prevBitmap = null - nextBitmap?.recycle() - nextBitmap = null - curBitmap?.recycle() - curBitmap = null } override fun onDraw(canvas: Canvas) { From cfd368f5c112752bffb79ded39d1feab3f64d412 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 13:30:28 +0800 Subject: [PATCH 66/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/page/delegate/SimulationPageDelegate.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt index 54daf98c1..b337a17a1 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt @@ -136,13 +136,13 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi override fun setTouchPoint(x: Float, y: Float, invalidate: Boolean) { super.setTouchPoint(x, y, false) //触摸y中间位置吧y变成屏幕高度 - if ((startY > viewHeight * 0.33 && startY < viewHeight * 0.66) + if ((startY > viewHeight / 3.0 && startY < viewHeight * 2 / 3.0) || mDirection == Direction.PREV ) { touchY = viewHeight.toFloat() } - if (startY > viewHeight * 0.33 && startY < viewHeight / 2.0 + if (startY > viewHeight / 3.0 && startY < viewHeight / 2.0 && mDirection == Direction.NEXT ) { touchY = 1f From de89f651ce49e10f8e269585837a356b3499aca8 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 13:31:26 +0800 Subject: [PATCH 67/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index b78537486..97a273d48 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -6,6 +6,7 @@ * 添加书源登录 * 替换规则实时生效 * 页面最后一行计算是否能放下时不计算行距 +* 优化翻页动画 **2020/03/01** * 修复书源解析的一个bug From f1657637e2ba13633f8a66b3c38a448669090a08 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 14:25:28 +0800 Subject: [PATCH 68/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/read/page/delegate/SlidePageDelegate.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt index 097ef41d9..80e5f97ed 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt @@ -5,6 +5,13 @@ import io.legado.app.ui.book.read.page.PageView class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { + override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) { + curPage.x = 0f + prevPage.x = -viewWidth.toFloat() + nextPage.x = viewWidth.toFloat() + super.setStartPoint(x, y, invalidate) + } + override fun onAnimStart() { val distanceX: Float when (mDirection) { @@ -37,17 +44,18 @@ class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { if (!isMoved) return if (mDirection == Direction.PREV) { - prevPage.translationX = offsetX - viewWidth curPage.translationX = offsetX + prevPage.translationX = offsetX - viewWidth + 1f } else if (mDirection == Direction.NEXT) { + nextPage.translationX = offsetX + viewWidth - 1f curPage.translationX = offsetX - nextPage.translationX = curPage.x + viewWidth } } override fun onAnimStop() { - curPage.x = 0.toFloat() + curPage.x = 0f prevPage.x = -viewWidth.toFloat() + nextPage.x = viewWidth.toFloat() if (!isCancel) { pageView.fillPage(mDirection) } From c44ab4817f42377ae42eca37c2005723f188123a Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 14:26:40 +0800 Subject: [PATCH 69/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt index 80e5f97ed..8a7f3e054 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt @@ -47,8 +47,8 @@ class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { curPage.translationX = offsetX prevPage.translationX = offsetX - viewWidth + 1f } else if (mDirection == Direction.NEXT) { - nextPage.translationX = offsetX + viewWidth - 1f curPage.translationX = offsetX + nextPage.translationX = offsetX + viewWidth - 1f } } From 77bbd731c1e5a4665734737c5322b903843ab068 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 14:32:10 +0800 Subject: [PATCH 70/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/page/delegate/SlidePageDelegate.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt index 8a7f3e054..ee325f099 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt @@ -45,10 +45,10 @@ class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { if (!isMoved) return if (mDirection == Direction.PREV) { curPage.translationX = offsetX - prevPage.translationX = offsetX - viewWidth + 1f + prevPage.translationX = offsetX - viewWidth } else if (mDirection == Direction.NEXT) { curPage.translationX = offsetX - nextPage.translationX = offsetX + viewWidth - 1f + nextPage.translationX = offsetX + viewWidth } } From c29b77f88b63f2c38fd85c85da9beff0e4c2b3ff Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 15:18:43 +0800 Subject: [PATCH 71/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/model/localBook/AnalyzeTxtFile.kt | 2 +- .../legado/app/model/localBook/LocalBook.kt | 14 ++++++++ .../app/ui/book/info/BookInfoActivity.kt | 35 ++++++++++++++++--- .../app/ui/book/info/BookInfoViewModel.kt | 6 +++- app/src/main/res/values/strings.xml | 1 + 5 files changed, 52 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt b/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt index fad747b34..16426a036 100644 --- a/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt +++ b/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt @@ -20,7 +20,7 @@ object AnalyzeTxtFile { private const val BUFFER_SIZE = 512 * 1024 //没有标题的时候,每个章节的最大长度 private const val MAX_LENGTH_WITH_NO_CHAPTER = 10 * 1024 - private val cacheFolder: File by lazy { + val cacheFolder: File by lazy { val rootFile = App.INSTANCE.getExternalFilesDir(null) ?: App.INSTANCE.externalCacheDir ?: App.INSTANCE.cacheDir diff --git a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt index 44ee337b2..c58410cd7 100644 --- a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt +++ b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt @@ -1,8 +1,10 @@ package io.legado.app.model.localBook +import android.net.Uri import androidx.documentfile.provider.DocumentFile import io.legado.app.App import io.legado.app.data.entities.Book +import io.legado.app.utils.FileUtils object LocalBook { @@ -27,4 +29,16 @@ object LocalBook { } } + fun deleteBook(book: Book, deleteOriginal: Boolean) { + if (book.isTxt()) { + val bookFile = + FileUtils.getFile(AnalyzeTxtFile.cacheFolder, book.originName, subDirs = *arrayOf()) + bookFile.delete() + } + + if (deleteOriginal) { + val uri = Uri.parse(book.bookUrl) + DocumentFile.fromSingleUri(App.INSTANCE, uri)?.delete() + } + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index efcddba7c..7db4032aa 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -20,6 +20,9 @@ import io.legado.app.data.entities.BookChapter import io.legado.app.help.BlurTransformation import io.legado.app.help.ImageLoader import io.legado.app.help.IntentDataHelp +import io.legado.app.lib.dialogs.alert +import io.legado.app.lib.dialogs.noButton +import io.legado.app.lib.dialogs.okButton import io.legado.app.ui.audio.AudioPlayActivity import io.legado.app.ui.book.group.GroupSelectDialog import io.legado.app.ui.book.info.edit.BookInfoEditActivity @@ -186,9 +189,7 @@ class BookInfoActivity : } tv_shelf.onClick { if (viewModel.inBookshelf) { - viewModel.delBook { - upTvBookshelf() - } + deleteBook() } else { viewModel.addToBookshelf { upTvBookshelf() @@ -223,6 +224,32 @@ class BookInfoActivity : } } + private fun deleteBook() { + viewModel.bookData.value?.let { + if (it.isLocalBook()) { + alert( + titleResource = R.string.sure, + messageResource = R.string.sure_delete_book_file + ) { + okButton { + viewModel.delBook(true) { + finish() + } + } + noButton { + viewModel.delBook(false) { + finish() + } + } + } + } else { + viewModel.delBook { + upTvBookshelf() + } + } + } + } + private fun openChapterList() { if (viewModel.chapterListData.value.isNullOrEmpty()) { toast(R.string.chapter_list_empty) @@ -320,7 +347,7 @@ class BookInfoActivity : } } else { if (!viewModel.inBookshelf) { - viewModel.delBook(null) + viewModel.delBook() } } } diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt index 1a675e21a..0364696a6 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt @@ -11,6 +11,7 @@ import io.legado.app.data.entities.BookChapter import io.legado.app.help.BookHelp import io.legado.app.model.WebBook import io.legado.app.model.localBook.AnalyzeTxtFile +import io.legado.app.model.localBook.LocalBook import kotlinx.coroutines.Dispatchers.IO class BookInfoViewModel(application: Application) : BaseViewModel(application) { @@ -198,9 +199,12 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { } } - fun delBook(success: (() -> Unit)?) { + fun delBook(deleteOriginal: Boolean = false, success: (() -> Unit)? = null) { execute { bookData.value?.let { + if (it.isLocalBook()) { + LocalBook.deleteBook(it, deleteOriginal) + } App.db.bookDao().delete(it) } inBookshelf = false diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7903cadfb..e7122ec5f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -630,4 +630,5 @@ 其它朗读设置 系统内置字体样式 + 是否删除源文件 From 1b4e3410e04d84854e86697f0b62542f1cdfb7fc Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 15:23:55 +0800 Subject: [PATCH 72/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values/strings.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e7122ec5f..3c92dae8f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -496,8 +496,6 @@ 文章内容为空 正在换源请等待… 目录列表为空 - 正文边距 - Tip边距 字距 基本 From 3fb47380084e4a3bdaeaf019aadf68d3c06d8a23 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 17:09:23 +0800 Subject: [PATCH 73/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt index 49cadb56c..95b5b1431 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt @@ -111,7 +111,7 @@ class BooksFragment : BaseFragment(R.layout.fragment_books), else -> list.sortedByDescending { it.durChapterTime } } val diffResult = DiffUtil - .calculateDiff(BooksDiffCallBack(ArrayList(booksAdapter.getItems()), books)) + .calculateDiff(BooksDiffCallBack(booksAdapter.getItems(), books)) booksAdapter.setItems(books, diffResult) }) } From c0b4b44f6c3f83cd99fcfdd4b36dce44a05b8796 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 17:13:10 +0800 Subject: [PATCH 74/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/ui/book/search/SearchActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt index 1227f4232..aebd0141b 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt @@ -284,7 +284,7 @@ class SearchActivity : VMBaseActivity(R.layout.activity_book_se return } refreshTime = System.currentTimeMillis() - val diffResult = DiffUtil.calculateDiff(DiffCallBack(ArrayList(adapter.getItems()), items)) + val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), items)) adapter.setItems(items, diffResult) } From 3bc2b9d514f6011f30619f8e573ba7af3e1af603 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 18:53:24 +0800 Subject: [PATCH 75/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 50 ++++++++++++----- .../legado/app/service/CheckSourceService.kt | 55 +++++++++++-------- 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e4bc75603..994e103c1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -133,7 +133,9 @@ android:name=".ui.about.DonateActivity" android:launchMode="singleTask" /> - + @@ -144,7 +146,9 @@ - + @@ -159,7 +163,7 @@ + android:launchMode="singleTop"> @@ -171,16 +175,36 @@ android:scheme="yuedu" /> - - - - - - - - - - + + + + + + + + + + diff --git a/app/src/main/java/io/legado/app/service/CheckSourceService.kt b/app/src/main/java/io/legado/app/service/CheckSourceService.kt index d94b3a6d1..a19604ede 100644 --- a/app/src/main/java/io/legado/app/service/CheckSourceService.kt +++ b/app/src/main/java/io/legado/app/service/CheckSourceService.kt @@ -18,11 +18,12 @@ import org.jetbrains.anko.toast import java.util.concurrent.Executors class CheckSourceService : BaseService() { - private var searchPool = - Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() + private val threadCount = AppConfig.threadCount + private var searchPool = Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher() private var task: Coroutine<*>? = null - private val allIds = LinkedHashSet() - private val checkedIds = LinkedHashSet() + private val allIds = ArrayList() + private val checkedIds = ArrayList() + private var processIndex = 0 override fun onCreate() { super.onCreate() @@ -50,30 +51,40 @@ class CheckSourceService : BaseService() { allIds.clear() checkedIds.clear() allIds.addAll(ids) + processIndex = 0 updateNotification(0, getString(R.string.progress_show, 0, allIds.size)) - task = execute(context = searchPool) { - allIds.forEach { sourceUrl -> - App.db.bookSourceDao().getBookSource(sourceUrl)?.let { source -> - val webBook = WebBook(source) - webBook.searchBook("我的", scope = this, context = searchPool) - .onError(IO) { - source.addGroup("失效") - App.db.bookSourceDao().update(source) - }.onFinally { - checkedIds.add(sourceUrl) - updateNotification( - checkedIds.size, - getString(R.string.progress_show, checkedIds.size, allIds.size) - ) - } - } + task = execute { + for (i in 0 until threadCount) { + check() } }.onError { toast("校验书源出错:${it.localizedMessage}") } + } + - task?.invokeOnCompletion { - stopSelf() + private fun check() { + processIndex++ + if (processIndex < allIds.size) { + val sourceUrl = allIds[processIndex] + + App.db.bookSourceDao().getBookSource(sourceUrl)?.let { source -> + val webBook = WebBook(source) + webBook.searchBook("我的", scope = this, context = searchPool) + .onError(IO) { + source.addGroup("失效") + App.db.bookSourceDao().update(source) + }.onFinally { + checkedIds.add(sourceUrl) + updateNotification( + checkedIds.size, + getString(R.string.progress_show, checkedIds.size, allIds.size) + ) + if (processIndex >= allIds.size + threadCount - 1) { + stopSelf() + } + } + } } } From 0ec2172359a0c448b0ec3852d2954d21fbb7a136 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 18:59:33 +0800 Subject: [PATCH 76/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/service/CheckSourceService.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/service/CheckSourceService.kt b/app/src/main/java/io/legado/app/service/CheckSourceService.kt index a19604ede..437bc41c4 100644 --- a/app/src/main/java/io/legado/app/service/CheckSourceService.kt +++ b/app/src/main/java/io/legado/app/service/CheckSourceService.kt @@ -74,7 +74,8 @@ class CheckSourceService : BaseService() { .onError(IO) { source.addGroup("失效") App.db.bookSourceDao().update(source) - }.onFinally { + }.onFinally(IO) { + check() checkedIds.add(sourceUrl) updateNotification( checkedIds.size, From 5f6e39bc9a96ce41563f64eb5bf54a713869b1b5 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 19:05:57 +0800 Subject: [PATCH 77/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/help/storage/ImportOldData.kt | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt index 9087c3e9f..8d0c9cd2c 100644 --- a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt +++ b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt @@ -36,7 +36,7 @@ object ImportOldData { try {// Book source val sourceFile = - FileUtils.createFileIfNotExist(file, "myBookSource.json") + FileUtils.getFile(file, "myBookSource.json") val json = sourceFile.readText() val importCount = importOldSource(json) withContext(Dispatchers.Main) { @@ -49,12 +49,17 @@ object ImportOldData { } try {// Replace rules - val ruleFile = - FileUtils.createFileIfNotExist(file, "myBookReplaceRule.json") - val json = ruleFile.readText() - val importCount = importOldReplaceRule(json) - withContext(Dispatchers.Main) { - context.toast("成功导入替换规则${importCount}") + val ruleFile = FileUtils.getFile(file, "myBookReplaceRule.json") + if (ruleFile.exists()) { + val json = ruleFile.readText() + val importCount = importOldReplaceRule(json) + withContext(Dispatchers.Main) { + context.toast("成功导入替换规则${importCount}") + } + } else { + withContext(Dispatchers.Main) { + context.toast("未找到替换规则") + } } } catch (e: Exception) { withContext(Dispatchers.Main) { From 70ab328d3bb28285f5ec416f2926d784bedba4ff Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 19:07:30 +0800 Subject: [PATCH 78/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 97a273d48..5a9b1b490 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -7,6 +7,7 @@ * 替换规则实时生效 * 页面最后一行计算是否能放下时不计算行距 * 优化翻页动画 +* 优化书源校验 **2020/03/01** * 修复书源解析的一个bug From 53f74cbbedf5a1d974fb488bce7adebdc2dc4ca3 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 19:25:24 +0800 Subject: [PATCH 79/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/ui/config/ThemeConfigFragment.kt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt index 0652350c5..22f5e7f3d 100644 --- a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt @@ -18,7 +18,6 @@ import io.legado.app.lib.dialogs.yesButton import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ColorUtils import io.legado.app.utils.* -import org.jetbrains.anko.defaultSharedPreferences class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener { @@ -27,7 +26,6 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_config_theme) - onSharedPreferenceChanged(requireContext().defaultSharedPreferences, "defaultTheme") } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -49,9 +47,7 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar sharedPreferences ?: return when (key) { PreferKey.launcherIcon -> LauncherIconHelp.changeIcon(getPrefString(key)) - "transparentStatusBar" -> { - recreateActivities() - } + "transparentStatusBar" -> recreateActivities() "colorPrimary", "colorAccent", "colorBackground", @@ -100,7 +96,6 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar upTheme(true) } } - "defaultTheme" -> findPreference(key)?.summary = items[getPrefInt(key)] } } @@ -147,7 +142,6 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar AppConfig.isNightTheme = true } } - putPrefInt("defaultTheme", which) App.INSTANCE.applyDayNight() recreateActivities() } From 52c366da4fec87c6a3479a19fa3a6c8f9c2784b0 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 20:04:08 +0800 Subject: [PATCH 80/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../read/page/delegate/CoverPageDelegate.kt | 51 +++++++++++-------- .../read/page/delegate/SlidePageDelegate.kt | 2 +- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt index c7d342213..7e73700e9 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt @@ -16,27 +16,11 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { shadowDrawableR.gradientType = GradientDrawable.LINEAR_GRADIENT } - override fun onAnimStart() { - val distanceX: Float - when (mDirection) { - Direction.NEXT -> distanceX = - if (isCancel) { - var dis = viewWidth - startX + touchX - if (dis > viewWidth) { - dis = viewWidth.toFloat() - } - viewWidth - dis - } else { - -(touchX + (viewWidth - startX)) - } - else -> distanceX = - if (isCancel) { - -(touchX - startX) - } else { - viewWidth - (touchX - startX) - } - } - startScroll(touchX.toInt(), 0, distanceX.toInt(), 0) + override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) { + curPage.x = 0.toFloat() + prevPage.x = -viewWidth.toFloat() + nextPage.x = 0.toFloat() + super.setStartPoint(x, y, invalidate) } override fun onDraw(canvas: Canvas) { @@ -47,7 +31,7 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { ) return val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth - if (!isMoved) return + if (!isRunning) return if (mDirection == Direction.PREV) { prevPage.translationX = offsetX - viewWidth } else if (mDirection == Direction.NEXT) { @@ -74,4 +58,27 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { } } + override fun onAnimStart() { + val distanceX: Float + when (mDirection) { + Direction.NEXT -> distanceX = + if (isCancel) { + var dis = viewWidth - startX + touchX + if (dis > viewWidth) { + dis = viewWidth.toFloat() + } + viewWidth - dis + } else { + -(touchX + (viewWidth - startX)) + } + else -> distanceX = + if (isCancel) { + -(touchX - startX) + } else { + viewWidth - (touchX - startX) + } + } + startScroll(touchX.toInt(), 0, distanceX.toInt(), 0) + } + } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt index ee325f099..850c6b4ea 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt @@ -42,7 +42,7 @@ class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { || (mDirection == Direction.PREV && offsetX < 0) ) return - if (!isMoved) return + if (!isRunning) return if (mDirection == Direction.PREV) { curPage.translationX = offsetX prevPage.translationX = offsetX - viewWidth From f6b3fb1b496bea92c0054829064b4d0d1352dada Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 20:10:33 +0800 Subject: [PATCH 81/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/ui/book/read/page/PageView.kt | 4 ++-- .../ui/book/read/page/delegate/HorizontalPageDelegate.kt | 2 ++ .../legado/app/ui/book/read/page/delegate/PageDelegate.kt | 8 ++++++-- .../app/ui/book/read/page/delegate/ScrollPageDelegate.kt | 2 ++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt index ee098e22c..18c9cb491 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt @@ -117,7 +117,7 @@ class PageView(context: Context, attrs: AttributeSet) : callBack.screenOffTimerStart() } - fun moveToPrevPage(noAnim: Boolean = true) { + fun moveToPrevPage(noAnim: Boolean = false) { if (noAnim) { fillPage(PageDelegate.Direction.PREV) } else { @@ -125,7 +125,7 @@ class PageView(context: Context, attrs: AttributeSet) : } } - fun moveToNextPage(noAnim: Boolean = true) { + fun moveToNextPage(noAnim: Boolean = false) { if (noAnim) { fillPage(PageDelegate.Direction.NEXT) } else { diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt index b42d04738..a95d01e2a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt @@ -54,6 +54,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie } override fun nextPageByAnim() { + super.nextPageByAnim() if (!hasNext()) return setDirection(Direction.NEXT) setTouchPoint(viewWidth.toFloat(), 0f) @@ -61,6 +62,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie } override fun prevPageByAnim() { + super.prevPageByAnim() if (!hasPrev()) return setDirection(Direction.PREV) setTouchPoint(0f, 0f) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt index 3ad9c31ce..7b649c958 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt @@ -161,9 +161,13 @@ abstract class PageDelegate(protected val pageView: PageView) : open fun onScroll() {}//移动contentView, slidePage - abstract fun nextPageByAnim() + open fun nextPageByAnim() { + abort() + } - abstract fun prevPageByAnim() + open fun prevPageByAnim() { + abort() + } @CallSuper open fun setDirection(direction: Direction) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt index a3434aeed..0e5fb7dff 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt @@ -61,10 +61,12 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) { } override fun nextPageByAnim() { + super.nextPageByAnim() startScroll(0, 0, 0, -ChapterProvider.visibleHeight) } override fun prevPageByAnim() { + super.prevPageByAnim() startScroll(0, 0, 0, ChapterProvider.visibleHeight) } } \ No newline at end of file From b791d443bf63a937ba9a98516a0738711ec6bc60 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 20:19:50 +0800 Subject: [PATCH 82/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/ui/book/read/ReadBookActivity.kt | 1 + .../io/legado/app/ui/replacerule/ReplaceRuleActivity.kt | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 09d29a7f3..0c954dbb0 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -215,6 +215,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo R.id.menu_enable_replace -> ReadBook.book?.let { it.useReplaceRule = !it.useReplaceRule menu?.findItem(R.id.menu_enable_replace)?.isChecked = it.useReplaceRule + onReplaceRuleSave() } R.id.menu_book_info -> ReadBook.book?.let { startActivity(Pair("bookUrl", it.bookUrl)) diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt index e5be539c5..91f7e7b75 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt @@ -141,8 +141,8 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi } private fun observeReplaceRuleData(key: String? = null) { - replaceRuleLiveData?.removeObservers(this) dataInit = false + replaceRuleLiveData?.removeObservers(this) replaceRuleLiveData = if (key.isNullOrEmpty()) { App.db.replaceRuleDao().liveDataAll() } else { @@ -304,22 +304,27 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi } override fun update(vararg rule: ReplaceRule) { + setResult(Activity.RESULT_OK) viewModel.update(*rule) } override fun delete(rule: ReplaceRule) { + setResult(Activity.RESULT_OK) viewModel.delete(rule) } override fun edit(rule: ReplaceRule) { + setResult(Activity.RESULT_OK) ReplaceEditDialog.show(supportFragmentManager, rule.id) } override fun toTop(rule: ReplaceRule) { + setResult(Activity.RESULT_OK) viewModel.toTop(rule) } override fun upOrder() { + setResult(Activity.RESULT_OK) viewModel.upOrder() } } \ No newline at end of file From 59793be8c5ebcbfe340061937f75b66e1d3d6e73 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 20:33:14 +0800 Subject: [PATCH 83/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/book/read/ReadBookActivity.kt | 12 ++++-------- .../io/legado/app/ui/book/read/page/PageView.kt | 16 ---------------- .../ui/book/read/page/delegate/PageDelegate.kt | 9 +++++++++ 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 0c954dbb0..e8e64d16a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -271,18 +271,18 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo } } KeyEvent.KEYCODE_SPACE -> { - page_view.moveToNextPage() + page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.NEXT) return true } getPrefInt(PreferKey.prevKey) -> { if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { - page_view.moveToPrevPage() + page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.PREV) return true } } getPrefInt(PreferKey.nextKey) -> { if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { - page_view.moveToNextPage() + page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.NEXT) return true } } @@ -440,11 +440,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo if (getPrefBoolean("volumeKeyPageOnPlay") || BaseReadAloudService.pause ) { - when (direction) { - PageDelegate.Direction.PREV -> page_view.moveToPrevPage() - PageDelegate.Direction.NEXT -> page_view.moveToNextPage() - else -> return true - } + page_view.pageDelegate?.keyTurnPage(direction) return true } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt index 18c9cb491..7f89a98a7 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt @@ -117,22 +117,6 @@ class PageView(context: Context, attrs: AttributeSet) : callBack.screenOffTimerStart() } - fun moveToPrevPage(noAnim: Boolean = false) { - if (noAnim) { - fillPage(PageDelegate.Direction.PREV) - } else { - pageDelegate?.prevPageByAnim() - } - } - - fun moveToNextPage(noAnim: Boolean = false) { - if (noAnim) { - fillPage(PageDelegate.Direction.NEXT) - } else { - pageDelegate?.nextPageByAnim() - } - } - fun upStyle() { ChapterProvider.upStyle() curPage.upStyle() diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt index 7b649c958..fef7e5b0e 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt @@ -169,6 +169,15 @@ abstract class PageDelegate(protected val pageView: PageView) : abort() } + open fun keyTurnPage(direction: Direction) { + if (isRunning) return + when (direction) { + Direction.NEXT -> nextPageByAnim() + Direction.PREV -> prevPageByAnim() + else -> return + } + } + @CallSuper open fun setDirection(direction: Direction) { mDirection = direction From b9755a317db80ba98caf77af388bf716a316ccbb Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 20:33:47 +0800 Subject: [PATCH 84/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 5a9b1b490..630769a92 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -8,6 +8,7 @@ * 页面最后一行计算是否能放下时不计算行距 * 优化翻页动画 * 优化书源校验 +* 按键翻页有动画了 **2020/03/01** * 修复书源解析的一个bug From cad97612d4ff0e783518af42face62a34b01b35f Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 20:38:05 +0800 Subject: [PATCH 85/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt index 7e73700e9..2f4b2c404 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt @@ -34,10 +34,11 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { if (!isRunning) return if (mDirection == Direction.PREV) { prevPage.translationX = offsetX - viewWidth + addShadow(distanceX.toInt(), canvas) } else if (mDirection == Direction.NEXT) { curPage.translationX = offsetX + addShadow(distanceX.toInt(), canvas) } - addShadow(distanceX.toInt(), canvas) } private fun addShadow(left: Int, canvas: Canvas) { From 13e0977607834187257684fea3595505e0fd9770 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 21:08:25 +0800 Subject: [PATCH 86/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 8 ++++---- .../main/java/io/legado/app/ui/audio/AudioPlayActivity.kt | 4 ++-- .../app/ui/{ => book}/changecover/ChangeCoverDialog.kt | 2 +- .../app/ui/{ => book}/changecover/ChangeCoverViewModel.kt | 2 +- .../legado/app/ui/{ => book}/changecover/CoverAdapter.kt | 2 +- .../app/ui/{ => book}/changesource/ChangeSourceAdapter.kt | 2 +- .../app/ui/{ => book}/changesource/ChangeSourceDialog.kt | 2 +- .../ui/{ => book}/changesource/ChangeSourceViewModel.kt | 2 +- .../legado/app/ui/{ => book}/changesource/DiffCallBack.kt | 2 +- .../app/ui/{ => book}/chapterlist/BookmarkAdapter.kt | 2 +- .../app/ui/{ => book}/chapterlist/BookmarkFragment.kt | 2 +- .../app/ui/{ => book}/chapterlist/ChapterListActivity.kt | 2 +- .../app/ui/{ => book}/chapterlist/ChapterListAdapter.kt | 2 +- .../app/ui/{ => book}/chapterlist/ChapterListFragment.kt | 2 +- .../app/ui/{ => book}/chapterlist/ChapterListViewModel.kt | 2 +- .../legado/app/ui/{ => book}/download/DownloadActivity.kt | 2 +- .../legado/app/ui/{ => book}/download/DownloadAdapter.kt | 2 +- .../app/ui/{ => book}/download/DownloadViewModel.kt | 2 +- .../app/ui/{ => book}/explore/ExploreShowActivity.kt | 2 +- .../app/ui/{ => book}/explore/ExploreShowAdapter.kt | 2 +- .../app/ui/{ => book}/explore/ExploreShowViewModel.kt | 2 +- .../java/io/legado/app/ui/book/info/BookInfoActivity.kt | 6 +++--- .../legado/app/ui/book/info/edit/BookInfoEditActivity.kt | 2 +- .../ui/{importbook => book/local}/ImportBookActivity.kt | 2 +- .../ui/{importbook => book/local}/ImportBookAdapter.kt | 2 +- .../ui/{importbook => book/local}/ImportBookViewModel.kt | 2 +- .../java/io/legado/app/ui/book/read/ReadBookActivity.kt | 4 ++-- .../io/legado/app/ui/main/bookshelf/BookshelfFragment.kt | 4 ++-- .../java/io/legado/app/ui/main/explore/ExploreFragment.kt | 2 +- 29 files changed, 37 insertions(+), 37 deletions(-) rename app/src/main/java/io/legado/app/ui/{ => book}/changecover/ChangeCoverDialog.kt (98%) rename app/src/main/java/io/legado/app/ui/{ => book}/changecover/ChangeCoverViewModel.kt (98%) rename app/src/main/java/io/legado/app/ui/{ => book}/changecover/CoverAdapter.kt (96%) rename app/src/main/java/io/legado/app/ui/{ => book}/changesource/ChangeSourceAdapter.kt (97%) rename app/src/main/java/io/legado/app/ui/{ => book}/changesource/ChangeSourceDialog.kt (99%) rename app/src/main/java/io/legado/app/ui/{ => book}/changesource/ChangeSourceViewModel.kt (99%) rename app/src/main/java/io/legado/app/ui/{ => book}/changesource/DiffCallBack.kt (97%) rename app/src/main/java/io/legado/app/ui/{ => book}/chapterlist/BookmarkAdapter.kt (98%) rename app/src/main/java/io/legado/app/ui/{ => book}/chapterlist/BookmarkFragment.kt (98%) rename app/src/main/java/io/legado/app/ui/{ => book}/chapterlist/ChapterListActivity.kt (98%) rename app/src/main/java/io/legado/app/ui/{ => book}/chapterlist/ChapterListAdapter.kt (98%) rename app/src/main/java/io/legado/app/ui/{ => book}/chapterlist/ChapterListFragment.kt (99%) rename app/src/main/java/io/legado/app/ui/{ => book}/chapterlist/ChapterListViewModel.kt (94%) rename app/src/main/java/io/legado/app/ui/{ => book}/download/DownloadActivity.kt (99%) rename app/src/main/java/io/legado/app/ui/{ => book}/download/DownloadAdapter.kt (98%) rename app/src/main/java/io/legado/app/ui/{ => book}/download/DownloadViewModel.kt (98%) rename app/src/main/java/io/legado/app/ui/{ => book}/explore/ExploreShowActivity.kt (98%) rename app/src/main/java/io/legado/app/ui/{ => book}/explore/ExploreShowAdapter.kt (97%) rename app/src/main/java/io/legado/app/ui/{ => book}/explore/ExploreShowViewModel.kt (97%) rename app/src/main/java/io/legado/app/ui/{importbook => book/local}/ImportBookActivity.kt (99%) rename app/src/main/java/io/legado/app/ui/{importbook => book/local}/ImportBookAdapter.kt (99%) rename app/src/main/java/io/legado/app/ui/{importbook => book/local}/ImportBookViewModel.kt (96%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 994e103c1..f4d3af897 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -182,16 +182,16 @@ android:name=".ui.book.source.debug.BookSourceDebugActivity" android:launchMode="singleTop" /> Date: Mon, 2 Mar 2020 21:10:18 +0800 Subject: [PATCH 87/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/ui/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/README.md b/app/src/main/java/io/legado/app/ui/README.md index a8bf2b216..b408eb6b9 100644 --- a/app/src/main/java/io/legado/app/ui/README.md +++ b/app/src/main/java/io/legado/app/ui/README.md @@ -7,14 +7,14 @@ * book\read 书籍阅读界面 * book\search 搜索书籍界面 * book\source 搜索书源界面 -* changeCover 封面换源界面 -* changeSource 换源界面 -* chapterList 目录界面 -* config 配置界面 -* download 下载界面 -* explore 发现界面 +* book\changeCover 封面换源界面 +* book\changeSource 换源界面 +* book\chapterList 目录界面 +* book\download 下载界面 +* book\explore 发现界面 +* book\local 书籍导入界面 * fileChooser 文件选择界面 -* importBook 书籍导入界面 +* config 配置界面 * main 主界面 * qrCode 二维码扫描界面 * replaceRule 替换净化界面 From 0983e43eafaaecffb1ebef556dcbf44277d4dcb0 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 21:12:49 +0800 Subject: [PATCH 88/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/ui/book/search/SearchActivity.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt index aebd0141b..a41fcd656 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt @@ -280,12 +280,16 @@ class SearchActivity : VMBaseActivity(R.layout.activity_book_se */ @Synchronized private fun upSearchItems(items: List, isMandatoryUpdate: Boolean) { - if (!isMandatoryUpdate && System.currentTimeMillis() - refreshTime < 1000) { + if (!isMandatoryUpdate && System.currentTimeMillis() - refreshTime < 500) { return } refreshTime = System.currentTimeMillis() - val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), items)) - adapter.setItems(items, diffResult) + try { + val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), items)) + adapter.setItems(items, diffResult) + } catch (e: Exception) { + e.printStackTrace() + } } /** From fa4ba277e28cb8a8ed1703f0be94ecf92e8f956c Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 21:22:53 +0800 Subject: [PATCH 89/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/service/CheckSourceService.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/service/CheckSourceService.kt b/app/src/main/java/io/legado/app/service/CheckSourceService.kt index 437bc41c4..19c839aff 100644 --- a/app/src/main/java/io/legado/app/service/CheckSourceService.kt +++ b/app/src/main/java/io/legado/app/service/CheckSourceService.kt @@ -64,7 +64,9 @@ class CheckSourceService : BaseService() { private fun check() { - processIndex++ + synchronized(this) { + processIndex++ + } if (processIndex < allIds.size) { val sourceUrl = allIds[processIndex] @@ -81,8 +83,10 @@ class CheckSourceService : BaseService() { checkedIds.size, getString(R.string.progress_show, checkedIds.size, allIds.size) ) - if (processIndex >= allIds.size + threadCount - 1) { - stopSelf() + synchronized(this) { + if (processIndex >= allIds.size + threadCount - 1) { + stopSelf() + } } } } From 0c3d9f5f1e5b5ca3aab9f7fc0a58d65252d291f3 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 21:23:42 +0800 Subject: [PATCH 90/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/service/CheckSourceService.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/service/CheckSourceService.kt b/app/src/main/java/io/legado/app/service/CheckSourceService.kt index 19c839aff..336dfb18d 100644 --- a/app/src/main/java/io/legado/app/service/CheckSourceService.kt +++ b/app/src/main/java/io/legado/app/service/CheckSourceService.kt @@ -69,7 +69,6 @@ class CheckSourceService : BaseService() { } if (processIndex < allIds.size) { val sourceUrl = allIds[processIndex] - App.db.bookSourceDao().getBookSource(sourceUrl)?.let { source -> val webBook = WebBook(source) webBook.searchBook("我的", scope = this, context = searchPool) From 4457ee25192f766736ad3503191f6d41f03f35fe Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 2 Mar 2020 22:44:08 +0800 Subject: [PATCH 91/91] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/audio/AudioPlayActivity.kt | 5 +-- .../ui/book/changecover/ChangeCoverDialog.kt | 4 +-- .../book/changesource/ChangeSourceDialog.kt | 16 +++++---- .../app/ui/book/info/BookInfoActivity.kt | 9 ++--- .../io/legado/app/utils/MenuExtensions.kt | 2 ++ .../main/res/layout/activity_audio_play.xml | 11 +++--- .../main/res/layout/activity_book_info.xml | 9 +++-- .../main/res/layout/dialog_change_cover.xml | 27 ++++++++++++++ .../main/res/layout/dialog_change_source.xml | 36 +++++++++++-------- app/src/main/res/menu/change_source.xml | 6 ++-- app/src/main/res/values/styles.xml | 1 - 11 files changed, 86 insertions(+), 40 deletions(-) create mode 100644 app/src/main/res/layout/dialog_change_cover.xml diff --git a/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt b/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt index adc7e0662..502e49986 100644 --- a/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt +++ b/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt @@ -46,8 +46,9 @@ class AudioPlayActivity : private var adjustProgress = false override fun onActivityCreated(savedInstanceState: Bundle?) { - title_bar.background.alpha = 0 - AudioPlay.titleData.observe(this, Observer { title_bar.title = it }) + setSupportActionBar(toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + AudioPlay.titleData.observe(this, Observer { toolbar.title = it }) AudioPlay.coverData.observe(this, Observer { upCover(it) }) viewModel.initData(intent) initView() diff --git a/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt index 0576f68f2..7a091c145 100644 --- a/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt @@ -52,12 +52,12 @@ class ChangeCoverDialog : DialogFragment(), callBack = activity as? CallBack viewModel = getViewModel(ChangeCoverViewModel::class.java) viewModel.callBack = this - return inflater.inflate(R.layout.dialog_change_source, container) + return inflater.inflate(R.layout.dialog_change_cover, container) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.searchStateData.observe(this, Observer { + viewModel.searchStateData.observe(viewLifecycleOwner, Observer { refresh_progress_bar.isAutoLoading = it }) tool_bar.setTitle(R.string.change_cover_source) diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt index 8edcae4b4..4ef9d56a3 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt @@ -16,9 +16,11 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import io.legado.app.R import io.legado.app.constant.PreferKey +import io.legado.app.constant.Theme import io.legado.app.data.entities.Book import io.legado.app.data.entities.SearchBook import io.legado.app.ui.widget.recycler.VerticalDivider +import io.legado.app.utils.applyTint import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getViewModel import io.legado.app.utils.putPrefBoolean @@ -69,10 +71,8 @@ class ChangeSourceDialog : DialogFragment(), super.onViewCreated(view, savedInstanceState) viewModel.initData(arguments) showTitle() - tool_bar.inflateMenu(R.menu.change_source) - tool_bar.setOnMenuItemClickListener(this) - initRecyclerView() initMenu() + initRecyclerView() initSearchView() initLiveData() viewModel.loadDbSearchBook() @@ -85,6 +85,9 @@ class ChangeSourceDialog : DialogFragment(), } private fun initMenu() { + tool_bar.inflateMenu(R.menu.change_source) + tool_bar.menu.applyTint(requireContext(), Theme.getTheme()) + tool_bar.setOnMenuItemClickListener(this) tool_bar.menu.findItem(R.id.menu_load_toc)?.isChecked = getPrefBoolean(PreferKey.changeSourceLoadToc) } @@ -110,16 +113,15 @@ class ChangeSourceDialog : DialogFragment(), } private fun initSearchView() { - val searchView = tool_bar.menu.findItem(R.id.menu_search).actionView as SearchView - searchView.setOnCloseListener { + search_view.setOnCloseListener { showTitle() false } - searchView.setOnSearchClickListener { + search_view.setOnSearchClickListener { tool_bar.title = "" tool_bar.subtitle = "" } - searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + search_view.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String?): Boolean { return false } diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index 2aa6f1848..782b538ee 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -24,13 +24,13 @@ import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.noButton import io.legado.app.lib.dialogs.okButton import io.legado.app.ui.audio.AudioPlayActivity +import io.legado.app.ui.book.changecover.ChangeCoverDialog +import io.legado.app.ui.book.changesource.ChangeSourceDialog +import io.legado.app.ui.book.chapterlist.ChapterListActivity import io.legado.app.ui.book.group.GroupSelectDialog import io.legado.app.ui.book.info.edit.BookInfoEditActivity import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.source.edit.BookSourceEditActivity -import io.legado.app.ui.book.changecover.ChangeCoverDialog -import io.legado.app.ui.book.changesource.ChangeSourceDialog -import io.legado.app.ui.book.chapterlist.ChapterListActivity import io.legado.app.utils.getViewModel import io.legado.app.utils.gone import io.legado.app.utils.visible @@ -55,7 +55,8 @@ class BookInfoActivity : get() = getViewModel(BookInfoViewModel::class.java) override fun onActivityCreated(savedInstanceState: Bundle?) { - title_bar.background.alpha = 0 + setSupportActionBar(toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) tv_intro.movementMethod = ScrollingMovementMethod.getInstance() viewModel.bookData.observe(this, Observer { showBook(it) }) viewModel.chapterListData.observe(this, Observer { upLoading(false, it) }) diff --git a/app/src/main/java/io/legado/app/utils/MenuExtensions.kt b/app/src/main/java/io/legado/app/utils/MenuExtensions.kt index d8ecff3e1..9f13d543d 100644 --- a/app/src/main/java/io/legado/app/utils/MenuExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/MenuExtensions.kt @@ -1,5 +1,6 @@ package io.legado.app.utils +import android.annotation.SuppressLint import android.content.Context import android.view.Menu import android.view.MenuItem @@ -13,6 +14,7 @@ import io.legado.app.lib.theme.primaryTextColor import java.lang.reflect.Method import java.util.* +@SuppressLint("RestrictedApi") fun Menu.applyTint(context: Context, theme: Theme = Theme.Auto): Menu = this.let { menu -> if (menu is MenuBuilder) { menu.setOptionalIconsVisible(true) diff --git a/app/src/main/res/layout/activity_audio_play.xml b/app/src/main/res/layout/activity_audio_play.xml index 71a7edfe8..7869b3b1c 100644 --- a/app/src/main/res/layout/activity_audio_play.xml +++ b/app/src/main/res/layout/activity_audio_play.xml @@ -21,10 +21,13 @@ android:layout_height="match_parent" android:background="#50000000" /> - + app:layout_constraintTop_toBottomOf="@+id/toolbar" /> - @@ -38,7 +41,7 @@ android:scaleType="centerCrop" android:src="@drawable/image_cover_default" app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintTop_toBottomOf="@+id/title_bar" /> + app:layout_constraintTop_toBottomOf="@+id/toolbar" /> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_change_source.xml b/app/src/main/res/layout/dialog_change_source.xml index 1199d4538..f007fae87 100644 --- a/app/src/main/res/layout/dialog_change_source.xml +++ b/app/src/main/res/layout/dialog_change_source.xml @@ -1,31 +1,39 @@ - - + android:background="@color/background_menu"> + + + + + + + android:layout_height="2dp" /> + android:layout_height="match_parent" /> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/menu/change_source.xml b/app/src/main/res/menu/change_source.xml index 9c24f2dfa..f9f49cdd6 100644 --- a/app/src/main/res/menu/change_source.xml +++ b/app/src/main/res/menu/change_source.xml @@ -4,9 +4,9 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index d5c9e7104..4cc3907e0 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -33,7 +33,6 @@ @style/Style.PopupMenu @color/transparent true - 0dp