MediaSampleActivity.kt 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package com.convenient.android.lib
  2. import android.content.Intent
  3. import android.net.Uri
  4. import android.os.Bundle
  5. import android.os.Environment
  6. import android.os.Environment.getExternalStorageDirectory
  7. import android.provider.DocumentsContract
  8. import android.util.Log
  9. import androidx.activity.result.contract.ActivityResultContracts
  10. import androidx.appcompat.app.AppCompatActivity
  11. import androidx.lifecycle.lifecycleScope
  12. import com.convenient.android.common.base.viewbinding.BaseBindingActivity
  13. import com.convenient.android.common.config.MyPdfBaseModule
  14. import com.convenient.android.common.extension.*
  15. import com.convenient.android.common.media.MediaBean
  16. import com.convenient.android.common.media.config.MediaQueryConfig
  17. import com.convenient.android.common.media.config.MediaSortOrder
  18. import com.convenient.android.common.media.config.MediaSortType
  19. import com.convenient.android.common.media.scan.FileStore
  20. import com.convenient.android.common.media.scan.MediaStore
  21. import com.convenient.android.common.utils.date.DateUtils
  22. import com.convenient.android.lib.databinding.ActivityMediaSampleBinding
  23. import kotlinx.coroutines.Dispatchers
  24. import kotlinx.coroutines.flow.MutableStateFlow
  25. import kotlinx.coroutines.launch
  26. import kotlinx.coroutines.withContext
  27. import java.io.File
  28. class MediaSampleActivity : BaseBindingActivity<ActivityMediaSampleBinding>(ActivityMediaSampleBinding::inflate) {
  29. private var dir: String? = ""
  30. val chooseDirLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
  31. it.data?.data?.let {
  32. val filepath = resolveContentUri(it)
  33. spSave("dir", filepath)
  34. dir = filepath
  35. binding.tvQueryDir.text = filepath
  36. }
  37. }
  38. enum class QueryMode {
  39. FILE, MEDIA_STORE
  40. }
  41. private var queryMode: MutableStateFlow<QueryMode> = MutableStateFlow(QueryMode.FILE)
  42. override fun onCreate(savedInstanceState: Bundle?) {
  43. super.onCreate(savedInstanceState)
  44. MyPdfBaseModule.init(application, true, "1")
  45. binding = ActivityMediaSampleBinding.inflate(layoutInflater)
  46. setContentView(binding.root)
  47. dir = spGetString("dir")
  48. binding.tvQueryDir.text = dir
  49. setViewsClick(
  50. {
  51. when (it) {
  52. binding.btnQueryFromFiles -> {
  53. queryMode.value = QueryMode.FILE
  54. query()
  55. }
  56. binding.btnQueryFromMediaStore -> {
  57. queryMode.value = QueryMode.MEDIA_STORE
  58. query()
  59. }
  60. binding.btnChooseDir -> {
  61. chooseDir()
  62. }
  63. }
  64. },
  65. binding.btnQueryFromFiles,
  66. binding.btnQueryFromMediaStore,
  67. binding.btnChooseDir
  68. )
  69. lifecycleScope.launch {
  70. queryMode.collect {
  71. binding.tvQueryType.text = when (it) {
  72. QueryMode.MEDIA_STORE -> "MediaStore"
  73. QueryMode.FILE -> "File"
  74. }
  75. }
  76. }
  77. }
  78. private fun chooseDir() {
  79. chooseDirLauncher.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
  80. })
  81. }
  82. private fun getConfig(): MediaQueryConfig = MediaQueryConfig().also {
  83. it.supportMimeTypes = listOf("pdf")
  84. it.recursively = true
  85. it.order = MediaSortOrder.ASC
  86. it.sortType = MediaSortType.SIZE
  87. it.includeFolder = false
  88. it.ignoreChildFiles = mutableListOf(
  89. // File(dir, "新建文件夹/"),
  90. // File(dir, "Child/Kotlin 语言文档.pdf")
  91. // File("/storage/emulated/0/17PDF/")
  92. )
  93. }.also { config ->
  94. lifecycleScope.launch(Dispatchers.Main) {
  95. binding.tvFileType.text = config.supportMimeTypes.toString()
  96. binding.tvQueryIgnoreChildDir.text = config.includeFolder.toString()
  97. binding.tvQueryRecursively.text = config.recursively.toString()
  98. val builder = StringBuilder()
  99. config.ignoreChildFiles.forEach {
  100. builder.append(it.absolutePath).append("\n")
  101. }
  102. binding.tvQueryIgnoreChildFiles.text = builder
  103. }
  104. }
  105. private fun query() {
  106. lifecycleScope.launch(Dispatchers.Main) {
  107. // dir = ""
  108. val data = when (queryMode.value) {
  109. QueryMode.FILE -> {
  110. queryFromFile()
  111. }
  112. else -> {
  113. queryFromMediaStore()
  114. }
  115. }
  116. val mimeTypes = data.map {
  117. it.extension
  118. }.distinct()
  119. binding.tvResultCount.text = data.size.toString()
  120. binding.tvResultFileTypes.text = mimeTypes.toString()
  121. for (datum in data) {
  122. Log.e("Media", "名称:${datum.mediaPath}, ${DateUtils.getFormatDate(datum.lastModified, "MM-dd HH:mm:ss")},--:${datum.mediaPath.toFile()?.lengthToFitMemorySize()}")
  123. }
  124. }
  125. }
  126. private suspend fun queryFromFile(): List<MediaBean> = withContext(Dispatchers.IO) {
  127. FileStore.query(dir ?: Environment.getExternalStorageDirectory().absolutePath, getConfig())
  128. }
  129. private suspend fun queryFromMediaStore(): List<MediaBean> = withContext(Dispatchers.IO) {
  130. MediaStore.query(dir = dir ?: "", config = getConfig())
  131. }
  132. fun resolveContentUri(uri: Uri): String {
  133. val docUri = DocumentsContract.buildDocumentUriUsingTree(uri, DocumentsContract.getTreeDocumentId(uri))
  134. val docCursor = contentResolver.query(docUri, null, null, null, null)
  135. var str: String = ""
  136. // get a string of the form : primary:Audiobooks or 1407-1105:Audiobooks
  137. while (docCursor!!.moveToNext()) {
  138. str = docCursor.getString(0)
  139. if (str.matches(Regex(".*:.*"))) break //Maybe useless
  140. }
  141. docCursor.close()
  142. val split = str.split(":")
  143. val base: File =
  144. if (split[0] == "primary") getExternalStorageDirectory()
  145. else File("/storage/${split[0]}")
  146. if (!base.isDirectory) throw Exception("'$uri' cannot be resolved in a valid path")
  147. return File(base, split[1]).canonicalPath
  148. }
  149. }