123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- package com.convenient.android.lib
- import android.content.Intent
- import android.net.Uri
- import android.os.Bundle
- import android.os.Environment
- import android.os.Environment.getExternalStorageDirectory
- import android.provider.DocumentsContract
- import android.util.Log
- import androidx.activity.result.contract.ActivityResultContracts
- import androidx.appcompat.app.AppCompatActivity
- import androidx.lifecycle.lifecycleScope
- import com.convenient.android.common.base.viewbinding.BaseBindingActivity
- import com.convenient.android.common.config.MyPdfBaseModule
- import com.convenient.android.common.extension.*
- import com.convenient.android.common.media.MediaBean
- import com.convenient.android.common.media.config.MediaQueryConfig
- import com.convenient.android.common.media.config.MediaSortOrder
- import com.convenient.android.common.media.config.MediaSortType
- import com.convenient.android.common.media.scan.FileStore
- import com.convenient.android.common.media.scan.MediaStore
- import com.convenient.android.common.utils.date.DateUtils
- import com.convenient.android.lib.databinding.ActivityMediaSampleBinding
- import kotlinx.coroutines.Dispatchers
- import kotlinx.coroutines.flow.MutableStateFlow
- import kotlinx.coroutines.launch
- import kotlinx.coroutines.withContext
- import java.io.File
- class MediaSampleActivity : BaseBindingActivity<ActivityMediaSampleBinding>(ActivityMediaSampleBinding::inflate) {
- private var dir: String? = ""
- val chooseDirLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- it.data?.data?.let {
- val filepath = resolveContentUri(it)
- spSave("dir", filepath)
- dir = filepath
- binding.tvQueryDir.text = filepath
- }
- }
- enum class QueryMode {
- FILE, MEDIA_STORE
- }
- private var queryMode: MutableStateFlow<QueryMode> = MutableStateFlow(QueryMode.FILE)
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- MyPdfBaseModule.init(application, true, "1")
- binding = ActivityMediaSampleBinding.inflate(layoutInflater)
- setContentView(binding.root)
- dir = spGetString("dir")
- binding.tvQueryDir.text = dir
- setViewsClick(
- {
- when (it) {
- binding.btnQueryFromFiles -> {
- queryMode.value = QueryMode.FILE
- query()
- }
- binding.btnQueryFromMediaStore -> {
- queryMode.value = QueryMode.MEDIA_STORE
- query()
- }
- binding.btnChooseDir -> {
- chooseDir()
- }
- }
- },
- binding.btnQueryFromFiles,
- binding.btnQueryFromMediaStore,
- binding.btnChooseDir
- )
- lifecycleScope.launch {
- queryMode.collect {
- binding.tvQueryType.text = when (it) {
- QueryMode.MEDIA_STORE -> "MediaStore"
- QueryMode.FILE -> "File"
- }
- }
- }
- }
- private fun chooseDir() {
- chooseDirLauncher.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
- })
- }
- private fun getConfig(): MediaQueryConfig = MediaQueryConfig().also {
- it.supportMimeTypes = listOf("pdf")
- it.recursively = true
- it.order = MediaSortOrder.ASC
- it.sortType = MediaSortType.SIZE
- it.includeFolder = false
- it.ignoreChildFiles = mutableListOf(
- // File(dir, "新建文件夹/"),
- // File(dir, "Child/Kotlin 语言文档.pdf")
- // File("/storage/emulated/0/17PDF/")
- )
- }.also { config ->
- lifecycleScope.launch(Dispatchers.Main) {
- binding.tvFileType.text = config.supportMimeTypes.toString()
- binding.tvQueryIgnoreChildDir.text = config.includeFolder.toString()
- binding.tvQueryRecursively.text = config.recursively.toString()
- val builder = StringBuilder()
- config.ignoreChildFiles.forEach {
- builder.append(it.absolutePath).append("\n")
- }
- binding.tvQueryIgnoreChildFiles.text = builder
- }
- }
- private fun query() {
- lifecycleScope.launch(Dispatchers.Main) {
- // dir = ""
- val data = when (queryMode.value) {
- QueryMode.FILE -> {
- queryFromFile()
- }
- else -> {
- queryFromMediaStore()
- }
- }
- val mimeTypes = data.map {
- it.extension
- }.distinct()
- binding.tvResultCount.text = data.size.toString()
- binding.tvResultFileTypes.text = mimeTypes.toString()
- for (datum in data) {
- Log.e("Media", "名称:${datum.mediaPath}, ${DateUtils.getFormatDate(datum.lastModified, "MM-dd HH:mm:ss")},--:${datum.mediaPath.toFile()?.lengthToFitMemorySize()}")
- }
- }
- }
- private suspend fun queryFromFile(): List<MediaBean> = withContext(Dispatchers.IO) {
- FileStore.query(dir ?: Environment.getExternalStorageDirectory().absolutePath, getConfig())
- }
- private suspend fun queryFromMediaStore(): List<MediaBean> = withContext(Dispatchers.IO) {
- MediaStore.query(dir = dir ?: "", config = getConfig())
- }
- fun resolveContentUri(uri: Uri): String {
- val docUri = DocumentsContract.buildDocumentUriUsingTree(uri, DocumentsContract.getTreeDocumentId(uri))
- val docCursor = contentResolver.query(docUri, null, null, null, null)
- var str: String = ""
- // get a string of the form : primary:Audiobooks or 1407-1105:Audiobooks
- while (docCursor!!.moveToNext()) {
- str = docCursor.getString(0)
- if (str.matches(Regex(".*:.*"))) break //Maybe useless
- }
- docCursor.close()
- val split = str.split(":")
- val base: File =
- if (split[0] == "primary") getExternalStorageDirectory()
- else File("/storage/${split[0]}")
- if (!base.isDirectory) throw Exception("'$uri' cannot be resolved in a valid path")
- return File(base, split[1]).canonicalPath
- }
- }
|