🔍 PrzeglądOverview

Biblioteka oferuje trzy adaptery do automatycznego wstrzykiwania reklam w listy i pagery:

AdapterUżycieUsageTypType reklam
AdInjectedAdapter RecyclerView (listy) Banner, Native
AdInjectedPagerAdapter ViewPager2 (strony z ViewHolder) Banner, Native
AdInjectedFragmentStateAdapter ViewPager2 (strony jako Fragment) Banner, Native

Wspólne cechy wszystkich adapterów:Common features of all adapters:

⚙️ KonfiguracjaConfiguration - AdsSpec

Wszystkie adaptery przyjmują obiektAll adapters accept an object AdsSpec do konfiguracji pozycji i typów reklam:for configuring ad positions and types:

Definicja AdsSpecAdsSpec Definition
AdsSpec(
    zoneName = "feed",                      // Strefa konfiguracji reklam
    callbackName = "default",               // Callback dla eventów
    firstAdPosition = 5,                    // Pierwsza reklama po 5 elementach contentu
    interval = 8,                           // Kolejne reklamy co 8 elementów
    bannerAdSize = AdViewContainer.BANNER,  // Rozmiar bannera (jeśli używasz)
    binder = myNativeBinder,                // Binder dla reklam natywnych (opcjonalny)
    adKindForSlot = { slotIndex ->          // Typ reklamy per slot
        if (slotIndex % 2 == 0) AdKind.NATIVE else AdKind.BANNER
    }
)

ParametryParameters

ParametrParameterTypTypeDomyślnieDefaultOpisDescription
zoneNameString"default"NazwaName strefy konfiguracji reklam
callbackNameString"default"NazwaName callbacka dla eventów
firstAdPositionInt4Po ilu elementach contentu pierwsza reklamaAfter how many content items the first ad appears
intervalInt6Co ile elementów contentu kolejna reklamaContent items between each ad
bannerAdSizeIntBANNERRozmiar bannera (z AdViewContainer)Banner size (from AdViewContainer)
binderNativeAdViewBinder?nullCustomowy binder dla reklam natywnychCustom binder for native ads
adKindForSlot(Int) → AdKind{ NATIVE }Funkcja określająca typ reklamy dla slotuFunction determining ad type for slot

📜 AdInjectedAdapter - RecyclerView

Adapter dla standardowego RecyclerView z automatycznym wstrzykiwaniem reklam między elementami listy.Adapter for standard RecyclerView with automatic ad injection between list items.

Implementacja adapteraAdapter Implementation
class FeedAdapter(ctx: Context) : AdInjectedAdapter<FeedItem, RecyclerView.ViewHolder>(
    ctx,
    AdsSpec(
        zoneName = "feed",
        callbackName = "default",
        firstAdPosition = 5,              // Pierwsza reklama po 5 itemach
        interval = 8,                     // Kolejne co 8 itemów contentu
        bannerAdSize = AdViewContainer.LARGE_BANNER,
        adKindForSlot = { idx ->
            // Parzyste sloty → native, nieparzyste → banner
            if (idx % 2 == 0) AdKind.NATIVE else AdKind.BANNER
        }
    )
) {
    companion object {
        private const val TYPE_TEXT = 1
        private const val TYPE_PHOTO = 2
    }

    // Mapowanie contentu na viewType (tylko dla własnych typów!)
    override fun getContentItemViewType(contentPosition: Int): Int =
        when (getContentItem(contentPosition)) {
            is FeedItem.Text -> TYPE_TEXT
            is FeedItem.Photo -> TYPE_PHOTO
        }

    override fun onCreateContentViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return when (viewType) {
            TYPE_TEXT -> TextVH(inflater.inflate(R.layout.item_text, parent, false))
            TYPE_PHOTO -> PhotoVH(inflater.inflate(R.layout.item_photo, parent, false))
            else -> error("Unknown viewType=$viewType")
        }
    }

    override fun onBindContentViewHolder(
        holder: RecyclerView.ViewHolder,
        item: FeedItem,
        contentPosition: Int
    ) {
        when (holder) {
            is TextVH -> holder.bind(item as FeedItem.Text)
            is PhotoVH -> holder.bind(item as FeedItem.Photo)
        }
    }
}
UżycieUsage
val adapter = FeedAdapter(requireContext())
binding.recyclerView.adapter = adapter

// Załaduj dane
adapter.submitList(listOf(
    FeedItem.Text("Tytuł 1", "Treść..."),
    FeedItem.Photo("Zdjęcie 1", R.drawable.photo1),
    FeedItem.Text("Tytuł 2", "Treść..."),
    // ... więcej itemów
))

// Ręczny refresh reklam (np. po zmianie reguł AdGate)
adapter.refreshAds()

MetodyMethods API

MetodaMethodOpisDescription
submitList(items: List<T>)Ustawia nową listę contentu i przebudowuje sloty reklamSets new content list and rebuilds ad slots
getContentItem(position: Int): TPobiera element contentu na danej pozycjiGets content item at given position
refreshAds()Ręczne odświeżenie slotów reklam (po zmianach AdGate)Manual ad slot refresh (after AdGate changes)

📖 AdInjectedPagerAdapter - ViewPager2

Adapter dla ViewPager2 używający ViewHolderów. Idealny do galerii, stories, onboardingu.Adapter for ViewPager2 using ViewHolders. Ideal for galleries, stories, onboarding.

