1

I am making a movie app in which on click of a button of type , It should make an api call with a diffrent sent of data. Ie whenever the user clicks on Series or Movie button , It should make an API call with that specific parameter.

enter image description here

Since I have used Android Paging3 for paging. The Initial data will be loaded. But the problem is when I click the Movie or Series button , I need to restart the PagingSource with new parameters and starting page number. But calling the paging source in viewmodel is not triggering anything .

I need a way to call the pagingSource with new Parameters , so that after that if paging happens , it should be done with those parameters.

My Paging Source

    class MoviePagingSource(
    private val searchQuery: String,
    private val searchType : String,
    private val movieRepository: MovieRepository
) : PagingSource<Int , Movie>() {

    private val INITIAL_PAGE = Constants.INITIAL_PAGE_NUMBER

    override fun getRefreshKey(state: PagingState<Int, Movie>): Int? {
        return state.anchorPosition?.let {
            state.closestPageToPosition(it)?.prevKey?.plus(1) ?: state.closestPageToPosition(it)?.nextKey?.minus(1)
        }
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Movie> {
        return try {
            val position = params.key ?: INITIAL_PAGE
            val response = movieRepository.searchMovie(searchQuery , position , searchType)
            val endOfPaginationReached = (response.data?.result == null)

            LoadResult.Page(
                data = response.data!!.result,
                prevKey = if(position == INITIAL_PAGE) null else position.minus(1),
                nextKey = if (endOfPaginationReached) null else position.plus(1)
            )
        }catch (e : Exception){
            LoadResult.Error(e)
        }
    }
}

Here the searchQuery and the searchType parameters are the ones that would be changed. Hence whenever that is changed , I need to call a new paging source. That is the problem

PagingRepository

class MoviePagingRepository @Inject constructor(
    private val movieRepository: MovieRepository
    ) {

    private var pagingConfig : PagingConfig

    init {
        pagingConfig = PagingConfig(pageSize = Constants.PAGE_SIZE , maxSize = Constants.MAX_PAGE_COUNT)
    }

    fun getMovies(searchQuery: String, movieType : String, )
    :LiveData<PagingData<Movie>> {
        return Pager(config = pagingConfig , pagingSourceFactory = {MoviePagingSource(searchQuery , movieType , movieRepository)}).liveData
    }
}

ViewModel

class HomeViewModel @Inject constructor(
    private val moviePagingRepository: MoviePagingRepository,
    private val movieRepository: MovieRepository
) : ViewModel(){

    var movieList : LiveData<PagingData<Movie>> = MutableLiveData()

    fun searchMovie(searchQuery : String , movieType : String) {
        movieList = moviePagingRepository.getMovies(searchQuery , movieType)
    }
}

MainActivity

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home)

        init()
    }


    private fun init(){

        movieViewModel = ViewModelProvider(this)[HomeViewModel::class.java]

        movieAdapter = MovieAdapter(movieViewModel)

        movies_recyclerview.apply {
            layoutManager = GridLayoutManager(context , Constants.COLUMN_COUNT)
            setHasFixedSize(true)
            adapter = movieAdapter.withLoadStateFooter(
                footer = LoaderAdapter()
            )
        }

        //This triggers the paging
        searchMovie(currentSearchQuery, currentMovieType)

        //This observes.
        observeViewModels()

    }

        private fun searchMovie(searchQuery : String , movieType : String){
        movieViewModel.searchMovie(searchQuery ,  movieType)
    }

    private fun observeViewModels(){

        movieViewModel.movieList.observe(this) {
            movieAdapter.submitData(lifecycle, it)
        }

        //This is triggered when I click on any movie type ( series , movie etc.)
       // This triggers the viewmodel method again. but apparently nothing happens
        movieViewModel.getMovieType().observe(this) { movieType ->
            searchMovie(currentSearchQuery,  currentMovieType)
        }
    }
}

Can someone help on this , Since I am relatively new to paging3 and this seems very confusing. adapter.refresh() or adapter.retry() both seem to not work with new variables. Any sort of suggestion or improvement would be appreciated. Thank you

Saneen K P
  • 303
  • 1
  • 8

1 Answers1

0

Do not call the searchMovie function in the init() function of your activity. That way, you are calling the function again and again on a configuration change. In your viewModel, add this code ->

val movieQueryParams = MutableLiveData(Pair("Your initial search query here","Your initial movie type here"))  
val movieList = movieQueryParams.switchMap { (searchQuery,movieType)->
   // call your movie repository function here 
}.cachedIn(viewModelScope) 

fun onSearchQuerySubmit(query: String){
  movieQueryParams.first = query  
}
fun onMovieTypeSubmit(movieType: String) {
  movieQueryParams.second = movieType
}

You call the onSeachQuerySubmit() and onMovieTypeSubmit() methods from your activity everytime the user inputs a new query, or clicks on a new movie type.
Edit : Btw, a slightly cleaner approach that would also help with dealing with stuff like process death would be to switch to kotlin flows instead of livedata

  • Thanks a lot , I did not know about switchMap . And about the Init function . How else would I initiate a movie search on the initial start of the application ? – Saneen K P Oct 04 '22 at 14:13
  • 1
    @SaneenKP The switchMap function gets triggered for the first time automatically. That would do the initial data fetch. No need to initiate the call from the activity. – Logging device Oct 05 '22 at 17:49
  • This is nice, for those looking to do this with stateflows as the query rather than using liveData, you could use the mutableStateFlow.flatMapLatest{ } – Neo Aug 10 '23 at 14:54