pull/1738/head
kunfei 3 years ago
parent fa11028997
commit a3b9917c47
  1. 29
      app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt
  2. 9
      app/src/main/java/io/legado/app/ui/book/group/GroupEditDialog.kt
  3. 11
      app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditActivity.kt
  4. 2
      app/src/main/java/io/legado/app/ui/book/local/ImportBookAdapter.kt
  5. 2
      app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt
  6. 9
      app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt
  7. 9
      app/src/main/java/io/legado/app/ui/config/CoverConfigFragment.kt
  8. 9
      app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
  9. 9
      app/src/main/java/io/legado/app/ui/config/WelcomeConfigFragment.kt
  10. 11
      app/src/main/java/io/legado/app/utils/DocumentExtensions.kt
  11. 42
      app/src/main/java/io/legado/app/utils/UriExtensions.kt

@ -102,43 +102,40 @@ class FileAssociationActivity :
private fun importBook(treeUri: Uri, uri: Uri) { private fun importBook(treeUri: Uri, uri: Uri) {
launch { launch {
runCatching { runCatching {
if (treeUri.isContentScheme()) {
val treeDoc = DocumentFile.fromTreeUri(this@FileAssociationActivity, treeUri)
val bookDoc = DocumentFile.fromSingleUri(this@FileAssociationActivity, uri)
withContext(IO) { withContext(IO) {
val name = bookDoc?.name!! if (treeUri.isContentScheme()) {
val treeDoc =
DocumentFile.fromTreeUri(this@FileAssociationActivity, treeUri)
readUri(uri) { fileDoc, inputStream ->
val name = fileDoc.name
var doc = treeDoc!!.findFile(name) var doc = treeDoc!!.findFile(name)
if (doc == null || bookDoc.lastModified() > doc.lastModified()) { if (doc == null || fileDoc.lastModified > doc.lastModified()) {
if (doc == null) { if (doc == null) {
doc = treeDoc.createFile(FileUtils.getMimeType(name), name) doc = treeDoc.createFile(FileUtils.getMimeType(name), name)
?: throw SecurityException("Permission Denial") ?: throw SecurityException("Permission Denial")
} }
contentResolver.openOutputStream(doc.uri)!!.use { oStream -> contentResolver.openOutputStream(doc.uri)!!.use { oStream ->
contentResolver.openInputStream(bookDoc.uri)!!.use { iStream -> inputStream.copyTo(oStream)
iStream.copyTo(oStream)
oStream.flush() oStream.flush()
} }
} }
}
viewModel.importBook(doc.uri) viewModel.importBook(doc.uri)
} }
} else { } else {
val treeFile = File(treeUri.path!!) val treeFile = File(treeUri.path ?: treeUri.toString())
val bookDoc = DocumentFile.fromSingleUri(this@FileAssociationActivity, uri) readUri(uri) { fileDoc, inputStream ->
withContext(IO) { val name = fileDoc.name
val name = bookDoc?.name!!
val file = treeFile.getFile(name) val file = treeFile.getFile(name)
if (!file.exists() || file.lastModified() < bookDoc.lastModified()) { if (!file.exists() || fileDoc.lastModified > file.lastModified()) {
FileOutputStream(file).use { oStream -> FileOutputStream(file).use { oStream ->
contentResolver.openInputStream(bookDoc.uri)!!.use { iStream -> inputStream.copyTo(oStream)
iStream.copyTo(oStream)
oStream.flush() oStream.flush()
} }
} }
}
viewModel.importBook(Uri.fromFile(file)) viewModel.importBook(Uri.fromFile(file))
} }
} }
}
}.onFailure { }.onFailure {
when (it) { when (it) {
is SecurityException -> localBookTreeSelect.launch { is SecurityException -> localBookTreeSelect.launch {

@ -13,6 +13,7 @@ import io.legado.app.lib.theme.primaryColor
import io.legado.app.utils.* import io.legado.app.utils.*
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import splitties.views.onClick import splitties.views.onClick
import java.io.FileOutputStream
class GroupEditDialog() : BaseDialogFragment(R.layout.dialog_book_group_edit) { class GroupEditDialog() : BaseDialogFragment(R.layout.dialog_book_group_edit) {
@ -26,10 +27,12 @@ class GroupEditDialog() : BaseDialogFragment(R.layout.dialog_book_group_edit) {
private val viewModel by viewModels<GroupViewModel>() private val viewModel by viewModels<GroupViewModel>()
private var bookGroup: BookGroup? = null private var bookGroup: BookGroup? = null
val selectImage = registerForActivityResult(SelectImageContract()) { val selectImage = registerForActivityResult(SelectImageContract()) {
readUri(it?.uri) { name, bytes -> readUri(it?.uri) { fileDoc, inputStream ->
var file = requireContext().externalFiles var file = requireContext().externalFiles
file = FileUtils.createFileIfNotExist(file, "covers", name) file = FileUtils.createFileIfNotExist(file, "covers", fileDoc.name)
file.writeBytes(bytes) FileOutputStream(file).use { outputStream ->
inputStream.copyTo(outputStream)
}
binding.ivCover.load(file.absolutePath) binding.ivCover.load(file.absolutePath)
} }
} }

@ -13,6 +13,7 @@ import io.legado.app.databinding.ActivityBookInfoEditBinding
import io.legado.app.ui.book.changecover.ChangeCoverDialog import io.legado.app.ui.book.changecover.ChangeCoverDialog
import io.legado.app.utils.* import io.legado.app.utils.*
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import java.io.FileOutputStream
class BookInfoEditActivity : class BookInfoEditActivity :
VMBaseActivity<ActivityBookInfoEditBinding, BookInfoEditViewModel>(), VMBaseActivity<ActivityBookInfoEditBinding, BookInfoEditViewModel>(),
@ -101,12 +102,16 @@ class BookInfoEditActivity :
} }
private fun coverChangeTo(uri: Uri) { private fun coverChangeTo(uri: Uri) {
readUri(uri) { name, bytes -> readUri(uri) { fileDoc, inputStream ->
inputStream.use {
var file = this.externalFiles var file = this.externalFiles
file = FileUtils.createFileIfNotExist(file, "covers", name) file = FileUtils.createFileIfNotExist(file, "covers", fileDoc.name)
file.writeBytes(bytes) FileOutputStream(file).use { outputStream ->
inputStream.copyTo(outputStream)
}
coverChangeTo(file.absolutePath) coverChangeTo(file.absolutePath)
} }
} }
}
} }

@ -52,7 +52,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
llBrief.visible() llBrief.visible()
tvTag.text = item.name.substringAfterLast(".") tvTag.text = item.name.substringAfterLast(".")
tvSize.text = ConvertUtils.formatFileSize(item.size) tvSize.text = ConvertUtils.formatFileSize(item.size)
tvDate.text = AppConst.dateFormat.format(item.date) tvDate.text = AppConst.dateFormat.format(item.lastModified)
cbSelect.isChecked = selectedUris.contains(item.toString()) cbSelect.isChecked = selectedUris.contains(item.toString())
} }
tvName.text = item.name tvName.text = item.name

@ -59,7 +59,7 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
}.map { docList -> }.map { docList ->
when (sort) { when (sort) {
2 -> docList.sortedWith( 2 -> docList.sortedWith(
compareBy({ !it.isDir }, { it.date }, { it.name }) compareBy({ !it.isDir }, { it.lastModified }, { it.name })
) )
1 -> docList.sortedWith( 1 -> docList.sortedWith(
compareBy({ !it.isDir }, { it.size }, { it.name }) compareBy({ !it.isDir }, { it.size }, { it.name })

@ -35,6 +35,7 @@ import io.legado.app.utils.*
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import java.io.File import java.io.File
import java.io.FileOutputStream
class BgTextConfigDialog : BaseDialogFragment(R.layout.dialog_read_bg_text) { class BgTextConfigDialog : BaseDialogFragment(R.layout.dialog_read_bg_text) {
@ -350,10 +351,12 @@ class BgTextConfigDialog : BaseDialogFragment(R.layout.dialog_read_bg_text) {
} }
private fun setBgFromUri(uri: Uri) { private fun setBgFromUri(uri: Uri) {
readUri(uri) { name, bytes -> readUri(uri) { fileDoc, inputStream ->
var file = requireContext().externalFiles var file = requireContext().externalFiles
file = FileUtils.createFileIfNotExist(file, "bg", name) file = FileUtils.createFileIfNotExist(file, "bg", fileDoc.name)
file.writeBytes(bytes) FileOutputStream(file).use { outputStream ->
inputStream.copyTo(outputStream)
}
ReadBookConfig.durConfig.setCurBg(2, file.absolutePath) ReadBookConfig.durConfig.setCurBg(2, file.absolutePath)
ReadBookConfig.upBg() ReadBookConfig.upBg()
postEvent(EventBus.UP_CONFIG, false) postEvent(EventBus.UP_CONFIG, false)

@ -14,6 +14,7 @@ import io.legado.app.lib.theme.primaryColor
import io.legado.app.model.BookCover import io.legado.app.model.BookCover
import io.legado.app.ui.widget.prefs.SwitchPreference import io.legado.app.ui.widget.prefs.SwitchPreference
import io.legado.app.utils.* import io.legado.app.utils.*
import java.io.FileOutputStream
class CoverConfigFragment : BasePreferenceFragment(), class CoverConfigFragment : BasePreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
@ -138,10 +139,12 @@ class CoverConfigFragment : BasePreferenceFragment(),
} }
private fun setCoverFromUri(preferenceKey: String, uri: Uri) { private fun setCoverFromUri(preferenceKey: String, uri: Uri) {
readUri(uri) { name, bytes -> readUri(uri) { fileDoc, inputStream ->
var file = requireContext().externalFiles var file = requireContext().externalFiles
file = FileUtils.createFileIfNotExist(file, "covers", name) file = FileUtils.createFileIfNotExist(file, "covers", fileDoc.name)
file.writeBytes(bytes) FileOutputStream(file).use {
inputStream.copyTo(it)
}
putPrefString(preferenceKey, file.absolutePath) putPrefString(preferenceKey, file.absolutePath)
BookCover.upDefaultCover() BookCover.upDefaultCover()
} }

@ -29,6 +29,7 @@ import io.legado.app.ui.widget.number.NumberPickerDialog
import io.legado.app.ui.widget.prefs.ColorPreference import io.legado.app.ui.widget.prefs.ColorPreference
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
import io.legado.app.utils.* import io.legado.app.utils.*
import java.io.FileOutputStream
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
@ -298,10 +299,12 @@ class ThemeConfigFragment : BasePreferenceFragment(),
} }
private fun setBgFromUri(uri: Uri, preferenceKey: String, success: () -> Unit) { private fun setBgFromUri(uri: Uri, preferenceKey: String, success: () -> Unit) {
readUri(uri) { name, bytes -> readUri(uri) { fileDoc, inputStream ->
var file = requireContext().externalFiles var file = requireContext().externalFiles
file = FileUtils.createFileIfNotExist(file, preferenceKey, name) file = FileUtils.createFileIfNotExist(file, preferenceKey, fileDoc.name)
file.writeBytes(bytes) FileOutputStream(file).use {
inputStream.copyTo(it)
}
putPrefString(preferenceKey, file.absolutePath) putPrefString(preferenceKey, file.absolutePath)
success() success()
} }

@ -13,6 +13,7 @@ import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.model.BookCover import io.legado.app.model.BookCover
import io.legado.app.utils.* import io.legado.app.utils.*
import java.io.FileOutputStream
class WelcomeConfigFragment : BasePreferenceFragment(), class WelcomeConfigFragment : BasePreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
@ -118,10 +119,12 @@ class WelcomeConfigFragment : BasePreferenceFragment(),
} }
private fun setCoverFromUri(preferenceKey: String, uri: Uri) { private fun setCoverFromUri(preferenceKey: String, uri: Uri) {
readUri(uri) { name, bytes -> readUri(uri) { fileDoc, inputStream ->
var file = requireContext().externalFiles var file = requireContext().externalFiles
file = FileUtils.createFileIfNotExist(file, "covers", name) file = FileUtils.createFileIfNotExist(file, "covers", fileDoc.name)
file.writeBytes(bytes) FileOutputStream(file).use {
inputStream.copyTo(it)
}
putPrefString(preferenceKey, file.absolutePath) putPrefString(preferenceKey, file.absolutePath)
} }
} }

@ -9,7 +9,6 @@ import io.legado.app.exception.NoStackTraceException
import splitties.init.appCtx import splitties.init.appCtx
import java.io.File import java.io.File
import java.nio.charset.Charset import java.nio.charset.Charset
import java.util.*
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
@ -125,7 +124,7 @@ object DocumentUtils {
name = cursor.getString(nci), name = cursor.getString(nci),
isDir = cursor.getString(mci) == DocumentsContract.Document.MIME_TYPE_DIR, isDir = cursor.getString(mci) == DocumentsContract.Document.MIME_TYPE_DIR,
size = cursor.getLong(sci), size = cursor.getLong(sci),
date = Date(cursor.getLong(dci)), lastModified = cursor.getLong(dci),
uri = DocumentsContract uri = DocumentsContract
.buildDocumentUriUsingTree(uri, cursor.getString(ici)) .buildDocumentUriUsingTree(uri, cursor.getString(ici))
) )
@ -150,7 +149,7 @@ object DocumentUtils {
it.name, it.name,
it.isDirectory, it.isDirectory,
it.length(), it.length(),
Date(it.lastModified()), it.lastModified(),
Uri.fromFile(it) Uri.fromFile(it)
) )
if (filter == null || filter.invoke(item)) { if (filter == null || filter.invoke(item)) {
@ -166,7 +165,7 @@ data class FileDoc(
val name: String, val name: String,
val isDir: Boolean, val isDir: Boolean,
val size: Long, val size: Long,
val date: Date, val lastModified: Long,
val uri: Uri val uri: Uri
) { ) {
@ -187,7 +186,7 @@ data class FileDoc(
name = doc.name ?: "", name = doc.name ?: "",
isDir = doc.isDirectory, isDir = doc.isDirectory,
size = doc.length(), size = doc.length(),
date = Date(doc.lastModified()), lastModified = doc.lastModified(),
uri = doc.uri uri = doc.uri
) )
} }
@ -197,7 +196,7 @@ data class FileDoc(
name = file.name, name = file.name,
isDir = file.isDirectory, isDir = file.isDirectory,
size = file.length(), size = file.length(),
date = Date(file.lastModified()), lastModified = file.lastModified(),
uri = Uri.fromFile(file) uri = Uri.fromFile(file)
) )
} }

@ -10,32 +10,41 @@ import io.legado.app.exception.NoStackTraceException
import io.legado.app.lib.permission.Permissions import io.legado.app.lib.permission.Permissions
import io.legado.app.lib.permission.PermissionsCompat import io.legado.app.lib.permission.PermissionsCompat
import java.io.File import java.io.File
import java.io.FileInputStream
import java.io.InputStream
fun Uri.isContentScheme() = this.scheme == "content" fun Uri.isContentScheme() = this.scheme == "content"
/** /**
* 读取URI * 读取URI
*/ */
fun AppCompatActivity.readUri(uri: Uri?, success: (name: String, bytes: ByteArray) -> Unit) { fun AppCompatActivity.readUri(
uri: Uri?,
success: (fileDoc: FileDoc, inputStream: InputStream) -> Unit
) {
uri ?: return uri ?: return
try { try {
if (uri.isContentScheme()) { if (uri.isContentScheme()) {
val doc = DocumentFile.fromSingleUri(this, uri) val doc = DocumentFile.fromSingleUri(this, uri)
doc ?: throw NoStackTraceException("未获取到文件") doc ?: throw NoStackTraceException("未获取到文件")
val name = doc.name ?: throw NoStackTraceException("未获取到文件名") val fileDoc = FileDoc.fromDocumentFile(doc)
val fileBytes = DocumentUtils.readBytes(this, doc.uri) contentResolver.openInputStream(uri)!!.use { inputStream ->
success.invoke(name, fileBytes) success.invoke(fileDoc, inputStream)
}
} else { } else {
PermissionsCompat.Builder(this) PermissionsCompat.Builder(this)
.addPermissions( .addPermissions(
Permissions.READ_EXTERNAL_STORAGE, Permissions.READ_EXTERNAL_STORAGE,
Permissions.WRITE_EXTERNAL_STORAGE Permissions.WRITE_EXTERNAL_STORAGE
) )
.rationale(R.string.bg_image_per) .rationale(R.string.get_storage_per)
.onGranted { .onGranted {
RealPathUtil.getPath(this, uri)?.let { path -> RealPathUtil.getPath(this, uri)?.let { path ->
val imgFile = File(path) val file = File(path)
success.invoke(imgFile.name, imgFile.readBytes()) val fileDoc = FileDoc.fromFile(file)
FileInputStream(file).use { inputStream ->
success.invoke(fileDoc, inputStream)
}
} }
} }
.request() .request()
@ -49,26 +58,31 @@ fun AppCompatActivity.readUri(uri: Uri?, success: (name: String, bytes: ByteArra
/** /**
* 读取URI * 读取URI
*/ */
fun Fragment.readUri(uri: Uri?, success: (name: String, bytes: ByteArray) -> Unit) { fun Fragment.readUri(uri: Uri?, success: (fileDoc: FileDoc, inputStream: InputStream) -> Unit) {
uri ?: return uri ?: return
try { try {
if (uri.isContentScheme()) { if (uri.isContentScheme()) {
val doc = DocumentFile.fromSingleUri(requireContext(), uri) val doc = DocumentFile.fromSingleUri(requireContext(), uri)
doc ?: throw NoStackTraceException("未获取到文件") doc ?: throw NoStackTraceException("未获取到文件")
val name = doc.name ?: throw NoStackTraceException("未获取到文件名") val fileDoc = FileDoc.fromDocumentFile(doc)
val fileBytes = DocumentUtils.readBytes(requireContext(), doc.uri) requireContext().contentResolver.openInputStream(uri)!!.use { inputStream ->
success.invoke(name, fileBytes) success.invoke(fileDoc, inputStream)
}
} else { } else {
PermissionsCompat.Builder(this) PermissionsCompat.Builder(this)
.addPermissions( .addPermissions(
Permissions.READ_EXTERNAL_STORAGE, Permissions.READ_EXTERNAL_STORAGE,
Permissions.WRITE_EXTERNAL_STORAGE Permissions.WRITE_EXTERNAL_STORAGE
) )
.rationale(R.string.bg_image_per) .rationale(R.string.get_storage_per)
.onGranted { .onGranted {
RealPathUtil.getPath(requireContext(), uri)?.let { path -> RealPathUtil.getPath(requireContext(), uri)?.let { path ->
val imgFile = File(path) val file = File(path)
success.invoke(imgFile.name, imgFile.readBytes()) val fileDoc = FileDoc.fromFile(file)
FileInputStream(file).use { inputStream ->
success.invoke(fileDoc, inputStream)
}
} }
} }
.request() .request()

Loading…
Cancel
Save