Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Appcleaner: Faster deletion and better progress information #1486

Merged
merged 3 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package eu.darken.sdmse.common.files

import eu.darken.sdmse.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.sdmse.common.debug.logging.log
import kotlinx.coroutines.flow.Flow
import okio.FileHandle

Expand Down Expand Up @@ -91,8 +93,14 @@ fun APathLookup<*>.removePrefix(prefix: APath, overlap: Int = 0) =
lookedUp.removePrefix(prefix, overlap)

fun Collection<APathLookup<*>>.filterDistinctRoots(): Set<APathLookup<*>> {
log(VERBOSE) { "Creating lookup map..." }
val lookupMap = this.associateBy { it.lookedUp }
return lookupMap.keys.filterDistinctRoots().map { lookupMap.getValue(it) }.toSet()
log(VERBOSE) { "Lookup map created with ${lookupMap.size} entries, now filtering..." }
return lookupMap.keys
.filterDistinctRoots()
.map { lookupMap.getValue(it) }
.toSet()
.also { log(VERBOSE) { "After filtering we got ${it.size} distinct roots" } }
}

val APathLookup<*>.extension: String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -798,33 +798,16 @@ class LocalGateway @Inject constructor(
log(TAG, VERBOSE) { "delete($mode->ROOT): $path" }
rootOps {
if (Bugs.isDryRun) log(TAG, INFO) { "DRYRUN: Not deleting (root) $javaFile" }
var success = it.delete(path, recursive = true, dryRun = Bugs.isDryRun)

if (!success) {
// TODO We could move this into the root service for better performance?
success = !it.exists(path)
if (success) log(TAG, WARN) { "Tried to delete file, but it's already gone: $path" }
}

if (!success) {
throw IOException("Root delete() call returned false")
}
val success = it.delete(path, recursive = true, dryRun = Bugs.isDryRun)
if (!success) throw IOException("Root delete() call returned false")
}
}

hasShizuku() && (mode == Mode.ADB || mode == Mode.AUTO) -> {
log(TAG, VERBOSE) { "delete($mode->ADB): $path" }
adbOps {
if (Bugs.isDryRun) log(TAG, INFO) { "DRYRUN: Not deleting (adb) $javaFile" }
var success = it.delete(path, recursive = true, dryRun = Bugs.isDryRun)


if (!success) {
// TODO We could move this into the ADB service for better performance?
success = !it.exists(path)
if (success) log(TAG, WARN) { "Tried to delete file, but it's already gone: $path" }
}

val success = it.delete(path, recursive = true, dryRun = Bugs.isDryRun)
if (!success) throw IOException("ADB delete() call returned false")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,14 @@ fun LocalPath.performLookupExtended(
}

fun LocalPath.isAncestorOf(child: LocalPath): Boolean {
val parentPath = this.asFile().absolutePath
val childPath = child.asFile().absolutePath
val parentPath = this.asFile().path
val childPath = child.asFile().path

return when (parentPath) {
childPath -> false
File.separator -> childPath.startsWith(parentPath)
else -> childPath.startsWith(parentPath + File.separator)
return when {
parentPath.length >= childPath.length -> false
!childPath.startsWith(parentPath) -> false
parentPath == File.separator -> true
else -> childPath[parentPath.length] == File.separatorChar
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,20 @@ class FileOpsHost @Inject constructor(

override fun delete(path: LocalPath, recursive: Boolean, dryRun: Boolean): Boolean = try {
log(TAG, VERBOSE) { "delete($path,recursive=$recursive,dryRun=$dryRun)..." }
path.asFile().run {
when {
dryRun -> canWrite()
recursive -> deleteRecursively()
else -> delete()
}
val javaFile = path.asFile()

var success = when {
dryRun -> javaFile.canWrite()
recursive -> javaFile.deleteRecursively()
else -> javaFile.delete()
}

if (!success) {
success = !javaFile.exists()
if (success) log(TAG, WARN) { "Tried to delete file, but it's already gone: $path" }
}

success
} catch (e: Exception) {
log(TAG, ERROR) { "delete(path=$path,recursive=$recursive,dryRun=$dryRun) failed\n${e.asLog()}" }
throw e.wrapToPropagate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,18 @@ class APathLookupExtensionTest : BaseTest() {
lookup4
)
}

@Test fun `filterDistinctRoots performance`() {
val targets = mutableSetOf<LocalPathLookup>()
(1..8000).forEach {
LocalPathLookup(
lookedUp = LocalPath.build("parentA", "parentB", "file$it"),
fileType = FileType.FILE,
size = 0,
modifiedAt = Instant.EPOCH,
target = null,
).run { targets.add(this) }
}
targets.filterDistinctRoots().size shouldBe 8000
}
}
12 changes: 9 additions & 3 deletions app/src/main/java/eu/darken/sdmse/appcleaner/core/AppCleaner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import javax.inject.Inject
Expand Down Expand Up @@ -231,11 +232,16 @@ class AppCleaner @Inject constructor(
?: throw IllegalStateException("Can't find filter for $filterIdentifier")

updateProgressSecondary(eu.darken.sdmse.common.R.string.general_progress_loading)

val currentProgress = progress.first()
filter.withProgress(
client = this,
onUpdate = { old, new -> old?.copy(secondary = new?.primary ?: CaString.EMPTY) },
onCompletion = { it }
onUpdate = { old, new ->
old?.copy(
secondary = new?.primary ?: CaString.EMPTY,
count = new?.count ?: Progress.Count.Indeterminate(),
)
},
onCompletion = { currentProgress }
) {
val result = process(targets, allMatches)
log(TAG, INFO) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ abstract class BaseExpendablesFilter : ExpendablesFilter {
val successful = mutableSetOf<ExpendablesFilter.Match>()
val failed = mutableSetOf<Pair<ExpendablesFilter.Match, Exception>>()

log(TAG, VERBOSE) { "Checking distinct roots..." }
val distinctRoots = targets.map { it.lookup }.filterDistinctRoots()

if (distinctRoots.size != targets.size) {
Expand All @@ -52,9 +53,11 @@ abstract class BaseExpendablesFilter : ExpendablesFilter {
}
}

log(TAG) { "Got ${distinctRoots.size} distinct roots" }
updateProgressCount(Progress.Count.Percent(distinctRoots.size))

distinctRoots.forEach { targetRoot ->
log(TAG) { "Processing root: $targetRoot" }
updateProgressPrimary(targetRoot.userReadablePath)
val main = targets.first { it.lookup == targetRoot }

Expand Down