feat: 下载功能初步完成

pull/1894/head
谢意帆 3 years ago
parent b84db00367
commit 3f1c95b7be
  1. 16
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBookActivity.kt
  2. 27
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBookAdapter.kt
  3. 107
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBookViewModel.kt
  4. 12
      app/src/main/res/layout/activity_remote_book.xml
  5. 5
      app/src/main/res/layout/item_remote_book.xml

@ -29,10 +29,13 @@ class RemoteBookActivity : VMBaseActivity<ActivityRemoteBookBinding,RemoteBookVi
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
initView() initView()
// initEvent()
initData() initData()
// toastOnUi("远程书籍") // toastOnUi("远程书籍")
} }
private fun initView() { private fun initView() {
binding.recyclerView.layoutManager = LinearLayoutManager(this) binding.recyclerView.layoutManager = LinearLayoutManager(this)
binding.recyclerView.adapter = adapter binding.recyclerView.adapter = adapter
@ -48,20 +51,21 @@ class RemoteBookActivity : VMBaseActivity<ActivityRemoteBookBinding,RemoteBookVi
// viewModel.getRemoteBooks().observe(this, { // viewModel.getRemoteBooks().observe(this, {
// adapter.submitList(it) // adapter.submitList(it)
// }) // })
viewModel.loadRemoteBookList()
launch { launch {
Log.e("TAG", "2") viewModel.dataFlow.collect { remoteBooks ->
viewModel.dataFlow.conflate().collect { remoteBooks ->
adapter.setItems(remoteBooks) adapter.setItems(remoteBooks)
} }
} }
// toastOnUi("1") // toastOnUi("1")
viewModel.loadRemoteBookList()
} }
override fun showRemoteBookInfo(book: Book) {
TODO("Not yet implemented")
override fun download(remoteBook: RemoteBook) {
viewModel.downloadRemoteBook(remoteBook.url)
} }
} }

