📖 PrzeglądOverview

Moduł EventBus to lekki system eventów oparty na Kotlin Flow. Umożliwia komunikację między komponentami aplikacji bez bezpośredniego powiązania.The EventBus module is a lightweight event system based on Kotlin Flow. Enables inter-component communication without direct coupling.

Szybki przykładQuick Example
// Definicja eventu
data class UserLoggedIn(val userId: String) : EventBus.Event

// Wysyłanie
ADict.EventBus.post(UserLoggedIn("user123"))

// Odbieranie (lifecycle-aware)
ADict.EventBus.on<UserLoggedIn>(lifecycleOwner) { event ->
    showWelcome(event.userId)
}

📤 Wysyłanie eventówSending Events

post(event: Event)

Wyślij event asynchronicznie do wszystkich subskrybentów.Send event asynchronously to all subscribers.

postSync(event: Event)

Wyślij event synchronicznie (blokuje do emisji).Send event synchronously (blocks until emission).

postSticky(event: Event)

Wyślij sticky event - zachowuje ostatnią wartość dla nowych subskrybentów.Send sticky event - retains last value for new subscribers.

Wysyłanie eventówSending Events
// Definicje eventów
data class UserLoggedIn(val userId: String, val name: String) : EventBus.Event
data class UserLoggedOut(val reason: String? = null) : EventBus.Event
data class CartUpdated(val itemCount: Int, val total: Double) : EventBus.Event
data class AppConfig(val theme: String, val language: String) : EventBus.Event

// Asynchroniczne wysłanie
ADict.EventBus.post(UserLoggedIn("123", "Jan"))

// Synchroniczne (rzadko potrzebne)
ADict.EventBus.postSync(CartUpdated(5, 99.99))

// Sticky - nowi subskrybenci od razu dostaną wartość
ADict.EventBus.postSticky(AppConfig(theme = "dark", language = "pl"))

📥 Subskrypcja eventówEvent Subscription

Lifecycle-aware (zalecane)Lifecycle-aware (recommended)

on<T : Event>(lifecycleOwner, callback): String

Subskrybuj eventy z automatycznym anulowaniem przy zniszczeniu lifecycle.

ZwracaReturns: ID subskrypcji

onAny(lifecycleOwner, callback): String

Subskrybuj wszystkie eventy.

once<T : Event>(lifecycleOwner, callback)

Subskrybuj tylko jeden event, potem automatycznie anuluj.

Flow-based (dla ViewModel/Coroutines)

eventsOf<T : Event>(): SharedFlow<T>

Pobierz Flow eventów określonego typu.Get a Flow of events of a specific type.

eventsFlow(): SharedFlow<Event>

Pobierz Flow wszystkich eventów.

Subskrypcja eventów
// W Activity/Fragment (lifecycle-aware)
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Subskrypcja konkretnego typu
        ADict.EventBus.on<UserLoggedIn>(this) { event ->
            showWelcome(event.name)
            updateUI(event.userId)
        }

        ADict.EventBus.on<CartUpdated>(this) { event ->
            updateCartBadge(event.itemCount)
        }

        // Subskrypcja wszystkich eventów
        ADict.EventBus.onAny(this) { event ->
            Log.d("EventBus", "Received: ${event::class.simpleName}")
        }

        // Tylko jeden event
        ADict.EventBus.once<UserLoggedIn>(this) { event ->
            showFirstLoginBonus()
        }
    }
}

// W ViewModel (Flow-based)
class MainViewModel : ViewModel() {
    init {
        viewModelScope.launch {
            ADict.EventBus.eventsOf<UserLoggedIn>().collect { event ->
                // Obsługa
            }
        }

        viewModelScope.launch {
            ADict.EventBus.eventsFlow().collect { event ->
                when (event) {
                    is UserLoggedIn -> handleLogin(event)
                    is UserLoggedOut -> handleLogout(event)
                }
            }
        }
    }
}