Implementacja adapteraAdapter Implementation
class StoryPagerAdapter(ctx: Context) : AdInjectedPagerAdapter<Story, RecyclerView.ViewHolder>(
    ctx,
    AdsSpec(
        zoneName = "stories",
        callbackName = "default",
        firstAdPosition = 3,              // Pierwsza reklama na stronie #3
        interval = 4,                     // Kolejne co 4 strony contentu
        adKindForSlot = { idx ->
            if (idx % 2 == 0) AdKind.NATIVE else AdKind.BANNER
        }
    )
) {
    companion object {
        private const val TYPE_TEXT = 1
        private const val TYPE_VIDEO = 2
    }

    override fun getContentItemViewType(contentPosition: Int): Int =
        when (getContentItem(contentPosition)) {
            is Story.Text -> TYPE_TEXT
            is Story.Video -> TYPE_VIDEO
        }

    override fun onCreateContentViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return when (viewType) {
            TYPE_TEXT -> TextStoryVH(inflater.inflate(R.layout.page_text_story, parent, false))
            TYPE_VIDEO -> VideoStoryVH(inflater.inflate(R.layout.page_video_story, parent, false))
            else -> error("Unknown viewType=$viewType")
        }
    }

    override fun onBindContentViewHolder(
        holder: RecyclerView.ViewHolder,
        item: Story,
        contentPosition: Int
    ) {
        when (holder) {
            is TextStoryVH -> holder.bind(item as Story.Text)
            is VideoStoryVH -> holder.bind(item as Story.Video)
        }
    }
}
UżycieUsage
val adapter = StoryPagerAdapter(requireContext())
binding.viewPager.adapter = adapter

adapter.submitList(listOf(
    Story.Text("Historia 1", "Treść..."),
    Story.Video("Film 1", videoUri),
    // ...
))

📄 AdInjectedFragmentStateAdapter - Fragmenty

Adapter dla ViewPager2 używający Fragmentów. Zalecany dla złożonych stron wymagających własnego lifecycle.Adapter for ViewPager2 using Fragments. Recommended for complex pages requiring their own lifecycle.

📌 Kiedy używać FragmentStateAdapter?When to use FragmentStateAdapter?
Implementacja adapteraAdapter Implementation
class OnboardingPagerAdapter(
    hostFragment: Fragment
) : AdInjectedFragmentStateAdapter<OnboardingPage>(
    hostFragment,
    AdsSpec(
        zoneName = "onboarding",
        callbackName = "default",
        firstAdPosition = 3,
        interval = 4,
        adKindForSlot = { idx ->
            if (idx % 2 == 0) AdKind.NATIVE else AdKind.BANNER
        }
    )
) {
    // Twórz fragment dla danego elementu contentu
    override fun createContentFragment(item: OnboardingPage, contentPosition: Int): Fragment =
        when (item) {
            is OnboardingPage.Welcome -> WelcomeFragment.newInstance(item.title)
            is OnboardingPage.Feature -> FeatureFragment.newInstance(item.title, item.description)
            is OnboardingPage.Final -> FinalFragment.newInstance()
        }

    // (Opcjonalnie) Stabilne ID dla lepszych animacji i zachowania stanu
    override fun stableId(item: OnboardingPage): Long =
        item.title.hashCode().toLong()
}
UżycieUsage
class OnboardingFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Przekaż `this` (Fragment) jako host
        val adapter = OnboardingPagerAdapter(this)
        binding.viewPager.adapter = adapter

        adapter.submitList(listOf(
            OnboardingPage.Welcome("Witaj!"),
            OnboardingPage.Feature("Funkcja 1", "Opis..."),
            OnboardingPage.Feature("Funkcja 2", "Opis..."),
            OnboardingPage.Final()
        ))
    }
}

Fragmenty reklam

Biblioteka dostarcza gotowe fragmenty dla reklam:

Są one automatycznie używane przez adapter - nie musisz ich tworzyć ręcznie.They are automatically used by the adapter - you don't need to create them manually.

🚧 IntegracjaIntegration z AdGate

Wszystkie adaptery automatycznie reagują na zmiany stanu AdGate:

Blokowanie reklam
// Zablokuj reklamy globalnie (np. użytkownik premium)
AdGate.block("premium_user")

// Odblokuj
AdGate.unblock("premium_user")

// Lokalna reguła dla strefy (np. blokuj tylko native)
AdGate.addLocalRule("feed") { request ->
    // true = pozwól, false = zablokuj
    request.format != AdGate.Format.NATIVE
}

// ⚠️ Po zmianie lokalnych reguł wywołaj refresh!
adapter.refreshAds()
⚠️ Ważne⚠️ Important

Zmiany globalne AdGate (block/unblock) są obsługiwane automatycznie przez adaptery. Tylko po addLocalRule() / removeLocalRule() musisz wywołać adapter.refreshAds().

Jak to działa?How does it work?

  1. Adaptery nasłuchują na AdGate.state (StateFlow)
  2. Gdy blocked zmieni się na true → wszystkie sloty reklam znikają
  3. Gdy blocked zmieni się na false → sloty są ponownie dodawane
  4. Widoczne reklamy są natychmiast zwijane (collapse) przy blokadzie