Просмотр исходного кода

feat(Cache/Export): ✨ 新增 开启自定义导出 菜单与对话框

Discut 3 лет назад
Родитель
Сommit
d8c5d79d2d

+ 1 - 0
app/src/main/java/io/legado/app/constant/PreferKey.kt

@@ -54,6 +54,7 @@ object PreferKey {
     const val webDavAccount = "web_dav_account"
     const val webDavPassword = "web_dav_password"
     const val webDavDir = "webDavDir"
+    const val enableCustomExport = "enableCustomExport"
     const val exportToWebDav = "webDavCacheBackup"
     const val exportNoChapterName = "exportNoChapterName"
     const val exportType = "exportType"

+ 6 - 0
app/src/main/java/io/legado/app/help/config/AppConfig.kt

@@ -268,6 +268,12 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
         set(value) {
             appCtx.putPrefBoolean(PreferKey.exportNoChapterName, value)
         }
+    var enableCustomExport: Boolean
+        get() = appCtx.getPrefBoolean(PreferKey.enableCustomExport)
+        set(value) {
+            appCtx.putPrefBoolean(PreferKey.enableCustomExport, value)
+        }
+
     var exportType: Int
         get() = appCtx.getPrefInt(PreferKey.exportType)
         set(value) {

+ 124 - 0
app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt

@@ -4,7 +4,9 @@ import android.annotation.SuppressLint
 import android.os.Bundle
 import android.view.Menu
 import android.view.MenuItem
+import android.view.View
 import androidx.activity.viewModels
+import androidx.appcompat.app.AlertDialog
 import androidx.recyclerview.widget.LinearLayoutManager
 import io.legado.app.R
 import io.legado.app.base.VMBaseActivity
@@ -17,6 +19,7 @@ import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookGroup
 import io.legado.app.databinding.ActivityCacheBookBinding
 import io.legado.app.databinding.DialogEditTextBinding
+import io.legado.app.databinding.DialogSelectSectionExportBinding
 import io.legado.app.help.book.isAudio
 import io.legado.app.help.config.AppConfig
 import io.legado.app.lib.dialogs.SelectItem
@@ -32,7 +35,11 @@ import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.*
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
+import androidx.core.widget.doOnTextChanged
 
+/**
+ * cache/download 缓存界面
+ */
 class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>(),
     CacheAdapter.CallBack {
 
@@ -88,6 +95,8 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
 
     override fun onMenuOpened(featureId: Int, menu: Menu): Boolean {
         menu.findItem(R.id.menu_enable_replace)?.isChecked = AppConfig.exportUseReplace
+        // 菜单打开时读取状态[enableCustomExport]
+        menu.findItem(R.id.menu_enable_custom_export)?.isChecked = AppConfig.enableCustomExport
         menu.findItem(R.id.menu_export_no_chapter_name)?.isChecked = AppConfig.exportNoChapterName
         menu.findItem(R.id.menu_export_web_dav)?.isChecked = AppConfig.exportToWebDav
         menu.findItem(R.id.menu_export_pics_file)?.isChecked = AppConfig.exportPictureFile
@@ -108,6 +117,9 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
         }
     }
 
+    /**
+     * 菜单按下回调
+     */
     override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
         when (item.itemId) {
             R.id.menu_download -> {
@@ -126,6 +138,8 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
             }
             R.id.menu_export_all -> exportAll()
             R.id.menu_enable_replace -> AppConfig.exportUseReplace = !item.isChecked
+            // 更改菜单状态[enableCustomExport]
+            R.id.menu_enable_custom_export -> AppConfig.enableCustomExport = !item.isChecked
             R.id.menu_export_no_chapter_name -> AppConfig.exportNoChapterName = !item.isChecked
             R.id.menu_export_web_dav -> AppConfig.exportToWebDav = !item.isChecked
             R.id.menu_export_pics_file -> AppConfig.exportPictureFile = !item.isChecked
@@ -233,6 +247,8 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
         val path = ACache.get().getAsString(exportBookPathKey)
         if (path.isNullOrEmpty()) {
             selectExportFolder(position)
+        } else if (AppConfig.enableCustomExport) {// 启用自定义导出
+            configExportSection(path, position);
         } else {
             startExport(path, position)
         }
@@ -247,6 +263,102 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
         }
     }
 
+    /**
+     * 配置自定义导出对话框
+     *
+     * @param path  导出路径
+     * @param position  book位置
+     * @author Discut
+     * @since 1.0.0
+     */
+    private fun configExportSection(path: String, position: Int) {
+        if (AppConfig.enableCustomExport) {
+            val alertBinding = DialogSelectSectionExportBinding.inflate(layoutInflater)
+                .apply {
+                    cbAllExport.isChecked = true
+                    cbSelectExport.isChecked = false
+                    etEpubSize.isEnabled = false
+                    etInputScope.isEnabled = false
+                    tvAllExport.setOnClickListener {
+                        cbAllExport.isChecked = true
+                    }
+                    tvSelectExport.setOnClickListener {
+                        cbSelectExport.isChecked = true
+                    }
+                    cbSelectExport.onCheckedChangeListener = { _, isChecked ->
+                        if (isChecked) {
+                            etEpubSize.isEnabled = true
+                            etInputScope.isEnabled = true
+                            cbAllExport.isChecked = false
+                        }
+                    }
+                    cbAllExport.onCheckedChangeListener = { _, isChecked ->
+                        if (isChecked) {
+                            etEpubSize.isEnabled = false
+                            etInputScope.isEnabled = false
+                            cbSelectExport.isChecked = false
+                        }
+                    }
+
+                    etInputScope.onFocusChangeListener =
+                        View.OnFocusChangeListener { v, hasFocus ->
+                            if (hasFocus) {
+                                etInputScope.hint = "例如:1-5,8,10-18"
+                            } else {
+                                etInputScope.hint = ""
+                            }
+                        }
+                    etInputScope.doOnTextChanged { text, start, before, count ->
+                        var a: Int = 1
+                        a = 2
+                    }
+
+                }
+            val alertDialog = alert(titleResource = R.string.select_section_export) {
+                customView { alertBinding.root }
+                positiveButton("确认")
+                //okButton {
+//                    alertBinding.apply {
+//                        it.dismiss()
+//                        if (cbAllExport.isChecked) {
+//                            // export all sections of the book.
+//                            startExport(path, position)
+//                        } else if (cbSelectExport.isChecked) {
+//
+//                        }
+//                    }
+                //}
+                cancelButton()
+            }
+            alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
+                alertBinding.apply {
+                    val text = etInputScope.text
+                    if (!verificationField(text.toString())) {
+                        etInputScope.error = "请输入正确的范围"
+                    } else {
+                        etInputScope.error = null
+                        val toInt = etEpubSize.text.toString().toInt()
+                        startExport(path, position, toInt, text.toString())
+                    }
+                }
+
+            }
+
+        }
+    }
+
+    /**
+     * 验证 输入的范围 是否正确
+     *
+     * @since 1.0.0
+     * @author Discut
+     * @param text 输入的范围 字符串
+     * @return 是否正确
+     */
+    private fun verificationField(text: String): Boolean {
+        return text.matches(Regex("\\d+(-\\d+)?(,\\d+(-\\d+)?)*"))
+    }
+
     private fun selectExportFolder(exportPosition: Int) {
         val default = arrayListOf<SelectItem<Int>>()
         val path = ACache.get().getAsString(exportBookPathKey)
@@ -259,6 +371,18 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
         }
     }
 
