kunfei 3 лет назад
Родитель
Сommit
d81685ab86

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

@@ -11,10 +11,11 @@
 * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
 * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
 
-**2023/03/30**
+**2023/03/31**
 
 * 原app最低支持版本提升到android7.0
 * 创建lollipop版本支持android5.0
+* 增加内置文件管理,管理私有文件
 
 **2023/03/29**
 

+ 185 - 99
app/src/main/java/io/legado/app/ui/file/FilePickerDialog.kt

@@ -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,101 +85,203 @@ 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("不能打开此文件")
-                }
-            }
-        }
+    private fun setResultData(path: String) {
+        val data = Intent().setData(Uri.fromFile(File(path)))
+        (parentFragment as? CallBack)?.onResult(data)
+        (activity as? CallBack)?.onResult(data)
     }
 
-    override fun onPathClick(position: Int) {
-        refreshCurrentDirPath(pathAdapter.getPath(position))
+    override fun onDismiss(dialog: DialogInterface) {
+        super.onDismiss(dialog)
+        activity?.finish()
     }
 
-    private fun refreshCurrentDirPath(currentPath: String) {
-        if (currentPath == "/") {
-            pathAdapter.updatePath("/")
-        } else {
-            pathAdapter.updatePath(currentPath)
+    @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)
+                    }
+                }
+            }
         }
-        fileAdapter.loadData(currentPath)
-        var adapterCount = fileAdapter.itemCount
-        if (isShowHomeDir) {
-            adapterCount--
+
+        override fun getViewBinding(parent: ViewGroup): ItemPathPickerBinding {
+            return ItemPathPickerBinding.inflate(inflater, parent, false).apply {
+                imageView.setImageDrawable(arrowIcon)
+            }
         }
-        if (isShowUpDir) {
-            adapterCount--
+
+        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())
+            }
         }
-        if (adapterCount < 1) {
-            binding.tvEmpty.visible()
-            binding.tvEmpty.setText(R.string.empty)
-        } else {
-            binding.tvEmpty.gone()
+
+        override fun convert(
+            holder: ItemViewHolder,
+            binding: ItemPathPickerBinding,
+            item: File,
+            payloads: MutableList<Any>
+        ) {
+            binding.textView.text = item.name
         }
-    }
 
-    private fun setData(path: String) {
-        val data = Intent().setData(Uri.fromFile(File(path)))
-        (parentFragment as? CallBack)?.onResult(data)
-        (activity as? CallBack)?.onResult(data)
     }
 
-    override fun onDismiss(dialog: DialogInterface) {
-        super.onDismiss(dialog)
-        activity?.finish()
+    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 {

+ 76 - 0
app/src/main/java/io/legado/app/ui/file/FilePickerViewModel.kt

@@ -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)
+        }
+    }
+
+}

+ 0 - 158
app/src/main/java/io/legado/app/ui/file/adapter/FileAdapter.kt

@@ -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
-    }
-
-}
-

+ 0 - 78
app/src/main/java/io/legado/app/ui/file/adapter/PathAdapter.kt

@@ -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
-    }
-}

+ 0 - 14
app/src/main/java/io/legado/app/ui/file/entity/FileItem.kt

@@ -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 - 0
app/src/main/res/drawable/ic_create_folder_outline.xml

@@ -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>

+ 11 - 1
app/src/main/res/layout/dialog_file_chooser.xml

@@ -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>

+ 4 - 2
app/src/main/res/layout/item_file.xml

@@ -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"

+ 24 - 16
app/src/main/res/layout/item_file_picker.xml

@@ -1,22 +1,30 @@
 <?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:orientation="horizontal"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:gravity="center_vertical"
-    android:padding="5dp">
+    android:layout_height="wrap_content">
 
-    <ImageView
-        android:id="@+id/image_view"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        tools:ignore="ContentDescription" />
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:background="?attr/selectableItemBackground"
+        android:padding="5dp"
+        tools:ignore="UseCompoundDrawables,UselessParent">
 
-    <TextView
-        android:id="@+id/text_view"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:gravity="center_vertical" />
+        <ImageView
+            android:id="@+id/image_view"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            tools:ignore="ContentDescription" />
 
-</LinearLayout>
+        <TextView
+            android:id="@+id/text_view"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical" />
+
+    </LinearLayout>
+
+</FrameLayout>

+ 3 - 4
app/src/main/res/menu/file_chooser.xml

@@ -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>

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

@@ -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>

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

@@ -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>

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

@@ -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>

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

@@ -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>

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

@@ -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>

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

@@ -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>

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

@@ -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>