MVVM with API calling and Data binding

MVVM with API calling and Data binding

 MVVM with API calling using retrofit and other things like data binding, Coroutines and all



sample demo which I have created sample using AndroidHiveJSON

https://drive.google.com/file/d/1ejbuQ6cw0WSnsgCl9NsFeugKh0HzJwiK/view?usp=sharing



sample demo with GIT

https://drive.google.com/file/d/1RQLsNJGTO0th61Tmzxdyn5V5oS1xMeDb/view?usp=sharing


MVP Android

MVP Android

 MVP Android

1) SearchInteractor

    package com.hexabrain.aeon.ui.activities.Search


import android.content.Context
import android.util.Log
import com.hexabrain.aeon.api.APIClient
import com.hexabrain.aeon.model.ItemMain.VerticalGrid.New.AllChannelMain
import com.hexabrain.aeon.utils.AppPreferences
import com.hexabrain.aeon.utils.addTo
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.internal.schedulers.IoScheduler

class SearchInteractor {

interface OnLoginFinishedListener {
fun onError(noData:Boolean)
fun rowItemisLoading(isloading :Boolean)
fun showProgress()
fun hideProgress()
fun totalPages(totalPages: Int)
fun onSuccess(
searchMain: AllChannelMain,
totalSize: Int?
)
}
private fun createArrayList(): List<String> = (1..10).map { "Item $it" }

fun getSearchData(
requireContext: Context,
listener: OnLoginFinishedListener,
newValue: String,
page: Int?,
disposables: CompositeDisposable,
totalSize: Int?
) {
val token: String? = AppPreferences.from(requireContext)?.token

Log.e("=====Searchquery","value: ${newValue},, page: ${page}")

val header = HashMap<String, String>()
header["Authorization"] = "$token"
header["Content-Type"] = "application/json"

listener.showProgress()
APIClient.api.getSearchVideosInSearchPage(header, newValue, page)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(IoScheduler())
.subscribe(
{
listener.totalPages(it?.totalPages!!)
// listener.totalPages(4)
if (it.videos?.isNullOrEmpty()?.not()!!) {
listener.onError(false)
listener.onSuccess(it,totalSize)
}else{
listener.onError(true)
}
listener.hideProgress()
listener.rowItemisLoading(false)

},
{
listener.hideProgress()
listener.onError(true)
listener.rowItemisLoading(false)
listener.hideProgress()
Log.e("SearchFragment", "Error:- ${it.message}")
}
)
.addTo(disposables)
}
}


2) SearchPresenter


package com.hexabrain.aeon.ui.activities.Search

import android.content.Context
import com.hexabrain.aeon.model.ItemMain.VerticalGrid.New.AllChannelMain
import io.reactivex.disposables.CompositeDisposable

class SearchPresenter(var searchView: SearchView?, val searchInteractor: SearchInteractor) :
SearchInteractor.OnLoginFinishedListener {

fun getSearchData(
requireContext: Context,
newValue: String,
page: Int?,
disposables: CompositeDisposable,
totalSize: Int?
) {
searchView?.showProgress()
searchInteractor.getSearchData(requireContext, this,newValue,page,disposables,totalSize)
}

fun onDestroy() {
searchView = null
}

override fun onError(noData:Boolean) {
searchView?.apply {
onError(noData)
hideProgress()
}
}

override fun rowItemisLoading(isloading: Boolean) {
searchView?.apply {
itemIsLoading(isloading)
}
}

override fun showProgress() {
searchView?.apply {
showProgress()
}
}

override fun hideProgress() {
searchView?.apply {
hideProgress()
}
}

override fun totalPages(totalPages: Int) {
searchView?.apply {
totalPages(totalPages)
}
}

override fun onSuccess(
searchMain: AllChannelMain,
totalSize: Int?
) {
searchView?.successs(searchMain,totalSize)
}
}

3) SearchView

package com.hexabrain.aeon.ui.activities.Search

import com.hexabrain.aeon.model.ItemMain.VerticalGrid.New.AllChannelMain

interface SearchView {
fun showProgress()
fun hideProgress()
fun onError(noData:Boolean)
fun itemIsLoading(isloading: Boolean)
fun totalPages(pages: Int)
fun successs(searchMain: AllChannelMain,totalSize: Int?)
}
4) MyVideoFragment
package com.hexabrain.aeon.ui.activities