+    private fun startExport(path: String, exportPosition: Int, size: Int, scope: String) {
+        if (exportPosition >= 0) {
+            adapter.getItem(exportPosition)?.let { book ->
+                when (AppConfig.exportType) {
+                    1 -> viewModel.exportEPUB(path, book, size, scope)
+                    // 目前仅支持 epub
+                    //else -> viewModel.export(path, book)
+                }
+            }
+        }
+    }
+
     private fun startExport(path: String, exportPosition: Int) {
         if (exportPosition == -10) {
             if (adapter.getItems().isNotEmpty()) {

+ 16 - 0
app/src/main/java/io/legado/app/ui/book/cache/CustomExportSectionDialog.kt

@@ -0,0 +1,16 @@
+package io.legado.app.ui.book.cache
+
+import android.os.Bundle
+import android.view.View
+import io.legado.app.R
+import io.legado.app.base.BaseDialogFragment
+import io.legado.app.databinding.DialogSelectSectionExportBinding
+import io.legado.app.utils.viewbindingdelegate.viewBinding
+
+class CustomExportSectionDialog() : BaseDialogFragment(R.layout.dialog_select_section_export) {
+
+    private val binding by viewBinding(DialogSelectSectionExportBinding::bind)
+    override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
+        TODO("Not yet implemented")
+    }
+}

+ 110 - 0
app/src/main/res/layout/dialog_select_section_export.xml

@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="16dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <!--        <androidx.core.widget.NestedScrollView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:overScrollMode="ifContentScrolls"
+                    android:paddingTop="6dp">-->
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="12dp"
+                android:orientation="horizontal">
+
+                <io.legado.app.ui.widget.checkbox.SmoothCheckBox
+                    android:id="@+id/cb_all_export"
+                    android:layout_width="20dp"
+                    android:layout_height="20dp"
+                    android:layout_marginStart="6dp"
+                    android:layout_marginEnd="16dp" />
+
+                <TextView
+                    android:id="@+id/tv_all_export"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="全部导出"
+                    android:textSize="18sp" />
+
+            </LinearLayout>
+
+<!--            <com.google.android.material.divider.MaterialDivider-->
+<!--                android:layout_width="match_parent"-->
+<!--                android:layout_height="wrap_content" />-->
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="12dp"
+                android:orientation="horizontal">
+
+                <io.legado.app.ui.widget.checkbox.SmoothCheckBox
+                    android:id="@+id/cb_select_export"
+                    android:layout_width="20dp"
+                    android:layout_height="20dp"
+                    android:layout_marginStart="6dp"
+                    android:layout_marginEnd="16dp" />
+
+                <TextView
+                    android:id="@+id/tv_select_export"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="选择导出"
+                    android:textSize="18sp" />
+
+            </LinearLayout>
+
+
+            <io.legado.app.ui.widget.text.TextInputLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="12dp"
+                android:hint="每个文件包含章节大小">
+
+                <io.legado.app.lib.theme.view.ThemeEditText
+                    android:id="@+id/et_epub_size"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:inputType="number"
+                    android:text="1"
+                    tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
+
+            </io.legado.app.ui.widget.text.TextInputLayout>
+
+            <io.legado.app.ui.widget.text.TextInputLayout
+                android:id="@+id/ly_et_input_scope"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:hint="输出章节序号">
+
+                <io.legado.app.lib.theme.view.ThemeEditText
+                    android:id="@+id/et_input_scope"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
+
+            </io.legado.app.ui.widget.text.TextInputLayout>
+
+        </LinearLayout>
+
+        <!--        </androidx.core.widget.NestedScrollView>-->
+    </LinearLayout>
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 7 - 0
app/src/main/res/menu/book_cache.xml

@@ -31,6 +31,13 @@
         android:checkable="true"
         app:showAsAction="never" />
 
+    <!--自定义导出章节 选择菜单-->
+    <item
+        android:id="@+id/menu_enable_custom_export"
+        android:title="@string/custom_export_section"
+        android:checkable="true"
+        app:showAsAction="never" />
+
     <item
         android:id="@+id/menu_export_web_dav"
         android:title="@string/export_to_web_dav"

+ 2 - 0
app/src/main/res/values-zh/strings.xml

@@ -31,6 +31,7 @@
     <string name="delete_all">删除所有</string>
     <string name="replace">替换</string>
     <string name="replace_purify">替换净化</string>
+    <string name="custom_export_section">自定义导出章节</string>
     <string name="replace_purify_desc">配置替换净化规则</string>
     <string name="not_available">暂无</string>
     <string name="enable">启用</string>
@@ -346,6 +347,7 @@
     <string name="ps_show_all_find">关闭则只显示勾选源的发现</string>
     <string name="update_toc">更新目录</string>
     <string name="txt_toc_rule">TXT 目录规则</string>
+    <string name="select_section_export">选择待导出章节</string>
     <string name="set_charset">设置编码</string>
     <string name="swap_sort">倒序-顺序</string>
     <string name="sort">排序</string>

+ 2 - 0
app/src/main/res/values/strings.xml

@@ -30,6 +30,7 @@
     <string name="edit">Edit</string>
     <string name="delete">Delete</string>
     <string name="delete_all">Delete all</string>
+    <string name="custom_export_section">Custom export chapter</string>
     <string name="replace">Replace</string>
     <string name="replace_purify">Replacement</string>
     <string name="replace_purify_desc">Configure replacement rules</string>
@@ -347,6 +348,7 @@
     <string name="ps_show_all_find">Display the selected origin\'s Discovery if closed</string>
     <string name="update_toc">Update chapters</string>
     <string name="txt_toc_rule">Txt Chapters Rule</string>
+    <string name="select_section_export">Choose some chapters to be exported</string>
     <string name="set_charset">Text encoding</string>
     <string name="swap_sort">Ascending/Descending order</string>
     <string name="sort">Sort</string>