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

Merge remote-tracking branch 'origin/master'

kunfei 4 лет назад
Родитель
Сommit
66802bc1a7

+ 1 - 1
app/build.gradle

@@ -190,7 +190,7 @@ dependencies {
 
     //media
     implementation("androidx.media:media:1.6.0")
-    def exoplayer_version = '2.17.1'
+    def exoplayer_version = '2.18.0'
     implementation("com.google.android.exoplayer:exoplayer-core:$exoplayer_version")
     implementation("com.google.android.exoplayer:extension-okhttp:$exoplayer_version")
 

BIN
app/cronetlib/cronet_api.jar


BIN
app/cronetlib/cronet_impl_common_java.jar


BIN
app/cronetlib/cronet_impl_native_java.jar


+ 1 - 1
app/src/main/assets/cronet.json

@@ -1 +1 @@
-{"x86_64":"136f62aca55e9479aa3994213be6ca1d","x86":"2a7c8d7107832839d2459334c1d8f966","armeabi-v7a":"d890a3023e76d3e942485b80c74f8e9b","arm64-v8a":"d4bb1dce5666d38fda7c46a53f2c5aaa","version":"102.0.5005.125"}
+{"x86_64":"cbb7148b4ac5771e6ebd9392bfb65010","armeabi-v7a":"f4006d0fef882d5c09f75e59e5256933","arm64-v8a":"ee4a9a6c4b5faf0588a050f3fbd0f0b5","x86":"a323610b6994b79bbf17223da1192945","version":"103.0.5060.53"}

+ 1 - 0
app/src/main/assets/updateLog.md

@@ -13,6 +13,7 @@
 
 **2022/06/16**
 
+* 更新cronet: 103.0.5060.53
 * 导出使用单线程,导出占用大量内存,并行容易OOM
 * 导出到WebDav文件夹改为books,方便导入
 * WebDav导入基本能用了

+ 24 - 17
app/src/main/java/io/legado/app/lib/webdav/WebDav.kt

@@ -6,6 +6,7 @@ import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.http.newCallResponse
 import io.legado.app.help.http.okHttpClient
 import io.legado.app.help.http.text
+import io.legado.app.utils.NetworkUtils
 import io.legado.app.utils.printOnDebug
 import okhttp3.MediaType.Companion.toMediaType
 import okhttp3.RequestBody.Companion.asRequestBody
@@ -61,24 +62,28 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
 
 
     /**
-     * 填充文件信息。实例化WebDAVFile对象时,并没有将远程文件的信息填充到实例中。需要手动填充!
-     * @return 远程文件是否存在
+     * 获取当前url文件信息
      */
-    suspend fun indexFileInfo(): Boolean {
-        return !propFindResponse(ArrayList()).isNullOrEmpty()
+    suspend fun getWebDavFile(): WebDavFile? {
+        return kotlin.runCatching {
+            propFindResponse(depth = 0)?.let {
+                return parseBody(it).firstOrNull()
+            }
+        }.getOrNull()
     }
 
     /**
      * 列出当前路径下的文件
-     *
      * @return 文件列表
      */
     @Throws(WebDavException::class)
     suspend fun listFiles(): List<WebDavFile> {
         propFindResponse()?.let { body ->
-            return parseDir(body)
+            return parseBody(body).filter {
+                it.path != path
+            }
         }
-        return ArrayList()
+        return emptyList()
     }
 
     /**
@@ -112,12 +117,15 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
         }.body?.text()
     }
 
-    private fun parseDir(s: String): List<WebDavFile> {
+    /**
+     * 解析webDav返回的xml
+     */
+    private fun parseBody(s: String): List<WebDavFile> {
         val list = ArrayList<WebDavFile>()
         val document = Jsoup.parse(s)
         val elements = document.getElementsByTag("d:response")
         httpUrl?.let { urlStr ->
-            val baseUrl = if (urlStr.endsWith("/")) urlStr else "$urlStr/"
+            val baseUrl = NetworkUtils.getBaseUrl(urlStr)
             for (element in elements) {
                 val href = element.getElementsByTag("d:href")[0].text()
                 if (!href.endsWith("/")) {
@@ -137,11 +145,12 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
                         val lastModify: Long = kotlin.runCatching {
                             element.getElementsByTag("d:getlastmodified")
                                 .firstOrNull()?.text()?.let {
-                                    LocalDateTime.parse(it, dateTimeFormatter).toInstant(ZoneOffset.of("+8")).toEpochMilli()
+                                    LocalDateTime.parse(it, dateTimeFormatter)
+                                        .toInstant(ZoneOffset.of("+8")).toEpochMilli()
                                 }
                         }.getOrNull() ?: 0
                         webDavFile = WebDavFile(
-                            baseUrl + fileName,
+                            "$baseUrl$href",
                             authorization,
                             displayName = fileName,
                             urlName = urlName,
@@ -163,12 +172,7 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
      * 文件是否存在
      */
     suspend fun exists(): Boolean {
-        return kotlin.runCatching {
-            val response = propFindResponse(depth = 0) ?: return false
-            val document = Jsoup.parse(response)
-            val elements = document.getElementsByTag("d:response")
-            return elements.isNotEmpty()
-        }.getOrDefault(false)
+        return getWebDavFile() != null
     }
 
     /**
@@ -297,6 +301,9 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
         }.isSuccess
     }
 
+    /**
+     * 检测返回结果是否正确
+     */
     private fun checkResult(response: Response) {
         if (!response.isSuccessful) {
             throw WebDavException("${url}\n${response.code}:${response.message}")

+ 2 - 1
app/src/main/java/io/legado/app/model/ReadBook.kt

@@ -21,6 +21,7 @@ import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.MainScope
 import kotlinx.coroutines.delay
 import splitties.init.appCtx
+import kotlin.math.min
 
 
 @Suppress("MemberVisibilityCanBePrivate")
@@ -437,7 +438,7 @@ object ReadBook : CoroutineScope by MainScope() {
                 delay(1000)
                 download(i)
             }
-            val minChapterIndex = durChapterIndex - 5
+            val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
             for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
                 delay(1000)
                 download(i)

+ 8 - 2
app/src/main/java/io/legado/app/ui/book/remote/RemoteBook.kt

@@ -2,9 +2,15 @@ package io.legado.app.ui.book.remote
 
 data class RemoteBook(
     val filename: String,
-    val urlName: String,
+    val path: String,
     val size: Long,
     val contentType: String,
     val lastModify: Long,
     val isOnBookShelf: Boolean
-)
+) {
+
+    val isDir by lazy {
+        contentType == "folder"
+    }
+
+}

+ 29 - 2
app/src/main/java/io/legado/app/ui/book/remote/RemoteBookActivity.kt

@@ -12,6 +12,7 @@ import io.legado.app.base.VMBaseActivity
 
 
 import io.legado.app.databinding.ActivityRemoteBookBinding
+import io.legado.app.ui.book.remote.manager.RemoteBookWebDav
 import io.legado.app.ui.widget.dialog.WaitDialog
 import io.legado.app.utils.toastOnUi
 
@@ -50,7 +51,7 @@ class RemoteBookActivity : VMBaseActivity<ActivityRemoteBookBinding, RemoteBookV
                 adapter.setItems(remoteBooks)
             }
         }
-        viewModel.loadRemoteBookList()
+        viewModel.loadRemoteBookList(RemoteBookWebDav.rootBookUrl)
     }
 
     override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
@@ -61,12 +62,38 @@ class RemoteBookActivity : VMBaseActivity<ActivityRemoteBookBinding, RemoteBookV
     override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
         when (item.itemId) {
             R.id.menu_refresh -> {
-                viewModel.loadRemoteBookList()
+                viewModel.loadRemoteBookList(
+                    viewModel.dirList.lastOrNull()?.path ?: RemoteBookWebDav.rootBookUrl
+                )
             }
         }
         return super.onCompatOptionsItemSelected(item)
     }
 
+    override fun finish() {
+        if (viewModel.dirList.isEmpty()) {
+            super.finish()
+        } else {
+            viewModel.dirList.removeLastOrNull()
+            val remoteBook = viewModel.dirList.lastOrNull()
+            viewModel.dataCallback?.clear()
+            if (remoteBook != null) {
+                binding.titleBar.title = remoteBook.filename
+                viewModel.loadRemoteBookList(remoteBook.path)
+            } else {
+                binding.titleBar.setTitle(R.string.remote_book)
+                viewModel.loadRemoteBookList(RemoteBookWebDav.rootBookUrl)
+            }
+        }
+    }
+
+    override fun openDir(remoteBook: RemoteBook) {
+        viewModel.dirList.add(remoteBook)
+        binding.titleBar.title = remoteBook.filename
+        viewModel.dataCallback?.clear()
+        viewModel.loadRemoteBookList(remoteBook.path)
+    }
+
     @SuppressLint("NotifyDataSetChanged")
     override fun addToBookshelf(remoteBook: RemoteBook) {
         waitDialog.show()

+ 19 - 8
app/src/main/java/io/legado/app/ui/book/remote/RemoteBookAdapter.kt

@@ -2,6 +2,7 @@ package io.legado.app.ui.book.remote
 
 import android.content.Context
 import android.view.ViewGroup
+import androidx.core.view.isGone
 import cn.hutool.core.date.LocalDateTimeUtil
 import io.legado.app.base.adapter.ItemViewHolder
 import io.legado.app.base.adapter.RecyclerAdapter
@@ -13,8 +14,8 @@ import io.legado.app.utils.ConvertUtils
  * 适配器
  * @author qianfanguojin
  */
-class RemoteBookAdapter (context: Context, val callBack: CallBack) :
-    RecyclerAdapter<RemoteBook, ItemRemoteBookBinding>(context){
+class RemoteBookAdapter(context: Context, val callBack: CallBack) :
+    RecyclerAdapter<RemoteBook, ItemRemoteBookBinding>(context) {
 
     override fun getViewBinding(parent: ViewGroup): ItemRemoteBookBinding {
         return ItemRemoteBookBinding.inflate(inflater, parent, false)
@@ -37,21 +38,31 @@ class RemoteBookAdapter (context: Context, val callBack: CallBack) :
             tvName.text = item.filename.substringBeforeLast(".")
             tvContentType.text = item.contentType
             tvSize.text = ConvertUtils.formatFileSize(item.size)
-            tvDate.text = LocalDateTimeUtil.format(LocalDateTimeUtil.of(item.lastModify), "yyyy-MM-dd")
+            tvDate.text =
+                LocalDateTimeUtil.format(LocalDateTimeUtil.of(item.lastModify), "yyyy-MM-dd")
+            llInfo.isGone = item.isDir
+            tvContentType.isGone = item.isDir
+            btnDownload.isGone = item.isDir
         }
     }
 
     override fun registerListener(holder: ItemViewHolder, binding: ItemRemoteBookBinding) {
-
-        binding.btnDownload.setOnClickListener {
-                getItem(holder.layoutPosition)?.let {
-                    callBack.addToBookshelf(it)
+        binding.root.setOnClickListener {
+            getItem(holder.layoutPosition)?.let {
+                if (it.isDir) {
+                    callBack.openDir(it)
                 }
+            }
+        }
+        binding.btnDownload.setOnClickListener {
+            getItem(holder.layoutPosition)?.let {
+                callBack.addToBookshelf(it)
+            }
         }
-
     }
 
     interface CallBack {
+        fun openDir(remoteBook: RemoteBook)
         fun addToBookshelf(remoteBook: RemoteBook)
     }
 }

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/remote/RemoteBookManager.kt

@@ -5,7 +5,7 @@ import android.net.Uri
 abstract class RemoteBookManager {
     protected val remoteBookFolder : String = "books"
     abstract suspend fun initRemoteContext()
-    abstract suspend fun getRemoteBookList(): MutableList<RemoteBook>
+    abstract suspend fun getRemoteBookList(path: String): MutableList<RemoteBook>
     abstract suspend fun upload(localBookUri: Uri): Boolean
     abstract suspend fun delete(remoteBookUrl: String): Boolean
 

+ 6 - 3
app/src/main/java/io/legado/app/ui/book/remote/RemoteBookViewModel.kt

@@ -13,7 +13,10 @@ import kotlinx.coroutines.flow.flowOn
 import java.util.*
 
 class RemoteBookViewModel(application: Application): BaseViewModel(application){
-    private var dataCallback : DataCallback? = null
+
+    val dirList = arrayListOf<RemoteBook>()
+
+    var dataCallback: DataCallback? = null
 
     val dataFlow = callbackFlow<List<RemoteBook>> {
 
@@ -49,10 +52,10 @@ class RemoteBookViewModel(application: Application): BaseViewModel(application){
         }
     }
 
-    fun loadRemoteBookList() {
+    fun loadRemoteBookList(path: String) {
         execute {
             dataCallback?.clear()
-            val bookList = RemoteBookWebDav.getRemoteBookList()
+            val bookList = RemoteBookWebDav.getRemoteBookList(path)
             dataCallback?.setItems(bookList)
         }.onError {
             context.toastOnUi("获取webDav书籍出错\n${it.localizedMessage}")

+ 23 - 18
app/src/main/java/io/legado/app/ui/book/remote/manager/RemoteBookWebDav.kt

@@ -19,7 +19,7 @@ import java.io.File
 import java.net.URLDecoder
 
 object RemoteBookWebDav : RemoteBookManager() {
-    private val remoteBookUrl get() = "${AppWebDav.rootWebDavUrl}${remoteBookFolder}"
+    val rootBookUrl get() = "${AppWebDav.rootWebDavUrl}${remoteBookFolder}"
 
     init {
         runBlocking {
@@ -29,7 +29,7 @@ object RemoteBookWebDav : RemoteBookManager() {
 
     override suspend fun initRemoteContext() {
         AppWebDav.authorization?.let {
-            WebDav(remoteBookUrl, it).makeAsDir()
+            WebDav(rootBookUrl, it).makeAsDir()
         }
     }
 
@@ -37,32 +37,37 @@ object RemoteBookWebDav : RemoteBookManager() {
      * 获取远程书籍列表
      */
     @Throws(Exception::class)
-    override suspend fun getRemoteBookList(): MutableList<RemoteBook> {
+    override suspend fun getRemoteBookList(path: String): MutableList<RemoteBook> {
         val remoteBooks = mutableListOf<RemoteBook>()
         AppWebDav.authorization?.let {
             //读取文件列表
-            val remoteWebDavFileList: List<WebDavFile> = WebDav(remoteBookUrl, it).listFiles()
+            val remoteWebDavFileList: List<WebDavFile> = WebDav(path, it).listFiles()
             //转化远程文件信息到本地对象
             remoteWebDavFileList.forEach { webDavFile ->
                 var webDavFileName = webDavFile.displayName
-                var webDavUrlName = "${remoteBookUrl}${File.separator}${webDavFile.displayName}"
                 webDavFileName = URLDecoder.decode(webDavFileName, "utf-8")
-                webDavUrlName = URLDecoder.decode(webDavUrlName, "utf-8")
 
-                webDavFile.isDir
-
-                //分割后缀
-                val fileExtension = webDavFileName.substringAfterLast(".")
-
-                //扩展名符合阅读的格式则认为是书籍
-                if (bookFileRegex.matches(webDavFileName)) {
-                    val isOnBookShelf = LocalBook.isOnBookShelf(webDavFileName)
+                if (webDavFile.isDir) {
                     remoteBooks.add(
                         RemoteBook(
-                            webDavFileName, webDavUrlName, webDavFile.size,
-                            fileExtension, webDavFile.lastModify, isOnBookShelf
+                            webDavFileName, webDavFile.path, webDavFile.size,
+                            "folder", webDavFile.lastModify, false
                         )
                     )
+                } else {
+                    //分割后缀
+                    val fileExtension = webDavFileName.substringAfterLast(".")
+
+                    //扩展名符合阅读的格式则认为是书籍
+                    if (bookFileRegex.matches(webDavFileName)) {
+                        val isOnBookShelf = LocalBook.isOnBookShelf(webDavFileName)
+                        remoteBooks.add(
+                            RemoteBook(
+                                webDavFileName, webDavFile.path, webDavFile.size,
+                                fileExtension, webDavFile.lastModify, isOnBookShelf
+                            )
+                        )
+                    }
                 }
             }
         } ?: throw NoStackTraceException("webDav没有配置")
@@ -74,7 +79,7 @@ object RemoteBookWebDav : RemoteBookManager() {
      */
     override suspend fun getRemoteBook(remoteBook: RemoteBook): Uri? {
         return AppWebDav.authorization?.let {
-            val webdav = WebDav(remoteBook.urlName, it)
+            val webdav = WebDav(remoteBook.path, it)
             webdav.download().let { bytes ->
                 LocalBook.saveBookFile(bytes, remoteBook.filename)
             }
@@ -88,7 +93,7 @@ object RemoteBookWebDav : RemoteBookManager() {
         if (!NetworkUtils.isAvailable()) return false
 
         val localBookName = localBookUri.path?.substringAfterLast(File.separator)
-        val putUrl = "${remoteBookUrl}${File.separator}${localBookName}"
+        val putUrl = "${rootBookUrl}${File.separator}${localBookName}"
         AppWebDav.authorization?.let {
             if (localBookUri.isContentScheme()) {
                 WebDav(putUrl, it).upload(

+ 1 - 1
app/src/main/res/layout/activity_remote_book.xml

@@ -10,7 +10,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         app:layout_constraintTop_toTopOf="parent"
-        app:title="远程书籍" />
+        app:title="@string/remote_book" />
 
     <io.legado.app.ui.widget.anima.RefreshProgressBar
         android:id="@+id/refresh_progress_bar"

+ 1 - 0
app/src/main/res/values-es-rES/strings.xml

@@ -996,4 +996,5 @@
     <string name="error_decode_bitmap">Fail to decode bitmap</string>
     <string name="error_image_url_empty">Image url is empty, check replacement rules</string>
     <string name="variable_comment">变量说明(variableComment)</string>
+    <string name="remote_book">远程书籍</string>
 </resources>

+ 1 - 0
app/src/main/res/values-ja-rJP/strings.xml

@@ -999,4 +999,5 @@
     <string name="error_decode_bitmap">Fail to decode bitmap</string>
     <string name="error_image_url_empty">Image url is empty, check replacement rules</string>
     <string name="variable_comment">变量说明(variableComment)</string>
+    <string name="remote_book">远程书籍</string>
 </resources>

+ 1 - 0
app/src/main/res/values-pt-rBR/strings.xml

@@ -999,4 +999,5 @@
     <string name="error_decode_bitmap">Fail to decode bitmap</string>
     <string name="error_image_url_empty">Image url is empty, check replacement rules</string>
     <string name="variable_comment">变量说明(variableComment)</string>
+    <string name="remote_book">远程书籍</string>
 </resources>

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

@@ -996,4 +996,5 @@
     <string name="error_decode_bitmap">图片解码失败</string>
     <string name="error_image_url_empty">图片链接为空,检查替换净化规则</string>
     <string name="variable_comment">变量说明(variableComment)</string>
+    <string name="remote_book">远程书籍</string>
 </resources>

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

@@ -998,4 +998,5 @@
     <string name="error_decode_bitmap">图片解码失败</string>
     <string name="error_image_url_empty">图片链接为空,检查替换净化规则</string>
     <string name="variable_comment">变量说明(variableComment)</string>
+    <string name="remote_book">远程书籍</string>
 </resources>

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

@@ -998,4 +998,5 @@
     <string name="error_decode_bitmap">图片解码失败</string>
     <string name="error_image_url_empty">图片链接为空,检查替换净化规则</string>
     <string name="variable_comment">变量说明(variableComment)</string>
+    <string name="remote_book">远程书籍</string>
 </resources>

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

@@ -999,4 +999,5 @@
     <string name="error_decode_bitmap">Fail to decode bitmap</string>
     <string name="error_image_url_empty">Image url is empty, check replacement rules</string>
     <string name="variable_comment">变量说明(variableComment)</string>
+    <string name="remote_book">远程书籍</string>
 </resources>

+ 1 - 1
gradle.properties

@@ -26,4 +26,4 @@ android.experimental.enableNewResourceShrinker.preciseShrinking=true
 # and none from the library's dependencies, thereby reducing the size of the R class for that library.
 android.nonTransitiveRClass=true
 #https://chromiumdash.appspot.com/releases?platform=Android
-CronetVersion=102.0.5005.125
+CronetVersion=103.0.5060.53