Manualna anulacjaManual Cancellation

unsubscribe(subscriptionId: String)

Ręczne anulowanie subskrypcji.Manual subscription cancellation.

unsubscribeAll()

Anuluj wszystkie subskrypcje.

📌 Sticky events

Sticky events zachowują ostatnią wartość i natychmiast ją dostarczają nowym subskrybentom.Sticky events retain the last value and deliver it immediately to new subscribers. Idealne dla stanu aplikacji (theme, config, user session).

postSticky(event: Event)

Wyślij sticky event.Send a sticky event.

onSticky<T : Event>(lifecycleOwner, callback): String

Subskrybuj sticky event - otrzymasz ostatnią wartość natychmiast + przyszłe.Subscribe to sticky event - receive last value immediately + future ones.

getStickyEvent<T : Event>(): T?

Pobierz aktualny sticky event (lub null).

removeStickyEvent<T : Event>()

Usuń sticky event danego typu.Remove sticky event of a given type.

clearStickyEvents()

Wyczyść wszystkie sticky eventy.Clear all sticky events.

Sticky events
// Definicja sticky eventu
data class AppConfig(
    val theme: String,
    val language: String,
    val isPremium: Boolean
) : EventBus.Event

// Wysłanie sticky
ADict.EventBus.postSticky(AppConfig(
    theme = "dark",
    language = "pl",
    isPremium = true
))

// Subskrypcja - od razu dostanie aktualną wartość
ADict.EventBus.onSticky<AppConfig>(this) { config ->
    applyTheme(config.theme)
    setLanguage(config.language)
}

// Pobranie bez subskrypcji
val currentConfig = ADict.EventBus.getStickyEvent<AppConfig>()
currentConfig?.let {
    if (it.isPremium) showPremiumFeatures()
}

// Aktualizacja (po prostu wyślij nowy)
val newConfig = currentConfig?.copy(theme = "light") ?: return
ADict.EventBus.postSticky(newConfig)

📦 Predefiniowane eventy

EventBus zawiera zestaw gotowych eventów w EventBus.Events:

Event OpisDescription WłaściwościProperties
ConfigUpdated KonfiguracjaConfiguration zaktualizowana key, value
PurchaseCompleted Zakup zakończonyPurchase completed productId, success
AdShown Reklama wyświetlonaAd displayed adType, zone
AdClosed Reklama zamkniętaAd closed adType, zone
RewardEarned Nagroda otrzymanaReward received type, amount
PremiumStatusChanged Zmiana statusu premiumPremium status changed isPremium
ConsentChanged Zmiana zgód GDPRGDPR consent changed canShowAds
Custom Generyczny eventGeneric event name, data
UżycieUsage predefiniowanych eventów
import rip.nerd.adictlibrary.modules.EventBus.Events.*

// Wysyłanie
ADict.EventBus.post(PurchaseCompleted("premium_monthly", true))
ADict.EventBus.post(AdShown("interstitial", "level_complete"))
ADict.EventBus.post(RewardEarned("coins", 100))
ADict.EventBus.postSticky(PremiumStatusChanged(true))

// Custom event z danymi
ADict.EventBus.post(Custom("custom_action", mapOf(
    "source" to "home_screen",
    "user_id" to "123"
)))

// Odbieranie
ADict.EventBus.on<PurchaseCompleted>(this) { event ->
    if (event.success) {
        showThankYou(event.productId)
    }
}

ADict.EventBus.on<RewardEarned>(this) { event ->
    addReward(event.type, event.amount)
}

ADict.EventBus.onSticky<PremiumStatusChanged>(this) { event ->
    updatePremiumUI(event.isPremium)
}

💡 PrzykładyExamples praktyczne

Komunikacja między Activity a FragmentCommunication between Activity and Fragment

Activity ↔ Fragment
// Event
data class FilterChanged(
    val category: String?,
    val priceRange: IntRange?,
    val sortBy: String
) : EventBus.Event