import android.os.Bundle
import androidx.leanback.app.RowsSupportFragment
import androidx.leanback.widget.*
import com.hexabrain.aeon.cardPresenter.SideInfoCardPresenter
import com.hexabrain.aeon.model.ItemMain.VerticalGrid.New.AllChannelMain
import com.hexabrain.aeon.model.ItemMain.VerticalGrid.New.AllChannelVideo
import com.hexabrain.aeon.ui.activities.Search.SearchInteractor
import com.hexabrain.aeon.ui.activities.Search.SearchPresenter
import com.hexabrain.aeon.ui.activities.Search.SearchView
import io.reactivex.disposables.CompositeDisposable
import okhttp3.OkHttpClient
import okhttp3.Response


class MyVideoFragment(
val onProgressbarVisibilityListener: (isStart: Boolean) -> Unit,
val onItemSelect: (seriesItem: AllChannelVideo, pos: Int, itemIndex: Int) -> Unit,
val onItemClick: (clickedSeries: AllChannelVideo, selectedIndex: Int) -> Unit,
val error: (error: Boolean, mainError: Boolean) -> Unit
) : RowsSupportFragment(),SearchView {

private val TAG = "MainFragment"
private val disposables = CompositeDisposable()
private var categoryAdapter: ArrayObjectAdapter? = null
private var categoryItemAdapter: ArrayObjectAdapter? = null
private val myVideosItemCount:Int = 10
private val presenter = SearchPresenter(this, SearchInteractor())

override fun onDestroy() {
super.onDestroy()
presenter.onDestroy()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val aa = ListRowPresenter(FocusHighlight.ZOOM_FACTOR_MEDIUM, false)
aa.shadowEnabled = false
aa.selectEffectEnabled = false
categoryAdapter = ArrayObjectAdapter(aa)
onProgressbarVisibilityListener.invoke(true)

presenter.getSearchData(requireContext(), "", 1, disposables, 0)

setOnItemViewSelectedListener { itemViewHolder, item, rowViewHolder, row ->
val rowPos = categoryAdapter?.indexOf(row) ?: 0
if (item is AllChannelVideo) {
onItemSelect.invoke(item, rowPos, categoryItemAdapter?.indexOf(item) ?: 0)
}
}

setOnItemViewClickedListener { itemViewHolder, item, rowViewHolder, row ->
if (item is AllChannelVideo) {

val listRow = row as ListRow
val currentRowAdapter = listRow.adapter as ArrayObjectAdapter
val selectedIndex = categoryItemAdapter?.indexOf(item) ?: 0

onItemClick.invoke(item, selectedIndex)
}
}
}

override fun showProgress() {
onProgressbarVisibilityListener.invoke(true)
}

override fun hideProgress() {
onProgressbarVisibilityListener.invoke(false)
}

override fun onError(noData: Boolean) {
error.invoke(noData, false)
}

override fun itemIsLoading(isloading: Boolean) {

}

override fun totalPages(pages: Int) {

}

override fun successs(searchMain: AllChannelMain, totalSize: Int?) {
var myList: MutableList<AllChannelVideo> = mutableListOf()

error.invoke(false, false)

val lastItem = AllChannelVideo()
lastItem?.videoUrl = "allchannel"
lastItem?.date = ""
lastItem?.name = "SEE ALL"
lastItem?.small_icon = ""
lastItem?.thumb = ""
lastItem?.type = "redirect"

if (searchMain?.videos?.size!! >= myVideosItemCount){
myList = searchMain?.videos?.take(myVideosItemCount)?.toMutableList()!!
myList?.add(lastItem)
}else{
myList = searchMain?.videos!!
}

val headerItem = HeaderItem("My Videos")

categoryItemAdapter = ArrayObjectAdapter(context?.let {
SideInfoCardPresenter(it)
})

if (myList?.isNullOrEmpty()?.not()!!) {
for (singleSeries in myList.indices) {
categoryItemAdapter?.add(myList!![singleSeries])
}
}

categoryAdapter?.add(ListRow(headerItem, categoryItemAdapter))


error.invoke(false, false)

onProgressbarVisibilityListener.invoke(false)
adapter = categoryAdapter
}
}