mirror of
https://github.com/gedoor/legado.git
synced 2024-09-03 09:55:00 +08:00
优化
This commit is contained in:
parent
00881414c2
commit
2a6dc8b9ed
@ -11,10 +11,11 @@
|
||||
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
|
||||
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
|
||||
|
||||
**2023/03/30**
|
||||
**2023/03/31**
|
||||
|
||||
* 原app最低支持版本提升到android7.0
|
||||
* 创建lollipop版本支持android5.0
|
||||
* 增加内置文件管理,管理私有文件
|
||||
|
||||
**2023/03/29**
|
||||
|
||||
|
@ -1,23 +1,35 @@
|
||||
package io.legado.app.ui.file
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.legado.app.R
|
||||
import io.legado.app.base.BaseDialogFragment
|
||||
import io.legado.app.base.adapter.ItemViewHolder
|
||||
import io.legado.app.base.adapter.RecyclerAdapter
|
||||
import io.legado.app.databinding.DialogEditTextBinding
|
||||
import io.legado.app.databinding.DialogFileChooserBinding
|
||||
import io.legado.app.databinding.ItemFilePickerBinding
|
||||
import io.legado.app.databinding.ItemPathPickerBinding
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.lib.dialogs.alert
|
||||
import io.legado.app.lib.theme.getPrimaryDisabledTextColor
|
||||
import io.legado.app.lib.theme.getPrimaryTextColor
|
||||
import io.legado.app.lib.theme.primaryColor
|
||||
import io.legado.app.ui.file.HandleFileContract.Companion.DIR
|
||||
import io.legado.app.ui.file.HandleFileContract.Companion.FILE
|
||||
import io.legado.app.ui.file.adapter.FileAdapter
|
||||
import io.legado.app.ui.file.adapter.PathAdapter
|
||||
import io.legado.app.ui.file.utils.FilePickerIcon
|
||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
||||
import io.legado.app.utils.*
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
@ -25,9 +37,7 @@ import java.io.File
|
||||
|
||||
|
||||
class FilePickerDialog : BaseDialogFragment(R.layout.dialog_file_chooser),
|
||||
Toolbar.OnMenuItemClickListener,
|
||||
FileAdapter.CallBack,
|
||||
PathAdapter.CallBack {
|
||||
Toolbar.OnMenuItemClickListener {
|
||||
|
||||
companion object {
|
||||
const val tag = "FileChooserDialog"
|
||||
@ -37,40 +47,26 @@ class FilePickerDialog : BaseDialogFragment(R.layout.dialog_file_chooser),
|
||||
mode: Int = FILE,
|
||||
title: String? = null,
|
||||
initPath: String? = null,
|
||||
isShowHomeDir: Boolean = false,
|
||||
isShowUpDir: Boolean = true,
|
||||
isShowHideDir: Boolean = false,
|
||||
allowExtensions: Array<String>? = null,
|
||||
menus: Array<String>? = null
|
||||
) {
|
||||
FilePickerDialog().apply {
|
||||
val bundle = Bundle()
|
||||
bundle.putInt("mode", mode)
|
||||
bundle.putString("title", title)
|
||||
bundle.putBoolean("isShowHomeDir", isShowHomeDir)
|
||||
bundle.putBoolean("isShowUpDir", isShowUpDir)
|
||||
bundle.putBoolean("isShowHideDir", isShowHideDir)
|
||||
bundle.putString("initPath", initPath)
|
||||
bundle.putStringArray("allowExtensions", allowExtensions)
|
||||
bundle.putStringArray("menus", menus)
|
||||
arguments = bundle
|
||||
}.show(manager, tag)
|
||||
}
|
||||
}
|
||||
|
||||
private val binding by viewBinding(DialogFileChooserBinding::bind)
|
||||
override var allowExtensions: Array<String>? = null
|
||||
override val isSelectDir: Boolean
|
||||
get() = mode == DIR
|
||||
override var isShowHomeDir: Boolean = false
|
||||
override var isShowUpDir: Boolean = true
|
||||
override var isShowHideDir: Boolean = false
|
||||
var title: String? = null
|
||||
private var initPath = FileUtils.getSdCardPath()
|
||||
private var mode: Int = FILE
|
||||
private lateinit var fileAdapter: FileAdapter
|
||||
private lateinit var pathAdapter: PathAdapter
|
||||
private var menus: Array<String>? = null
|
||||
private val viewModel by viewModels<FilePickerViewModel>()
|
||||
private val dirParent = ".."
|
||||
private val pathAdapter by lazy { PathAdapter() }
|
||||
private val fileAdapter by lazy { FileAdapter() }
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
@ -80,20 +76,8 @@ class FilePickerDialog : BaseDialogFragment(R.layout.dialog_file_chooser),
|
||||
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
|
||||
binding.toolBar.setBackgroundColor(primaryColor)
|
||||
view.setBackgroundResource(R.color.background_card)
|
||||
arguments?.let {
|
||||
mode = it.getInt("mode", FILE)
|
||||
title = it.getString("title")
|
||||
isShowHomeDir = it.getBoolean("isShowHomeDir")
|
||||
isShowUpDir = it.getBoolean("isShowUpDir")
|
||||
isShowHideDir = it.getBoolean("isShowHideDir")
|
||||
it.getString("initPath")?.let { path ->
|
||||
initPath = path
|
||||
}
|
||||
allowExtensions = it.getStringArray("allowExtensions")
|
||||
menus = it.getStringArray("menus")
|
||||
}
|
||||
binding.toolBar.title = title ?: let {
|
||||
if (isSelectDir) {
|
||||
binding.toolBar.title = arguments?.getString("title") ?: let {
|
||||
if (viewModel.isSelectDir) {
|
||||
getString(R.string.folder_chooser)
|
||||
} else {
|
||||
getString(R.string.file_chooser)
|
||||
@ -101,93 +85,67 @@ class FilePickerDialog : BaseDialogFragment(R.layout.dialog_file_chooser),
|
||||
}
|
||||
initMenu()
|
||||
initContentView()
|
||||
refreshCurrentDirPath(initPath)
|
||||
viewModel.filesLiveData.observe(viewLifecycleOwner) {
|
||||
fileAdapter.selectFile = null
|
||||
fileAdapter.setItems(it)
|
||||
}
|
||||
viewModel.initData(arguments)
|
||||
}
|
||||
|
||||
private fun initMenu() {
|
||||
binding.toolBar.inflateMenu(R.menu.file_chooser)
|
||||
if (isSelectDir) {
|
||||
binding.toolBar.menu.findItem(R.id.menu_ok).isVisible = true
|
||||
}
|
||||
menus?.let {
|
||||
it.forEach { menuTitle ->
|
||||
binding.toolBar.menu.add(menuTitle)
|
||||
}
|
||||
}
|
||||
binding.toolBar.menu.applyTint(requireContext())
|
||||
binding.toolBar.setOnMenuItemClickListener(this)
|
||||
}
|
||||
|
||||
private fun initContentView() {
|
||||
fileAdapter = FileAdapter(requireContext(), this)
|
||||
pathAdapter = PathAdapter(requireContext(), this)
|
||||
binding.rvPath.layoutManager = LinearLayoutManager(activity, RecyclerView.HORIZONTAL, false)
|
||||
binding.rvPath.adapter = pathAdapter
|
||||
|
||||
binding.rvFile.addItemDecoration(VerticalDivider(requireContext()))
|
||||
binding.rvFile.layoutManager = LinearLayoutManager(activity)
|
||||
binding.rvFile.adapter = fileAdapter
|
||||
|
||||
binding.rvPath.layoutManager = LinearLayoutManager(activity, RecyclerView.HORIZONTAL, false)
|
||||
binding.rvPath.adapter = pathAdapter
|
||||
|
||||
binding.tvOk.setOnClickListener {
|
||||
if (viewModel.isSelectDir) {
|
||||
viewModel.lastDir?.let {
|
||||
setResultData(it.path)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
} else {
|
||||
val file = fileAdapter.selectFile
|
||||
if (file == null) {
|
||||
toastOnUi("请选择文件")
|
||||
} else {
|
||||
setResultData(file.path)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem?): Boolean {
|
||||
when (item?.itemId) {
|
||||
R.id.menu_ok -> fileAdapter.currentPath?.let {
|
||||
setData(it)
|
||||
dismissAllowingStateLoss()
|
||||
R.id.menu_create -> alert(R.string.create_folder) {
|
||||
val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
|
||||
editView.hint = "文件夹名"
|
||||
}
|
||||
customView { alertBinding.root }
|
||||
okButton {
|
||||
val text = alertBinding.editView.text?.toString()
|
||||
if (text.isNullOrBlank()) {
|
||||
toastOnUi("文件夹名不能为空")
|
||||
} else {
|
||||
viewModel.createFolder(text.trim())
|
||||
}
|
||||
}
|
||||
cancelButton()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onFileClick(position: Int) {
|
||||
val fileItem = fileAdapter.getItem(position)
|
||||
if (fileItem?.isDirectory == true) {
|
||||
refreshCurrentDirPath(fileItem.path)
|
||||
} else {
|
||||
fileItem?.path?.let { path ->
|
||||
if (mode == DIR) {
|
||||
toastOnUi("这是文件夹选择,不能选择文件,点击右上角的确定选择文件夹")
|
||||
} else if (allowExtensions.isNullOrEmpty() ||
|
||||
allowExtensions?.contains(FileUtils.getExtension(path)) == true
|
||||
) {
|
||||
setData(path)
|
||||
dismissAllowingStateLoss()
|
||||
} else {
|
||||
toastOnUi("不能打开此文件")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPathClick(position: Int) {
|
||||
refreshCurrentDirPath(pathAdapter.getPath(position))
|
||||
}
|
||||
|
||||
private fun refreshCurrentDirPath(currentPath: String) {
|
||||
if (currentPath == "/") {
|
||||
pathAdapter.updatePath("/")
|
||||
} else {
|
||||
pathAdapter.updatePath(currentPath)
|
||||
}
|
||||
fileAdapter.loadData(currentPath)
|
||||
var adapterCount = fileAdapter.itemCount
|
||||
if (isShowHomeDir) {
|
||||
adapterCount--
|
||||
}
|
||||
if (isShowUpDir) {
|
||||
adapterCount--
|
||||
}
|
||||
if (adapterCount < 1) {
|
||||
binding.tvEmpty.visible()
|
||||
binding.tvEmpty.setText(R.string.empty)
|
||||
} else {
|
||||
binding.tvEmpty.gone()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setData(path: String) {
|
||||
private fun setResultData(path: String) {
|
||||
val data = Intent().setData(Uri.fromFile(File(path)))
|
||||
(parentFragment as? CallBack)?.onResult(data)
|
||||
(activity as? CallBack)?.onResult(data)
|
||||
@ -198,6 +156,134 @@ class FilePickerDialog : BaseDialogFragment(R.layout.dialog_file_chooser),
|
||||
activity?.finish()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
inner class PathAdapter :
|
||||
RecyclerAdapter<File, ItemPathPickerBinding>(requireContext()) {
|
||||
|
||||
private val arrowIcon = ConvertUtils.toDrawable(FilePickerIcon.getArrow())
|
||||
|
||||
init {
|
||||
addHeaderView {
|
||||
ItemPathPickerBinding.inflate(inflater, it, false).apply {
|
||||
textView.text = "root"
|
||||
imageView.setImageDrawable(arrowIcon)
|
||||
root.setOnClickListener {
|
||||
viewModel.subDocs.clear()
|
||||
setItems(viewModel.subDocs)
|
||||
viewModel.upFiles(viewModel.rootDoc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getViewBinding(parent: ViewGroup): ItemPathPickerBinding {
|
||||
return ItemPathPickerBinding.inflate(inflater, parent, false).apply {
|
||||
imageView.setImageDrawable(arrowIcon)
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerListener(holder: ItemViewHolder, binding: ItemPathPickerBinding) {
|
||||
binding.root.setOnClickListener {
|
||||
viewModel.subDocs = viewModel.subDocs.subList(0, holder.layoutPosition)
|
||||
setItems(viewModel.subDocs)
|
||||
viewModel.upFiles(viewModel.subDocs.lastOrNull())
|
||||
}
|
||||
}
|
||||
|
||||
override fun convert(
|
||||
holder: ItemViewHolder,
|
||||
binding: ItemPathPickerBinding,
|
||||
item: File,
|
||||
payloads: MutableList<Any>
|
||||
) {
|
||||
binding.textView.text = item.name
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inner class FileAdapter : RecyclerAdapter<File, ItemFilePickerBinding>(requireContext()) {
|
||||
private val primaryTextColor = context.getPrimaryTextColor(!AppConfig.isNightTheme)
|
||||
private val disabledTextColor = context.getPrimaryDisabledTextColor(!AppConfig.isNightTheme)
|
||||
private val upIcon = ConvertUtils.toDrawable(FilePickerIcon.getUpDir())!!
|
||||
private val folderIcon = ConvertUtils.toDrawable(FilePickerIcon.getFolder())!!
|
||||
private val fileIcon = ConvertUtils.toDrawable(FilePickerIcon.getFile())!!
|
||||
private val selectDrawable =
|
||||
ResourcesCompat.getDrawable(resources, R.drawable.shape_radius_1dp, null)!!.apply {
|
||||
DrawableCompat.setTint(this, primaryTextColor)
|
||||
}
|
||||
var selectFile: File? = null
|
||||
|
||||
override fun getViewBinding(parent: ViewGroup): ItemFilePickerBinding {
|
||||
return ItemFilePickerBinding.inflate(inflater, parent, false)
|
||||
}
|
||||
|
||||
override fun registerListener(holder: ItemViewHolder, binding: ItemFilePickerBinding) {
|
||||
binding.root.setOnClickListener {
|
||||
val item = getItemByLayoutPosition(holder.layoutPosition)
|
||||
item?.let {
|
||||
if (item == viewModel.lastDir) {
|
||||
viewModel.subDocs.removeLastOrNull()
|
||||
pathAdapter.setItems(viewModel.subDocs)
|
||||
viewModel.upFiles(viewModel.subDocs.lastOrNull() ?: viewModel.rootDoc)
|
||||
} else if (item.isDirectory) {
|
||||
viewModel.subDocs.add(item)
|
||||
pathAdapter.setItems(viewModel.subDocs)
|
||||
viewModel.upFiles(item)
|
||||
} else if (viewModel.isSelectFile) {
|
||||
viewModel.allowExtensions.let {
|
||||
if (it.isNullOrEmpty() || it.contains(FileUtils.getExtension(item.path))) {
|
||||
selectFile = item
|
||||
notifyItemRangeChanged(getHeaderCount(), itemCount, "selectFile")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun convert(
|
||||
holder: ItemViewHolder,
|
||||
binding: ItemFilePickerBinding,
|
||||
item: File,
|
||||
payloads: MutableList<Any>
|
||||
) {
|
||||
if (payloads.isEmpty()) {
|
||||
if (item == viewModel.lastDir) {
|
||||
binding.imageView.setImageDrawable(upIcon)
|
||||
binding.textView.text = dirParent
|
||||
} else if (item.isDirectory) {
|
||||
binding.imageView.setImageDrawable(folderIcon)
|
||||
binding.textView.text = item.name
|
||||
} else {
|
||||
binding.imageView.setImageDrawable(fileIcon)
|
||||
binding.textView.text = item.name
|
||||
}
|
||||
if (item.isDirectory) {
|
||||
binding.textView.setTextColor(primaryTextColor)
|
||||
} else {
|
||||
if (viewModel.isSelectDir) {
|
||||
binding.textView.setTextColor(disabledTextColor)
|
||||
} else {
|
||||
viewModel.allowExtensions?.let {
|
||||
if (it.isEmpty() || it.contains(FileUtils.getExtension(item.path))) {
|
||||
binding.textView.setTextColor(primaryTextColor)
|
||||
} else {
|
||||
binding.textView.setTextColor(disabledTextColor)
|
||||
}
|
||||
} ?: binding.textView.setTextColor(primaryTextColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.root.isSelected = item == selectFile
|
||||
if (item == selectFile) {
|
||||
binding.root.background = selectDrawable
|
||||
} else {
|
||||
binding.root.setBackgroundColor(getCompatColor(R.color.transparent))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface CallBack {
|
||||
fun onResult(data: Intent)
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
package io.legado.app.ui.file
|
||||
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.legado.app.base.BaseViewModel
|
||||
import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import java.io.File
|
||||
|
||||
class FilePickerViewModel(application: Application) : BaseViewModel(application) {
|
||||
|
||||
var rootDoc: File? = Environment.getExternalStorageDirectory()
|
||||
var subDocs = mutableListOf<File>()
|
||||
val filesLiveData = MutableLiveData<List<File>>()
|
||||
var mode: Int = HandleFileContract.FILE
|
||||
var isShowHideDir: Boolean = false
|
||||
var allowExtensions: Array<String>? = null
|
||||
val isSelectDir: Boolean get() = mode == HandleFileContract.DIR
|
||||
val isSelectFile: Boolean get() = mode == HandleFileContract.FILE
|
||||
val lastDir: File? get() = subDocs.lastOrNull() ?: rootDoc
|
||||
|
||||
fun initData(arguments: Bundle?) {
|
||||
arguments?.let {
|
||||
mode = it.getInt("mode", HandleFileContract.FILE)
|
||||
isShowHideDir = it.getBoolean("isShowHideDir")
|
||||
it.getString("initPath")?.let { path ->
|
||||
rootDoc = File(path)
|
||||
}
|
||||
allowExtensions = it.getStringArray("allowExtensions")
|
||||
}
|
||||
upFiles(rootDoc)
|
||||
}
|
||||
|
||||
fun upFiles(parentFile: File?) {
|
||||
execute {
|
||||
parentFile ?: return@execute emptyList()
|
||||
if (parentFile == rootDoc) {
|
||||
parentFile.listFiles()?.sortedWith(
|
||||
compareBy({ it.isFile }, { it.name })
|
||||
)
|
||||
} else {
|
||||
val list = arrayListOf(parentFile)
|
||||
parentFile.listFiles()?.sortedWith(
|
||||
compareBy({ it.isFile }, { it.name })
|
||||
)?.let {
|
||||
list.addAll(it)
|
||||
}
|
||||
list
|
||||
}
|
||||
}.onStart {
|
||||
filesLiveData.postValue(emptyList())
|
||||
}.onSuccess {
|
||||
filesLiveData.postValue(it ?: emptyList())
|
||||
}.onError {
|
||||
context.toastOnUi(it.localizedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
fun createFolder(name: String) {
|
||||
execute {
|
||||
val dir = lastDir ?: throw NoStackTraceException("父文件夹不存在")
|
||||
val folder = File(dir, name)
|
||||
if (!folder.canonicalPath.contains(dir.canonicalPath)) {
|
||||
throw NoStackTraceException("非法文件名")
|
||||
}
|
||||
folder.mkdir()
|
||||
}.onSuccess {
|
||||
upFiles(lastDir)
|
||||
}.onError {
|
||||
context.toastOnUi(it.localizedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
package io.legado.app.ui.file.adapter
|
||||
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import io.legado.app.base.adapter.ItemViewHolder
|
||||
import io.legado.app.base.adapter.RecyclerAdapter
|
||||
import io.legado.app.databinding.ItemFilePickerBinding
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.lib.theme.getPrimaryDisabledTextColor
|
||||
import io.legado.app.lib.theme.getPrimaryTextColor
|
||||
import io.legado.app.ui.file.entity.FileItem
|
||||
import io.legado.app.ui.file.utils.FilePickerIcon
|
||||
import io.legado.app.utils.ConvertUtils
|
||||
import io.legado.app.utils.FileUtils
|
||||
import java.io.File
|
||||
|
||||
|
||||
class FileAdapter(context: Context, val callBack: CallBack) :
|
||||
RecyclerAdapter<FileItem, ItemFilePickerBinding>(context) {
|
||||
private var rootPath: String? = null
|
||||
var currentPath: String? = null
|
||||
private set
|
||||
private val homeIcon = ConvertUtils.toDrawable(FilePickerIcon.getHome())!!
|
||||
private val upIcon = ConvertUtils.toDrawable(FilePickerIcon.getUpDir())!!
|
||||
private val folderIcon = ConvertUtils.toDrawable(FilePickerIcon.getFolder())!!
|
||||
private val fileIcon = ConvertUtils.toDrawable(FilePickerIcon.getFile())!!
|
||||
private val primaryTextColor = context.getPrimaryTextColor(!AppConfig.isNightTheme)
|
||||
private val disabledTextColor = context.getPrimaryDisabledTextColor(!AppConfig.isNightTheme)
|
||||
private val dirRoot = "."
|
||||
private val dirParent = ".."
|
||||
|
||||
fun loadData(path: String?) {
|
||||
if (path == null) {
|
||||
return
|
||||
}
|
||||
val data = ArrayList<FileItem>()
|
||||
if (rootPath == null) {
|
||||
rootPath = path
|
||||
}
|
||||
currentPath = path
|
||||
if (callBack.isShowHomeDir) {
|
||||
//添加“返回主目录”
|
||||
val fileRoot = FileItem(
|
||||
isDirectory = true,
|
||||
icon = homeIcon,
|
||||
name = dirRoot,
|
||||
path = rootPath ?: path
|
||||
)
|
||||
data.add(fileRoot)
|
||||
}
|
||||
if (callBack.isShowUpDir && path != PathAdapter.sdCardDirectory) {
|
||||
//添加“返回上一级目录”
|
||||
val fileParent = FileItem(
|
||||
isDirectory = true,
|
||||
icon = upIcon,
|
||||
name = dirParent,
|
||||
path = File(path).parent ?: ""
|
||||
)
|
||||
data.add(fileParent)
|
||||
}
|
||||
currentPath?.let { currentPath ->
|
||||
val files: Array<File>? = FileUtils.listDirsAndFiles(currentPath)
|
||||
if (files != null) {
|
||||
for (file in files) {
|
||||
if (!callBack.isShowHideDir && file.name.startsWith(".")) {
|
||||
continue
|
||||
}
|
||||
val fileItem = if (file.isDirectory) {
|
||||
FileItem(
|
||||
name = file.name,
|
||||
icon = folderIcon,
|
||||
path = file.absolutePath,
|
||||
isDirectory = true
|
||||
)
|
||||
} else {
|
||||
FileItem(
|
||||
name = file.name,
|
||||
icon = fileIcon,
|
||||
path = file.absolutePath,
|
||||
size = file.length(),
|
||||
isDirectory = true
|
||||
)
|
||||
}
|
||||
data.add(fileItem)
|
||||
}
|
||||
}
|
||||
setItems(data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getViewBinding(parent: ViewGroup): ItemFilePickerBinding {
|
||||
return ItemFilePickerBinding.inflate(inflater, parent, false)
|
||||
}
|
||||
|
||||
override fun convert(
|
||||
holder: ItemViewHolder,
|
||||
binding: ItemFilePickerBinding,
|
||||
item: FileItem,
|
||||
payloads: MutableList<Any>
|
||||
) {
|
||||
binding.apply {
|
||||
imageView.setImageDrawable(item.icon)
|
||||
textView.text = item.name
|
||||
if (item.isDirectory) {
|
||||
textView.setTextColor(primaryTextColor)
|
||||
} else {
|
||||
if (callBack.isSelectDir) {
|
||||
textView.setTextColor(disabledTextColor)
|
||||
} else {
|
||||
callBack.allowExtensions?.let {
|
||||
if (it.isEmpty() || it.contains(FileUtils.getExtension(item.path))) {
|
||||
textView.setTextColor(primaryTextColor)
|
||||
} else {
|
||||
textView.setTextColor(disabledTextColor)
|
||||
}
|
||||
} ?: textView.setTextColor(primaryTextColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerListener(holder: ItemViewHolder, binding: ItemFilePickerBinding) {
|
||||
holder.itemView.setOnClickListener {
|
||||
callBack.onFileClick(holder.layoutPosition)
|
||||
}
|
||||
}
|
||||
|
||||
interface CallBack {
|
||||
fun onFileClick(position: Int)
|
||||
|
||||
//允许的扩展名
|
||||
var allowExtensions: Array<String>?
|
||||
|
||||
/**
|
||||
* 是否选取目录
|
||||
*/
|
||||
val isSelectDir: Boolean
|
||||
|
||||
/**
|
||||
* 是否显示返回主目录
|
||||
*/
|
||||
var isShowHomeDir: Boolean
|
||||
|
||||
/**
|
||||
* 是否显示返回上一级
|
||||
*/
|
||||
var isShowUpDir: Boolean
|
||||
|
||||
/**
|
||||
* 是否显示隐藏的目录(以“.”开头)
|
||||
*/
|
||||
var isShowHideDir: Boolean
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,78 +0,0 @@
|
||||
package io.legado.app.ui.file.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.view.ViewGroup
|
||||
import io.legado.app.base.adapter.ItemViewHolder
|
||||
import io.legado.app.base.adapter.RecyclerAdapter
|
||||
import io.legado.app.databinding.ItemPathPickerBinding
|
||||
import io.legado.app.ui.file.utils.FilePickerIcon
|
||||
import io.legado.app.utils.ConvertUtils
|
||||
import java.util.*
|
||||
|
||||
|
||||
class PathAdapter(context: Context, val callBack: CallBack) :
|
||||
RecyclerAdapter<String, ItemPathPickerBinding>(context) {
|
||||
private val paths = LinkedList<String>()
|
||||
private val arrowIcon = ConvertUtils.toDrawable(FilePickerIcon.getArrow())
|
||||
|
||||
fun getPath(position: Int): String {
|
||||
val tmp = StringBuilder("$sdCardDirectory/")
|
||||
//忽略根目录
|
||||
if (position == 0) {
|
||||
return tmp.toString()
|
||||
}
|
||||
for (i in 1..position) {
|
||||
tmp.append(paths[i]).append("/")
|
||||
}
|
||||
return tmp.toString()
|
||||
}
|
||||
|
||||
fun updatePath(path: String) {
|
||||
var path1 = path
|
||||
path1 = path1.replace(sdCardDirectory, "")
|
||||
paths.clear()
|
||||
if (path1 != "/" && path1 != "") {
|
||||
val subDirs = path1.substring(path1.indexOf("/") + 1)
|
||||
.split("/")
|
||||
.dropLastWhile { it.isEmpty() }
|
||||
.toTypedArray()
|
||||
Collections.addAll(paths, *subDirs)
|
||||
}
|
||||
paths.addFirst(ROOT_HINT)
|
||||
setItems(paths)
|
||||
}
|
||||
|
||||
override fun getViewBinding(parent: ViewGroup): ItemPathPickerBinding {
|
||||
return ItemPathPickerBinding.inflate(inflater, parent, false)
|
||||
}
|
||||
|
||||
override fun convert(
|
||||
holder: ItemViewHolder,
|
||||
binding: ItemPathPickerBinding,
|
||||
item: String,
|
||||
payloads: MutableList<Any>
|
||||
) {
|
||||
binding.apply {
|
||||
textView.text = item
|
||||
imageView.setImageDrawable(arrowIcon)
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerListener(holder: ItemViewHolder, binding: ItemPathPickerBinding) {
|
||||
holder.itemView.setOnClickListener {
|
||||
callBack.onPathClick(holder.layoutPosition)
|
||||
}
|
||||
}
|
||||
|
||||
interface CallBack {
|
||||
fun onPathClick(position: Int)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ROOT_HINT = "SD"
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val sdCardDirectory: String = Environment.getExternalStorageDirectory().absolutePath
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package io.legado.app.ui.file.entity
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
/**
|
||||
* 文件项信息
|
||||
*/
|
||||
data class FileItem(
|
||||
var icon: Drawable,
|
||||
var name: String,
|
||||
var path: String = "/",
|
||||
var size: Long = 0,
|
||||
var isDirectory: Boolean = false,
|
||||
)
|
10
app/src/main/res/drawable/ic_create_folder_outline.xml
Normal file
10
app/src/main/res/drawable/ic_create_folder_outline.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector android:height="24dp"
|
||||
android:tint="#000000"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,6h-8l-2,-2L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM20,18L4,18L4,6h5.17l2,2L20,8v10zM12,14h2v2h2v-2h2v-2h-2v-2h-2v2h-2z" />
|
||||
</vector>
|
@ -25,7 +25,8 @@
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@color/background_card">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
@ -42,4 +43,13 @@
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<io.legado.app.ui.widget.text.AccentBgTextView
|
||||
android:id="@+id/tv_ok"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:layout_margin="3dp"
|
||||
android:text="@string/ok"
|
||||
android:gravity="center" />
|
||||
|
||||
</LinearLayout>
|
@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="5dp">
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_view"
|
||||
|
@ -1,11 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="5dp">
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="5dp"
|
||||
tools:ignore="UseCompoundDrawables,UselessParent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_view"
|
||||
@ -19,4 +25,6 @@
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
@ -3,10 +3,9 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_ok"
|
||||
android:title="@string/ok"
|
||||
android:visible="false"
|
||||
android:id="@+id/menu_create"
|
||||
android:title="@string/create_folder"
|
||||
android:icon="@drawable/ic_create_folder_outline"
|
||||
app:showAsAction="always" />
|
||||
|
||||
|
||||
</menu>
|
@ -1089,4 +1089,5 @@
|
||||
<string name="refuse">拒绝</string>
|
||||
<string name="file_manage">文件管理</string>
|
||||
<string name="file_manage_summary">管理私有文件夹的文件</string>
|
||||
<string name="create_folder">创建文件夹</string>
|
||||
</resources>
|
||||
|
@ -1092,4 +1092,5 @@
|
||||
<string name="refuse">拒绝</string>
|
||||
<string name="file_manage">文件管理</string>
|
||||
<string name="file_manage_summary">管理私有文件夹的文件</string>
|
||||
<string name="create_folder">创建文件夹</string>
|
||||
</resources>
|
||||
|
@ -1092,4 +1092,5 @@
|
||||
<string name="refuse">拒绝</string>
|
||||
<string name="file_manage">文件管理</string>
|
||||
<string name="file_manage_summary">管理私有文件夹的文件</string>
|
||||
<string name="create_folder">创建文件夹</string>
|
||||
</resources>
|
||||
|
@ -1089,4 +1089,5 @@
|
||||
<string name="refuse">拒绝</string>
|
||||
<string name="file_manage">文件管理</string>
|
||||
<string name="file_manage_summary">管理私有文件夹的文件</string>
|
||||
<string name="create_folder">创建文件夹</string>
|
||||
</resources>
|
||||
|
@ -1091,4 +1091,5 @@
|
||||
<string name="refuse">拒绝</string>
|
||||
<string name="file_manage">文件管理</string>
|
||||
<string name="file_manage_summary">管理私有文件夹的文件</string>
|
||||
<string name="create_folder">创建文件夹</string>
|
||||
</resources>
|
||||
|
@ -1091,4 +1091,5 @@
|
||||
<string name="refuse">拒绝</string>
|
||||
<string name="file_manage">文件管理</string>
|
||||
<string name="file_manage_summary">管理私有文件夹的文件</string>
|
||||
<string name="create_folder">创建文件夹</string>
|
||||
</resources>
|
||||
|
@ -1092,4 +1092,5 @@
|
||||
<string name="refuse">拒绝</string>
|
||||
<string name="file_manage">文件管理</string>
|
||||
<string name="file_manage_summary">管理私有文件夹的文件</string>
|
||||
<string name="create_folder">创建文件夹</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user