diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 91fe322..c40e5f7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,7 +14,7 @@ android { applicationId = "com.d4rk.cleaner" minSdk = 26 targetSdk = 34 - versionCode = 77 + versionCode = 78 versionName = "2.0.0" archivesName = "${applicationId}-v${versionName}" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/release/baselineProfiles/0/com.d4rk.cleaner-v1.0.0-release.dm b/app/release/baselineProfiles/0/com.d4rk.cleaner-v1.0.0-release.dm deleted file mode 100644 index 26dd784..0000000 Binary files a/app/release/baselineProfiles/0/com.d4rk.cleaner-v1.0.0-release.dm and /dev/null differ diff --git a/app/release/baselineProfiles/1/com.d4rk.cleaner-v1.0.0-release.dm b/app/release/baselineProfiles/1/com.d4rk.cleaner-v1.0.0-release.dm deleted file mode 100644 index 15ec63e..0000000 Binary files a/app/release/baselineProfiles/1/com.d4rk.cleaner-v1.0.0-release.dm and /dev/null differ diff --git a/app/release/com.d4rk.cleaner-v1.0.0-release.apk b/app/release/com.d4rk.cleaner-v1.0.0-release.apk deleted file mode 100644 index cfa854e..0000000 Binary files a/app/release/com.d4rk.cleaner-v1.0.0-release.apk and /dev/null differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 64dc47b..10449fd 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -11,9 +11,9 @@ "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 68, - "versionName": "1.0.0", - "outputFile": "com.d4rk.cleaner-v1.0.0-release.apk" + "versionCode": 78, + "versionName": "2.0.0", + "outputFile": "com.d4rk.cleaner-v2.0.0-release.apk" } ], "elementType": "File", @@ -22,14 +22,14 @@ "minApi": 28, "maxApi": 30, "baselineProfiles": [ - "baselineProfiles/1/com.d4rk.cleaner-v1.0.0-release.dm" + "baselineProfiles/1/com.d4rk.cleaner-v2.0.0-release.dm" ] }, { "minApi": 31, "maxApi": 2147483647, "baselineProfiles": [ - "baselineProfiles/0/com.d4rk.cleaner-v1.0.0-release.dm" + "baselineProfiles/0/com.d4rk.cleaner-v2.0.0-release.dm" ] } ], diff --git a/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeComposable.kt b/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeComposable.kt index 9f88e8a..f2f77db 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeComposable.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeComposable.kt @@ -43,9 +43,11 @@ import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -78,6 +80,9 @@ fun HomeComposable() { val showCleaningComposable by viewModel.showCleaningComposable.observeAsState(false) val isAnalyzing by viewModel.isAnalyzing.observeAsState(false) val selectedFileCount by viewModel.selectedFileCount.collectAsState() + + val launchScanningKey = remember { mutableStateOf(false) } + Column( modifier = Modifier.fillMaxSize() ) { @@ -105,7 +110,7 @@ fun HomeComposable() { ) } else { - AnalyzeComposable() + AnalyzeComposable(launchScanningKey) } } Row( @@ -205,12 +210,18 @@ fun HomeComposable() { * @param viewModel The HomeViewModel instance used to interact with the data and business logic. */ @Composable -fun AnalyzeComposable() { +fun AnalyzeComposable(launchScanningKey: MutableState) { val viewModel : HomeViewModel = viewModel() val files by viewModel.scannedFiles.asFlow().collectAsState(initial = listOf()) + val allFilesSelected by viewModel.allFilesSelected val selectedFileCount by viewModel.selectedFileCount.collectAsState() + LaunchedEffect(key1 = launchScanningKey.value) { + viewModel.fileScanner.startScanning() + launchScanningKey.value = false + } + LaunchedEffect(Unit) { viewModel.fileScanner.startScanning() } @@ -234,7 +245,7 @@ fun AnalyzeComposable() { modifier = Modifier.padding(8.dp) ) { items(files) { file -> - FileCard(file = file , viewModel = viewModel) + FileCard(file = file , viewModel = viewModel) // FIXME: Type mismatch: inferred type is Int but File was expected } } } @@ -274,7 +285,7 @@ fun FileCard(file: File, viewModel: HomeViewModel) { val context = LocalContext.current val fileExtension = getFileExtension(file.name) val thumbnail = remember { - getVideoThumbnail(file.absolutePath, thumbnailWidth = 128, thumbnailHeight = 128) + getVideoThumbnail(file.absolutePath, thumbnailWidth = 64, thumbnailHeight = 64) } Card( modifier = Modifier diff --git a/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeViewModel.kt b/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeViewModel.kt index edb49a9..5e3a3d1 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeViewModel.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeViewModel.kt @@ -23,6 +23,7 @@ import androidx.lifecycle.viewModelScope import com.d4rk.cleaner.data.datastore.DataStore import com.d4rk.cleaner.utils.FileScanner import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -104,7 +105,9 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { } isAnalyzing.value = true showCleaningComposable.value = true - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch { + delay(100) + isAnalyzing.value = true fileScanner.startScanning() scannedFiles.postValue(fileScanner.getFilteredFiles()) isAnalyzing.postValue(false) diff --git a/app/src/main/kotlin/com/d4rk/cleaner/utils/FileScanner.kt b/app/src/main/kotlin/com/d4rk/cleaner/utils/FileScanner.kt index 34a8d22..fc5dc95 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/utils/FileScanner.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/utils/FileScanner.kt @@ -4,7 +4,12 @@ import android.content.res.Resources import android.os.Environment import com.d4rk.cleaner.R import com.d4rk.cleaner.data.datastore.DataStore +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.withContext import java.io.File /** @@ -28,9 +33,12 @@ class FileScanner(private val dataStore: DataStore, private val resources: Resou * @throws Exception If an error occurs during the scanning process. */ suspend fun startScanning() { - loadPreferences() - val allFiles = getAllFiles() - filteredFiles = filterFiles(allFiles).toList() + withContext(Dispatchers.IO) { + loadPreferences() + val allFiles = getAllFiles() + filteredFiles = filterFiles(allFiles) + .toList() // Collect the flow into a list asynchronously + } } /** @@ -72,62 +80,57 @@ class FileScanner(private val dataStore: DataStore, private val resources: Resou return files } - /** - * Filters files based on user-defined preferences and returns them as a sequence. - * - * This function takes a list of all files as input and returns a sequence of files that match the user-defined preferences. The sequence allows for real-time display of filtered files as they are discovered during the scanning process. - * - * @param allFiles The list of all files to filter. - * @return A sequence of files filtered based on user-defined preferences. - */ - private fun filterFiles(allFiles: List): Sequence { - return sequence { + private fun filterFiles(allFiles: List): Flow { + return flow { for (file in allFiles) { - if (preferences.any { (key, value) -> - val result = when (key) { - "generic_extensions" -> { - val extensions = - resources.getStringArray(R.array.generic_extensions) - value && extensions.map { it.removePrefix(".") } - .contains(file.extension) - } - - "archive_extensions" -> { - val extensions = - resources.getStringArray(R.array.archive_extensions) - value && extensions.contains(file.extension) - } - - "apk_extensions" -> { - val extensions = resources.getStringArray(R.array.apk_extensions) - value && extensions.contains(file.extension) - } - - "audio_extensions" -> { - val extensions = resources.getStringArray(R.array.audio_extensions) - value && extensions.contains(file.extension) - } - - "video_extensions" -> { - val extensions = resources.getStringArray(R.array.video_extensions) - value && extensions.contains(file.extension) - } - - "image_extensions" -> { - val extensions = resources.getStringArray(R.array.image_extensions) - value && extensions.contains(file.extension) - } - - else -> false - } - result - }) { - yield(file) + if (shouldFilterFile(file)) { + emit(file) } } } } + private fun shouldFilterFile(file: File): Boolean { + return preferences.any { (key, value) -> + when (key) { + "generic_extensions" -> { + val extensions = + resources.getStringArray(R.array.generic_extensions) + value && extensions.map { it.removePrefix(".") } + .contains(file.extension) + } + + "archive_extensions" -> { + val extensions = + resources.getStringArray(R.array.archive_extensions) + value && extensions.contains(file.extension) + } + + "apk_extensions" -> { + val extensions = resources.getStringArray(R.array.apk_extensions) + value && extensions.contains(file.extension) + } + + "audio_extensions" -> { + val extensions = resources.getStringArray(R.array.audio_extensions) + value && extensions.contains(file.extension) + } + + "video_extensions" -> { + val extensions = resources.getStringArray(R.array.video_extensions) + value && extensions.contains(file.extension) + } + + "image_extensions" -> { + val extensions = resources.getStringArray(R.array.image_extensions) + value && extensions.contains(file.extension) + } + + else -> false + } + } + } + /** * Returns the list of filtered files. *