Skip to content
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
5 changes: 3 additions & 2 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
Expand All @@ -30,8 +31,8 @@ java {
targetCompatibility = JavaVersion.VERSION_21
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_21.toString()
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}

Expand Down
5 changes: 3 additions & 2 deletions build-logic/obfuscation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
Expand All @@ -30,8 +31,8 @@ java {
targetCompatibility = JavaVersion.VERSION_21
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_21.toString()
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}

Expand Down
5 changes: 3 additions & 2 deletions build-logic/source-download/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
Expand All @@ -29,8 +30,8 @@ java {
targetCompatibility = JavaVersion.VERSION_21
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_21.toString()
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}

Expand Down
5 changes: 5 additions & 0 deletions core/common/ui/src/main/res/layout/include_loadable_list.xml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@
android:visibility="gone"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:fastScrollEnabled="true"
app:fastScrollVerticalThumbDrawable="@drawable/scroll_thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/scroll_track_drawable"
app:fastScrollHorizontalThumbDrawable="@drawable/scroll_thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/scroll_track_drawable"
tools:listitem="@layout/item_dropdown"
tools:visibility="gone"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ internal class DebugEngine @Inject constructor(
CoroutineScope(SupervisorJob() + ioDispatcher.limitedParallelism(1))

private var isReportEnabled: Boolean = false
private var isATry: Boolean = false

private val shouldWriteReport: Boolean
get() = isReportEnabled && !isATry

private val _isDebuggingSession: MutableStateFlow<Boolean> = MutableStateFlow(false)
val isDebuggingSession: StateFlow<Boolean> = _isDebuggingSession
Expand All @@ -76,12 +80,18 @@ internal class DebugEngine @Inject constructor(
val lastImageEventFulfilled: StateFlow<DebugLiveImageEventOccurrence?> = _lastImageEventFulfilled


override fun onSessionStarted(scenario: Scenario, imageEvents: List<ImageEvent>, triggerEvents: List<TriggerEvent>) {
override fun onSessionStarted(
scenario: Scenario,
imageEvents: List<ImageEvent>,
triggerEvents: List<TriggerEvent>,
isAnElementTry: Boolean,
) {
coroutineScopeIo.launch {
isReportEnabled = debugConfigurationLocalDataSource.isDebugReportEnabled()
isATry = isAnElementTry
_isDebuggingSession.value = true

if (isReportEnabled) {
if (shouldWriteReport) {
overviewRecorder.onSessionStart(scenario)
counterValuesRecorder.onSessionStarted(imageEvents, triggerEvents)
debugReportLocalDataSource.startReportWrite()
Expand All @@ -92,7 +102,7 @@ internal class DebugEngine @Inject constructor(
// Processing started on current frame
override fun onImageEventsProcessingStarted() {
coroutineScopeIo.launch {
if (!isReportEnabled) return@launch
if (!shouldWriteReport) return@launch

overviewRecorder.onFrameProcessingStarted()
}
Expand All @@ -101,7 +111,7 @@ internal class DebugEngine @Inject constructor(
// Processing started for current Event
override fun onImageEventProcessingStarted() {
coroutineScopeIo.launch {
if (!isReportEnabled) return@launch
if (!shouldWriteReport) return@launch

imgEventOccurrenceRecorder.onImageEventProcessingStarted()
counterValuesRecorder.onEventProcessingStarted()
Expand All @@ -112,15 +122,15 @@ internal class DebugEngine @Inject constructor(
// Condition is processed
override fun onImageConditionProcessingStarted() {
coroutineScopeIo.launch {
if (!isReportEnabled) return@launch
if (!shouldWriteReport) return@launch

imgEventOccurrenceRecorder.onImageConditionProcessingStarted()
}
}

override fun onImageConditionProcessingCompleted(result: ProcessedConditionResult.Image) {
coroutineScopeIo.launch {
if (!isReportEnabled) return@launch
if (!shouldWriteReport) return@launch

imgEventOccurrenceRecorder.onImageConditionProcessingCompleted(result)
}
Expand All @@ -133,6 +143,7 @@ internal class DebugEngine @Inject constructor(
DebugLiveImageEventOccurrence(
event = event,
imageConditionsResults = results.map { result ->
println("TOTO: results=$results")
DebugLiveImageConditionResult(
condition = result.condition,
isFulfilled = result.isFulfilled,
Expand All @@ -144,7 +155,7 @@ internal class DebugEngine @Inject constructor(
)
}

if (isReportEnabled) {
if (shouldWriteReport) {
overviewRecorder.onEventFulfilled(event)
debugReportLocalDataSource.writeEventOccurrenceToReport(
occurrence = DebugReportEventOccurrence.ImageEvent(
Expand All @@ -164,15 +175,15 @@ internal class DebugEngine @Inject constructor(
// Processing ended on current frame
override fun onImageEventsProcessingCompleted() {
coroutineScopeIo.launch {
if (!isReportEnabled) return@launch
if (!shouldWriteReport) return@launch

overviewRecorder.onFrameProcessingStopped()
}
}

override fun onImageEventsProcessingCancelled() {
coroutineScopeIo.launch {
if (!isReportEnabled) return@launch
if (!shouldWriteReport) return@launch

overviewRecorder.onFrameProcessingStopped()
imgEventOccurrenceRecorder.reset()
Expand All @@ -181,15 +192,15 @@ internal class DebugEngine @Inject constructor(

override fun onTriggerEventProcessingStarted() {
coroutineScopeIo.launch {
if (!isReportEnabled) return@launch
if (!shouldWriteReport) return@launch

counterValuesRecorder.onEventProcessingStarted()
}
}

override fun onTriggerEventFulfilled(event: TriggerEvent, results: List<ProcessedConditionResult.Trigger>) {
coroutineScopeIo.launch {
if (!isReportEnabled) return@launch
if (!shouldWriteReport) return@launch

overviewRecorder.onEventFulfilled(event)
debugReportLocalDataSource.writeEventOccurrenceToReport(
Expand All @@ -211,21 +222,21 @@ internal class DebugEngine @Inject constructor(

override fun onCounterValueChanged(counterName: String, previousValue: Int, newValue: Int) {
coroutineScopeIo.launch {
if (!isReportEnabled) return@launch
if (!shouldWriteReport) return@launch
counterValuesRecorder.onCounterValueChanged(counterName, previousValue, newValue)
}
}

override fun onEventStateChanged(event: Event, newValue: Boolean) {
coroutineScopeIo.launch {
if (!isReportEnabled) return@launch
if (!shouldWriteReport) return@launch
eventStateRecorder.onEventStateChanged(event, newValue)
}
}

override fun onSessionEnded() {
coroutineScopeIo.launch {
if (isReportEnabled) {
if (shouldWriteReport) {
debugReportLocalDataSource.stopReportWrite(
overview = DebugReportOverview(
scenarioId = overviewRecorder.scenarioId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class DetectorEngine @Inject constructor(
scenario: Scenario,
imageEvents: List<ImageEvent>,
triggerEvents: List<TriggerEvent>,
isATry: Boolean,
) {
if (_state.value != DetectorState.RECORDING) {
Log.w(TAG, "startDetection: Screen record is not started.")
Expand Down Expand Up @@ -200,6 +201,7 @@ class DetectorEngine @Inject constructor(
scenario = scenario,
imageEvents = imageEvents,
triggerEvents = triggerEvents,
isAnElementTry = isATry,
)

// Instantiate the processor and initialize its detection state.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*/
package com.buzbuz.smartautoclicker.core.processing.domain

import com.buzbuz.smartautoclicker.core.domain.model.action.ToggleEvent
import com.buzbuz.smartautoclicker.core.domain.model.condition.ImageCondition
import com.buzbuz.smartautoclicker.core.domain.model.condition.TriggerCondition
import com.buzbuz.smartautoclicker.core.domain.model.event.Event
Expand All @@ -35,8 +34,14 @@ interface SmartProcessingListener {
* @param scenario the [Scenario] running for the processing session.
* @param imageEvents the list of [ImageEvent] to be processed for this scenario.
* @param triggerEvents the list of [TriggerEvent] to be processed for this scenario.
* @param isATry tells if this session is a try or not.
*/
fun onSessionStarted(scenario: Scenario, imageEvents: List<ImageEvent>, triggerEvents: List<TriggerEvent>) = Unit
fun onSessionStarted(
scenario: Scenario,
imageEvents: List<ImageEvent>,
triggerEvents: List<TriggerEvent>,
isAnElementTry: Boolean = false,
) = Unit

/** The processing of an [TriggerEvent] has begun. */
fun onTriggerEventProcessingStarted() = Unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ internal class SmartProcessingRepositoryImpl @Inject constructor(
scenario = scenario,
imageEvents = events,
triggerEvents = triggerEvents,
isATry = false,
)

autoStopDuration?.let { duration ->
Expand Down Expand Up @@ -222,6 +223,7 @@ internal class SmartProcessingRepositoryImpl @Inject constructor(
scenario = elementTry.scenario,
imageEvents = elementTry.imageEvents,
triggerEvents = elementTry.triggerEvents,
isATry = true,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,17 @@ internal class ImageEventTry(
val event: ImageEvent,
) : ScenarioTry() {

override val imageEvents: List<ImageEvent> = listOf(event)
override val imageEvents: List<ImageEvent> = listOf(getTestEvent())
override val triggerEvents: List<TriggerEvent> = emptyList()

private fun getTestEvent(): ImageEvent =
event.copy(
enabledOnStart = true,
actions = event.actions.filter { action -> action.canBeTried() }
)

private fun Action.canBeTried(): Boolean =
this !is ToggleEvent
}

internal class ImageConditionTry(
Expand Down Expand Up @@ -102,7 +111,7 @@ internal class ActionTry(
ToggleEvent(
id = Identifier(databaseId = 1L),
eventId = eventId,
name = "Test Pause",
name = "Test Stop",
priority = 0,
toggleAll = true,
toggleAllType = ToggleEvent.ToggleType.DISABLE,
Expand All @@ -113,7 +122,7 @@ internal class ActionTry(
id = Identifier(databaseId = 1L),
eventId = eventId,
name = "Test timer reached",
durationMs = 1L,
durationMs = 500L,
restartWhenReached = false,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,53 @@ package com.buzbuz.smartautoclicker.feature.revenue.data.ads.sdk

import android.app.Activity
import android.content.Context
import android.util.Log
import androidx.annotation.MainThread

import com.buzbuz.smartautoclicker.core.base.di.Dispatcher
import com.buzbuz.smartautoclicker.core.base.di.HiltCoroutineDispatchers.Main
import com.buzbuz.smartautoclicker.feature.revenue.BuildConfig
import com.google.android.gms.ads.AdError

import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.FullScreenContentCallback
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.MobileAds
import com.google.android.gms.ads.RequestConfiguration
import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.min

@Singleton
class GoogleAdsSdk @Inject constructor() : IAdsSdk {
class GoogleAdsSdk @Inject constructor(
@Dispatcher(Main) dispatcherMain: CoroutineDispatcher,
) : IAdsSdk {

private val coroutineScopeMain: CoroutineScope =
CoroutineScope(SupervisorJob() + dispatcherMain)

/** How long before the data source tries to reconnect to Google play. */
private var reconnectMilliseconds = RECONNECT_TIMER_START_MILLISECONDS
private var reconnectJob: Job? = null

private var interstitialAd: InterstitialAd? = null

@MainThread
override fun initializeSdk(context: Context, onComplete: () -> Unit) {
MobileAds.initialize(context) { onComplete() }
try {
MobileAds.initialize(context) { onComplete() }
} catch (ex: Exception) {
Log.e(LOG_TAG, "Error while initializing Google Ads SDK, retrying...", ex)
retryInitWithExponentialBackoff(context, onComplete)
}
}

@MainThread
Expand Down Expand Up @@ -101,4 +125,27 @@ class GoogleAdsSdk @Inject constructor() : IAdsSdk {
override fun onAdDismissedFullScreenContent(): Unit = onDismiss(impression)
override fun onAdFailedToShowFullScreenContent(adError: AdError): Unit = onError(adError.code, adError.message)
}
}

/**
* Retries the Google Ads SDK with exponential backoff, maxing out at the time
* specified by RECONNECT_TIMER_MAX_TIME_MILLISECONDS.
*/
private fun retryInitWithExponentialBackoff(context: Context, onComplete: () -> Unit) {
if (reconnectJob != null) {
Log.e(LOG_TAG, "Reconnection job is already running")
return
}

reconnectJob = coroutineScopeMain.launch {
delay(reconnectMilliseconds)
reconnectMilliseconds = min(reconnectMilliseconds * 2, RECONNECT_TIMER_MAX_TIME_MILLISECONDS)

reconnectJob = null
initializeSdk(context, onComplete)
}
}
}

private const val RECONNECT_TIMER_START_MILLISECONDS = 1000L
private const val RECONNECT_TIMER_MAX_TIME_MILLISECONDS = 1000L * 60L * 15L // 15 minutes
private const val LOG_TAG = "GoogleAdsSdk"
Loading