Skip to content

Commit

Permalink
Fix V13 and bellow scenario import
Browse files Browse the repository at this point in the history
Several issues have been fixed:
* clickOnConditionId should not be mandatory when the condition operator is OR
* toggleAllType was not parsed properly
* domain object completion methods were inconsistent, this is now formalized
  • Loading branch information
Nain57 committed Dec 8, 2024
1 parent 4f8324b commit 6700b1f
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 Kevin Buzeau
* Copyright (C) 2024 Kevin Buzeau
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -18,4 +18,11 @@ package com.buzbuz.smartautoclicker.core.base.interfaces

interface Completable {
fun isComplete(): Boolean
}
}

fun List<Completable>.areComplete(): Boolean {
forEach { completable ->
if (!completable.isComplete()) return false
}
return true
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,13 @@ internal open class CompatDeserializer : Deserializer {
}

val completeActions = jsonCompleteEvent.jsonObject.getJsonArray("actions")
?.getListOf { jsonAction -> deserializeCompleteAction(jsonAction, eventEntityList, conditions)}
?.getListOf { jsonAction -> deserializeCompleteAction(
jsonAction,
eventEntityList,
conditions,
eventEntity.conditionOperator,
)
}
if (completeActions.isNullOrEmpty()) {
Log.w(TAG, "Can't deserialize this complete event, there is no actions")
return@mapNotNull null
Expand All @@ -151,9 +157,10 @@ internal open class CompatDeserializer : Deserializer {
jsonCompleteAction: JsonObject,
scenarioEvents: List<EventEntity>,
eventConditions: List<ConditionEntity>,
conditionsOperator: Int,
): CompleteActionEntity? {
val actionEntity = jsonCompleteAction.getJsonObject("action")?.let { jsonAction ->
deserializeAction(jsonAction, eventConditions)
deserializeAction(jsonAction, eventConditions, conditionsOperator)
} ?: return null

val extraEntityList = jsonCompleteAction.getJsonArray("intentExtras")
Expand Down Expand Up @@ -333,9 +340,13 @@ internal open class CompatDeserializer : Deserializer {
// ======================= ACTION

@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
open fun deserializeAction(jsonAction: JsonObject, eventConditions: List<ConditionEntity>): ActionEntity? =
open fun deserializeAction(
jsonAction: JsonObject,
eventConditions: List<ConditionEntity>,
conditionsOperator: Int,
): ActionEntity? =
when (deserializeActionType(jsonAction)) {
ActionType.CLICK -> deserializeActionClick(jsonAction, eventConditions)
ActionType.CLICK -> deserializeActionClick(jsonAction, eventConditions, conditionsOperator)
ActionType.SWIPE -> deserializeActionSwipe(jsonAction)
ActionType.PAUSE -> deserializeActionPause(jsonAction)
ActionType.INTENT -> deserializeActionIntent(jsonAction)
Expand All @@ -346,7 +357,11 @@ internal open class CompatDeserializer : Deserializer {
}

@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
open fun deserializeActionClick(jsonClick: JsonObject, eventConditions: List<ConditionEntity>): ActionEntity? {
open fun deserializeActionClick(
jsonClick: JsonObject,
eventConditions: List<ConditionEntity>,
conditionsOperator: Int,
): ActionEntity? {
val id = jsonClick.getLong("id", true) ?: return null
val eventId = jsonClick.getLong("eventId", true) ?: return null

Expand All @@ -358,12 +373,14 @@ internal open class CompatDeserializer : Deserializer {
val clickOffsetX: Int?
val clickOffsetY: Int?

val isAndConditionsOperator = conditionsOperator == 1

when (clickPositionType) {
ClickPositionType.ON_DETECTED_CONDITION -> {
x = null
y = null
clickOnConditionId = jsonClick.getLong("clickOnConditionId", true) ?: return null
if (!eventConditions.containsId(clickOnConditionId)) {
clickOnConditionId = jsonClick.getLong("clickOnConditionId", isAndConditionsOperator)
if (isAndConditionsOperator && (clickOnConditionId == null || !eventConditions.containsId(clickOnConditionId))) {
Log.w(TAG, "Can't deserialize action, clickOnConditionId is not valid.")
return null
}
Expand Down Expand Up @@ -471,7 +488,7 @@ internal open class CompatDeserializer : Deserializer {
priority = jsonToggleEvent.getInt("priority")?.coerceAtLeast(0) ?: 0,
type = ActionType.TOGGLE_EVENT,
toggleAll = toggleAll,
toggleAllType = jsonToggleEvent.getEnum<EventToggleType>("toggleEventType"),
toggleAllType = jsonToggleEvent.getEnum<EventToggleType>("toggleAllType"),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ internal open class CompatV11Deserializer : CompatV13Deserializer() {
* condition id is attached and position type is clearly defined. To fix this, we search for the first condition
* that should be detected.
*/
override fun deserializeActionClick(jsonClick: JsonObject, eventConditions: List<ConditionEntity>): ActionEntity? {
override fun deserializeActionClick(
jsonClick: JsonObject,
eventConditions: List<ConditionEntity>,
conditionsOperator: Int,
): ActionEntity? {

val id = jsonClick.getLong("id", true) ?: return null
val eventId = jsonClick.getLong("eventId", true) ?: return null
val clickOnCondition = jsonClick.getBoolean("clickOnCondition") ?: return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class CompatV11DeserializerTests {

// When
val decodedClick = (DeserializerFactory.create(VERSION_MAXIMUM) as CompatV11Deserializer)
.deserializeAction(jsonClick, emptyList())
.deserializeAction(jsonClick, emptyList(), 1)

// Then
assertNotNull(decodedClick)
Expand All @@ -118,7 +118,7 @@ class CompatV11DeserializerTests {
val condition = getDefaultImageCondition(true)
// When
val decodedClick = (DeserializerFactory.create(VERSION_MAXIMUM) as CompatV11Deserializer)
.deserializeAction(jsonClick, listOf(condition))
.deserializeAction(jsonClick, listOf(condition), 1)

// Then
assertNotNull(decodedClick)
Expand All @@ -134,7 +134,7 @@ class CompatV11DeserializerTests {
val jsonClick = createJsonClick(clickOnCondition = true)
// When
val decodedClick = (DeserializerFactory.create(VERSION_MAXIMUM) as CompatV11Deserializer)
.deserializeAction(jsonClick, emptyList())
.deserializeAction(jsonClick, emptyList(), 1)

// Then
assertNotNull(decodedClick)
Expand All @@ -151,7 +151,7 @@ class CompatV11DeserializerTests {
val condition = getDefaultImageCondition(shouldBeDetected = false)
// When
val decodedClick = (DeserializerFactory.create(VERSION_MAXIMUM) as CompatV11Deserializer)
.deserializeAction(jsonClick, listOf(condition))
.deserializeAction(jsonClick, listOf(condition), 1)

// Then
assertNotNull(decodedClick)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.room.withTransaction

import com.buzbuz.smartautoclicker.core.base.DatabaseListUpdater
import com.buzbuz.smartautoclicker.core.base.identifier.Identifier
import com.buzbuz.smartautoclicker.core.base.interfaces.areComplete
import com.buzbuz.smartautoclicker.core.bitmaps.IBitmapManager
import com.buzbuz.smartautoclicker.core.database.ScenarioDatabase
import com.buzbuz.smartautoclicker.core.database.dao.ActionDao
Expand Down Expand Up @@ -141,7 +142,7 @@ internal class ScenarioDataSource(
Log.d(TAG, "Add scenario copy to the database: ${scenario.id}")

// Check the events correctness
if (events.find { !it.isComplete() } != null)
if (!events.areComplete())
throw IllegalArgumentException("Can't update scenario content, one of the event is not complete")

return try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import com.buzbuz.smartautoclicker.core.domain.model.action.intent.toDomainInten
import com.buzbuz.smartautoclicker.core.domain.model.action.toggleevent.toDomain

internal fun Action.toEntity(): ActionEntity {
if (!isComplete()) throw IllegalStateException("Can't transform to entity, action is incomplete.")
if (!isComplete()) throw IllegalStateException("Can't transform to entity, action is incomplete: $this")

return when (this) {
is Click -> toClickEntity()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.buzbuz.smartautoclicker.core.domain.model.action

import com.buzbuz.smartautoclicker.core.base.identifier.Identifier
import com.buzbuz.smartautoclicker.core.base.interfaces.areComplete
import com.buzbuz.smartautoclicker.core.database.entity.EventToggleType
import com.buzbuz.smartautoclicker.core.domain.model.action.toggleevent.EventToggle

Expand Down Expand Up @@ -60,7 +61,7 @@ data class ToggleEvent(
return if (toggleAll) {
toggleAllType != null
} else {
eventToggles.isNotEmpty() && eventToggles.find { !it.isComplete() } == null
eventToggles.areComplete()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.buzbuz.smartautoclicker.core.base.identifier.Identifier
import com.buzbuz.smartautoclicker.core.base.interfaces.Completable
import com.buzbuz.smartautoclicker.core.base.interfaces.Identifiable
import com.buzbuz.smartautoclicker.core.base.interfaces.Prioritizable
import com.buzbuz.smartautoclicker.core.base.interfaces.areComplete
import com.buzbuz.smartautoclicker.core.domain.model.AND
import com.buzbuz.smartautoclicker.core.domain.model.action.Action
import com.buzbuz.smartautoclicker.core.domain.model.condition.ImageCondition
Expand Down Expand Up @@ -63,7 +64,8 @@ sealed class Event: Identifiable, Completable {

@CallSuper
override fun isComplete(): Boolean =
name.isNotEmpty() && actions.isNotEmpty() && conditions.isNotEmpty()
name.isNotEmpty() && actions.isNotEmpty() && actions.areComplete() &&
conditions.isNotEmpty() && conditions.areComplete()
}

/**
Expand All @@ -87,12 +89,7 @@ data class ImageEvent(
override fun isComplete(): Boolean {
if (!super.isComplete()) return false

conditions.forEach { condition ->
if (!condition.isComplete()) return false
}

actions.forEach { action ->
if (!action.isComplete()) return false
if (conditionOperator == AND && action is Click && !action.isClickOnConditionValid()) return false
}

Expand All @@ -113,11 +110,7 @@ data class TriggerEvent(

override fun isComplete(): Boolean {
if (!super.isComplete()) return false
conditions.forEach { condition ->
if (!condition.isComplete()) return false
}

if (actions.isEmpty()) return false
actions.forEach { action ->
if (!action.isValidForTrigger()) return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ internal class BackupEngine(appDataDir: File, private val contentResolver: Conte
* @param progress the object notified about the backup import progress.
*/
suspend fun loadBackup(zipFileUri: Uri, screenSize: Point, progress: BackupProgress) {
Log.d(TAG, "Load backup: $zipFileUri")
Log.i(TAG, "Load backup: $zipFileUri")

dumbBackupDataSource.reset()
smartBackupDataSource.reset()

Expand Down Expand Up @@ -139,7 +140,7 @@ internal class BackupEngine(appDataDir: File, private val contentResolver: Conte
}
}

else -> Log.i(TAG, "Nothing found to handle zip entry ${zipEntry.name}.")
else -> Log.w(TAG, "Nothing found to handle zip entry ${zipEntry.name}")
}
}
}
Expand All @@ -148,23 +149,26 @@ internal class BackupEngine(appDataDir: File, private val contentResolver: Conte
dumbBackupDataSource.verifyExtractedScenarios(screenSize)
smartBackupDataSource.verifyExtractedScenarios(screenSize)

Log.i(TAG, "Backup loading completed: $zipFileUri")
Log.i(TAG, "Inserting extracted scenarios into database")

progress.onCompleted(
dumbBackupDataSource.validBackups,
smartBackupDataSource.validBackups,
dumbBackupDataSource.failureCount + smartBackupDataSource.failureCount,
smartBackupDataSource.screenCompatWarning,
)
} catch (ioEx: IOException) {
Log.e(TAG, "Error while reading backup archive.")
Log.e(TAG, "Error while loading backup archive", ioEx)
progress.onError()
} catch (secEx: SecurityException) {
Log.e(TAG, "Error while reading backup archive, permission is denied")
Log.e(TAG, "Error while loading backup archive, permission is denied", secEx)
progress.onError()
} catch (iaEx: IllegalArgumentException) {
Log.e(TAG, "Error while reading backup archive, file is invalid")
Log.e(TAG, "Error while loading backup archive, file is invalid", iaEx)
progress.onError()
} catch (npEx: NullPointerException) {
Log.e(TAG, "Error while reading backup archive, file path is null")
Log.e(TAG, "Error while loading backup archive, file path is null", npEx)
progress.onError()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ internal class DumbBackupDataSource(
)

override fun verifyExtractedBackup(backup: DumbScenarioBackup, screenSize: Point): DumbScenarioWithActions? {
Log.i(TAG, "Verifying dumb scenario ${backup.dumbScenario.scenario.id}")

if (backup.dumbScenario.dumbActions.isEmpty()) {
Log.w(TAG, "Invalid dumb scenario, dumb action list is empty.")
return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ internal class SmartBackupDataSource(
)

override fun verifyExtractedBackup(backup: ScenarioBackup, screenSize: Point): CompleteScenario? {
Log.d(TAG, "Verifying scenario ${backup.scenario.scenario.id}")
Log.i(TAG, "Verifying smart scenario ${backup.scenario.scenario.id}")

backup.scenario.events.forEach { event ->
if (event.actions.isEmpty()) {
Expand All @@ -115,6 +115,7 @@ internal class SmartBackupDataSource(
screenCompatWarning = screenSize != Point(backup.screenWidth, backup.screenHeight)
}

Log.i(TAG, "Smart scenario is valid, has warnings: $screenCompatWarning")
return backup.scenario
}

Expand Down

0 comments on commit 6700b1f

Please sign in to comment.