-
-
Notifications
You must be signed in to change notification settings - Fork 440
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #595 from d4rken-org/systemcleaner_legacy_filter_i…
…mport SystemCleaner: Support for importing SD Maid 1/Legacy filters
- Loading branch information
Showing
21 changed files
with
452 additions
and
37 deletions.
There are no files selected for viewing
25 changes: 18 additions & 7 deletions
25
app-common/src/main/java/eu/darken/sdmse/common/serialization/RegexAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,27 @@ | ||
package eu.darken.sdmse.common.serialization | ||
|
||
import com.squareup.moshi.FromJson | ||
import com.squareup.moshi.Json | ||
import com.squareup.moshi.JsonClass | ||
import com.squareup.moshi.ToJson | ||
|
||
class RegexAdapter(private val caseInsensitive: Boolean) { | ||
class RegexAdapter { | ||
|
||
@ToJson | ||
fun toJson(value: Regex): String = value.toString() | ||
fun toJson(value: Regex): Wrapper = Wrapper( | ||
pattern = value.pattern, | ||
options = value.options, | ||
) | ||
|
||
@FromJson | ||
fun fromJson(raw: String): Regex = if (caseInsensitive) { | ||
Regex(raw, RegexOption.IGNORE_CASE) | ||
} else { | ||
Regex(raw) | ||
} | ||
fun fromJson(raw: Wrapper): Regex = Regex( | ||
pattern = raw.pattern, | ||
options = raw.options | ||
) | ||
|
||
@JsonClass(generateAdapter = true) | ||
data class Wrapper( | ||
@Json(name = "pattern") val pattern: String, | ||
@Json(name = "options") val options: Set<RegexOption> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
app-common/src/test/java/eu/darken/sdmse/common/serialization/RegexAdapterTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package eu.darken.sdmse.common.serialization | ||
|
||
import com.squareup.moshi.JsonClass | ||
import com.squareup.moshi.Moshi | ||
import io.kotest.matchers.shouldBe | ||
import org.junit.jupiter.api.Test | ||
import testhelpers.BaseTest | ||
import testhelpers.json.toComparableJson | ||
|
||
class RegexAdapterTest : BaseTest() { | ||
|
||
val moshi = Moshi.Builder().apply { | ||
add(RegexAdapter()) | ||
}.build() | ||
|
||
@JsonClass(generateAdapter = true) | ||
data class TestContainer( | ||
val regexValue: Regex?, | ||
val regexList: List<Regex> | ||
) | ||
|
||
val adapter = moshi.adapter(TestContainer::class.java) | ||
|
||
@Test | ||
fun `serialize test container`() { | ||
val before = TestContainer( | ||
regexValue = Regex("value", RegexOption.LITERAL), | ||
regexList = listOf( | ||
Regex("ele1", RegexOption.COMMENTS), | ||
Regex("ele2", setOf(RegexOption.DOT_MATCHES_ALL, RegexOption.MULTILINE)), | ||
) | ||
) | ||
|
||
val rawJson = adapter.toJson(before) | ||
|
||
rawJson.toComparableJson() shouldBe """ | ||
{ | ||
"regexValue": { | ||
"pattern": "value", | ||
"options": [ | ||
"LITERAL" | ||
] | ||
}, | ||
"regexList": [ | ||
{ | ||
"pattern": "ele1", | ||
"options": [ | ||
"COMMENTS" | ||
] | ||
}, | ||
{ | ||
"pattern": "ele2", | ||
"options": [ | ||
"MULTILINE", | ||
"DOT_MATCHES_ALL" | ||
] | ||
} | ||
] | ||
} | ||
""".toComparableJson() | ||
|
||
val after = adapter.fromJson(rawJson)!! | ||
after.regexValue!!.apply { | ||
pattern shouldBe before.regexValue!!.pattern | ||
options shouldBe before.regexValue.options | ||
} | ||
after.regexList[0].apply { | ||
pattern shouldBe before.regexList[0].pattern | ||
options shouldBe before.regexList[0].options | ||
} | ||
after.regexList[1].apply { | ||
pattern shouldBe before.regexList[1].pattern | ||
options shouldBe before.regexList[1].options | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
188 changes: 188 additions & 0 deletions
188
app/src/main/java/eu/darken/sdmse/systemcleaner/core/filter/custom/LegacyFilterSupport.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package eu.darken.sdmse.systemcleaner.core.filter.custom | ||
|
||
import androidx.annotation.Keep | ||
import com.squareup.moshi.Json | ||
import com.squareup.moshi.JsonClass | ||
import com.squareup.moshi.Moshi | ||
import eu.darken.sdmse.common.areas.DataArea | ||
import eu.darken.sdmse.common.debug.logging.Logging.Priority.WARN | ||
import eu.darken.sdmse.common.debug.logging.asLog | ||
import eu.darken.sdmse.common.debug.logging.log | ||
import eu.darken.sdmse.common.debug.logging.logTag | ||
import eu.darken.sdmse.common.files.FileType | ||
import eu.darken.sdmse.common.files.toSegs | ||
import eu.darken.sdmse.systemcleaner.core.sieve.NameCriterium | ||
import eu.darken.sdmse.systemcleaner.core.sieve.SegmentCriterium | ||
import java.time.Duration | ||
import java.util.UUID | ||
import javax.inject.Inject | ||
|
||
|
||
class LegacyFilterSupport @Inject constructor( | ||
private val moshi: Moshi, | ||
) { | ||
private val adapter by lazy { moshi.adapter(Filter::class.java) } | ||
|
||
suspend fun import(payload: String): CustomFilterConfig? { | ||
log(TAG) { "Importing $payload" } | ||
|
||
val legacyFilter = try { | ||
adapter.fromJson(payload)!! | ||
} catch (e: Exception) { | ||
log(TAG, WARN) { "Failed to import $payload: ${e.asLog()}" } | ||
return null | ||
} | ||
|
||
val pathCriteria = mutableSetOf<SegmentCriterium>() | ||
legacyFilter.possibleBasePathes | ||
?.map { | ||
SegmentCriterium( | ||
segments = it.toSegs(), | ||
mode = SegmentCriterium.Mode.Start(allowPartial = true, ignoreCase = true) | ||
) | ||
} | ||
?.forEach { pathCriteria.add(it) } | ||
legacyFilter.possiblePathContains | ||
?.map { | ||
SegmentCriterium( | ||
segments = it.toSegs(), | ||
mode = SegmentCriterium.Mode.Contain(allowPartial = true, ignoreCase = true) | ||
) | ||
} | ||
?.forEach { pathCriteria.add(it) } | ||
|
||
val nameCriteria = mutableSetOf<NameCriterium>() | ||
legacyFilter.possibleNameInits | ||
?.map { NameCriterium(name = it, mode = NameCriterium.Mode.Start(ignoreCase = true)) } | ||
?.forEach { nameCriteria.add(it) } | ||
|
||
legacyFilter.possibleNameEndings | ||
?.map { NameCriterium(name = it, mode = NameCriterium.Mode.End(ignoreCase = true)) } | ||
?.forEach { nameCriteria.add(it) } | ||
|
||
return CustomFilterConfig( | ||
label = legacyFilter.label, | ||
identifier = UUID.randomUUID().toString(), | ||
pathCriteria = pathCriteria, | ||
nameCriteria = nameCriteria, | ||
exclusionCriteria = legacyFilter.exclusions?.map { | ||
SegmentCriterium( | ||
segments = it.toSegs(), | ||
mode = SegmentCriterium.Mode.Contain(allowPartial = true, ignoreCase = true) | ||
) | ||
}?.toSet(), | ||
pathRegexes = legacyFilter.regex?.map { | ||
Regex(it) | ||
}?.toSet(), | ||
sizeMinimum = legacyFilter.minimumSize, | ||
sizeMaximum = legacyFilter.maximumSize, | ||
ageMinimum = legacyFilter.minimumAge?.let { Duration.ofMillis(it) }, | ||
ageMaximum = legacyFilter.maximumAge?.let { Duration.ofMillis(it) }, | ||
fileTypes = legacyFilter.targetType?.let { | ||
when (it) { | ||
Filter.TargetType.FILE -> setOf(FileType.FILE) | ||
Filter.TargetType.DIRECTORY -> setOf(FileType.DIRECTORY) | ||
Filter.TargetType.UNDEFINED -> null | ||
} | ||
}, | ||
areas = legacyFilter.locations?.mapNotNull { | ||
when (it) { | ||
Filter.Location.SDCARD -> DataArea.Type.SDCARD | ||
Filter.Location.PUBLIC_MEDIA -> DataArea.Type.PUBLIC_MEDIA | ||
Filter.Location.PUBLIC_DATA -> DataArea.Type.PUBLIC_DATA | ||
Filter.Location.PUBLIC_OBB -> DataArea.Type.PUBLIC_OBB | ||
Filter.Location.PRIVATE_DATA -> DataArea.Type.PRIVATE_DATA | ||
Filter.Location.APP_LIB -> DataArea.Type.APP_LIB | ||
Filter.Location.APP_ASEC -> DataArea.Type.APP_ASEC | ||
Filter.Location.APP_APP -> DataArea.Type.APP_APP | ||
Filter.Location.APP_APP_PRIVATE -> DataArea.Type.APP_APP_PRIVATE | ||
Filter.Location.DALVIK_DEX -> DataArea.Type.DALVIK_DEX | ||
Filter.Location.DALVIK_PROFILE -> DataArea.Type.DALVIK_PROFILE | ||
Filter.Location.SYSTEM -> DataArea.Type.SYSTEM | ||
Filter.Location.SYSTEM_APP -> DataArea.Type.SYSTEM_APP | ||
Filter.Location.SYSTEM_PRIV_APP -> DataArea.Type.SYSTEM_PRIV_APP | ||
Filter.Location.DOWNLOAD_CACHE -> DataArea.Type.DOWNLOAD_CACHE | ||
Filter.Location.DATA -> DataArea.Type.DATA | ||
Filter.Location.DATA_SYSTEM -> DataArea.Type.DATA_SYSTEM | ||
Filter.Location.DATA_SYSTEM_CE -> DataArea.Type.DATA_SYSTEM_CE | ||
Filter.Location.DATA_SYSTEM_DE -> DataArea.Type.DATA_SYSTEM_DE | ||
Filter.Location.PORTABLE -> DataArea.Type.PORTABLE | ||
Filter.Location.OEM -> DataArea.Type.OEM | ||
Filter.Location.DATA_SDEXT2 -> DataArea.Type.DATA_SDEXT2 | ||
Filter.Location.ROOT -> null | ||
Filter.Location.VENDOR -> null | ||
Filter.Location.MNT_SECURE_ASEC -> null | ||
Filter.Location.UNKNOWN -> null | ||
} | ||
}?.toSet()?.takeIf { it.isNotEmpty() } | ||
) | ||
} | ||
|
||
@JsonClass(generateAdapter = true) | ||
data class Filter( | ||
@Json(name = "label") val label: String, | ||
@Json(name = "targetType") val targetType: TargetType?, | ||
@Json(name = "isEmpty") val isEmpty: Boolean?, | ||
@Json(name = "locations") val locations: Set<Location>?, | ||
@Json(name = "mainPath") val possibleBasePathes: Set<String>?, | ||
@Json(name = "pathContains") val possiblePathContains: Set<String>?, | ||
@Json(name = "possibleNameInits") val possibleNameInits: Set<String>?, | ||
@Json(name = "possibleNameEndings") val possibleNameEndings: Set<String>?, | ||
@Json(name = "exclusions") val exclusions: Set<String>?, | ||
@Json(name = "regexes") val regex: Set<String>?, | ||
@Json(name = "maximumSize") val maximumSize: Long?, | ||
@Json(name = "minimumSize") val minimumSize: Long?, | ||
@Json(name = "maximumAge") val maximumAge: Long?, | ||
@Json(name = "minimumAge") val minimumAge: Long?, | ||
) { | ||
|
||
// @Json(name = "version") private int version = VERSION; | ||
// @Json(name = "identifier") private String identifier; | ||
// @Json(name = "color") private String color; | ||
// @Json(name = "description") private String description; | ||
// @Nullable @Json(name = "rootOnly") private Boolean rootOnly; | ||
|
||
@Keep | ||
@JsonClass(generateAdapter = false) | ||
enum class TargetType { | ||
@Json(name = "FILE") FILE, | ||
@Json(name = "DIRECTORY") DIRECTORY, | ||
@Json(name = "UNDEFINED") UNDEFINED, | ||
} | ||
|
||
@Keep | ||
@JsonClass(generateAdapter = false) | ||
enum class Location(val raw: String) { | ||
@Json(name = "SDCARD") SDCARD("SDCARD"), | ||
@Json(name = "PUBLIC_MEDIA") PUBLIC_MEDIA("PUBLIC_MEDIA"), | ||
@Json(name = "PUBLIC_DATA") PUBLIC_DATA("PUBLIC_DATA"), | ||
@Json(name = "PUBLIC_OBB") PUBLIC_OBB("PUBLIC_OBB"), | ||
@Json(name = "PRIVATE_DATA") PRIVATE_DATA("PRIVATE_DATA"), | ||
@Json(name = "APP_LIB") APP_LIB("APP_LIB"), | ||
@Json(name = "APP_ASEC") APP_ASEC("APP_ASEC"), | ||
@Json(name = "MNT_SECURE_ASEC") MNT_SECURE_ASEC("MNT_SECURE_ASEC"), | ||
@Json(name = "APP_APP") APP_APP("APP_APP"), | ||
@Json(name = "APP_APP_PRIVATE") APP_APP_PRIVATE("APP_APP_PRIVATE"), | ||
@Json(name = "DALVIK_DEX") DALVIK_DEX("DALVIK_DEX"), | ||
@Json(name = "DALVIK_PROFILE") DALVIK_PROFILE("DALVIK_PROFILE"), | ||
@Json(name = "SYSTEM_APP") SYSTEM_APP("SYSTEM_APP"), | ||
@Json(name = "SYSTEM_PRIV_APP") SYSTEM_PRIV_APP("SYSTEM_PRIV_APP"), | ||
@Json(name = "DOWNLOAD_CACHE") DOWNLOAD_CACHE("DOWNLOAD_CACHE"), | ||
@Json(name = "SYSTEM") SYSTEM("SYSTEM"), | ||
@Json(name = "DATA") DATA("DATA"), | ||
@Json(name = "DATA_SYSTEM") DATA_SYSTEM("DATA_SYSTEM"), | ||
@Json(name = "DATA_SYSTEM_CE") DATA_SYSTEM_CE("DATA_SYSTEM_CE"), | ||
@Json(name = "DATA_SYSTEM_DE") DATA_SYSTEM_DE("DATA_SYSTEM_DE"), | ||
@Json(name = "PORTABLE") PORTABLE("PORTABLE"), | ||
@Json(name = "ROOT") ROOT("ROOT"), | ||
@Json(name = "VENDOR") VENDOR("VENDOR"), | ||
@Json(name = "OEM") OEM("OEM"), | ||
@Json(name = "DATA_SDEXT2") DATA_SDEXT2("DATA_SDEXT2"), | ||
@Json(name = "UNKNOWN") UNKNOWN("UNKNOWN"), | ||
} | ||
} | ||
|
||
companion object { | ||
private val TAG = logTag("SystemCleaner", "CustomFilter", "Repo") | ||
} | ||
} |
Oops, something went wrong.