Skip to content

Commit

Permalink
Revamp notification actions
Browse files Browse the repository at this point in the history
  • Loading branch information
Nain57 committed Jun 29, 2024
1 parent 2730cd7 commit 1fa008c
Show file tree
Hide file tree
Showing 12 changed files with 370 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,9 @@ internal class LifecycleStatesRegistry {
return states
}

fun clearStates() {
overlaysLifecycleState.clear()
}

fun haveStates(): Boolean = overlaysLifecycleState.isNotEmpty()
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ import android.content.Context
import android.graphics.Point
import android.util.Log
import android.view.KeyEvent

import androidx.lifecycle.Lifecycle

import com.buzbuz.smartautoclicker.core.base.Dumpable
import com.buzbuz.smartautoclicker.core.base.addDumpTabulationLvl
import com.buzbuz.smartautoclicker.core.common.overlays.base.BaseOverlay

import com.buzbuz.smartautoclicker.core.display.DisplayMetrics
import com.buzbuz.smartautoclicker.core.common.overlays.base.Overlay
import com.buzbuz.smartautoclicker.core.common.overlays.manager.navigation.OverlayNavigationRequest
Expand Down Expand Up @@ -72,6 +71,8 @@ class OverlayManager @Inject internal constructor(
/** Notifies the caller of [navigateUpToRoot] once the all overlays above the root are destroyed. */
private var navigateUpToRootCompletionListener: (() -> Unit)? = null

var onVisibilityChangedListener: (() -> Unit)? = null

/** Flow on the top of the overlay stack. Null if the stack is empty. */
val backStackTop: Flow<Overlay?> = isNavigating
.filter { navigating -> !navigating }
Expand Down Expand Up @@ -129,6 +130,7 @@ class OverlayManager @Inject internal constructor(
Log.d(TAG, "Close all overlays (${overlayBackStack.size}, currently navigating: ${isNavigating.value}")

overlayNavigationRequestStack.clear()
lifecyclesRegistry.clearStates()
topOverlay?.destroy()
repeat(overlayBackStack.size) {
overlayNavigationRequestStack.push(OverlayNavigationRequest.NavigateUp)
Expand Down Expand Up @@ -158,6 +160,8 @@ class OverlayManager @Inject internal constructor(
lifecyclesRegistry.saveStates(overlayBackStack.toList())
// Hide from top to bottom of the stack
overlayBackStack.forEachReversed { it.hide() }

onVisibilityChangedListener?.invoke()
}

/**
Expand Down Expand Up @@ -185,6 +189,8 @@ class OverlayManager @Inject internal constructor(

} ?: Log.w(TAG, "State for overlay ${overlay.hashCode()} not found, can't restore state")
}

onVisibilityChangedListener?.invoke()
}

/** @return true if the overlay stack has been hidden via [hideAll], false if not. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package com.buzbuz.smartautoclicker.core.common.quality.domain
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

/** Describe the different quality levels felt by the user. */
sealed class Quality(internal val backToHighDelay: Duration? = null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.buzbuz.smartautoclicker.core.domain.model.condition.ImageCondition
import com.buzbuz.smartautoclicker.core.domain.model.event.ImageEvent
import com.buzbuz.smartautoclicker.core.domain.model.scenario.Scenario
import com.buzbuz.smartautoclicker.core.processing.data.DetectorEngine
import com.buzbuz.smartautoclicker.core.processing.data.DetectorState
import com.buzbuz.smartautoclicker.core.processing.domain.trying.ImageConditionProcessingTryListener
import com.buzbuz.smartautoclicker.core.processing.domain.trying.ImageConditionTry
import com.buzbuz.smartautoclicker.core.processing.domain.trying.ImageEventProcessingTryListener
Expand Down Expand Up @@ -103,6 +104,9 @@ class DetectionRepository @Inject constructor(

fun getScenarioId(): Identifier? = _scenarioId.value

fun isRunning(): Boolean =
detectorEngine.state.value == DetectorState.DETECTING

fun startScreenRecord(
context: Context,
resultCode: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,29 @@ import android.view.KeyEvent

import com.buzbuz.smartautoclicker.core.base.AndroidExecutor
import com.buzbuz.smartautoclicker.core.bitmaps.IBitmapManager
import com.buzbuz.smartautoclicker.core.common.overlays.manager.OverlayManager
import com.buzbuz.smartautoclicker.core.display.DisplayMetrics
import com.buzbuz.smartautoclicker.core.domain.model.scenario.Scenario
import com.buzbuz.smartautoclicker.core.dumb.domain.model.DumbScenario
import com.buzbuz.smartautoclicker.core.dumb.engine.DumbEngine
import com.buzbuz.smartautoclicker.core.processing.domain.DetectionRepository
import com.buzbuz.smartautoclicker.core.common.overlays.manager.OverlayManager
import com.buzbuz.smartautoclicker.core.processing.domain.DetectionState
import com.buzbuz.smartautoclicker.feature.smart.config.ui.MainMenu
import com.buzbuz.smartautoclicker.feature.dumb.config.ui.DumbMainMenu
import com.buzbuz.smartautoclicker.feature.qstile.domain.QSTileRepository
import com.buzbuz.smartautoclicker.feature.revenue.IRevenueRepository
import com.buzbuz.smartautoclicker.feature.revenue.UserBillingState
import com.buzbuz.smartautoclicker.feature.smart.debugging.domain.DebuggingRepository

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch

class LocalService(
Expand All @@ -48,22 +55,44 @@ class LocalService(
private val detectionRepository: DetectionRepository,
private val bitmapManager: IBitmapManager,
private val dumbEngine: DumbEngine,
val tileRepository: QSTileRepository,
private val tileRepository: QSTileRepository,
private val revenueRepository: IRevenueRepository,
private val debugRepository: DebuggingRepository,
private val androidExecutor: AndroidExecutor,
private val onStart: (isSmart: Boolean, name: String) -> Unit,
private val onStop: () -> Unit,
onStateChanged: (isRunning: Boolean, isMenuHidden: Boolean) -> Unit,
) : SmartAutoClickerService.ILocalService {

/** Scope for this LocalService. */
private val serviceScope: CoroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
/** Coroutine job for the delayed start of engine & ui. */
private var startJob: Job? = null
/** Coroutine job for the paywall result upon start from notification. */
private var paywallResultJob: Job? = null

/** State of this LocalService. */
private var state: LocalServiceState = LocalServiceState(isStarted = false, isSmartLoaded = false)
/** True if the overlay is started, false if not. */
internal var isStarted: Boolean = false
internal val isStarted: Boolean
get() = state.isStarted

init {
combine(dumbEngine.isRunning, detectionRepository.detectionState) { dumbIsRunning, smartState ->
dumbIsRunning || smartState == DetectionState.DETECTING
}.onEach { onStateChanged(it, overlayManager.isStackHidden()) }.launchIn(serviceScope)

overlayManager.onVisibilityChangedListener = {
onStateChanged(
dumbEngine.isRunning.value || detectionRepository.isRunning(),
overlayManager.isStackHidden()
)
}
}

override fun startDumbScenario(dumbScenario: DumbScenario) {
if (isStarted) return
isStarted = true
if (state.isStarted) return
state = LocalServiceState(isStarted = true, isSmartLoaded = false)
onStart(false, dumbScenario.name)

displayMetrics.startMonitoring(context)
Expand Down Expand Up @@ -96,7 +125,7 @@ class LocalService(
*/
override fun startSmartScenario(resultCode: Int, data: Intent, scenario: Scenario) {
if (isStarted) return
isStarted = true
state = LocalServiceState(isStarted = true, isSmartLoaded = true)
onStart(true, scenario.name)

displayMetrics.startMonitoring(context)
Expand All @@ -123,9 +152,63 @@ class LocalService(
}
}

override fun playAndHide() {
serviceScope.launch {
overlayManager.hideAll()

if (state.isSmartLoaded && !detectionRepository.isRunning()) {
if (revenueRepository.userBillingState.value == UserBillingState.AD_REQUESTED) startPaywall()
else startSmartScenario()
} else if (!state.isSmartLoaded && !dumbEngine.isRunning.value) {
dumbEngine.startDumbScenario()
}
}
}

private fun startPaywall() {
revenueRepository.startPaywallUiFlow(context)

paywallResultJob = combine(revenueRepository.isBillingFlowInProgress, revenueRepository.userBillingState) { inProgress, state ->
if (inProgress) return@combine

if (state != UserBillingState.AD_REQUESTED) startSmartScenario()
paywallResultJob?.cancel()
paywallResultJob = null
}.launchIn(serviceScope)
}

private fun startSmartScenario() {
serviceScope.launch {
detectionRepository.startDetection(
context,
debugRepository.getDebugDetectionListenerIfNeeded(context),
revenueRepository.consumeTrial(),
)
}
}

override fun pauseAndShow() {
serviceScope.launch {
when {
dumbEngine.isRunning.value -> dumbEngine.stopDumbScenario()
detectionRepository.isRunning() -> detectionRepository.stopDetection()
}

overlayManager.restoreVisibility()
}
}

override fun hide() {
overlayManager.hideAll()
}

override fun show() {
overlayManager.restoreVisibility()
}

override fun stop() {
if (!isStarted) return
isStarted = false
state = LocalServiceState(isStarted = false, isSmartLoaded = false)

serviceScope.launch {
startJob?.join()
Expand All @@ -149,14 +232,9 @@ class LocalService(
event ?: return false
return overlayManager.propagateKeyEvent(event)
}
}

fun toggleOverlaysVisibility() {
overlayManager.apply {
if (isStackHidden()) {
restoreVisibility()
} else {
hideAll()
}
}
}
}
private data class LocalServiceState(
val isStarted: Boolean,
val isSmartLoaded: Boolean
)
Loading

0 comments on commit 1fa008c

Please sign in to comment.