kunfei %!s(int64=3) %!d(string=hai) anos
pai
achega
777fd81bad

+ 7 - 2
app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt

@@ -20,12 +20,17 @@ import io.legado.app.lib.dialogs.alert
 import io.legado.app.lib.theme.primaryTextColor
 import io.legado.app.ui.book.read.ReadBookActivity
 import io.legado.app.ui.book.search.SearchActivity
-import io.legado.app.utils.*
+import io.legado.app.utils.applyTint
+import io.legado.app.utils.cnCompare
+import io.legado.app.utils.getInt
+import io.legado.app.utils.putInt
+import io.legado.app.utils.startActivity
 import io.legado.app.utils.viewbindingdelegate.viewBinding
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import java.text.SimpleDateFormat
+import java.util.Locale
 
 class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
 
@@ -149,7 +154,7 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
     inner class RecordAdapter(context: Context) :
         RecyclerAdapter<ReadRecordShow, ItemReadRecordBinding>(context) {
 
-        private val dateFormat = SimpleDateFormat("yyyy-MM-dd")
+        private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
 
         override fun getViewBinding(parent: ViewGroup): ItemReadRecordBinding {
             return ItemReadRecordBinding.inflate(inflater, parent, false)

+ 13 - 6
app/src/main/java/io/legado/app/ui/book/bookmark/AllBookmarkViewModel.kt

@@ -1,10 +1,10 @@
 package io.legado.app.ui.book.bookmark
 
-import android.annotation.SuppressLint
 import android.app.Application
 import android.net.Uri
 import androidx.documentfile.provider.DocumentFile
 import io.legado.app.base.BaseViewModel
+import io.legado.app.constant.AppLog
 import io.legado.app.data.appDb
 import io.legado.app.utils.*
 import java.io.File
@@ -16,25 +16,32 @@ class AllBookmarkViewModel(application: Application) : BaseViewModel(application
 
 
     @Suppress("BlockingMethodInNonBlockingContext")
-    @SuppressLint("SimpleDateFormat")
     fun saveToFile(treeUri: Uri) {
         execute {
-            val dateFormat = SimpleDateFormat("yyMMddHHmmss")
+            val dateFormat = SimpleDateFormat("yyMMddHHmmss", Locale.getDefault())
+            val bookmark = appDb.bookmarkDao.all
             if (treeUri.isContentScheme()) {
                 val doc = DocumentFile.fromTreeUri(context, treeUri)
                     ?.createFile("", "bookmark-${dateFormat.format(Date())}")
                 doc?.let {
                     context.contentResolver.openOutputStream(doc.uri)!!.use {
-                        GSON.writeToOutputStream(it, appDb.bookmarkDao.all)
+                        GSON.writeToOutputStream(it, bookmark)
                     }
                 }
             } else {
                 val path = treeUri.path!!
-                val file = FileUtils.createFileIfNotExist(File(path), "bookmark-${dateFormat.format(Date())}")
+                val file = FileUtils.createFileIfNotExist(
+                    File(path),
+                    "bookmark-${dateFormat.format(Date())}"
+                )
                 FileOutputStream(file).use {
-                    GSON.writeToOutputStream(it, appDb.bookmarkDao.all)
+                    GSON.writeToOutputStream(it, bookmark)
                 }
             }
+        }.onError {
+            AppLog.put("导出失败\n${it.localizedMessage}", it, true)
+        }.onSuccess {
+            context.toastOnUi("导出成功")
         }
     }
 

+ 21 - 3
app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt

@@ -24,6 +24,7 @@ import io.legado.app.lib.theme.primaryTextColor
 import io.legado.app.model.ReadBook
 import io.legado.app.ui.about.AppLogDialog
 import io.legado.app.ui.book.toc.rule.TxtTocRuleDialog
+import io.legado.app.ui.file.HandleFileContract
 import io.legado.app.ui.widget.dialog.WaitDialog
 import io.legado.app.utils.*
 import io.legado.app.utils.viewbindingdelegate.viewBinding
@@ -41,6 +42,11 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
     private var menu: Menu? = null
     private var searchView: SearchView? = null
     private val waitDialog by lazy { WaitDialog(this) }
+    private val exportDir = registerForActivityResult(HandleFileContract()) {
+        it.uri?.let { uri ->
+            viewModel.saveBookmark(uri)
+        }
+    }
 
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         tabLayout = binding.titleBar.findViewById(R.id.tab_layout)
@@ -71,9 +77,6 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
     override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
         menuInflater.inflate(R.menu.book_toc, menu)
         this.menu = menu
-        viewModel.bookData.value?.let {
-            menu.setGroupVisible(R.id.menu_group_text, it.isLocalTxt)
-        }
         val search = menu.findItem(R.id.menu_search)
         searchView = (search.actionView as SearchView).apply {
             applyTint(primaryTextColor)
@@ -110,6 +113,15 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
     }
 
     override fun onMenuOpened(featureId: Int, menu: Menu): Boolean {
+        if (tabLayout.selectedTabPosition == 1) {
+            menu.setGroupVisible(R.id.menu_group_bookmark, true)
+            menu.setGroupVisible(R.id.menu_group_toc, false)
+            menu.setGroupVisible(R.id.menu_group_text, false)
+        } else {
+            menu.setGroupVisible(R.id.menu_group_bookmark, false)
+            menu.setGroupVisible(R.id.menu_group_toc, true)
+            menu.setGroupVisible(R.id.menu_group_text, viewModel.bookData.value?.isLocalTxt == true)
+        }
         menu.findItem(R.id.menu_use_replace)?.isChecked =
             AppConfig.tocUiUseReplace
         menu.findItem(R.id.menu_split_long_chapter)?.isChecked =
@@ -122,6 +134,7 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
             R.id.menu_toc_regex -> showDialogFragment(
                 TxtTocRuleDialog(viewModel.bookData.value?.tocUrl)
             )
+
             R.id.menu_split_long_chapter -> {
                 viewModel.bookData.value?.let { book ->
                     item.isChecked = !item.isChecked
@@ -129,6 +142,7 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
                     upBookAndToc(book)
                 }
             }
+
             R.id.menu_reverse_toc -> viewModel.reverseToc {
                 viewModel.chapterListCallBack?.upChapterList(searchView?.query?.toString())
                 setResult(RESULT_OK, Intent().apply {
@@ -136,11 +150,15 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
                     putExtra("chapterPos", 0)
                 })
             }
+
             R.id.menu_use_replace -> {
                 AppConfig.tocUiUseReplace = !item.isChecked
                 viewModel.chapterListCallBack?.clearDisplayTitle()
                 viewModel.chapterListCallBack?.upChapterList(searchView?.query?.toString())
             }
+
+            R.id.menu_export -> exportDir.launch()
+
             R.id.menu_log -> showDialogFragment<AppLogDialog>()
         }
         return super.onCompatOptionsItemSelected(item)

+ 50 - 0
app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt

@@ -2,11 +2,26 @@ package io.legado.app.ui.book.toc
 
 
 import android.app.Application
+import android.net.Uri
+import androidx.documentfile.provider.DocumentFile
 import androidx.lifecycle.MutableLiveData
+import io.legado.app.R
 import io.legado.app.base.BaseViewModel
+import io.legado.app.constant.AppLog
 import io.legado.app.data.appDb
 import io.legado.app.data.entities.Book
+import io.legado.app.exception.NoStackTraceException
 import io.legado.app.model.localBook.LocalBook
+import io.legado.app.utils.FileUtils
+import io.legado.app.utils.GSON
+import io.legado.app.utils.isContentScheme
+import io.legado.app.utils.toastOnUi
+import io.legado.app.utils.writeToOutputStream
+import java.io.File
+import java.io.FileOutputStream
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
 
 class TocViewModel(application: Application) : BaseViewModel(application) {
     var bookUrl: String = ""
@@ -63,6 +78,41 @@ class TocViewModel(application: Application) : BaseViewModel(application) {
         bookMarkCallBack?.upBookmark(newText)
     }
 
+    @Suppress("BlockingMethodInNonBlockingContext")
+    fun saveBookmark(treeUri: Uri) {
+        execute {
+            val book = bookData.value
+                ?: throw NoStackTraceException(context.getString(R.string.no_book))
+            val dateFormat = SimpleDateFormat("yyMMddHHmmss", Locale.getDefault())
+            val bookmark = appDb.bookmarkDao.getByBook(book.name, book.author)
+            if (treeUri.isContentScheme()) {
+                val doc = DocumentFile.fromTreeUri(context, treeUri)
+                    ?.createFile("", "bookmark-${dateFormat.format(Date())}")
+                doc?.let {
+                    context.contentResolver.openOutputStream(doc.uri)!!.use {
+                        GSON.writeToOutputStream(it, bookmark)
+                    }
+                }
+            } else {
+                val path = treeUri.path!!
+                val file = FileUtils.createFileIfNotExist(
+                    File(path), "bookmark-${
+                        dateFormat.format(
+                            Date()
+                        )
+                    }"
+                )
+                FileOutputStream(file).use {
+                    GSON.writeToOutputStream(it, bookmark)
+                }
+            }
+        }.onError {
+            AppLog.put("导出失败\n${it.localizedMessage}", it, true)
+        }.onSuccess {
+            context.toastOnUi("导出成功")
+        }
+    }
+
     interface ChapterListCallBack {
         fun upChapterList(searchKey: String?)
 

+ 1 - 2
app/src/main/java/io/legado/app/utils/StringUtils.kt

@@ -50,8 +50,7 @@ object StringUtils {
      * 将日期转换成昨天、今天、明天
      */
     fun dateConvert(source: String, pattern: String): String {
-        @SuppressLint("SimpleDateFormat")
-        val format = SimpleDateFormat(pattern)
+        val format = SimpleDateFormat(pattern, Locale.getDefault())
         val calendar = Calendar.getInstance()
         kotlin.runCatching {
             val date = format.parse(source) ?: return ""

+ 26 - 9
app/src/main/res/menu/book_toc.xml

@@ -21,22 +21,39 @@
 
         <item
             android:id="@+id/menu_split_long_chapter"
+            android:checkable="true"
             android:title="@string/split_long_chapter"
+            app:showAsAction="never" />
+
+    </group>
+
+    <group
+        android:id="@+id/menu_group_toc"
+        android:visible="true">
+
+        <item
+            android:id="@+id/menu_reverse_toc"
+            android:title="@string/reverse_toc"
+            app:showAsAction="never" />
+
+        <item
+            android:id="@+id/menu_use_replace"
             android:checkable="true"
+            android:title="@string/use_replace"
             app:showAsAction="never" />
 
     </group>
 
-    <item
-        android:id="@+id/menu_reverse_toc"
-        android:title="@string/reverse_toc"
-        app:showAsAction="never" />
+    <group
+        android:id="@+id/menu_group_bookmark"
+        android:visible="false">
 
-    <item
-        android:id="@+id/menu_use_replace"
-        android:checkable="true"
-        android:title="@string/use_replace"
-        app:showAsAction="never" />
+        <item
+            android:id="@+id/menu_export_bookmark"
+            android:title="@string/export"
+            app:showAsAction="never" />
+
+    </group>
 
     <item
         android:id="@+id/menu_log"