// Fragment wysyła event
class FilterFragment : Fragment() {
    fun onApplyFilters() {
        ADict.EventBus.post(FilterChanged(
            category = selectedCategory,
            priceRange = priceRange,
            sortBy = sortOrder
        ))
        dismiss()
    }
}

// Activity odbiera
class ProductListActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        ADict.EventBus.on<FilterChanged>(this) { filters ->
            viewModel.applyFilters(filters)
        }
    }
}

Synchronizacja stanu między ekranamiState synchronization between screens

Synchronizacja koszyka
// Sticky event dla stanu koszyka
data class CartState(
    val items: List<CartItem>,
    val total: Double,
    val itemCount: Int
) : EventBus.Event

// CartManager aktualizuje stan
object CartManager {
    private var items = mutableListOf<CartItem>()

    fun addItem(product: Product, quantity: Int) {
        items.add(CartItem(product, quantity))
        notifyStateChanged()
    }

    fun removeItem(productId: String) {
        items.removeAll { it.product.id == productId }
        notifyStateChanged()
    }

    private fun notifyStateChanged() {
        ADict.EventBus.postSticky(CartState(
            items = items.toList(),
            total = items.sumOf { it.totalPrice },
            itemCount = items.sumOf { it.quantity }
        ))
    }
}

// Każdy ekran subskrybuje
class HomeFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        ADict.EventBus.onSticky<CartState>(viewLifecycleOwner) { state ->
            cartBadge.text = state.itemCount.toString()
            cartBadge.isVisible = state.itemCount > 0
        }
    }
}

class ProductDetailActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        ADict.EventBus.onSticky<CartState>(this) { state ->
            updateAddToCartButton(state)
        }
    }
}

Obsługa premium/reklamPremium/ad handling

Premium status
// Po zakupie premium
fun onPremiumPurchased() {
    ADict.EventBus.postSticky(EventBus.Events.PremiumStatusChanged(true))
}

// Wszystkie miejsca z reklamami reagują
class AdBannerView : View {
    init {
        ADict.EventBus.onSticky<EventBus.Events.PremiumStatusChanged>(
            findViewTreeLifecycleOwner()!!
        ) { event ->
            visibility = if (event.isPremium) View.GONE else View.VISIBLE
        }
    }
}

class InterstitialManager {
    fun showIfAllowed() {
        val isPremium = ADict.EventBus
            .getStickyEvent<EventBus.Events.PremiumStatusChanged>()
            ?.isPremium ?: false

        if (!isPremium) {
            showInterstitial()
        }
    }
}

📚 API Reference

MetodaMethod OpisDescription
post(event) Wyślij event asynchronicznieSend event asynchronously
postSync(event) Wyślij event synchronicznieSend event synchronously
postSticky(event) Wyślij sticky eventSend sticky event
on<T>(lifecycleOwner, callback) Subskrybuj event (lifecycle-aware)Subscribe to event (lifecycle-aware)
onAny(lifecycleOwner, callback) Subskrybuj wszystkie eventySubscribe to all events
onSticky<T>(lifecycleOwner, callback) Subskrybuj sticky eventSubscribe to sticky event
once<T>(lifecycleOwner, callback) Subskrybuj tylko jeden eventSubscribe to a single event only
eventsOf<T>() Pobierz Flow eventów typu TGet Flow of events of type T
eventsFlow() Pobierz Flow wszystkich eventówGet Flow of all events
getStickyEvent<T>() Pobierz aktualny sticky eventGet current sticky event
removeStickyEvent<T>() Usuń sticky eventRemove sticky event
unsubscribe(subscriptionId) Anuluj subskrypcjęCancel subscription
unsubscribeAll() Anuluj wszystkie subskrypcjeCancel all subscriptions
clearStickyEvents() Wyczyść sticky eventyClear sticky events
reset() Resetuj wszystkoReset everything