title: RecyclerView with multiple view types and paging library with boundary callback
body: <p>I have a project in which I want to show multiple Items ( Image, Text, Video etc.) to the user. I am aware of how multiple view types work for the RecyclerView.
  In my project, I have included most of the recommended <code>Android Architecture Components</code>
  like <code>Room</code>, <code>Paging</code>, <code>LiveData</code>, etc. </p>
  
  <p>To summarize the project design, I followed this <a href="https://codelabs.developers.google.com/codelabs/android-paging/#1" rel="nofollow noreferrer">codelab</a> which helped me a lot. The <code>Room</code> library + <code>Paging</code> library with a <code>BoundaryCallback</code> provides a good a way to implement an offline cache. Considering the database as source of truth and letting the <code>Paging</code> library request more data whenever the <code>Recyclerview</code> needs it via a <code>DataSource.Factory</code>, seemed to me a very good approach.</p>
  
  <p>But the downside of that <a href="https://codelabs.developers.google.com/codelabs/android-paging/#0" rel="nofollow noreferrer">project</a> is that they show how the whole architecture components stuff (<code>Room</code> and <code>Paging</code> with <code>BoundaryCallback</code>) works for only one item type. <strong>But I have multiple view types</strong> and I could not handle that.</p>
  
  <p>In the following I show you code snippets from my project to illustrate where I was stucked.</p>
  
  <p>Let's start with the models. Suppose, we have two item types: Image and Text.</p>
  
  <pre><code>sealed class Item {
  
      @JsonClass(generateAdapter = true)
      @Entity(tableName = "image_table")
      data class Image(
          @PrimaryKey
          @Json(name = "id")
          val imageId: Long,
          val image: String
      ): Item()
  
  
      @JsonClass(generateAdapter = true)
      @Entity(tableName = "text_table")
      data class Text(
          @PrimaryKey
          @Json(name = "id")
          val textId: Long,
          val text:String
      ):Item()
  }
  </code></pre>
  
  <p>As you can see, my model classes are extending the <code>Item</code> sealed class. So, I can treat the <code>Image</code> and <code>Text</code> class as <code>Item</code>s.
  The adapter class looks then like this:</p>
  
  <pre><code>private val ITEM_VIEW_TYPE_IMAGE = 0
  private val ITEM_VIEW_TYPE_TEXT = 1
  
  class ItemAdapter():
          PagedListAdapter&lt;Item, RecyclerView.ViewHolder&gt;(ItemDiffCallback()) {
  
      override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
          when (holder) {
              is TextItemViewHolder -&gt; {
                  val textItem = getItem(position) as Item.Text
                  holder.bind(textItem)
              }
              is ImageItemViewHolder -&gt; {
                  val imageItem = getItem(position) as Item.Image
                  holder.bind(imageItem)
              }
          }
      }
  
      override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
          return when (viewType) {
              ITEM_VIEW_TYPE_IMAGE -&gt; ImageItemViewHolder.from(parent)
              ITEM_VIEW_TYPE_TEXT -&gt; TextItemViewHolder.from(parent)
              else -&gt; throw ClassCastException("Unknown viewType ${viewType}")
          }
      }
  
      override fun getItemViewType(position: Int): Int {
          return when (getItem(position)) {
              is Item.Image -&gt; ITEM_VIEW_TYPE_IMAGE
              is Item.Text -&gt; ITEM_VIEW_TYPE_TEXT
          }
      }
  
      class TextItemViewHolder private constructor(val binding: ListItemTextBinding): RecyclerView.ViewHolder(binding.root) {
  
          fun bind(item: Text) {
              binding.text = item
              binding.executePendingBindings()
          }
          companion object {
              fun from(parent: ViewGroup): TextItemViewHolder {
                  val layoutInflater = LayoutInflater.from(parent.context)
                  val binding = ListItemTextBinding.inflate(layoutInflater, parent, false)
                  return TextItemViewHolder(binding)
              }
          }
      }
  
  
      class ImageItemViewHolder private constructor(val binding: ListItemImageBinding) : RecyclerView.ViewHolder(binding.root){
  
          fun bind(item: Image) {
              binding.image = item
              binding.executePendingBindings()
          }
  
          companion object {
              fun from(parent: ViewGroup): ImageItemViewHolder {
                  val layoutInflater = LayoutInflater.from(parent.context)
                  val binding = ListItemImageBinding.inflate(layoutInflater, parent, false)
                  return ImageItemViewHolder(binding)
              }
          }
      }
  }
  
  class ItemDiffCallback : DiffUtil.ItemCallback&lt;Item&gt;() {
  
      // HERE, I have the problem that I can not access the id attribute
      override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
          return oldItem.id == newItem.id
      }
  
      override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
          return oldItem == newItem
      }
  }
  </code></pre>
  
  <p>}</p>
  
  <p>Here, the problem I am faced with is the <code>ItemDiffCallback</code> class. In the <code>areItemsTheSame</code> method I can not access the <code>id</code> attribute using the superclass <code>Item</code>. What can I do here?</p>
  
  <p>Now, I am going to the repository class. As you might know, the repository class is responsible for retrieving the data first from the database since in my project the database is the source of truth. If no data is there or if more data is needed, the Paging library uses a BoundaryCallback class to request more data from the service and store them into the database. Here is my repository class for the Items:</p>
  
  <pre><code>class ItemRepository(private val database: MyLimDatabase, private val service: ApiService) {
  
      fun getItems(): LiveData&lt;PagedList&lt;Item&gt;&gt; {
          val dataSourceFactory = ???????????? FROM WHICH TABLE I HAVE TO CHOOSE ???????? database.textDao.getTexts() or database.imageDao.getImages()
          val config = PagedList.Config.Builder()
              .setPageSize(30)            // defines the number of items loaded at once from the DataSource
              .setInitialLoadSizeHint(50) // defines how many items to load when first load occurs
              .setPrefetchDistance(10)    // defines how far from the edge of loaded content an access must be to trigger further loading
              .build()
  
          val itemBoundaryCallback = ItemBoundaryCallback(service, database)
  
          return LivePagedListBuilder(dataSourceFactory, config)
              .setBoundaryCallback(itemBoundaryCallback)
              .build()
      }
  }
  </code></pre>
  
  <p>In this case, I have the problem that I do not know how to initialize the <code>dataSourceFactory</code> variable because I have two <code>DAO</code> classes. These are:</p>
  
  <pre><code>@Dao
  interface ImageDao{
      @Query("SELECT * from image_table")
      fun getImages(): DataSource.Factory&lt;Int, Image&gt;
  
      @Insert(onConflict = OnConflictStrategy.REPLACE)
      fun insertAll(images: List&lt;Image&gt;)
  }
  
  @Dao
  interface TextDao{
      @Query("SELECT * from text_table")
      fun getTexts(): DataSource.Factory&lt;Int, Text&gt;
  
      @Insert(onConflict = OnConflictStrategy.REPLACE)
      fun insertAll(texts: List&lt;Text&gt;)
  }
  </code></pre>
  
  <p>So, how to handle this ? </p>
  
  <p>Can someone help ?</p>