add search and sort
This commit is contained in:
parent
f133a4446b
commit
2190a509ef
21 changed files with 825 additions and 183 deletions
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="17" />
|
<bytecodeTargetLevel target="21" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -4,8 +4,9 @@
|
||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
<option name="gradleJvm" value="#JAVA_HOME" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|
|
@ -69,6 +69,7 @@ dependencies {
|
||||||
implementation(libs.ui.graphics)
|
implementation(libs.ui.graphics)
|
||||||
implementation(libs.ui.tooling.preview)
|
implementation(libs.ui.tooling.preview)
|
||||||
implementation(libs.material3)
|
implementation(libs.material3)
|
||||||
|
implementation(libs.androidx.datastore.preferences)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.ext.junit)
|
androidTestImplementation(libs.ext.junit)
|
||||||
androidTestImplementation(libs.espresso.core)
|
androidTestImplementation(libs.espresso.core)
|
||||||
|
|
|
@ -2,6 +2,15 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||||
|
tools:ignore="ScopedStorage" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
@ -12,6 +21,11 @@
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.MyApplication"
|
android:theme="@style/Theme.MyApplication"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
|
<activity
|
||||||
|
android:name=".compose.SettingActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/title_activity_setting"
|
||||||
|
android:theme="@style/Theme.MyApplication" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".compose.SearchActivity"
|
android:name=".compose.SearchActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
|
@ -30,15 +44,12 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".compose.ViewFileActivity"
|
android:name=".compose.ViewFileActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".main_page"
|
android:name=".main_page"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".store_page"
|
android:name=".store_page"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".document_page"
|
android:name=".document_page"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
@ -63,14 +74,4 @@
|
||||||
</provider>
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
|
||||||
tools:ignore="ScopedStorage" />
|
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.example.myapplication
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
val Context.settingStore: DataStore<Preferences> by preferencesDataStore(name = "app_settings")
|
||||||
|
|
||||||
|
class SettingStorage(private val context: Context) {
|
||||||
|
val showExtension = booleanPreferencesKey("show_extension")
|
||||||
|
|
||||||
|
|
||||||
|
fun <T> get(key: Preferences.Key<T>): T? =
|
||||||
|
runBlocking {
|
||||||
|
context.settingStore.data
|
||||||
|
.map { value ->
|
||||||
|
value[key]
|
||||||
|
}
|
||||||
|
.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> set(key: Preferences.Key<T>, value: T) =
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
context.settingStore.edit {
|
||||||
|
it[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,13 @@ import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import com.example.myapplication.R
|
||||||
|
import com.example.myapplication.compose.ui.SearchFileColumn
|
||||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister
|
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister
|
||||||
import com.example.myapplication.fileSystem.byTypeFileLister.ImageLister
|
import com.example.myapplication.fileSystem.byTypeFileLister.ImageLister
|
||||||
import com.example.myapplication.fileSystem.byTypeFileLister.MusicLister
|
import com.example.myapplication.fileSystem.byTypeFileLister.MusicLister
|
||||||
|
@ -20,10 +27,22 @@ class SearchActivity : ComponentActivity() {
|
||||||
"document" -> DocumentLister.regex
|
"document" -> DocumentLister.regex
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
val searchFileColumn = SearchFileColumn(this,when (type) {
|
||||||
|
"music" -> getString(R.string.music)
|
||||||
|
"image" -> getString(R.string.picture)
|
||||||
|
"video" -> getString(R.string.video)
|
||||||
|
"document" -> getString(R.string.document)
|
||||||
|
else -> ""
|
||||||
|
},searchTypeRegex)
|
||||||
|
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color(getColor(R.color.WhiteSmoke)))
|
||||||
|
)
|
||||||
|
{ searchFileColumn.Draw() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.example.myapplication.compose
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
|
||||||
|
class SettingActivity : ComponentActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
enableEdgeToEdge()
|
||||||
|
setContent {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,9 @@ import com.example.myapplication.fileSystem.WrappedFile.Type
|
||||||
import com.example.myapplication.main_page
|
import com.example.myapplication.main_page
|
||||||
import com.example.myapplication.utils.AlertHelper
|
import com.example.myapplication.utils.AlertHelper
|
||||||
import com.example.myapplication.utils.ClipHelper
|
import com.example.myapplication.utils.ClipHelper
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
|
@ -74,17 +77,26 @@ class FileColumn(val context: Context) {
|
||||||
fun Draw(startFolder: String) {
|
fun Draw(startFolder: String) {
|
||||||
var path by remember { mutableStateOf(startFolder) }
|
var path by remember { mutableStateOf(startFolder) }
|
||||||
var shouldUpdate by remember { mutableStateOf(false) }
|
var shouldUpdate by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// 检查当前在的目录,有问题就不渲染
|
||||||
val cwd = File(path)
|
val cwd = File(path)
|
||||||
if (!cwd.isDirectory) {
|
if (!cwd.isDirectory) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var isOkay by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
LaunchedEffect(path, shouldUpdate) {
|
var isOkay by remember { mutableStateOf(false) }
|
||||||
|
var sortByTime by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
|
LaunchedEffect(path, shouldUpdate, sortByTime) {
|
||||||
isOkay = false
|
isOkay = false
|
||||||
fileList.clear()
|
fileList.clear()
|
||||||
cwd.listFiles()?.forEach { f ->
|
val wfList = cwd.listFiles()?.map { WrappedFile(it) }
|
||||||
fileList.add(WrappedFile(f))
|
if (wfList!=null) {
|
||||||
|
if (sortByTime) {
|
||||||
|
fileList.addAll(wfList.sortedBy { it.lastModifiedTime })
|
||||||
|
} else {
|
||||||
|
fileList.addAll(wfList.sortedBy { it.size })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
isOkay = true
|
isOkay = true
|
||||||
}
|
}
|
||||||
|
@ -120,11 +132,42 @@ class FileColumn(val context: Context) {
|
||||||
Text(
|
Text(
|
||||||
text = path,
|
text = path,
|
||||||
fontSize = 24.sp,
|
fontSize = 24.sp,
|
||||||
modifier = Modifier.padding(start = 16.dp),
|
modifier = Modifier.padding(start = 10.dp)
|
||||||
|
.fillMaxWidth(0.75f),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
sortByTime = !sortByTime
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
Toast.makeText(
|
||||||
|
context, context.getString(
|
||||||
|
if (sortByTime) {
|
||||||
|
R.string.sort_by_time
|
||||||
|
} else {
|
||||||
|
R.string.sort_by_size
|
||||||
|
}
|
||||||
|
), Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
imageVector = ImageVector.vectorResource(
|
||||||
|
if (sortByTime) {
|
||||||
|
R.drawable.baseline_access_time_24
|
||||||
|
} else {
|
||||||
|
R.drawable.baseline_storage_24
|
||||||
|
}
|
||||||
|
), "sortMethod"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!isOkay) {
|
if (!isOkay) {
|
||||||
Column(
|
Column(
|
||||||
|
|
|
@ -0,0 +1,358 @@
|
||||||
|
package com.example.myapplication.compose.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import com.example.myapplication.R
|
||||||
|
import com.example.myapplication.fileSystem.CutHelper
|
||||||
|
import com.example.myapplication.fileSystem.WrappedFile
|
||||||
|
import com.example.myapplication.fileSystem.WrappedFile.Type
|
||||||
|
import com.example.myapplication.fileSystem.searchFile
|
||||||
|
import com.example.myapplication.main_page
|
||||||
|
import com.example.myapplication.utils.AlertHelper
|
||||||
|
import com.example.myapplication.utils.ClipHelper
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class SearchFileColumn(
|
||||||
|
val context: Context,
|
||||||
|
private val searchTypeName: String,
|
||||||
|
private val searchRegex: Regex?
|
||||||
|
) {
|
||||||
|
private val fileList = mutableStateListOf<WrappedFile>()
|
||||||
|
private val searchText = mutableStateOf("")
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Draw() {
|
||||||
|
var list by remember { mutableStateOf<List<String>>(emptyList()) }
|
||||||
|
var isOkay by remember { mutableStateOf(false) }
|
||||||
|
var sortByTime by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
|
LaunchedEffect(isOkay, list,sortByTime) {
|
||||||
|
isOkay = false
|
||||||
|
fileList.clear()
|
||||||
|
val wfList = list.map { WrappedFile(File(it)) }
|
||||||
|
if (sortByTime) {
|
||||||
|
fileList.addAll(wfList.sortedBy { it.lastModifiedTime })
|
||||||
|
} else {
|
||||||
|
fileList.addAll(wfList.sortedBy { it.size })
|
||||||
|
}
|
||||||
|
isOkay = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.statusBarsPadding()
|
||||||
|
.navigationBarsPadding()
|
||||||
|
.background(Color(context.getColor(R.color.WhiteSmoke)))
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 10.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
val intent = Intent(
|
||||||
|
context,
|
||||||
|
main_page::class.java
|
||||||
|
)
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
imageVector = ImageVector.vectorResource(R.drawable.ic_left_arrow), "back"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = context.getString(R.string.search_result, searchTypeName),
|
||||||
|
fontSize = 28.sp,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 10.dp)
|
||||||
|
.padding(vertical = 5.dp),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
sortByTime = !sortByTime
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
Toast.makeText(
|
||||||
|
context, context.getString(
|
||||||
|
if (sortByTime) {
|
||||||
|
R.string.sort_by_time
|
||||||
|
} else {
|
||||||
|
R.string.sort_by_size
|
||||||
|
}
|
||||||
|
), Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
imageVector = ImageVector.vectorResource(
|
||||||
|
if (sortByTime) {
|
||||||
|
R.drawable.baseline_access_time_24
|
||||||
|
} else {
|
||||||
|
R.drawable.baseline_storage_24
|
||||||
|
}
|
||||||
|
), "sortMethod"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!isOkay) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = context.getString(R.string.loading),
|
||||||
|
fontSize = 34.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DrawColumns(fileList,
|
||||||
|
searchText = searchText.value,
|
||||||
|
onSearch = { name, searchWhat ->
|
||||||
|
searchText.value = searchWhat
|
||||||
|
searchFile(name, searchRegex) {
|
||||||
|
list = it
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.search_no_result), Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.search_some_result, list.size), Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
isOkay = !isOkay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
val file = File(it)
|
||||||
|
if (file.isFile) {
|
||||||
|
val uri = FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
context.packageName + ".provider",
|
||||||
|
file
|
||||||
|
)
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
|
intent.setDataAndType(uri, WrappedFile(file).mime)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DrawColumns(
|
||||||
|
fileList: List<WrappedFile>,
|
||||||
|
update: (() -> Unit)? = null,
|
||||||
|
searchText: String = "",
|
||||||
|
onSearch: ((String, String) -> Unit)? = null,
|
||||||
|
onItemClick: ((String) -> Unit)? = null
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.padding(vertical = 5.dp)
|
||||||
|
) {
|
||||||
|
// 最顶上那个
|
||||||
|
item {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 3.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
var searchInput by remember { mutableStateOf(searchText) }
|
||||||
|
|
||||||
|
TextField(
|
||||||
|
value = searchInput,
|
||||||
|
maxLines = 1,
|
||||||
|
onValueChange = { searchInput = it },
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 15.dp, vertical = 10.dp)
|
||||||
|
.fillParentMaxWidth(0.9f),
|
||||||
|
colors = TextFieldDefaults.colors(
|
||||||
|
focusedContainerColor = Color(0xFFFFFAFA),
|
||||||
|
unfocusedContainerColor = Color.White,
|
||||||
|
focusedIndicatorColor = Color(0xFF03A9F4),
|
||||||
|
unfocusedIndicatorColor = Color.Transparent
|
||||||
|
),
|
||||||
|
textStyle = TextStyle(fontSize = 18.sp),
|
||||||
|
trailingIcon = {
|
||||||
|
Image(
|
||||||
|
ImageVector.vectorResource(R.drawable.ic_search),
|
||||||
|
context.getString(R.string.search),
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable {
|
||||||
|
if (searchInput.isNotEmpty()) {
|
||||||
|
if (searchInput == "." || searchInput == "..") {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.error_search_input_illegal),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
onSearch?.invoke(searchInput, searchInput)
|
||||||
|
} else {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.error_need_search_input), Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下面的内容
|
||||||
|
items(fileList) { file ->
|
||||||
|
FileSingleView(
|
||||||
|
file,
|
||||||
|
update = update,
|
||||||
|
onItemClick = onItemClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun FileSingleView(
|
||||||
|
file: WrappedFile,
|
||||||
|
update: (() -> Unit)? = null,
|
||||||
|
onItemClick: ((String) -> Unit)? = null
|
||||||
|
) {
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.border(
|
||||||
|
width = Dp.Hairline,
|
||||||
|
color = Color.Gray,
|
||||||
|
shape = RectangleShape
|
||||||
|
)
|
||||||
|
.padding(vertical = 3.dp)
|
||||||
|
.clickable {
|
||||||
|
onItemClick?.invoke(file.path)
|
||||||
|
},
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
ImageVector.vectorResource(
|
||||||
|
when (file.mime.split('/').first()) {
|
||||||
|
"dir" -> R.drawable.type_directory
|
||||||
|
"image" -> R.drawable.type_image
|
||||||
|
"video" -> R.drawable.type_video
|
||||||
|
"audio" -> R.drawable.type_audio
|
||||||
|
else -> R.drawable.type_file
|
||||||
|
}
|
||||||
|
), file.mime,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 8.dp)
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 15.dp)
|
||||||
|
.fillMaxWidth(0.8f)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = file.name,
|
||||||
|
fontSize = 24.sp,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = file.getModifiedTimeString(context),
|
||||||
|
fontSize = 15.sp,
|
||||||
|
color = Color.Gray,
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(Modifier.weight(1f))
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
if (file.type == Type.FILE) { // 普通文件
|
||||||
|
AlertHelper.showOnlyInfoNewAlert(context,
|
||||||
|
onInfo = {
|
||||||
|
AlertHelper.showFileInfoAlert(context, file.path)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.padding(horizontal = 10.dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
ImageVector.vectorResource(R.drawable.outline_info_24), "info"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.example.myapplication.compose.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
class Setting(
|
||||||
|
private val rowModifier: Modifier = Modifier.fillMaxWidth(),
|
||||||
|
private val nameModifier: Modifier = Modifier,
|
||||||
|
private val nameTextStyle: TextStyle = TextStyle.Default,
|
||||||
|
private val descriptionModifier: Modifier = Modifier,
|
||||||
|
private val descriptionTextStyle: TextStyle = TextStyle.Default,
|
||||||
|
private val switchModifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BooleanSetting(
|
||||||
|
name: String,
|
||||||
|
initialState: Boolean = true,
|
||||||
|
description: String? = null,
|
||||||
|
onCheckedChange: ((Boolean) -> Unit)? = null
|
||||||
|
) {
|
||||||
|
var checkState by remember { mutableStateOf(initialState) }
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = rowModifier
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(0.8f)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = name,
|
||||||
|
modifier = nameModifier,
|
||||||
|
style = nameTextStyle
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.size(5.dp))
|
||||||
|
Text(
|
||||||
|
text = description ?: "",
|
||||||
|
modifier = descriptionModifier,
|
||||||
|
style = descriptionTextStyle
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
Switch(
|
||||||
|
checked = checkState,
|
||||||
|
onCheckedChange = {
|
||||||
|
checkState = it
|
||||||
|
onCheckedChange?.invoke(it)
|
||||||
|
},
|
||||||
|
modifier = switchModifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -222,7 +222,7 @@ class document_page : AppCompatActivity() {
|
||||||
loadingTextView.visibility = View.GONE
|
loadingTextView.visibility = View.GONE
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
this@document_page,
|
this@document_page,
|
||||||
"已选择按时间排序",
|
getString(R.string.sort_by_time),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,7 @@ class document_page : AppCompatActivity() {
|
||||||
loadingTextView.visibility = View.GONE
|
loadingTextView.visibility = View.GONE
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
this@document_page,
|
this@document_page,
|
||||||
"已选择按大小排序",
|
getString(R.string.sort_by_size),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.example.myapplication.fileSystem
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Environment
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun searchFile(name: String,typeRegex: Regex? = null, onFinished: (List<String>) -> Unit){
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val resultList = searchDir(name,Environment.getExternalStorageDirectory().path)
|
||||||
|
if (typeRegex == null){
|
||||||
|
onFinished.invoke(resultList)
|
||||||
|
}else{
|
||||||
|
val finalResult = resultList.filter { it.contains(typeRegex) }
|
||||||
|
onFinished.invoke(finalResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun searchDir(name: String, directory: String): List<String> {
|
||||||
|
val list = mutableListOf<String>()
|
||||||
|
val dir = File(directory)
|
||||||
|
if (!dir.isDirectory || dir.name.startsWith('.')){
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val inside = dir.listFiles()
|
||||||
|
if (inside == null || inside.isEmpty()){
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
inside.forEach {
|
||||||
|
if (it.isDirectory){
|
||||||
|
list.addAll(coroutineScope{ searchDir(name, it.path) })
|
||||||
|
}else if (it.isFile){
|
||||||
|
if (it.name.contains(name) && !it.name.startsWith('.') ){
|
||||||
|
list.add(it.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
|
@ -10,6 +10,8 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.graphics.Insets;
|
import androidx.core.graphics.Insets;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.core.view.WindowInsetsCompat;
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import com.example.myapplication.compose.SearchActivity;
|
||||||
import com.example.myapplication.compose.ViewFileActivity;
|
import com.example.myapplication.compose.ViewFileActivity;
|
||||||
import com.example.myapplication.fileSystem.DeleteHelper;
|
import com.example.myapplication.fileSystem.DeleteHelper;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -41,6 +43,7 @@ public class main_page extends AppCompatActivity {
|
||||||
findViewById(R.id.MainPageRecordingButton).setOnClickListener(buttonClickerHandler);
|
findViewById(R.id.MainPageRecordingButton).setOnClickListener(buttonClickerHandler);
|
||||||
findViewById(R.id.MainPageDCIMButton).setOnClickListener(buttonClickerHandler);
|
findViewById(R.id.MainPageDCIMButton).setOnClickListener(buttonClickerHandler);
|
||||||
findViewById(R.id.MainPagePicturesButton).setOnClickListener(buttonClickerHandler);
|
findViewById(R.id.MainPagePicturesButton).setOnClickListener(buttonClickerHandler);
|
||||||
|
findViewById(R.id.MainPageSearchButton).setOnClickListener(buttonClickerHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void onDestroy() {
|
@Override protected void onDestroy() {
|
||||||
|
@ -124,6 +127,10 @@ public class main_page extends AppCompatActivity {
|
||||||
intent.putExtras(bundle);
|
intent.putExtras(bundle);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
if (view.getId() == R.id.MainPageSearchButton) {
|
||||||
|
Intent intent = new Intent(context, SearchActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -125,6 +125,26 @@ class AlertHelper {
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showOnlyInfoNewAlert(
|
||||||
|
context: Context,
|
||||||
|
onInfo: () -> Unit,
|
||||||
|
) {
|
||||||
|
val builder = Builder(context)
|
||||||
|
builder.setTitle(context.getString(R.string.select_action))
|
||||||
|
.setItems(
|
||||||
|
arrayOf<CharSequence>(
|
||||||
|
context.getString(R.string.action_info)
|
||||||
|
)
|
||||||
|
) { _: DialogInterface?, which: Int ->
|
||||||
|
when (which) {
|
||||||
|
1 -> onInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setNegativeButton(context.getString(R.string.action_cancel))
|
||||||
|
{ dialog: DialogInterface, which: Int -> dialog.dismiss() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
fun showDeleteAlert(context: Context, file: String, onConfirm: (() -> Unit)?) {
|
fun showDeleteAlert(context: Context, file: String, onConfirm: (() -> Unit)?) {
|
||||||
val builder = Builder(context)
|
val builder = Builder(context)
|
||||||
builder.setTitle(context.getString(R.string.confirm_to_delete))
|
builder.setTitle(context.getString(R.string.confirm_to_delete))
|
||||||
|
|
7
app/src/main/res/drawable/baseline_access_time_24.xml
Normal file
7
app/src/main/res/drawable/baseline_access_time_24.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#1A1B3F" android:viewportHeight="24" android:viewportWidth="24" android:width="40dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
|
||||||
|
|
||||||
|
</vector>
|
|
@ -1,7 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#000000" android:viewportHeight="23" android:viewportWidth="23" android:width="40dp">
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:tint="#000000"
|
||||||
|
android:viewportWidth="23"
|
||||||
|
android:viewportHeight="23">
|
||||||
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:fillColor="@android:color/white"
|
||||||
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
|
||||||
|
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
android:scrollbars="none"
|
android:scrollbars="none"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/searchDocument"
|
app:layout_constraintTop_toBottomOf="@+id/MainPageSearchButton"
|
||||||
>
|
>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -329,19 +329,21 @@
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|
||||||
<SearchView
|
<Button
|
||||||
android:id="@+id/searchDocument"
|
android:id="@+id/MainPageSearchButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:backgroundTint="@color/white"
|
||||||
android:layout_marginTop="15dp"
|
android:drawableLeft="@drawable/ic_search"
|
||||||
android:background="@drawable/search_border"
|
android:gravity="center"
|
||||||
android:iconifiedByDefault="false"
|
android:layout_marginHorizontal="60dp"
|
||||||
android:queryHint="搜索文件"
|
android:text="@string/search_file"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
|
android:textSize="20sp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/MainPageTitleRow"
|
app:layout_constraintTop_toBottomOf="@+id/MainPageTitleRow"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -128,4 +128,13 @@
|
||||||
<string name="error_already_exist">已存在此文件(夹)</string>
|
<string name="error_already_exist">已存在此文件(夹)</string>
|
||||||
<string name="title_activity_view_file">ViewFileActivity</string>
|
<string name="title_activity_view_file">ViewFileActivity</string>
|
||||||
<string name="title_activity_search">SearchActivity</string>
|
<string name="title_activity_search">SearchActivity</string>
|
||||||
|
<string name="search_result">搜索%s结果</string>
|
||||||
|
<string name="error_need_search_input">请输入要搜索的内容</string>
|
||||||
|
<string name="error_search_input_illegal">输入的搜索内容不合法</string>
|
||||||
|
<string name="search_file">搜索文件</string>
|
||||||
|
<string name="search_no_result">未找到任何结果</string>
|
||||||
|
<string name="search_some_result">找到%d个结果</string>
|
||||||
|
<string name="sort_by_size">已选择按大小排序</string>
|
||||||
|
<string name="sort_by_time">已选择按时间排序</string>
|
||||||
|
<string name="title_activity_setting">SettingActivity</string>
|
||||||
</resources>
|
</resources>
|
|
@ -15,6 +15,7 @@ lifecycleRuntimeKtx = "2.8.6"
|
||||||
activityCompose = "1.9.2"
|
activityCompose = "1.9.2"
|
||||||
composeBom = "2024.09.03"
|
composeBom = "2024.09.03"
|
||||||
commonsIO = "2.17.0"
|
commonsIO = "2.17.0"
|
||||||
|
datastorePreferences = "1.1.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
|
@ -38,6 +39,7 @@ ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
material3 = { group = "androidx.compose.material3", name = "material3" }
|
material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
commons-io = { group = "commons-io", name = "commons-io", version.ref = "commonsIO" }
|
commons-io = { group = "commons-io", name = "commons-io", version.ref = "commonsIO" }
|
||||||
|
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|
Loading…
Reference in a new issue