@ -2,14 +2,23 @@ package io.legado.app.ui.book.remote
import android.content.Context import android.content.Context
import android.view.ViewGroup import android.view.ViewGroup
import cn.hutool.core.date.LocalDateTimeUtil
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.databinding.ItemRemoteBookBinding import io.legado.app.databinding.ItemRemoteBookBinding
import io.legado.app.utils.ConvertUtils
import io.legado.app.utils.FileUtils
import io.legado.app.utils.toastOnUi
import splitties.init.appCtx
/**
* 适配器
* @author qianfanguojin
*/
class RemoteBookAdapter (context: Context, val callBack: CallBack) : class RemoteBookAdapter (context: Context, val callBack: CallBack) :
RecyclerAdapter<String, ItemRemoteBookBinding>(context){ RecyclerAdapter<RemoteBook, ItemRemoteBookBinding>(context){
override fun getViewBinding(parent: ViewGroup): ItemRemoteBookBinding { override fun getViewBinding(parent: ViewGroup): ItemRemoteBookBinding {
return ItemRemoteBookBinding.inflate(inflater, parent, false) return ItemRemoteBookBinding.inflate(inflater, parent, false)
@ -25,19 +34,29 @@ class RemoteBookAdapter (context: Context, val callBack: CallBack) :
override fun convert( override fun convert(
holder: ItemViewHolder, holder: ItemViewHolder,
binding: ItemRemoteBookBinding, binding: ItemRemoteBookBinding,
item: String, item: RemoteBook,
payloads: MutableList<Any> payloads: MutableList<Any>
) { ) {
binding.run { binding.run {
tvName.text = item tvName.text = item.name.substringBeforeLast(".")
tvContentType.text = item.name.substringAfterLast(".")
tvSize.text = ConvertUtils.formatFileSize(item.size)
tvDate.text = LocalDateTimeUtil.format(LocalDateTimeUtil.of(item.lastModify), "yyyy-MM-dd")
} }
} }
override fun registerListener(holder: ItemViewHolder, binding: ItemRemoteBookBinding) { override fun registerListener(holder: ItemViewHolder, binding: ItemRemoteBookBinding) {
binding.btnDownload.setOnClickListener {
getItem(holder.layoutPosition)?.let {
context.toastOnUi("开始下载")
callBack.download(it)
}
}
} }
interface CallBack { interface CallBack {
fun showRemoteBookInfo(book: Book) fun download(remoteBook: RemoteBook)
} }
} }

@ -2,36 +2,43 @@ package io.legado.app.ui.book.remote
import android.app.Application import android.app.Application
import android.util.Log import android.util.Log
import android.widget.Toast
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.utils.FileDoc import io.legado.app.constant.PreferKey
import kotlinx.coroutines.Dispatchers import io.legado.app.lib.webdav.Authorization
import io.legado.app.lib.webdav.WebDav
import io.legado.app.utils.FileUtils
import io.legado.app.utils.exists
import io.legado.app.utils.externalFiles
import io.legado.app.utils.getPrefString
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import splitties.init.appCtx
import kotlinx.coroutines.withContext import java.io.File
import java.nio.charset.Charset
import java.util.* import java.util.*
import kotlin.reflect.typeOf
class RemoteBookViewModel(application: Application): BaseViewModel(application){ class RemoteBookViewModel(application: Application): BaseViewModel(application){
private val remoteBookFolderName = "book_remote"
private var dataCallback : DataCallback? = null private var dataCallback : DataCallback? = null
var dataFlowStart: (() -> Unit)? = null var dataFlowStart: (() -> Unit)? = null
val dataFlow = callbackFlow<List<String>> { private var authorization: Authorization? = null
val dataFlow = callbackFlow<List<RemoteBook>> {
val list = Collections.synchronizedList(ArrayList<String>()) val list = Collections.synchronizedList(ArrayList<RemoteBook>())
dataCallback = object : DataCallback { dataCallback = object : DataCallback {
override fun setItems(remoteFiles: List<String>) { override fun setItems(remoteFiles: List<RemoteBook>) {
list.clear() list.clear()
list.addAll(remoteFiles) list.addAll(remoteFiles)
Log.e("TAG", ": 1", )
trySend(list) trySend(list)
} }
override fun addItems(remoteFiles: List<String>) { override fun addItems(remoteFiles: List<RemoteBook>) {
list.addAll(remoteFiles) list.addAll(remoteFiles)
trySend(list) trySend(list)
} }
@ -41,28 +48,88 @@ class RemoteBookViewModel(application: Application): BaseViewModel(application){
trySend(emptyList()) trySend(emptyList())
} }
} }
// withContext(Dispatchers.Main) { // withContext(Dispatchers.Main) {
// dataFlowStart?.invoke() // dataFlowStart?.invoke()
// } // }
awaitClose { awaitClose {
dataCallback = null // dataCallback = null
} }
}.flowOn(Dispatchers.Main) }.flowOn(Dispatchers.IO)
fun loadRemoteBookList() { fun loadRemoteBookList() {
Log.e("TAG", dataCallback.toString(), ) execute {
dataCallback?.setItems(listOf("1", "2", "3")) dataCallback?.clear()
kotlin.runCatching {
authorization = null
val account = appCtx.getPrefString(PreferKey.webDavAccount)
val password = appCtx.getPrefString(PreferKey.webDavPassword)
if (!account.isNullOrBlank() && !password.isNullOrBlank()) {
val mAuthorization = Authorization(account, password)
authorization = mAuthorization
}
}
authorization?.let { it ->
val remoteWebDavFileList = WebDav("http://txc.qianfanguojin.top/",it).listFiles()
val remoteList = remoteWebDavFileList.map {
RemoteBook(it.displayName,it.urlName,it.size,"epub",it.lastModify)
}
dataCallback?.setItems(remoteList)
}
// dataCallback?.setItems()
}
// dataCallback?.setItems(listOf("1", "2", "3"))
} }
fun downloadRemoteBook(urlName: String) {
execute {
// kotlin.runCatching {
// val remoteWebDavFile = WebDav("http://txc.qianfanguojin.top/",authorization!!).getFile(url)
// val remoteBook = RemoteBook(remoteWebDavFile.displayName,remoteWebDavFile.urlName,remoteWebDavFile.size,"epub",remoteWebDavFile.lastModify)
// dataCallback?.addItems(listOf(remoteBook))
// }
kotlin.runCatching {
authorization = null
val account = appCtx.getPrefString(PreferKey.webDavAccount)
val password = appCtx.getPrefString(PreferKey.webDavPassword)
if (!account.isNullOrBlank() && !password.isNullOrBlank()) {
val mAuthorization = Authorization(account, password)
authorization = mAuthorization
}
}
authorization?.let { it ->
Log.e("TAG", "downloadRemoteBook: 1", )
val saveFolder = "${appCtx.externalFiles.absolutePath}${File.separator}${remoteBookFolderName}"
FileUtils.createFolderIfNotExist(saveFolder).run{
// Log.e("TAG", "downloadRemoteBook: 2 ${appCtx.externalFiles.absoluteFile}", )
val trueCodeURLName = String(urlName.toByteArray(Charset.forName("GBK")), Charset.forName("UTF-8"))
withTimeout(15000L) {
val webdav = WebDav("http://txc.qianfanguojin.top${trueCodeURLName}", it)
webdav.downloadTo("${saveFolder}${trueCodeURLName}", true).apply {
}
}
}
}
}
}
interface DataCallback { interface DataCallback {
fun setItems(remoteFiles: List<String>) fun setItems(remoteFiles: List<RemoteBook>)
fun addItems(remoteFiles: List<String>) fun addItems(remoteFiles: List<RemoteBook>)
fun clear() fun clear()
} }
} }
data class RemoteBook(
val name: String,
val url: String,
val size: Long,
val contentType: String,
val lastModify: Long
)

@ -36,9 +36,11 @@
</io.legado.app.ui.widget.dynamiclayout.DynamicFrameLayout> </io.legado.app.ui.widget.dynamiclayout.DynamicFrameLayout>
<io.legado.app.ui.widget.SelectActionBar <!-- <io.legado.app.ui.widget.SelectActionBar-->
android:id="@+id/select_action_bar" <!-- android:id="@+id/select_action_bar"-->
android:layout_width="match_parent" <!-- android:layout_width="match_parent"-->
android:layout_height="wrap_content" <!-- android:layout_height="wrap_content"-->
app:layout_constraintBottom_toBottomOf="parent" /> <!-- app:layout_constraintBottom_toBottomOf="parent"-->
<!-- app:layout_constraintTop_toBottomOf="@id/content_view"-->
<!-- />-->
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -55,7 +55,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
tools:text="\u4e28" android:text="\u4e28"
android:textColor="@color/tv_text_summary" android:textColor="@color/tv_text_summary"
android:textSize="11sp" /> android:textSize="11sp" />
@ -96,10 +96,9 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:id="@+id/tv_download"
> >
<Button <Button
android:id="@+id/tv_download1" android:id="@+id/btn_download"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="下载" android:text="下载"

Loading…
Cancel
Save