|
@@ -0,0 +1,277 @@
|
|
|
+package com.convenient.android.lib.ui.sample.media
|
|
|
+
|
|
|
+import android.annotation.SuppressLint
|
|
|
+import android.content.Intent
|
|
|
+import android.os.Build
|
|
|
+import androidx.activity.compose.rememberLauncherForActivityResult
|
|
|
+import androidx.activity.result.contract.ActivityResultContracts
|
|
|
+import androidx.annotation.RequiresApi
|
|
|
+import androidx.compose.foundation.Image
|
|
|
+import androidx.compose.foundation.background
|
|
|
+import androidx.compose.foundation.layout.*
|
|
|
+import androidx.compose.foundation.lazy.LazyColumn
|
|
|
+import androidx.compose.foundation.lazy.items
|
|
|
+import androidx.compose.foundation.rememberScrollState
|
|
|
+import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
+import androidx.compose.foundation.text.KeyboardOptions
|
|
|
+import androidx.compose.foundation.verticalScroll
|
|
|
+import androidx.compose.material.*
|
|
|
+import androidx.compose.material.icons.Icons
|
|
|
+import androidx.compose.material.icons.filled.Delete
|
|
|
+import androidx.compose.material3.MaterialTheme
|
|
|
+import androidx.compose.runtime.Composable
|
|
|
+import androidx.compose.runtime.collectAsState
|
|
|
+import androidx.compose.ui.Alignment
|
|
|
+import androidx.compose.ui.Modifier
|
|
|
+import androidx.compose.ui.graphics.Color
|
|
|
+import androidx.compose.ui.graphics.vector.ImageVector
|
|
|
+import androidx.compose.ui.platform.LocalContext
|
|
|
+import androidx.compose.ui.res.painterResource
|
|
|
+import androidx.compose.ui.res.vectorResource
|
|
|
+import androidx.compose.ui.text.TextStyle
|
|
|
+import androidx.compose.ui.text.font.FontWeight
|
|
|
+import androidx.compose.ui.text.input.KeyboardCapitalization
|
|
|
+import androidx.compose.ui.tooling.preview.Preview
|
|
|
+import androidx.compose.ui.unit.dp
|
|
|
+import androidx.compose.ui.unit.sp
|
|
|
+import androidx.constraintlayout.compose.ConstraintLayout
|
|
|
+import androidx.constraintlayout.compose.Dimension
|
|
|
+import androidx.lifecycle.viewmodel.compose.viewModel
|
|
|
+import com.convenient.android.common.extension.lengthToFitMemorySize
|
|
|
+import com.convenient.android.common.extension.spSave
|
|
|
+import com.convenient.android.common.extension.toFile
|
|
|
+import com.convenient.android.common.utils.date.DateUtils
|
|
|
+import com.convenient.android.common.utils.image.BitmapUtils
|
|
|
+import com.convenient.android.lib.R
|
|
|
+import com.kdanmobile.jetpackcompose.sample.ui.theme.SampleTheme
|
|
|
+
|
|
|
+/**
|
|
|
+ * @classname:
|
|
|
+ * @auther: LiuXiaoLong
|
|
|
+ * @date: 2022/8/4
|
|
|
+ * description:
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+@Composable
|
|
|
+fun MediaFilesPage() {
|
|
|
+ val viewModel: MediaFilesViewModel = viewModel()
|
|
|
+ val context = LocalContext.current
|
|
|
+
|
|
|
+ val chooseDirLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult(), onResult = {
|
|
|
+ it.data?.data?.let {
|
|
|
+ val path = resolveContentUri(context, it)
|
|
|
+ context.spSave("dir", path)
|
|
|
+ viewModel.changeQueryDir(path)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+
|
|
|
+ Column(
|
|
|
+ modifier = Modifier
|
|
|
+ .fillMaxWidth()
|
|
|
+ .padding(horizontal = 16.dp)
|
|
|
+ .verticalScroll(rememberScrollState())
|
|
|
+ ) {
|
|
|
+
|
|
|
+ ConstraintLayout(
|
|
|
+ modifier = Modifier
|
|
|
+ .fillMaxWidth()
|
|
|
+ ) {
|
|
|
+ val (btnFromFiles, btnFromMediaStore, btnChooseDir) = createRefs()
|
|
|
+
|
|
|
+ Button(onClick = { viewModel.changeQueryType(MediaQueryType.FILES) }, modifier = Modifier
|
|
|
+ .padding(end = 8.dp)
|
|
|
+ .constrainAs(btnFromFiles) {
|
|
|
+ top.linkTo(parent.top)
|
|
|
+ start.linkTo(parent.start)
|
|
|
+ end.linkTo(btnFromMediaStore.start)
|
|
|
+ width = Dimension.fillToConstraints
|
|
|
+ }) {
|
|
|
+ Text(text = "从FilesMedia获取")
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ Button(onClick = { viewModel.changeQueryType(MediaQueryType.MEDIA_STORE) },
|
|
|
+ modifier = Modifier
|
|
|
+ .padding(start = 8.dp)
|
|
|
+ .constrainAs(btnFromMediaStore) {
|
|
|
+ top.linkTo(btnFromFiles.top)
|
|
|
+ start.linkTo(btnFromFiles.end)
|
|
|
+ end.linkTo(parent.end)
|
|
|
+ width = Dimension.fillToConstraints
|
|
|
+ }) {
|
|
|
+ Text(text = "从MediaStore获取")
|
|
|
+ }
|
|
|
+ Button(
|
|
|
+ onClick = { chooseDirLauncher.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)) },
|
|
|
+ modifier = Modifier
|
|
|
+ .fillMaxWidth()
|
|
|
+ .constrainAs(btnChooseDir) {
|
|
|
+ top.linkTo(btnFromFiles.bottom)
|
|
|
+ start.linkTo(parent.start)
|
|
|
+ end.linkTo(parent.end)
|
|
|
+ }) {
|
|
|
+ Text(text = "选择文件夹")
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ QueryInfoPage(viewModel = viewModel)
|
|
|
+ Spacer(
|
|
|
+ modifier = Modifier
|
|
|
+ .fillMaxWidth()
|
|
|
+ .height(1.dp)
|
|
|
+ .background(color = Color.LightGray)
|
|
|
+ )
|
|
|
+ ResultInfoPage(viewModel = viewModel)
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@Composable
|
|
|
+fun QueryInfoPage(viewModel: MediaFilesViewModel) {
|
|
|
+ val config = viewModel.config.collectAsState()
|
|
|
+
|
|
|
+ val context = LocalContext.current
|
|
|
+ val addIgnoreFolderLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult(), onResult = {
|
|
|
+ it.data?.data?.let {
|
|
|
+ val folder = resolveContentUri(context, it)
|
|
|
+ viewModel.changeConfig(config = config.value.copy(ignoreChildFiles = config.value.ignoreChildFiles.toMutableList().apply {
|
|
|
+ folder.toFile()?.let { it1 -> add(it1) }
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ val addIgnoreFileLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent(), onResult = {
|
|
|
+ it?.let { uri ->
|
|
|
+ val folder = UriTofilePath.getFilePathByUri(context, uri)
|
|
|
+ viewModel.changeConfig(config = config.value.copy(ignoreChildFiles = config.value.ignoreChildFiles.toMutableList().apply {
|
|
|
+ folder?.toFile()?.let { it1 -> add(it1) }
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ val dir = viewModel.dir.collectAsState()
|
|
|
+ val queryType = viewModel.queryType.collectAsState()
|
|
|
+
|
|
|
+ InfoItem(title = "目录:", info = dir.value)
|
|
|
+ InfoItem(title = "获取方式:", info = queryType.value.name)
|
|
|
+
|
|
|
+ Column() {
|
|
|
+ InfoItem(title = "查询的文件格式:", info = config.value.supportMimeTypes.toString())
|
|
|
+ TextField(
|
|
|
+ keyboardOptions = KeyboardOptions(autoCorrect = false, capitalization = KeyboardCapitalization.None),
|
|
|
+ value = viewModel.getSupportMimeTypesFiledTextValue(config.value),
|
|
|
+ singleLine = true,
|
|
|
+ shape = RoundedCornerShape(4.dp),
|
|
|
+ onValueChange = {
|
|
|
+ val a = it.replace("[", "").replace("]", "").trim().lowercase()
|
|
|
+ val supportMimeTypes = a.split(",").toMutableList()
|
|
|
+ viewModel.changeConfig(config = config.value.copy(supportMimeTypes = supportMimeTypes))
|
|
|
+ }, placeholder = {
|
|
|
+ Text(text = "png,jpg 以,分割")
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ Row {
|
|
|
+ InfoItem(title = "结果是否包含文件夹:", info = config.value.includeFolder.toString())
|
|
|
+ val includeFolder = config.value.includeFolder
|
|
|
+ RadioButton(selected = includeFolder, onClick = {
|
|
|
+ viewModel.changeConfigIncludeFolder(includeFolder.not())
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ Row {
|
|
|
+ InfoItem(title = "遍历子文件夹:", info = config.value.recursively.toString())
|
|
|
+ val recursively = config.value.recursively
|
|
|
+ RadioButton(selected = recursively, onClick = {
|
|
|
+ viewModel.changeConfig(config.value.copy(recursively = recursively.not()))
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ Column {
|
|
|
+ InfoItem(title = "忽略的文件:", info = "")
|
|
|
+ Button(onClick = { addIgnoreFolderLauncher.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)) }) {
|
|
|
+ Text(text = "选择忽略的文件夹")
|
|
|
+ }
|
|
|
+
|
|
|
+ Button(onClick = { addIgnoreFileLauncher.launch("*/*") }) {
|
|
|
+ Text(text = "选择忽略的文件")
|
|
|
+ }
|
|
|
+
|
|
|
+ for (ignoreChildFile in config.value.ignoreChildFiles) {
|
|
|
+ Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
|
|
+ Text(modifier = Modifier.weight(3F), text = ignoreChildFile.absolutePath)
|
|
|
+ IconButton(modifier = Modifier.weight(1F), onClick = {
|
|
|
+ viewModel.removeConfigIgnoreChildFiles(ignoreChildFile)
|
|
|
+ }) {
|
|
|
+ Icon(imageVector = Icons.Default.Delete, contentDescription = null)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+@Composable
|
|
|
+fun InfoItem(title: String, info: String) {
|
|
|
+
|
|
|
+ Column {
|
|
|
+ Text(text = title, style = TextStyle(fontSize = 14.sp, color = Color.Black, fontWeight = FontWeight.Bold))
|
|
|
+ if (info.isNotEmpty() && info.equals("[]").not()) {
|
|
|
+ Text(text = info, style = TextStyle(fontSize = 12.sp))
|
|
|
+ }
|
|
|
+ Spacer(modifier = Modifier.padding(top = 8.dp))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@Composable
|
|
|
+fun ResultInfoPage(viewModel: MediaFilesViewModel) {
|
|
|
+
|
|
|
+ InfoItem(title = "查询结果", info = "")
|
|
|
+
|
|
|
+ val result = viewModel.result.collectAsState()
|
|
|
+ val count = result.value.size
|
|
|
+ val mimeTypes = result.value.distinctBy {
|
|
|
+ it.extension
|
|
|
+ }.map {
|
|
|
+ it.extension
|
|
|
+ }
|
|
|
+
|
|
|
+ InfoItem(title = "数量", info = count.toString())
|
|
|
+ InfoItem(title = "结果文件格式", info = mimeTypes.toString())
|
|
|
+
|
|
|
+ result.value.forEach { item ->
|
|
|
+
|
|
|
+ Row(modifier = Modifier.padding(vertical = 8.dp).fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
|
|
+
|
|
|
+ Image(
|
|
|
+ imageVector = ImageVector.vectorResource(id = if (item.isFile) R.drawable.ic_icons8_file else R.drawable.ic_icons8_folder),
|
|
|
+ modifier = Modifier.padding(top = 8.dp, bottom = 8.dp),
|
|
|
+ contentDescription = null)
|
|
|
+
|
|
|
+ Column {
|
|
|
+ Text(text = item.name, style = TextStyle(color = Color.Black, fontWeight = FontWeight.Bold, fontSize = 14.sp))
|
|
|
+ Row {
|
|
|
+ Text(text = DateUtils.getFormatDate(item.lastModified), fontSize = 12.sp)
|
|
|
+ Text(text = item.mediaPath.toFile()?.lengthToFitMemorySize()?:"", fontSize = 12.sp)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+@Preview(showSystemUi = true)
|
|
|
+@Composable
|
|
|
+fun MediaFilesPagePreview() {
|
|
|
+ SampleTheme {
|
|
|
+ MediaFilesPage()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|