chayanforyou

RecyclerView with NativeAd - 2

May 7th, 2026
52
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Kotlin 8.04 KB | None | 0 0
  1. package io.github.chayanforyou.stickynotes.adapter
  2.  
  3. import android.content.Context
  4. import android.graphics.BitmapFactory
  5. import android.os.Handler
  6. import android.os.Looper
  7. import android.view.LayoutInflater
  8. import android.view.View
  9. import android.view.ViewGroup
  10. import androidx.recyclerview.widget.DiffUtil
  11. import androidx.recyclerview.widget.ListAdapter
  12. import androidx.recyclerview.widget.RecyclerView
  13. import com.google.android.gms.ads.AdListener
  14. import com.google.android.gms.ads.AdLoader
  15. import com.google.android.gms.ads.AdRequest
  16. import com.google.android.gms.ads.LoadAdError
  17. import com.google.android.gms.ads.nativead.NativeAd
  18. import io.github.chayanforyou.stickynotes.data.Note
  19. import io.github.chayanforyou.stickynotes.databinding.ItemNativeAdBinding
  20. import io.github.chayanforyou.stickynotes.databinding.ItemNoteBinding
  21. import io.github.chayanforyou.stickynotes.utils.NoteUtils
  22. import io.github.chayanforyou.stickynotes.R
  23.  
  24. private const val VIEW_TYPE_NOTE = 0
  25. private const val VIEW_TYPE_AD = 1
  26. private const val ADS_AFTER = 5
  27. private const val AD_UNIT_ID = "ca-app-pub-3940256099942544/2247696110"
  28.  
  29. sealed class AdapterItem {
  30.     data class NoteItem(val note: Note) : AdapterItem()
  31.     data class AdItem(val nativeAd: NativeAd) : AdapterItem()
  32. }
  33.  
  34. class NoteAdapter(
  35.     private val context: Context,
  36.     private val onNoteClick: (Note) -> Unit,
  37.     private val onNoteLongClick: (Note) -> Unit
  38. ) : ListAdapter<AdapterItem, RecyclerView.ViewHolder>(AdapterItemDiffCallback()) {
  39.  
  40.     private val adPositions = mutableSetOf<Int>()
  41.     private val mainHandler = Handler(Looper.getMainLooper())
  42.  
  43.     override fun getItemViewType(position: Int): Int {
  44.         return when (getItem(position)) {
  45.             is AdapterItem.NoteItem -> VIEW_TYPE_NOTE
  46.             is AdapterItem.AdItem -> VIEW_TYPE_AD
  47.         }
  48.     }
  49.  
  50.     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
  51.         val inflater = LayoutInflater.from(parent.context)
  52.         return when (viewType) {
  53.             VIEW_TYPE_AD -> {
  54.                 val binding = ItemNativeAdBinding.inflate(inflater, parent, false)
  55.                 NativeAdViewHolder(binding)
  56.             }
  57.  
  58.             else -> {
  59.                 val binding = ItemNoteBinding.inflate(inflater, parent, false)
  60.                 NoteViewHolder(binding, onNoteClick, onNoteLongClick)
  61.             }
  62.         }
  63.     }
  64.  
  65.     override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
  66.         when (val item = getItem(position)) {
  67.             is AdapterItem.NoteItem -> (holder as NoteViewHolder).bind(item.note)
  68.             is AdapterItem.AdItem -> (holder as NativeAdViewHolder).bind(item.nativeAd)
  69.         }
  70.     }
  71.  
  72.     fun submitNotes(notes: List<Note>) {
  73.         val items = mutableListOf<AdapterItem>()
  74.         adPositions.clear()
  75.  
  76.         notes.forEachIndexed { index, note ->
  77.             items.add(AdapterItem.NoteItem(note))
  78.             if ((index + 1) % ADS_AFTER == 0) {
  79.                 adPositions.add(items.size)
  80.             }
  81.         }
  82.  
  83.         super.submitList(items)
  84.         loadAds()
  85.     }
  86.  
  87.     private fun loadAds() {
  88.         adPositions.forEach { position -> loadAdForPosition(position) }
  89.     }
  90.  
  91.     private fun loadAdForPosition(position: Int) {
  92.         val adLoader = AdLoader.Builder(context, AD_UNIT_ID)
  93.             .forNativeAd { nativeAd ->
  94.                 mainHandler.post { insertAdAtPosition(position, nativeAd) }
  95.             }
  96.             .withAdListener(object : AdListener() {
  97.                 override fun onAdFailedToLoad(adError: LoadAdError) {
  98.                     adPositions.remove(position)
  99.                 }
  100.             })
  101.             .build()
  102.  
  103.         adLoader.loadAd(AdRequest.Builder().build())
  104.     }
  105.  
  106.     private fun insertAdAtPosition(position: Int, nativeAd: NativeAd) {
  107.         val currentList = currentList.toMutableList()
  108.         val adsInsertedBefore = adPositions.count { it < position }
  109.         val adjustedPosition = position + adsInsertedBefore
  110.  
  111.         if (adjustedPosition <= currentList.size) {
  112.             currentList.add(adjustedPosition, AdapterItem.AdItem(nativeAd))
  113.             submitList(currentList)
  114.         }
  115.     }
  116.  
  117.     class NoteViewHolder(
  118.         private val binding: ItemNoteBinding,
  119.         private val onNoteClick: (Note) -> Unit,
  120.         private val onNoteLongClick: (Note) -> Unit
  121.     ) : RecyclerView.ViewHolder(binding.root) {
  122.  
  123.         fun bind(note: Note) {
  124.             binding.apply {
  125.                 tvText.text = note.text
  126.                 tvText.textSize = note.textSize.toFloat()
  127.                 tvText.gravity = note.textAlign
  128.                 tvText.typeface = NoteUtils.getTypeface(root.context, note.textFont)
  129.                 tvText.setTextColor(NoteUtils.getTextColor(note.textColor))
  130.  
  131.                 ivHolder.apply {
  132.                     if (note.holderStyle != 0) {
  133.                         setImageResource(NoteUtils.getHolderResource(note.holderStyle))
  134.                         rotation = note.angle.toFloat()
  135.                     } else {
  136.                         setImageDrawable(null)
  137.                     }
  138.                 }
  139.  
  140.                 rlBg.apply {
  141.                     setBackgroundResource(NoteUtils.getBackgroundResource(note.backgroundColor))
  142.                     alpha = note.alpha / 255f
  143.                     rotation = note.angle.toFloat()
  144.                 }
  145.  
  146.                 ivDrawing.visibility = if (note.imageData != null && note.imageData.isNotEmpty()) {
  147.                     val bitmap =
  148.                         BitmapFactory.decodeByteArray(note.imageData, 0, note.imageData.size)
  149.                     ivDrawing.setImageBitmap(bitmap)
  150.                     View.VISIBLE
  151.                 } else {
  152.                     View.GONE
  153.                 }
  154.  
  155.                 ivLock.visibility = if (note.password != null) View.VISIBLE else View.GONE
  156.                 ivSelected.visibility = View.GONE
  157.             }
  158.  
  159.             itemView.setOnClickListener { onNoteClick(note) }
  160.             itemView.setOnLongClickListener {
  161.                 onNoteLongClick(note)
  162.                 true
  163.             }
  164.         }
  165.     }
  166.  
  167.     class NativeAdViewHolder(private val binding: ItemNativeAdBinding) :
  168.         RecyclerView.ViewHolder(binding.root) {
  169.  
  170.         fun bind(nativeAd: NativeAd) {
  171.             binding.apply {
  172.                 nativeAdView.mediaView = adMedia
  173.                 nativeAdView.headlineView = adHeadline
  174.                 nativeAdView.bodyView = adBody
  175.                 nativeAdView.callToActionView = adCallToAction
  176.  
  177.                 adHeadline.text = nativeAd.headline
  178.                 adBody.text = nativeAd.body
  179.                 adCallToAction.text = nativeAd.callToAction
  180.  
  181.                 nativeAdView.setNativeAd(nativeAd)
  182.             }
  183.         }
  184.     }
  185.  
  186.     private class AdapterItemDiffCallback : DiffUtil.ItemCallback<AdapterItem>() {
  187.         override fun areItemsTheSame(oldItem: AdapterItem, newItem: AdapterItem): Boolean {
  188.             return when {
  189.                 oldItem is AdapterItem.NoteItem && newItem is AdapterItem.NoteItem -> oldItem.note.id == newItem.note.id
  190.                 oldItem is AdapterItem.AdItem && newItem is AdapterItem.AdItem -> true
  191.                 else -> false
  192.             }
  193.         }
  194.  
  195.         override fun areContentsTheSame(oldItem: AdapterItem, newItem: AdapterItem): Boolean {
  196.             return when {
  197.                 oldItem is AdapterItem.NoteItem && newItem is AdapterItem.NoteItem -> {
  198.                     val baseEqual = oldItem.note.copy(imageData = null) == newItem.note.copy(imageData = null)
  199.                     val imageEqual = oldItem.note.imageData.contentEqualsNullable(newItem.note.imageData)
  200.                     baseEqual && imageEqual
  201.                 }
  202.  
  203.                 oldItem is AdapterItem.AdItem && newItem is AdapterItem.AdItem -> true
  204.                 else -> false
  205.             }
  206.         }
  207.     }
  208. }
  209.  
  210. private fun ByteArray?.contentEqualsNullable(other: ByteArray?): Boolean {
  211.     if (this === other) return true
  212.     if (this == null || other == null) return false
  213.     return this.contentEquals(other)
  214. }
  215.  
Advertisement
Add Comment
Please, Sign In to add comment