Skip to content

Instantly share code, notes, and snippets.

@Morteza-QN
Last active May 9, 2023 08:32
Show Gist options
  • Save Morteza-QN/e59a3a05fab513152315f479cede1288 to your computer and use it in GitHub Desktop.
Save Morteza-QN/e59a3a05fab513152315f479cede1288 to your computer and use it in GitHub Desktop.
custom abstract class
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.*
import javax.inject.Inject
@AndroidEntryPoint
abstract class BaseActivity<T : ViewBinding> : AppCompatActivity(), BaseView {
// SomeDependency with your own dependency
@Inject
lateinit var activity: AppCompatActivity
private var _binding: T? = null
protected val binding get() = _binding!!
private val job = SupervisorJob()
private val errorHandler = CoroutineExceptionHandler { _, exception ->
// handle errors here
}
val uiScope: CoroutineScope = CoroutineScope(Dispatchers.Main + job + errorHandler)
private val myActivityScope = CoroutineScope(Dispatchers.Main.immediate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_binding = getViewBinding()
setContentView(binding.root)
initView()
}
abstract fun initView()
abstract fun getViewBinding(): T
override fun onDestroy() {
super.onDestroy()
job.cancel()
myActivityScope.cancel()
_binding = null
}
}
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
abstract class BaseAdapter<T, VB : ViewBinding>(
private val itemClickListener: ((T) -> Unit)? = null
) : RecyclerView.Adapter<BaseAdapter<T, VB>.BaseViewHolder<VB>>() {
fun updateList(newList: List<T>) {
val diffResult = DiffUtil.calculateDiff(DiffUtilCallback(items, newList))
items.clear()
items.addAll(newList)
diffResult.dispatchUpdatesTo(this)
}
protected val items = mutableListOf<T>()
protected abstract fun getViewHolder(binding: VB): BaseViewHolder<VB>
protected abstract fun createBinding(parent: ViewGroup, viewType: Int): VB
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<VB> {
val binding = createBinding(parent, viewType)
return getViewHolder(binding)
}
override fun getItemCount(): Int = items.size
override fun onBindViewHolder(holder: BaseViewHolder<VB>, position: Int) {
val item = items[position]
holder.bind(item)
itemClickListener?.let { clickListener ->
holder.itemView.setOnClickListener {
clickListener(item)
}
}
}
abstract inner class BaseViewHolder<VB : ViewBinding>(val binding: VB) : RecyclerView.ViewHolder(binding.root) {
abstract fun bind(item: T)
}
private class DiffUtilCallback<T>(
private val oldList: List<T>,
private val newList: List<T>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
}
import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
import com.devhoony.lottieproegressdialog.LottieProgressDialog
import ir.adorateb.adorax.common.network.Resource
import timber.log.Timber
typealias Inflate<R> = (LayoutInflater, ViewGroup?, Boolean) -> R
abstract class BaseFragment<Binding : ViewBinding>(
private val inflate: Inflate<Binding>
) : Fragment(), BaseView {
private var _binding: Binding? = null
protected val binding get() = _binding!!
private val dialogLoading: LottieProgressDialog by lazy {
LottieProgressDialog(
context = requireContext(),
isCancel = false,
dialogWidth = null,
dialogHeight = null,
animationViewWidth = null,
animationViewHeight = null,
fileName = LottieProgressDialog.SAMPLE_1,
title = null,
titleVisible = null
)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = inflate.invoke(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
initObservers()
}
// handle changes to someLiveData
open fun initObservers() {}
// set up views and UI elements here
abstract fun initView()
protected fun <T> handleUIResult(resource: Resource<T>?) {
if (resource == null) return
when (resource) {
is Resource.Loading -> {
dialogLoading.show()
Timber.v(" >>> UI Result BaseFragment LOADING dialog show")
}
is Resource.Success -> {
dialogLoading.dismiss()
Timber.d(" >>> UI Result BaseFragment LOADING dismiss - SUCCESS")
}
is Resource.Error -> {
dialogLoading.dismiss()
Timber.e(" >>> UI Result BaseFragment LOADING dismiss - ERROR IS MSG=${resource.errorMessage} ")
}
is Resource.Failure -> {
dialogLoading.dismiss()
Timber.e(" >>> UI Result BaseFragment LOADING dismiss - FAILURE IS MSG=${(resource.message) ?: "failure"} ")
}
is Resource.NoInternet -> {
dialogLoading.dismiss()
Timber.i(" >>> UI Result BaseFragment LOADING dismiss - No Internet IS MSG=${resource.msg} ")
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
@Morteza-QN
Copy link
Author

lifecycleScope.launch{
    repeatOnLifecycle(Lifecycle.State.STARTED){
        SomeFlow.collect{
            // some things 
        }
    }
}

@Morteza-QN
Copy link
Author

private fun setUpSearch(){

        binding.etSearchNews.addTextChangedListener{ editable->
            job?.cancel()
            job = MainScope().launch {
                delay(500L)
            }

            editable?.let {
                if (editable.toString().isNotEmpty()){
                    allNewsViewModel.doSearchForNews(editable.toString())
                }
            }
        }

    }

@Morteza-QN
Copy link
Author

val scrollListener = object : RecyclerView.OnScrollListener(){
        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
            val layoutManager = recyclerView.layoutManager as LinearLayoutManager
            val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
            val visibleItemCount = layoutManager.childCount
            val totalItemCount = layoutManager.itemCount

            val isNoErrors = !isError

            val isLoadingAndNotLastPage = !isLoading && !isLastPage
            val isAtLastItem = firstVisibleItemPosition + visibleItemCount >= totalItemCount
            val isNotAtBeginning = firstVisibleItemPosition >= 0
            val isTotalMoreThanVisible = totalItemCount >= QUERY_PAGE_SIZE
            val shouldPaginate = isNoErrors && isLoadingAndNotLastPage && isAtLastItem && isNotAtBeginning &&
                    isTotalMoreThanVisible && isScrolling

            if(shouldPaginate) {
                allNewsViewModel.doSearchForNews(binding.etSearchNews.text.toString())
                isScrolling = false
            }
        }

        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            super.onScrollStateChanged(recyclerView, newState)
            if(newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                isScrolling = true
            }
        }

    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment