This commit is contained in:
kunfei 2023-04-01 01:23:13 +08:00
parent 00881414c2
commit 2a6dc8b9ed
18 changed files with 325 additions and 376 deletions

View File

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

View File

@ -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
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_ok -> fileAdapter.currentPath?.let {
setData(it)
dismissAllowingStateLoss()
}
}
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)
binding.tvOk.setOnClickListener {
if (viewModel.isSelectDir) {
viewModel.lastDir?.let {
setResultData(it.path)
dismissAllowingStateLoss()
}
} else {
val file = fileAdapter.selectFile
if (file == null) {
toastOnUi("请选择文件")
} else {
toastOnUi("不能打开此文件")
setResultData(file.path)
dismissAllowingStateLoss()
}
}
}
}
override fun onPathClick(position: Int) {
refreshCurrentDirPath(pathAdapter.getPath(position))
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
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
}
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)
}

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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