<commit_before>package com.benoitquenaudon.tvfoot.red.app.domain.matches

import android.databinding.DataBindingUtil
import android.databinding.ViewDataBinding
import android.support.v7.util.DiffUtil
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import com.benoitquenaudon.tvfoot.red.R
import com.benoitquenaudon.tvfoot.red.app.domain.matches.MatchesItemViewHolder.LoadingRowViewHolder
import com.benoitquenaudon.tvfoot.red.app.domain.matches.MatchesItemViewHolder.MatchHeaderViewHolder
import com.benoitquenaudon.tvfoot.red.app.domain.matches.MatchesItemViewHolder.MatchRowViewHolder
import com.benoitquenaudon.tvfoot.red.app.domain.matches.displayable.HeaderRowDisplayable
import com.benoitquenaudon.tvfoot.red.app.domain.matches.displayable.LoadingRowDisplayable
import com.benoitquenaudon.tvfoot.red.app.domain.matches.displayable.MatchRowDisplayable
import com.benoitquenaudon.tvfoot.red.app.domain.matches.displayable.MatchesItemDisplayable
import com.benoitquenaudon.tvfoot.red.app.domain.matches.displayable.MatchesItemDisplayableDiffUtilCallback
import com.benoitquenaudon.tvfoot.red.databinding.MatchesRowHeaderBinding
import com.benoitquenaudon.tvfoot.red.databinding.MatchesRowMatchBinding
import com.benoitquenaudon.tvfoot.red.databinding.RowLoadingBinding
import com.benoitquenaudon.tvfoot.red.injection.scope.ActivityScope
import io.reactivex.subjects.PublishSubject
import javax.inject.Inject

@ActivityScope class MatchesAdapter @Inject constructor() : RecyclerView.Adapter<MatchesItemViewHolder<*, *>>() {
  private var matchesItems = emptyList<MatchesItemDisplayable>()
  val matchRowClickObservable: PublishSubject<MatchRowDisplayable> =
      PublishSubject.create<MatchRowDisplayable>()

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MatchesItemViewHolder<*, *> {
    val layoutInflater = LayoutInflater.from(parent.context)
    val binding = DataBindingUtil.inflate<ViewDataBinding>(layoutInflater, viewType, parent, false)

    return when (viewType) {
      R.layout.matches_row_header -> MatchesItemViewHolder.MatchHeaderViewHolder(
          binding as MatchesRowHeaderBinding)
      R.layout.matches_row_match -> MatchesItemViewHolder.MatchRowViewHolder(
          binding as MatchesRowMatchBinding, this)
      R.layout.row_loading -> MatchesItemViewHolder.LoadingRowViewHolder(
          binding as RowLoadingBinding)
      else -> throw UnsupportedOperationException(
          "don't know how to deal with this viewType: " + viewType)
    }
  }

  override fun onBindViewHolder(holder: MatchesItemViewHolder<*, *>, position: Int) {
    val item: MatchesItemDisplayable = matchesItems[position]
    return when (holder) {
      is MatchHeaderViewHolder -> {
        if (item is HeaderRowDisplayable) {
          holder.bind(item)
        } else {
          throw IllegalStateException("Wrong item for MatchHeaderViewHolder $item")
        }
      }
      is MatchRowViewHolder -> {
        if (item is MatchRowDisplayable) {
          holder.bind(item)
        } else {
          throw IllegalStateException("Wrong item for MatchRowViewHolder $item")
        }
      }
      is LoadingRowViewHolder -> {
        if (item is LoadingRowDisplayable) {
          holder.bind(item)
        } else {
          throw IllegalStateException("Wrong item for LoadingRowViewHolder $item")
        }
      }
    }
  }

  override fun onViewRecycled(holder: MatchesItemViewHolder<*, *>?) {
    holder?.unbind()
    super.onViewRecycled(holder)
  }

  override fun getItemCount(): Int {
    return matchesItems.size
  }

  override fun getItemViewType(position: Int): Int =
      when (matchesItems[position]) {
        is MatchRowDisplayable -> R.layout.matches_row_match
        is HeaderRowDisplayable -> R.layout.matches_row_header
        is LoadingRowDisplayable -> R.layout.row_loading
        else -> throw UnsupportedOperationException(
            "Don't know how to deal with this item: $matchesItems[position]")
      }

  fun onClick(match: MatchRowDisplayable) {
    matchRowClickObservable.onNext(match)
  }

  private val diffUtilCallback = MatchesItemDisplayableDiffUtilCallback()

  fun setMatchesItems(newItems: List<MatchesItemDisplayable>) {
    val oldItems = this.matchesItems
    this.matchesItems = newItems

    diffUtilCallback.bindItems(oldItems, newItems)
    // TODO(benoit) calculate diff on worker thread
    // https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/common/DataBoundListAdapter.java#L77
    DiffUtil.calculateDiff(diffUtilCallback, true).dispatchUpdatesTo(this)
  }
}<commit_msg>RxJava for diffutil on background thread<commit_after>package com.benoitquenaudon.tvfoot.red.app.domain.matches

