|
@@ -4,7 +4,6 @@ import android.Manifest
|
|
|
import android.app.Activity
|
|
|
import android.content.Intent
|
|
|
import android.content.pm.PackageManager
|
|
|
-import android.net.Uri
|
|
|
import android.os.Build
|
|
|
import android.os.Environment
|
|
|
import android.provider.Settings
|
|
@@ -13,14 +12,20 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|
|
import androidx.compose.foundation.Image
|
|
|
import androidx.compose.foundation.background
|
|
|
import androidx.compose.foundation.layout.*
|
|
|
-import androidx.compose.foundation.rememberScrollState
|
|
|
+import androidx.compose.foundation.lazy.LazyColumn
|
|
|
+import androidx.compose.foundation.lazy.items
|
|
|
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.Icon
|
|
|
+import androidx.compose.material.RadioButton
|
|
|
+import androidx.compose.material.Text
|
|
|
+import androidx.compose.material.TextField
|
|
|
import androidx.compose.material.icons.Icons
|
|
|
import androidx.compose.material.icons.filled.Delete
|
|
|
-import androidx.compose.material3.ElevatedButton
|
|
|
+import androidx.compose.material3.*
|
|
|
+import androidx.compose.material3.ButtonDefaults
|
|
|
+import androidx.compose.material3.MaterialTheme
|
|
|
import androidx.compose.runtime.*
|
|
|
import androidx.compose.ui.Alignment
|
|
|
import androidx.compose.ui.Modifier
|
|
@@ -41,7 +46,8 @@ 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.ToastUtil
|
|
|
+import com.convenient.android.common.media.config.MediaSortOrder
|
|
|
+import com.convenient.android.common.media.config.MediaSortType
|
|
|
import com.convenient.android.common.utils.date.DateUtils
|
|
|
import com.convenient.android.lib.R
|
|
|
import com.convenient.android.lib.ui.theme.SampleTheme
|
|
@@ -54,16 +60,15 @@ import com.convenient.android.lib.ui.theme.SampleTheme
|
|
|
*/
|
|
|
|
|
|
@Composable
|
|
|
-fun MediaPage(){
|
|
|
+fun MediaPage() {
|
|
|
val context = LocalContext.current
|
|
|
-
|
|
|
var rememberPermission by remember {
|
|
|
mutableStateOf(ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
|
|
|
}
|
|
|
- if (rememberPermission){
|
|
|
+ if (rememberPermission) {
|
|
|
MediaFilesPage()
|
|
|
- }else{
|
|
|
- PermissionPage{
|
|
|
+ } else {
|
|
|
+ PermissionPage {
|
|
|
rememberPermission = true
|
|
|
}
|
|
|
}
|
|
@@ -72,132 +77,51 @@ fun MediaPage(){
|
|
|
|
|
|
@Composable
|
|
|
fun MediaFilesPage() {
|
|
|
-
|
|
|
val viewModel: MediaFilesViewModel = viewModel()
|
|
|
+ val result = viewModel.result.collectAsState()
|
|
|
|
|
|
- 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()
|
|
|
-
|
|
|
- ElevatedButton(
|
|
|
- onClick = { chooseDirLauncher.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)) },
|
|
|
- modifier = Modifier
|
|
|
- .fillMaxWidth()
|
|
|
- .constrainAs(btnChooseDir) {
|
|
|
- top.linkTo(parent.top)
|
|
|
- start.linkTo(parent.start)
|
|
|
- end.linkTo(parent.end)
|
|
|
- }) {
|
|
|
- Text(text = "选择文件夹")
|
|
|
- }
|
|
|
-
|
|
|
- ElevatedButton(onClick = { viewModel.changeQueryType(MediaQueryType.FILES) }, modifier = Modifier
|
|
|
- .padding(end = 8.dp)
|
|
|
- .constrainAs(btnFromFiles) {
|
|
|
- top.linkTo(btnChooseDir.bottom)
|
|
|
- start.linkTo(parent.start)
|
|
|
- end.linkTo(btnFromMediaStore.start)
|
|
|
- width = Dimension.fillToConstraints
|
|
|
- }) {
|
|
|
- Text(text = "从FilesMedia获取")
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- ElevatedButton(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获取")
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- QueryInfoPage(viewModel = viewModel)
|
|
|
- Spacer(
|
|
|
- modifier = Modifier
|
|
|
- .fillMaxWidth()
|
|
|
- .height(1.dp)
|
|
|
- .background(color = Color.LightGray)
|
|
|
- )
|
|
|
- ResultInfoPage(viewModel = viewModel)
|
|
|
-
|
|
|
- }
|
|
|
-}
|
|
|
+ LazyColumn {
|
|
|
|
|
|
-@Composable
|
|
|
-private fun PermissionPage(permissionCallback : ()-> Unit) {
|
|
|
-
|
|
|
- val context = LocalContext.current
|
|
|
- val androidRStoragePermissionLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult(), onResult = {
|
|
|
- if (it.resultCode == Activity.RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
|
- if (Environment.isExternalStorageManager()) {
|
|
|
- ToastUtil.showToast(context, "访问所有文件权限获取成功")
|
|
|
- permissionCallback.invoke()
|
|
|
- }
|
|
|
+ item {
|
|
|
+ QueryInfoPage(viewModel = viewModel)
|
|
|
}
|
|
|
- })
|
|
|
-
|
|
|
- val permissionLaunch = rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission(), onResult = {
|
|
|
- if (it) {
|
|
|
- ToastUtil.showToast(context, "存储权限获取成功")
|
|
|
- permissionCallback.invoke()
|
|
|
|
|
|
+ item {
|
|
|
+ ResultInfoPage(viewModel = viewModel)
|
|
|
}
|
|
|
- })
|
|
|
|
|
|
- Column(modifier = Modifier
|
|
|
- .fillMaxWidth()
|
|
|
- .fillMaxHeight(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) {
|
|
|
-
|
|
|
- ElevatedButton(onClick = {
|
|
|
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
|
- if (Environment.isExternalStorageManager().not()) {
|
|
|
- androidRStoragePermissionLauncher.launch(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION, Uri.parse("package:${context.packageName}")))
|
|
|
- } else {
|
|
|
- if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
|
|
|
- ToastUtil.showToast(context, "已获取存储权限")
|
|
|
- }else{
|
|
|
- permissionLaunch.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
|
+ items(result.value){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)
|
|
|
+ Spacer(modifier = Modifier.padding(horizontal = 8.dp))
|
|
|
+ Text(text = item.mediaPath.toFile()?.lengthToFitMemorySize() ?: "", fontSize = 12.sp)
|
|
|
}
|
|
|
}
|
|
|
- } else {
|
|
|
- permissionLaunch.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
|
}
|
|
|
- }) {
|
|
|
- Text(text = "获取存储权限")
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
fun QueryInfoPage(viewModel: MediaFilesViewModel) {
|
|
|
- val config = viewModel.config.collectAsState()
|
|
|
val context = LocalContext.current
|
|
|
+ val config = viewModel.config.collectAsState()
|
|
|
+ val dir = viewModel.dir.collectAsState()
|
|
|
+ val queryType = viewModel.queryType.collectAsState()
|
|
|
|
|
|
val addIgnoreFolderLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult(), onResult = {
|
|
|
it.data?.data?.let {
|
|
@@ -214,13 +138,62 @@ fun QueryInfoPage(viewModel: MediaFilesViewModel) {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- val dir = viewModel.dir.collectAsState()
|
|
|
- val queryType = viewModel.queryType.collectAsState()
|
|
|
+ val chooseDirLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult(), onResult = {
|
|
|
+ it.data?.data?.let {
|
|
|
+ val path = resolveContentUri(context, it)
|
|
|
+ context.spSave("dir", path)
|
|
|
+ viewModel.changeQueryDir(path)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ ConstraintLayout(
|
|
|
+ modifier = Modifier
|
|
|
+ .fillMaxWidth()
|
|
|
+ ) {
|
|
|
+ val (btnFromFiles, btnFromMediaStore, btnChooseDir) = createRefs()
|
|
|
+
|
|
|
+ ElevatedButton(
|
|
|
+ onClick = { chooseDirLauncher.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)) },
|
|
|
+ modifier = Modifier
|
|
|
+ .fillMaxWidth()
|
|
|
+ .constrainAs(btnChooseDir) {
|
|
|
+ top.linkTo(parent.top)
|
|
|
+ start.linkTo(parent.start)
|
|
|
+ end.linkTo(parent.end)
|
|
|
+ }) {
|
|
|
+ Text(text = "选择文件夹")
|
|
|
+ }
|
|
|
+
|
|
|
+ ElevatedButton(onClick = { viewModel.changeQueryType(MediaQueryType.FILES) }, modifier = Modifier
|
|
|
+ .padding(end = 8.dp)
|
|
|
+ .constrainAs(btnFromFiles) {
|
|
|
+ top.linkTo(btnChooseDir.bottom)
|
|
|
+ start.linkTo(parent.start)
|
|
|
+ end.linkTo(btnFromMediaStore.start)
|
|
|
+ width = Dimension.fillToConstraints
|
|
|
+ }) {
|
|
|
+ Text(text = "从FilesMedia获取")
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ ElevatedButton(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获取")
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- InfoItem(title = "目录:", info = dir.value, infoPosition = InfoPosition.END)
|
|
|
- InfoItem(title = "获取方式:", info = queryType.value.name, infoPosition = InfoPosition.END)
|
|
|
|
|
|
Column() {
|
|
|
+ InfoItem(title = "目录:", info = dir.value, infoPosition = InfoPosition.END)
|
|
|
+ InfoItem(title = "获取方式:", info = queryType.value.name, infoPosition = InfoPosition.END)
|
|
|
+
|
|
|
InfoItem(title = "查询的文件格式:", info = config.value.supportMimeTypes.toString(), infoPosition = InfoPosition.END)
|
|
|
TextField(
|
|
|
keyboardOptions = KeyboardOptions(autoCorrect = false, capitalization = KeyboardCapitalization.None),
|
|
@@ -234,27 +207,25 @@ fun QueryInfoPage(viewModel: MediaFilesViewModel) {
|
|
|
}, placeholder = {
|
|
|
Text(text = "png,jpg 以,分割")
|
|
|
})
|
|
|
- }
|
|
|
-
|
|
|
- Row {
|
|
|
- val includeFolder = config.value.includeFolder
|
|
|
- RadioButton(selected = includeFolder, onClick = {
|
|
|
- viewModel.changeConfigIncludeFolder(includeFolder.not())
|
|
|
- })
|
|
|
- InfoItem(title = "结果是否包含文件夹:", info = config.value.includeFolder.toString())
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- Row {
|
|
|
- val recursively = config.value.recursively
|
|
|
- RadioButton(selected = recursively, onClick = {
|
|
|
- viewModel.changeConfig(config.value.copy(recursively = recursively.not()))
|
|
|
- })
|
|
|
- InfoItem(title = "遍历子文件夹:", info = config.value.recursively.toString())
|
|
|
-
|
|
|
- }
|
|
|
+ Row {
|
|
|
+ val includeFolder = config.value.includeFolder
|
|
|
+ RadioButton(selected = includeFolder, onClick = {
|
|
|
+ viewModel.changeConfigIncludeFolder(includeFolder.not())
|
|
|
+ })
|
|
|
+ InfoItem(title = "结果是否包含文件夹:", info = config.value.includeFolder.toString())
|
|
|
+ }
|
|
|
+ Row {
|
|
|
+ val recursively = config.value.recursively
|
|
|
+ RadioButton(selected = recursively, onClick = {
|
|
|
+ viewModel.changeConfig(config.value.copy(recursively = recursively.not()))
|
|
|
+ })
|
|
|
+ InfoItem(title = "遍历子文件夹:", info = config.value.recursively.toString())
|
|
|
+ }
|
|
|
+ InfoItem(title = "排序类型:", info = "")
|
|
|
+ SortTypeButton(viewModel = viewModel)
|
|
|
+ InfoItem(title = "排序方式:", info = "")
|
|
|
+ OrderTypeButton(viewModel = viewModel)
|
|
|
|
|
|
- Column {
|
|
|
InfoItem(title = "忽略的文件:", info = "")
|
|
|
Row {
|
|
|
ElevatedButton(onClick = { addIgnoreFolderLauncher.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)) }) {
|
|
@@ -275,6 +246,55 @@ fun QueryInfoPage(viewModel: MediaFilesViewModel) {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ Spacer(
|
|
|
+ modifier = Modifier
|
|
|
+ .fillMaxWidth()
|
|
|
+ .height(1.dp)
|
|
|
+ .background(color = Color.LightGray)
|
|
|
+ )
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@Composable
|
|
|
+fun SortTypeButton(viewModel: MediaFilesViewModel) {
|
|
|
+ val sortType = viewModel.config.collectAsState()
|
|
|
+
|
|
|
+ val types = listOf(MediaSortType.DATE, MediaSortType.SIZE, MediaSortType.NAME)
|
|
|
+
|
|
|
+ Row {
|
|
|
+ types.forEach {
|
|
|
+ FilledTonalButton(
|
|
|
+ colors = ButtonDefaults.filledTonalButtonColors(
|
|
|
+ containerColor = if (it == sortType.value.sortType) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.primary.copy(0.1F)
|
|
|
+ )
|
|
|
+ ,onClick = {
|
|
|
+ viewModel.changeSortType(it)
|
|
|
+ }) {
|
|
|
+ Text(text = it.name, color = Color.White)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@Composable
|
|
|
+fun OrderTypeButton(viewModel: MediaFilesViewModel) {
|
|
|
+ val orderType = viewModel.config.collectAsState()
|
|
|
+
|
|
|
+ val types = listOf(MediaSortOrder.ASC, MediaSortOrder.DESC)
|
|
|
+
|
|
|
+ Row {
|
|
|
+ types.forEach {
|
|
|
+ FilledTonalButton(
|
|
|
+ colors = ButtonDefaults.filledTonalButtonColors(
|
|
|
+ containerColor = if (it == orderType.value.order) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.primary.copy(0.1F)
|
|
|
+ )
|
|
|
+ ,onClick = {
|
|
|
+ viewModel.changeOrder(it)
|
|
|
+ }) {
|
|
|
+ Text(text = it.name, color = Color.White)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -329,32 +349,6 @@ fun ResultInfoPage(viewModel: MediaFilesViewModel) {
|
|
|
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)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
}
|
|
|
|
|
|
|