import android.databinding.DataBindingUtil
import android.databinding.ViewDataBinding
import android.support.v7.util.DiffUtil
import android.support.v7.util.DiffUtil.DiffResult
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import com.benoitquenaudon.tvfoot.red.R
import com.benoitquenaudon.tvfoot.red.app.common.schedulers.BaseSchedulerProvider
import com.benoitquenaudon.tvfoot.red.app.domain.matches.MatchesItemViewHolder.LoadingRowViewHolder
import com.benoitquenaudon.tvfoot.red.app.domain.matches.MatchesItemViewHolder.MatchHeaderViewHolder
import com.benoitquenaudon.tvfoot.red.app.domain.matches.MatchesItemViewHolder.MatchRowViewHolder
import com.benoitquenaudon.tvfoot.red.app.domain.matches.displayable.HeaderRowDisplayable
import com.benoitquenaudon.tvfoot.red.app.domain.matches.displayable.LoadingRowDisplayable
import com.benoitquenaudon.tvfoot.red.app.domain.matches.displayable.MatchRowDisplayable
import com.benoitquenaudon.tvfoot.red.app.domain.matches.displayable.MatchesItemDisplayable
import com.benoitquenaudon.tvfoot.red.app.domain.matches.displayable.MatchesItemDisplayableDiffUtilCallback
import com.benoitquenaudon.tvfoot.red.databinding.MatchesRowHeaderBinding
import com.benoitquenaudon.tvfoot.red.databinding.MatchesRowMatchBinding
import com.benoitquenaudon.tvfoot.red.databinding.RowLoadingBinding
import com.benoitquenaudon.tvfoot.red.injection.scope.ActivityScope
import io.reactivex.disposables.Disposable
import io.reactivex.subjects.PublishSubject
import javax.inject.Inject

@ActivityScope
class MatchesAdapter @Inject constructor(
    private val schedulerProvider: BaseSchedulerProvider
) : RecyclerView.Adapter<MatchesItemViewHolder<*, *>>() {
  private var matchesItems = emptyList<MatchesItemDisplayable>()
  private val matchesObservable: PublishSubject<List<MatchesItemDisplayable>> = PublishSubject.create()
  private val diffUtilCallback = MatchesItemDisplayableDiffUtilCallback()
  val matchRowClickObservable: PublishSubject<MatchRowDisplayable> = PublishSubject.create()

  init {
    processItemDiffs()
  }

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MatchesItemViewHolder<*, *> {
    val layoutInflater = LayoutInflater.from(parent.context)
    val binding = DataBindingUtil.inflate<ViewDataBinding>(layoutInflater, viewType, parent, false)

    return when (viewType) {
      R.layout.matches_row_header -> MatchesItemViewHolder.MatchHeaderViewHolder(
          binding as MatchesRowHeaderBinding)
      R.layout.matches_row_match -> MatchesItemViewHolder.MatchRowViewHolder(
          binding as MatchesRowMatchBinding, this)
      R.layout.row_loading -> MatchesItemViewHolder.LoadingRowViewHolder(
          binding as RowLoadingBinding)
      else -> throw UnsupportedOperationException(
          "don't know how to deal with this viewType: " + viewType)
    }
  }

  override fun onBindViewHolder(holder: MatchesItemViewHolder<*, *>, position: Int) {
    val item: MatchesItemDisplayable = matchesItems[position]
    return when (holder) {
      is MatchHeaderViewHolder -> {
        if (item is HeaderRowDisplayable) {
          holder.bind(item)
        } else {
          throw IllegalStateException("Wrong item for MatchHeaderViewHolder $item")
        }
      }
      is MatchRowViewHolder -> {
        if (item is MatchRowDisplayable) {
          holder.bind(item)
        } else {
          throw IllegalStateException("Wrong item for MatchRowViewHolder $item")
        }
      }
      is LoadingRowViewHolder -> {
        if (item is LoadingRowDisplayable) {
          holder.bind(item)
        } else {
          throw IllegalStateException("Wrong item for LoadingRowViewHolder $item")
        }
      }
    }
  }

  override fun onViewRecycled(holder: MatchesItemViewHolder<*, *>?) {
    holder?.unbind()
    super.onViewRecycled(holder)
  }

  override fun getItemCount(): Int {
    return matchesItems.size
  }

  override fun getItemViewType(position: Int): Int =
      when (matchesItems[position]) {
        is MatchRowDisplayable -> R.layout.matches_row_match
        is HeaderRowDisplayable -> R.layout.matches_row_header
        is LoadingRowDisplayable -> R.layout.row_loading
        else -> throw UnsupportedOperationException(
            "Don't know how to deal with this item: $matchesItems[position]")
      }

  fun onClick(match: MatchRowDisplayable) {
    matchRowClickObservable.onNext(match)
  }

  fun setMatchesItems(newItems: List<MatchesItemDisplayable>) {
    matchesObservable.onNext(newItems)
  }

  private fun processItemDiffs(): Disposable =
      matchesObservable
          .scan(Pair(emptyList(), null),
              { lastResult: Pair<List<MatchesItemDisplayable>, DiffResult?>, newItems: List<MatchesItemDisplayable> ->
                diffUtilCallback.let { callback ->
                  callback.bindItems(lastResult.first, newItems)
                  Pair(newItems, DiffUtil.calculateDiff(callback, true))
                }
              })
          .skip(1)
          .subscribeOn(schedulerProvider.computation())
          .observeOn(schedulerProvider.ui())
          .subscribe { (newItems, diffResult) ->
            matchesItems = newItems
            diffResult?.dispatchUpdatesTo(this)
          }
}
{'fraction_non_alphanumeric': 0.08599605522682446, 'fraction_numerical': 0.0007889546351084812, 'mean_word_length': 4.090863453815261, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 52, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6545066', 'n_tokens_mistral': 2991, 'n_tokens_neox': 2889, 'n_words': 489}